はじめに
プリザンターには v2 テーマとして Cerulean(青)・Green Tea(緑)・Mandarin(橙)・Midnight(紫・ダーク)の 4 種類が用意されています。テーマはプロフィール編集画面から変更できますが、都度設定画面を開く手間があり気軽に試せません。
この記事では、画面左下に FAB(Floating Action Button) を配置し、クリックでメニューを展開して v2 テーマを即座に切り替えられる仕組みを、拡張スクリプトと拡張スタイルだけで実装します。テーマの切替にはプリザンター標準の $p.apiUsersUpdate を使用するため、サーバー側で設定が保存され、ブラウザやデバイスが変わっても設定が維持されます。
バージョン 1.4 以降の v2 テーマ(cerulean・green-tea・mandarin・midnight)を対象にしています。
この機能はユーザーのプロフィール変更が許可されている環境が前提です。パラメータファイル App_Data/Parameters/Service.json の ShowProfiles が true になっていることを確認してください。false の場合、テーマ変更 API が利用できないため FAB メニューは動作しません。
この記事では Api.json の Compatibility_1_3_12 が false(既定値)であることを前提としています。true の環境では $p.api~ 関数での ApiVersion 指定が正しく反映されません。
この機能はユーザーのプロフィール変更が許可されている環境が前提です。パラメータファイル App_Data/Parameters/Service.json の ShowProfiles が true に設定されていることを確認してください。無効の場合、テーマ変更 API が利用できないため FAB メニューは動作しません。パラメータ変更後はプリザンターの再起動が必要です。
仕組みを整理する
プロフィール編集のテーマ変更を調べる
まず、プロフィール編集画面でテーマを変更したときに何が起きているかを確認します。
プリザンターのナビゲーションメニューでは、アカウントメニューに「プロフィールの編集」リンクが表示されます。このリンク先は NavigationMenus.json で次のように定義されています。
{
"MenuId": "AccountMenu_EditProfile",
"Name": "EditProfile",
"Icon": "ui-icon ui-icon-wrench",
"LinkParams": [ "Users", "{UserId}", "Edit" ]
}
{UserId} はサーバー側で context.UserId に置換されるため、実際のリンク先は {ApplicationPath}users/{userId}/edit になります。
プロフィール編集画面には Users_Theme ドロップダウンがあり、テーマを選択して保存すると Users コントローラーの Update アクション が呼ばれます。
つまり、テーマの切替はプリザンター標準のユーザー更新 APIで行われています。FAB メニューからも $p.apiUsersUpdate を呼べば、プロフィール編集画面を開かずにテーマを切り替えられます。
実装のポイント
| 項目 | 内容 |
|---|---|
| 対象テーマ | v2 テーマ(cerulean・green-tea・mandarin・midnight) |
| 切替方式 |
$p.apiUsersUpdate でテーマを変更し、ページリロードで反映 |
| ボタン配置 |
position: fixed で画面左下に固定する FAB |
| ボタン UI | メインボタンをクリックするとテーマ選択ボタンが上方向に展開 |
| アイコン | Material Symbols(palette)+各テーマのカラーチップ |
| 永続化 |
$p.apiUsersUpdate で Users テーブルに保存されるため、別途の保存処理は不要 |
| テーマ判定 |
$p.apiUsersGet でユーザーの現在のテーマを取得 |
テーマの概要
各テーマのカラースキームは次のとおりです。
| テーマ | キー | プライマリカラー | 特徴 |
|---|---|---|---|
| Cerulean | cerulean |
#106ebe |
標準の青系ライトテーマ |
| Green Tea | green-tea |
#058266 |
落ち着いた緑系ライトテーマ |
| Mandarin | mandarin |
#eb9151 |
暖かみのある橙系ライトテーマ |
| Midnight | midnight |
#7a43b1 |
紫系のダークテーマ |
FAB メニューの動作
メインボタンをクリックすると選択肢が展開され、テーマを選ぶとユーザー更新 API が呼ばれてページがリロードされます。
実装してみよう
拡張スタイルと拡張スクリプトの 2 ファイルで構成します。
| 拡張機能 | 役割 |
|---|---|
| 拡張スタイル | FAB メニューの見た目 |
| 拡張スクリプト | FAB メニューの生成・ユーザー更新 API によるテーマ切替 |
FAB メニューのスタイル(拡張スタイル)
拡張スタイルとして App_Data/Parameters/ExtendedStyles/ に配置します。テーマごとの CSS 変数上書きは不要です。テーマの切替はサーバー側で完結し、リロード後にプリザンターが正しいテーマの CSS を自動的に読み込みます。
/* =============================================
FAB メニュー(コンテナ)
============================================= */
.ts-fab {
position: fixed;
left: 24px;
bottom: 24px;
z-index: 900;
display: flex;
flex-direction: column-reverse;
align-items: center;
gap: 8px;
}
/* ── メインボタン ── */
.ts-fab-main {
width: 48px;
height: 48px;
border: none;
border-radius: 50%;
background: var(--primaryColor, #106ebe);
color: var(--invert-text, #fff);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.35);
transition: background 0.2s, transform 0.3s;
padding: 0;
}
.ts-fab-main:hover {
background: var(--primaryDark, #005a9e);
}
.ts-fab.is-open .ts-fab-main {
transform: rotate(45deg);
}
.ts-fab-main .material-symbols-outlined {
font-size: 24px;
}
/* ── 選択肢ボタン ── */
.ts-fab-item {
width: 40px;
height: 40px;
border: 2px solid transparent;
border-radius: 50%;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.25);
padding: 0;
opacity: 0;
transform: scale(0.3) translateY(20px);
pointer-events: none;
transition: opacity 0.25s, transform 0.25s, border-color 0.2s;
}
.ts-fab.is-open .ts-fab-item {
opacity: 1;
transform: scale(1) translateY(0);
pointer-events: auto;
}
.ts-fab.is-open .ts-fab-item:nth-child(2) { transition-delay: 0.03s; }
.ts-fab.is-open .ts-fab-item:nth-child(3) { transition-delay: 0.06s; }
.ts-fab.is-open .ts-fab-item:nth-child(4) { transition-delay: 0.09s; }
.ts-fab.is-open .ts-fab-item:nth-child(5) { transition-delay: 0.12s; }
.ts-fab-item:hover {
opacity: 0.85;
}
.ts-fab-item.is-active {
border-color: var(--nonColor01, #1f1f1f);
box-shadow: 0 0 0 2px var(--nonColor16, #fff), 0 2px 6px rgba(0, 0, 0, 0.35);
}
/* ── ツールチップ(選択肢の左横) ── */
.ts-fab-item::after {
content: attr(data-label);
position: absolute;
left: 52px;
white-space: nowrap;
background: rgba(0, 0, 0, 0.75);
color: #fff;
font-size: 12px;
padding: 4px 10px;
border-radius: 4px;
pointer-events: none;
opacity: 0;
transition: opacity 0.2s;
}
.ts-fab-item:hover::after {
opacity: 1;
}
FAB メニューの生成とテーマ切替(拡張スクリプト)
拡張スクリプトとして App_Data/Parameters/ExtendedScripts/ に配置します。
$(function () {
/* ── テーマ定義 ── */
var themes = [
{ key: 'cerulean', label: 'Cerulean', color: '#106ebe' },
{ key: 'green-tea', label: 'Green Tea', color: '#058266' },
{ key: 'mandarin', label: 'Mandarin', color: '#eb9151' },
{ key: 'midnight', label: 'Midnight', color: '#7a43b1' }
];
/* ── ユーザー情報を取得して現在のテーマを判定 ── */
$p.apiUsersGet({
id: $p.userId(),
data: { ApiVersion: 1.1 },
done: function (data) {
var user = data.Response.Data[0];
var currentKey = user.Theme || 'cerulean';
buildFab(currentKey);
}
});
/* ── FAB メニュー生成 ── */
function buildFab(currentKey) {
var $fab = $('<div>', { class: 'ts-fab' });
// メインボタン
var $main = $('<button>', {
type: 'button',
class: 'ts-fab-main',
title: 'テーマ切替'
}).append(
$('<span>', { class: 'material-symbols-outlined', text: 'palette' })
);
$main.on('click', function (e) {
e.stopPropagation();
$fab.toggleClass('is-open');
});
$fab.append($main);
// 選択肢ボタンを生成
themes.forEach(function (theme) {
var $item = $('<button>', {
type: 'button',
class: 'ts-fab-item' + (theme.key === currentKey ? ' is-active' : ''),
title: theme.label,
'data-key': theme.key,
'data-label': theme.label
}).css('background-color', theme.color);
$item.on('click', function (e) {
e.stopPropagation();
var key = $(this).data('key');
if (key === currentKey) {
$fab.removeClass('is-open');
return;
}
updateTheme(key);
});
$fab.append($item);
});
$('body').append($fab);
// 外側クリックでメニューを閉じる
$(document).on('click', function () {
$fab.removeClass('is-open');
});
$fab.on('click', function (e) {
e.stopPropagation();
});
}
/* ── ユーザー更新 API でテーマを変更 ── */
function updateTheme(key) {
$p.apiUsersUpdate({
id: $p.userId(),
data: {
ApiVersion: 1.1,
Theme: key
},
done: function () {
location.reload();
},
fail: function () {
alert('テーマの変更に失敗しました。');
}
});
}
});
各処理のポイントを見ていきましょう。
ユーザー情報の取得と現在のテーマ判定
$p.apiUsersGet で現在のユーザー情報を取得し、Theme フィールドから現在のテーマを判定します。ユーザー ID は $p.userId() で取得できます。
$p.apiUsersGet({
id: $p.userId(),
data: { ApiVersion: 1.1 },
done: function (data) {
var user = data.Response.Data[0];
var currentKey = user.Theme || 'cerulean';
buildFab(currentKey);
}
});
| 関数 | 説明 |
|---|---|
$p.userId() |
現在ログイン中のユーザー ID を返す |
$p.apiUsersGet |
指定ユーザーの情報を取得する標準関数 |
data.Response.Data[0].Theme |
ユーザーに設定されているテーマキー(未設定の場合は空文字列) |
Theme が空文字列の場合はデフォルトの cerulean として扱います。$p.apiUsersGet の呼び出しに失敗した場合は FAB を生成しないため、プロフィール編集が許可されていない環境では自動的に非表示になります。
FAB メニューの動き
FAB メニューの構造は以下のとおりです。
.ts-fab(コンテナ:column-reverse で下から積む)
├─ .ts-fab-main(メインボタン:常時表示)
├─ .ts-fab-item[data-key="cerulean"] 背景色: #106ebe
├─ .ts-fab-item[data-key="green-tea"] 背景色: #058266
├─ .ts-fab-item[data-key="mandarin"] 背景色: #eb9151
└─ .ts-fab-item[data-key="midnight"] 背景色: #7a43b1
| 操作 | 動作 |
|---|---|
| メインボタンをクリック |
is-open クラスを付け替え、選択肢ボタンが上方向にアニメーション展開 |
| 選択肢ボタンをクリック | API でテーマを更新 → ページリロード |
| 同じテーマをクリック | メニューを閉じるだけ(API 呼び出しなし) |
| 外側をクリック | メニューを閉じる(document の click イベントで制御) |
ユーザー更新 API の呼び出し
テーマの変更には $p.apiUsersUpdate を使います。
$p.apiUsersUpdate({
id: $p.userId(),
data: {
ApiVersion: 1.1,
Theme: key
},
done: function () {
location.reload();
},
fail: function () {
alert('テーマの変更に失敗しました。');
}
});
| パラメータ | 説明 |
|---|---|
id |
$p.userId() で取得した現在のユーザー ID |
data.Theme |
切替先のテーマキー(cerulean・green-tea・mandarin・midnight) |
done |
更新成功時のコールバック |
fail |
更新失敗時のコールバック |
API の成功コールバックで location.reload() を呼び出し、ページを自動的にリロードします。リロード後、プリザンターがサーバー側で新しいテーマの CSS を読み込むため、テーマが正しく反映されます。$p.apiUsersUpdate は内部で認証やリクエストトークンを処理するため、手動での CSRF トークン取得は不要です。
カスタマイズ
ボタンの位置を変える
FAB の位置は left と bottom の値を変更するだけで調整できます。
.ts-fab {
- left: 24px;
- bottom: 24px;
+ right: 24px;
+ bottom: 24px;
}
まとめ
プリザンターの v2 テーマに、テーマ切替 FAB メニューを拡張スタイルと拡張スクリプトだけで追加しました。
- 画面左下の FAB メニューをクリックすると 4 つのテーマ選択ボタンが展開し、各テーマのプライマリカラーがカラーチップとして表示される直感的な UI
-
$p.apiUsersGetで現在のテーマを取得し、$p.apiUsersUpdateでテーマを更新。サーバー側で Users テーブルの Theme 列が更新されるため、localStorageや Sessions API による保存は不要 - API 成功後に
location.reload()で自動リロードし、プリザンターが正しいテーマの CSS を読み込むため、テーマごとの CSS 変数上書きも不要