モーダルダイアログのラッパー
プリザンターには jQuery UI が組み込まれており、.dialog() メソッドをそのまま使えます。このページでは、.dialog() をラップして $p.modal.alert()・$p.modal.confirm()・$p.modal.prompt()・$p.modal.show() の形で呼び出せる軽量なラッパーを、拡張スタイルと拡張スクリプトの 2 ファイルで実装する方法を示します。 外部ライブラリは不要で、v1・v2 どちらのテーマでも利用できます。
前提: ダイアログは jQuery UI ベース
v2 テーマではデートピッカーが flatpickr に置き換わるなど jQuery UI 離れが進んでいますが、ダイアログ機能についてはテーマバージョンを問わず jquery-ui.min.js と jquery-ui.min.css が読み込まれています。$p.openDialog / $p.closeDialog も内部で .dialog() を呼んでおり、v2 テーマの style.scss でも .ui-dialog に CSS カスタムプロパティでスタイルを当てているため、ダイアログは引き続き jQuery UI ベースで動作します。
仕様
| 項目 | 内容 |
|---|---|
| 依存ライブラリ | なし(プリザンター標準の jQuery / jQuery UI のみ) |
| 名前空間 | $p.modal に各メソッドを追加 |
| メソッド | alert / confirm / prompt / show の 4 種類 |
| 戻り値 | すべて jQuery Deferred(done / fail でコールバック) |
| スタイル | 拡張スタイルで jQuery UI のデフォルトテーマを上書き |
メソッド一覧
| メソッド | 用途 | ボタン | 戻り値 |
|---|---|---|---|
$p.modal.alert(message, title) | 情報・警告の表示 | OK | done で OK 押下後の処理 |
$p.modal.confirm(message, title) | 操作の確認 | OK / キャンセル | done(OK) / fail(キャンセル) |
$p.modal.prompt(message, title, defaultValue) | テキスト入力 | OK / キャンセル | done(value)(入力値) / fail(キャンセル) |
$p.modal.show(options) | 任意の HTML を表示 | 自由に設定 | done(result) / fail |
title を省略した場合の既定タイトルは、alert が「メッセージ」、confirm が「確認」、prompt が「入力」、show が「ダイアログ」です。
実装
| 拡張機能 | 配置先 | 役割 |
|---|---|---|
| 拡張スタイル | App_Data/Parameters/ExtendedStyles/ | jQuery UI ダイアログの見た目を調整 |
| 拡張スクリプト | App_Data/Parameters/ExtendedScripts/ | $p.modal 名前空間にメソッドを追加 |
拡張スタイル
前半は v1 テーマ向けの固定値、後半は v2 テーマ向けに CSS カスタムプロパティ(--base-bg / --base-text / --btn-positive-bg など)で上書きする構成です。v2 テーマでは :root にカスタムプロパティが定義されているため自動でテーマの配色に切り替わり、v1 テーマではカスタムプロパティが未定義のため前半の固定値がそのまま使われます。
/* --- モーダルラッパー専用スタイル --- */
/* ── 共通 / v1テーマ向け(固定値) ─────────────── */
/* オーバーレイ */
.modal-wrapper-overlay {
background: rgba(0, 0, 0, 0.5) !important;
z-index: 10000 !important;
}
/* ダイアログ本体 */
.ui-dialog.modal-wrapper {
border: none;
border-radius: 8px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.25);
padding: 0;
z-index: 10001;
font-family: inherit;
}
/* タイトルバー */
.ui-dialog.modal-wrapper .ui-dialog-titlebar {
background: #fff;
border: none;
border-bottom: 1px solid #e0e0e0;
border-radius: 8px 8px 0 0;
padding: 16px 20px;
font-size: 16px;
font-weight: bold;
color: #333;
}
/* 閉じるボタン非表示 */
.ui-dialog.modal-wrapper .ui-dialog-titlebar-close {
display: none;
}
/* コンテンツ */
.ui-dialog.modal-wrapper .ui-dialog-content {
padding: 20px;
font-size: 14px;
line-height: 1.7;
color: #444;
}
/* メッセージテキスト */
.ui-dialog.modal-wrapper .mw-message {
white-space: pre-wrap;
word-break: break-word;
}
/* 入力欄 */
.ui-dialog.modal-wrapper .mw-input {
width: 100%;
padding: 8px 12px;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 14px;
box-sizing: border-box;
margin-top: 12px;
color: #333;
background: #fff;
}
.ui-dialog.modal-wrapper .mw-input:focus {
outline: none;
border-color: #1976d2;
box-shadow: 0 0 0 2px rgba(25, 118, 210, 0.2);
}
/* ボタン領域 */
.ui-dialog.modal-wrapper .ui-dialog-buttonpane {
border-top: 1px solid #e0e0e0;
padding: 12px 20px;
background: #fafafa;
border-radius: 0 0 8px 8px;
margin-top: 0;
}
/* ボタン共通 */
.ui-dialog.modal-wrapper .ui-dialog-buttonpane button {
border: none;
border-radius: 4px;
padding: 8px 24px;
font-size: 14px;
cursor: pointer;
transition: background 0.2s;
margin-left: 8px;
}
/* プライマリボタン(OK) */
.ui-dialog.modal-wrapper .ui-dialog-buttonpane button.mw-primary {
background: #1976d2;
color: #fff;
}
.ui-dialog.modal-wrapper .ui-dialog-buttonpane button.mw-primary:hover {
background: #1565c0;
}
/* セカンダリボタン(キャンセル) */
.ui-dialog.modal-wrapper .ui-dialog-buttonpane button.mw-secondary {
background: #e0e0e0;
color: #333;
}
.ui-dialog.modal-wrapper .ui-dialog-buttonpane button.mw-secondary:hover {
background: #bdbdbd;
}
/* ── v2テーマ向け(CSSカスタムプロパティで自動切替) ── */
/* v2テーマ(cerulean / green-tea / mandarin / midnight)では
:root にカスタムプロパティが定義されるため以下で上書きされます。
v1テーマではカスタムプロパティが未定義のため
上の固定値がそのまま使われます。 */
.ui-dialog.modal-wrapper {
box-shadow: 0 8px 32px var(--base-shadow, rgba(0, 0, 0, 0.25));
}
.ui-dialog.modal-wrapper .ui-dialog-titlebar {
background: var(--base-bg, #fff);
border-bottom-color: var(--base-border, #e0e0e0);
color: var(--base-text, #333);
}
.ui-dialog.modal-wrapper .ui-dialog-content {
color: var(--base-text, #444);
}
.ui-dialog.modal-wrapper .mw-input {
border-color: var(--base-border, #ccc);
color: var(--base-text, #333);
background: var(--base-bg, #fff);
}
.ui-dialog.modal-wrapper .mw-input:focus {
border-color: var(--primaryColor, #1976d2);
}
.ui-dialog.modal-wrapper .ui-dialog-buttonpane {
border-top-color: var(--base-border, #e0e0e0);
background: var(--base-bg-light, #fafafa);
}
.ui-dialog.modal-wrapper .ui-dialog-buttonpane button.mw-primary {
background: var(--btn-positive-bg, #1976d2);
color: var(--btn-positive-label, #fff);
}
.ui-dialog.modal-wrapper .ui-dialog-buttonpane button.mw-primary:hover {
background: var(--btn-positive-hover, #1565c0);
}
.ui-dialog.modal-wrapper .ui-dialog-buttonpane button.mw-secondary {
background: var(--btn-normal-bg, #e0e0e0);
color: var(--btn-normal-label, #333);
}
.ui-dialog.modal-wrapper .ui-dialog-buttonpane button.mw-secondary:hover {
background: var(--btn-normal-hover, #bdbdbd);
}.modal-wrapperクラスでスコープを限定し、プリザンター標準のダイアログに影響しないようにしています- jQuery UI が自動生成する
.ui-dialog/.ui-dialog-titlebar/.ui-dialog-buttonpaneを上書きしています - v2 テーマ(cerulean / green-tea / mandarin / midnight)では、後半のカスタムプロパティによってテーマの配色に切り替わります
.modal-wrapper-overlayクラスで jQuery UI のオーバーレイ(背景の半透明マスク)をカスタマイズしますz-index: 10000以上にすることで、プリザンター標準の UI 要素より前面に表示されます- 閉じるボタン(×)は非表示にし、ボタン操作で明示的に閉じるようにしています
拡張スクリプト
$(function () {
// ─── $p.modal 名前空間 ─────────────────────────────
// 本体が用意した $p.modal を上書きせず、そこにメソッドを追加する
$p.modal = $p.modal || {};
// ─── 共通:ダイアログ生成 ──────────────────────────
function createDialog(title, $content, buttons, options) {
var dfd = $.Deferred();
var $dlg = $('<div></div>').append($content);
var dialogClass = 'modal-wrapper'
+ (options && options.dialogClass ? ' ' + options.dialogClass : '');
$dlg.dialog($.extend({
title: title,
modal: true,
width: 420,
resizable: false,
closeOnEscape: false,
dialogClass: dialogClass,
buttons: buttons(dfd, $dlg),
create: function () {
// オーバーレイにカスタムクラスを付与
$(this).closest('.ui-dialog')
.prev('.ui-widget-overlay')
.addClass('modal-wrapper-overlay');
},
close: function () {
$(this).dialog('destroy').remove();
if (dfd.state() === 'pending') dfd.reject();
}
}, options || {}));
return dfd.promise();
}
// ─── alert ─────────────────────────────────────────
$p.modal.alert = function (message, title) {
var $content = $('<div class="mw-message"></div>').text(message);
return createDialog(title || 'メッセージ', $content, function (dfd, $dlg) {
return [
{
text: 'OK',
class: 'mw-primary',
click: function () {
dfd.resolve();
$dlg.dialog('close');
}
}
];
});
};
// ─── confirm ───────────────────────────────────────
$p.modal.confirm = function (message, title) {
var $content = $('<div class="mw-message"></div>').text(message);
return createDialog(title || '確認', $content, function (dfd, $dlg) {
return [
{
text: 'キャンセル',
class: 'mw-secondary',
click: function () {
dfd.reject();
$dlg.dialog('close');
}
},
{
text: 'OK',
class: 'mw-primary',
click: function () {
dfd.resolve();
$dlg.dialog('close');
}
}
];
});
};
// ─── prompt ────────────────────────────────────────
$p.modal.prompt = function (message, title, defaultValue) {
var $content = $('<div></div>')
.append($('<div class="mw-message"></div>').text(message))
.append(
$('<input type="text" class="mw-input">')
.val(defaultValue || '')
);
return createDialog(title || '入力', $content, function (dfd, $dlg) {
return [
{
text: 'キャンセル',
class: 'mw-secondary',
click: function () {
dfd.reject();
$dlg.dialog('close');
}
},
{
text: 'OK',
class: 'mw-primary',
click: function () {
dfd.resolve($dlg.find('.mw-input').val());
$dlg.dialog('close');
}
}
];
}, {
open: function () {
var $input = $(this).find('.mw-input');
$input.trigger('focus');
$input.on('keydown', function (e) {
if (e.key === 'Enter') {
$(this).closest('.ui-dialog')
.find('.mw-primary')
.trigger('click');
}
});
}
});
};
// ─── show(汎用) ──────────────────────────────────
$p.modal.show = function (options) {
var opt = $.extend({
title: 'ダイアログ',
content: '',
width: 420,
buttons: []
}, options);
var $content = typeof opt.content === 'string'
? $('<div></div>').html(opt.content)
: opt.content;
return createDialog(opt.title, $content, function (dfd, $dlg) {
if (!opt.buttons.length) {
return [{
text: '閉じる',
class: 'mw-secondary',
click: function () {
dfd.resolve();
$dlg.dialog('close');
}
}];
}
return opt.buttons.map(function (btn) {
return {
text: btn.text,
class: btn.class || 'mw-secondary',
click: function () {
if (btn.action) {
btn.action(dfd, $dlg);
} else {
dfd.resolve(btn.text);
$dlg.dialog('close');
}
}
};
});
}, { width: opt.width, dialogClass: opt.dialogClass || '' });
};
});$p.modal を上書きしない理由
$p.modal は本体が最初から用意しているオブジェクトです。_init.js で modal: {} として定義され(_init.js)、1.5.8.1 のソースでは id 属性を持つ <ui-modal> 要素が画面に追加されると、その要素が $p.modal[id] に登録されます(ui-modal.ts)。$p.modal = {} と代入すると、それまでに登録された要素が失われるため、上のコードでは既存のオブジェクトにメソッドを追加しています。登録は id が未登録のときだけ行われるため、alert などの名前を id に持つ <ui-modal> を置かない限り、追加したメソッドが上書きされることはありません。
共通関数 createDialog
すべてのメソッドの中核となる関数です。
| 引数 | 型 | 説明 |
|---|---|---|
title | string | ダイアログのタイトル |
$content | jQuery | ダイアログ本文の jQuery オブジェクト |
buttons | function | Deferred と $dlg を受け取り、ボタン配列を返す関数 |
options | object | jQuery UI の追加オプション |
$.Deferred()で非同期の結果を返します。ボタンが押されたらresolve(OK)またはreject(キャンセル)を呼び、呼び出し側は.done()/.fail()で後続処理を書けますcloseイベントではdestroyとremoveで DOM をクリーンアップします。Escape キーや×ボタンで閉じた場合もrejectされるため、キャンセル扱いになります
各メソッド
| メソッド | 動作 |
|---|---|
alert | メッセージと OK ボタンだけのダイアログ。OK で done |
confirm | OK とキャンセルの 2 ボタン。OK で done、キャンセルで fail |
prompt | テキスト入力欄付き。OK で入力値が done に渡される。入力欄で Enter キーを押すと OK ボタンが押される |
show | 任意の HTML コンテンツとボタンを設定できる汎用メソッド。ボタンを省略すると「閉じる」ボタンだけが表示される |
show の options には title / content(HTML 文字列または jQuery オブジェクト)/ width / buttons / dialogClass を指定できます。buttons の各要素は text / class / action を持ち、action を省略したボタンはボタンの text で resolve してダイアログを閉じます。
使い方
拡張スクリプトやスクリプトの中で $p.modal を呼び出します。
メッセージを表示する
$p.modal.alert('処理が完了しました。');
// タイトルを指定
$p.modal.alert('入力内容に不備があります。', '入力エラー');確認してから処理を実行する
$p.modal.confirm('この操作は取り消せません。実行しますか?')
.done(function () {
// OK が押された場合の処理
$p.send($('#UpdateButton'));
});fail でキャンセル時の処理も書けます。
$p.modal.confirm('レコードを削除しますか?', '削除確認')
.done(function () {
$p.send($('#DeleteButton'));
})
.fail(function () {
console.log('キャンセルされました');
});テキストを入力してもらう
$p.modal.prompt('コメントを入力してください', 'コメント追加')
.done(function (value) {
// 入力された値を分類項目にセット
$p.set($p.getControl('ClassA'), value);
});第 3 引数で初期値を指定できます。
$p.modal.prompt('理由を入力してください', '却下理由', '内容不備のため')
.done(function (value) {
$p.set($p.getControl('DescriptionA'), value);
$p.send($('#UpdateButton'));
});自由なコンテンツを表示する
$p.modal.show({
title: 'レコード情報',
width: 500,
content:
'<table style="width:100%; border-collapse:collapse;">' +
'<tr><th style="text-align:left; padding:4px;">ID</th>' +
'<td style="padding:4px;">' + $p.getControl('ResultId').val() + '</td></tr>' +
'<tr><th style="text-align:left; padding:4px;">状態</th>' +
'<td style="padding:4px;">' + $p.getControl('Status').val() + '</td></tr>' +
'</table>',
buttons: [
{ text: 'コピー', class: 'mw-primary', action: function (dfd, $dlg) {
navigator.clipboard.writeText($p.getControl('ResultId').val());
dfd.resolve('copied');
$dlg.dialog('close');
}
},
{ text: '閉じる', class: 'mw-secondary' }
]
}).done(function (result) {
if (result === 'copied') {
$p.modal.alert('ID をクリップボードにコピーしました。');
}
});イベントと組み合わせる
$p.events と組み合わせると、ボタンクリック時の確認ダイアログとして使えます。
$p.events.on_editor_load = function () {
// 更新ボタンにハンドラを追加
$('#UpdateButton').on('click', function (e) {
e.preventDefault();
e.stopImmediatePropagation();
$p.modal.confirm('変更内容を保存しますか?', '保存確認')
.done(function () {
// 確認後に送信
$p.send($('#UpdateButton'));
});
return false;
});
};カスタマイズ
ダイアログの幅
alert・confirm・prompt の既定の幅は 420px です。拡張スクリプト内の createDialog 関数の width: 420 を変更するか、show メソッドの width オプションで個別に指定します。
ボタンの色
v1 テーマ向けの固定値を変更します。v2 テーマではテーマの配色が自動で適用されるため、v2 テーマ向けセクションのフォールバック値もあわせて変更します。
/* v1テーマ向け(固定値) */
.ui-dialog.modal-wrapper .ui-dialog-buttonpane button.mw-primary {
- background: #1976d2;
+ background: #2e7d32;
}
.ui-dialog.modal-wrapper .ui-dialog-buttonpane button.mw-primary:hover {
- background: #1565c0;
+ background: #1b5e20;
}
/* v2テーマ向け(CSS変数のフォールバック値もあわせて変更) */
.ui-dialog.modal-wrapper .ui-dialog-buttonpane button.mw-primary {
- background: var(--btn-positive-bg, #1976d2);
+ background: var(--btn-positive-bg, #2e7d32);
}
.ui-dialog.modal-wrapper .ui-dialog-buttonpane button.mw-primary:hover {
- background: var(--btn-positive-hover, #1565c0);
+ background: var(--btn-positive-hover, #1b5e20);
}タイトルバーの背景色
/* v1テーマ向け(固定値) */
.ui-dialog.modal-wrapper .ui-dialog-titlebar {
- background: #fff;
+ background: #e3f2fd;
}
/* v2テーマ向け(CSS変数のフォールバック値もあわせて変更) */
.ui-dialog.modal-wrapper .ui-dialog-titlebar {
- background: var(--base-bg, #fff);
+ background: var(--base-bg, #e3f2fd);
}ブラウザ標準ダイアログとの比較
| 項目 | ブラウザ標準(alert / confirm / prompt) | $p.modal |
|---|---|---|
| 見た目 | ブラウザ依存 | CSS でカスタマイズ可能 |
| 処理の流れ | 同期(ページが止まる) | 非同期(done / fail コールバック) |
| 入力の種類 | テキストのみ | HTML で自由に構成可能 |
| 複数ボタン | 2 種類まで | 自由に追加可能 |
| 外部ライブラリ | 不要 | 不要(jQuery UI を使用) |
| 他のダイアログとの共存 | 非対応(ブロック) | 対応 |