domma-cms 0.55.1 → 0.66.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +130 -2
- package/README.md +4 -4
- package/admin/css/admin.css +1 -1
- package/admin/dist/domma/domma-tools.css +3 -3
- package/admin/dist/domma/domma-tools.min.js +3 -3
- package/admin/js/api.js +1 -1
- package/admin/js/app.js +3 -3
- package/admin/js/lib/page-picker.js +1 -0
- package/admin/js/lib/plugin-accent.js +1 -0
- package/admin/js/lib/plugin-chrome.js +1 -0
- package/admin/js/lib/shortcode-context-menu.js +2 -2
- package/admin/js/lib/sidebar-grouping.js +1 -1
- package/admin/js/lib/sidebar-grouping.test.js +1 -1
- package/admin/js/lib/sidebar-renderer.js +4 -4
- package/admin/js/lib/slideover-resizable.js +1 -0
- package/admin/js/templates/context-menu-editor.html +212 -0
- package/admin/js/templates/context-menus.html +16 -0
- package/admin/js/templates/plugin-code.html +1 -1
- package/{plugins/site-search/admin/templates/site-search.html → admin/js/templates/search.html} +50 -4
- package/admin/js/templates/settings.html +16 -97
- package/admin/js/templates/theme.html +173 -0
- package/admin/js/views/context-menu-editor.js +55 -0
- package/admin/js/views/context-menus.js +5 -0
- package/admin/js/views/form-editor.js +7 -7
- package/admin/js/views/index.js +1 -1
- package/admin/js/views/plugin-marketplace.js +1 -1
- package/admin/js/views/plugins.js +25 -23
- package/admin/js/views/search.js +1 -0
- package/admin/js/views/settings.js +3 -3
- package/admin/js/views/theme.js +1 -0
- package/bin/cli.js +2 -0
- package/bin/update.js +13 -2
- package/config/menus/admin-sidebar.json +129 -23
- package/config/plugins.json +5 -5
- package/config/search.json +13 -0
- package/config/theme.json +18 -0
- package/package.json +12 -4
- package/plugins/_lib/admin/mail/compose-window.js +914 -0
- package/plugins/_lib/admin/mail/contacts.js +301 -0
- package/plugins/_lib/admin/mail/diagnostics-section.js +133 -0
- package/plugins/_lib/admin/mail/folder-tree.js +254 -0
- package/plugins/_lib/admin/mail/identity.js +480 -0
- package/plugins/_lib/admin/mail/image-senders-section.js +131 -0
- package/plugins/_lib/admin/mail/keyboard.js +136 -0
- package/plugins/_lib/admin/mail/mail.css +1 -0
- package/plugins/_lib/admin/mail/mail.html +71 -0
- package/plugins/_lib/admin/mail/panes.js +253 -0
- package/plugins/_lib/admin/mail/reader-view.js +4453 -0
- package/plugins/_lib/admin/mail/resizable.js +26 -0
- package/plugins/_lib/admin/mail/rules.js +343 -0
- package/plugins/_lib/admin/mail/scheduling.js +203 -0
- package/plugins/_lib/admin/mail/section-kit.js +277 -0
- package/plugins/_lib/admin/mail/templates.js +238 -0
- package/plugins/_lib/admin/mail/threads.js +200 -0
- package/plugins/_lib/admin/mail/vacation.js +269 -0
- package/plugins/_lib/admin/ui/help.css +1 -0
- package/plugins/_lib/admin/ui/help.js +174 -0
- package/plugins/_lib/admin/ui/resizable.js +151 -0
- package/plugins/_lib/dataStore.js +117 -0
- package/plugins/_lib/mail/accounts.js +919 -0
- package/plugins/_lib/mail/bodyTokens.js +101 -0
- package/plugins/_lib/mail/compose.js +256 -0
- package/plugins/_lib/mail/defaults.js +57 -0
- package/plugins/_lib/mail/diagnostics.js +154 -0
- package/plugins/_lib/mail/envelope.js +161 -0
- package/plugins/_lib/mail/folders.js +192 -0
- package/plugins/_lib/mail/handoff.js +274 -0
- package/plugins/_lib/mail/imapPool.js +293 -0
- package/plugins/_lib/mail/mbox.js +74 -0
- package/plugins/_lib/mail/pollSchedule.js +79 -0
- package/plugins/_lib/mail/poller.js +135 -0
- package/plugins/_lib/mail/priority.js +138 -0
- package/plugins/_lib/mail/readRoutes.js +680 -0
- package/plugins/_lib/mail/render.js +291 -0
- package/plugins/_lib/mail/ruleRunner.js +152 -0
- package/plugins/_lib/mail/scheduler.js +254 -0
- package/plugins/_lib/mail/secretbox.js +229 -0
- package/plugins/_lib/mail/send.js +396 -0
- package/plugins/_lib/mail/store.js +1002 -0
- package/plugins/_lib/mail/sync.js +292 -0
- package/plugins/_lib/mail/syncPlan.js +126 -0
- package/plugins/_lib/mail/syncSelection.js +82 -0
- package/plugins/_lib/mail/unsubscribe.js +183 -0
- package/plugins/_lib/mail/vacationRunner.js +114 -0
- package/plugins/_lib/mail/write.js +473 -0
- package/plugins/_lib/schemaSync.js +83 -0
- package/plugins/_template/admin/css/index.css +0 -0
- package/plugins/_template/admin/templates/index.html +4 -4
- package/plugins/_template/admin/views/index.js +7 -0
- package/plugins/analytics/admin/css/index.css +1 -0
- package/plugins/analytics/admin/templates/analytics.html +22 -13
- package/plugins/analytics/plugin.json +3 -0
- package/plugins/blog/admin/css/index.css +1 -0
- package/plugins/blog/admin/templates/blog.html +30 -18
- package/plugins/blog/admin/templates/categories.html +2 -2
- package/plugins/blog/admin/templates/comments.html +2 -2
- package/plugins/blog/admin/templates/post-editor.html +34 -34
- package/plugins/blog/admin/templates/settings.html +6 -3
- package/plugins/blog/admin/views/blog.js +8 -5
- package/plugins/blog/admin/views/categories.js +5 -10
- package/plugins/blog/admin/views/comments.js +5 -5
- package/plugins/blog/admin/views/post-editor.js +39 -20
- package/plugins/blog/admin/views/settings.js +52 -50
- package/plugins/blog/collections/categories/schema.json +7 -6
- package/plugins/blog/collections/comments/schema.json +11 -10
- package/plugins/blog/collections/posts/schema.json +14 -13
- package/plugins/blog/plugin.js +36 -13
- package/plugins/blog/plugin.json +13 -5
- package/plugins/blog/plugin.public.js +312 -0
- package/plugins/contacts/admin/css/index.css +1 -0
- package/plugins/contacts/admin/templates/contacts.html +128 -0
- package/plugins/contacts/admin/views/contacts.js +237 -4
- package/plugins/contacts/collections/user-contacts/schema.json +108 -0
- package/plugins/contacts/plugin.js +214 -27
- package/plugins/contacts/plugin.json +4 -1
- package/plugins/invoice/admin/css/index.css +1 -0
- package/plugins/invoice/admin/templates/editor.html +140 -49
- package/plugins/invoice/admin/templates/index.html +153 -23
- package/plugins/invoice/admin/templates/issuers.html +2 -5
- package/plugins/invoice/admin/templates/receivers.html +2 -5
- package/plugins/invoice/admin/views/contacts-source.js +266 -0
- package/plugins/invoice/admin/views/editor.js +366 -199
- package/plugins/invoice/admin/views/export.js +199 -0
- package/plugins/invoice/admin/views/help-content.js +61 -0
- package/plugins/invoice/admin/views/index.js +582 -94
- package/plugins/invoice/admin/views/issuers.js +24 -17
- package/plugins/invoice/admin/views/media.js +172 -0
- package/plugins/invoice/admin/views/party-view.js +305 -67
- package/plugins/invoice/admin/views/payments.js +127 -0
- package/plugins/invoice/admin/views/print.js +130 -0
- package/plugins/invoice/admin/views/receivers.js +49 -16
- package/plugins/invoice/admin/views/send.js +212 -0
- package/plugins/invoice/admin/views/settings.js +594 -0
- package/plugins/invoice/admin/views/view-lifecycle.js +33 -0
- package/plugins/invoice/collections/invoice-issuers/schema.json +77 -11
- package/plugins/invoice/collections/invoice-receivers/schema.json +10 -9
- package/plugins/invoice/collections/invoices/schema.json +19 -13
- package/plugins/invoice/config.js +27 -6
- package/plugins/invoice/pdf.js +164 -0
- package/plugins/invoice/plugin.js +1217 -44
- package/plugins/invoice/plugin.json +10 -9
- package/plugins/invoice/templates/_base.css +1 -0
- package/plugins/invoice/templates/classic-nologo.html +100 -0
- package/plugins/invoice/templates/classic.html +91 -0
- package/plugins/invoice/templates/invoice-print.html +24 -0
- package/plugins/invoice/templates/minimal.html +99 -0
- package/plugins/invoice/templates/modern-nologo.html +114 -0
- package/plugins/invoice/templates/modern.html +113 -0
- package/plugins/invoice/templates/templates.json +11 -0
- package/plugins/mail-reader/admin/views/mail.js +19 -0
- package/plugins/mail-reader/config.js +7 -0
- package/plugins/mail-reader/plugin.js +48 -0
- package/plugins/mail-reader/plugin.json +33 -0
- package/plugins/notes/admin/views/notes.js +1 -1
- package/plugins/notes/plugin.json +2 -2
- package/plugins/surveys/lib/audience.js +37 -0
- package/plugins/surveys/lib/campaigns.js +43 -0
- package/plugins/surveys/lib/ledger.js +110 -0
- package/plugins/surveys/lib/sending.js +106 -0
- package/plugins/surveys/lib/stats.js +62 -0
- package/plugins/surveys/lib/submit.js +95 -0
- package/plugins/surveys/lib/tokens.js +28 -0
- package/plugins/surveys/plugin.public.js +149 -0
- package/plugins/theme-switcher/admin/templates/theme-switcher.html +1 -1
- package/public/css/forms.css +1 -1
- package/public/css/search.css +1 -0
- package/public/css/site.css +1 -1
- package/public/js/collection-context.js +2 -2
- package/public/js/context-menus.js +1 -0
- package/public/js/form-logic-engine.js +1 -1
- package/public/js/forms.js +2 -2
- package/public/js/search.js +1 -0
- package/public/js/site.js +1 -1
- package/scripts/build.js +37 -3
- package/scripts/copy-domma.js +48 -0
- package/scripts/seed.js +1996 -0
- package/server/routes/api/collections.js +34 -0
- package/server/routes/api/context-menus.js +104 -0
- package/server/routes/api/forms.js +42 -3
- package/server/routes/api/notifications.js +69 -19
- package/server/routes/api/plugins.js +50 -6
- package/server/routes/api/search.js +43 -0
- package/server/routes/api/theme.js +69 -0
- package/server/routes/public.js +42 -7
- package/server/server.js +74 -0
- package/server/services/adapters/FileAdapter.js +6 -1
- package/server/services/content.js +26 -0
- package/server/services/contextMenus.js +477 -0
- package/server/services/markdown.js +70 -9
- package/server/services/permissionRegistry.js +24 -0
- package/server/services/pluginFiles.js +52 -11
- package/server/services/plugins.js +229 -6
- package/server/services/renderer.js +144 -22
- package/server/services/roles.js +1 -1
- package/server/services/search-migration.js +82 -0
- package/server/services/search.js +413 -0
- package/server/services/sidebar-migration.js +1 -0
- package/server/services/themeSettings.js +541 -0
- package/server/services/users.js +8 -0
- package/server/templates/page.html +4 -2
- package/plugins/contacts/data/contacts.json +0 -20
- package/plugins/notes/data/notes.json +0 -1
- package/plugins/site-search/admin/views/site-search.js +0 -116
- package/plugins/site-search/config.js +0 -15
- package/plugins/site-search/plugin.js +0 -188
- package/plugins/site-search/plugin.json +0 -40
- package/plugins/site-search/public/inject-body.html +0 -17
- package/plugins/site-search/public/inject-head.html +0 -1
- package/plugins/site-search/public/search.css +0 -1
- package/plugins/site-search/public/search.js +0 -1
- package/plugins/todo/data/todos.json +0 -1
package/{plugins/site-search/admin/templates/site-search.html → admin/js/templates/search.html}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<div class="view-header">
|
|
2
|
-
<h1><span data-icon="search"></span>
|
|
2
|
+
<h1><span data-icon="search"></span> Search</h1>
|
|
3
3
|
<div>
|
|
4
4
|
<button id="save-settings-btn" class="btn btn-primary">
|
|
5
5
|
<span data-icon="save"></span> Save
|
|
@@ -20,6 +20,16 @@
|
|
|
20
20
|
<div class="card mb-4" style="margin-top:1rem;">
|
|
21
21
|
<div class="card-body">
|
|
22
22
|
|
|
23
|
+
<div class="row mb-3">
|
|
24
|
+
<div class="col">
|
|
25
|
+
<label class="form-check-label">
|
|
26
|
+
<input id="field-enabled" type="checkbox">
|
|
27
|
+
Enable search on the public site
|
|
28
|
+
</label>
|
|
29
|
+
<span class="form-hint">When off, no search trigger, stylesheet or script is added to public pages.</span>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
23
33
|
<div class="row mb-3">
|
|
24
34
|
<div class="col">
|
|
25
35
|
<label class="form-label">Search placeholder text</label>
|
|
@@ -100,6 +110,41 @@
|
|
|
100
110
|
</div>
|
|
101
111
|
</div>
|
|
102
112
|
|
|
113
|
+
<div class="row mb-3" id="ss-size-row">
|
|
114
|
+
<div class="col">
|
|
115
|
+
<label class="form-label" for="field-icon-size">Icon size</label>
|
|
116
|
+
<div style="display:flex;align-items:center;gap:1rem;flex-wrap:wrap;">
|
|
117
|
+
<input id="field-icon-size" type="range" min="32" max="96" step="2" value="52"
|
|
118
|
+
style="flex:1 1 200px;max-width:260px;">
|
|
119
|
+
<span id="ss-size-value" class="text-sm font-mono" style="min-width:3.5rem;">52px</span>
|
|
120
|
+
</div>
|
|
121
|
+
<span class="form-hint">How big the floating button is, in pixels (32-96). The search glyph inside
|
|
122
|
+
scales with it. 44px and up is the comfortable range for tapping on a phone.</span>
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
|
|
126
|
+
<div class="row mb-3" id="ss-radius-row">
|
|
127
|
+
<div class="col">
|
|
128
|
+
<label class="form-label" for="field-icon-radius">Icon corner radius</label>
|
|
129
|
+
<div style="display:flex;align-items:center;gap:1rem;flex-wrap:wrap;">
|
|
130
|
+
<input id="field-icon-radius" type="range" min="0" max="26" step="1" value="26"
|
|
131
|
+
style="flex:1 1 200px;max-width:260px;">
|
|
132
|
+
<span id="ss-radius-value" class="text-sm font-mono" style="min-width:3.5rem;">26px</span>
|
|
133
|
+
<span id="ss-radius-preview" aria-hidden="true"
|
|
134
|
+
style="width:52px;height:52px;display:inline-flex;align-items:center;justify-content:center;
|
|
135
|
+
background:var(--dm-surface);color:var(--dm-text);
|
|
136
|
+
border:1px solid var(--dm-border);border-radius:26px;
|
|
137
|
+
box-shadow:0 4px 16px rgba(0,0,0,.14);flex:0 0 auto;transition:border-radius .12s;">
|
|
138
|
+
<span data-icon="search" style="width:20px;height:20px;"></span>
|
|
139
|
+
</span>
|
|
140
|
+
</div>
|
|
141
|
+
<span class="form-hint">0 is a square, <span id="ss-radius-max-hint">26</span> a circle - the top of the
|
|
142
|
+
range is always half the icon size, so it moves when you change the size above. The preview is the real
|
|
143
|
+
button at its real size. Applies to the floating button in icon mode only - the navbar icon has no
|
|
144
|
+
surface to round, and the labelled variant is a pill by definition.</span>
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
147
|
+
|
|
103
148
|
<div class="row" style="align-items:flex-start;gap:0;">
|
|
104
149
|
|
|
105
150
|
<div class="col-auto" style="margin-right:1.5rem;margin-bottom:1rem;">
|
|
@@ -175,14 +220,15 @@
|
|
|
175
220
|
<div class="tab-panel" id="tab-how-it-works">
|
|
176
221
|
<div class="card mb-4" style="margin-top:1rem;">
|
|
177
222
|
<div class="card-body">
|
|
178
|
-
<p class="text-muted mb-2">
|
|
179
|
-
it or use
|
|
180
|
-
|
|
223
|
+
<p class="text-muted mb-2">Search adds a search icon to the public navbar, or a floating button
|
|
224
|
+
if you prefer. Visitors can click it or use the keyboard shortcut to open a full-screen overlay
|
|
225
|
+
with live results.</p>
|
|
181
226
|
<ul class="text-muted" style="padding-left:1.25rem;line-height:1.8;">
|
|
182
227
|
<li>Results are weighted: title matches score highest, followed by tags, description, and page
|
|
183
228
|
content.
|
|
184
229
|
</li>
|
|
185
230
|
<li>Draft pages and private pages are excluded from results.</li>
|
|
231
|
+
<li>An authored context menu can offer "Site search" as an action, which opens the same overlay.</li>
|
|
186
232
|
<li>Use <kbd>↑</kbd> <kbd>↓</kbd> to navigate results, <kbd>↵</kbd> to open, <kbd>Esc</kbd> to
|
|
187
233
|
close.
|
|
188
234
|
</li>
|
|
@@ -30,101 +30,20 @@
|
|
|
30
30
|
</div>
|
|
31
31
|
</div>
|
|
32
32
|
<div class="row mb-3">
|
|
33
|
-
<div class="col
|
|
34
|
-
<
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
<
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
<
|
|
45
|
-
<option value="Raleway">Raleway</option>
|
|
46
|
-
<option value="DM Sans">DM Sans</option>
|
|
47
|
-
<option value="Work Sans">Work Sans</option>
|
|
48
|
-
</optgroup>
|
|
49
|
-
<optgroup label="Serif">
|
|
50
|
-
<option value="Merriweather">Merriweather</option>
|
|
51
|
-
<option value="Playfair Display">Playfair Display</option>
|
|
52
|
-
<option value="Lora">Lora</option>
|
|
53
|
-
<option value="Roboto Slab">Roboto Slab</option>
|
|
54
|
-
</optgroup>
|
|
55
|
-
<optgroup label="Dyslexia-friendly">
|
|
56
|
-
<option value="Lexend">Lexend</option>
|
|
57
|
-
<option value="Atkinson Hyperlegible">Atkinson Hyperlegible</option>
|
|
58
|
-
<option value="Andika">Andika</option>
|
|
59
|
-
</optgroup>
|
|
60
|
-
<optgroup label="System">
|
|
61
|
-
<option value="system-ui">System UI (no web font)</option>
|
|
62
|
-
</optgroup>
|
|
63
|
-
</select>
|
|
64
|
-
<p class="form-hint text-muted" style="margin-top:0.4rem;font-size:0.8rem;">Applied to the public-facing
|
|
65
|
-
website body text. The dyslexia-friendly group uses wider letterforms and spacing to reduce character
|
|
66
|
-
confusion.</p>
|
|
67
|
-
</div>
|
|
68
|
-
<div class="col-3">
|
|
69
|
-
<label class="form-label">Font Size</label>
|
|
70
|
-
<select id="field-font-size" class="form-select">
|
|
71
|
-
<option value="13">13px - Compact</option>
|
|
72
|
-
<option value="14">14px - Small</option>
|
|
73
|
-
<option value="15">15px - Slightly small</option>
|
|
74
|
-
<option value="16">16px - Default</option>
|
|
75
|
-
<option value="17">17px - Slightly large</option>
|
|
76
|
-
<option value="18">18px - Large</option>
|
|
77
|
-
<option value="20">20px - Extra large</option>
|
|
78
|
-
</select>
|
|
79
|
-
<p class="form-hint text-muted" style="margin-top:0.4rem;font-size:0.8rem;">Root font size. All rem/em values
|
|
80
|
-
scale with this.</p>
|
|
81
|
-
</div>
|
|
82
|
-
</div>
|
|
83
|
-
<div class="row">
|
|
84
|
-
<div class="col-6">
|
|
85
|
-
<label class="form-label">Public Site Theme</label>
|
|
86
|
-
<select id="field-theme" class="form-select"></select>
|
|
87
|
-
<p class="form-hint text-muted" style="margin-top:0.4rem;font-size:0.8rem;">Applied to the public-facing
|
|
88
|
-
website.</p>
|
|
89
|
-
</div>
|
|
90
|
-
<div class="col-6">
|
|
91
|
-
<label class="form-label">Admin Theme</label>
|
|
92
|
-
<select id="field-admin-theme" class="form-select"></select>
|
|
93
|
-
<p class="form-hint text-muted" style="margin-top:0.4rem;font-size:0.8rem;">Applied to the admin panel.</p>
|
|
94
|
-
</div>
|
|
95
|
-
</div>
|
|
96
|
-
<!-- Auto day/night theme -->
|
|
97
|
-
<div class="row mt-3">
|
|
98
|
-
<div class="col">
|
|
99
|
-
<label class="form-check-label" style="display:flex;align-items:center;gap:.5rem;cursor:pointer;">
|
|
100
|
-
<input id="field-auto-theme-enabled" type="checkbox" class="form-check"> Enable automatic day/night
|
|
101
|
-
theme switching
|
|
102
|
-
</label>
|
|
103
|
-
<p id="auto-theme-roller-hint" class="form-hint text-muted"
|
|
104
|
-
style="display:none;margin-top:.35rem;font-size:.8rem;">Custom themes (Theme Roller) do not support
|
|
105
|
-
auto-switching.</p>
|
|
106
|
-
</div>
|
|
107
|
-
</div>
|
|
108
|
-
<div id="auto-theme-fields" class="row mt-3" style="display:none;">
|
|
109
|
-
<div class="col-6">
|
|
110
|
-
<label class="form-label">Day Theme</label>
|
|
111
|
-
<select id="field-day-theme" class="form-select"></select>
|
|
112
|
-
</div>
|
|
113
|
-
<div class="col-6">
|
|
114
|
-
<label class="form-label">Night Theme</label>
|
|
115
|
-
<select id="field-night-theme" class="form-select"></select>
|
|
116
|
-
</div>
|
|
117
|
-
<div class="col-3 mt-3">
|
|
118
|
-
<label class="form-label">Day Starts At</label>
|
|
119
|
-
<input id="field-day-start" type="time" class="form-input" value="07:00">
|
|
120
|
-
</div>
|
|
121
|
-
<div class="col-3 mt-3">
|
|
122
|
-
<label class="form-label">Night Starts At</label>
|
|
123
|
-
<input id="field-night-start" type="time" class="form-input" value="19:00">
|
|
33
|
+
<div class="col">
|
|
34
|
+
<div class="card" style="background:var(--dm-surface-raised);">
|
|
35
|
+
<div class="card-body" style="display:flex;align-items:center;gap:1rem;flex-wrap:wrap;">
|
|
36
|
+
<span data-icon="droplet"></span>
|
|
37
|
+
<div style="flex:1 1 240px;">
|
|
38
|
+
<strong>Themes, fonts and element overrides moved.</strong>
|
|
39
|
+
<p class="text-muted" style="margin:.25rem 0 0;font-size:.85rem;">
|
|
40
|
+
The site theme, admin theme, automatic day/night pair and typography now live in
|
|
41
|
+
Content › Theme, alongside per-element theme overrides.
|
|
42
|
+
</p>
|
|
43
|
+
</div>
|
|
44
|
+
<a href="#/theme" class="btn btn-ghost btn-sm">Open Theme</a>
|
|
124
45
|
</div>
|
|
125
|
-
|
|
126
|
-
<p class="form-hint text-muted" style="font-size:.8rem;">Themes switch based on each visitor's local
|
|
127
|
-
time. Any theme can be used - they need not be light/dark pairs.</p>
|
|
46
|
+
</div>
|
|
128
47
|
</div>
|
|
129
48
|
</div>
|
|
130
49
|
<div class="row mt-3">
|
|
@@ -448,7 +367,7 @@
|
|
|
448
367
|
</div>
|
|
449
368
|
|
|
450
369
|
<div class="row mb-4" style="align-items:flex-end;gap:0;">
|
|
451
|
-
<div class="col-
|
|
370
|
+
<div class="col-4">
|
|
452
371
|
<label class="form-label">Home label</label>
|
|
453
372
|
<input id="field-breadcrumbs-home-label" type="text" class="form-input" placeholder="Home">
|
|
454
373
|
</div>
|
|
@@ -490,7 +409,7 @@
|
|
|
490
409
|
</div>
|
|
491
410
|
|
|
492
411
|
<!-- Offsets -->
|
|
493
|
-
<div class="col-
|
|
412
|
+
<div class="col-3" style="margin-right:1rem;">
|
|
494
413
|
<label class="form-label" for="field-bc-offset-x">Horizontal offset</label>
|
|
495
414
|
<div style="display:flex;align-items:center;gap:.4rem;">
|
|
496
415
|
<input id="field-bc-offset-x" type="number" class="form-input" min="0" max="500" placeholder="8"
|
|
@@ -501,7 +420,7 @@
|
|
|
501
420
|
right edge.</p>
|
|
502
421
|
</div>
|
|
503
422
|
|
|
504
|
-
<div class="col-
|
|
423
|
+
<div class="col-3">
|
|
505
424
|
<label class="form-label" for="field-bc-offset-y">Vertical offset</label>
|
|
506
425
|
<div style="display:flex;align-items:center;gap:.4rem;">
|
|
507
426
|
<input id="field-bc-offset-y" type="number" class="form-input" min="0" max="500" placeholder="60"
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
<div class="page-header">
|
|
2
|
+
<h1 id="theme-title"><span data-icon="droplet"></span> Theme</h1>
|
|
3
|
+
<div style="display:flex;gap:.5rem;align-items:center;">
|
|
4
|
+
<a href="/" target="_blank" rel="noopener" class="btn btn-ghost btn-sm">
|
|
5
|
+
<span data-icon="external-link"></span> View site
|
|
6
|
+
</a>
|
|
7
|
+
<button id="theme-save-btn" class="btn btn-primary"><span data-icon="save"></span> Save</button>
|
|
8
|
+
</div>
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
<div id="theme-errors" class="alert alert-danger mb-3" style="display:none;"></div>
|
|
12
|
+
|
|
13
|
+
<div class="tabs" id="theme-tabs">
|
|
14
|
+
<div class="tab-list">
|
|
15
|
+
<button class="tab-item active">Themes</button>
|
|
16
|
+
<button class="tab-item">Element Overrides</button>
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
<!-- ------------------------------------------------------------------ -->
|
|
20
|
+
<!-- Themes -->
|
|
21
|
+
<!-- ------------------------------------------------------------------ -->
|
|
22
|
+
<div class="tab-panel active">
|
|
23
|
+
<div class="card mb-4">
|
|
24
|
+
<div class="card-header"><h2>Site & Admin</h2></div>
|
|
25
|
+
<div class="card-body">
|
|
26
|
+
<div class="row mb-3">
|
|
27
|
+
<div class="col-6">
|
|
28
|
+
<label class="form-label">Front-end Theme</label>
|
|
29
|
+
<select id="theme-frontend" class="form-select"></select>
|
|
30
|
+
<p class="form-hint text-muted" style="margin-top:.4rem;font-size:.8rem;">
|
|
31
|
+
Applied to every public page.</p>
|
|
32
|
+
</div>
|
|
33
|
+
<div class="col-6">
|
|
34
|
+
<label class="form-label">Admin Theme</label>
|
|
35
|
+
<select id="theme-admin" class="form-select"></select>
|
|
36
|
+
<p class="form-hint text-muted" style="margin-top:.4rem;font-size:.8rem;">
|
|
37
|
+
Applied to this admin panel. Takes effect as soon as you save.</p>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
<div class="row">
|
|
42
|
+
<div class="col">
|
|
43
|
+
<label class="form-check-label" style="display:flex;align-items:center;gap:.5rem;cursor:pointer;">
|
|
44
|
+
<input id="theme-auto-enabled" type="checkbox" class="form-check">
|
|
45
|
+
Switch automatically between a day and a night theme
|
|
46
|
+
</label>
|
|
47
|
+
<p id="theme-auto-roller-hint" class="form-hint text-muted"
|
|
48
|
+
style="display:none;margin-top:.35rem;font-size:.8rem;">
|
|
49
|
+
Custom themes (Theme Roller) do not support auto-switching.</p>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
<div id="theme-auto-fields" class="row mt-3" style="display:none;">
|
|
53
|
+
<div class="col-6">
|
|
54
|
+
<label class="form-label">Day Theme</label>
|
|
55
|
+
<select id="theme-day" class="form-select"></select>
|
|
56
|
+
</div>
|
|
57
|
+
<div class="col-6">
|
|
58
|
+
<label class="form-label">Night Theme</label>
|
|
59
|
+
<select id="theme-night" class="form-select"></select>
|
|
60
|
+
</div>
|
|
61
|
+
<div class="col-3 mt-3">
|
|
62
|
+
<label class="form-label">Day Starts At</label>
|
|
63
|
+
<input id="theme-day-start" type="time" class="form-input" value="07:00">
|
|
64
|
+
</div>
|
|
65
|
+
<div class="col-3 mt-3">
|
|
66
|
+
<label class="form-label">Night Starts At</label>
|
|
67
|
+
<input id="theme-night-start" type="time" class="form-input" value="19:00">
|
|
68
|
+
</div>
|
|
69
|
+
<div class="col-12 mt-2">
|
|
70
|
+
<p class="form-hint text-muted" style="font-size:.8rem;">Themes switch on each visitor's local
|
|
71
|
+
time. They need not be a light/dark pair.</p>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
<div class="card">
|
|
78
|
+
<div class="card-header"><h2>Typography</h2></div>
|
|
79
|
+
<div class="card-body">
|
|
80
|
+
<div class="row">
|
|
81
|
+
<div class="col-6">
|
|
82
|
+
<label class="form-label">Font Family</label>
|
|
83
|
+
<select id="theme-font-family" class="form-select">
|
|
84
|
+
<optgroup label="Sans-serif">
|
|
85
|
+
<option value="Roboto">Roboto (default)</option>
|
|
86
|
+
<option value="Inter">Inter</option>
|
|
87
|
+
<option value="Open Sans">Open Sans</option>
|
|
88
|
+
<option value="Lato">Lato</option>
|
|
89
|
+
<option value="Nunito">Nunito</option>
|
|
90
|
+
<option value="Poppins">Poppins</option>
|
|
91
|
+
<option value="Source Sans 3">Source Sans 3</option>
|
|
92
|
+
<option value="Montserrat">Montserrat</option>
|
|
93
|
+
<option value="Raleway">Raleway</option>
|
|
94
|
+
<option value="DM Sans">DM Sans</option>
|
|
95
|
+
<option value="Work Sans">Work Sans</option>
|
|
96
|
+
</optgroup>
|
|
97
|
+
<optgroup label="Serif">
|
|
98
|
+
<option value="Merriweather">Merriweather</option>
|
|
99
|
+
<option value="Playfair Display">Playfair Display</option>
|
|
100
|
+
<option value="Lora">Lora</option>
|
|
101
|
+
<option value="Roboto Slab">Roboto Slab</option>
|
|
102
|
+
</optgroup>
|
|
103
|
+
<optgroup label="Dyslexia-friendly">
|
|
104
|
+
<option value="Lexend">Lexend</option>
|
|
105
|
+
<option value="Atkinson Hyperlegible">Atkinson Hyperlegible</option>
|
|
106
|
+
<option value="Andika">Andika</option>
|
|
107
|
+
</optgroup>
|
|
108
|
+
<optgroup label="System">
|
|
109
|
+
<option value="system-ui">System UI (no web font)</option>
|
|
110
|
+
</optgroup>
|
|
111
|
+
</select>
|
|
112
|
+
<p class="form-hint text-muted" style="margin-top:.4rem;font-size:.8rem;">Public site body text.
|
|
113
|
+
The dyslexia-friendly group uses wider letterforms and spacing.</p>
|
|
114
|
+
</div>
|
|
115
|
+
<div class="col-3">
|
|
116
|
+
<label class="form-label">Font Size</label>
|
|
117
|
+
<select id="theme-font-size" class="form-select">
|
|
118
|
+
<option value="13">13px - Compact</option>
|
|
119
|
+
<option value="14">14px - Small</option>
|
|
120
|
+
<option value="15">15px - Slightly small</option>
|
|
121
|
+
<option value="16">16px - Default</option>
|
|
122
|
+
<option value="17">17px - Slightly large</option>
|
|
123
|
+
<option value="18">18px - Large</option>
|
|
124
|
+
<option value="20">20px - Extra large</option>
|
|
125
|
+
</select>
|
|
126
|
+
<p class="form-hint text-muted" style="margin-top:.4rem;font-size:.8rem;">Root font size. Every
|
|
127
|
+
rem value scales with it.</p>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
|
|
134
|
+
<!-- ------------------------------------------------------------------ -->
|
|
135
|
+
<!-- Element overrides -->
|
|
136
|
+
<!-- ------------------------------------------------------------------ -->
|
|
137
|
+
<div class="tab-panel">
|
|
138
|
+
<div class="card mb-4">
|
|
139
|
+
<div class="card-body">
|
|
140
|
+
<p class="text-muted" style="margin:0 0 .75rem;">
|
|
141
|
+
An override re-themes one region of the page. Give it a CSS selector, then either adopt another
|
|
142
|
+
theme wholesale or set individual design tokens. Everything inside that region follows - including
|
|
143
|
+
components rendered later - because a Domma theme is nothing but a block of
|
|
144
|
+
<code>--dm-*</code> values.
|
|
145
|
+
</p>
|
|
146
|
+
<button id="theme-add-override" class="btn btn-ghost btn-sm" type="button">
|
|
147
|
+
<span data-icon="plus"></span> Add override
|
|
148
|
+
</button>
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
|
|
152
|
+
<div id="theme-overrides"></div>
|
|
153
|
+
<p id="theme-overrides-empty" class="text-muted" style="text-align:center;padding:2rem 0;">
|
|
154
|
+
No overrides yet. The whole site uses the front-end theme.
|
|
155
|
+
</p>
|
|
156
|
+
|
|
157
|
+
<div class="card mt-4">
|
|
158
|
+
<div class="card-header">
|
|
159
|
+
<h2>Generated CSS</h2>
|
|
160
|
+
<button id="theme-refresh-css" class="btn btn-ghost btn-sm" type="button">
|
|
161
|
+
<span data-icon="refresh-cw"></span> Refresh
|
|
162
|
+
</button>
|
|
163
|
+
</div>
|
|
164
|
+
<div class="card-body">
|
|
165
|
+
<p class="text-muted" style="margin-top:0;font-size:.85rem;">
|
|
166
|
+
Exactly what is injected into every public page, ahead of your custom CSS.
|
|
167
|
+
</p>
|
|
168
|
+
<pre id="theme-css-preview" class="p-3"
|
|
169
|
+
style="max-height:320px;overflow:auto;background:var(--dm-surface-raised);border-radius:var(--dm-radius-md);font-size:.8rem;margin:0;">(nothing yet)</pre>
|
|
170
|
+
</div>
|
|
171
|
+
</div>
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import{api as v}from"../api.js";import{makeIconInput as B}from"/admin/js/lib/shortcode-modal.js";import{makePageInput as L}from"/admin/js/lib/page-picker.js";const C=[{label:"This page",actions:{navigate:"Go to a URL",reload:"Reload the page",back:"Back",forward:"Forward",print:"Print","copy-link":"Copy link to this view","site-search":"Open site search"}},{label:"Clipboard and selection",actions:{copy:"Copy the selection",cut:"Cut",paste:"Paste","select-all":"Select all"}},{label:"Collection displays",actions:{filter:"Filter the display",search:"Search this display",sort:"Sort the display",export:"Export the display","mark-as-spam":"Mark as spam"}},{label:"This site",actions:{"open-modal":"Open a block in a modal","run-action":"Run an Action"}}],q=Object.assign({},...C.map(c=>c.actions)),I={back:"greys out when there is no history behind the page",forward:"greys out unless you have gone back",copy:"greys out with nothing selected",cut:"greys out outside an editable field, or with nothing selected",paste:"greys out outside an editable field","site-search":"greys out on a site where search is unavailable",search:"greys out unless the click lands in a collection display",filter:"greys out unless the click lands in a collection display",sort:"greys out unless the click lands in a collection display",export:"greys out unless the click lands in a collection display"},M={"mark-as-spam":"needs a right-click on an entry inside a collection display"},O={navigate:{key:"url",label:"URL",placeholder:"/shop"},"run-action":{key:"slug",label:"Action slug",placeholder:"archive-entry"},"open-modal":{key:"block",label:"Block name",placeholder:"contact-card"},export:{key:"format",label:"Format",placeholder:"csv"},sort:{key:"field",label:"Field",placeholder:"price"},filter:{key:"field",label:"Field",placeholder:"category"},"mark-as-spam":{key:"field",label:"Flag field (optional)",placeholder:"spam"}};function d(c){return String(c??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function R(c){return String(c||"").toLowerCase().trim().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,64)}let g=null;function b(){if(g){try{g.destroy()}catch{}g=null}const c=document.getElementById("ctx-preview");if(c&&E.contextMenu&&E.contextMenu.registry)for(const m of E.contextMenu.registry(c))try{m.destroy()}catch{}}function j(c){return String(c||"").split(",").map(m=>m.trim()).filter(Boolean)}export const contextMenuEditorView={templateUrl:"/admin/js/templates/context-menu-editor.html",async onMount(c){E.tabs&&E.tabs(c.find("#ctx-tabs").get(0)),b();const m=location.hash.match(/^#\/context-menus\/edit\/([^?#]+)/),y=m?decodeURIComponent(m[1]):null,x=!y;let a=[];if(!x)try{const t=await v.contextMenus.get(y);document.getElementById("ctx-title").textContent=`Edit: ${t.name||t.slug}`,$("#ctx-name").val(t.name||""),$("#ctx-slug").val(t.slug).prop("disabled",!0),$("#ctx-selector").val(t.target?.selector||""),$("#ctx-match").val(t.target?.match||""),$("#ctx-exclude").val(t.target?.exclude||""),$("#ctx-inherit").val(String(t.inherit===!1?"false":t.inherit||"append")),$("#ctx-enabled").prop("checked",t.enabled!==!1),$("#ctx-accent").val(t.theme?.accent||""),$("#ctx-transition").val(t.theme?.transition||""),$("#ctx-density").val(t.theme?.density||""),$("#ctx-radius").val(t.theme?.radius||""),$("#ctx-shadow").val(t.theme?.shadow||""),$("#ctx-opacity").val(t.theme?.opacity??""),$("#ctx-pages").val((t.binding?.pages||[]).join(", ")),$("#ctx-projects").val((t.binding?.projects||[]).join(", ")),a=Array.isArray(t.items)?t.items:[]}catch(t){E.toast(`Could not load "${y}": ${t.message||t}`,{type:"error"});return}let f=!x;$("#ctx-slug").on("input",()=>{f=!0}),$("#ctx-name").on("input",function(){f||$("#ctx-slug").val(R(this.value))});function u(){const t=document.getElementById("ctx-items"),s=document.getElementById("ctx-items-empty");s.style.display=a.length?"none":"",t.replaceChildren(),a.forEach((e,i)=>{const n=e.type||"item",o=O[e.action?.type],r=document.createElement("div");r.className="card mb-2",r.innerHTML=`
|
|
2
|
+
<div class="card-body">
|
|
3
|
+
<div class="row">
|
|
4
|
+
<div class="col-4">
|
|
5
|
+
<label class="form-label">Type</label>
|
|
6
|
+
<select class="form-select ctx-item-type" data-i="${i}">
|
|
7
|
+
<option value="item"${n==="item"?" selected":""}>Item</option>
|
|
8
|
+
<option value="divider"${n==="divider"?" selected":""}>Divider</option>
|
|
9
|
+
<option value="header"${n==="header"?" selected":""}>Heading</option>
|
|
10
|
+
</select>
|
|
11
|
+
</div>
|
|
12
|
+
${n==="divider"?"":`
|
|
13
|
+
<div class="col-8">
|
|
14
|
+
<label class="form-label">Text</label>
|
|
15
|
+
<input class="form-input ctx-item-text" data-i="${i}" type="text" value="${d(e.text)}">
|
|
16
|
+
</div>`}
|
|
17
|
+
</div>
|
|
18
|
+
${n==="item"?`
|
|
19
|
+
<div class="row mt-2">
|
|
20
|
+
<div class="col-4">
|
|
21
|
+
<label class="form-label">Icon</label>
|
|
22
|
+
<div class="ctx-icon-slot" data-i="${i}"></div>
|
|
23
|
+
</div>
|
|
24
|
+
<div class="col-4">
|
|
25
|
+
<label class="form-label">Does</label>
|
|
26
|
+
<select class="form-select ctx-item-action" data-i="${i}">
|
|
27
|
+
${C.map(l=>`
|
|
28
|
+
<optgroup label="${d(l.label)}">
|
|
29
|
+
${Object.entries(l.actions).map(([S,T])=>`<option value="${S}"${e.action?.type===S?" selected":""}>${d(T)}</option>`).join("")}
|
|
30
|
+
</optgroup>`).join("")}
|
|
31
|
+
</select>
|
|
32
|
+
${I[e.action?.type]?`<small class="text-muted">${d(I[e.action.type])}</small>`:""}
|
|
33
|
+
${M[e.action?.type]?`<small class="text-muted">${d(M[e.action.type])}</small>`:""}
|
|
34
|
+
</div>
|
|
35
|
+
${o?`
|
|
36
|
+
<div class="col-4">
|
|
37
|
+
<label class="form-label">${d(o.label)}</label>
|
|
38
|
+
${o.key==="url"?`<div class="ctx-url-slot" data-i="${i}"></div>`:`<input class="form-input ctx-item-arg" data-i="${i}" data-key="${o.key}"
|
|
39
|
+
type="text" value="${d(e.action?.[o.key])}" placeholder="${d(o.placeholder)}">`}
|
|
40
|
+
</div>`:""}
|
|
41
|
+
</div>
|
|
42
|
+
<div class="row mt-2">
|
|
43
|
+
<div class="col-6">
|
|
44
|
+
<label class="form-check">
|
|
45
|
+
<input type="checkbox" class="ctx-item-danger" data-i="${i}"${e.danger?" checked":""}>
|
|
46
|
+
Destructive (red)
|
|
47
|
+
</label>
|
|
48
|
+
</div>
|
|
49
|
+
</div>`:""}
|
|
50
|
+
<div class="mt-2 text-right">
|
|
51
|
+
<button class="btn btn-sm btn-ghost ctx-item-up" data-i="${i}" ${i===0?"disabled":""}>Up</button>
|
|
52
|
+
<button class="btn btn-sm btn-ghost ctx-item-down" data-i="${i}" ${i===a.length-1?"disabled":""}>Down</button>
|
|
53
|
+
<button class="btn btn-sm btn-ghost ctx-item-remove" data-i="${i}">Remove</button>
|
|
54
|
+
</div>
|
|
55
|
+
</div>`;const w=r.querySelector(".ctx-icon-slot");if(w){const l=B("edit",e.icon||"");l.input.className+=" ctx-item-icon",l.input.dataset.i=String(i),w.appendChild(l.el)}const N=r.querySelector(".ctx-url-slot");if(N){const l=L(o.placeholder,e.action?.url||"");l.input.className+=" ctx-item-arg",l.input.dataset.i=String(i),l.input.dataset.key="url",N.appendChild(l.el)}t.appendChild(r)}),Domma.icons.scan("#ctx-items"),p()}c.on("change",".ctx-item-type",function(){const t=Number(this.dataset.i);a[t].type=this.value,this.value!=="item"?delete a[t].action:a[t].action||(a[t].action={type:"navigate"}),u()}),c.on("input",".ctx-item-text",function(){a[Number(this.dataset.i)].text=this.value,p()}),c.on("input",".ctx-item-icon",function(){a[Number(this.dataset.i)].icon=this.value,p()}),c.on("change",".ctx-item-action",function(){const t=Number(this.dataset.i);a[t].action={type:this.value},u()}),c.on("input",".ctx-item-arg",function(){const t=Number(this.dataset.i);a[t].action=a[t].action||{},a[t].action[this.dataset.key]=this.value,p()}),c.on("change",".ctx-item-danger",function(){a[Number(this.dataset.i)].danger=this.checked,p()}),c.on("click",".ctx-item-remove",function(){a.splice(Number(this.dataset.i),1),u()}),c.on("click",".ctx-item-up",function(){const t=Number(this.dataset.i);[a[t-1],a[t]]=[a[t],a[t-1]],u()}),c.on("click",".ctx-item-down",function(){const t=Number(this.dataset.i);[a[t+1],a[t]]=[a[t],a[t+1]],u()}),$("#ctx-add-item").on("click",()=>{a.push({type:"item",text:"New item",action:{type:"navigate",url:""}}),u()});function k(){const t={},s=$("#ctx-accent").val(),e=$("#ctx-transition").val(),i=$("#ctx-density").val(),n=$("#ctx-radius").val(),o=$("#ctx-shadow").val(),r=$("#ctx-opacity").val();return s&&(t.accent=s),e&&(t.transition=e),i&&(t.density=i),n&&(t.radius=n),o&&(t.shadow=o),r!==""&&r!=null&&(t.opacity=Number(r)),t}function A(){const t=$("#ctx-inherit").val(),s=j($("#ctx-pages").val()),e=j($("#ctx-projects").val()),i={slug:$("#ctx-slug").val().trim(),name:$("#ctx-name").val().trim(),enabled:$("#ctx-enabled").is(":checked"),target:{selector:$("#ctx-selector").val().trim(),match:$("#ctx-match").val().trim(),exclude:$("#ctx-exclude").val().trim()},inherit:t==="false"?!1:t,items:a},n=k();return Object.keys(n).length&&(i.theme=n),(s.length||e.length)&&(i.binding={},s.length&&(i.binding.pages=s),e.length&&(i.binding.projects=e)),i}E.tooltip&&E.tooltip(".ctx-help",{trigger:"click",position:"right"});function p(){b();const t=a.map(e=>{const i=e.type||"item";return i==="divider"?{type:"divider"}:i==="header"?{type:"header",header:e.text||""}:{label:e.text||"(no text)",icon:e.icon||void 0,danger:!!e.danger}});if(!t.some(e=>e.label))return;const s=k();g=E.contextMenu("#ctx-preview",{items:t,inherit:!1,accent:s.accent,transition:s.transition,density:s.density,radius:s.radius,shadow:s.shadow,opacity:s.opacity})}c.on("change","#ctx-accent, #ctx-transition, #ctx-density, #ctx-radius, #ctx-shadow",p),c.on("input","#ctx-opacity",p);function h(t){const s=document.getElementById("ctx-errors");if(!t){s.style.display="none";return}s.style.display="",s.innerHTML=String(t).split("; ").map(e=>`<div>${d(e)}</div>`).join(""),s.scrollIntoView({behavior:"smooth",block:"nearest"})}$("#ctx-save").on("click",async function(){h(null);const t=A();if(!t.slug)return h("Slug is required");if(!t.name)return h("Name is required");if(!t.target.selector)return h("Container selector is required");this.disabled=!0;try{x?(await v.contextMenus.create(t),E.toast("Context menu created.",{type:"success"}),location.hash="#/context-menus"):(await v.contextMenus.update(y,t),E.toast("Context menu saved.",{type:"success"}))}catch(s){h(s.message||String(s))}finally{this.disabled=!1}}),u(),Domma.icons.scan(c.get(0))},onUnmount(){E.contextMenu&&E.contextMenu.closeAll&&E.contextMenu.closeAll(),b()}};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import{api as l}from"../api.js";import{filterByProject as u,getProjectFromHash as g}from"../lib/project-context.js";import{keepTableIconsAlive as m}from"../lib/table-icons.js";function o(s){return String(s).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}export const contextMenusView={templateUrl:"/admin/js/templates/context-menus.html",async onMount(s){const r=E.loader(s.get(0),{type:"dots"});let c;try{c=await l.contextMenus.list().catch(()=>[])}finally{r.destroy()}const i=g(),d=i?u(c,i):c;T.create("#context-menus-table",{pageSize:25,data:d,emptyMessage:'No context menus yet. Click "New context menu" to create one.',columns:[{key:"slug",title:"Slug",sortable:!0,render:e=>`<code>${o(e)}</code>`},{key:"name",title:"Name",sortable:!0,render:(e,t)=>`<a href="#/context-menus/edit/${encodeURIComponent(t.slug)}" style="font-weight:600;">${o(e||t.slug)}</a>`},{key:"selector",title:"Binds to",render:(e,t)=>{const n=t.selector?`<code>${o(t.selector)}</code>`:'<span class="text-muted">-</span>',a=t.match?` → <code>${o(t.match)}</code>`:"";return n+a}},{key:"itemsCount",title:"Items",render:(e,t)=>String(t.itemsCount??0)},{key:"binding",title:"Scope",render:(e,t)=>{const n=t.binding;if(!n)return'<span class="badge badge-secondary">site-wide</span>';const a=[...(n.projects||[]).map(p=>`project:${p}`),...n.pages||[]];return a.length?`<span class="badge badge-info" data-tooltip="${o(a.join(", "))}">${a.length} rule${a.length===1?"":"s"}</span>`:'<span class="badge badge-warning" data-tooltip="Binding matches nothing, so this menu never appears">never matches</span>'}},{key:"enabled",title:"Enabled",render:(e,t)=>t.enabled===!1?'<span class="badge badge-secondary">off</span>':'<span class="badge badge-success">on</span>'},{key:"_actions",title:"Actions",render:(e,t)=>{const n=o(t.slug);return`
|
|
2
|
+
<a class="btn btn-sm btn-ghost" href="#/context-menus/edit/${encodeURIComponent(t.slug)}" data-tooltip="Edit"><span data-icon="edit" data-icon-size="14"></span></a>
|
|
3
|
+
<button class="btn btn-sm btn-ghost btn-duplicate" data-slug="${n}" data-tooltip="Duplicate"><span data-icon="copy" data-icon-size="14"></span></button>
|
|
4
|
+
<button class="btn btn-sm btn-ghost btn-delete" data-slug="${n}" data-tooltip="Delete"><span data-icon="trash" data-icon-size="14"></span></button>
|
|
5
|
+
`}}]}),Domma.icons.scan("#context-menus-table"),m(s.get(0)),document.querySelectorAll("#context-menus-table [data-tooltip]").forEach(e=>{E.tooltip(e,{content:e.getAttribute("data-tooltip"),position:"top"})}),s.off("click",".btn-duplicate").on("click",".btn-duplicate",async function(){const e=$(this).data("slug");try{const t=await l.contextMenus.duplicate(e);E.toast(`Duplicated as "${t.slug}".`,{type:"success"}),location.reload()}catch(t){E.toast(`Duplicate failed: ${t.message||t}`,{type:"error"})}}),s.off("click",".btn-delete").on("click",".btn-delete",async function(){const e=$(this).data("slug");if(await E.confirm(`Delete context menu "${e}"? This cannot be undone.`))try{await l.contextMenus.remove(e),E.toast("Context menu deleted.",{type:"success"}),location.reload()}catch(n){E.toast(`Delete failed: ${n.message||n}`,{type:"error"})}})}};
|