7
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Microsoft Power AppsAdvent Calendar 2024

Day 22

Power AppsのNotify関数が小さすぎる問題

Posted at

はじめに

Power AppsではNotify 関数という、バナーメッセージを表示する関数があります。

コチラを使うことで、メッセージの種類に応じて、色とアイコンが使用されたメッセージの表示ができます。

これ小さすぎない?

Notify 関数の弱点として、メッセージが小さいです。
実際の画面を見てみると、ほとんど見えない。特にPCでアプリケーションを使っている際には、ユーザーはポップアップに慣れている感覚があるためか、私の実体験として不評です。

スクリーンショット 2024-12-22 141914.png

画面上部に表示されているバナーがNotify 関数による表示です。

スクリーンショット 2024-12-22 141928.png

タイムアウトを指定し、フェードアウトをさせられる便利な機能です。

ブラウザで起動させたパターンですが、黄色警告のバナーのサイズもかなり小さい。

スクリーンショット 2024-12-22 142147.png

表示されているんだから見てください、というストロングスタイルもありますが、決してユーザー満足度が高くなるとは言い難いのではないでしょうか。

スクリーンショット 2024-12-22 141933.png

HTML文字列でNotifyを作る

対処方法として、HTML文字列で代替となる通知を自作する方法があります。
画面の最前面にHTML 文字列を用意し、下記の要素を入れてみるとしましょう。

ℹ️ information
ボタンコントロールで、HTML文字列を定義し、表示させます。
下記はinforomationのバージョンです。
アイコンは絵文字で代用しています。

スクリーンショット 2024-12-22 141941.png

OnSelect
UpdateContext(
    {
        ShowNotification: {
            type: "information",
            message: "Thanks for trying this!",
            html: "<div style='
                    background-color: #ebf5ff;
                    color: #1e429f;
                    padding: 12px;
                    border-radius: 6px;
                    border: 1px solid #c3dafe;
                    text-align: center;
                    position: relative;
                    font-size: 18px;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                    gap: 8px;
                    animation: fadeOut 3s linear forwards;
                    '>
                    ℹ️ Thanks for trying this!
                    </div>"
        }
    }
);
UpdateContext({locNotifyVisible: true});

⚠️ warning

共通化する箇所はApp.Formulasで定義することが可能です。

スクリーンショット 2024-12-22 141958.png

OnSelect
UpdateContext(
    {
        ShowNotification: {
            type: "warning",
            message: "Please check your input!",
            html: "<div style='
                        background-color: #fff3cd;
                        color: #856404;
                        padding: 12px;
                        border-radius: 6px;
                        border: 1px solid #ffeeba;
                        text-align: center;
                        position: relative;
                        font-size: 18px;
                        display: flex;
                        align-items: center;
                        justify-content: center;
                        gap: 8px;
                        animation: fadeOut 3s linear forwards;
                    '>
                        ⚠️ Please check your input!
                    </div>"
        }
    }
);
UpdateContext({locNotifyVisible: true});

✅ success
locNotifyVisibleによって、パーツの表示・非表示をコントロールします。

スクリーンショット 2024-12-22 141951.png

OnSelect
UpdateContext(
    {
        ShowNotification: {
            type: "success",
            message: "Operation completed successfully!",
            html: "<div style='
                    background-color: #d4edda;
                    color: #155724;
                    padding: 12px;
                    border-radius: 6px;
                    border: 1px solid #c3e6cb;
                    text-align: center;
                    position: relative;
                    font-size: 18px;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                    gap: 8px;
                    animation: fadeOut 3s linear forwards;
                '>
                    ✅ Operation completed successfully!
                </div>"
        }
    }
);
UpdateContext({locNotifyVisible: true});

❌ error
infowarningsuccesserrorほぼ共通です。
プレビュー段階のため避けていますが、再利用できる関数で作成できることが理想的ですね。

スクリーンショット 2024-12-22 142004.png

OnSelect
UpdateContext(
    {
        ShowNotification: {
            type: "error",
            message: "An error has occurred!",
            html: "<div style='
                        background-color: #f8d7da;
                        color: #721c24;
                        padding: 12px;
                        border-radius: 6px;
                        border: 1px solid #f5c6cb;
                        text-align: center;
                        position: relative;
                        font-size: 18px;
                        display: flex;
                        align-items: center;
                        justify-content: center;
                        gap: 8px;
                        animation: fadeOut 3s linear forwards;
                    '>
                        ❌ An error has occurred!
                    </div>"
        }
    }
);
UpdateContext({locNotifyVisible: true});

ポップアップを自作する

次にPopUpHTML文字列で再現してみましょう。
YesNoのハンドリングは発生しないあくまで一時的な通知として使う想定で作成します。

関連する要素が多くなるため、定型的な文字列は、App.Formulasで設定してしまいます。
変動しない要素は、コチラで設定してしまうことがおススメです。

