mnfst 0.5.162 → 0.5.164
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/lib/manifest.appwrite.auth.js +107 -199
- package/lib/manifest.appwrite.data.js +1 -3
- package/lib/manifest.charts.js +88 -97
- package/lib/manifest.chat.js +515 -0
- package/lib/manifest.code.js +110 -340
- package/lib/manifest.colorpicker.js +252 -551
- package/lib/manifest.combobox.js +73 -91
- package/lib/manifest.components.js +34 -95
- package/lib/manifest.css +18 -144
- package/lib/manifest.data.js +189 -562
- package/lib/manifest.datepicker.js +43 -128
- package/lib/manifest.dropdowns.js +18 -39
- package/lib/manifest.export.js +29 -147
- package/lib/manifest.form.css +18 -12
- package/lib/manifest.icons.js +9 -24
- package/lib/manifest.integrity.json +23 -23
- package/lib/manifest.js +62 -126
- package/lib/manifest.localization.js +67 -200
- package/lib/manifest.markdown.js +65 -174
- package/lib/manifest.min.css +1 -1
- package/lib/manifest.payments.js +40 -139
- package/lib/manifest.router.js +24 -73
- package/lib/manifest.status.js +20 -56
- package/lib/manifest.svg.js +12 -30
- package/lib/manifest.tooltips.js +28 -65
- package/lib/manifest.utilities.js +40 -107
- package/lib/manifest.virtual.js +15 -53
- package/package.json +1 -1
- package/lib/manifest.appwrite.presence.js +0 -1650
package/lib/manifest.combobox.js
CHANGED
|
@@ -2,15 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
(function () {
|
|
4
4
|
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
// value: single (default) | multiple (+ max cap)
|
|
9
|
-
// display: text (default) | chips
|
|
10
|
-
//
|
|
11
|
-
// Config is the directive value: a bare id string (the options source, like
|
|
12
|
-
// x-dropdown) or a { source, max, filter, separators, min, debounce } object.
|
|
13
|
-
// Modes are modifiers. The dropdown reuses Manifest's menu[popover] styles.
|
|
5
|
+
// Select-like field across four axes: trigger (input/textarea/button), options
|
|
6
|
+
// (none/datalist/menu/async), value (single/multiple), display (text/chips). Modes
|
|
7
|
+
// are modifiers; the value is a bare source id or a { source, max, filter, … } object.
|
|
14
8
|
|
|
15
9
|
/* ------------------------------------------------------------------ *
|
|
16
10
|
* Shared localized-UI resolver (byte-identical to the datepicker /
|
|
@@ -82,7 +76,12 @@ function initializeComboboxPlugin() {
|
|
|
82
76
|
label: li.dataset.label || li.textContent.trim(),
|
|
83
77
|
pattern: li.dataset.pattern || null,
|
|
84
78
|
locked: li.hasAttribute('data-locked'),
|
|
85
|
-
html: li.innerHTML
|
|
79
|
+
html: li.innerHTML,
|
|
80
|
+
// Carry the row's own attributes into the option, minus combobox-managed
|
|
81
|
+
// ones and Alpine bindings (x-/:/@ reference the source's scope, not the clone).
|
|
82
|
+
attrs: Array.from(li.attributes)
|
|
83
|
+
.filter(a => { const n = a.name; return n !== 'role' && n !== 'id' && n !== 'aria-selected' && n[0] !== ':' && n[0] !== '@' && n.indexOf('x-') !== 0; })
|
|
84
|
+
.map(a => [a.name, a.value])
|
|
86
85
|
}));
|
|
87
86
|
}
|
|
88
87
|
return Array.from(src.querySelectorAll('option')).map(o => ({
|
|
@@ -94,10 +93,8 @@ function initializeComboboxPlugin() {
|
|
|
94
93
|
}));
|
|
95
94
|
}
|
|
96
95
|
|
|
97
|
-
//
|
|
98
|
-
//
|
|
99
|
-
// a transient typing buffer — Alpine's x-model would dump the bound array into it
|
|
100
|
-
// and write partial typing back to the model. We read/write the model ourselves.
|
|
96
|
+
// Capture x-model's expression and strip the attribute so Alpine never binds the
|
|
97
|
+
// editor (in chips mode it's a typing buffer; we own read/write ourselves).
|
|
101
98
|
function captureModel(el) {
|
|
102
99
|
if (el.__cbModelExpr !== undefined) return;
|
|
103
100
|
let attr = null;
|
|
@@ -108,9 +105,8 @@ function initializeComboboxPlugin() {
|
|
|
108
105
|
if (attr) el.removeAttribute(attr);
|
|
109
106
|
}
|
|
110
107
|
|
|
111
|
-
//
|
|
112
|
-
//
|
|
113
|
-
// a literal attribute name that `[x-combobox]` does NOT match.
|
|
108
|
+
// Match by attribute-name prefix, not `[x-combobox]` — modifiers make the literal
|
|
109
|
+
// attribute name (x-combobox.multiple.chips) that the CSS selector won't match.
|
|
114
110
|
function isComboboxEl(el) {
|
|
115
111
|
if (!el.attributes) return false;
|
|
116
112
|
for (const a of el.attributes) if (a.name === 'x-combobox' || a.name.indexOf('x-combobox.') === 0) return true;
|
|
@@ -123,10 +119,8 @@ function initializeComboboxPlugin() {
|
|
|
123
119
|
for (let i = 0; i < all.length; i++) if (isComboboxEl(all[i])) captureModel(all[i]);
|
|
124
120
|
}
|
|
125
121
|
|
|
126
|
-
// Strip x-model before Alpine
|
|
127
|
-
// (
|
|
128
|
-
// components — are handled by wrapping the public initTree they call to mount, so the
|
|
129
|
-
// attribute is gone before Alpine's model directive looks for it.
|
|
122
|
+
// Strip x-model before Alpine binds it: static DOM here (pre-walk), later-mounted
|
|
123
|
+
// subtrees (x-markdown, components) via the wrapped public initTree below.
|
|
130
124
|
stripModels(document.body);
|
|
131
125
|
if (typeof Alpine.initTree === 'function' && !Alpine.__cbInitTreeWrapped) {
|
|
132
126
|
Alpine.__cbInitTreeWrapped = true;
|
|
@@ -144,17 +138,12 @@ function initializeComboboxPlugin() {
|
|
|
144
138
|
if (el.__mnfstCombobox) return;
|
|
145
139
|
el.__mnfstCombobox = true;
|
|
146
140
|
|
|
147
|
-
// If a mount path bypassed the
|
|
148
|
-
//
|
|
149
|
-
//
|
|
150
|
-
// value-sync would bleed the raw model into the editor ("a,b") and its input
|
|
151
|
-
// listener would write partial typing back to the model. We own read/write below,
|
|
152
|
-
// so make both native paths no-ops. captureModel still recovered the expression.
|
|
141
|
+
// If a mount path bypassed the strip (x-if/x-for via Alpine's internal initTree)
|
|
142
|
+
// and native x-model already bound the editor, neutralize both native paths here
|
|
143
|
+
// (value-sync would bleed the model in; the input listener would write typing back).
|
|
153
144
|
if (el._x_model) {
|
|
154
|
-
el._x_forceModelUpdate = function () { }; // kill the model→editor value-sync
|
|
155
|
-
// Remove Alpine's input/change listener
|
|
156
|
-
// (a local closure — overriding _x_model.set isn't enough). _x_removeModelListeners
|
|
157
|
-
// is Alpine's own removal hook for exactly this.
|
|
145
|
+
el._x_forceModelUpdate = function () { }; // kill the model→editor value-sync
|
|
146
|
+
// Remove Alpine's input/change listener (a closure; overriding .set isn't enough).
|
|
158
147
|
try {
|
|
159
148
|
const rm = el._x_removeModelListeners;
|
|
160
149
|
if (rm) Object.keys(rm).forEach(k => { try { rm[k](); } catch (_) { } });
|
|
@@ -163,19 +152,15 @@ function initializeComboboxPlugin() {
|
|
|
163
152
|
if (el.value) el.value = '';
|
|
164
153
|
}
|
|
165
154
|
|
|
166
|
-
// Sweep generated menus
|
|
167
|
-
//
|
|
168
|
-
// an old prerender baked into <body>). Either way, no connected editor points
|
|
169
|
-
// at them, so they can only sit as stale, sometimes-open duplicates.
|
|
155
|
+
// Sweep orphaned generated menus (editor gone after a re-render, or a baked
|
|
156
|
+
// prerender duplicate) — no connected editor points at them.
|
|
170
157
|
document.querySelectorAll('body > menu[id^="combobox-menu-"]').forEach(m => {
|
|
171
158
|
if (!document.querySelector('[aria-controls="' + m.id + '"]')) m.remove();
|
|
172
159
|
});
|
|
173
160
|
|
|
174
161
|
// ----- Prerender hydration -----
|
|
175
|
-
// A prerendered page bakes in
|
|
176
|
-
//
|
|
177
|
-
// than nesting a second .combobox inside it (which shrinks the field and
|
|
178
|
-
// truncates the chips). A fresh SPA run has no such wrapper and skips this.
|
|
162
|
+
// A prerendered page bakes in our wrapper; adopt it and re-derive the selection
|
|
163
|
+
// from the baked chips (nesting a second .combobox would shrink/truncate). SPA skips.
|
|
179
164
|
const adopt = el.parentElement && el.parentElement.classList.contains('combobox') ? el.parentElement : null;
|
|
180
165
|
let recoveredName = null, seedSelected = null;
|
|
181
166
|
if (adopt) {
|
|
@@ -190,8 +175,8 @@ function initializeComboboxPlugin() {
|
|
|
190
175
|
}
|
|
191
176
|
adopt.querySelectorAll('.combobox-chip, input[data-cb], [role=status]').forEach(n => n.remove());
|
|
192
177
|
const bakedMenuId = el.getAttribute('aria-controls');
|
|
193
|
-
// Remove
|
|
194
|
-
//
|
|
178
|
+
// Remove the baked generated menu (id combobox-menu-…); leave the source
|
|
179
|
+
// element (author's own id) in place for readOptions to re-read.
|
|
195
180
|
if (bakedMenuId && bakedMenuId.indexOf('combobox-menu-') === 0) {
|
|
196
181
|
const m = document.getElementById(bakedMenuId);
|
|
197
182
|
if (m) m.remove();
|
|
@@ -241,8 +226,7 @@ function initializeComboboxPlugin() {
|
|
|
241
226
|
wrap.className = 'combobox';
|
|
242
227
|
el.parentNode.insertBefore(wrap, el);
|
|
243
228
|
wrap.appendChild(el);
|
|
244
|
-
// The wrapper
|
|
245
|
-
// it. Otherwise the editor/button is constrained inside a full-width field.
|
|
229
|
+
// The wrapper is the field, so move the author's inline sizing onto it.
|
|
246
230
|
const authorStyle = el.getAttribute('style');
|
|
247
231
|
if (authorStyle) { wrap.setAttribute('style', authorStyle); el.removeAttribute('style'); }
|
|
248
232
|
}
|
|
@@ -266,9 +250,8 @@ function initializeComboboxPlugin() {
|
|
|
266
250
|
};
|
|
267
251
|
|
|
268
252
|
// ----- Locked values (non-removable chips) -----
|
|
269
|
-
// From a `locked: [...]` config list (
|
|
270
|
-
// `data-locked
|
|
271
|
-
// refuse removal while staying selected.
|
|
253
|
+
// From a `locked: [...]` config list (reactive below) and/or per-option
|
|
254
|
+
// `data-locked`. Locked chips hide their × and refuse removal.
|
|
272
255
|
const lockedFromOptions = options.filter(o => o.locked).map(o => String(o.value).toLowerCase());
|
|
273
256
|
const computeLocked = (cfgObj) => {
|
|
274
257
|
const set = new Set(lockedFromOptions);
|
|
@@ -280,10 +263,9 @@ function initializeComboboxPlugin() {
|
|
|
280
263
|
let lockedSet = computeLocked();
|
|
281
264
|
const isLocked = (v) => lockedSet.has(String(v).toLowerCase());
|
|
282
265
|
|
|
283
|
-
// ----- Reactive model (x-model)
|
|
284
|
-
//
|
|
285
|
-
//
|
|
286
|
-
// values/tokens. Read/effect/set are wired near mount, once render() exists.
|
|
266
|
+
// ----- Reactive model (x-model), owned here -----
|
|
267
|
+
// Preserve the author's shape (array or CSV; array default for .multiple). Chips
|
|
268
|
+
// show labels, the model carries values. Read/effect/set wired near mount.
|
|
287
269
|
const modelExpr = el.__cbModelExpr || null;
|
|
288
270
|
let modelArrayShape = null; // null until first read; true = array, false = CSV
|
|
289
271
|
let modelSet = null;
|
|
@@ -301,6 +283,7 @@ function initializeComboboxPlugin() {
|
|
|
301
283
|
|
|
302
284
|
function makeOption(o, i) {
|
|
303
285
|
const li = document.createElement('li');
|
|
286
|
+
if (o.attrs) o.attrs.forEach(([n, v]) => { try { li.setAttribute(n, v); } catch (_) { } });
|
|
304
287
|
li.id = menu.id + '-opt-' + i;
|
|
305
288
|
li.dataset.value = o.value;
|
|
306
289
|
li.dataset.label = o.label;
|
|
@@ -321,21 +304,18 @@ function initializeComboboxPlugin() {
|
|
|
321
304
|
}
|
|
322
305
|
|
|
323
306
|
if (hasMenu) {
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
}
|
|
307
|
+
// Always render into a generated menu, even for an authored <menu>: the source
|
|
308
|
+
// stays a data layer x-for/$x can own (reread below mirrors it), the combobox
|
|
309
|
+
// owns the listbox. readOptions keeps each row's innerHTML, preserving markup.
|
|
310
|
+
menu = document.createElement('menu');
|
|
311
|
+
document.body.appendChild(menu); // anchor positioning needs it out of overflow contexts
|
|
312
|
+
generatedMenu = menu; // tracked so cleanup() can remove it on re-render
|
|
313
|
+
if (src) src.style.setProperty('display', 'none', 'important');
|
|
332
314
|
menu.setAttribute('popover', 'manual');
|
|
333
|
-
|
|
315
|
+
menu.id = 'combobox-menu-' + rand();
|
|
334
316
|
menu.setAttribute('role', 'listbox');
|
|
335
317
|
if (multiple) menu.setAttribute('aria-multiselectable', 'true');
|
|
336
318
|
|
|
337
|
-
Array.from(menu.querySelectorAll('li')).forEach(li => li.remove());
|
|
338
|
-
|
|
339
319
|
if (create) {
|
|
340
320
|
createEl = document.createElement('li');
|
|
341
321
|
createEl.className = 'combobox-create';
|
|
@@ -548,8 +528,8 @@ function initializeComboboxPlugin() {
|
|
|
548
528
|
applyLock(chip, s.value, s.label);
|
|
549
529
|
return chip;
|
|
550
530
|
}
|
|
551
|
-
// Add
|
|
552
|
-
//
|
|
531
|
+
// Add/drop the × to match locked state. Re-run from render() so a reactive
|
|
532
|
+
// `locked` change toggles existing chips too.
|
|
553
533
|
function applyLock(chip, value, label) {
|
|
554
534
|
const locked = isLocked(value);
|
|
555
535
|
chip.toggleAttribute('data-locked', locked);
|
|
@@ -574,10 +554,8 @@ function initializeComboboxPlugin() {
|
|
|
574
554
|
|
|
575
555
|
function render() {
|
|
576
556
|
if (chips) {
|
|
577
|
-
// Incremental: keep existing chip nodes,
|
|
578
|
-
//
|
|
579
|
-
// focused editor, which collapses them to an ellipsis in WebKit; untouched
|
|
580
|
-
// nodes keep their width.
|
|
557
|
+
// Incremental: keep existing chip nodes, add/drop/refresh only. Recreating
|
|
558
|
+
// all re-inserts next to the focused editor, collapsing them in WebKit.
|
|
581
559
|
const norm = v => String(v).toLowerCase();
|
|
582
560
|
const have = new Map();
|
|
583
561
|
wrap.querySelectorAll('.combobox-chip').forEach(c => have.set(norm(c.dataset.value), c));
|
|
@@ -590,9 +568,8 @@ function initializeComboboxPlugin() {
|
|
|
590
568
|
if (!node) { const n = makeChip(s); have.set(key, n); wrap.insertBefore(n, el); }
|
|
591
569
|
else applyLock(node, s.value, s.label);
|
|
592
570
|
});
|
|
593
|
-
// Reorder
|
|
594
|
-
//
|
|
595
|
-
// common append path (order already matches) skips this.
|
|
571
|
+
// Reorder only when order differs — needless re-insertion collapses chips
|
|
572
|
+
// in WebKit; the common append path (order matches) skips this.
|
|
596
573
|
const cur = Array.from(wrap.querySelectorAll('.combobox-chip')).map(c => norm(c.dataset.value));
|
|
597
574
|
if (!sameList(cur, want)) selected.forEach(s => wrap.insertBefore(have.get(norm(s.value)), el));
|
|
598
575
|
// Hidden inputs are type=hidden (no layout), so a clean rebuild is harmless.
|
|
@@ -611,9 +588,8 @@ function initializeComboboxPlugin() {
|
|
|
611
588
|
if (el.hidden) closeMenu();
|
|
612
589
|
}
|
|
613
590
|
|
|
614
|
-
// Pull
|
|
615
|
-
//
|
|
616
|
-
// keydown avoids the keydown/character-insert race, and covers paste too.
|
|
591
|
+
// Pull separator-terminated tokens out of the field into chips, leaving the
|
|
592
|
+
// trailing partial. Reading the value (not keydown) dodges the insert race and covers paste.
|
|
617
593
|
function extractTokens() {
|
|
618
594
|
if (!separators.length) return;
|
|
619
595
|
const val = el.value;
|
|
@@ -628,8 +604,8 @@ function initializeComboboxPlugin() {
|
|
|
628
604
|
el.value = buf;
|
|
629
605
|
}
|
|
630
606
|
|
|
631
|
-
// Open for fresh entry. A committed single value
|
|
632
|
-
//
|
|
607
|
+
// Open for fresh entry. A committed single value shows the full list (swappable);
|
|
608
|
+
// selectText (keyboard focus) also selects it to type over.
|
|
633
609
|
function openForEntry(selectText) {
|
|
634
610
|
if (menu) menu.removeAttribute('data-kbd'); // opening is mouse-mode until a key drives it
|
|
635
611
|
const committed = !multiple && selected.length && el.value === selected[0].label;
|
|
@@ -702,8 +678,7 @@ function initializeComboboxPlugin() {
|
|
|
702
678
|
if (e.target === wrap) { e.preventDefault(); refocus(); }
|
|
703
679
|
});
|
|
704
680
|
|
|
705
|
-
// Outside dismiss (manual popover)
|
|
706
|
-
// so repeated re-renders don't leak document listeners.
|
|
681
|
+
// Outside dismiss (manual popover); self-removes when the menu leaves the DOM.
|
|
707
682
|
if (menu && !menu.__mnfstCbDismiss) {
|
|
708
683
|
menu.__mnfstCbDismiss = true;
|
|
709
684
|
const onDocPointer = (e) => {
|
|
@@ -713,15 +688,13 @@ function initializeComboboxPlugin() {
|
|
|
713
688
|
document.addEventListener('pointerdown', onDocPointer);
|
|
714
689
|
if (cleanup) cleanup(() => document.removeEventListener('pointerdown', onDocPointer));
|
|
715
690
|
}
|
|
716
|
-
//
|
|
717
|
-
//
|
|
718
|
-
// a stale, sometimes-open duplicate. Authored/adopted menus go with their container.
|
|
691
|
+
// The generated menu lives in <body>; remove it on teardown so it doesn't orphan
|
|
692
|
+
// as a stale duplicate. Authored/adopted menus go with their container.
|
|
719
693
|
if (cleanup && generatedMenu) cleanup(() => { if (generatedMenu.isConnected) generatedMenu.remove(); });
|
|
720
694
|
|
|
721
695
|
// ----- Reactive model (x-model) -----
|
|
722
|
-
//
|
|
723
|
-
//
|
|
724
|
-
// read runs inside an Alpine effect so $x / nested state stay reactive.
|
|
696
|
+
// Render chips from the bound value on init and external change; write back on
|
|
697
|
+
// add/remove. Read inside an Alpine effect so $x / nested state stay reactive.
|
|
725
698
|
if (modelExpr) {
|
|
726
699
|
const read = Alpine.evaluateLater(el, modelExpr);
|
|
727
700
|
modelSet = (vals) => {
|
|
@@ -741,8 +714,7 @@ function initializeComboboxPlugin() {
|
|
|
741
714
|
});
|
|
742
715
|
}
|
|
743
716
|
|
|
744
|
-
// ----- Reactive locked list (config-object form
|
|
745
|
-
// gate it on state, e.g. last-owner: locked: owners.length <= 1 ? ['owner'] : []) -----
|
|
717
|
+
// ----- Reactive locked list (config-object form, so `locked` can gate on state) -----
|
|
746
718
|
if (expr.startsWith('{')) {
|
|
747
719
|
Alpine.effect(() => {
|
|
748
720
|
let c; try { c = Alpine.evaluate(el, expr); } catch (_) { return; }
|
|
@@ -753,8 +725,8 @@ function initializeComboboxPlugin() {
|
|
|
753
725
|
});
|
|
754
726
|
}
|
|
755
727
|
|
|
756
|
-
// ----- Dynamic options: re-read when x-for / $x fills the source
|
|
757
|
-
//
|
|
728
|
+
// ----- Dynamic options: re-read when x-for / $x fills the source after build,
|
|
729
|
+
// keeping the menu, chip labels, and data-locked flags current. -----
|
|
758
730
|
if (src) {
|
|
759
731
|
const reread = () => {
|
|
760
732
|
options = readOptions(src);
|
|
@@ -762,21 +734,31 @@ function initializeComboboxPlugin() {
|
|
|
762
734
|
options.filter(o => o.locked).forEach(o => lockedFromOptions.push(String(o.value).toLowerCase()));
|
|
763
735
|
lockedSet = computeLocked();
|
|
764
736
|
selected = selected.map(s => ({ value: s.value, label: labelFor(s.value) }));
|
|
765
|
-
|
|
737
|
+
setOptions(options);
|
|
766
738
|
render();
|
|
739
|
+
// Keep an open list (and its empty-state) correct after the source changes.
|
|
740
|
+
if (menu && menu.matches(':popover-open')) filter();
|
|
767
741
|
};
|
|
768
742
|
const mo = new MutationObserver(reread);
|
|
769
|
-
|
|
743
|
+
// childList/characterData: x-for row edits. attributes: bound data-* toggles.
|
|
744
|
+
mo.observe(src, {
|
|
745
|
+
childList: true, subtree: true, characterData: true,
|
|
746
|
+
attributes: true, attributeFilter: ['data-value', 'data-label', 'data-locked', 'data-pattern']
|
|
747
|
+
});
|
|
770
748
|
if (cleanup) cleanup(() => mo.disconnect());
|
|
771
749
|
}
|
|
772
750
|
|
|
773
|
-
// ----- Seed initial chips from value="a, b" — only when
|
|
774
|
-
//
|
|
751
|
+
// ----- Seed initial chips from value="a, b" — only when no x-model (it wins)
|
|
752
|
+
// and no adopted wrapper (already seeded). -----
|
|
775
753
|
if (!modelExpr && !adopt && !editorNone && chips && el.value) {
|
|
776
754
|
const seeds = el.value.split(/[,\n;]+/).map(s => s.trim()).filter(Boolean);
|
|
777
755
|
el.value = '';
|
|
778
756
|
seeds.forEach(s => commitText(s));
|
|
779
757
|
}
|
|
758
|
+
// In chips/multiple the editor is a typing buffer. Clear value AND remove the
|
|
759
|
+
// attribute: mnfst-render serializes the attribute, so leaving it bakes seed text
|
|
760
|
+
// into the prerendered editor (shows as inline text on hydration in Safari).
|
|
761
|
+
if ((chips || multiple) && !editorNone) { el.value = ''; el.removeAttribute('value'); }
|
|
780
762
|
render();
|
|
781
763
|
}
|
|
782
764
|
}
|