@zeyos/zx 3.0.0 → 4.0.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.
Files changed (88) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/DESIGN-SYSTEM.md +28 -68
  3. package/README.md +16 -37
  4. package/dist/types/components/app-sidebar/app-sidebar.d.ts +106 -32
  5. package/dist/types/components/card/card.d.ts +128 -0
  6. package/dist/types/components/chart/chart.d.ts +1 -0
  7. package/dist/types/components/launcher/launcher.d.ts +65 -6
  8. package/dist/types/components/select/select.d.ts +51 -0
  9. package/dist/types/components/table/table.d.ts +4 -3
  10. package/dist/types/core/dom.d.ts +8 -0
  11. package/dist/types/index.d.ts +2 -1
  12. package/dist/types/{components/app-rail → internal}/app-rail.d.ts +34 -19
  13. package/dist/types/zeyos/icons.d.ts +5 -0
  14. package/dist/types/zeyos/index.d.ts +2 -1
  15. package/dist/types/zeyos/launcher.d.ts +147 -0
  16. package/dist/types/zeyos/select.d.ts +39 -0
  17. package/dist/zx-zeyos.esm.js +1324 -46
  18. package/dist/zx-zeyos.esm.min.js +1 -1
  19. package/dist/zx.css +400 -12
  20. package/dist/zx.esm.js +1059 -189
  21. package/dist/zx.esm.min.js +3 -3
  22. package/dist/zx.global.js +1059 -189
  23. package/dist/zx.global.min.js +3 -3
  24. package/dist/zx.min.css +1 -1
  25. package/docs/api.json +258 -145
  26. package/docs/llms.md +93 -112
  27. package/docs/llms.txt +16 -15
  28. package/package.json +8 -17
  29. package/src/components/app-sidebar/app-sidebar.css +12 -0
  30. package/src/components/app-sidebar/app-sidebar.js +310 -110
  31. package/src/components/card/card.css +149 -0
  32. package/src/components/card/card.js +285 -0
  33. package/src/components/chart/chart.js +8 -3
  34. package/src/components/grid/grid.js +10 -3
  35. package/src/components/launcher/launcher.css +113 -3
  36. package/src/components/launcher/launcher.js +234 -26
  37. package/src/components/select/field-adapter.js +9 -4
  38. package/src/components/select/select.css +144 -10
  39. package/src/components/select/select.js +259 -27
  40. package/src/components/table/table.js +8 -7
  41. package/src/core/dom.js +20 -0
  42. package/src/index.js +2 -1
  43. package/src/{components/app-rail → internal}/app-rail.js +80 -30
  44. package/src/zeyos/icons.js +8 -3
  45. package/src/zeyos/index.js +7 -1
  46. package/src/zeyos/launcher.js +338 -0
  47. package/src/zeyos/modules.js +1 -0
  48. package/src/zeyos/select.js +115 -1
  49. package/styles/tokens/global.css +1 -1
  50. package/styles/tokens/modules.css +1 -0
  51. package/styles/zx.css +1 -0
  52. package/MIGRATION.md +0 -749
  53. package/dist/types/compat/base.d.ts +0 -156
  54. package/dist/types/compat/globals.d.ts +0 -20
  55. package/dist/types/compat/index.d.ts +0 -128
  56. package/dist/types/compat/map/basic.d.ts +0 -175
  57. package/dist/types/compat/map/data.d.ts +0 -103
  58. package/dist/types/compat/map/date-time.d.ts +0 -80
  59. package/dist/types/compat/map/factory.d.ts +0 -9
  60. package/dist/types/compat/map/form.d.ts +0 -98
  61. package/dist/types/compat/map/helpers.d.ts +0 -12
  62. package/dist/types/compat/map/http.d.ts +0 -36
  63. package/dist/types/compat/map/layout.d.ts +0 -106
  64. package/dist/types/compat/map/options.d.ts +0 -80
  65. package/dist/types/compat/map/select.d.ts +0 -95
  66. package/dist/types/compat/map/stubs.d.ts +0 -32
  67. package/dist/types/compat/map/utilities.d.ts +0 -37
  68. package/dist/types/compat-entry.d.ts +0 -5
  69. package/dist/zx-compat.esm.js +0 -17848
  70. package/dist/zx-compat.esm.min.js +0 -1
  71. package/dist/zx-compat.global.js +0 -29008
  72. package/dist/zx-compat.global.min.js +0 -3
  73. package/src/compat/base.js +0 -291
  74. package/src/compat/globals.js +0 -167
  75. package/src/compat/index.js +0 -84
  76. package/src/compat/map/basic.js +0 -464
  77. package/src/compat/map/data.js +0 -254
  78. package/src/compat/map/date-time.js +0 -177
  79. package/src/compat/map/factory.js +0 -45
  80. package/src/compat/map/form.js +0 -330
  81. package/src/compat/map/helpers.js +0 -46
  82. package/src/compat/map/http.js +0 -117
  83. package/src/compat/map/layout.js +0 -256
  84. package/src/compat/map/options.js +0 -267
  85. package/src/compat/map/select.js +0 -198
  86. package/src/compat/map/stubs.js +0 -26
  87. package/src/compat/map/utilities.js +0 -113
  88. package/src/compat-entry.js +0 -11
