@zeyos/zx 4.3.0 → 4.3.4
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 +111 -1
- package/README.md +0 -3
- package/dist/types/components/calendar/calendar-model.d.ts +22 -0
- package/dist/types/components/card-view/card-view.d.ts +1 -1
- package/dist/types/components/card-view/record-card.d.ts +47 -5
- package/dist/types/components/kanban-view/kanban-policy.d.ts +268 -0
- package/dist/types/components/kanban-view/kanban-view.d.ts +669 -24
- package/dist/types/core/component.d.ts +6 -1
- package/dist/types/index.d.ts +2 -1
- package/dist/zx-zeyos.esm.js +35 -11
- package/dist/zx-zeyos.esm.min.js +1 -1
- package/dist/zx.css +597 -78
- package/dist/zx.esm.js +1758 -238
- package/dist/zx.esm.min.js +3 -3
- package/dist/zx.global.js +1758 -238
- package/dist/zx.global.min.js +3 -3
- package/dist/zx.min.css +1 -1
- package/docs/api.json +214 -2
- package/docs/llms.md +30 -30
- package/docs/llms.txt +0 -2
- package/package.json +1 -2
- package/src/components/calendar/calendar-model.js +45 -0
- package/src/components/calendar/calendar.css +30 -10
- package/src/components/calendar/calendar.js +8 -10
- package/src/components/card-view/card-view.css +211 -9
- package/src/components/card-view/card-view.js +7 -7
- package/src/components/card-view/record-card.js +92 -19
- package/src/components/kanban-view/kanban-policy.js +412 -0
- package/src/components/kanban-view/kanban-view.css +480 -36
- package/src/components/kanban-view/kanban-view.js +1514 -235
- package/src/components/message/message.css +9 -10
- package/src/core/component.js +8 -3
- package/src/index.js +8 -3
- package/DESIGN-SYSTEM.md +0 -182
package/docs/api.json
CHANGED
|
@@ -1468,7 +1468,7 @@
|
|
|
1468
1468
|
{
|
|
1469
1469
|
"name": "minCardWidth",
|
|
1470
1470
|
"type": "number|string",
|
|
1471
|
-
"default": "'
|
|
1471
|
+
"default": "'15rem'",
|
|
1472
1472
|
"description": "Responsive minimum card width."
|
|
1473
1473
|
},
|
|
1474
1474
|
{
|
|
@@ -4330,6 +4330,132 @@
|
|
|
4330
4330
|
"default": "'local'",
|
|
4331
4331
|
"description": "Whether accepted moves update local data."
|
|
4332
4332
|
},
|
|
4333
|
+
{
|
|
4334
|
+
"name": "dragFrom",
|
|
4335
|
+
"type": "'card'|'handle'",
|
|
4336
|
+
"default": "'card'",
|
|
4337
|
+
"description": "Whether the whole card or only its handle starts a pointer drag. Touch drags from the card body require a long press so the board still scrolls."
|
|
4338
|
+
},
|
|
4339
|
+
{
|
|
4340
|
+
"name": "multiMove",
|
|
4341
|
+
"type": "boolean",
|
|
4342
|
+
"default": "true",
|
|
4343
|
+
"description": "Move the whole selection when a selected card is picked up."
|
|
4344
|
+
},
|
|
4345
|
+
{
|
|
4346
|
+
"name": "autoScroll",
|
|
4347
|
+
"type": "boolean",
|
|
4348
|
+
"default": "true",
|
|
4349
|
+
"description": "Scroll the board and columns while dragging near an edge."
|
|
4350
|
+
},
|
|
4351
|
+
{
|
|
4352
|
+
"name": "wipPolicy",
|
|
4353
|
+
"type": "'warn'|'block'",
|
|
4354
|
+
"default": "'warn'",
|
|
4355
|
+
"description": "Whether exceeding a limit warns or refuses."
|
|
4356
|
+
},
|
|
4357
|
+
{
|
|
4358
|
+
"name": "rules",
|
|
4359
|
+
"type": "KanbanRule[]",
|
|
4360
|
+
"default": "[]",
|
|
4361
|
+
"description": "Ordered visual card rules."
|
|
4362
|
+
},
|
|
4363
|
+
{
|
|
4364
|
+
"name": "search",
|
|
4365
|
+
"type": "string",
|
|
4366
|
+
"default": "''",
|
|
4367
|
+
"description": "Board search text."
|
|
4368
|
+
},
|
|
4369
|
+
{
|
|
4370
|
+
"name": "searchFields",
|
|
4371
|
+
"type": "string[]|null",
|
|
4372
|
+
"default": "null",
|
|
4373
|
+
"description": "Field ids searched; null searches visible fields plus the title and subtitle sources."
|
|
4374
|
+
},
|
|
4375
|
+
{
|
|
4376
|
+
"name": "filter",
|
|
4377
|
+
"type": "((record:KanbanRecord,index:number)=>boolean)|null",
|
|
4378
|
+
"default": "null",
|
|
4379
|
+
"description": "Extra card predicate."
|
|
4380
|
+
},
|
|
4381
|
+
{
|
|
4382
|
+
"name": "searchControl",
|
|
4383
|
+
"type": "boolean",
|
|
4384
|
+
"default": "false",
|
|
4385
|
+
"description": "Render the built-in board search control."
|
|
4386
|
+
},
|
|
4387
|
+
{
|
|
4388
|
+
"name": "history",
|
|
4389
|
+
"type": "boolean",
|
|
4390
|
+
"default": "true",
|
|
4391
|
+
"description": "Record committed local moves for undo and redo."
|
|
4392
|
+
},
|
|
4393
|
+
{
|
|
4394
|
+
"name": "historyLimit",
|
|
4395
|
+
"type": "number",
|
|
4396
|
+
"default": "50",
|
|
4397
|
+
"description": "Maximum remembered move steps."
|
|
4398
|
+
},
|
|
4399
|
+
{
|
|
4400
|
+
"name": "historyControls",
|
|
4401
|
+
"type": "boolean",
|
|
4402
|
+
"default": "false",
|
|
4403
|
+
"description": "Render built-in undo and redo controls."
|
|
4404
|
+
},
|
|
4405
|
+
{
|
|
4406
|
+
"name": "columnHeight",
|
|
4407
|
+
"type": "string|null",
|
|
4408
|
+
"default": "null",
|
|
4409
|
+
"description": "CSS length turning each column into a scroll region."
|
|
4410
|
+
},
|
|
4411
|
+
{
|
|
4412
|
+
"name": "allowAdd",
|
|
4413
|
+
"type": "boolean|((column:KanbanColumn,swimlane:KanbanSwimlane|null)=>boolean)",
|
|
4414
|
+
"default": "false",
|
|
4415
|
+
"description": "Render a per-column add control that emits `recordadd`."
|
|
4416
|
+
},
|
|
4417
|
+
{
|
|
4418
|
+
"name": "contextMenu",
|
|
4419
|
+
"type": "boolean|MenuItem[]|((context:KanbanCardContext)=>MenuItem[])",
|
|
4420
|
+
"default": "false",
|
|
4421
|
+
"description": "Card context menu. `true` offers move commands; an array or function appends application items."
|
|
4422
|
+
},
|
|
4423
|
+
{
|
|
4424
|
+
"name": "renderCard",
|
|
4425
|
+
"type": "((context:KanbanCardContext)=>Node|null|undefined)|null",
|
|
4426
|
+
"default": "null",
|
|
4427
|
+
"description": "Replaces card content."
|
|
4428
|
+
},
|
|
4429
|
+
{
|
|
4430
|
+
"name": "renderColumnHeader",
|
|
4431
|
+
"type": "((context:KanbanHeaderContext)=>Node|null|undefined)|null",
|
|
4432
|
+
"default": "null",
|
|
4433
|
+
"description": "Replaces column header content."
|
|
4434
|
+
},
|
|
4435
|
+
{
|
|
4436
|
+
"name": "renderSwimlaneHeader",
|
|
4437
|
+
"type": "((context:KanbanHeaderContext)=>Node|null|undefined)|null",
|
|
4438
|
+
"default": "null",
|
|
4439
|
+
"description": "Replaces lane header content."
|
|
4440
|
+
},
|
|
4441
|
+
{
|
|
4442
|
+
"name": "renderColumnEmpty",
|
|
4443
|
+
"type": "((context:KanbanHeaderContext)=>Node|null|undefined)|null",
|
|
4444
|
+
"default": "null",
|
|
4445
|
+
"description": "Empty-column placeholder."
|
|
4446
|
+
},
|
|
4447
|
+
{
|
|
4448
|
+
"name": "renderDragPreview",
|
|
4449
|
+
"type": "((context:{records:KanbanRecord[],ids:unknown[],count:number})=>Node|null|undefined)|null",
|
|
4450
|
+
"default": "null",
|
|
4451
|
+
"description": "Replaces the floating drag preview."
|
|
4452
|
+
},
|
|
4453
|
+
{
|
|
4454
|
+
"name": "labels",
|
|
4455
|
+
"type": "Record<string,string>",
|
|
4456
|
+
"default": "{}",
|
|
4457
|
+
"description": "Overrides for every visible and announced string."
|
|
4458
|
+
},
|
|
4333
4459
|
{
|
|
4334
4460
|
"name": "columnOrder",
|
|
4335
4461
|
"type": "string[]",
|
|
@@ -4385,10 +4511,30 @@
|
|
|
4385
4511
|
"payload": "Record<string,unknown>",
|
|
4386
4512
|
"description": ""
|
|
4387
4513
|
},
|
|
4514
|
+
{
|
|
4515
|
+
"name": "movereject",
|
|
4516
|
+
"payload": "Record<string,unknown>",
|
|
4517
|
+
"description": ""
|
|
4518
|
+
},
|
|
4388
4519
|
{
|
|
4389
4520
|
"name": "recordaction",
|
|
4390
4521
|
"payload": "Record<string,unknown>",
|
|
4391
4522
|
"description": ""
|
|
4523
|
+
},
|
|
4524
|
+
{
|
|
4525
|
+
"name": "recordadd",
|
|
4526
|
+
"payload": "Record<string,unknown>",
|
|
4527
|
+
"description": ""
|
|
4528
|
+
},
|
|
4529
|
+
{
|
|
4530
|
+
"name": "searchchange",
|
|
4531
|
+
"payload": "{search:string}",
|
|
4532
|
+
"description": ""
|
|
4533
|
+
},
|
|
4534
|
+
{
|
|
4535
|
+
"name": "historychange",
|
|
4536
|
+
"payload": "{canUndo:boolean,canRedo:boolean,depth:{undo:number,redo:number}}",
|
|
4537
|
+
"description": ""
|
|
4392
4538
|
}
|
|
4393
4539
|
],
|
|
4394
4540
|
"methods": [
|
|
@@ -4492,7 +4638,73 @@
|
|
|
4492
4638
|
"name": "moveRecord",
|
|
4493
4639
|
"signature": "moveRecord(id, destination)",
|
|
4494
4640
|
"returns": "this",
|
|
4495
|
-
"description": "Proposes and, in local mode, commits a record move.
|
|
4641
|
+
"description": "Proposes and, in local mode, commits a record move. Equivalent to `moveRecords([id], …)`."
|
|
4642
|
+
},
|
|
4643
|
+
{
|
|
4644
|
+
"name": "moveRecords",
|
|
4645
|
+
"signature": "moveRecords(ids, destination)",
|
|
4646
|
+
"returns": "this",
|
|
4647
|
+
"description": "Proposes and, in local mode, commits an atomic move of one or more records. Destination eligibility, transition rules, and work-in-progress policy are all checked before a single cancelable `recordmove` event; the move commits completely or not at all, and the moving records keep their relative order at the destination."
|
|
4648
|
+
},
|
|
4649
|
+
{
|
|
4650
|
+
"name": "canUndo",
|
|
4651
|
+
"signature": "canUndo()",
|
|
4652
|
+
"returns": "boolean",
|
|
4653
|
+
"description": "Whether a committed local move can be reverted."
|
|
4654
|
+
},
|
|
4655
|
+
{
|
|
4656
|
+
"name": "canRedo",
|
|
4657
|
+
"signature": "canRedo()",
|
|
4658
|
+
"returns": "boolean",
|
|
4659
|
+
"description": "Whether a reverted local move can be reapplied."
|
|
4660
|
+
},
|
|
4661
|
+
{
|
|
4662
|
+
"name": "undo",
|
|
4663
|
+
"signature": "undo()",
|
|
4664
|
+
"returns": "this",
|
|
4665
|
+
"description": "Reverts the last committed local move, restoring the exact record order and selection."
|
|
4666
|
+
},
|
|
4667
|
+
{
|
|
4668
|
+
"name": "redo",
|
|
4669
|
+
"signature": "redo()",
|
|
4670
|
+
"returns": "this",
|
|
4671
|
+
"description": "Reapplies the last reverted local move."
|
|
4672
|
+
},
|
|
4673
|
+
{
|
|
4674
|
+
"name": "clearHistory",
|
|
4675
|
+
"signature": "clearHistory()",
|
|
4676
|
+
"returns": "this",
|
|
4677
|
+
"description": "Empties both history stacks."
|
|
4678
|
+
},
|
|
4679
|
+
{
|
|
4680
|
+
"name": "getSearch",
|
|
4681
|
+
"signature": "getSearch()",
|
|
4682
|
+
"returns": "string",
|
|
4683
|
+
"description": "Returns the current board search text."
|
|
4684
|
+
},
|
|
4685
|
+
{
|
|
4686
|
+
"name": "setSearch",
|
|
4687
|
+
"signature": "setSearch(search, [options])",
|
|
4688
|
+
"returns": "this",
|
|
4689
|
+
"description": "Narrows visible cards to those matching a search. Hidden records keep their position in the data, so a card dropped between two visible cards still lands between them."
|
|
4690
|
+
},
|
|
4691
|
+
{
|
|
4692
|
+
"name": "setFilter",
|
|
4693
|
+
"signature": "setFilter(filter)",
|
|
4694
|
+
"returns": "this",
|
|
4695
|
+
"description": "Replaces the extra card predicate. Pass null to clear it."
|
|
4696
|
+
},
|
|
4697
|
+
{
|
|
4698
|
+
"name": "getVisibleRecords",
|
|
4699
|
+
"signature": "getVisibleRecords()",
|
|
4700
|
+
"returns": "KanbanRecord[]",
|
|
4701
|
+
"description": "Returns the records currently rendered, in board order."
|
|
4702
|
+
},
|
|
4703
|
+
{
|
|
4704
|
+
"name": "setData",
|
|
4705
|
+
"signature": "setData(records)",
|
|
4706
|
+
"returns": "this",
|
|
4707
|
+
"description": "Replaces every record. History is dropped because its snapshots describe records the host has just replaced, and reinstating them would resurrect data the application no longer owns."
|
|
4496
4708
|
},
|
|
4497
4709
|
{
|
|
4498
4710
|
"name": "getViewState",
|
package/docs/llms.md
CHANGED
|
@@ -31,6 +31,8 @@ const t = new Table(target, options);
|
|
|
31
31
|
(always an object, e.g. `{ value, item }`).
|
|
32
32
|
- Every `emit` also dispatches a **bubbling, composed** `zx-<type>` `CustomEvent` on `t.el`, so
|
|
33
33
|
`t.el.addEventListener('zx-change', …)` and event delegation work.
|
|
34
|
+
- Mutation boundaries may opt into honoring cancellation of that mirrored DOM event; Kanban's
|
|
35
|
+
`zx-recordmove` does, so delegated hosts can veto a move without a component reference.
|
|
34
36
|
- `t.destroy()` aborts all listeners (one AbortController), unregisters the element, and removes an
|
|
35
37
|
owned root. `Component.from(el)` returns the component for a root element (replaces the legacy
|
|
36
38
|
`el.retrieve('com')`).
|
|
@@ -253,28 +255,6 @@ reflection and ElementInternals form association: `<zx-toggle>`, `<zx-check-butt
|
|
|
253
255
|
`<zx-checklist>`, `<zx-datebox>`, `<zx-timebox>`, `<zx-search>`, `<zx-groupbox>`, `<zx-tabbox>`,
|
|
254
256
|
`<zx-table>`, `<zx-dialog>`.
|
|
255
257
|
|
|
256
|
-
## Conventions (editing component source)
|
|
257
|
-
|
|
258
|
-
Follow `AGENTS.md`. Key rules: never declare instance class fields for state used in `render()`
|
|
259
|
-
(render runs inside the base constructor before field initializers); route all DOM listeners
|
|
260
|
-
through `this.listen()`; no `innerHTML` outside `h.raw()`; express state via ARIA/`data-*`, not
|
|
261
|
-
state classes; small glyph buttons use the shared `.zx-icon-btn` utility; follow the named APG
|
|
262
|
-
pattern with `:focus-visible` rings and `prefers-reduced-motion` guards.
|
|
263
|
-
|
|
264
|
-
## File map & commands
|
|
265
|
-
|
|
266
|
-
```
|
|
267
|
-
src/components/<name>/ component + CSS src/core/ kernel (component, dom, http, i18n, date…)
|
|
268
|
-
website/ marketing page + docs.html (docs.js) specs/ per-component specs
|
|
269
|
-
website/demos/ per-component demo modules website/layouts/ application layout examples
|
|
270
|
-
tests/ node unit + smoke dist/ built bundles
|
|
271
|
-
|
|
272
|
-
npm run serve # http://127.0.0.1:8321/website/docs.html (no build)
|
|
273
|
-
npm test # node --test tests/unit/*.test.js + node tests/lint-tokens.js
|
|
274
|
-
npm run build # dist/: ESM, global, ZeyOS binding, and CSS assets
|
|
275
|
-
node tools/build-module-tokens.js # regenerate styles/tokens/modules.css from src/zeyos/modules.js
|
|
276
|
-
```
|
|
277
|
-
|
|
278
258
|
---
|
|
279
259
|
|
|
280
260
|
# Components
|
|
@@ -1151,8 +1131,15 @@ portable state as TableView and KanbanView.
|
|
|
1151
1131
|
`previewAlt`, `link`, `actions`, `groupBy`, `groupOrder`, `minCardWidth`, `maxColumns`,
|
|
1152
1132
|
`variant: 'outlined'|'raised'|'filled'`, `loadingCount`, `headingLevel`, and `label`.
|
|
1153
1133
|
- **Cards** — each record is a focusable `<li>` with a native title link, separate secondary
|
|
1154
|
-
actions, optional native multi-select checkbox, labelled metadata, and
|
|
1155
|
-
|
|
1134
|
+
actions, optional native multi-select checkbox, labelled metadata, and a global ARIA selection
|
|
1135
|
+
description that retains valid listitem semantics.
|
|
1136
|
+
CardView uses a dense collection rhythm: titles clamp to two lines, previews stay at
|
|
1137
|
+
thumbnail/avatar scale, and visible fields become compact labelled metadata chips. Title/subtitle
|
|
1138
|
+
fields are not duplicated in metadata unless their field sets `duplicate:true`.
|
|
1139
|
+
- **Shared card slots** — a visible field may set
|
|
1140
|
+
`view.card.slot: 'eyebrow-start'|'eyebrow-end'|'title-prefix'`. These slots create a semantic
|
|
1141
|
+
eyebrow row or put labelled content immediately before (and outside) the title heading/link.
|
|
1142
|
+
Unknown slots remain ordinary metadata; visibility and field order still control rendering.
|
|
1156
1143
|
- **Media and URLs** — `preview` accepts a field ID or callback returning an image URL,
|
|
1157
1144
|
`{src,alt,fit}`, Node, or null. Executable/non-image schemes are rejected and image failures show
|
|
1158
1145
|
a stable fallback. Title/action links are normalized; `_blank` links receive `noopener`.
|
|
@@ -1176,14 +1163,25 @@ cards, advisory work-in-progress limits, and equivalent pointer/programmatic/key
|
|
|
1176
1163
|
columns may also declare an advisory `limit`.
|
|
1177
1164
|
- **Methods** — configure/get/move columns and swim lanes, collapse either axis, and
|
|
1178
1165
|
`moveRecord(id,{column?,lane?,index?})`. Board-specific order/collapse joins the common view
|
|
1179
|
-
state; record order, movement history, and selection do not.
|
|
1166
|
+
state; record order, movement history, and selection do not. Saved preferences for data-derived
|
|
1167
|
+
axes survive an empty/partial result set and reconcile as later records introduce their IDs.
|
|
1168
|
+
- **Dense board layout** — bounded muted columns use a stable 20rem rail with native horizontal
|
|
1169
|
+
overflow, 48px headings, compact counts, 8–12px card padding, and 8px card gaps. Surface cards
|
|
1170
|
+
use an optional square thumbnail and a two-tier hierarchy from shared card field slots:
|
|
1171
|
+
`eyebrow-start`, `eyebrow-end`, and `title-prefix`. Unslotted metadata stays as quiet inline
|
|
1172
|
+
facts. Titles clamp visually to two lines while retaining their complete accessible text. Move
|
|
1173
|
+
and action controls reveal on hover/focus/selection/grab, and remain visible on touch devices.
|
|
1180
1174
|
- **Movement boundary** — `recordmove {record,id,from,to,column,swimlane,limitExceeded}` is
|
|
1181
|
-
cancelable and fires before a commit/request
|
|
1182
|
-
|
|
1183
|
-
|
|
1175
|
+
cancelable and fires before a commit/request; either the component event or bubbling
|
|
1176
|
+
`zx-recordmove` mirror may veto it. `accept` is a hard eligibility gate; WIP is visual advice.
|
|
1177
|
+
Local mode clones the moved record and never mutates host data, and a manual local move clears an
|
|
1178
|
+
active local sort so saved state matches visible order. External mode emits only, for server
|
|
1179
|
+
validation and optimistic rollback owned by ZeyOS.
|
|
1184
1180
|
- **Keyboard** — focus the move handle; Enter/Space grabs or drops, Escape cancels, Left/Right
|
|
1185
1181
|
chooses a column, Up/Down chooses a bucket-relative position, and Alt+Up/Down chooses a swim
|
|
1186
|
-
lane. A polite live region announces every target
|
|
1182
|
+
lane. A polite live region announces every target, result, and reached/exceeded WIP limit.
|
|
1183
|
+
Collapse keeps focus on its control, including when a programmatic collapse hides the focused
|
|
1184
|
+
card. Native drag is an enhancement.
|
|
1187
1185
|
<!-- /doc -->
|
|
1188
1186
|
|
|
1189
1187
|
<!-- doc:grid -->
|
|
@@ -1256,7 +1254,9 @@ windowed loader.
|
|
|
1256
1254
|
HTML, and suspicious CSS colour strings are ignored.
|
|
1257
1255
|
- **Methods** — `getEvents()`, `setEvents()`, `addEvent()`, `updateEvent()`, `removeEvent()`,
|
|
1258
1256
|
`getDate()`, `setDate()`, `getView()`, `setView()`, `prev()`, `next()`, `today()`,
|
|
1259
|
-
`setLoading()`, `focusEvent()`, `destroy()`.
|
|
1257
|
+
`setLoading()`, `focusEvent()`, `destroy()`. `prev()`/`next()` step one visible period and clamp
|
|
1258
|
+
the day of month into the target month, so a 29th–31st anchor cannot skip a shorter month; the
|
|
1259
|
+
same arithmetic is exported as `calendarPageDate()` and `addCalendarMonths()`.
|
|
1260
1260
|
- **Events** — `dateschange {view,start,end}`, `viewchange {view,oldView}`, `eventclick`,
|
|
1261
1261
|
`eventdblclick`, `dateclick {date,allDay,view}`, `select {start,end,allDay,view}`,
|
|
1262
1262
|
`new {date,view}`, `moreclick {date,count}`, `eventschange {events}`, and cancelable
|
package/docs/llms.txt
CHANGED
|
@@ -16,9 +16,7 @@ exports and as `window.zx.*`.
|
|
|
16
16
|
- [Documentation site](https://zx.zeyos.com/docs.html): getting started, every component with a live demo and its JavaScript source, and the application layouts. Machine index: [llms.txt](https://zx.zeyos.com/llms.txt).
|
|
17
17
|
- [README](../README.md): what Zx is, quick start (ESM + global script), component index, theming, and browser support.
|
|
18
18
|
- [API reference](llms.md): per-component constructor options, methods, and events for all public components plus the core helpers.
|
|
19
|
-
- [Skill](https://zx.zeyos.com/skills/zx/SKILL.md): task-oriented guide — mental model, recipes, ZeyOS HTTP client, theming, gotchas, commands.
|
|
20
19
|
- [Changelog](../CHANGELOG.md): what changed in each release, including behaviour changes an upgrade has to account for.
|
|
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
20
|
|
|
23
21
|
## Components
|
|
24
22
|
- Inputs: `button()`, `buttonGroup()`, `badge()`/`badgeGroup()`, `CheckButton`,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zeyos/zx",
|
|
3
|
-
"version": "4.3.
|
|
3
|
+
"version": "4.3.4",
|
|
4
4
|
"description": "Dependency-free JavaScript implementation of the Xenon Design System.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -65,7 +65,6 @@
|
|
|
65
65
|
"docs/api.json",
|
|
66
66
|
"CHANGELOG.md",
|
|
67
67
|
"README.md",
|
|
68
|
-
"DESIGN-SYSTEM.md",
|
|
69
68
|
"LICENSE"
|
|
70
69
|
],
|
|
71
70
|
"engines": {
|
|
@@ -70,6 +70,51 @@ export function calendarRange(view, anchor, options = {}) {
|
|
|
70
70
|
return { start, end };
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
/**
|
|
74
|
+
* Returns the anchor date one visible period away from `anchor`.
|
|
75
|
+
*
|
|
76
|
+
* Month and year steps clamp the day of month into the target month instead of letting it
|
|
77
|
+
* overflow: `setMonth()` alone turns 31 January into 3 March, which makes February unreachable
|
|
78
|
+
* from a month-end anchor and leaves `prev()` on 31 March landing back inside March.
|
|
79
|
+
* @param {CalendarView} view Current view.
|
|
80
|
+
* @param {Date} anchor Current anchor date.
|
|
81
|
+
* @param {number} direction -1 for the previous period, 1 for the next.
|
|
82
|
+
* @param {{agendaDays?: number}} [options={}] Paging options.
|
|
83
|
+
* @returns {Date}
|
|
84
|
+
*/
|
|
85
|
+
export function calendarPageDate(view, anchor, direction, options = {}) {
|
|
86
|
+
const date = validDate(anchor, 'Calendar date');
|
|
87
|
+
const step = integer(direction, 'direction');
|
|
88
|
+
if (view === 'day') date.setDate(date.getDate() + step);
|
|
89
|
+
else if (view === 'agenda') {
|
|
90
|
+
date.setDate(date.getDate() + step * positiveInteger(options.agendaDays ?? 14, 'agendaDays'));
|
|
91
|
+
} else if (view === 'week') date.setDate(date.getDate() + step * 7);
|
|
92
|
+
else if (view === 'month') return addCalendarMonths(date, step);
|
|
93
|
+
else if (view === 'year') return addCalendarMonths(date, step * 12);
|
|
94
|
+
else throw new RangeError(`Unknown calendar view: ${view}`);
|
|
95
|
+
return date;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Adds whole months, clamping the day of month to the last day the target month actually has.
|
|
100
|
+
* @param {Date} value Local date.
|
|
101
|
+
* @param {number} months Months to add.
|
|
102
|
+
* @returns {Date}
|
|
103
|
+
*/
|
|
104
|
+
export function addCalendarMonths(value, months) {
|
|
105
|
+
const date = validDate(value, 'Calendar date');
|
|
106
|
+
const day = date.getDate();
|
|
107
|
+
date.setDate(1);
|
|
108
|
+
date.setMonth(date.getMonth() + integer(months, 'month amount'));
|
|
109
|
+
date.setDate(Math.min(day, daysInCalendarMonth(date.getFullYear(), date.getMonth())));
|
|
110
|
+
return date;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** @param {number} year @param {number} month @returns {number} */
|
|
114
|
+
function daysInCalendarMonth(year, month) {
|
|
115
|
+
return new Date(year, month + 1, 0).getDate();
|
|
116
|
+
}
|
|
117
|
+
|
|
73
118
|
/**
|
|
74
119
|
* Normalizes caller records atomically into the Calendar event contract.
|
|
75
120
|
* @param {Record<string, any>[]} records Source records.
|
|
@@ -9,15 +9,20 @@
|
|
|
9
9
|
min-inline-size: 0;
|
|
10
10
|
overflow: hidden;
|
|
11
11
|
border: 1px solid var(--zx-color-border);
|
|
12
|
-
border-radius:
|
|
12
|
+
border-radius: 0;
|
|
13
13
|
background: var(--zx-color-bg-surface);
|
|
14
14
|
color: var(--zx-color-text);
|
|
15
15
|
font-size: var(--zx-text-sm);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
/*
|
|
19
|
+
* The heading is the only elastic track. With `1fr auto 1fr` the end-justified commands group
|
|
20
|
+
* kept its min-content width inside a narrower `1fr` track and bled back over the centered
|
|
21
|
+
* heading; giving navigation and commands their natural width lets the heading ellipsize instead.
|
|
22
|
+
*/
|
|
18
23
|
.zx-calendar__toolbar {
|
|
19
24
|
display: grid;
|
|
20
|
-
grid-template-columns: 1fr auto
|
|
25
|
+
grid-template-columns: auto minmax(0, 1fr) auto;
|
|
21
26
|
align-items: center;
|
|
22
27
|
gap: var(--zx-space-3);
|
|
23
28
|
min-inline-size: 0;
|
|
@@ -35,7 +40,14 @@
|
|
|
35
40
|
|
|
36
41
|
.zx-calendar__navigation { justify-self: start; gap: var(--zx-space-1); }
|
|
37
42
|
.zx-calendar__commands { justify-self: end; gap: var(--zx-space-3); }
|
|
38
|
-
.zx-calendar__views {
|
|
43
|
+
.zx-calendar__views {
|
|
44
|
+
block-size: var(--zx-control-height);
|
|
45
|
+
gap: 2px;
|
|
46
|
+
overflow-x: auto;
|
|
47
|
+
border-radius: 0;
|
|
48
|
+
background: var(--zx-color-bg-muted);
|
|
49
|
+
padding: 3px;
|
|
50
|
+
}
|
|
39
51
|
|
|
40
52
|
.zx-calendar__toolbar-button,
|
|
41
53
|
.zx-calendar__view-button,
|
|
@@ -59,15 +71,21 @@
|
|
|
59
71
|
}
|
|
60
72
|
|
|
61
73
|
.zx-calendar__toolbar-button:has(.zx-icon) { padding-inline: var(--zx-space-2); }
|
|
62
|
-
.zx-calendar__view-button {
|
|
63
|
-
|
|
64
|
-
|
|
74
|
+
.zx-calendar__view-button {
|
|
75
|
+
block-size: 100%;
|
|
76
|
+
min-block-size: 0;
|
|
77
|
+
border-color: transparent;
|
|
78
|
+
border-radius: 0;
|
|
79
|
+
background: transparent;
|
|
80
|
+
padding-inline: var(--zx-space-3);
|
|
81
|
+
}
|
|
65
82
|
.zx-calendar__view-button[aria-pressed="true"] {
|
|
66
83
|
position: relative;
|
|
67
84
|
z-index: 1;
|
|
68
|
-
border-color: var(--zx-color-
|
|
69
|
-
background: var(--zx-color-bg-
|
|
70
|
-
|
|
85
|
+
border-color: var(--zx-color-border-control);
|
|
86
|
+
background: var(--zx-color-bg-control);
|
|
87
|
+
box-shadow: var(--zx-shadow-2);
|
|
88
|
+
color: var(--zx-color-text);
|
|
71
89
|
}
|
|
72
90
|
|
|
73
91
|
.zx-calendar__new {
|
|
@@ -81,7 +99,7 @@
|
|
|
81
99
|
}
|
|
82
100
|
|
|
83
101
|
.zx-calendar__toolbar-button:hover:not(:disabled),
|
|
84
|
-
.zx-calendar__view-button:hover:not(:disabled) { background: var(--zx-color-bg-hover); }
|
|
102
|
+
.zx-calendar__view-button:hover:not(:disabled):not([aria-pressed="true"]) { background: var(--zx-color-bg-hover); }
|
|
85
103
|
.zx-calendar__new:hover:not(:disabled) { background: var(--zx-color-accent-hover); }
|
|
86
104
|
.zx-calendar button:disabled { cursor: default; opacity: .55; }
|
|
87
105
|
|
|
@@ -148,6 +166,7 @@
|
|
|
148
166
|
min-block-size: calc(var(--zx-control-height) + var(--zx-space-2));
|
|
149
167
|
place-content: center;
|
|
150
168
|
border: 0;
|
|
169
|
+
border-radius: 0;
|
|
151
170
|
border-inline-end: 1px solid var(--zx-color-border);
|
|
152
171
|
background: var(--zx-color-bg-surface);
|
|
153
172
|
color: var(--zx-color-text);
|
|
@@ -227,6 +246,7 @@
|
|
|
227
246
|
.zx-calendar__slot {
|
|
228
247
|
min-inline-size: 0;
|
|
229
248
|
border: 0;
|
|
249
|
+
border-radius: 0;
|
|
230
250
|
border-block-start: 1px solid var(--zx-color-border);
|
|
231
251
|
border-inline-end: 1px solid var(--zx-color-border);
|
|
232
252
|
background: var(--zx-color-bg-surface);
|
|
@@ -3,8 +3,8 @@ import { h } from '../../core/dom.js';
|
|
|
3
3
|
import { icon } from '../../core/icons.js';
|
|
4
4
|
import {
|
|
5
5
|
CALENDAR_VIEWS, addCalendarDays, calendarDayDifference, calendarDayKey,
|
|
6
|
-
calendarEventIntersects, calendarEventSpansDays,
|
|
7
|
-
layoutCalendarSpans, layoutTimedCalendarEvents, normalizeCalendarEvents,
|
|
6
|
+
calendarEventIntersects, calendarEventSpansDays, calendarPageDate, calendarRange,
|
|
7
|
+
cloneCalendarEvent, layoutCalendarSpans, layoutTimedCalendarEvents, normalizeCalendarEvents,
|
|
8
8
|
shiftCalendarEvent, startOfCalendarDay
|
|
9
9
|
} from './calendar-model.js';
|
|
10
10
|
|
|
@@ -289,7 +289,9 @@ export class Calendar extends Component {
|
|
|
289
289
|
? changes.durationEditable : current.durationEditable,
|
|
290
290
|
data: changes.data ?? current.data
|
|
291
291
|
};
|
|
292
|
-
|
|
292
|
+
// `source` is assembled from canonical keys, so the configured readers must not be reapplied
|
|
293
|
+
// here — but `dateUnit` must, or a numeric change is read in the wrong unit.
|
|
294
|
+
const [next] = normalizeCalendarEvents([source], { dateUnit: this.options.dateUnit });
|
|
293
295
|
next.data = source.data;
|
|
294
296
|
this._events = this._events.map((event, eventIndex) => eventIndex === index ? next : event);
|
|
295
297
|
this._mutationVersions.delete(String(id));
|
|
@@ -1335,13 +1337,9 @@ export class Calendar extends Component {
|
|
|
1335
1337
|
|
|
1336
1338
|
/** @param {number} direction @returns {this} */
|
|
1337
1339
|
_page(direction) {
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
else if (this._view === 'week') date.setDate(date.getDate() + direction * 7);
|
|
1342
|
-
else if (this._view === 'month') date.setMonth(date.getMonth() + direction);
|
|
1343
|
-
else date.setFullYear(date.getFullYear() + direction);
|
|
1344
|
-
return this.setDate(date);
|
|
1340
|
+
return this.setDate(calendarPageDate(this._view, this._date, direction, {
|
|
1341
|
+
agendaDays: this.options.agendaDays
|
|
1342
|
+
}));
|
|
1345
1343
|
}
|
|
1346
1344
|
|
|
1347
1345
|
/** @param {{start: Date, end: Date}} range @returns {string} */
|