@zeyos/zx 2.3.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 (150) hide show
  1. package/CHANGELOG.md +138 -0
  2. package/DESIGN-SYSTEM.md +96 -0
  3. package/README.md +29 -39
  4. package/dist/types/components/account-menu/account-menu.d.ts +144 -0
  5. package/dist/types/components/app-sidebar/app-sidebar.d.ts +243 -0
  6. package/dist/types/components/avatar/avatar.d.ts +100 -0
  7. package/dist/types/components/card/card.d.ts +128 -0
  8. package/dist/types/components/chart/chart.d.ts +322 -0
  9. package/dist/types/components/date-picker/month-picker.d.ts +6 -0
  10. package/dist/types/components/dialog/dialog.d.ts +9 -0
  11. package/dist/types/components/dock/dock.d.ts +495 -0
  12. package/dist/types/components/dropdown/dropdown.d.ts +2 -2
  13. package/dist/types/components/field/field.d.ts +14 -0
  14. package/dist/types/components/field-upload/field-upload.d.ts +37 -4
  15. package/dist/types/components/form/form.d.ts +25 -3
  16. package/dist/types/components/grid/grid.d.ts +172 -0
  17. package/dist/types/components/launcher/launcher.d.ts +359 -0
  18. package/dist/types/components/layout/layout.d.ts +4 -4
  19. package/dist/types/components/menu-button/menu-button.d.ts +10 -2
  20. package/dist/types/components/modal/modal.d.ts +25 -0
  21. package/dist/types/components/multi-value-editor/multi-value-editor.d.ts +7 -1
  22. package/dist/types/components/questionnaire/items.d.ts +327 -0
  23. package/dist/types/components/questionnaire/questionnaire.d.ts +449 -0
  24. package/dist/types/components/select/select.d.ts +51 -0
  25. package/dist/types/components/sheet/sheet-stack.d.ts +167 -0
  26. package/dist/types/components/sheet/sheet.d.ts +294 -0
  27. package/dist/types/components/split-view/split-view.d.ts +2 -21
  28. package/dist/types/components/table/table.d.ts +223 -15
  29. package/dist/types/components/tooltip/tooltip.d.ts +2 -2
  30. package/dist/types/components/value-list/value-list.d.ts +24 -3
  31. package/dist/types/core/dom.d.ts +122 -0
  32. package/dist/types/core/drag-axis.d.ts +111 -0
  33. package/dist/types/core/position.d.ts +4 -2
  34. package/dist/types/index.d.ts +13 -2
  35. package/dist/types/internal/app-rail.d.ts +265 -0
  36. package/dist/types/zeyos/icons.d.ts +5 -0
  37. package/dist/types/zeyos/index.d.ts +2 -1
  38. package/dist/types/zeyos/launcher.d.ts +147 -0
  39. package/dist/types/zeyos/select.d.ts +39 -0
  40. package/dist/zx-zeyos.esm.js +1870 -133
  41. package/dist/zx-zeyos.esm.min.js +1 -1
  42. package/dist/zx.css +2006 -267
  43. package/dist/zx.esm.js +8987 -2757
  44. package/dist/zx.esm.min.js +3 -3
  45. package/dist/zx.global.js +8987 -2757
  46. package/dist/zx.global.min.js +3 -3
  47. package/dist/zx.min.css +1 -1
  48. package/docs/api.json +4396 -2344
  49. package/docs/llms.md +463 -86
  50. package/docs/llms.txt +32 -19
  51. package/package.json +10 -17
  52. package/src/components/account-menu/account-menu.css +70 -0
  53. package/src/components/account-menu/account-menu.js +208 -0
  54. package/src/components/app-rail/app-rail.css +107 -0
  55. package/src/components/app-sidebar/app-sidebar.css +80 -0
  56. package/src/components/app-sidebar/app-sidebar.js +588 -0
  57. package/src/components/avatar/avatar.css +61 -0
  58. package/src/components/avatar/avatar.js +222 -0
  59. package/src/components/button/button.css +0 -1
  60. package/src/components/card/card.css +149 -0
  61. package/src/components/card/card.js +285 -0
  62. package/src/components/chart/chart.css +61 -0
  63. package/src/components/chart/chart.js +523 -0
  64. package/src/components/date-picker/month-picker.js +9 -0
  65. package/src/components/dialog/dialog.js +11 -4
  66. package/src/components/dock/dock.css +228 -0
  67. package/src/components/dock/dock.js +977 -0
  68. package/src/components/dropdown/dropdown.js +1 -1
  69. package/src/components/field/field.js +2 -0
  70. package/src/components/field-upload/field-upload.js +9 -4
  71. package/src/components/form/form.js +6 -3
  72. package/src/components/grid/grid.css +6 -0
  73. package/src/components/grid/grid.js +220 -0
  74. package/src/components/launcher/launcher.css +210 -0
  75. package/src/components/launcher/launcher.js +765 -0
  76. package/src/components/layout/layout.js +2 -2
  77. package/src/components/menu-button/menu-button.js +27 -2
  78. package/src/components/modal/modal.css +5 -1
  79. package/src/components/modal/modal.js +37 -2
  80. package/src/components/multi-value-editor/multi-value-editor.js +2 -1
  81. package/src/components/questionnaire/items.js +370 -0
  82. package/src/components/questionnaire/questionnaire.css +387 -0
  83. package/src/components/questionnaire/questionnaire.js +1033 -0
  84. package/src/components/select/field-adapter.js +9 -4
  85. package/src/components/select/select.css +144 -11
  86. package/src/components/select/select.js +259 -27
  87. package/src/components/sheet/sheet-stack.js +304 -0
  88. package/src/components/sheet/sheet.css +351 -0
  89. package/src/components/sheet/sheet.js +551 -0
  90. package/src/components/split-view/split-view.js +76 -161
  91. package/src/components/table/table.css +43 -0
  92. package/src/components/table/table.js +531 -52
  93. package/src/components/tooltip/tooltip.js +5 -2
  94. package/src/components/value-list/value-list.js +6 -3
  95. package/src/core/dom.js +322 -0
  96. package/src/core/drag-axis.js +203 -0
  97. package/src/core/position.js +68 -22
  98. package/src/index.js +20 -2
  99. package/src/internal/app-rail.js +718 -0
  100. package/src/zeyos/icons.js +8 -3
  101. package/src/zeyos/index.js +7 -1
  102. package/src/zeyos/launcher.js +338 -0
  103. package/src/zeyos/modules.js +1 -0
  104. package/src/zeyos/select.js +115 -1
  105. package/styles/tokens/dark.css +2 -3
  106. package/styles/tokens/global.css +6 -6
  107. package/styles/tokens/modules.css +1 -0
  108. package/styles/tokens/semantic.css +5 -3
  109. package/styles/tokens/themes.css +1 -1
  110. package/styles/zx.css +11 -1
  111. package/MIGRATION.md +0 -598
  112. package/dist/types/compat/base.d.ts +0 -156
  113. package/dist/types/compat/globals.d.ts +0 -20
  114. package/dist/types/compat/index.d.ts +0 -128
  115. package/dist/types/compat/map/basic.d.ts +0 -175
  116. package/dist/types/compat/map/data.d.ts +0 -102
  117. package/dist/types/compat/map/date-time.d.ts +0 -80
  118. package/dist/types/compat/map/factory.d.ts +0 -9
  119. package/dist/types/compat/map/form.d.ts +0 -98
  120. package/dist/types/compat/map/helpers.d.ts +0 -12
  121. package/dist/types/compat/map/http.d.ts +0 -36
  122. package/dist/types/compat/map/layout.d.ts +0 -106
  123. package/dist/types/compat/map/options.d.ts +0 -80
  124. package/dist/types/compat/map/select.d.ts +0 -95
  125. package/dist/types/compat/map/stubs.d.ts +0 -32
  126. package/dist/types/compat/map/utilities.d.ts +0 -37
  127. package/dist/types/compat-entry.d.ts +0 -5
  128. package/dist/types/components/permission/permission.d.ts +0 -96
  129. package/dist/zx-compat.esm.js +0 -17484
  130. package/dist/zx-compat.esm.min.js +0 -1
  131. package/dist/zx-compat.global.js +0 -23635
  132. package/dist/zx-compat.global.min.js +0 -3
  133. package/src/compat/base.js +0 -291
  134. package/src/compat/globals.js +0 -165
  135. package/src/compat/index.js +0 -84
  136. package/src/compat/map/basic.js +0 -464
  137. package/src/compat/map/data.js +0 -232
  138. package/src/compat/map/date-time.js +0 -177
  139. package/src/compat/map/factory.js +0 -45
  140. package/src/compat/map/form.js +0 -330
  141. package/src/compat/map/helpers.js +0 -46
  142. package/src/compat/map/http.js +0 -117
  143. package/src/compat/map/layout.js +0 -256
  144. package/src/compat/map/options.js +0 -267
  145. package/src/compat/map/select.js +0 -198
  146. package/src/compat/map/stubs.js +0 -26
  147. package/src/compat/map/utilities.js +0 -113
  148. package/src/compat-entry.js +0 -11
  149. package/src/components/permission/permission.css +0 -47
  150. package/src/components/permission/permission.js +0 -201
@@ -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
- }