@cyprnet/node-red-contrib-uibuilder-formgen 0.5.36 → 0.5.37
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/CHANGELOG.md
CHANGED
|
@@ -80,6 +80,10 @@ All notable changes to this package will be documented in this file.
|
|
|
80
80
|
|
|
81
81
|
- Checkbox defaults: Vue 2 portals now normalize checkbox <code>defaultValue</code> (accepts <code>true</code>/<code>false</code>, <code>1</code>/<code>0</code>, and common strings). Fixes cases where a checkbox appears checked but <code>showIf</code> dependent fields do not render until toggled.
|
|
82
82
|
|
|
83
|
+
## 0.5.37
|
|
84
|
+
|
|
85
|
+
- Portal UX: added a live Theme selector (Auto/Light/Dark) in the portal header. Selection applies immediately and is persisted in <code>localStorage</code> (<code>portalsmith:theme</code>) for both Vue 2 and Vue 3 portals (including legacy uib2).
|
|
86
|
+
|
|
83
87
|
## 0.5.21
|
|
84
88
|
|
|
85
89
|
- Legacy uibuilder 2.x: fixed vendor library paths (<code>../vendor/...</code>) in the uib2 portal template and uib2 Schema Builder so uibuilder comms work and <code>lookup:get</code> messages emit correctly.
|
package/package.json
CHANGED
|
@@ -148,6 +148,22 @@
|
|
|
148
148
|
gap: 1rem;
|
|
149
149
|
margin-bottom: 0.5rem;
|
|
150
150
|
}
|
|
151
|
+
.ps-header-right {
|
|
152
|
+
display: flex;
|
|
153
|
+
align-items: center;
|
|
154
|
+
justify-content: flex-end;
|
|
155
|
+
gap: 0.75rem;
|
|
156
|
+
flex-wrap: wrap;
|
|
157
|
+
}
|
|
158
|
+
.ps-theme-label {
|
|
159
|
+
font-size: 12px;
|
|
160
|
+
color: var(--ps-muted);
|
|
161
|
+
margin: 0;
|
|
162
|
+
}
|
|
163
|
+
.ps-theme-select {
|
|
164
|
+
width: auto;
|
|
165
|
+
min-width: 120px;
|
|
166
|
+
}
|
|
151
167
|
.ps-logo {
|
|
152
168
|
max-height: 48px;
|
|
153
169
|
max-width: 220px;
|
|
@@ -229,18 +245,29 @@
|
|
|
229
245
|
<div class="container form-container">
|
|
230
246
|
<div class="ps-header" style="--ps-logo-max-height: [[logoMaxHeightPx]]px; --ps-logo-max-width: [[logoMaxWidthPx]]px;">
|
|
231
247
|
<h1 class="mb-0">[[title]]</h1>
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
248
|
+
<div class="ps-header-right">
|
|
249
|
+
<div>
|
|
250
|
+
<div class="ps-theme-label">Theme</div>
|
|
251
|
+
<select class="custom-select custom-select-sm ps-theme-select" v-model="themeMode" @change="setTheme(themeMode)">
|
|
252
|
+
<option value="auto">Auto</option>
|
|
253
|
+
<option value="light">Light</option>
|
|
254
|
+
<option value="dark">Dark</option>
|
|
255
|
+
</select>
|
|
256
|
+
</div>
|
|
257
|
+
|
|
258
|
+
[[#logoUrl]]
|
|
259
|
+
<img class="ps-custom-logo" src="[[logoUrl]]" alt="[[logoAlt]]">
|
|
260
|
+
[[/logoUrl]]
|
|
261
|
+
[[^logoUrl]]
|
|
262
|
+
[[^licensed]]
|
|
263
|
+
<svg class="ps-logo" viewBox="0 0 420 96" role="img" aria-label="PortalSmith">
|
|
264
|
+
<rect x="0" y="0" width="420" height="96" rx="14" fill="var(--ps-surface)" stroke="var(--ps-border)"/>
|
|
265
|
+
<text x="24" y="58" font-size="34" font-family="Arial, Helvetica, sans-serif" fill="var(--ps-text)">PortalSmith</text>
|
|
266
|
+
<text x="24" y="78" font-size="14" font-family="Arial, Helvetica, sans-serif" fill="var(--ps-muted)">FormGen</text>
|
|
267
|
+
</svg>
|
|
268
|
+
[[/licensed]]
|
|
269
|
+
[[/logoUrl]]
|
|
270
|
+
</div>
|
|
244
271
|
</div>
|
|
245
272
|
<p v-if="description" class="text-muted mb-4">[[description]]</p>
|
|
246
273
|
|
|
@@ -495,18 +495,31 @@
|
|
|
495
495
|
.replace(/'/g, ''');
|
|
496
496
|
}
|
|
497
497
|
|
|
498
|
-
function
|
|
498
|
+
function normalizeThemeMode(mode, fallback) {
|
|
499
|
+
const m = String(mode || '').trim().toLowerCase();
|
|
500
|
+
if (m === 'light' || m === 'dark' || m === 'auto') return m;
|
|
501
|
+
const fb = String(fallback || '').trim().toLowerCase();
|
|
502
|
+
return (fb === 'light' || fb === 'dark' || fb === 'auto') ? fb : 'auto';
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
function getThemeMode() {
|
|
499
506
|
try {
|
|
500
|
-
// Optional override via localStorage (lets you tweak without regenerating)
|
|
501
507
|
const stored = localStorage.getItem('portalsmith:theme');
|
|
502
|
-
|
|
508
|
+
if (stored && stored.trim()) return normalizeThemeMode(stored, CONFIG.themeMode);
|
|
509
|
+
} catch (e) { /* ignore */ }
|
|
510
|
+
return normalizeThemeMode(CONFIG.themeMode, 'auto');
|
|
511
|
+
}
|
|
503
512
|
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
513
|
+
function applyTheme(mode) {
|
|
514
|
+
const desired = normalizeThemeMode(mode, getThemeMode());
|
|
515
|
+
document.documentElement.setAttribute('data-theme', desired);
|
|
516
|
+
return desired;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
function setThemeMode(mode) {
|
|
520
|
+
const desired = normalizeThemeMode(mode, getThemeMode());
|
|
521
|
+
try { localStorage.setItem('portalsmith:theme', desired); } catch (e) { /* ignore */ }
|
|
522
|
+
return applyTheme(desired);
|
|
510
523
|
}
|
|
511
524
|
|
|
512
525
|
// Vue app instance
|
|
@@ -778,7 +791,8 @@
|
|
|
778
791
|
resultSortKey: '',
|
|
779
792
|
resultSortDir: 'asc',
|
|
780
793
|
copyBlockActions: schema.actions ? schema.actions.filter(a => a.type === 'copyBlock') : [],
|
|
781
|
-
exportFormats: CONFIG.exportFormats
|
|
794
|
+
exportFormats: CONFIG.exportFormats,
|
|
795
|
+
themeMode: getThemeMode(),
|
|
782
796
|
},
|
|
783
797
|
computed: {
|
|
784
798
|
description() {
|
|
@@ -965,6 +979,9 @@
|
|
|
965
979
|
}
|
|
966
980
|
},
|
|
967
981
|
methods: {
|
|
982
|
+
setTheme(mode) {
|
|
983
|
+
this.themeMode = setThemeMode(mode);
|
|
984
|
+
},
|
|
968
985
|
toggleResultSort(key) {
|
|
969
986
|
const k = String(key || '').trim();
|
|
970
987
|
if (!k) return;
|
|
@@ -146,6 +146,22 @@
|
|
|
146
146
|
gap: 1rem;
|
|
147
147
|
margin-bottom: 0.5rem;
|
|
148
148
|
}
|
|
149
|
+
.ps-header-right {
|
|
150
|
+
display: flex;
|
|
151
|
+
align-items: center;
|
|
152
|
+
justify-content: flex-end;
|
|
153
|
+
gap: 0.75rem;
|
|
154
|
+
flex-wrap: wrap;
|
|
155
|
+
}
|
|
156
|
+
.ps-theme-label {
|
|
157
|
+
font-size: 12px;
|
|
158
|
+
color: var(--ps-muted);
|
|
159
|
+
margin: 0;
|
|
160
|
+
}
|
|
161
|
+
.ps-theme-select {
|
|
162
|
+
width: auto;
|
|
163
|
+
min-width: 120px;
|
|
164
|
+
}
|
|
149
165
|
.ps-logo {
|
|
150
166
|
max-height: 48px;
|
|
151
167
|
max-width: 220px;
|
|
@@ -227,18 +243,29 @@
|
|
|
227
243
|
<div class="container form-container">
|
|
228
244
|
<div class="ps-header" style="--ps-logo-max-height: [[logoMaxHeightPx]]px; --ps-logo-max-width: [[logoMaxWidthPx]]px;">
|
|
229
245
|
<h1 class="mb-0">[[title]]</h1>
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
246
|
+
<div class="ps-header-right">
|
|
247
|
+
<div>
|
|
248
|
+
<div class="ps-theme-label">Theme</div>
|
|
249
|
+
<select class="custom-select custom-select-sm ps-theme-select" v-model="themeMode" @change="setTheme(themeMode)">
|
|
250
|
+
<option value="auto">Auto</option>
|
|
251
|
+
<option value="light">Light</option>
|
|
252
|
+
<option value="dark">Dark</option>
|
|
253
|
+
</select>
|
|
254
|
+
</div>
|
|
255
|
+
|
|
256
|
+
[[#logoUrl]]
|
|
257
|
+
<img class="ps-custom-logo" src="[[logoUrl]]" alt="[[logoAlt]]">
|
|
258
|
+
[[/logoUrl]]
|
|
259
|
+
[[^logoUrl]]
|
|
260
|
+
[[^licensed]]
|
|
261
|
+
<svg class="ps-logo" viewBox="0 0 420 96" role="img" aria-label="PortalSmith">
|
|
262
|
+
<rect x="0" y="0" width="420" height="96" rx="14" fill="var(--ps-surface)" stroke="var(--ps-border)"/>
|
|
263
|
+
<text x="24" y="58" font-size="34" font-family="Arial, Helvetica, sans-serif" fill="var(--ps-text)">PortalSmith</text>
|
|
264
|
+
<text x="24" y="78" font-size="14" font-family="Arial, Helvetica, sans-serif" fill="var(--ps-muted)">FormGen</text>
|
|
265
|
+
</svg>
|
|
266
|
+
[[/licensed]]
|
|
267
|
+
[[/logoUrl]]
|
|
268
|
+
</div>
|
|
242
269
|
</div>
|
|
243
270
|
<p v-if="description" class="text-muted mb-4">[[description]]</p>
|
|
244
271
|
|
|
@@ -131,6 +131,22 @@
|
|
|
131
131
|
gap: 1rem;
|
|
132
132
|
margin-bottom: 0.5rem;
|
|
133
133
|
}
|
|
134
|
+
.ps-header-right {
|
|
135
|
+
display: flex;
|
|
136
|
+
align-items: center;
|
|
137
|
+
justify-content: flex-end;
|
|
138
|
+
gap: 0.75rem;
|
|
139
|
+
flex-wrap: wrap;
|
|
140
|
+
}
|
|
141
|
+
.ps-theme-label {
|
|
142
|
+
font-size: 12px;
|
|
143
|
+
color: var(--ps-muted);
|
|
144
|
+
margin: 0;
|
|
145
|
+
}
|
|
146
|
+
.ps-theme-select {
|
|
147
|
+
width: auto;
|
|
148
|
+
min-width: 120px;
|
|
149
|
+
}
|
|
134
150
|
.ps-logo {
|
|
135
151
|
max-height: 48px;
|
|
136
152
|
max-width: 220px;
|
|
@@ -212,18 +228,29 @@
|
|
|
212
228
|
<div class="container form-container">
|
|
213
229
|
<div class="ps-header" style="--ps-logo-max-height: [[logoMaxHeightPx]]px; --ps-logo-max-width: [[logoMaxWidthPx]]px;">
|
|
214
230
|
<h1 class="mb-0">[[title]]</h1>
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
231
|
+
<div class="ps-header-right">
|
|
232
|
+
<div>
|
|
233
|
+
<div class="ps-theme-label">Theme</div>
|
|
234
|
+
<select class="form-select form-select-sm ps-theme-select" v-model="themeMode" @change="setTheme(themeMode)">
|
|
235
|
+
<option value="auto">Auto</option>
|
|
236
|
+
<option value="light">Light</option>
|
|
237
|
+
<option value="dark">Dark</option>
|
|
238
|
+
</select>
|
|
239
|
+
</div>
|
|
240
|
+
|
|
241
|
+
[[#logoUrl]]
|
|
242
|
+
<img class="ps-custom-logo" src="[[logoUrl]]" alt="[[logoAlt]]">
|
|
243
|
+
[[/logoUrl]]
|
|
244
|
+
[[^logoUrl]]
|
|
245
|
+
[[^licensed]]
|
|
246
|
+
<svg class="ps-logo" viewBox="0 0 420 96" role="img" aria-label="PortalSmith">
|
|
247
|
+
<rect x="0" y="0" width="420" height="96" rx="14" fill="var(--ps-surface)" stroke="var(--ps-border)"/>
|
|
248
|
+
<text x="24" y="58" font-size="34" font-family="Arial, Helvetica, sans-serif" fill="var(--ps-text)">PortalSmith</text>
|
|
249
|
+
<text x="24" y="78" font-size="14" font-family="Arial, Helvetica, sans-serif" fill="var(--ps-muted)">FormGen</text>
|
|
250
|
+
</svg>
|
|
251
|
+
[[/licensed]]
|
|
252
|
+
[[/logoUrl]]
|
|
253
|
+
</div>
|
|
227
254
|
</div>
|
|
228
255
|
<p v-if="description" class="text-muted mb-4">[[description]]</p>
|
|
229
256
|
|
|
@@ -352,9 +352,31 @@
|
|
|
352
352
|
});
|
|
353
353
|
}
|
|
354
354
|
|
|
355
|
-
function
|
|
356
|
-
const m = String(
|
|
357
|
-
|
|
355
|
+
function normalizeThemeMode(mode, fallback) {
|
|
356
|
+
const m = String(mode || '').trim().toLowerCase();
|
|
357
|
+
if (m === 'light' || m === 'dark' || m === 'auto') return m;
|
|
358
|
+
const fb = String(fallback || '').trim().toLowerCase();
|
|
359
|
+
return (fb === 'light' || fb === 'dark' || fb === 'auto') ? fb : 'auto';
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function getThemeMode() {
|
|
363
|
+
try {
|
|
364
|
+
const stored = localStorage.getItem('portalsmith:theme');
|
|
365
|
+
if (stored && stored.trim()) return normalizeThemeMode(stored, CONFIG.themeMode);
|
|
366
|
+
} catch (e) { /* ignore */ }
|
|
367
|
+
return normalizeThemeMode(CONFIG.themeMode, 'auto');
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function applyTheme(mode) {
|
|
371
|
+
const desired = normalizeThemeMode(mode, getThemeMode());
|
|
372
|
+
document.documentElement.setAttribute('data-theme', desired);
|
|
373
|
+
return desired;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function setThemeMode(mode) {
|
|
377
|
+
const desired = normalizeThemeMode(mode, getThemeMode());
|
|
378
|
+
try { localStorage.setItem('portalsmith:theme', desired); } catch (e) { /* ignore */ }
|
|
379
|
+
return applyTheme(desired);
|
|
358
380
|
}
|
|
359
381
|
|
|
360
382
|
function tryParseHeadersJson(s) {
|
|
@@ -516,7 +538,8 @@
|
|
|
516
538
|
resultSortKey: '',
|
|
517
539
|
resultSortDir: 'asc',
|
|
518
540
|
copyBlockActions: schema.actions ? schema.actions.filter(a => a.type === 'copyBlock') : [],
|
|
519
|
-
exportFormats: CONFIG.exportFormats
|
|
541
|
+
exportFormats: CONFIG.exportFormats,
|
|
542
|
+
themeMode: getThemeMode(),
|
|
520
543
|
};
|
|
521
544
|
},
|
|
522
545
|
computed: {
|
|
@@ -657,6 +680,9 @@
|
|
|
657
680
|
}
|
|
658
681
|
},
|
|
659
682
|
methods: {
|
|
683
|
+
setTheme(mode) {
|
|
684
|
+
this.themeMode = setThemeMode(mode);
|
|
685
|
+
},
|
|
660
686
|
toggleResultSort(key) {
|
|
661
687
|
const k = String(key || '').trim();
|
|
662
688
|
if (!k) return;
|