@zeyos/zx 2.1.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +39 -0
- package/README.md +15 -0
- package/dist/types/compat/base.d.ts +156 -0
- package/dist/types/compat/globals.d.ts +20 -0
- package/dist/types/compat/index.d.ts +128 -0
- package/dist/types/compat/map/basic.d.ts +175 -0
- package/dist/types/compat/map/data.d.ts +102 -0
- package/dist/types/compat/map/date-time.d.ts +80 -0
- package/dist/types/compat/map/factory.d.ts +9 -0
- package/dist/types/compat/map/form.d.ts +98 -0
- package/dist/types/compat/map/helpers.d.ts +12 -0
- package/dist/types/compat/map/http.d.ts +36 -0
- package/dist/types/compat/map/layout.d.ts +106 -0
- package/dist/types/compat/map/options.d.ts +80 -0
- package/dist/types/compat/map/select.d.ts +95 -0
- package/dist/types/compat/map/stubs.d.ts +32 -0
- package/dist/types/compat/map/utilities.d.ts +37 -0
- package/dist/types/compat-entry.d.ts +5 -0
- package/dist/types/components/badge/badge.d.ts +46 -0
- package/dist/types/components/breadcrumb/breadcrumb.d.ts +200 -0
- package/dist/types/components/button/button.d.ts +42 -0
- package/dist/types/components/check-button/check-button.d.ts +98 -0
- package/dist/types/components/checklist/checklist.d.ts +143 -0
- package/dist/types/components/checklist/field-adapter.d.ts +5 -0
- package/dist/types/components/context-menu/context-menu.d.ts +130 -0
- package/dist/types/components/copy/copy-button.d.ts +48 -0
- package/dist/types/components/copy/copy-input.d.ts +91 -0
- package/dist/types/components/data-filter/data-filter.d.ts +250 -0
- package/dist/types/components/data-filter/filter-core.d.ts +52 -0
- package/dist/types/components/date-picker/date-picker.d.ts +137 -0
- package/dist/types/components/date-picker/date-range-picker.d.ts +350 -0
- package/dist/types/components/date-picker/field-adapter.d.ts +6 -0
- package/dist/types/components/date-picker/month-picker.d.ts +75 -0
- package/dist/types/components/date-picker/range-field-adapter.d.ts +7 -0
- package/dist/types/components/date-picker/time-picker.d.ts +127 -0
- package/dist/types/components/datebox/date-range-box.d.ts +218 -0
- package/dist/types/components/datebox/datebox.d.ts +160 -0
- package/dist/types/components/datebox/field-adapter.d.ts +5 -0
- package/dist/types/components/dialog/dialog.d.ts +284 -0
- package/dist/types/components/dropdown/dropdown.d.ts +118 -0
- package/dist/types/components/empty-state/empty-state.d.ts +38 -0
- package/dist/types/components/field/field.d.ts +231 -0
- package/dist/types/components/field-upload/field-adapter.d.ts +5 -0
- package/dist/types/components/field-upload/field-upload.d.ts +213 -0
- package/dist/types/components/fieldset/fieldset.d.ts +125 -0
- package/dist/types/components/finder/finder.d.ts +252 -0
- package/dist/types/components/form/form.d.ts +194 -0
- package/dist/types/components/groupbox/groupbox.d.ts +89 -0
- package/dist/types/components/layout/layout.d.ts +132 -0
- package/dist/types/components/loading/inline-loading.d.ts +86 -0
- package/dist/types/components/loading/progress-bar.d.ts +168 -0
- package/dist/types/components/loading/spinner.d.ts +34 -0
- package/dist/types/components/master-panel/master-panel.d.ts +112 -0
- package/dist/types/components/menu-button/menu-button.d.ts +142 -0
- package/dist/types/components/message/message.d.ts +212 -0
- package/dist/types/components/modal/modal.d.ts +105 -0
- package/dist/types/components/multi-value-editor/field-adapter.d.ts +5 -0
- package/dist/types/components/multi-value-editor/multi-value-editor.d.ts +66 -0
- package/dist/types/components/navigation-bar/navigation-bar.d.ts +229 -0
- package/dist/types/components/number-field/field-adapter.d.ts +5 -0
- package/dist/types/components/number-field/number-field.d.ts +292 -0
- package/dist/types/components/pagination/pagination.d.ts +267 -0
- package/dist/types/components/panel/panel.d.ts +194 -0
- package/dist/types/components/permission/permission.d.ts +93 -0
- package/dist/types/components/rating/field-adapter.d.ts +5 -0
- package/dist/types/components/rating/rating.d.ts +213 -0
- package/dist/types/components/search/search.d.ts +98 -0
- package/dist/types/components/select/field-adapter.d.ts +5 -0
- package/dist/types/components/select/filter.d.ts +11 -0
- package/dist/types/components/select/select.d.ts +235 -0
- package/dist/types/components/skeleton/skeleton.d.ts +97 -0
- package/dist/types/components/slider/field-adapter.d.ts +5 -0
- package/dist/types/components/slider/slider.d.ts +216 -0
- package/dist/types/components/split-view/split-view.d.ts +335 -0
- package/dist/types/components/stepper/stepper.d.ts +314 -0
- package/dist/types/components/stepper/steps.d.ts +131 -0
- package/dist/types/components/tabbox/tabbox.d.ts +230 -0
- package/dist/types/components/table/sort.d.ts +49 -0
- package/dist/types/components/table/table.d.ts +872 -0
- package/dist/types/components/tag-picker/field-adapter.d.ts +5 -0
- package/dist/types/components/tag-picker/tag-picker.d.ts +375 -0
- package/dist/types/components/timebox/field-adapter.d.ts +5 -0
- package/dist/types/components/timebox/timebox.d.ts +121 -0
- package/dist/types/components/toggle/field-adapter.d.ts +5 -0
- package/dist/types/components/toggle/toggle.d.ts +94 -0
- package/dist/types/components/toolbar/toolbar.d.ts +230 -0
- package/dist/types/components/tooltip/tooltip.d.ts +183 -0
- package/dist/types/components/tree/hierarchy.d.ts +113 -0
- package/dist/types/components/tree/tree.d.ts +383 -0
- package/dist/types/components/truncate/truncate.d.ts +61 -0
- package/dist/types/components/value-list/field-adapter.d.ts +5 -0
- package/dist/types/components/value-list/value-list.d.ts +143 -0
- package/dist/types/core/breakpoint.d.ts +68 -0
- package/dist/types/core/component.d.ts +115 -0
- package/dist/types/core/date.d.ts +50 -0
- package/dist/types/core/dom.d.ts +86 -0
- package/dist/types/core/export.d.ts +70 -0
- package/dist/types/core/fontawesome.d.ts +107 -0
- package/dist/types/core/format.d.ts +107 -0
- package/dist/types/core/http.d.ts +122 -0
- package/dist/types/core/i18n.d.ts +35 -0
- package/dist/types/core/icons.d.ts +137 -0
- package/dist/types/core/keyboard.d.ts +72 -0
- package/dist/types/core/position.d.ts +45 -0
- package/dist/types/core/storage.d.ts +46 -0
- package/dist/types/core/util.d.ts +117 -0
- package/dist/types/elements/define.d.ts +73 -0
- package/dist/types/elements/reflect.d.ts +27 -0
- package/dist/types/index.d.ts +79 -0
- package/dist/types/zeyos/connect.d.ts +65 -0
- package/dist/types/zeyos/form.d.ts +140 -0
- package/dist/types/zeyos/icons.d.ts +104 -0
- package/dist/types/zeyos/index.d.ts +12 -0
- package/dist/types/zeyos/modules.d.ts +110 -0
- package/dist/types/zeyos/query.d.ts +157 -0
- package/dist/types/zeyos/schema.d.ts +79 -0
- package/dist/types/zeyos/select.d.ts +167 -0
- package/dist/types/zeyos/table.d.ts +205 -0
- package/dist/zx-compat.esm.js +362 -33
- package/dist/zx-compat.esm.min.js +1 -1
- package/dist/zx-compat.global.js +359 -67
- package/dist/zx-compat.global.min.js +3 -3
- package/dist/zx-zeyos.esm.js +342 -43
- package/dist/zx-zeyos.esm.min.js +1 -1
- package/dist/zx.css +68 -6
- package/dist/zx.esm.js +346 -67
- package/dist/zx.esm.min.js +3 -3
- package/dist/zx.global.js +346 -67
- package/dist/zx.global.min.js +3 -3
- package/dist/zx.min.css +1 -1
- package/docs/api.json +48 -2
- package/docs/llms.md +28 -0
- package/package.json +37 -9
- package/src/compat/base.js +291 -0
- package/src/compat/globals.js +165 -0
- package/src/compat/index.js +84 -0
- package/src/compat/map/basic.js +464 -0
- package/src/compat/map/data.js +232 -0
- package/src/compat/map/date-time.js +177 -0
- package/src/compat/map/factory.js +45 -0
- package/src/compat/map/form.js +330 -0
- package/src/compat/map/helpers.js +46 -0
- package/src/compat/map/http.js +117 -0
- package/src/compat/map/layout.js +256 -0
- package/src/compat/map/options.js +267 -0
- package/src/compat/map/select.js +198 -0
- package/src/compat/map/stubs.js +26 -0
- package/src/compat/map/utilities.js +113 -0
- package/src/compat-entry.js +11 -0
- package/src/components/badge/badge.css +147 -0
- package/src/components/badge/badge.js +92 -0
- package/src/components/breadcrumb/breadcrumb.css +105 -0
- package/src/components/breadcrumb/breadcrumb.js +331 -0
- package/src/components/button/button.css +104 -0
- package/src/components/button/button.js +76 -0
- package/src/components/check-button/check-button.css +66 -0
- package/src/components/check-button/check-button.js +167 -0
- package/src/components/checklist/checklist.css +65 -0
- package/src/components/checklist/checklist.js +282 -0
- package/src/components/checklist/field-adapter.js +46 -0
- package/src/components/context-menu/context-menu.css +54 -0
- package/src/components/context-menu/context-menu.js +334 -0
- package/src/components/copy/copy-button.css +27 -0
- package/src/components/copy/copy-button.js +78 -0
- package/src/components/copy/copy-input.css +59 -0
- package/src/components/copy/copy-input.js +123 -0
- package/src/components/data-filter/data-filter.css +53 -0
- package/src/components/data-filter/data-filter.js +400 -0
- package/src/components/data-filter/filter-core.js +128 -0
- package/src/components/date-picker/date-picker.css +181 -0
- package/src/components/date-picker/date-picker.js +500 -0
- package/src/components/date-picker/date-range-picker.css +264 -0
- package/src/components/date-picker/date-range-picker.js +990 -0
- package/src/components/date-picker/field-adapter.js +36 -0
- package/src/components/date-picker/month-picker.js +223 -0
- package/src/components/date-picker/range-field-adapter.js +31 -0
- package/src/components/date-picker/time-picker.css +191 -0
- package/src/components/date-picker/time-picker.js +406 -0
- package/src/components/datebox/date-range-box.css +128 -0
- package/src/components/datebox/date-range-box.js +492 -0
- package/src/components/datebox/datebox.css +108 -0
- package/src/components/datebox/datebox.js +358 -0
- package/src/components/datebox/field-adapter.js +34 -0
- package/src/components/dialog/dialog.css +74 -0
- package/src/components/dialog/dialog.js +450 -0
- package/src/components/dropdown/dropdown.css +24 -0
- package/src/components/dropdown/dropdown.js +288 -0
- package/src/components/empty-state/empty-state.css +80 -0
- package/src/components/empty-state/empty-state.js +92 -0
- package/src/components/field/field.css +127 -0
- package/src/components/field/field.js +605 -0
- package/src/components/field-upload/field-adapter.js +21 -0
- package/src/components/field-upload/field-upload.css +108 -0
- package/src/components/field-upload/field-upload.js +475 -0
- package/src/components/fieldset/fieldset.css +37 -0
- package/src/components/fieldset/fieldset.js +307 -0
- package/src/components/finder/finder.css +98 -0
- package/src/components/finder/finder.js +509 -0
- package/src/components/form/form.css +32 -0
- package/src/components/form/form.js +302 -0
- package/src/components/groupbox/groupbox.css +51 -0
- package/src/components/groupbox/groupbox.js +199 -0
- package/src/components/layout/layout.css +117 -0
- package/src/components/layout/layout.js +136 -0
- package/src/components/loading/inline-loading.css +34 -0
- package/src/components/loading/inline-loading.js +140 -0
- package/src/components/loading/progress-bar.css +115 -0
- package/src/components/loading/progress-bar.js +259 -0
- package/src/components/loading/spinner.css +58 -0
- package/src/components/loading/spinner.js +57 -0
- package/src/components/master-panel/master-panel.css +51 -0
- package/src/components/master-panel/master-panel.js +203 -0
- package/src/components/menu-button/menu-button.css +49 -0
- package/src/components/menu-button/menu-button.js +294 -0
- package/src/components/message/message.css +122 -0
- package/src/components/message/message.js +499 -0
- package/src/components/modal/modal.css +47 -0
- package/src/components/modal/modal.js +203 -0
- package/src/components/multi-value-editor/field-adapter.js +44 -0
- package/src/components/multi-value-editor/multi-value-editor.css +56 -0
- package/src/components/multi-value-editor/multi-value-editor.js +170 -0
- package/src/components/navigation-bar/navigation-bar.css +130 -0
- package/src/components/navigation-bar/navigation-bar.js +450 -0
- package/src/components/number-field/field-adapter.js +40 -0
- package/src/components/number-field/number-field.css +77 -0
- package/src/components/number-field/number-field.js +545 -0
- package/src/components/pagination/pagination.css +95 -0
- package/src/components/pagination/pagination.js +481 -0
- package/src/components/panel/panel.css +123 -0
- package/src/components/panel/panel.js +336 -0
- package/src/components/permission/permission.css +47 -0
- package/src/components/permission/permission.js +198 -0
- package/src/components/rating/field-adapter.js +34 -0
- package/src/components/rating/rating.css +97 -0
- package/src/components/rating/rating.js +395 -0
- package/src/components/search/search.css +70 -0
- package/src/components/search/search.js +193 -0
- package/src/components/select/field-adapter.js +24 -0
- package/src/components/select/filter.js +39 -0
- package/src/components/select/select.css +180 -0
- package/src/components/select/select.js +663 -0
- package/src/components/skeleton/skeleton.css +76 -0
- package/src/components/skeleton/skeleton.js +139 -0
- package/src/components/slider/field-adapter.js +36 -0
- package/src/components/slider/slider.css +181 -0
- package/src/components/slider/slider.js +399 -0
- package/src/components/split-view/split-view.css +139 -0
- package/src/components/split-view/split-view.js +806 -0
- package/src/components/stepper/stepper.css +234 -0
- package/src/components/stepper/stepper.js +459 -0
- package/src/components/stepper/steps.js +131 -0
- package/src/components/tabbox/tabbox.css +281 -0
- package/src/components/tabbox/tabbox.js +484 -0
- package/src/components/table/sort.js +85 -0
- package/src/components/table/table.css +364 -0
- package/src/components/table/table.js +1693 -0
- package/src/components/tag-picker/field-adapter.js +35 -0
- package/src/components/tag-picker/tag-picker.css +169 -0
- package/src/components/tag-picker/tag-picker.js +837 -0
- package/src/components/timebox/field-adapter.js +24 -0
- package/src/components/timebox/timebox.css +51 -0
- package/src/components/timebox/timebox.js +276 -0
- package/src/components/toggle/field-adapter.js +24 -0
- package/src/components/toggle/toggle.css +66 -0
- package/src/components/toggle/toggle.js +150 -0
- package/src/components/toolbar/toolbar.css +81 -0
- package/src/components/toolbar/toolbar.js +511 -0
- package/src/components/tooltip/tooltip.css +36 -0
- package/src/components/tooltip/tooltip.js +420 -0
- package/src/components/tree/hierarchy.js +151 -0
- package/src/components/tree/tree.css +99 -0
- package/src/components/tree/tree.js +784 -0
- package/src/components/truncate/truncate.css +20 -0
- package/src/components/truncate/truncate.js +92 -0
- package/src/components/value-list/field-adapter.js +24 -0
- package/src/components/value-list/value-list.css +108 -0
- package/src/components/value-list/value-list.js +336 -0
- package/src/core/breakpoint.js +150 -0
- package/src/core/component.js +292 -0
- package/src/core/date.js +216 -0
- package/src/core/dom.js +260 -0
- package/src/core/export.js +204 -0
- package/src/core/fontawesome.js +177 -0
- package/src/core/format.js +283 -0
- package/src/core/http.js +246 -0
- package/src/core/i18n.js +83 -0
- package/src/core/icons.js +283 -0
- package/src/core/keyboard.js +240 -0
- package/src/core/position.js +177 -0
- package/src/core/storage.js +175 -0
- package/src/core/util.js +336 -0
- package/src/elements/define.js +715 -0
- package/src/elements/reflect.js +79 -0
- package/src/index.js +131 -0
- package/src/zeyos/connect.js +142 -0
- package/src/zeyos/form.js +254 -0
- package/src/zeyos/icons.js +92 -0
- package/src/zeyos/index.js +21 -0
- package/src/zeyos/modules.js +269 -0
- package/src/zeyos/query.js +199 -0
- package/src/zeyos/schema.js +474 -0
- package/src/zeyos/select.js +175 -0
- package/src/zeyos/table.js +272 -0
- package/styles/base.css +124 -0
- package/styles/icons.css +34 -0
- package/styles/tokens/dark.css +84 -0
- package/styles/tokens/density.css +9 -0
- package/styles/tokens/global.css +80 -0
- package/styles/tokens/modules.css +109 -0
- package/styles/tokens/semantic.css +49 -0
- package/styles/zx.css +61 -0
package/docs/api.json
CHANGED
|
@@ -4119,6 +4119,18 @@
|
|
|
4119
4119
|
"default": "true",
|
|
4120
4120
|
"description": "Whether column headers stick while scrolling."
|
|
4121
4121
|
},
|
|
4122
|
+
{
|
|
4123
|
+
"name": "growing",
|
|
4124
|
+
"type": "false|number",
|
|
4125
|
+
"default": "false",
|
|
4126
|
+
"description": "Number of rows to render at first, with a control that appends the next batch on demand. A result set an ERP query returns happily — tens of thousands of rows — costs more to lay out than anyone will ever scroll through."
|
|
4127
|
+
},
|
|
4128
|
+
{
|
|
4129
|
+
"name": "responsive",
|
|
4130
|
+
"type": "false|'sm'|'md'|'lg'|'xl'",
|
|
4131
|
+
"default": "false",
|
|
4132
|
+
"description": "Width below which each row stacks into a card of label/value pairs instead of scrolling sideways. Measured on the table's own container, not the viewport, so a table inside a split pane stacks when *it* is narrow."
|
|
4133
|
+
},
|
|
4122
4134
|
{
|
|
4123
4135
|
"name": "height",
|
|
4124
4136
|
"type": "number|string|null",
|
|
@@ -4127,9 +4139,9 @@
|
|
|
4127
4139
|
},
|
|
4128
4140
|
{
|
|
4129
4141
|
"name": "emptyText",
|
|
4130
|
-
"type": "string|null",
|
|
4142
|
+
"type": "string|Node|(() => Node|string)|null",
|
|
4131
4143
|
"default": "null",
|
|
4132
|
-
"description": "
|
|
4144
|
+
"description": "What to show when there are no rows; null resolves `table.empty`. A Node — an `emptyState()` with an action in it, say — is placed as given, and a function is called on each render, which is what to use when the placeholder holds live controls."
|
|
4133
4145
|
},
|
|
4134
4146
|
{
|
|
4135
4147
|
"name": "rowClass",
|
|
@@ -4176,6 +4188,16 @@
|
|
|
4176
4188
|
"payload": "{rows}",
|
|
4177
4189
|
"description": ""
|
|
4178
4190
|
},
|
|
4191
|
+
{
|
|
4192
|
+
"name": "stackedchange",
|
|
4193
|
+
"payload": "{stacked: boolean}",
|
|
4194
|
+
"description": "Fired when the table crosses its `responsive` width in either direction."
|
|
4195
|
+
},
|
|
4196
|
+
{
|
|
4197
|
+
"name": "grow",
|
|
4198
|
+
"payload": "{rendered: number, total: number}",
|
|
4199
|
+
"description": "Fired after more rows are rendered."
|
|
4200
|
+
},
|
|
4179
4201
|
{
|
|
4180
4202
|
"name": "editstart",
|
|
4181
4203
|
"payload": "{row, id, column, columnId, value}",
|
|
@@ -4258,6 +4280,30 @@
|
|
|
4258
4280
|
"returns": "this",
|
|
4259
4281
|
"description": "Opens the editor for a cell. No-op unless the table runs with `editMode` and the column resolves to an editable type for that row. In `'row'` mode every editable cell of the row is opened and `columnId` only decides which editor receives focus. Any other open edit is committed first."
|
|
4260
4282
|
},
|
|
4283
|
+
{
|
|
4284
|
+
"name": "growBy",
|
|
4285
|
+
"signature": "growBy([count])",
|
|
4286
|
+
"returns": "TableRow[]",
|
|
4287
|
+
"description": "The rows currently rendered — all of them unless `growing` is on."
|
|
4288
|
+
},
|
|
4289
|
+
{
|
|
4290
|
+
"name": "showAll",
|
|
4291
|
+
"signature": "showAll()",
|
|
4292
|
+
"returns": "this",
|
|
4293
|
+
"description": "Renders every remaining row."
|
|
4294
|
+
},
|
|
4295
|
+
{
|
|
4296
|
+
"name": "getRenderedCount",
|
|
4297
|
+
"signature": "getRenderedCount()",
|
|
4298
|
+
"returns": "number",
|
|
4299
|
+
"description": "How many rows are currently rendered."
|
|
4300
|
+
},
|
|
4301
|
+
{
|
|
4302
|
+
"name": "isStacked",
|
|
4303
|
+
"signature": "isStacked(key, fallback, args, stacked, stacked)",
|
|
4304
|
+
"returns": "void",
|
|
4305
|
+
"description": "Keeps the \"show more\" control in step with what is rendered. The control sits outside the table rather than in a `<tfoot>` row spanning every column: it is a command, not data, and a button inside the grid would be reachable by the row navigation keys as though it were a cell."
|
|
4306
|
+
},
|
|
4261
4307
|
{
|
|
4262
4308
|
"name": "destroy",
|
|
4263
4309
|
"signature": "destroy()",
|
package/docs/llms.md
CHANGED
|
@@ -38,6 +38,9 @@ const t = new Table(target, options);
|
|
|
38
38
|
## Entry points
|
|
39
39
|
|
|
40
40
|
- ESM: `import { … } from '/assets/zx.esm.js'` (or `../src/index.js` in-repo, no build).
|
|
41
|
+
- Granular: `import { Slider } from '@zeyos/zx/src/components/slider/slider.js'` — one component
|
|
42
|
+
rather than the whole library, for consumers running a bundler. Two components bundle 26 kB
|
|
43
|
+
minified against 157 kB through the package entry. The stylesheet is still loaded whole.
|
|
41
44
|
- Global: `<script src="/assets/zx.global.js">` → `window.zx.*`.
|
|
42
45
|
- Styles: load `/assets/zx.css` once.
|
|
43
46
|
- Build bundles with `npm run build` (esbuild). No build is needed to develop against `src/`.
|
|
@@ -770,6 +773,23 @@ horizontally, and multi-select adds a tri-state header checkbox plus Shift+click
|
|
|
770
773
|
editable cell (Shift+Tab the previous), wrapping across rows, and tabbing past the last editable
|
|
771
774
|
cell commits and leaves the table normally. Clicking outside the editor commits rather than
|
|
772
775
|
discarding what was typed.
|
|
776
|
+
- **Growing** — `growing: <number>` renders that many rows and offers a control for the next batch,
|
|
777
|
+
instead of laying out a result set nobody will scroll through. `growBy(count?)`, `showAll()`,
|
|
778
|
+
`getRenderedCount()`, and `grow {rendered, total}`. `setData()` starts the batch over, since a new
|
|
779
|
+
result set is not the old one grown. Row indices stay absolute — a row's index is its place in the
|
|
780
|
+
data, not in what is on screen.
|
|
781
|
+
- **Responsive stacking** — `responsive: false|'sm'|'md'|'lg'|'xl'` stacks each row into a card of
|
|
782
|
+
label/value pairs below that width, instead of leaving a horizontal scrollbar with the data
|
|
783
|
+
hidden behind it. The width measured is **the table's own container**, not the viewport, so this
|
|
784
|
+
triggers inside a narrow split pane on a wide screen. A column with `popin: false` leads the card
|
|
785
|
+
instead of becoming another labelled line — the identifying column usually wants that. Every cell
|
|
786
|
+
carries `data-label`, so the labels are there whether or not the option is on. `isStacked()`
|
|
787
|
+
reports the current state and `stackedchange {stacked}` fires on each crossing. Stacking drops
|
|
788
|
+
`display: table`, which drops the implicit ARIA roles, so the component states
|
|
789
|
+
`table`/`rowgroup`/`row`/`cell`/`columnheader` explicitly for exactly as long as the mode is on.
|
|
790
|
+
- **Empty state** — `emptyText` takes a string, a Node, or a function returning one, so an empty
|
|
791
|
+
table can carry an icon, an explanation, and the action that resolves it (`emptyState({…})`).
|
|
792
|
+
A function is re-called on every render, which is what to use when the placeholder holds controls.
|
|
773
793
|
<!-- /doc -->
|
|
774
794
|
|
|
775
795
|
<!-- doc:data-filter -->
|
|
@@ -1312,4 +1332,12 @@ colour literals and tier-1 references in component CSS.
|
|
|
1312
1332
|
`--zx-radius-sm/md/lg/full`, the type scale, shadows, and motion.
|
|
1313
1333
|
- **Tier 2** — the semantic set, the only tokens components may use: `--zx-color-*`,
|
|
1314
1334
|
`--zx-control-*`, `--zx-focus-ring`, `--zx-overlay-shadow`.
|
|
1335
|
+
- **Component styling hooks** — a short, deliberately small set of component-level custom
|
|
1336
|
+
properties for restyling one component without touching its internals. Each falls back to the
|
|
1337
|
+
semantic token it replaces, so setting none of them changes nothing:
|
|
1338
|
+
`--zx-table-header-bg`, `--zx-table-row-hover-bg`, `--zx-table-row-selected-bg`,
|
|
1339
|
+
`--zx-table-border-color`, `--zx-button-radius`, `--zx-panel-header-bg`.
|
|
1340
|
+
Reach for the least specific override that works: a global semantic token first, one of these
|
|
1341
|
+
second, an internal selector never. Component CSS declares many other `--zx-<component>-*`
|
|
1342
|
+
properties — those are internal geometry, not API, and will change without notice.
|
|
1315
1343
|
<!-- /doc -->
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zeyos/zx",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Dependency-free, accessible UI component library for ZeyOS business applications.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -30,13 +30,33 @@
|
|
|
30
30
|
"./dist/zx-compat.global.min.js"
|
|
31
31
|
],
|
|
32
32
|
"exports": {
|
|
33
|
-
".":
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
"./
|
|
33
|
+
".": {
|
|
34
|
+
"types": "./dist/types/index.d.ts",
|
|
35
|
+
"default": "./dist/zx.esm.js"
|
|
36
|
+
},
|
|
37
|
+
"./zeyos": {
|
|
38
|
+
"types": "./dist/types/zeyos/index.d.ts",
|
|
39
|
+
"default": "./dist/zx-zeyos.esm.js"
|
|
40
|
+
},
|
|
41
|
+
"./compat": {
|
|
42
|
+
"types": "./dist/types/compat/index.d.ts",
|
|
43
|
+
"default": "./dist/zx-compat.esm.js"
|
|
44
|
+
},
|
|
45
|
+
"./global": {
|
|
46
|
+
"types": "./dist/types/index.d.ts",
|
|
47
|
+
"default": "./dist/zx.global.js"
|
|
48
|
+
},
|
|
49
|
+
"./compat-global": {
|
|
50
|
+
"types": "./dist/types/compat/index.d.ts",
|
|
51
|
+
"default": "./dist/zx-compat.global.js"
|
|
52
|
+
},
|
|
38
53
|
"./zx.css": "./dist/zx.css",
|
|
39
54
|
"./zx.min.css": "./dist/zx.min.css",
|
|
55
|
+
"./src/*.js": {
|
|
56
|
+
"types": "./dist/types/*.d.ts",
|
|
57
|
+
"default": "./src/*.js"
|
|
58
|
+
},
|
|
59
|
+
"./styles/*": "./styles/*",
|
|
40
60
|
"./dist/*": "./dist/*",
|
|
41
61
|
"./package.json": "./package.json"
|
|
42
62
|
},
|
|
@@ -45,6 +65,9 @@
|
|
|
45
65
|
"style": "./dist/zx.css",
|
|
46
66
|
"files": [
|
|
47
67
|
"dist",
|
|
68
|
+
"dist/types",
|
|
69
|
+
"styles",
|
|
70
|
+
"src",
|
|
48
71
|
"docs/llms.md",
|
|
49
72
|
"docs/llms.txt",
|
|
50
73
|
"docs/api.json",
|
|
@@ -66,9 +89,14 @@
|
|
|
66
89
|
"build:site": "node tools/build-api.js && node tools/build-site.js",
|
|
67
90
|
"test": "node --test && node tests/lint-tokens.js",
|
|
68
91
|
"prepublishOnly": "npm test && npm run build",
|
|
69
|
-
"build:api": "node tools/build-api.js"
|
|
92
|
+
"build:api": "node tools/build-api.js",
|
|
93
|
+
"build:types": "tsc --project tsconfig.json",
|
|
94
|
+
"test:browser": "node tools/run-smoke.js"
|
|
70
95
|
},
|
|
71
96
|
"devDependencies": {
|
|
72
|
-
"esbuild": "latest"
|
|
73
|
-
|
|
97
|
+
"esbuild": "latest",
|
|
98
|
+
"playwright-core": "^1.62.1",
|
|
99
|
+
"typescript": "^7.0.2"
|
|
100
|
+
},
|
|
101
|
+
"types": "./dist/types/index.d.ts"
|
|
74
102
|
}
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import { installElementStorage } from './globals.js';
|
|
2
|
+
|
|
3
|
+
/** @typedef {(detail: Record<string, any>, event: Event) => unknown[]} LegacyArgumentMapper */
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {Object} LegacyEventMapping
|
|
6
|
+
* @property {string} type Zx event name.
|
|
7
|
+
* @property {LegacyArgumentMapper} [args] Legacy positional argument adapter.
|
|
8
|
+
* @property {(detail: Record<string, any>, event: Event) => boolean} [filter] Optional event filter.
|
|
9
|
+
*/
|
|
10
|
+
/** @typedef {Record<string, string|LegacyEventMapping>} LegacyEventMap */
|
|
11
|
+
/**
|
|
12
|
+
* @typedef {Object} WrapperConfiguration
|
|
13
|
+
* @property {LegacyEventMap} [events]
|
|
14
|
+
* @property {Record<string, string|Element|(() => Element|null)>} [ui]
|
|
15
|
+
* @property {Record<string, string|((value: unknown) => void)>} [setters]
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* MooTools-flavoured façade shared by compatibility component wrappers.
|
|
20
|
+
* @fires GxWrapper#legacy-event
|
|
21
|
+
*/
|
|
22
|
+
export class GxWrapper {
|
|
23
|
+
/** Fully qualified legacy class name shown in migration warnings. */
|
|
24
|
+
static legacyName = 'gx.ui.Container';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Initializes legacy options and callback-style `onEvent` options.
|
|
28
|
+
* Subclasses call `_attach()` after constructing their Zx implementation.
|
|
29
|
+
* @param {Record<string, any>} [options={}] Legacy options.
|
|
30
|
+
* @param {Record<string, any>} [defaults={}] Legacy defaults.
|
|
31
|
+
*/
|
|
32
|
+
constructor(options = {}, defaults = {}) {
|
|
33
|
+
warnClassOnce(this.constructor);
|
|
34
|
+
this.gx = this.constructor.legacyName;
|
|
35
|
+
this.options = { ...defaults, ...(options && typeof options === 'object' ? options : {}) };
|
|
36
|
+
this._events = new Map();
|
|
37
|
+
this._mappedListeners = [];
|
|
38
|
+
this._domAbort = new AbortController();
|
|
39
|
+
this._zx = null;
|
|
40
|
+
this._ui = {};
|
|
41
|
+
this._display = this._ui;
|
|
42
|
+
this._uiMap = {};
|
|
43
|
+
this._optionSetters = {};
|
|
44
|
+
this._destroyed = false;
|
|
45
|
+
for (const [key, value] of Object.entries(this.options)) {
|
|
46
|
+
if (/^on[A-Z]/.test(key) && typeof value === 'function') this.addEvent(key, value);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Subscribes a legacy callback.
|
|
52
|
+
* @param {string} type Legacy event name, with or without an `on` prefix.
|
|
53
|
+
* @param {Function} fn Callback receiving legacy positional arguments.
|
|
54
|
+
* @returns {this}
|
|
55
|
+
*/
|
|
56
|
+
addEvent(type, fn) {
|
|
57
|
+
if (typeof fn !== 'function') return this;
|
|
58
|
+
const name = normalizeEventName(type);
|
|
59
|
+
const handlers = this._events.get(name) ?? new Set();
|
|
60
|
+
handlers.add(fn);
|
|
61
|
+
this._events.set(name, handlers);
|
|
62
|
+
return this;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Removes one callback, or all callbacks of a type when omitted.
|
|
67
|
+
* @param {string} type Legacy event name.
|
|
68
|
+
* @param {Function} [fn] Callback to remove.
|
|
69
|
+
* @returns {this}
|
|
70
|
+
*/
|
|
71
|
+
removeEvent(type, fn) {
|
|
72
|
+
const name = normalizeEventName(type);
|
|
73
|
+
if (fn === undefined) this._events.delete(name);
|
|
74
|
+
else {
|
|
75
|
+
const handlers = this._events.get(name);
|
|
76
|
+
handlers?.delete(fn);
|
|
77
|
+
if (handlers?.size === 0) this._events.delete(name);
|
|
78
|
+
}
|
|
79
|
+
return this;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Fires a legacy event synchronously.
|
|
84
|
+
* @param {string} type Legacy event name.
|
|
85
|
+
* @param {unknown|unknown[]} [args=[]] Positional argument array or one argument.
|
|
86
|
+
* @returns {this}
|
|
87
|
+
* @fires GxWrapper#legacy-event
|
|
88
|
+
*/
|
|
89
|
+
fireEvent(type, args = []) {
|
|
90
|
+
const values = Array.isArray(args) ? args : [args];
|
|
91
|
+
const handlers = [...(this._events.get(normalizeEventName(type)) ?? [])];
|
|
92
|
+
for (const handler of handlers) handler.apply(this, values);
|
|
93
|
+
return this;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Applies multiple post-construction legacy options.
|
|
98
|
+
* @param {Record<string, unknown>} options Option values.
|
|
99
|
+
* @returns {this}
|
|
100
|
+
*/
|
|
101
|
+
setOptions(options) {
|
|
102
|
+
if (!options || typeof options !== 'object') return this;
|
|
103
|
+
for (const [name, value] of Object.entries(options)) this.setOption(name, value);
|
|
104
|
+
return this;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Applies one post-construction option through a declared setter.
|
|
109
|
+
* Unsupported dynamic options are retained on `options` and warn once per class/key.
|
|
110
|
+
* @param {string} option Legacy option name.
|
|
111
|
+
* @param {unknown} value New value.
|
|
112
|
+
* @returns {this}
|
|
113
|
+
*/
|
|
114
|
+
setOption(option, value) {
|
|
115
|
+
const name = String(option);
|
|
116
|
+
this.options[name] = value;
|
|
117
|
+
if (/^on[A-Z]/.test(name) && typeof value === 'function') {
|
|
118
|
+
this.addEvent(name, value);
|
|
119
|
+
return this;
|
|
120
|
+
}
|
|
121
|
+
const setter = this._optionSetters[name];
|
|
122
|
+
if (typeof setter === 'function') setter.call(this, value);
|
|
123
|
+
else if (typeof setter === 'string' && typeof this[setter] === 'function') this[setter](value);
|
|
124
|
+
else warnOptionOnce(this.constructor, name);
|
|
125
|
+
return this;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Returns the wrapped component root.
|
|
130
|
+
* @returns {Element|null}
|
|
131
|
+
*/
|
|
132
|
+
toElement() {
|
|
133
|
+
return this._zx?.toElement?.() ?? this._zx?.el ?? null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Resolves a legacy `_ui` key, falling back to the component root.
|
|
138
|
+
* @param {string} [key='root'] Legacy display key.
|
|
139
|
+
* @returns {Element|null}
|
|
140
|
+
*/
|
|
141
|
+
display(key = 'root') {
|
|
142
|
+
const name = key || 'root';
|
|
143
|
+
if (name === 'root') return this.toElement();
|
|
144
|
+
const mapped = this._uiMap[name];
|
|
145
|
+
if (typeof mapped === 'function') return mapped.call(this) ?? this.toElement();
|
|
146
|
+
if (typeof mapped === 'string') return this._zx?.refs?.[mapped] ?? this.toElement();
|
|
147
|
+
if (mapped?.nodeType === 1) return mapped;
|
|
148
|
+
return this._ui[name] ?? this.toElement();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Returns the supported legacy coordinate subset.
|
|
153
|
+
* @returns {{width: number, height: number, top: number, left: number}}
|
|
154
|
+
*/
|
|
155
|
+
getCoordinates() {
|
|
156
|
+
const rectangle = this.toElement()?.getBoundingClientRect?.();
|
|
157
|
+
return rectangle ? {
|
|
158
|
+
width: rectangle.width,
|
|
159
|
+
height: rectangle.height,
|
|
160
|
+
top: rectangle.top,
|
|
161
|
+
left: rectangle.left
|
|
162
|
+
} : { width: 0, height: 0, top: 0, left: 0 };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Updates one root style property.
|
|
167
|
+
* @param {string} property CSS property.
|
|
168
|
+
* @param {unknown} value CSS value.
|
|
169
|
+
* @returns {this}
|
|
170
|
+
*/
|
|
171
|
+
setStyle(property, value) {
|
|
172
|
+
const element = /** @type {HTMLElement|null} */ (this.toElement());
|
|
173
|
+
if (!element) return this;
|
|
174
|
+
if (String(property).startsWith('--') || String(property).includes('-')) {
|
|
175
|
+
element.style.setProperty(String(property), String(value));
|
|
176
|
+
} else {
|
|
177
|
+
element.style[property] = value;
|
|
178
|
+
}
|
|
179
|
+
return this;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Updates supported root coordinates.
|
|
184
|
+
* @param {Record<string, unknown>} coordinates Coordinate styles.
|
|
185
|
+
* @returns {this}
|
|
186
|
+
*/
|
|
187
|
+
setCoordinates(coordinates = {}) {
|
|
188
|
+
for (const key of ['width', 'height', 'top', 'right', 'bottom', 'left']) {
|
|
189
|
+
if (coordinates[key] == null) continue;
|
|
190
|
+
const value = typeof coordinates[key] === 'number' ? `${coordinates[key]}px` : coordinates[key];
|
|
191
|
+
this.setStyle(key, value);
|
|
192
|
+
}
|
|
193
|
+
return this;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Destroys the wrapped component and legacy event subscriptions.
|
|
198
|
+
* @returns {void}
|
|
199
|
+
*/
|
|
200
|
+
destroy() {
|
|
201
|
+
if (this._destroyed) return;
|
|
202
|
+
this._destroyed = true;
|
|
203
|
+
this._domAbort.abort();
|
|
204
|
+
const root = this.toElement();
|
|
205
|
+
if (root?.retrieve?.('com') === this) root.eliminate('com');
|
|
206
|
+
for (const [type, listener] of this._mappedListeners) this._zx?.off?.(type, listener);
|
|
207
|
+
this._mappedListeners.length = 0;
|
|
208
|
+
this._events.clear();
|
|
209
|
+
this._zx?.destroy?.();
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Attaches a constructed Zx component and its translation metadata.
|
|
214
|
+
* @param {any} component Wrapped Zx component.
|
|
215
|
+
* @param {WrapperConfiguration} [configuration={}] Event, display, and setter maps.
|
|
216
|
+
* @returns {this}
|
|
217
|
+
*/
|
|
218
|
+
_attach(component, configuration = {}) {
|
|
219
|
+
this._zx = component;
|
|
220
|
+
this._uiMap = { ...(configuration.ui ?? {}) };
|
|
221
|
+
this._optionSetters = { ...(configuration.setters ?? {}) };
|
|
222
|
+
const root = this.toElement();
|
|
223
|
+
if (root) {
|
|
224
|
+
installElementStorage(globalThis);
|
|
225
|
+
root.store?.('com', this);
|
|
226
|
+
this._ui.root = root;
|
|
227
|
+
for (const key of Object.keys(this._uiMap)) this._ui[key] = this.display(key);
|
|
228
|
+
}
|
|
229
|
+
for (const [legacyType, declaration] of Object.entries(configuration.events ?? {})) {
|
|
230
|
+
const mapping = typeof declaration === 'string' ? { type: declaration } : declaration;
|
|
231
|
+
const listener = (event) => {
|
|
232
|
+
const detail = event.detail ?? {};
|
|
233
|
+
if (mapping.filter && !mapping.filter.call(this, detail, event)) return;
|
|
234
|
+
const args = mapping.args ? mapping.args.call(this, detail, event) : [];
|
|
235
|
+
this.fireEvent(legacyType, args);
|
|
236
|
+
};
|
|
237
|
+
component.on?.(mapping.type, listener);
|
|
238
|
+
this._mappedListeners.push([mapping.type, listener]);
|
|
239
|
+
}
|
|
240
|
+
return this;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Adds a DOM listener owned by this wrapper.
|
|
245
|
+
* @param {EventTarget} target Event target.
|
|
246
|
+
* @param {string} type Event type.
|
|
247
|
+
* @param {EventListenerOrEventListenerObject} listener Listener.
|
|
248
|
+
* @param {AddEventListenerOptions} [options={}] Listener options.
|
|
249
|
+
* @returns {this}
|
|
250
|
+
*/
|
|
251
|
+
_listen(target, type, listener, options = {}) {
|
|
252
|
+
target.addEventListener(type, listener, { ...options, signal: this._domAbort.signal });
|
|
253
|
+
return this;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/** @param {Element|null} element @param {boolean} active @returns {void} */
|
|
257
|
+
_mirrorAct(element, active) {
|
|
258
|
+
element?.classList.toggle('act', Boolean(active));
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/** Generic legacy event dispatched by a mapped Zx event. @event GxWrapper#legacy-event */
|
|
263
|
+
|
|
264
|
+
/** @param {typeof GxWrapper} constructor @returns {void} */
|
|
265
|
+
function warnClassOnce(constructor) {
|
|
266
|
+
if (Object.hasOwn(constructor, '_compatWarned') && constructor._compatWarned) return;
|
|
267
|
+
Object.defineProperty(constructor, '_compatWarned', { configurable: true, value: true, writable: true });
|
|
268
|
+
console.warn(`${constructor.legacyName} is running on the Zx compat layer — migrate to ${replacementName(constructor.legacyName)}`);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/** @param {typeof GxWrapper} constructor @param {string} option @returns {void} */
|
|
272
|
+
function warnOptionOnce(constructor, option) {
|
|
273
|
+
if (!Object.hasOwn(constructor, '_compatOptionWarnings')) {
|
|
274
|
+
Object.defineProperty(constructor, '_compatOptionWarnings', { configurable: true, value: new Set() });
|
|
275
|
+
}
|
|
276
|
+
if (constructor._compatOptionWarnings.has(option)) return;
|
|
277
|
+
constructor._compatOptionWarnings.add(option);
|
|
278
|
+
console.warn(`${constructor.legacyName}.setOption("${option}") cannot update the wrapped Zx component dynamically`);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/** @param {string} name @returns {string} */
|
|
282
|
+
function replacementName(name) {
|
|
283
|
+
const className = name.split('.').at(-1);
|
|
284
|
+
return `zx.${className}`;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/** @param {string} type @returns {string} */
|
|
288
|
+
function normalizeEventName(type) {
|
|
289
|
+
const value = String(type ?? '');
|
|
290
|
+
return (/^on[A-Z]/.test(value) ? value.slice(2) : value).toLowerCase();
|
|
291
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { h, htmlEscape } from '../core/dom.js';
|
|
2
|
+
import { translate } from '../core/i18n.js';
|
|
3
|
+
|
|
4
|
+
/** @type {WeakMap<object, Map<unknown, unknown>>} */
|
|
5
|
+
const elementStorage = new WeakMap();
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Installs the small WeakMap-backed MooTools Element storage surface.
|
|
9
|
+
* Existing implementations are never overwritten.
|
|
10
|
+
* @param {typeof globalThis} [host=globalThis] Realm whose Element prototype is extended.
|
|
11
|
+
* @returns {void}
|
|
12
|
+
*/
|
|
13
|
+
export function installElementStorage(host = globalThis) {
|
|
14
|
+
const prototype = host?.Element?.prototype;
|
|
15
|
+
if (!prototype) return;
|
|
16
|
+
if (typeof prototype.store !== 'function') {
|
|
17
|
+
Object.defineProperty(prototype, 'store', {
|
|
18
|
+
configurable: true,
|
|
19
|
+
writable: true,
|
|
20
|
+
value: function store(key, value) {
|
|
21
|
+
let values = elementStorage.get(this);
|
|
22
|
+
if (!values) {
|
|
23
|
+
values = new Map();
|
|
24
|
+
elementStorage.set(this, values);
|
|
25
|
+
}
|
|
26
|
+
values.set(key, value);
|
|
27
|
+
return this;
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
if (typeof prototype.retrieve !== 'function') {
|
|
32
|
+
Object.defineProperty(prototype, 'retrieve', {
|
|
33
|
+
configurable: true,
|
|
34
|
+
writable: true,
|
|
35
|
+
value: function retrieve(key, fallback = null) {
|
|
36
|
+
let values = elementStorage.get(this);
|
|
37
|
+
if (values?.has(key)) return values.get(key);
|
|
38
|
+
if (arguments.length > 1) {
|
|
39
|
+
if (!values) {
|
|
40
|
+
values = new Map();
|
|
41
|
+
elementStorage.set(this, values);
|
|
42
|
+
}
|
|
43
|
+
values.set(key, fallback);
|
|
44
|
+
}
|
|
45
|
+
return fallback;
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
if (typeof prototype.eliminate !== 'function') {
|
|
50
|
+
Object.defineProperty(prototype, 'eliminate', {
|
|
51
|
+
configurable: true,
|
|
52
|
+
writable: true,
|
|
53
|
+
value: function eliminate(key) {
|
|
54
|
+
elementStorage.get(this)?.delete(key);
|
|
55
|
+
return this;
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Parses the legacy gx object-literal DOM dialect.
|
|
63
|
+
* `html` is intentionally treated as trusted legacy markup via `h.raw()`.
|
|
64
|
+
* @param {unknown} source Legacy tree value.
|
|
65
|
+
* @returns {Node|false} Parsed node, or false for unsupported input.
|
|
66
|
+
*/
|
|
67
|
+
export function parse(source) {
|
|
68
|
+
if (source && typeof source === 'object' && typeof source.toElement === 'function') {
|
|
69
|
+
return source.toElement() ?? false;
|
|
70
|
+
}
|
|
71
|
+
if (source && typeof source === 'object' && typeof source.nodeType === 'number') return source;
|
|
72
|
+
if (typeof source === 'string' || typeof source === 'number') return document.createTextNode(String(source));
|
|
73
|
+
if (!source || typeof source !== 'object' || Array.isArray(source)) return false;
|
|
74
|
+
|
|
75
|
+
const descriptor = source;
|
|
76
|
+
const props = {};
|
|
77
|
+
const events = [];
|
|
78
|
+
const reserved = new Set(['tag', 'html', 'styles', 'classes', 'child', 'children', '_adopt', 'events']);
|
|
79
|
+
for (const [key, value] of Object.entries(descriptor)) {
|
|
80
|
+
if (reserved.has(key)) continue;
|
|
81
|
+
if (/^on[A-Z]/.test(key) && typeof value === 'function') {
|
|
82
|
+
events.push([key.slice(2).toLowerCase(), value]);
|
|
83
|
+
} else if (key === 'class') {
|
|
84
|
+
props.class = value;
|
|
85
|
+
} else if (key === 'text') {
|
|
86
|
+
props.textContent = value == null ? '' : String(value);
|
|
87
|
+
} else {
|
|
88
|
+
props[key] = value;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (descriptor.styles && typeof descriptor.styles === 'object') props.style = descriptor.styles;
|
|
92
|
+
if (descriptor.classes != null) {
|
|
93
|
+
const classes = Array.isArray(descriptor.classes) ? descriptor.classes : [descriptor.classes];
|
|
94
|
+
props.class = [props.class, ...classes].flat().filter(Boolean);
|
|
95
|
+
}
|
|
96
|
+
const element = h(String(descriptor.tag ?? 'div'), props);
|
|
97
|
+
for (const [type, listener] of events) element.addEventListener(type, listener);
|
|
98
|
+
if (descriptor.events && typeof descriptor.events === 'object') {
|
|
99
|
+
for (const [type, listener] of Object.entries(descriptor.events)) {
|
|
100
|
+
if (typeof listener === 'function') element.addEventListener(type, listener);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (descriptor.html != null) element.append(h.raw(String(descriptor.html)));
|
|
104
|
+
appendParsed(element, descriptor._adopt);
|
|
105
|
+
appendParsed(element, descriptor.child);
|
|
106
|
+
if (Array.isArray(descriptor.children)) {
|
|
107
|
+
for (const child of descriptor.children) appendParsed(element, child);
|
|
108
|
+
} else if (descriptor.children && typeof descriptor.children === 'object') {
|
|
109
|
+
const names = [];
|
|
110
|
+
for (const [name, childSource] of Object.entries(descriptor.children)) {
|
|
111
|
+
const child = parse(childSource);
|
|
112
|
+
if (!child) continue;
|
|
113
|
+
element.append(child);
|
|
114
|
+
element[`_${name}`] = child.retrieve?.('com') ?? child;
|
|
115
|
+
names.push(name);
|
|
116
|
+
}
|
|
117
|
+
element.store?.('children', names);
|
|
118
|
+
}
|
|
119
|
+
return element;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Explicitly installs legacy globals and prototype conveniences.
|
|
124
|
+
* @param {typeof globalThis} [host=globalThis] Realm to modify.
|
|
125
|
+
* @returns {typeof globalThis} The supplied realm.
|
|
126
|
+
*/
|
|
127
|
+
export function installGlobals(host = globalThis) {
|
|
128
|
+
installElementStorage(host);
|
|
129
|
+
host.__ = parse;
|
|
130
|
+
const stringPrototype = host.String?.prototype;
|
|
131
|
+
if (stringPrototype && typeof stringPrototype.htmlSpecialChars !== 'function') {
|
|
132
|
+
Object.defineProperty(stringPrototype, 'htmlSpecialChars', {
|
|
133
|
+
configurable: true,
|
|
134
|
+
writable: true,
|
|
135
|
+
value: function htmlSpecialChars() { return htmlEscape(String(this)); }
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
const arrayPrototype = host.Array?.prototype;
|
|
139
|
+
if (arrayPrototype && typeof arrayPrototype.findBy !== 'function') {
|
|
140
|
+
Object.defineProperty(arrayPrototype, 'findBy', {
|
|
141
|
+
configurable: true,
|
|
142
|
+
writable: true,
|
|
143
|
+
value: function findBy(matcher, value) {
|
|
144
|
+
const predicate = typeof matcher === 'string' ? (item) => item?.[matcher] === value : matcher;
|
|
145
|
+
if (typeof predicate !== 'function') return null;
|
|
146
|
+
for (let index = this.length - 1; index >= 0; index -= 1) {
|
|
147
|
+
if (predicate(this[index]) === true) return this[index];
|
|
148
|
+
}
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
if (host._ === undefined) host._ = (key, args) => translate(String(key), args);
|
|
154
|
+
return host;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** @param {Node} parent @param {unknown} source @returns {void} */
|
|
158
|
+
function appendParsed(parent, source) {
|
|
159
|
+
if (source == null) return;
|
|
160
|
+
const list = Array.isArray(source) ? source : [source];
|
|
161
|
+
for (const item of list) {
|
|
162
|
+
const child = parse(item);
|
|
163
|
+
if (child) parent.append(child);
|
|
164
|
+
}
|
|
165
|
+
}
|