@zeyos/zx 2.1.0 → 2.2.1

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 (311) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/README.md +15 -0
  3. package/dist/types/compat/base.d.ts +156 -0
  4. package/dist/types/compat/globals.d.ts +20 -0
  5. package/dist/types/compat/index.d.ts +128 -0
  6. package/dist/types/compat/map/basic.d.ts +175 -0
  7. package/dist/types/compat/map/data.d.ts +102 -0
  8. package/dist/types/compat/map/date-time.d.ts +80 -0
  9. package/dist/types/compat/map/factory.d.ts +9 -0
  10. package/dist/types/compat/map/form.d.ts +98 -0
  11. package/dist/types/compat/map/helpers.d.ts +12 -0
  12. package/dist/types/compat/map/http.d.ts +36 -0
  13. package/dist/types/compat/map/layout.d.ts +106 -0
  14. package/dist/types/compat/map/options.d.ts +80 -0
  15. package/dist/types/compat/map/select.d.ts +95 -0
  16. package/dist/types/compat/map/stubs.d.ts +32 -0
  17. package/dist/types/compat/map/utilities.d.ts +37 -0
  18. package/dist/types/compat-entry.d.ts +5 -0
  19. package/dist/types/components/badge/badge.d.ts +46 -0
  20. package/dist/types/components/breadcrumb/breadcrumb.d.ts +200 -0
  21. package/dist/types/components/button/button.d.ts +42 -0
  22. package/dist/types/components/check-button/check-button.d.ts +98 -0
  23. package/dist/types/components/checklist/checklist.d.ts +143 -0
  24. package/dist/types/components/checklist/field-adapter.d.ts +5 -0
  25. package/dist/types/components/context-menu/context-menu.d.ts +130 -0
  26. package/dist/types/components/copy/copy-button.d.ts +48 -0
  27. package/dist/types/components/copy/copy-input.d.ts +91 -0
  28. package/dist/types/components/data-filter/data-filter.d.ts +250 -0
  29. package/dist/types/components/data-filter/filter-core.d.ts +52 -0
  30. package/dist/types/components/date-picker/date-picker.d.ts +137 -0
  31. package/dist/types/components/date-picker/date-range-picker.d.ts +350 -0
  32. package/dist/types/components/date-picker/field-adapter.d.ts +6 -0
  33. package/dist/types/components/date-picker/month-picker.d.ts +75 -0
  34. package/dist/types/components/date-picker/range-field-adapter.d.ts +7 -0
  35. package/dist/types/components/date-picker/time-picker.d.ts +127 -0
  36. package/dist/types/components/datebox/date-range-box.d.ts +218 -0
  37. package/dist/types/components/datebox/datebox.d.ts +160 -0
  38. package/dist/types/components/datebox/field-adapter.d.ts +5 -0
  39. package/dist/types/components/dialog/dialog.d.ts +284 -0
  40. package/dist/types/components/dropdown/dropdown.d.ts +118 -0
  41. package/dist/types/components/empty-state/empty-state.d.ts +38 -0
  42. package/dist/types/components/field/field.d.ts +231 -0
  43. package/dist/types/components/field-upload/field-adapter.d.ts +5 -0
  44. package/dist/types/components/field-upload/field-upload.d.ts +213 -0
  45. package/dist/types/components/fieldset/fieldset.d.ts +125 -0
  46. package/dist/types/components/finder/finder.d.ts +252 -0
  47. package/dist/types/components/form/form.d.ts +194 -0
  48. package/dist/types/components/groupbox/groupbox.d.ts +89 -0
  49. package/dist/types/components/layout/layout.d.ts +132 -0
  50. package/dist/types/components/loading/inline-loading.d.ts +86 -0
  51. package/dist/types/components/loading/progress-bar.d.ts +168 -0
  52. package/dist/types/components/loading/spinner.d.ts +34 -0
  53. package/dist/types/components/master-panel/master-panel.d.ts +112 -0
  54. package/dist/types/components/menu-button/menu-button.d.ts +142 -0
  55. package/dist/types/components/message/message.d.ts +212 -0
  56. package/dist/types/components/modal/modal.d.ts +105 -0
  57. package/dist/types/components/multi-value-editor/field-adapter.d.ts +5 -0
  58. package/dist/types/components/multi-value-editor/multi-value-editor.d.ts +66 -0
  59. package/dist/types/components/navigation-bar/navigation-bar.d.ts +229 -0
  60. package/dist/types/components/number-field/field-adapter.d.ts +5 -0
  61. package/dist/types/components/number-field/number-field.d.ts +292 -0
  62. package/dist/types/components/pagination/pagination.d.ts +267 -0
  63. package/dist/types/components/panel/panel.d.ts +194 -0
  64. package/dist/types/components/permission/permission.d.ts +93 -0
  65. package/dist/types/components/rating/field-adapter.d.ts +5 -0
  66. package/dist/types/components/rating/rating.d.ts +213 -0
  67. package/dist/types/components/search/search.d.ts +98 -0
  68. package/dist/types/components/select/field-adapter.d.ts +5 -0
  69. package/dist/types/components/select/filter.d.ts +11 -0
  70. package/dist/types/components/select/select.d.ts +235 -0
  71. package/dist/types/components/skeleton/skeleton.d.ts +97 -0
  72. package/dist/types/components/slider/field-adapter.d.ts +5 -0
  73. package/dist/types/components/slider/slider.d.ts +216 -0
  74. package/dist/types/components/split-view/split-view.d.ts +335 -0
  75. package/dist/types/components/stepper/stepper.d.ts +314 -0
  76. package/dist/types/components/stepper/steps.d.ts +131 -0
  77. package/dist/types/components/tabbox/tabbox.d.ts +230 -0
  78. package/dist/types/components/table/sort.d.ts +49 -0
  79. package/dist/types/components/table/table.d.ts +872 -0
  80. package/dist/types/components/tag-picker/field-adapter.d.ts +5 -0
  81. package/dist/types/components/tag-picker/tag-picker.d.ts +375 -0
  82. package/dist/types/components/timebox/field-adapter.d.ts +5 -0
  83. package/dist/types/components/timebox/timebox.d.ts +121 -0
  84. package/dist/types/components/toggle/field-adapter.d.ts +5 -0
  85. package/dist/types/components/toggle/toggle.d.ts +94 -0
  86. package/dist/types/components/toolbar/toolbar.d.ts +230 -0
  87. package/dist/types/components/tooltip/tooltip.d.ts +183 -0
  88. package/dist/types/components/tree/hierarchy.d.ts +113 -0
  89. package/dist/types/components/tree/tree.d.ts +383 -0
  90. package/dist/types/components/truncate/truncate.d.ts +61 -0
  91. package/dist/types/components/value-list/field-adapter.d.ts +5 -0
  92. package/dist/types/components/value-list/value-list.d.ts +143 -0
  93. package/dist/types/core/breakpoint.d.ts +68 -0
  94. package/dist/types/core/component.d.ts +115 -0
  95. package/dist/types/core/date.d.ts +50 -0
  96. package/dist/types/core/dom.d.ts +86 -0
  97. package/dist/types/core/export.d.ts +70 -0
  98. package/dist/types/core/fontawesome.d.ts +107 -0
  99. package/dist/types/core/format.d.ts +107 -0
  100. package/dist/types/core/http.d.ts +122 -0
  101. package/dist/types/core/i18n.d.ts +35 -0
  102. package/dist/types/core/icons.d.ts +137 -0
  103. package/dist/types/core/keyboard.d.ts +72 -0
  104. package/dist/types/core/position.d.ts +45 -0
  105. package/dist/types/core/storage.d.ts +46 -0
  106. package/dist/types/core/util.d.ts +117 -0
  107. package/dist/types/elements/define.d.ts +73 -0
  108. package/dist/types/elements/reflect.d.ts +27 -0
  109. package/dist/types/index.d.ts +79 -0
  110. package/dist/types/zeyos/connect.d.ts +65 -0
  111. package/dist/types/zeyos/form.d.ts +140 -0
  112. package/dist/types/zeyos/icons.d.ts +104 -0
  113. package/dist/types/zeyos/index.d.ts +12 -0
  114. package/dist/types/zeyos/modules.d.ts +110 -0
  115. package/dist/types/zeyos/query.d.ts +157 -0
  116. package/dist/types/zeyos/schema.d.ts +79 -0
  117. package/dist/types/zeyos/select.d.ts +167 -0
  118. package/dist/types/zeyos/table.d.ts +205 -0
  119. package/dist/zx-compat.esm.js +375 -39
  120. package/dist/zx-compat.esm.min.js +1 -1
  121. package/dist/zx-compat.global.js +372 -73
  122. package/dist/zx-compat.global.min.js +3 -3
  123. package/dist/zx-zeyos.esm.js +355 -49
  124. package/dist/zx-zeyos.esm.min.js +1 -1
  125. package/dist/zx.css +68 -6
  126. package/dist/zx.esm.js +359 -73
  127. package/dist/zx.esm.min.js +3 -3
  128. package/dist/zx.global.js +359 -73
  129. package/dist/zx.global.min.js +3 -3
  130. package/dist/zx.min.css +1 -1
  131. package/docs/api.json +48 -2
  132. package/docs/llms.md +31 -0
  133. package/package.json +37 -9
  134. package/src/compat/base.js +291 -0
  135. package/src/compat/globals.js +165 -0
  136. package/src/compat/index.js +84 -0
  137. package/src/compat/map/basic.js +464 -0
  138. package/src/compat/map/data.js +232 -0
  139. package/src/compat/map/date-time.js +177 -0
  140. package/src/compat/map/factory.js +45 -0
  141. package/src/compat/map/form.js +330 -0
  142. package/src/compat/map/helpers.js +46 -0
  143. package/src/compat/map/http.js +117 -0
  144. package/src/compat/map/layout.js +256 -0
  145. package/src/compat/map/options.js +267 -0
  146. package/src/compat/map/select.js +198 -0
  147. package/src/compat/map/stubs.js +26 -0
  148. package/src/compat/map/utilities.js +113 -0
  149. package/src/compat-entry.js +11 -0
  150. package/src/components/badge/badge.css +147 -0
  151. package/src/components/badge/badge.js +92 -0
  152. package/src/components/breadcrumb/breadcrumb.css +105 -0
  153. package/src/components/breadcrumb/breadcrumb.js +331 -0
  154. package/src/components/button/button.css +104 -0
  155. package/src/components/button/button.js +76 -0
  156. package/src/components/check-button/check-button.css +66 -0
  157. package/src/components/check-button/check-button.js +167 -0
  158. package/src/components/checklist/checklist.css +65 -0
  159. package/src/components/checklist/checklist.js +282 -0
  160. package/src/components/checklist/field-adapter.js +46 -0
  161. package/src/components/context-menu/context-menu.css +54 -0
  162. package/src/components/context-menu/context-menu.js +334 -0
  163. package/src/components/copy/copy-button.css +27 -0
  164. package/src/components/copy/copy-button.js +78 -0
  165. package/src/components/copy/copy-input.css +59 -0
  166. package/src/components/copy/copy-input.js +123 -0
  167. package/src/components/data-filter/data-filter.css +53 -0
  168. package/src/components/data-filter/data-filter.js +400 -0
  169. package/src/components/data-filter/filter-core.js +128 -0
  170. package/src/components/date-picker/date-picker.css +181 -0
  171. package/src/components/date-picker/date-picker.js +500 -0
  172. package/src/components/date-picker/date-range-picker.css +264 -0
  173. package/src/components/date-picker/date-range-picker.js +990 -0
  174. package/src/components/date-picker/field-adapter.js +36 -0
  175. package/src/components/date-picker/month-picker.js +223 -0
  176. package/src/components/date-picker/range-field-adapter.js +31 -0
  177. package/src/components/date-picker/time-picker.css +191 -0
  178. package/src/components/date-picker/time-picker.js +406 -0
  179. package/src/components/datebox/date-range-box.css +128 -0
  180. package/src/components/datebox/date-range-box.js +492 -0
  181. package/src/components/datebox/datebox.css +108 -0
  182. package/src/components/datebox/datebox.js +358 -0
  183. package/src/components/datebox/field-adapter.js +34 -0
  184. package/src/components/dialog/dialog.css +74 -0
  185. package/src/components/dialog/dialog.js +450 -0
  186. package/src/components/dropdown/dropdown.css +24 -0
  187. package/src/components/dropdown/dropdown.js +288 -0
  188. package/src/components/empty-state/empty-state.css +80 -0
  189. package/src/components/empty-state/empty-state.js +92 -0
  190. package/src/components/field/field.css +127 -0
  191. package/src/components/field/field.js +605 -0
  192. package/src/components/field-upload/field-adapter.js +21 -0
  193. package/src/components/field-upload/field-upload.css +108 -0
  194. package/src/components/field-upload/field-upload.js +475 -0
  195. package/src/components/fieldset/fieldset.css +37 -0
  196. package/src/components/fieldset/fieldset.js +307 -0
  197. package/src/components/finder/finder.css +98 -0
  198. package/src/components/finder/finder.js +509 -0
  199. package/src/components/form/form.css +32 -0
  200. package/src/components/form/form.js +302 -0
  201. package/src/components/groupbox/groupbox.css +51 -0
  202. package/src/components/groupbox/groupbox.js +199 -0
  203. package/src/components/layout/layout.css +117 -0
  204. package/src/components/layout/layout.js +136 -0
  205. package/src/components/loading/inline-loading.css +34 -0
  206. package/src/components/loading/inline-loading.js +140 -0
  207. package/src/components/loading/progress-bar.css +115 -0
  208. package/src/components/loading/progress-bar.js +259 -0
  209. package/src/components/loading/spinner.css +58 -0
  210. package/src/components/loading/spinner.js +57 -0
  211. package/src/components/master-panel/master-panel.css +51 -0
  212. package/src/components/master-panel/master-panel.js +203 -0
  213. package/src/components/menu-button/menu-button.css +49 -0
  214. package/src/components/menu-button/menu-button.js +294 -0
  215. package/src/components/message/message.css +122 -0
  216. package/src/components/message/message.js +499 -0
  217. package/src/components/modal/modal.css +47 -0
  218. package/src/components/modal/modal.js +203 -0
  219. package/src/components/multi-value-editor/field-adapter.js +44 -0
  220. package/src/components/multi-value-editor/multi-value-editor.css +56 -0
  221. package/src/components/multi-value-editor/multi-value-editor.js +170 -0
  222. package/src/components/navigation-bar/navigation-bar.css +130 -0
  223. package/src/components/navigation-bar/navigation-bar.js +450 -0
  224. package/src/components/number-field/field-adapter.js +40 -0
  225. package/src/components/number-field/number-field.css +77 -0
  226. package/src/components/number-field/number-field.js +545 -0
  227. package/src/components/pagination/pagination.css +95 -0
  228. package/src/components/pagination/pagination.js +481 -0
  229. package/src/components/panel/panel.css +123 -0
  230. package/src/components/panel/panel.js +336 -0
  231. package/src/components/permission/permission.css +47 -0
  232. package/src/components/permission/permission.js +198 -0
  233. package/src/components/rating/field-adapter.js +34 -0
  234. package/src/components/rating/rating.css +97 -0
  235. package/src/components/rating/rating.js +395 -0
  236. package/src/components/search/search.css +70 -0
  237. package/src/components/search/search.js +193 -0
  238. package/src/components/select/field-adapter.js +24 -0
  239. package/src/components/select/filter.js +39 -0
  240. package/src/components/select/select.css +180 -0
  241. package/src/components/select/select.js +663 -0
  242. package/src/components/skeleton/skeleton.css +76 -0
  243. package/src/components/skeleton/skeleton.js +139 -0
  244. package/src/components/slider/field-adapter.js +36 -0
  245. package/src/components/slider/slider.css +181 -0
  246. package/src/components/slider/slider.js +399 -0
  247. package/src/components/split-view/split-view.css +139 -0
  248. package/src/components/split-view/split-view.js +806 -0
  249. package/src/components/stepper/stepper.css +234 -0
  250. package/src/components/stepper/stepper.js +459 -0
  251. package/src/components/stepper/steps.js +131 -0
  252. package/src/components/tabbox/tabbox.css +281 -0
  253. package/src/components/tabbox/tabbox.js +484 -0
  254. package/src/components/table/sort.js +85 -0
  255. package/src/components/table/table.css +364 -0
  256. package/src/components/table/table.js +1711 -0
  257. package/src/components/tag-picker/field-adapter.js +35 -0
  258. package/src/components/tag-picker/tag-picker.css +169 -0
  259. package/src/components/tag-picker/tag-picker.js +837 -0
  260. package/src/components/timebox/field-adapter.js +24 -0
  261. package/src/components/timebox/timebox.css +51 -0
  262. package/src/components/timebox/timebox.js +276 -0
  263. package/src/components/toggle/field-adapter.js +24 -0
  264. package/src/components/toggle/toggle.css +66 -0
  265. package/src/components/toggle/toggle.js +150 -0
  266. package/src/components/toolbar/toolbar.css +81 -0
  267. package/src/components/toolbar/toolbar.js +511 -0
  268. package/src/components/tooltip/tooltip.css +36 -0
  269. package/src/components/tooltip/tooltip.js +420 -0
  270. package/src/components/tree/hierarchy.js +151 -0
  271. package/src/components/tree/tree.css +99 -0
  272. package/src/components/tree/tree.js +784 -0
  273. package/src/components/truncate/truncate.css +20 -0
  274. package/src/components/truncate/truncate.js +92 -0
  275. package/src/components/value-list/field-adapter.js +24 -0
  276. package/src/components/value-list/value-list.css +108 -0
  277. package/src/components/value-list/value-list.js +336 -0
  278. package/src/core/breakpoint.js +150 -0
  279. package/src/core/component.js +292 -0
  280. package/src/core/date.js +216 -0
  281. package/src/core/dom.js +260 -0
  282. package/src/core/export.js +204 -0
  283. package/src/core/fontawesome.js +177 -0
  284. package/src/core/format.js +283 -0
  285. package/src/core/http.js +246 -0
  286. package/src/core/i18n.js +83 -0
  287. package/src/core/icons.js +283 -0
  288. package/src/core/keyboard.js +240 -0
  289. package/src/core/position.js +177 -0
  290. package/src/core/storage.js +175 -0
  291. package/src/core/util.js +336 -0
  292. package/src/elements/define.js +715 -0
  293. package/src/elements/reflect.js +79 -0
  294. package/src/index.js +131 -0
  295. package/src/zeyos/connect.js +142 -0
  296. package/src/zeyos/form.js +254 -0
  297. package/src/zeyos/icons.js +92 -0
  298. package/src/zeyos/index.js +21 -0
  299. package/src/zeyos/modules.js +269 -0
  300. package/src/zeyos/query.js +199 -0
  301. package/src/zeyos/schema.js +474 -0
  302. package/src/zeyos/select.js +175 -0
  303. package/src/zeyos/table.js +272 -0
  304. package/styles/base.css +124 -0
  305. package/styles/icons.css +34 -0
  306. package/styles/tokens/dark.css +84 -0
  307. package/styles/tokens/density.css +9 -0
  308. package/styles/tokens/global.css +80 -0
  309. package/styles/tokens/modules.css +109 -0
  310. package/styles/tokens/semantic.css +49 -0
  311. 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": "Empty message; null resolves `table.empty`."
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,26 @@ 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. **Select-all covers the rendered rows, not the whole result set**;
781
+ call `showAll()` first to mean all of them. While growing, the table carries `aria-rowcount` and
782
+ each row an `aria-rowindex`, so assistive technology hears the real total rather than the size of
783
+ the batch.
784
+ - **Responsive stacking** — `responsive: false|'sm'|'md'|'lg'|'xl'` stacks each row into a card of
785
+ label/value pairs below that width, instead of leaving a horizontal scrollbar with the data
786
+ hidden behind it. The width measured is **the table's own container**, not the viewport, so this
787
+ triggers inside a narrow split pane on a wide screen. A column with `popin: false` leads the card
788
+ instead of becoming another labelled line — the identifying column usually wants that. Every cell
789
+ carries `data-label`, so the labels are there whether or not the option is on. `isStacked()`
790
+ reports the current state and `stackedchange {stacked}` fires on each crossing. Stacking drops
791
+ `display: table`, which drops the implicit ARIA roles, so the component states
792
+ `table`/`rowgroup`/`row`/`cell`/`columnheader` explicitly for exactly as long as the mode is on.
793
+ - **Empty state** — `emptyText` takes a string, a Node, or a function returning one, so an empty
794
+ table can carry an icon, an explanation, and the action that resolves it (`emptyState({…})`).
795
+ A function is re-called on every render, which is what to use when the placeholder holds controls.
773
796
  <!-- /doc -->
