@zeyos/zx 4.1.0 → 4.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.
Files changed (83) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/DESIGN-SYSTEM.md +33 -3
  3. package/README.md +17 -5
  4. package/dist/types/components/app-icon/app-icon.d.ts +8 -1
  5. package/dist/types/components/aurora/aurora.d.ts +72 -0
  6. package/dist/types/components/calendar/calendar-model.d.ts +202 -0
  7. package/dist/types/components/calendar/calendar.d.ts +500 -0
  8. package/dist/types/components/card-view/card-view.d.ts +303 -0
  9. package/dist/types/components/card-view/record-card.d.ts +242 -0
  10. package/dist/types/components/kanban-view/kanban-view.d.ts +572 -0
  11. package/dist/types/components/table/table.d.ts +120 -31
  12. package/dist/types/components/table-view/table-view.d.ts +192 -0
  13. package/dist/types/components/view/record-view.d.ts +464 -0
  14. package/dist/types/index.d.ts +7 -0
  15. package/dist/types/zeyos/calendar.d.ts +91 -0
  16. package/dist/types/zeyos/icons.d.ts +19 -3
  17. package/dist/types/zeyos/index.d.ts +4 -0
  18. package/dist/types/zeyos/legacy-saved-views.d.ts +66 -0
  19. package/dist/types/zeyos/saved-views.d.ts +331 -0
  20. package/dist/types/zeyos/view.d.ts +305 -0
  21. package/dist/zx-zeyos.esm.js +4627 -1460
  22. package/dist/zx-zeyos.esm.min.js +1 -1
  23. package/dist/zx.css +2492 -410
  24. package/dist/zx.esm.js +4891 -115
  25. package/dist/zx.esm.min.js +3 -3
  26. package/dist/zx.global.js +4891 -115
  27. package/dist/zx.global.min.js +3 -3
  28. package/dist/zx.min.css +1 -1
  29. package/docs/api.json +1702 -191
  30. package/docs/llms.md +207 -20
  31. package/docs/llms.txt +16 -6
  32. package/package.json +1 -1
  33. package/src/components/app-icon/app-icon.css +140 -30
  34. package/src/components/app-icon/app-icon.js +32 -5
  35. package/src/components/app-rail/app-rail.css +11 -2
  36. package/src/components/app-sidebar/app-sidebar.css +11 -2
  37. package/src/components/aurora/aurora.css +143 -0
  38. package/src/components/aurora/aurora.js +146 -0
  39. package/src/components/avatar/avatar.css +1 -1
  40. package/src/components/button/button.css +34 -2
  41. package/src/components/calendar/calendar-model.js +402 -0
  42. package/src/components/calendar/calendar.css +567 -0
  43. package/src/components/calendar/calendar.js +1524 -0
  44. package/src/components/card/card.css +25 -3
  45. package/src/components/card-view/card-view.css +389 -0
  46. package/src/components/card-view/card-view.js +527 -0
  47. package/src/components/card-view/record-card.js +392 -0
  48. package/src/components/context-menu/context-menu.css +2 -2
  49. package/src/components/date-picker/date-picker.css +15 -3
  50. package/src/components/datebox/date-range-box.css +15 -2
  51. package/src/components/datebox/datebox.css +16 -3
  52. package/src/components/dropdown/dropdown.css +15 -3
  53. package/src/components/kanban-view/kanban-view.css +216 -0
  54. package/src/components/kanban-view/kanban-view.js +1228 -0
  55. package/src/components/launcher/launcher.css +18 -5
  56. package/src/components/master-panel/master-panel.css +14 -3
  57. package/src/components/menu-button/menu-button.css +2 -2
  58. package/src/components/message/message.css +15 -10
  59. package/src/components/modal/modal.css +15 -3
  60. package/src/components/navigation-bar/navigation-bar.css +17 -5
  61. package/src/components/panel/panel.css +10 -2
  62. package/src/components/select/select.css +20 -4
  63. package/src/components/sheet/sheet.css +4 -1
  64. package/src/components/table/table.css +80 -4
  65. package/src/components/table/table.js +322 -37
  66. package/src/components/table-view/table-view.css +18 -0
  67. package/src/components/table-view/table-view.js +347 -0
  68. package/src/components/tag-picker/tag-picker.css +15 -3
  69. package/src/components/tooltip/tooltip.css +15 -3
  70. package/src/components/view/record-view.css +114 -0
  71. package/src/components/view/record-view.js +770 -0
  72. package/src/index.js +15 -0
  73. package/src/zeyos/calendar.js +115 -0
  74. package/src/zeyos/icons.js +15 -8
  75. package/src/zeyos/index.js +13 -0
  76. package/src/zeyos/launcher.js +2 -2
  77. package/src/zeyos/legacy-saved-views.js +155 -0
  78. package/src/zeyos/saved-views.js +815 -0
  79. package/src/zeyos/table.js +3 -1
  80. package/src/zeyos/view.js +543 -0
  81. package/styles/icons.css +3 -10
  82. package/styles/tokens/semantic.css +74 -2
  83. package/styles/zx.css +6 -0
package/docs/llms.md CHANGED
@@ -75,11 +75,19 @@ Keep the 600 stop at 4.5:1 or better against white and the 400 stop at 4.5:1 aga
75
75
 
76
76
  The theme studio treats those stable IDs as complete recipes: selecting a preset also changes its
77
77
  density, neutral tint, radius, control height, base text size, font stack, and material strength.
78
- Its **Material** control is CSS-first (`Flat`, `Glass`, or `Deep glass`); only the deep setting uses
79
- backdrop blur, and components fall back to solid surfaces under reduced-transparency preferences.
78
+ Its **Material** control is CSS-first (`Flat`, `Glass`, or `Deep glass`). Aurora is the independent
79
+ ambient canvas layer; material controls how surfaces respond to that light. The shared recipe
80
+ applies to AppIcons, ordinary controls, persistent chrome, raised cards, and every transient overlay: Dropdown, ContextMenu,
81
+ MenuButton, AccountMenu, AppRail flyouts, Select, TagPicker, date pickers, tooltips, column menus,
82
+ and toasts use `--zx-color-overlay-surface`; Launcher, Modal, Dialog, and overlay Sheets use the
83
+ more opaque `--zx-color-overlay-panel`. A docked Sheet is layout rather than
84
+ an overlay and becomes opaque, as do dense data surfaces. Glass and Deep glass progressively raise
85
+ translucency, blur, highlights, and elevation; reduced-transparency preferences restore solid
86
+ surfaces.
80
87
 