@@ -1,464 +0,0 @@
1
- import {
2
- CheckButton as ZxCheckButton,
3
- Dialog as ZxDialog,
4
- MenuButton as ZxMenuButton,
5
- Message as ZxMessage,
6
- Toggle as ZxToggle
7
- } from '../../index.js';
8
- import { GxWrapper } from '../base.js';
9
- import { legacyContent, messageKind, targetElement, textLabel, warnFunctionOnce, warnMethodOnce } from './helpers.js';
10
-
11
- /** Legacy ZeyOS switch. */
12
- export class Toggle extends GxWrapper {
13
- static legacyName = 'gx.zeyos.Toggle';
14
-
15
- /** @param {Element|string|null} display @param {Record<string, any>} [options={}] */
16
- constructor(display, options = {}) {
17
- super(options, { on: false, value: true });
18
- const { target, host } = buttonTarget(display);
19
- const component = new ZxToggle(target, {
20
- checked: Boolean(options.on), value: options.value ?? true,
21
- label: options.label ?? null, disabled: Boolean(options.disabled)
22
- });
23
- if (host) host.append(component.toElement());
24
- this._attach(component, {
25
- events: {
26
- check: { type: 'change', filter: (detail) => detail.checked, args: () => [this] },
27
- uncheck: { type: 'change', filter: (detail) => !detail.checked, args: () => [this] },
28
- change: { type: 'change', args: (detail) => [detail.checked] }
29
- },
30
- setters: { on: (value) => value ? this.setChecked() : this.setUnchecked(), value: (value) => { component.options.value = value; } }
31
- });
32
- component.on('change', () => this._syncAct());
33
- this._syncAct();
34
- }
35
-
36
- /** @returns {boolean} */
37
- getState() { return this._zx.get(); }
38
- /** @returns {unknown} */
39
- getValue() { return this._zx.getValue(); }
40
- /** @returns {this} */
41
- toggle() { this._zx.toggle(); this._syncAct(); return this; }
42
- /** @param {boolean} [suppress=false] @returns {this} */
43
- setChecked(suppress = false) { this._zx.set(true, { silent: Boolean(suppress) }); this._syncAct(); return this; }
44
- /** @param {boolean} [suppress=false] @returns {this} */
45
- setUnchecked(suppress = false) { this._zx.set(false, { silent: Boolean(suppress) }); this._syncAct(); return this; }
46
- /** @returns {void} */
47
- _syncAct() { this._mirrorAct(this.toElement(), this.getState()); }
48
- }
49
-
50
- /** Legacy ZeyOS floating message box. */
51
- export class Msgbox extends GxWrapper {
52
- static legacyName = 'gx.zeyos.Msgbox';
53
-
54
- /** @param {Record<string, any>} [options={}] */
55
- constructor(options = {}) {
56
- super(options, { closable: true, content: false });
57
- const component = new ZxMessage(null, { timeout: 0, closable: options.closable !== false, maxVisible: 1 });
58
- document.body.append(component.toElement());
59
- this._handle = null;
60
- this._content = options.content || '';
61
- this._attach(component, { ui: { frame: 'root', content: 'root', img: 'root' } });
62
- }
63
-
64
- /** @param {unknown} content @returns {this} */
65
- setContent(content) { this._content = legacyContent(content); return this; }
66
- /** @param {unknown} [message] @param {string} [className='info'] @returns {this} */
67
- show(message, className = 'info') {
68
- if (message != null) this.setContent(message);
69
- this._handle?.close();
70
- this._handle = this._zx.show(this._content, { kind: messageKind(className), timeout: 0, closable: this.options.closable !== false });
71
- this._mirrorAct(this.toElement(), true);
72
- return this;
73
- }
74
- /** @returns {this} */
75
- hide() { this._handle?.close(); this._handle = null; this._mirrorAct(this.toElement(), false); return this; }
76
- }
77
-
78
- /** Legacy bootstrap message/status service. */
79
- export class BootstrapMessage extends GxWrapper {
80
- static legacyName = 'gx.bootstrap.Message';
81
-
82
- /** @param {Element|string|null} display @param {Record<string, any>} [options={}] */
83
- constructor(display, options = {}) {
84
- super(options, { duration: 3000 });
85
- const component = new ZxMessage(targetElement(display), {
86
- timeout: Number(options.duration) || 0, closable: true, maxVisible: options.maxVisible ?? 5
87
- });
88
- if (!targetElement(display)) document.body.append(component.toElement());
89
- this._handles = new Map();
90
- this._progress = null;
91
- this._progressValue = 0;
92
- this._attach(component, { ui: { windows: 'root', status: 'root' } });
93
- }
94
-
95
- /** @param {unknown} message @param {string} [iconClass='info'] @param {boolean} [closable=true] @param {boolean} [_blend=false] @param {boolean} [autoclose=true] @returns {Element} */
96
- addMessage(message, iconClass = 'info', closable = true, _blend = false, autoclose = true) {
97
- const marker = document.createElement('span');
98
- marker.hidden = true;
99
- const handle = this._zx.show(legacyContent(message), {
100
- kind: messageKind(iconClass), closable: closable !== false,
101
- timeout: autoclose === false ? 0 : Number(this.options.duration) || 0
102
- });
103
- this._handles.set(marker, handle);
104
- return marker;
105
- }
106
- /** @param {Element} marker @returns {this} */
107
- closeMessage(marker) { this._handles.get(marker)?.close(); this._handles.delete(marker); return this; }
108
- /** @returns {this} */
109
- clear() { for (const handle of this._handles.values()) handle.close(); this._handles.clear(); return this; }
110
- /** @param {number} progress @param {string} [message=''] @param {boolean} [_blend=false] @returns {this} */
111
- showStatus(progress, message = '', _blend = false) {
112
- if (!this._progress) this._progress = ZxMessage.progress(String(message));
113
- this._progressValue = Number(progress) || 0;
114
- this._progress.update(this._progressValue, String(message));
115
- return this;
116
- }
117
- /** @param {number} progress @param {string} [message] @returns {this} */
118
- incProgress(progress, message) { return this.setProgress(this._progressValue + (Number(progress) || 0), message); }
119
- /** @param {number} progress @param {string} [message] @param {unknown} [_tween] @returns {this} */
120
- setProgress(progress, message, _tween) {
121
- if (!this._progress) this._progress = ZxMessage.progress(String(message ?? ''));
122
- this._progressValue = Number(progress) || 0;
123
- this._progress.update(this._progressValue, message == null ? undefined : String(message));
124
- return this;
125
- }
126
- /** @returns {this} */
127
- hideStatus() { this._progress?.done(); this._progress = null; this._progressValue = 0; return this; }
128
- }
129
-
130
- /** Shared Dialog-backed popup behavior. */
131
- class PopupBase extends GxWrapper {
132
- /** @param {Record<string, any>} options */
133
- constructor(options = {}) {
134
- super(options, { width: 600, closable: true, content: null });
135
- const component = new ZxDialog(null, {
136
- title: String(options.title ?? ''), size: numericWidth(options.width),
137
- closable: options.closable !== false,
138
- lightDismiss: Boolean(options.overlayDismiss ?? options.clickable),
139
- content: legacyContent(options.content)
140
- });
141
- this._attach(component, {
142
- events: {
143
- show: { type: 'open', args: () => [this] },
144
- hide: { type: 'close', args: (detail) => [detail.result, this] },
145
- open: { type: 'open', args: () => [this] }
146
- },
147
- ui: { modal: 'root', aside: 'content', body: 'content', content: 'content', title: 'title', footer: 'footer', cross: 'close' },
148
- setters: { content: 'setContent', title: 'setTitle', open: (value) => value ? this.show() : this.hide() }
149
- });
150
- this.isOpen = false;
151
- if (options.footer) component.setButtons(footerButtons(options.footer));
152
- if (options.open) this.show();
153
- }
154
-
155
- /** @param {unknown} [argument] @returns {this} */
156
- show(argument) { this.isOpen = true; this._showArgument = argument; this._zx.open(); return this; }
157
- /** @param {unknown} [argument] @returns {this} */
158
- hide(argument) { this.isOpen = false; this._zx.close(argument); return this; }
159
- /** @param {unknown} content @returns {this} */
160
- setContent(content) { this._zx.setContent(legacyContent(content)); return this; }
161
- /** @param {string} title @returns {this} */
162
- setTitle(title) { this._zx.setTitle(title); return this; }
163
- /** @returns {Element} */
164
- getContent() { return this._zx.refs.content; }
165
- /** @param {unknown} [_x] @param {unknown} [_y] @returns {this} */
166
- setPosition(_x, _y) { warnMethodOnce(this.constructor, 'setPosition', 'CSS dialog positioning'); return this; }
167
- }
168
-
169
- /** Legacy ZeyOS popup. */
170
- export class Popup extends PopupBase {
171
- static legacyName = 'gx.zeyos.Popup';
172
- }
173
-
174
- /** Legacy bootstrap popup. */
175
- export class BootstrapPopup extends PopupBase {
176
- static legacyName = 'gx.bootstrap.Popup';
177
- }
178
-
179
- /** Sentinel returned when a legacy confirmation is canceled. */
180
- export const PopupConfirmCanceled = Object.freeze({});
181
-
182
- /** @param {string} title @param {unknown} message @param {Record<string, any>|string} [options={}] @returns {Promise<void>} */
183
- export function PopupAlert(title, message, options = {}) {
184
- warnFunctionOnce(PopupAlert, 'gx.bootstrap.PopupAlert', 'Dialog.alert');
185
- const settings = typeof options === 'string' ? { type: options } : options;
186
- return new Promise((resolve) => {
187
- const popup = new BootstrapPopup({ ...settings, title, content: message, width: settings.width ?? 400 });
188
- popup._zx.setButtons([{ label: settings.okLabel ?? 'Ok', kind: 'primary', autofocus: true, action: () => {
189
- if (typeof settings.onOk === 'function' && settings.onOk() === false) return;
190
- popup.hide(); resolve();
191
- } }]);
192
- popup._zx.once('close', () => popup.destroy());
193
- popup.show();
194
- });
195
- }
196
-
197
- /** @param {string} title @param {unknown} message @param {Record<string, any>|string} [options={}] @returns {Promise<void|typeof PopupConfirmCanceled>} */
198
- export function PopupConfirm(title, message, options = {}) {
199
- warnFunctionOnce(PopupConfirm, 'gx.bootstrap.PopupConfirm', 'Dialog.confirm');
200
- const settings = typeof options === 'string' ? { type: options } : options;
201
- return new Promise((resolve) => {
202
- const popup = new BootstrapPopup({ ...settings, title, content: message, width: settings.width ?? 400, closable: false });
203
- popup._zx.setButtons([
204
- { label: settings.cancelLabel ?? 'Cancel', action: () => { popup.hide(); resolve(PopupConfirmCanceled); } },
205
- { label: settings.okLabel ?? 'Ok', kind: 'primary', action: () => {
206
- if (typeof settings.onOk === 'function' && settings.onOk() === false) return;
207
- popup.hide(); resolve();
208
- } }
209
- ]);
210
- popup._zx.once('close', () => popup.destroy());
211
- popup.show();
212
- });
213
- }
214
-
215
- /** Legacy multi-frame ZeyOS dialog. */
216
- export class Dialog extends GxWrapper {
217
- static legacyName = 'gx.zeyos.Dialog';
218
-
219
- /** @param {Element|string|null} _display @param {Record<string, any>} [options={}] */
220
- constructor(_display, options = {}) {
221
- super(options, { title: '', height: 400 });
222
- const component = new ZxDialog(null, { title: String(options.title ?? ''), content: null });
223
- this._frames = {};
224
- this._current = null;
225
- this._attach(component, { ui: { title: 'title', content: 'content', footer: 'footer' }, setters: { title: 'setTitle' } });
226
- }
227
-
228
- /** @param {string} title @returns {this} */
229
- setTitle(title) { this._zx.setTitle(textLabel(title)); return this; }
230
- /** @param {string} key @param {Record<string, any>} options @param {boolean} [open] @returns {this} */
231
- addFrame(key, options, open) {
232
- const buttons = footerButtons(options?.footer);
233
- const view = this._zx.addView(String(key), {
234
- title: options?.title == null ? undefined : textLabel(options.title),
235
- content: legacyContent(options?.content), buttons
236
- });
237
- this._frames[key] = { title: options?.title ?? null, content: view.content, footer: buttons, view };
238
- if ((!this._current && open !== false) || open === true) this.openFrame(key);
239
- return this;
240
- }
241
- /** @param {string} key @returns {Record<string, any>} */
242
- openFrame(key) { this._zx.showView(String(key)); this._current = this._frames[key]; return this._current; }
243
- /** @param {string} key @returns {Record<string, any>|null} */
244
- getFrame(key) { return this._frames[key] ?? null; }
245
- /** @param {string} key @param {unknown} content @param {Function} onSubmit @param {boolean} [open] @returns {unknown} */
246
- addSubmitFrame(key, content, onSubmit, open) {
247
- this.addFrame(key, { title: this.options.title, content: containerFor(content), footer: {
248
- close: { label: 'Close', click: () => this._zx.close() },
249
- ok: { label: 'OK', primary: true, click: () => onSubmit?.() }
250
- } }, open);
251
- return content;
252
- }
253
- /** @param {string} key @param {Record<string, [unknown, unknown]>} form @param {Function} onSubmit @param {boolean} [open] @returns {Record<string, unknown>} */
254
- addFormFrame(key, form, onSubmit, open) {
255
- const fields = {};
256
- const container = document.createElement('div');
257
- for (const [name, definition] of Object.entries(form ?? {})) {
258
- const label = document.createElement('p');
259
- label.textContent = definition?.[0] == null ? '' : String(definition[0]);
260
- container.append(label);
261
- const field = definition?.[1];
262
- const node = legacyContent(field);
263
- if (node?.nodeType) container.append(node); else if (node?.toElement?.()) container.append(node.toElement());
264
- fields[name] = field;
265
- }
266
- this.addSubmitFrame(key, container, onSubmit, open);
267
- return fields;
268
- }
269
- /** @param {string} key @param {unknown} message @param {string} [link] @param {boolean} [open] @returns {this} */
270
- addSuccessFrame(key, message, link, open) {
271
- const container = containerFor(message);
272
- if (link) {
273
- const anchor = document.createElement('a'); anchor.href = link; anchor.textContent = 'Open'; container.append(anchor);
274
- }
275
- return this.addFrame(key, { title: this.options.title, content: container }, open);
276
- }
277
- /** @param {Record<string, any>} form @returns {Record<string, unknown>} */
278
- getFormValues(form) {
279
- const values = {};
280
- for (const [key, field] of Object.entries(form ?? {})) {
281
- if (field?.value != null) values[key] = field.value;
282
- else if (typeof field?.getValue === 'function') values[key] = field.getValue();
283
- else if (typeof field?.getValues === 'function') values[key] = field.getValues();
284
- else if (typeof field?.getId === 'function') values[key] = field.getId();
285
- else if (typeof field?.get === 'function') values[key] = field.get();
286
- }
287
- return values;
288
- }
289
- }
290
-
291
- /** Legacy object-backed dropdown. */
292
- export class Dropdown extends GxWrapper {
293
- static legacyName = 'gx.zeyos.Dropdown';
294
-
295
- /** @param {Element|string|null} display @param {Record<string, any>} [options={}] */
296
- constructor(display, options = {}) {
297
- super(options, { items: {}, label: '', resettable: false, compact: false, upside: false });
298
- const { target, host } = buttonTarget(display);
299
- this._items = normalizeDropdownItems(options.items, options.resettable, options.emptyCaption);
300
- this._selected = null;
301
- const component = new ZxMenuButton(target, {
302
- label: String(options.label ?? ''), items: this._items,
303
- placement: options.upside ? 'top-start' : 'bottom-start'
304
- });
305
- if (host) host.append(component.toElement());
306
- this._attach(component, {
307
- events: {
308
- change: { type: 'select', args: (detail) => {
309
- this._selected = detail.value == null ? null : detail.item;
310
- component.setLabel(detail.value == null ? String(options.label ?? '') : String(detail.item.label));
311
- return [detail.value, detail.item.label, this];
312
- } }
313
- },
314
- ui: { frame: 'root', button: 'root', section: () => document.querySelector(`[aria-labelledby="${component.el.id}"]`) },
315
- setters: { items: 'setItems' }
316
- });
317
- }
318
- /** @param {Record<string, unknown>} items @returns {this} */
319
- setItems(items) { this._items = normalizeDropdownItems(items, this.options.resettable, this.options.emptyCaption); this._zx.setItems(this._items); return this; }
320
- /** @param {unknown} value @param {string} [text] @returns {this} */
321
- selectItem(value, text) {
322
- const item = this._items.find((entry) => entry !== '-' && entry.value == value);
323
- this._selected = item ?? { value, label: text ?? String(value) };
324
- this._zx.setLabel(text ?? this._selected.label); this.fireEvent('change', [value, text ?? this._selected.label, this]); this._zx.close(); return this;
325
- }
326
- /** @returns {this} */
327
- reset() { this._selected = null; this._zx.setLabel(String(this.options.label ?? '')); this.fireEvent('change', [null, this.options.emptyCaption, this]); this._zx.close(); return this; }
328
- /** @returns {{value: unknown, label: string}|null} */
329
- getSelected() { return this._selected ? { value: this._selected.value, label: this._selected.label } : null; }
330
- /** @returns {unknown} */
331
- getValue() { return this._selected?.value ?? ''; }
332
- /** @returns {this} */
333
- show() { this._zx.open(); return this; }
334
- /** @returns {this} */
335
- close() { this._zx.close(); return this; }
336
- /** @returns {this} */
337
- toggle() { this._zx.isOpen() ? this._zx.close() : this._zx.open(); return this; }
338
- }
339
-
340
- /** Legacy bootstrap menu button. */
341
- export class MenuButton extends GxWrapper {
342
- static legacyName = 'gx.bootstrap.MenuButton';
343
-
344
- /** @param {Element|string|null} display @param {Record<string, any>} [options={}] */
345
- constructor(display, options = {}) {
346
- super(options, { entries: [], label: '', orientation: 'left', direction: 'down' });
347
- const { target, host } = buttonTarget(display);
348
- this._entries = [];
349
- const component = new ZxMenuButton(target, {
350
- label: String(options.label ?? ''), items: [], kind: styleKind(options.style),
351
- placement: `${options.direction === 'up' ? 'top' : 'bottom'}-${options.orientation === 'right' ? 'end' : 'start'}`
352
- });
353
- if (host) host.append(component.toElement());
354
- this._attach(component, { events: { change: { type: 'select', args: (detail) => [detail.value, detail.item, this] } }, ui: { button: 'root', frame: 'root', menu: 'root' } });
355
- for (const entry of options.entries ?? []) this.add(entry);
356
- }
357
- /** @param {string|false|null} label @param {string} [icon] @returns {Element|null} */
358
- add(label, icon) {
359
- if (!label) { this._entries.push('-'); this._zx.setItems(this._entries); return null; }
360
- const item = { label: String(label), icon, value: this._entries.length };
361
- this._entries.push(item); this._zx.setItems(this._entries);
362
- return this.toElement();
363
- }
364
- /** @returns {this} */
365
- divider() { this.add(false); return this; }
366
- /** @returns {this} */
367
- toggle() { this._zx.isOpen() ? this._zx.close() : this._zx.open(); return this; }
368
- /** @returns {this} */
369
- open() { this._zx.open(); return this; }
370
- /** @returns {this} */
371
- close() { this._zx.close(); return this; }
372
- /** @param {string} label @returns {this} */
373
- setLabel(label) { this._zx.setLabel(label); return this; }
374
- /** @param {string} style @returns {this} */
375
- setStyle(style) { this.toElement().setAttribute('data-kind', styleKind(style)); return this; }
376
- }
377
-
378
- /** Legacy bootstrap two-state check button. */
379
- export class CheckButton extends GxWrapper {
380
- static legacyName = 'gx.bootstrap.CheckButton';
381
-
382
- /** @param {Element|string|null} display @param {Record<string, any>} [options={}] */
383
- constructor(display, options = {}) {
384
- super(options, { label: '', value: false });
385
- const { target, host } = buttonTarget(display);
386
- const component = new ZxCheckButton(target, { label: options.label, checked: Boolean(options.value), disabled: Boolean(options.disabled) });
387
- if (host) host.append(component.toElement());
388
- this._attach(component, {
389
- events: {
390
- change: { type: 'change', args: (detail) => [detail.checked] },
391
- check: { type: 'change', filter: (detail) => detail.checked, args: () => [] },
392
- uncheck: { type: 'change', filter: (detail) => !detail.checked, args: () => [] }
393
- },
394
- ui: { button: 'root', indicator: 'root', group: 'root' },
395
- setters: { value: 'set', disabled: 'setDisabled', label: 'setLabel' }
396
- });
397
- }
398
- /** @returns {boolean} */ get() { return this._zx.get(); }
399
- /** @param {boolean} [suppress=false] @returns {false} */ toggle(suppress = false) { this._zx.set(!this.get(), { silent: Boolean(suppress) }); return false; }
400
- /** @param {boolean} [suppress=false] @returns {this} */ check(suppress = false) { this._zx.set(true, { silent: Boolean(suppress) }); return this; }
401
- /** @param {boolean} [suppress=false] @returns {this} */ uncheck(suppress = false) { this._zx.set(false, { silent: Boolean(suppress) }); return this; }
402
- /** @param {boolean} value @returns {this} */ set(value) { return value ? this.check() : this.uncheck(); }
403
- /** @param {boolean} disabled @returns {this} */ setDisabled(disabled) { disabled ? this._zx.disable() : this._zx.enable(); return this; }
404
- /** @param {string|string[]} label @returns {this} */ setLabel(label) { this._zx.setLabel(label); return this; }
405
- }
406
-
407
- /** @param {Element|string|null|undefined} display @returns {{target: HTMLButtonElement|null, host: Element|null}} */
408
- function buttonTarget(display) {
409
- const element = targetElement(display);
410
- if (element?.localName === 'button') return { target: element, host: null };
411
- return { target: null, host: element };
412
- }
413
-
414
- /** @param {unknown} width @returns {'sm'|'md'|'lg'|number} */
415
- function numericWidth(width) {
416
- if (typeof width === 'number' && Number.isFinite(width)) return width;
417
- const number = Number.parseFloat(String(width ?? ''));
418
- return Number.isFinite(number) ? number : 'md';
419
- }
420
-
421
- /** @param {unknown} footer @returns {Array<Record<string, any>>} */
422
- function footerButtons(footer) {
423
- if (!footer) return [];
424
- const entries = Array.isArray(footer) ? footer : (footer?.nodeType ? [footer] : Object.values(footer));
425
- return entries.map((entry) => {
426
- if (entry?.nodeType) return { label: entry.textContent ?? '', action: () => entry.click?.() };
427
- return { label: String(entry?.label ?? ''), kind: entry?.primary ? 'primary' : 'default', action: () => entry?.click?.() };
428
- });
429
- }
430
-
431
- /** @param {unknown} content @returns {HTMLElement} */
432
- function containerFor(content) {
433
- const container = document.createElement('div');
434
- const values = Array.isArray(content) ? content : [content];
435
- for (const value of values) {
436
- const node = legacyContent(value);
437
- if (node?.nodeType) container.append(node);
438
- else if (node != null) container.append(document.createTextNode(String(node)));
439
- }
440
- return container;
441
- }
442
-
443
- /** @param {Record<string, unknown>} items @param {boolean} resettable @param {unknown} emptyCaption @returns {Array<Record<string, any>>} */
444
- function normalizeDropdownItems(items = {}, resettable = false, emptyCaption = null) {
445
- const list = [];
446
- if (resettable) list.push({ label: String(emptyCaption ?? 'Empty'), value: null });
447
- for (const [value, source] of Object.entries(items ?? {})) {
448
- const object = source && typeof source === 'object' ? source : null;
449
- list.push({
450
- label: String(object?.text ?? object?.label ?? source), value,
451
- onselect: typeof object?.onClick === 'function' ? object.onClick : undefined
452
- });
453
- }
454
- return list;
455
- }
456
-
457
- /** @param {unknown} style @returns {'default'|'primary'|'danger'|'ghost'} */
458
- function styleKind(style) {
459
- const value = String(style ?? '').toLowerCase();
460
- if (value.includes('danger') || value.includes('error')) return 'danger';
461
- if (value.includes('primary') || value.includes('success')) return 'primary';
462
- if (value.includes('link') || value.includes('ghost')) return 'ghost';
463
- return 'default';
464
- }