774
797
 
775
798
  <!-- doc:data-filter -->
@@ -1312,4 +1335,12 @@ colour literals and tier-1 references in component CSS.
1312
1335
  `--zx-radius-sm/md/lg/full`, the type scale, shadows, and motion.
1313
1336
  - **Tier 2** — the semantic set, the only tokens components may use: `--zx-color-*`,
1314
1337
  `--zx-control-*`, `--zx-focus-ring`, `--zx-overlay-shadow`.
1338
+ - **Component styling hooks** — a short, deliberately small set of component-level custom
1339
+ properties for restyling one component without touching its internals. Each falls back to the
1340
+ semantic token it replaces, so setting none of them changes nothing:
1341
+ `--zx-table-header-bg`, `--zx-table-row-hover-bg`, `--zx-table-row-selected-bg`,
1342
+ `--zx-table-border-color`, `--zx-button-radius`, `--zx-panel-header-bg`.
1343
+ Reach for the least specific override that works: a global semantic token first, one of these
1344
+ second, an internal selector never. Component CSS declares many other `--zx-<component>-*`
1345
+ properties — those are internal geometry, not API, and will change without notice.
1315
1346
  <!-- /doc -->
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeyos/zx",
3
- "version": "2.1.0",
3
+ "version": "2.2.1",
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
- ".": "./dist/zx.esm.js",
34
- "./zeyos": "./dist/zx-zeyos.esm.js",
35
- "./compat": "./dist/zx-compat.esm.js",
36
- "./global": "./dist/zx.global.js",
37
- "./compat-global": "./dist/zx-compat.global.js",
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
+ }