81
88
  Define a product theme by overriding **semantic** tokens under a `[data-zx-theme="name"]` selector:
82
- `--zx-color-bg-page/surface/raised/control/hover/selected`, `--zx-color-border(-strong/-control)`,
89
+ `--zx-color-bg-page/surface/raised/control/hover/selected`, `--zx-color-glass-chrome/surface`,
90
+ `--zx-color-overlay-surface/panel/border`, `--zx-color-border(-strong/-control)`,
83
91
  `--zx-color-text(-muted/-placeholder)`, `--zx-color-accent(-hover)/on-accent`,
84
92
  `--zx-color-danger/warning/success/info(+ -bg)`, `--zx-focus-ring`, `--zx-control-height/-radius`,
85
93
  `--zx-space-*`, `--zx-radius-*`, `--zx-text-*`. Never use tier-1 palette tokens (`--zx-gray-*`,
@@ -119,8 +127,10 @@ bundles `@zeyos/client`; the application owns client creation, authentication, a
119
127
  ```js
120
128
  import { createZeyosClient } from '@zeyos/client';
121
129
  import {
122
- connect, dataFilterStateToFilters, zeyosForm, zeyosSelect, zeyosTable
130
+ connect, createLegacySavedViewTransport, createSavedViewRegistry,
131
+ dataFilterStateToFilters, legacySavedViewRequest, zeyosForm, zeyosSelect, zeyosTable, zeyosView
123
132
  } from '/assets/zx-zeyos.esm.js';
133
+ import { CardView, TableView } from '/assets/zx.esm.js';
124
134
 
125
135
  const client = createZeyosClient({ platform, auth });
126
136
  const api = connect(client, { locale: 'en-GB' });
@@ -138,6 +148,17 @@ const editor = zeyosForm(client, 'transactions', {
138
148
  });
139
149
  await editor.load(transactionId);
140
150
  await editor.save();
151
+
152
+ const records = zeyosView(client, 'transactions', TableView, {
153
+ fields: ['transactionnum', 'account', 'date', 'netamount', 'status'],
154
+ savedViews: {
155
+ transport: savedViewTransport,
156
+ scope: { userId: currentUserId, workspaceId: currentWorkspaceId }
157
+ }
158
+ });
159
+ await records.ready; // restores a compatible default before the first list request
160
+ await records.load();
161
+ await records.saveView({ name: 'My open invoices', setDefault: true });
141
162
  ```
142
163
 
143
164
  - `connect(client, { onError, locale })` returns `{client, list, get, create, update, reportError}`;
@@ -154,6 +175,21 @@ await editor.save();
154
175
  `{table, load(), setSearch(), setFilters(), loadMore(), count, page, hasMore, destroy()}`. Columns
155
176
  come from schema metadata, sorting uses `sortMode:'server'`, and page zero replaces rows while
156
177
  later pages append them using server `limit`/`offset` and `count`.
178
+ - `buildZeyosViewConfig(client, resource, opts)` resolves one schema to shared `ViewField[]`, a
179
+ complete projection, and view options. `zeyosView(client, resource, CardView|TableView|KanbanView,
180
+ opts)` returns `{view, load(), setSearch(), setFilters(), loadMore(), count, page, hasMore,
181
+ savedViews, ready, restoreDefault(), captureView(), saveView(), applyView(), destroy()}` with
182
+ paging and server sort. Title, preview, group, column, and swim-lane fields stay in the projection
183
+ even when hidden. Without `savedViews`, the host may persist `view.getViewState()` directly.
184
+ - `createSavedViewRegistry(transport, {userId,workspaceId,resource})` coordinates private named
185
+ Table/Card/Kanban configurations as one versioned, atomically replaced document. Entries capture
186
+ component state plus filters/search and an explicit presentation type, never records, selection,
187
+ pages, callbacks, or DOM. Duplicate names, defaults, migration, type mismatch, failed writes, and
188
+ teardown are explicit. `workspaceId:null` addresses ZeyOS's base workspace.
189
+ - `createLegacySavedViewTransport(request)` reuses the authenticated `fields`, `fields_save`, and
190
+ `fields_remove` endpoints through one reserved, namespaced `userfields` record.
191
+ `legacySavedViewRequest(PG.load.bind(PG))` is the optional Promise bridge for a legacy host. The
192
+ scope's `userId` is intentionally not sent to the endpoint; the server session remains authority.
157
193
 
158
194
  Field mapping is schema-driven: entity/FK → async `zxselect`; enum/list → `optionlist` or native
159
195
  `select`; indexed `date` bigint → `date`; other date/time bigints → `datetime`; money/price/numeric
@@ -194,8 +230,8 @@ registerModules({ tickets: '#f04639', 'my-fork': { label: 'My Fork', icon: 'zeyo
194
230
  - `moduleIcon(name, {size, label, standard})` → the bare glyph; `standard: true` uses the stock
195
231
  Font Awesome fallback, for pages on a kit without the ZeyOS uploads.
196
232
  - `moduleChip(name, {size, iconSize, label, title, standard})` → `<span class="zx-module-icon">`
197
- with the glyph on the module colour. The glyph colour is whichever of ZeyOS's two foregrounds
198
- (`#ffffff`, `#141414`) contrasts better, via `moduleGlyphColor(hex)`.
233
+ with a centred white glyph on the module colour. `moduleGlyphColor(hex)` remains available as a
234
+ separate contrast helper for non-AppIcon integrations that deliberately choose their foreground.
199
235
  - `moduleInfo`/`moduleColor`/`moduleIconName`/`moduleKeys`/`normalizeModuleName` read the config.
200
236
  Module, entity, and API resource names are all accepted, case-insensitively, with dots and
201
237
  underscores normalized: `'transactions.billing'`, `'invoices'`, and `'Transactions Billing'` all
@@ -453,25 +489,34 @@ permissions, recent-history/cache policy, and the route or command ultimately in
453
489
  <!-- doc:app-icon -->
454
490
  ### AppIcon — application identity
455
491
 
456
- A fixed-size, product-agnostic application tile. It keeps glyph geometry stable in launchers,
492
+ A fixed-size, product-agnostic application identity. It keeps glyph geometry stable in launchers,
457
493
  sidebars, entity results, and recent-item rows while identity colour and material remain explicit
458
494
  options rather than application globals.
459
495
 
460
496
  - **Options** — `icon` (bundled name, Node, or lazy renderer), `color`, `size: 36`,
461
- `iconSize: '52%'`, `label`, `badge`, `selected: false`, `glass: false|'subtle'|'strong'`, and
497
+ `iconSize: '52%'`, `label`, `badge`, `selected: false`, `shape: 'tile'|'circle'`,
498
+ `glass: false|'subtle'|'strong'`, and
462
499
  additional `class` names.
463
500
  - **Methods** — `set(partialOptions)` updates the existing root; `destroy()` restores an enhanced
464
501
  target. `appIcon(options)` is the element-returning convenience factory.
465
502
  - **ZeyOS preset** — import `zeyosAppIcon(module, options)` from `@zeyos/zx/zeyos`. Every current
466
503
  ZeyOS application/module has a canonical colour and icon mapping in `src/zeyos/modules.js`;
467
- entity names and aliases resolve through the same catalogue. `moduleChip()` is the compatible
468
- alias. An application may call `useZeyosIcons()` for the existing custom glyph kit or inject an
469
- offline `icon` override; Zx never loads a network asset automatically.
470
- - **Material** — subtle glass is a CSS border/highlight treatment; strong glass progressively adds
471
- `backdrop-filter`. `prefers-reduced-transparency` and browsers without support receive a solid
472
- tile. The glyph and label semantics do not depend on the visual treatment.
473
- - **Accessibility** — a non-empty `label` gives the root `role="img"`; otherwise it is decorative.
474
- The badge is visual supplementary status, so include its meaning in `label` when it matters.
504
+ entity names and aliases resolve through the same catalogue. `zeyosAppIcon()` defaults to a
505
+ circular application vessel; `moduleChip()` deliberately keeps a compact tile for entity and
506
+ record identity. An application may call `useZeyosIcons()` for the existing custom glyph kit or
507
+ inject an offline `icon` override; Zx never loads a network asset automatically.
508
+ - **Shape and material** — core `AppIcon` keeps the backward-compatible `tile` default. Application
509
+ launchers and rails use `circle`; entity rows, recent records, badges, and tables use `tile`.
510
+ Subtle glass uses a quiet colour bloom, rim, reflection, and shadow without per-icon backdrop
511
+ blur; strong glass increases those material cues without changing geometry.
512
+ `prefers-reduced-transparency` receives a solid surface. Glyphs remain crisp white and
513
+ geometrically centred in either built-in SVG or Font Awesome form. Enabled interactive hosts
514
+ respond through rim, light, and shadow without translating the icon; decorative and disabled
515
+ identities do not imply clickability.
516
+ - **Accessibility** — on an owned identity surface, a non-empty `label` gives the root
517
+ `role="img"`; otherwise it is decorative. When AppIcon enhances a button or link, it preserves
518
+ that control's native role and uses `label` only as its accessible name. The badge is visual
519
+ supplementary status, so include its meaning in `label` when it matters.
475
520
  <!-- /doc -->
476
521
 
477
522
  <!-- doc:avatar -->
@@ -751,8 +796,8 @@ messages rather than the page, and uses the translucent glass surface tokens wit
751
796
  <!-- doc:modal -->
752
797
  ### Modal
753
798
 
754
- Thin overlay on the native `<dialog>` element. The backdrop uses `--zx-color-bg-backdrop`, and
755
- focus returns to the opener on close.
799
+ Thin overlay on the native `<dialog>` element. The surface uses the shared, reading-sized overlay
800
+ panel material; the backdrop uses `--zx-color-bg-backdrop`, and focus returns to the opener on close.
756
801
 
757
802
  - **Options** — `content`, `width: 'auto'`, `closable: true`, `lightDismiss: false`,
758
803
  `destroyOnClose: false`.
@@ -780,7 +825,8 @@ Structured modal: a header with title and close button, a body, and footer butto
780
825
  Edge-anchored surface — a `Dialog` attached to one side of the viewport instead of floating in the
781
826
  middle. One primitive covers both side sheets and mobile drawers, which differ mainly in the edge they take, so
782
827
  `side` replaces what would otherwise be two components. Inherits the whole Dialog anatomy: title,
783
- footer buttons, switchable views, and focus restoration.
828
+ footer buttons, switchable views, focus restoration, and the reading-sized overlay panel material.
829
+ When a Dock adopts it, the Sheet becomes an opaque layout surface instead.
784
830
 
785
831
  - **Options** — `side: 'end'` (`'start' | 'end' | 'top' | 'bottom'`, logical), `modal: true`
786
832
  (`true | 'trap-focus' | false`), `backdrop: 'dim'` (`'dim' | 'blur' | 'none'`), `size: null`
@@ -969,7 +1015,7 @@ range select.
969
1015
  `hierarchy: false|{parentId, column?, expanded?}`. Locale, currency, unit, and decimals may be
970
1016
  row callbacks; values remain numbers in row data while display formatting is locale-aware.
971
1017
  - **Methods** — `setData()`, `addData()`, `updateRow(id,row)`, `removeRow(id)`, `getRow(id)`,
972
- `getData()`, `empty()`, `setSort(id,dir)`, `getSelection()`, `setSelection(ids)`,
1018
+ `getData()`, `empty()`, `setSort(id,dir)`, `getSort()`, `clearSort()`, `getSelection()`, `setSelection(ids)`,
973
1019
  `clearSelection()`, `setLoading(bool)` (busy/skeleton state — dims rows, shows an indeterminate
974
1020
  top bar, sets `aria-busy`, and is cleared by the next `setData`).
975
1021
  - **Events** — `rowclick {row, id, index, event}`, `rowdblclick`, `sort {id, dir}`,
@@ -1049,6 +1095,82 @@ range select.
1049
1095
  - **Column visibility** — `columnVisibility: true` renders a chooser; `hiddenColumns` seeds it.
1050
1096
  `getVisibleColumns()`, `getHiddenColumns()`, `setColumnVisible()`, and `toggleColumn()` preserve
1051
1097
  caller column order and never allow the final visible column to disappear.
1098
+ - **Column order** — `columnReorder: true` adds up/down buttons to the chooser and ArrowUp/
1099
+ ArrowDown support while a column option is focused. `getColumns()`, `setColumns()`,
1100
+ `getColumnOrder()`, `setColumnOrder()`, and `moveColumn()` reconcile stale saved IDs and keep
1101
+ hidden/sorted state attached to stable column IDs. `columnorderchange {order}` fires after a
1102
+ user or non-silent programmatic change.
1103
+ <!-- /doc -->
1104
+
1105
+ <!-- doc:table-view -->
1106
+ ### TableView and the shared RecordView contract
1107
+
1108
+ `TableView` is the record-oriented entry point above the lower-level `Table`. It extends
1109
+ `RecordView` and composes one complete `Table`, so editing, hierarchy, growing, row movement,
1110
+ responsive stacking, loading, and advanced table events remain available through `getTable()`.
1111
+
1112
+ - **Shared fields** — every view accepts `fields: [{id,label,get?,render?,sortValue?,sortable?,
1113
+ visible?,duplicate?,type?,view?}]`. Primitive rendered values become text; an explicit Node is
1114
+ adopted. `field.view.table` carries Table-only column keys without changing CardView or KanbanView.
1115
+ - **Shared options** — `data`, `recordId: 'ID'`, `sort`, `sortMode: 'local'|'server'`,
1116
+ `selectable: false|'single'|'multi'`, `selection`, `fieldOrder`, `hiddenFields`,
1117
+ `fieldControls: true`, and `emptyText`. At least one field remains visible.
1118
+ - **Shared methods** — `setData()`, `addData()`, `updateRecord()`, `removeRecord()`, `getRecord()`,
1119
+ `getData()`, field getters/order/visibility methods, `setSort()`/`getSort()`, selection methods,
1120
+ `setLoading()`, `getViewState()`, and `setViewState()`.
1121
+ - **Portable state** — `{version:1,fieldOrder,hiddenFields,sort}` is JSON-safe configuration only.
1122
+ Records, selection, DOM nodes, rendered values, and callbacks are never serialized. A state from
1123
+ one record view may seed another; unknown fields are ignored and new fields append.
1124
+ - **TableView composition** — `table: {...}` forwards every advanced Table option, while shared
1125
+ fields/data/id/sort/selection/visibility/order win on overlaps. `getTable()` returns the composed
1126
+ instance. Row events become shared `recordclick`/`recorddblclick` details.
1127
+ - **Events** — `recordclick`, `recorddblclick`, `datachange`, `selectionchange`, `sortchange`,
1128
+ `fieldvisibilitychange`, `fieldorderchange`, and `statechange {state,reason}`.
1129
+ <!-- /doc -->
1130
+
1131
+ <!-- doc:card-view -->
1132
+ ### CardView
1133
+
1134
+ A responsive semantic record collection using the same fields, records, sort, selection, and
1135
+ portable state as TableView and KanbanView.
1136
+
1137
+ - **Options** — the shared RecordView options plus `titleField`, `subtitleField`, `preview`,
1138
+ `previewAlt`, `link`, `actions`, `groupBy`, `groupOrder`, `minCardWidth`, `maxColumns`,
1139
+ `variant: 'outlined'|'raised'|'filled'`, `loadingCount`, `headingLevel`, and `label`.
1140
+ - **Cards** — each record is a focusable `<li>` with a native title link, separate secondary
1141
+ actions, optional native multi-select checkbox, labelled metadata, and `aria-selected` state.
1142
+ Title/subtitle fields are not duplicated in metadata unless their field sets `duplicate:true`.
1143
+ - **Media and URLs** — `preview` accepts a field ID or callback returning an image URL,
1144
+ `{src,alt,fit}`, Node, or null. Executable/non-image schemes are rejected and image failures show
1145
+ a stable fallback. Title/action links are normalized; `_blank` links receive `noopener`.
1146
+ - **Grouping and layout** — `groupBy` is a field ID or callback; `groupOrder` may deliberately show
1147
+ empty groups. Cards reflow through container queries and may cap columns without viewport logic.
1148
+ - **Interaction** — Enter activates a focused card; Space toggles selection. Links, actions,
1149
+ selection controls, and selected text do not also activate the card. `recordaction` adds the
1150
+ resolved action to the shared event set.
1151
+ <!-- /doc -->
1152
+
1153
+ <!-- doc:kanban-view -->
1154
+ ### KanbanView
1155
+
1156
+ A semantic record board with explicit or data-derived columns, optional swim lanes, shared record
1157
+ cards, advisory work-in-progress limits, and equivalent pointer/programmatic/keyboard movement.
1158
+
1159
+ - **Options** — the shared RecordView options plus `columnBy`, `columns`, `swimlaneBy`,
1160
+ `swimlanes`, card title/subtitle/preview/link/actions, `moveMode: 'local'|'external'`,
1161
+ `columnOrder`, `swimlaneOrder`, collapsed IDs, `showCounts`, `showEmptyColumns`, `variant`, and
1162
+ `label`. Column/lane descriptors use stable `id`, `label`, optional stored `value`, and `accept`;
1163
+ columns may also declare an advisory `limit`.
1164
+ - **Methods** — configure/get/move columns and swim lanes, collapse either axis, and
1165
+ `moveRecord(id,{column?,lane?,index?})`. Board-specific order/collapse joins the common view
1166
+ state; record order, movement history, and selection do not.
1167
+ - **Movement boundary** — `recordmove {record,id,from,to,column,swimlane,limitExceeded}` is
1168
+ cancelable and fires before a commit/request. `accept` is a hard eligibility gate; WIP is visual
1169
+ advice. Local mode clones the moved record and never mutates host data. External mode emits only,
1170
+ for server validation and optimistic rollback owned by ZeyOS.
1171
+ - **Keyboard** — focus the move handle; Enter/Space grabs or drops, Escape cancels, Left/Right
1172
+ chooses a column, Up/Down chooses a bucket-relative position, and Alt+Up/Down chooses a swim
1173
+ lane. A polite live region announces every target and result. Native drag is an enhancement.
1052
1174
  <!-- /doc -->
1053
1175
 
1054
1176
  <!-- doc:grid -->
@@ -1100,6 +1222,47 @@ code, so future adapters do not change the public `Chart` API.
1100
1222
  for assistive technology; `'visible'` makes exact values part of the interface.
1101
1223
  <!-- /doc -->
1102
1224
 
1225
+ <!-- doc:calendar -->
1226
+ ### Calendar
1227
+
1228
+ A dependency-free scheduling surface for operational events. One component owns agenda, day,
1229
+ week, month, and year views; local-time range math; timed collision columns; multi-day lanes;
1230
+ loading and empty states; pointer range selection; and equivalent pointer/keyboard move and resize
1231
+ interactions. Visible ranges use an exclusive `end`, so `dateschange` can be passed directly to a
1232
+ windowed loader.
1233
+
1234
+ - **Options** — `events`, `date`, `view: 'month'`, `views: ['agenda','day','week','month','year']`,
1235
+ `locale`, `weekStart: 1`, `weekNumbers: true`, `workweek: false`, `agendaDays: 14`,
1236
+ `slotDuration: 30`, `slotMinTime: 0`, `slotMaxTime: 1440`, `scrollTime: 480`, `eventLimit: 3`,
1237
+ `selectable: false`, `editable: false`, `eventStartEditable`, `eventDurationEditable`,
1238
+ `optimistic: true`, `nowIndicator: true`, `now`, `disabled`, and `renderEvent(event)`.
1239
+ - **Event input** — generic records default to `{id,title,start,end,allDay,color,location,editable,
1240
+ durationEditable}`. Every field has a string-or-function reader (`eventId`, `eventTitle`,
1241
+ `eventStart`, …); numeric dates use `dateUnit: 'milliseconds'|'seconds'`. Inputs are normalized
1242
+ atomically, duplicate IDs and inverted intervals throw, Dates are copied, text is never parsed as
1243
+ HTML, and suspicious CSS colour strings are ignored.
1244
+ - **Methods** — `getEvents()`, `setEvents()`, `addEvent()`, `updateEvent()`, `removeEvent()`,
1245
+ `getDate()`, `setDate()`, `getView()`, `setView()`, `prev()`, `next()`, `today()`,
1246
+ `setLoading()`, `focusEvent()`, `destroy()`.
1247
+ - **Events** — `dateschange {view,start,end}`, `viewchange {view,oldView}`, `eventclick`,
1248
+ `eventdblclick`, `dateclick {date,allDay,view}`, `select {start,end,allDay,view}`,
1249
+ `new {date,view}`, `moreclick {date,count}`, `eventschange {events}`, and cancelable
1250
+ `eventchange {event,oldEvent,action,delta,jsEvent,revert}`.
1251
+ - **Persistence** — `optimistic: true` updates local events before `eventchange`; call the
1252
+ idempotent, version-guarded `detail.revert()` if persistence fails. With `optimistic: false`, the
1253
+ component stays controlled: it emits the proposal without changing data, and the application
1254
+ applies an accepted response through `updateEvent()` or `setEvents()`. Zx does not send requests.
1255
+ - **Keyboard** — time slots and date grids use arrow-key navigation, Home/End, PageUp/PageDown,
1256
+ Enter/Space activation, and roving focus. On an editable event, Space grabs it, arrows propose a
1257
+ slot/day move (or change the focused resize grip), Space drops, and Escape cancels. Live status
1258
+ text announces the edit and its proposed times.
1259
+ - **ZeyOS binding** — `zeyosCalendar()` maps current appointment occurrence rows (`ID`, `name`,
1260
+ `location`, `color`, `datefrom`, `dateto`, and association text). `calendarEventToZeyosPatch()`
1261
+ returns the base `ID` with Unix-second dates. The current server already expands recurrence into
1262
+ the requested window; recurrence rules, DAV synchronization, permissions, and routes therefore
1263
+ remain in ZeyOS instead of being reimplemented in core.
1264
+ <!-- /doc -->
1265
+
1103
1266
  <!-- doc:data-filter -->
1104
1267
  ### DataFilter
1105
1268
 
@@ -1403,6 +1566,30 @@ so buttons and links in its action, content, and footer regions remain valid sib
1403
1566
  container query when the card itself becomes narrow.
1404
1567
  <!-- /doc -->
1405
1568
 
1569
+ <!-- doc:aurora -->
1570
+ ### Aurora
1571
+
1572
+ A decorative, CSS-driven ambient light field for an existing application surface. Aurora enhances
1573
+ the target in place: it does not wrap, replace, or relocate the surface's content, and it does not
1574
+ own routing, overlays, or interaction. Its gradients sit behind the target's children, so raised
1575
+ Cards and other Liquid Glass surfaces can pick up the underlying colour naturally.
1576
+
1577
+ - **Options** — `preset: 'source'|'confluence'|'horizon'|'diagonal'|'edge'|'curtain'`,
1578
+ `colors: []` (zero to four concrete CSS colours),
1579
+ `intensity: 'subtle'|'balanced'|'vivid'`.
1580
+ - **Methods** — `setPreset(preset)`, `setColors(colors)`, `setIntensity(intensity)`.
1581
+ - **Palette** — an empty list uses semantic accent, info, success, and warning colours. One colour
1582
+ fills all four fields; two alternate; three repeat the second colour in the fourth field; four
1583
+ map directly. Network/image-bearing CSS values and palettes longer than four are rejected.
1584
+ - **Geometry** — Source starts at the leading chrome; Confluence meets from the corners; Horizon
1585
+ forms a shallow top band; Diagonal crosses the surface; Edge preserves a neutral centre; Curtain
1586
+ descends as four elongated fields. All use ordinary CSS gradients without animation.
1587
+ - **Lifecycle** — `destroy()` removes an owned root or restores every original attribute and child
1588
+ of an enhanced target. Content is never rebuilt when a setter changes the effect.
1589
+ - **Accessibility** — the light field is pointer-transparent and purely decorative. Forced-colour
1590
+ and reduced-transparency preferences remove it entirely.
1591
+ <!-- /doc -->
1592
+
1406
1593
  <!-- doc:panel -->
1407
1594
  ### Panel / MasterPanel
1408
1595
 
package/docs/llms.txt CHANGED
@@ -21,21 +21,26 @@ exports and as `window.zx.*`.
21
21
  - [Contributor contract](https://github.com/zeyos/zx/blob/main/AGENTS.md): conventions for editing component source (token rules, render-time state, listeners, ARIA/APG).
22
22
 
23
23
  ## Components
24
- - Inputs and identity: `button()`, `buttonGroup()`, `badge()`/`badgeGroup()`, `CheckButton`,
25
- `Toggle`, `Search`, `AppIcon` (+ `zeyosAppIcon()` presets), `Avatar`,
24
+ - Inputs: `button()`, `buttonGroup()`, `badge()`/`badgeGroup()`, `CheckButton`,
25
+ `Toggle`, `Search`,
26
26
  `Select` (APG combobox; `filter: false|'local'|async`; priority, status, permission, and
27
27
  `Select.entity()` presets), `Checklist`, `TagPicker` (icon/color identity), `NumberField`, `Rating`, `Slider`,
28
28
  `copyButton()`/`CopyInput`, `DatePicker`, `MonthPicker`, `TimePicker`,
29
29
  `Datebox`/`DateTimeBox`, `DateRangePicker`/`DateRangeBox`, `Timebox`.
30
30
  - Overlays: `Message` (toasts/inline/progress), `Modal`, `Dialog` (+ `alert`/`confirm`/`prompt` Promises), `Sheet` (edge-anchored Dialog; `side`, `modal: true|'trap-focus'|false`, `backdrop`, `snap` detents, swipe-to-dismiss), `SheetStack` (drill-down; `stack`/`cascade`), `Dropdown`, `MenuButton`, `ContextMenu` (right-click; `selector` for per-row menus), `Tooltip`/`tooltip()`/`describe()`.
31
31
  - Data: `Table` (sortable, selectable, sticky header, opt-in inline cell/row editing, row reorder,
32
- column visibility), `Grid` with single-click editing and `Grid.BillingItems()`, engine-neutral
33
- `Chart` with injected `ChartJsAdapter`, backend-neutral nested `Filter`, simple client-side
32
+ column visibility), shared `RecordView` state through `TableView`, `CardView`, and `KanbanView`,
33
+ `Grid` with single-click editing and `Grid.BillingItems()`, engine-neutral
34
+ `Chart` with injected `ChartJsAdapter`, `Calendar` (agenda/day/week/month/year, pointer/keyboard
35
+ drag and resize, `optimistic: true|false`), backend-neutral nested `Filter`, simple client-side
34
36
  `DataFilter`, `Pagination` (+ pure `paginationRange()`), `TreeView` (APG tree),
35
37
  `Finder` (Miller columns).
36
38
  - Forms: `Form`, `Fieldset`, `Questionnaire` (guided one-question-at-a-time flow with branching), `Field` (type registry), `ValueList`, `MultiValueEditor`, `FieldUpload`.
37
39
  - Layout: `stack()`/`grid()`/`aspect()` (spacing, an intrinsically reflowing grid, a fixed-ratio
38
- box), `Groupbox`, `Card` (media/title link/actions/footer), `Panel` (header/footer action
40
+ box), `Groupbox`, `Card` (media/title link/actions/footer), `AppIcon` (generic tile geometry;
41
+ circular `zeyosAppIcon()` application presets and compact `moduleChip()` identities),
42
+ `Aurora` (one- to four-colour ambient
43
+ canvas light; shared material roles govern chrome and overlays), `Avatar`, `Panel` (header/footer action
39
44
  buttons), `MasterPanel`, `Tabbox`
40
45
  (`variant: 'divided'|'bracket'|'line'|'segmented'`), `NavigationBar`, `AppSidebar` (expanded and
41
46
  minimized vertical states plus horizontal rail layouts), `AccountMenu`, `Launcher`,
@@ -47,11 +52,16 @@ exports and as `window.zx.*`.
47
52
  - Helpers: `formatNumber`/`formatCurrency`/`formatPercent`/`formatFileSize`/`formatRelativeTime` (locale-aware, cached `Intl`), `debounce`/`throttle`, `groupBy`/`sortBy`/`uniqueBy`, `escapeRegExp`/`highlightMatch`, `storage()` (namespaced JSON, degrades to memory), `toCsv`/`downloadBlob`/`copyToClipboard`, `truncate()`/`isTruncated()` (line clamp with the full value on hover).
48
53
 
49
54
  ## Theming
50
- Load `zx.css` once; set `data-zx-theme` (`light`|`dark`|`auto`) and `data-zx-density` (`cozy`|`compact`) on any ancestor. Define a product theme by overriding semantic `--zx-color-*` / `--zx-control-*` tokens under `[data-zx-theme="name"]`. The theme studio controls accent, neutral tint, density, font, type size, radius, control height, and CSS-first glass material. Never use tier-1 palette tokens or raw color literals in application CSS.
55
+ Load `zx.css` once; set `data-zx-theme` (`light`|`dark`|`auto`) and `data-zx-density` (`cozy`|`compact`) on any ancestor. Define a product theme by overriding semantic `--zx-color-*` / `--zx-control-*` tokens under `[data-zx-theme="name"]`. The theme studio controls accent, neutral tint, density, font, type size, radius, control height, and CSS-first glass material. Compact menus, pickers, tooltips, column menus, and toasts share a floating overlay material; Launcher, Modal, Dialog, and overlay Sheets share a more opaque panel material. Docked Sheets and dense data surfaces remain opaque. Never use tier-1 palette tokens or raw color literals in application CSS.
51
56
 
52
57
  ## Talking to ZeyOS
53
58
  Use the dedicated ZeyOS client library **`@zeyos/client`** (`npm install @zeyos/client`) for ZeyOS business data: `createZeyosClient({ platform, auth })`, typed `client.api.list/get/create/update<Resource>` over the full OpenAPI surface, and `normalizeListResult`. Zx accepts the client by injection and does not bundle it. See the `website/layouts/zeyos-invoices.layout.js` layout for a Zx-components-on-@zeyos/client reference. Zx also ships a minimal `Http`/`zeyosService` for ad-hoc in-app `remotecall`, but `@zeyos/client` is the recommended default.
54
59
 
60
+ The optional `./zeyos` entry maps schemas into all three record views. `SavedViewRegistry` adds
61
+ private named configurations scoped by authenticated user, workspace/fork (including null for the
62
+ base workspace), and resource through an injected atomic transport. The compatibility transport
63
+ reuses ZeyOS `userfields`; records, selection, pages, callbacks, and DOM are never persisted.
64
+
55
65
  ## Installing
56
66
  `npm install @zeyos/zx`. Entry points: `.` (components), `./zeyos` (ZeyOS binding), `./zx.css`
57
67
  (styles), and `./global` (classic script tag). Docs: https://zx.zeyos.com
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeyos/zx",
3
- "version": "4.1.0",
3
+ "version": "4.2.0",
4
4
  "description": "Dependency-free JavaScript implementation of the Xenon Design System.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -2,6 +2,12 @@
2
2
  --zx-app-icon-size: 36px;
3
3
  --zx-app-icon-glyph-size: 52%;
4
4
  --zx-app-icon-color: var(--zx-color-accent);
5
+ --zx-app-icon-core: color-mix(in srgb, var(--zx-app-icon-color) var(--zx-app-icon-core-strength), var(--zx-color-app-icon-shade));
6
+ --zx-app-icon-rim: color-mix(in srgb, var(--zx-app-icon-color) var(--zx-app-icon-rim-strength), var(--zx-app-icon-rim-base));
7
+ --zx-app-icon-bloom: color-mix(in srgb, var(--zx-app-icon-color) var(--zx-app-icon-bloom-strength), transparent);
8
+ --zx-app-icon-specular: color-mix(in srgb, var(--zx-color-glass-highlight-strong) var(--zx-app-icon-specular-strength), transparent);
9
+ --zx-app-icon-halo: 0 0 var(--zx-app-icon-halo-size) color-mix(in srgb, var(--zx-app-icon-color) var(--zx-app-icon-halo-strength), transparent);
10
+ --zx-app-icon-selected-ring: color-mix(in srgb, var(--zx-app-icon-color) 54%, transparent);
5
11
  position: relative;
6
12
  isolation: isolate;
7
13
  display: inline-grid;
@@ -9,71 +15,134 @@
9
15
  flex: none;
10
16
  inline-size: var(--zx-app-icon-size);
11
17
  block-size: var(--zx-app-icon-size);
12
- overflow: hidden;
13
- border: 1px solid var(--zx-color-glass-border);
14
- border-radius: max(var(--zx-radius-md), calc(var(--zx-app-icon-size) * .22));
15
- background: var(--zx-app-icon-color);
16
- box-shadow: var(--zx-app-icon-shadow);
17
- color: var(--zx-app-icon-glyph, var(--zx-color-text-invert));
18
+ overflow: visible;
19
+ border-radius: max(var(--zx-radius-sm), calc(var(--zx-app-icon-size) * .12));
20
+ color: var(--zx-app-icon-glyph, var(--zx-color-app-icon-glyph));
18
21
  font-size: var(--zx-app-icon-size);
19
22
  vertical-align: middle;
20
23
  }
21
24
 
22
- .zx-app-icon[data-glass="subtle"],
23
- .zx-app-icon[data-glass="strong"] {
25
+ .zx-app-icon[data-shape="circle"] {
26
+ border-radius: var(--zx-radius-full);
27
+ }
28
+
29
+ .zx-app-icon__surface {
30
+ position: absolute;
31
+ z-index: 0;
32
+ inset: 0;
33
+ display: grid;
34
+ overflow: hidden;
35
+ place-items: center;
36
+ border: 1px solid var(--zx-app-icon-rim);
37
+ border-radius: inherit;
38
+ background: var(--zx-app-icon-core);
39
+ box-shadow: var(--zx-app-icon-shadow);
40
+ }
41
+
42
+ .zx-app-icon[data-glass="subtle"] .zx-app-icon__surface,
43
+ .zx-app-icon[data-glass="strong"] .zx-app-icon__surface {
24
44
  background:
25
- linear-gradient(145deg, var(--zx-color-glass-highlight), transparent 44%),
26
- color-mix(in srgb, var(--zx-app-icon-color) var(--zx-glass-color-strength), transparent);
45
+ radial-gradient(112% 92% at 50% 122%, var(--zx-app-icon-bloom), transparent 64%),
46
+ var(--zx-app-icon-core);
47
+ -webkit-backdrop-filter: none;
48
+ backdrop-filter: none;
27
49
  }
28
50
 
29
- .zx-app-icon[data-glass="strong"] {
30
- backdrop-filter: blur(var(--zx-glass-blur)) saturate(var(--zx-glass-saturation));
51
+ .zx-app-icon[data-glass="strong"] .zx-app-icon__surface {
52
+ box-shadow: var(--zx-app-icon-shadow-hover), var(--zx-app-icon-halo);
31
53
  }
32
54
 
33
- .zx-app-icon[data-glass="none"] {
34
- border-color: var(--zx-color-border);
55
+ .zx-app-icon[data-glass="none"] .zx-app-icon__surface {
56
+ border-color: color-mix(in srgb, var(--zx-app-icon-color) 62%, var(--zx-color-border));
57
+ background: color-mix(in srgb, var(--zx-app-icon-color) 72%, var(--zx-color-app-icon-shade));
35
58
  box-shadow: none;
36
59
  }
37
60
 
38
61
  .zx-app-icon[data-glass="none"] .zx-app-icon__shine { display: none; }
62
+ .zx-app-icon[data-glass="none"] .zx-app-icon__surface::after { display: none; }
39
63
 
40
- .zx-app-icon[data-selected] {
41
- outline: 2px solid var(--zx-color-bg-page);
42
- box-shadow: 0 0 0 3px var(--zx-color-ring), var(--zx-overlay-shadow);
64
+ .zx-app-icon[data-selected] .zx-app-icon__surface {
65
+ outline: none;
66
+ box-shadow: 0 0 0 1px var(--zx-color-bg-page),
67
+ 0 0 0 3px var(--zx-app-icon-selected-ring),
68
+ var(--zx-app-icon-shadow-hover),
69
+ var(--zx-app-icon-halo);
43
70
  }
44
71
 
45
72
  .zx-app-icon__shine {
46
73
  position: absolute;
47
- z-index: -1;
48
- inset: 1px 12% auto;
49
- block-size: 42%;
50
- border-radius: 999px;
51
- background: linear-gradient(to bottom, var(--zx-color-glass-highlight-strong), transparent);
52
- filter: blur(2px);
74
+ z-index: 1;
75
+ inset: 2px 18% auto;
76
+ block-size: 1px;
77
+ border-radius: var(--zx-radius-full);
78
+ background: linear-gradient(90deg, transparent, var(--zx-app-icon-specular), transparent);
79
+ opacity: .72;
80
+ pointer-events: none;
81
+ }
82
+
83
+ .zx-app-icon[data-glass="strong"] .zx-app-icon__shine { opacity: .9; }
84
+
85
+ .zx-app-icon__surface::after {
86
+ content: "";
87
+ position: absolute;
88
+ z-index: 1;
89
+ inset: 0;
90
+ border-radius: inherit;
91
+ box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--zx-color-glass-highlight) 56%, transparent);
92
+ opacity: .56;
53
93
  pointer-events: none;
54
94
  }
55
95
 
56
96
  .zx-app-icon__glyph {
57
- display: inline-grid;
97
+ position: absolute;
98
+ z-index: 2;
99
+ inset-block-start: 50%;
100
+ inset-inline-start: 50%;
101
+ display: grid;
58
102
  place-items: center;
59
103
  inline-size: var(--zx-app-icon-glyph-size);
60
104
  block-size: var(--zx-app-icon-glyph-size);
105
+ margin: 0;
106
+ padding: 0;
61
107
  font-size: var(--zx-app-icon-glyph-size);
62
108
  line-height: 1;
63
- filter: drop-shadow(var(--zx-glass-glyph-shadow));
109
+ filter: none;
110
+ transform: translate(-50%, -50%);
64
111
  }
65
112
 
66
113
  .zx-app-icon__glyph > .zx-icon,
67
114
  .zx-app-icon__glyph > svg,
68
115
  .zx-app-icon__glyph > i {
116
+ display: grid;
117
+ place-items: center;
69
118
  inline-size: 1em;
70
119
  block-size: 1em;
120
+ margin: 0;
121
+ color: inherit;
122
+ font-size: 1em;
123
+ line-height: 1;
124
+ text-align: center;
125
+ vertical-align: middle;
126
+ }
127
+
128
+ .zx-app-icon__glyph > i::before {
129
+ display: block;
130
+ line-height: 1;
131
+ }
132
+
133
+ .zx-app-icon__glyph svg [fill]:not([fill="none"]) {
134
+ fill: currentColor;
135
+ }
136
+
137
+ .zx-app-icon__glyph svg [stroke]:not([stroke="none"]) {
138
+ stroke: currentColor;
71
139
  }
72
140
 
73
141
  .zx-app-icon__badge {
74
142
  position: absolute;
75
- inset-block-end: 1px;
76
- inset-inline-end: 1px;
143
+ z-index: 3;
144
+ inset-block-start: -3px;
145
+ inset-inline-end: -3px;
77
146
  display: grid;
78
147
  place-items: center;
79
148
  min-inline-size: 14px;
@@ -88,18 +157,59 @@
88
157
  line-height: 1;
89
158
  }
90
159
 
160
+ .zx-app-icon:is(a, button, [role="button"]):not(:disabled):not([aria-disabled="true"]):hover .zx-app-icon__surface,
161
+ :is(a, button, [role="button"]):not(:disabled):not([aria-disabled="true"]):hover .zx-app-icon__surface {
162
+ border-color: color-mix(in srgb, var(--zx-app-icon-color) 76%, var(--zx-color-glass-highlight-strong));
163
+ box-shadow: var(--zx-app-icon-shadow-hover), var(--zx-app-icon-halo);
164
+ filter: brightness(1.06) saturate(1.04);
165
+ }
166
+
167
+ .zx-app-icon:is(a, button, [role="button"]):not(:disabled):not([aria-disabled="true"]):focus-visible .zx-app-icon__surface,
168
+ :is(a, button, [role="button"]):not(:disabled):not([aria-disabled="true"]):focus-visible .zx-app-icon__surface {
169
+ outline: none;
170
+ box-shadow: var(--zx-focus-ring), var(--zx-app-icon-shadow-hover), var(--zx-app-icon-halo);
171
+ }
172
+
173
+ @media (prefers-reduced-motion: no-preference) {
174
+ .zx-app-icon__surface {
175
+ transition: border-color var(--zx-dur-fast) var(--zx-ease),
176
+ box-shadow var(--zx-dur-fast) var(--zx-ease),
177
+ filter var(--zx-dur-fast) var(--zx-ease);
178
+ }
179
+ }
180
+
91
181
  @media (prefers-reduced-transparency: reduce) {
92
- .zx-app-icon[data-glass="strong"] {
93
- background: var(--zx-app-icon-color);
182
+ .zx-app-icon[data-glass="strong"] .zx-app-icon__surface {
183
+ background: var(--zx-app-icon-core);
184
+ -webkit-backdrop-filter: none;
185
+ backdrop-filter: none;
186
+ }
187
+
188
+ .zx-app-icon[data-glass="subtle"] .zx-app-icon__surface {
189
+ background: var(--zx-app-icon-core);
190
+ -webkit-backdrop-filter: none;
94
191
  backdrop-filter: none;
95
192
  }
96
193
  }
97
194
 
98
195
  @media (forced-colors: active) {
99
- .zx-app-icon {
196
+ .zx-app-icon__surface {
100
197
  border-color: ButtonText;
101
198
  background: ButtonFace;
102
199
  color: ButtonText;
103
200
  box-shadow: none;
201
+ filter: none;
202
+ transform: none;
203
+ -webkit-backdrop-filter: none;
204
+ backdrop-filter: none;
205
+ }
206
+
207
+ .zx-app-icon__surface::after,
208
+ .zx-app-icon__shine { display: none; }
209
+
210
+ .zx-app-icon__badge {
211
+ border-color: Canvas;
212
+ background: Highlight;
213
+ color: HighlightText;
104
214
  }
105
215
  }