App.Formulas
popupColors = {
    info: {
        primary: "#4A90E2",
        icon: "ℹ️"
    },
    success: {
        primary: "#28a745",
        icon: ""
    },
    warning: {
        primary: "#ffc107",
        icon: "⚠️"
    },
    error: {
        primary: "#dc3545",
        icon: ""
    },
    overlay: "rgba(0, 0, 0, 0.5)",
    white: "white",
    text: "#333",
    close: "#666"
};
popupSizes = {
    width: "50vw",
    maxWidth: "500px",
    minWidth: "300px",
    height: "200px",
    accentWidth: "6px",
    padding: "1.5rem",
    borderRadius: "8px"
};
popupIcon = {
    size: "18px",
    fontSize: "12px"
};

ℹ️ information
ポップアップのイメージです。あくまでなんちゃってポップアップです。

スクリーンショット 2024-12-22 142014.png

OnSelect
UpdateContext(
    {
        currentPopup: Switch(
            Self.Text,
            "Info",
            {
                primary: popupColors.info.primary,
                icon: popupColors.info.icon,
                title: "確認",
                content: "このアクションを実行してもよろしいですか?"
            },
            "Success",
            {
                primary: popupColors.success.primary,
                icon: popupColors.success.icon,
                title: "完了",
                content: "アクションは正常に完了しました。"
            },
            "Warn",
            {
                primary: popupColors.warning.primary,
                icon: popupColors.warning.icon,
                title: "警告",
                content: "このアクションは取り消せません。続行しますか?"
            },
            "Error",
            {
                primary: popupColors.error.primary,
                icon: popupColors.error.icon,
                title: "エラー",
                content: "エラーが発生しました。もう一度お試しください。"
            }
        )
    }
);
UpdateContext(
    {
        popupStyles: {
            overlay: 
                $"position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: {popupColors.overlay}; display: flex; justify-content: center; align-items: center;",
            container: 
                $"background-color: {popupColors.white}; padding: {popupSizes.padding}; border-radius: {popupSizes.borderRadius}; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); width: {popupSizes.width}; max-width: {popupSizes.maxWidth}; min-width: {popupSizes.minWidth}; height: {popupSizes.height}; position: relative; display: flex;",
            accentLine: 
                $"position: absolute; left: 0; top: 0; width: {popupSizes.accentWidth}; height: 100%; background-color: {currentPopup.primary}; border-top-left-radius: {popupSizes.borderRadius}; border-bottom-left-radius: {popupSizes.borderRadius};",
            contentWrapper: 
                $"flex-grow: 1; padding-left: 1rem; display: flex; flex-direction: column;",
            header: $"margin-bottom: 1rem;",
            title: $"font-size: 1.125rem; font-weight: 600; margin: 0; color: {popupColors.text}; display: flex; align-items: center; gap: 0.5rem;",
            infoIcon: $"display: flex; align-items: center; justify-content: center; width: {popupIcon.size}; height: {popupIcon.size}; solid {currentPopup.primary}; border-radius: 50%; color: {currentPopup.primary}; font-size: {popupIcon.fontSize}; font-weight: bold; flex-shrink: 0;",
            content: $"flex-grow: 1; display: flex; align-items: center; font-size: 0.975rem;",
            buttonArea: $"display: flex; justify-content: flex-end; padding-top: 1rem;",
            button: 
                $"padding: 0.5rem 2rem; border-radius: 4px; border: none; cursor: pointer; font-weight: 500; background-color: {currentPopup.primary}; color: {popupColors.white}; min-width: 100px;",
            closeButton: 
                $"position: absolute; top: 1rem; right: 1rem; width: 24px; height: 24px; border: none; background: none; cursor: pointer; padding: 0; display: flex; align-items: center; justify-content: center;",
            closeLine: $"position: absolute; width: 2px; height: 16px; background-color: {popupColors.close};"
        },
        popupTexts: {
            title: currentPopup.title,
            content: currentPopup.content,
            button: "Yes",
            close: "閉じる",
            info: currentPopup.icon
        }
    }
);
UpdateContext({locPopupVisible: true});

ポップアップOnSelectUpdateContext({locPopupVisible: true});を設定して通知を消します。
つまりボタンでもどこでもクリックすれば通知が消えるという仕様です。
クリックされたコントロールによって操作をわけるためには、コンポーネントで自作する方法が挙げられます。

⚠️ warning
Switch(Self.Text・・・は、ボタンコントロールのテキストInfoSuccessWarningErrorから設定する値を定義を分けています。

スクリーンショット 2024-12-22 142021.png

OnSelect
UpdateContext(
    {
        currentPopup: Switch(
            Self.Text,
            "Info",
            {
                primary: popupColors.info.primary,
                icon: popupColors.info.icon,
                title: "確認",
                content: "このアクションを実行してもよろしいですか?"
            },
            "Success",
            {
                primary: popupColors.success.primary,
                icon: popupColors.success.icon,
                title: "完了",
                content: "アクションは正常に完了しました。"
            },
            "Warning",
            {
                primary: popupColors.warning.primary,
                icon: popupColors.warning.icon,
                title: "警告",
                content: "このアクションは取り消せません。続行しますか?"
            },
            "Error",
            {
                primary: popupColors.error.primary,
                icon: popupColors.error.icon,
                title: "エラー",
                content: "エラーが発生しました。もう一度お試しください。"
            }
        )
    }
);
UpdateContext(
    {
        popupStyles: {
            overlay: 
                $"position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: {popupColors.overlay}; display: flex; justify-content: center; align-items: center;",
            container: 
                $"background-color: {popupColors.white}; padding: {popupSizes.padding}; border-radius: {popupSizes.borderRadius}; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); width: {popupSizes.width}; max-width: {popupSizes.maxWidth}; min-width: {popupSizes.minWidth}; height: {popupSizes.height}; position: relative; display: flex;",
            accentLine: 
                $"position: absolute; left: 0; top: 0; width: {popupSizes.accentWidth}; height: 100%; background-color: {currentPopup.primary}; border-top-left-radius: {popupSizes.borderRadius}; border-bottom-left-radius: {popupSizes.borderRadius};",
            contentWrapper: 
                $"flex-grow: 1; padding-left: 1rem; display: flex; flex-direction: column;",
            header: $"margin-bottom: 1rem;",
            title: $"font-size: 1.125rem; font-weight: 600; margin: 0; color: {popupColors.text}; display: flex; align-items: center; gap: 0.5rem;",
            infoIcon: $"display: flex; align-items: center; justify-content: center; width: {popupIcon.size}; height: {popupIcon.size}; solid {currentPopup.primary}; border-radius: 50%; color: {currentPopup.primary}; font-size: {popupIcon.fontSize}; font-weight: bold; flex-shrink: 0;",
            content: $"flex-grow: 1; display: flex; align-items: center; font-size: 0.975rem;",
            buttonArea: $"display: flex; justify-content: flex-end; padding-top: 1rem;",
            button: 
                $"padding: 0.5rem 2rem; border-radius: 4px; border: none; cursor: pointer; font-weight: 500; background-color: {currentPopup.primary}; color: {popupColors.white}; min-width: 100px;",
            closeButton: 
                $"position: absolute; top: 1rem; right: 1rem; width: 24px; height: 24px; border: none; background: none; cursor: pointer; padding: 0; display: flex; align-items: center; justify-content: center;",
            closeLine: $"position: absolute; width: 2px; height: 16px; background-color: {popupColors.close};"
        },
        popupTexts: {
            title: currentPopup.title,
            content: currentPopup.content,
            button: "Yes",
            close: "閉じる",
            info: currentPopup.icon
        }
    }
);
UpdateContext({locPopupVisible: true});

✅ success

スクリーンショット 2024-12-22 142037.png

OnSelect
UpdateContext(
    {
        currentPopup: Switch(
            Self.Text,
            "Info",
            {
                primary: popupColors.info.primary,
                icon: popupColors.info.icon,
                title: "確認",
                content: "このアクションを実行してもよろしいですか?"
            },
            "Success",
            {
                primary: popupColors.success.primary,
                icon: popupColors.success.icon,
                title: "完了",
                content: "アクションは正常に完了しました。"
            },
            "Warn",
            {
                primary: popupColors.warning.primary,
                icon: popupColors.warning.icon,
                title: "警告",
                content: "このアクションは取り消せません。続行しますか?"
            },
            "Error",
            {
                primary: popupColors.error.primary,
                icon: popupColors.error.icon,
                title: "エラー",
                content: "エラーが発生しました。もう一度お試しください。"
            }
        )
    }
);
UpdateContext(
    {
        popupStyles: {
            overlay: 
                $"position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: {popupColors.overlay}; display: flex; justify-content: center; align-items: center;",
            container: 
                $"background-color: {popupColors.white}; padding: {popupSizes.padding}; border-radius: {popupSizes.borderRadius}; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); width: {popupSizes.width}; max-width: {popupSizes.maxWidth}; min-width: {popupSizes.minWidth}; height: {popupSizes.height}; position: relative; display: flex;",
            accentLine: 
                $"position: absolute; left: 0; top: 0; width: {popupSizes.accentWidth}; height: 100%; background-color: {currentPopup.primary}; border-top-left-radius: {popupSizes.borderRadius}; border-bottom-left-radius: {popupSizes.borderRadius};",
            contentWrapper: 
                $"flex-grow: 1; padding-left: 1rem; display: flex; flex-direction: column;",
            header: $"margin-bottom: 1rem;",
            title: $"font-size: 1.125rem; font-weight: 600; margin: 0; color: {popupColors.text}; display: flex; align-items: center; gap: 0.5rem;",
            infoIcon: $"display: flex; align-items: center; justify-content: center; width: {popupIcon.size}; height: {popupIcon.size}; solid {currentPopup.primary}; border-radius: 50%; color: {currentPopup.primary}; font-size: {popupIcon.fontSize}; font-weight: bold; flex-shrink: 0;",
            content: $"flex-grow: 1; display: flex; align-items: center; font-size: 0.975rem;",
            buttonArea: $"display: flex; justify-content: flex-end; padding-top: 1rem;",
            button: 
                $"padding: 0.5rem 2rem; border-radius: 4px; border: none; cursor: pointer; font-weight: 500; background-color: {currentPopup.primary}; color: {popupColors.white}; min-width: 100px;",
            closeButton: 
                $"position: absolute; top: 1rem; right: 1rem; width: 24px; height: 24px; border: none; background: none; cursor: pointer; padding: 0; display: flex; align-items: center; justify-content: center;",
            closeLine: $"position: absolute; width: 2px; height: 16px; background-color: {popupColors.close};"
        },
        popupTexts: {
            title: currentPopup.title,
            content: currentPopup.content,
            button: "Yes",
            close: "閉じる",
            info: currentPopup.icon
        }
    }
);
UpdateContext({locPopupVisible: true});

❌ error

スクリーンショット 2024-12-22 142046.png

OnSelect
UpdateContext(
    {
        currentPopup: Switch(
            Self.Text,
            "Info",
            {
                primary: popupColors.info.primary,
                icon: popupColors.info.icon,
                title: "確認",
                content: "このアクションを実行してもよろしいですか?"
            },
            "Success",
            {
                primary: popupColors.success.primary,
                icon: popupColors.success.icon,
                title: "完了",
                content: "アクションは正常に完了しました。"
            },
            "Warn",
            {
                primary: popupColors.warning.primary,
                icon: popupColors.warning.icon,
                title: "警告",
                content: "このアクションは取り消せません。続行しますか?"
            },
            "Error",
            {
                primary: popupColors.error.primary,
                icon: popupColors.error.icon,
                title: "エラー",
                content: "エラーが発生しました。もう一度お試しください。"
            }
        )
    }
);
UpdateContext(
    {
        popupStyles: {
            overlay: 
                $"position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: {popupColors.overlay}; display: flex; justify-content: center; align-items: center;",
            container: 
                $"background-color: {popupColors.white}; padding: {popupSizes.padding}; border-radius: {popupSizes.borderRadius}; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); width: {popupSizes.width}; max-width: {popupSizes.maxWidth}; min-width: {popupSizes.minWidth}; height: {popupSizes.height}; position: relative; display: flex;",
            accentLine: 
                $"position: absolute; left: 0; top: 0; width: {popupSizes.accentWidth}; height: 100%; background-color: {currentPopup.primary}; border-top-left-radius: {popupSizes.borderRadius}; border-bottom-left-radius: {popupSizes.borderRadius};",
            contentWrapper: 
                $"flex-grow: 1; padding-left: 1rem; display: flex; flex-direction: column;",
            header: $"margin-bottom: 1rem;",
            title: $"font-size: 1.125rem; font-weight: 600; margin: 0; color: {popupColors.text}; display: flex; align-items: center; gap: 0.5rem;",
            infoIcon: $"display: flex; align-items: center; justify-content: center; width: {popupIcon.size}; height: {popupIcon.size}; solid {currentPopup.primary}; border-radius: 50%; color: {currentPopup.primary}; font-size: {popupIcon.fontSize}; font-weight: bold; flex-shrink: 0;",
            content: $"flex-grow: 1; display: flex; align-items: center; font-size: 0.975rem;",
            buttonArea: $"display: flex; justify-content: flex-end; padding-top: 1rem;",
            button: 
                $"padding: 0.5rem 2rem; border-radius: 4px; border: none; cursor: pointer; font-weight: 500; background-color: {currentPopup.primary}; color: {popupColors.white}; min-width: 100px;",
            closeButton: 
                $"position: absolute; top: 1rem; right: 1rem; width: 24px; height: 24px; border: none; background: none; cursor: pointer; padding: 0; display: flex; align-items: center; justify-content: center;",
            closeLine: $"position: absolute; width: 2px; height: 16px; background-color: {popupColors.close};"
        },
        popupTexts: {
            title: currentPopup.title,
            content: currentPopup.content,
            button: "Yes",
            close: "閉じる",
            info: currentPopup.icon
        }
    }
);
UpdateContext({locPopupVisible: true});

おわりに

より本格的なものを参照したい場合は下記のサイトがおススメです。
Power AppsのYAML ソースコードが参照できます。

7
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?