@zeyos/zx 4.2.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.
Files changed (69) hide show
  1. package/CHANGELOG.md +148 -1
  2. package/README.md +5 -3
  3. package/dist/types/components/calendar/calendar-model.d.ts +22 -0
  4. package/dist/types/components/calendar/calendar.d.ts +19 -2
  5. package/dist/types/components/card-view/card-view.d.ts +1 -1
  6. package/dist/types/components/card-view/record-card.d.ts +47 -5
  7. package/dist/types/components/dialog/dialog.d.ts +6 -1
  8. package/dist/types/components/dropdown/dropdown.d.ts +5 -0
  9. package/dist/types/components/kanban-view/kanban-policy.d.ts +268 -0
  10. package/dist/types/components/kanban-view/kanban-view.d.ts +669 -24
  11. package/dist/types/components/launcher/launcher.d.ts +5 -0
  12. package/dist/types/components/message/message.d.ts +9 -1
  13. package/dist/types/components/modal/modal.d.ts +44 -7
  14. package/dist/types/components/sheet/sheet.d.ts +22 -12
  15. package/dist/types/core/component.d.ts +6 -1
  16. package/dist/types/core/overlay-host.d.ts +12 -0
  17. package/dist/types/index.d.ts +2 -1
  18. package/dist/zx-zeyos.esm.js +173 -43
  19. package/dist/zx-zeyos.esm.min.js +1 -1
  20. package/dist/zx.css +876 -230
  21. package/dist/zx.esm.js +2053 -338
  22. package/dist/zx.esm.min.js +3 -3
  23. package/dist/zx.global.js +2053 -338
  24. package/dist/zx.global.min.js +3 -3
  25. package/dist/zx.min.css +1 -1
  26. package/docs/api.json +250 -2
  27. package/docs/llms.md +67 -48
  28. package/docs/llms.txt +1 -3
  29. package/package.json +1 -2
  30. package/src/components/account-menu/account-menu.css +1 -1
  31. package/src/components/app-rail/app-rail.css +1 -1
  32. package/src/components/calendar/calendar-model.js +45 -0
  33. package/src/components/calendar/calendar.css +96 -34
  34. package/src/components/calendar/calendar.js +122 -29
  35. package/src/components/card-view/card-view.css +211 -9
  36. package/src/components/card-view/card-view.js +7 -7
  37. package/src/components/card-view/record-card.js +92 -19
  38. package/src/components/context-menu/context-menu.css +2 -2
  39. package/src/components/context-menu/context-menu.js +5 -1
  40. package/src/components/date-picker/date-picker.css +18 -11
  41. package/src/components/datebox/date-range-box.css +4 -2
  42. package/src/components/datebox/datebox.css +4 -2
  43. package/src/components/dialog/dialog.css +4 -3
  44. package/src/components/dialog/dialog.js +2 -1
  45. package/src/components/dropdown/dropdown.css +4 -2
  46. package/src/components/dropdown/dropdown.js +7 -2
  47. package/src/components/kanban-view/kanban-policy.js +412 -0
  48. package/src/components/kanban-view/kanban-view.css +480 -36
  49. package/src/components/kanban-view/kanban-view.js +1514 -235
  50. package/src/components/launcher/launcher.css +10 -8
  51. package/src/components/launcher/launcher.js +9 -2
  52. package/src/components/menu-button/menu-button.css +2 -2
  53. package/src/components/message/message.css +15 -25
  54. package/src/components/message/message.js +17 -11
  55. package/src/components/modal/modal.css +6 -4
  56. package/src/components/modal/modal.js +99 -25
  57. package/src/components/select/select.css +9 -7
  58. package/src/components/sheet/sheet.css +25 -7
  59. package/src/components/sheet/sheet.js +43 -23
  60. package/src/components/table/table.css +5 -3
  61. package/src/components/tag-picker/tag-picker.css +5 -3
  62. package/src/components/tooltip/tooltip.css +4 -2
  63. package/src/components/tooltip/tooltip.js +4 -1
  64. package/src/core/component.js +8 -3
  65. package/src/core/overlay-host.js +23 -0
  66. package/src/index.js +8 -3
  67. package/styles/tokens/dark.css +4 -0
  68. package/styles/tokens/semantic.css +83 -8
  69. package/DESIGN-SYSTEM.md +0 -182
@@ -1,9 +1,14 @@
1
+ import { ContextMenu } from '../context-menu/context-menu.js';
1
2
  import { RecordView } from '../view/record-view.js';
2
3
  export type KanbanRecord = Record<string, any>;
3
4
  export type RecordViewOptions = import('../view/record-view.js').RecordViewOptions;
4
5
  export type RecordCardPreview = import('../card-view/record-card.js').RecordCardPreview;
5
6
  export type RecordCardLink = import('../card-view/record-card.js').RecordCardLink;
6
7
  export type RecordCardAction = import('../card-view/record-card.js').RecordCardAction;
8
+ export type MenuItem = import('../menu-button/menu-button.js').MenuItem;
9
+ export type KanbanRule = import('./kanban-policy.js').KanbanRule;
10
+ export type KanbanRejectReason = import('./kanban-policy.js').KanbanRejectReason;
11
+ export type KanbanMoveEvaluation = import('./kanban-policy.js').KanbanMoveEvaluation;
7
12
  export type KanbanAccessor = string | ((record: KanbanRecord, index: number) => unknown) | null;
8
13
  export type KanbanCardField = string | ((record: KanbanRecord, index: number) => unknown) | null;
9
14
  export type KanbanMovePoint = {
@@ -56,9 +61,22 @@ export type KanbanColumn = {
56
61
  */
57
62
  value?: unknown;
58
63
  /**
59
- * Advisory work-in-progress limit.
64
+ * Work-in-progress limit across every lane.
60
65
  */
61
66
  limit?: number | null;
67
+ /**
68
+ * Per-lane limits inside this column.
69
+ */
70
+ laneLimits?: Record<string, number>;
71
+ /**
72
+ * Overrides the board `wipPolicy` for this column.
73
+ */
74
+ wipPolicy?: 'warn' | 'block';
75
+ /**
76
+ * Origin column ids allowed to move in. `null`, an absent
77
+ * value, or `['*']` admits any origin; a move that stays in the column is never restricted.
78
+ */
79
+ from?: string[] | null;
62
80
  /**
63
81
  * Eligibility
64
82
  * predicate. Returning false rejects the move before the cancelable event is emitted.
@@ -78,6 +96,14 @@ export type KanbanSwimlane = {
78
96
  * Value written by local moves; defaults to `id`.
79
97
  */
80
98
  value?: unknown;
99
+ /**
100
+ * Work-in-progress limit across every column in the lane.
101
+ */
102
+ limit?: number | null;
103
+ /**
104
+ * Origin lane ids allowed to move in.
105
+ */
106
+ from?: string[] | null;
81
107
  /**
82
108
  * Eligibility
83
109
  * predicate. Returning false rejects the move before the cancelable event is emitted.
@@ -135,6 +161,70 @@ export type KanbanViewState = {
135
161
  */
136
162
  collapsedSwimlanes: string[];
137
163
  };
164
+ export type KanbanCardContext = {
165
+ /**
166
+ * Record being rendered.
167
+ */
168
+ record: KanbanRecord;
169
+ /**
170
+ * Record id.
171
+ */
172
+ id: unknown;
173
+ /**
174
+ * Display index in the current record order.
175
+ */
176
+ index: number;
177
+ /**
178
+ * Column id.
179
+ */
180
+ column: string;
181
+ /**
182
+ * Lane id, or null when lanes are disabled.
183
+ */
184
+ lane: string | null;
185
+ /**
186
+ * Rules matching this record, in configured order.
187
+ */
188
+ rules: KanbanRule[];
189
+ /**
190
+ * Whether the record is selected.
191
+ */
192
+ selected: boolean;
193
+ };
194
+ export type KanbanHeaderContext = {
195
+ /**
196
+ * Column being rendered.
197
+ */
198
+ column?: KanbanColumn;
199
+ /**
200
+ * Lane being rendered, or the lane a column belongs to.
201
+ */
202
+ swimlane?: KanbanSwimlane | null;
203
+ /**
204
+ * Records rendered in this section.
205
+ */
206
+ count: number;
207
+ /**
208
+ * Records this section holds before search and filters.
209
+ */
210
+ total: number;
211
+ /**
212
+ * Records in the whole column, across lanes.
213
+ */
214
+ columnTotal: number;
215
+ /**
216
+ * Applicable work-in-progress limit.
217
+ */
218
+ limit: number | null;
219
+ /**
220
+ * Capacity state.
221
+ */
222
+ wip: 'none' | 'within' | 'limit' | 'exceeded';
223
+ /**
224
+ * Whether the section is collapsed.
225
+ */
226
+ collapsed: boolean;
227
+ };
138
228
  export type KanbanViewOptions = {
139
229
  /**
140
230
  * Shared field descriptors.
@@ -227,6 +317,96 @@ export type KanbanViewOptions = {
227
317
  * Whether accepted moves update local data.
228
318
  */
229
319
  moveMode?: 'local' | 'external';
320
+ /**
321
+ * Whether the whole card or only its handle starts a
322
+ * pointer drag. Touch drags from the card body require a long press so the board still scrolls.
323
+ */
324
+ dragFrom?: 'card' | 'handle';
325
+ /**
326
+ * Move the whole selection when a selected card is picked up.
327
+ */
328
+ multiMove?: boolean;
329
+ /**
330
+ * Scroll the board and columns while dragging near an edge.
331
+ */
332
+ autoScroll?: boolean;
333
+ /**
334
+ * Whether exceeding a limit warns or refuses.
335
+ */
336
+ wipPolicy?: 'warn' | 'block';
337
+ /**
338
+ * Ordered visual card rules.
339
+ */
340
+ rules?: KanbanRule[];
341
+ /**
342
+ * Board search text.
343
+ */
344
+ search?: string;
345
+ /**
346
+ * Field ids searched; null searches visible fields
347
+ * plus the title and subtitle sources.
348
+ */
349
+ searchFields?: string[] | null;
350
+ /**
351
+ * Extra card predicate.
352
+ */
353
+ filter?: ((record: KanbanRecord, index: number) => boolean) | null;
354
+ /**
355
+ * Render the built-in board search control.
356
+ */
357
+ searchControl?: boolean;
358
+ /**
359
+ * Record committed local moves for undo and redo.
360
+ */
361
+ history?: boolean;
362
+ /**
363
+ * Maximum remembered move steps.
364
+ */
365
+ historyLimit?: number;
366
+ /**
367
+ * Render built-in undo and redo controls.
368
+ */
369
+ historyControls?: boolean;
370
+ /**
371
+ * CSS length turning each column into a scroll region.
372
+ */
373
+ columnHeight?: string | null;
374
+ /**
375
+ * Render a per-column add control that emits `recordadd`.
376
+ */
377
+ allowAdd?: boolean | ((column: KanbanColumn, swimlane: KanbanSwimlane | null) => boolean);
378
+ /**
379
+ * Card context menu. `true` offers move commands; an array or function appends application items.
380
+ */
381
+ contextMenu?: boolean | MenuItem[] | ((context: KanbanCardContext) => MenuItem[]);
382
+ /**
383
+ * Replaces card content.
384
+ */
385
+ renderCard?: ((context: KanbanCardContext) => Node | null | undefined) | null;
386
+ /**
387
+ * Replaces column header content.
388
+ */
389
+ renderColumnHeader?: ((context: KanbanHeaderContext) => Node | null | undefined) | null;
390
+ /**
391
+ * Replaces lane header content.
392
+ */
393
+ renderSwimlaneHeader?: ((context: KanbanHeaderContext) => Node | null | undefined) | null;
394
+ /**
395
+ * Empty-column placeholder.
396
+ */
397
+ renderColumnEmpty?: ((context: KanbanHeaderContext) => Node | null | undefined) | null;
398
+ /**
399
+ * Replaces the floating drag preview.
400
+ */
401
+ renderDragPreview?: ((context: {
402
+ records: KanbanRecord[];
403
+ ids: unknown[];
404
+ count: number;
405
+ }) => Node | null | undefined) | null;
406
+ /**
407
+ * Overrides for every visible and announced string.
408
+ */
409
+ labels?: Record<string, string>;
230
410
  /**
231
411
  * Preferred column order.
232
412
  */
@@ -260,7 +440,20 @@ export type KanbanViewOptions = {
260
440
  */
261
441
  label?: string;
262
442
  onrecordmove?: (event: CustomEvent<Record<string, unknown>>) => void;
443
+ onmovereject?: (event: CustomEvent<Record<string, unknown>>) => void;
263
444
  onrecordaction?: (event: CustomEvent<Record<string, unknown>>) => void;
445
+ onrecordadd?: (event: CustomEvent<Record<string, unknown>>) => void;
446
+ onsearchchange?: (event: CustomEvent<{
447
+ search: string;
448
+ }>) => void;
449
+ onhistorychange?: (event: CustomEvent<{
450
+ canUndo: boolean;
451
+ canRedo: boolean;
452
+ depth: {
453
+ undo: number;
454
+ redo: number;
455
+ };
456
+ }>) => void;
264
457
  };
265
458
  export type KanbanCardMeta = {
266
459
  record: KanbanRecord;
@@ -275,16 +468,74 @@ export type KanbanSectionMeta = {
275
468
  list: HTMLElement;
276
469
  };
277
470
  export type KanbanKeyboardMove = {
278
- id: unknown;
471
+ ids: unknown[];
279
472
  from: KanbanMovePoint;
280
473
  to: KanbanMovePoint;
281
474
  };
282
475
  /**
283
- * Configurable record board with semantic columns, optional swim lanes, and equivalent pointer and
284
- * keyboard movement. Local moves clone records; callback grouping remains fully usable for display
285
- * and external moves but cannot be written safely by local cross-axis moves.
476
+ * Every user-visible and announced string, so a host can translate the board without patching it.
477
+ * Values run through `printf`, so named placeholders may be reordered or dropped by a translation.
478
+ */
479
+ declare const DEFAULT_LABELS: Readonly<{
480
+ fields: "Fields";
481
+ search: "Search board";
482
+ searchPlaceholder: "Search cards";
483
+ clearSearch: "Clear search";
484
+ searchSummary: "%count% of %total% cards match.";
485
+ undo: "Undo";
486
+ redo: "Redo";
487
+ add: "Add card";
488
+ addTo: "Add card to %target%";
489
+ move: "Move %name%";
490
+ moveMultiple: "Move %count% selected cards";
491
+ collapseColumn: "Collapse column %label%";
492
+ expandColumn: "Expand column %label%";
493
+ collapseLane: "Collapse swim lane %label%";
494
+ expandLane: "Expand swim lane %label%";
495
+ count: "%count% records";
496
+ countFiltered: "%count% of %total% records";
497
+ countLimit: "%count% records, work-in-progress limit %limit%";
498
+ countLimitInLane: "%count% records in this lane, %total% of work-in-progress limit %limit% in the column";
499
+ empty: "No records";
500
+ columnEmpty: "";
501
+ selected: "Selected";
502
+ notSelected: "Not selected";
503
+ noMatches: "No cards match the current search.";
504
+ record: "record";
505
+ grabbed: "Grabbed %name%. Use left and right for columns, up and down for position, and Alt plus up or down for swim lanes.";
506
+ grabbedMultiple: "Grabbed %count% cards. Use left and right for columns, up and down for position, and Alt plus up or down for swim lanes.";
507
+ target: "Target %target%, position %position% of %total%.";
508
+ moved: "Moved %name% to %target%.%wip%";
509
+ movedMultiple: "Moved %count% cards to %target%.%wip%";
510
+ moveRequested: "Move requested for %name% to %target%.%wip%";
511
+ moveCanceled: "Move canceled for %name%.";
512
+ moveCanceledPlain: "Move canceled.";
513
+ limitReached: " Work-in-progress limit %limit% reached.";
514
+ limitExceeded: " Work-in-progress limit %limit% exceeded.";
515
+ rejectAccept: "Move rejected: %target% does not accept %name%.";
516
+ rejectTransition: "Move rejected: %target% does not accept cards from %origin%.";
517
+ rejectWip: "Move rejected: %target% is at its work-in-progress limit of %limit%.";
518
+ rejectDestination: "Move rejected: the destination is not available.";
519
+ rejectGrouping: "Move rejected: callback grouping requires external move mode.";
520
+ undone: "Move undone.";
521
+ redone: "Move redone.";
522
+ nothingToUndo: "Nothing to undo.";
523
+ nothingToRedo: "Nothing to redo.";
524
+ menuMoveTo: "Move to %target%";
525
+ menuMoveToLane: "Move to lane %target%";
526
+ dragPreview: "%count% cards";
527
+ }>;
528
+ /**
529
+ * Configurable record board with semantic columns, optional swim lanes, and equivalent pointer,
530
+ * touch, keyboard, context-menu, and programmatic movement. Local moves clone records; callback
531
+ * grouping remains fully usable for display and external moves but cannot be written safely by
532
+ * local cross-axis moves.
286
533
  * @fires KanbanView#recordmove
534
+ * @fires KanbanView#movereject
287
535
  * @fires KanbanView#recordaction
536
+ * @fires KanbanView#recordadd
537
+ * @fires KanbanView#searchchange
538
+ * @fires KanbanView#historychange
288
539
  * @extends {RecordView}
289
540
  */
290
541
  export declare class KanbanView extends RecordView {
@@ -296,8 +547,12 @@ export declare class KanbanView extends RecordView {
296
547
  _kanbanSwimlaneOrder: string[];
297
548
  _collapsedColumns: Set<string>;
298
549
  _collapsedSwimlanes: Set<string>;
550
+ _kanbanRules: import("./kanban-policy.js").KanbanRule[];
551
+ _kanbanSearch: any;
552
+ _kanbanFilter: ((record: KanbanRecord, index: number) => boolean) | ((record: KanbanRecord, index: number) => boolean);
553
+ _kanbanHistory: import("./kanban-policy.js").KanbanHistory;
299
554
  _keyboardMove: KanbanKeyboardMove | {
300
- id: unknown;
555
+ ids: unknown[];
301
556
  from: {
302
557
  column: string;
303
558
  lane: string | null;
@@ -309,12 +564,57 @@ export declare class KanbanView extends RecordView {
309
564
  index: number;
310
565
  };
311
566
  };
312
- _dragId: unknown;
313
- _dragDestination: KanbanMovePoint;
567
+ _dragIds: any[] | unknown[];
568
+ _dragDestination: KanbanMovePoint | {
569
+ column: any;
570
+ lane: any;
571
+ index: number;
572
+ };
573
+ _pointerStart: {
574
+ x: number;
575
+ y: number;
576
+ id: number;
577
+ ids: unknown[];
578
+ touch: boolean;
579
+ } | {
580
+ x: number;
581
+ y: number;
582
+ id: number;
583
+ ids: unknown[];
584
+ touch: boolean;
585
+ };
586
+ _pointerHold: number;
587
+ _dragFrame: number;
588
+ _dragPreview: HTMLElement;
589
+ _dragPointer: number;
590
+ _dragCaptured: boolean;
591
+ _searchTimer: number;
592
+ _dragPoint: {
593
+ x: number;
594
+ y: number;
595
+ } | {
596
+ x: number;
597
+ y: number;
598
+ } | {
599
+ x: number;
600
+ y: number;
601
+ } | {
602
+ x: number;
603
+ y: number;
604
+ } | {
605
+ x: number;
606
+ y: number;
607
+ };
608
+ _suppressClick: boolean;
314
609
  _announcementToken: number;
315
610
  _cardMeta: WeakMap<object, any>;
316
611
  _sectionMeta: WeakMap<object, any>;
317
612
  _cardsById: Map<any, any>;
613
+ _visibleIds: Set<any>;
614
+ _kanbanMenu: ContextMenu;
615
+ _searchInput: HTMLInputElement;
616
+ _undoButton: HTMLButtonElement;
617
+ _redoButton: HTMLButtonElement;
318
618
  _toolbar: HTMLElement;
319
619
  _board: HTMLElement;
320
620
  _live: HTMLElement;
@@ -412,15 +712,82 @@ export declare class KanbanView extends RecordView {
412
712
  /** Toggles a lane. @param {string} id Lane id. @returns {this} */
413
713
  toggleSwimlane(id: string): this;
414
714
  /**
415
- * Proposes and, in local mode, commits a record move. Destination acceptance is checked before
416
- * a cancelable `recordmove` event. WIP limits are reported but remain advisory.
715
+ * Proposes and, in local mode, commits a record move. Equivalent to `moveRecords([id], …)`.
417
716
  * @param {unknown} id Record id.
418
717
  * @param {KanbanDestination} destination Destination.
419
718
  * @returns {this}
420
719
  * @fires KanbanView#recordmove
720
+ * @fires KanbanView#movereject
421
721
  * @fires RecordView#datachange
422
722
  */
423
723
  moveRecord(id: unknown, destination: KanbanDestination): this;
724
+ /**
725
+ * Proposes and, in local mode, commits an atomic move of one or more records. Destination
726
+ * eligibility, transition rules, and work-in-progress policy are all checked before a single
727
+ * cancelable `recordmove` event; the move commits completely or not at all, and the moving
728
+ * records keep their relative order at the destination.
729
+ * @param {unknown[]} ids Record ids, in any order.
730
+ * @param {KanbanDestination} destination Destination.
731
+ * @returns {this}
732
+ * @fires KanbanView#recordmove
733
+ * @fires KanbanView#movereject
734
+ * @fires RecordView#datachange
735
+ */
736
+ moveRecords(ids: unknown[], destination: KanbanDestination): this;
737
+ /** Whether a committed local move can be reverted. @returns {boolean} */
738
+ canUndo(): boolean;
739
+ /** Whether a reverted local move can be reapplied. @returns {boolean} */
740
+ canRedo(): boolean;
741
+ /**
742
+ * Reverts the last committed local move, restoring the exact record order and selection.
743
+ * @returns {this}
744
+ * @fires RecordView#datachange
745
+ * @fires KanbanView#historychange
746
+ */
747
+ undo(): this;
748
+ /**
749
+ * Reapplies the last reverted local move.
750
+ * @returns {this}
751
+ * @fires RecordView#datachange
752
+ * @fires KanbanView#historychange
753
+ */
754
+ redo(): this;
755
+ /** Empties both history stacks. @returns {this} @fires KanbanView#historychange */
756
+ clearHistory(): this;
757
+ /** Returns the current board search text. @returns {string} */
758
+ getSearch(): string;
759
+ /**
760
+ * Narrows visible cards to those matching a search. Hidden records keep their position in the
761
+ * data, so a card dropped between two visible cards still lands between them.
762
+ * @param {string} search Search text.
763
+ * @param {{silent?:boolean}} [options={}] Event behavior.
764
+ * @returns {this}
765
+ * @fires KanbanView#searchchange
766
+ */
767
+ setSearch(search: string, options?: {
768
+ silent?: boolean;
769
+ }): this;
770
+ /**
771
+ * Replaces the extra card predicate. Pass null to clear it.
772
+ * @param {((record:KanbanRecord,index:number)=>boolean)|null} filter Predicate.
773
+ * @returns {this}
774
+ */
775
+ setFilter(filter: ((record: KanbanRecord, index: number) => boolean) | null): this;
776
+ /** Returns the records currently rendered, in board order. @returns {KanbanRecord[]} */
777
+ getVisibleRecords(): KanbanRecord[];
778
+ /**
779
+ * Replaces every record. History is dropped because its snapshots describe records the host has
780
+ * just replaced, and reinstating them would resurrect data the application no longer owns.
781
+ * @param {KanbanRecord[]} records New records.
782
+ * @returns {this}
783
+ */
784
+ setData(records: KanbanRecord[]): this;
785
+ /** @param {KanbanRecord[]} records Records to append. @returns {this} */
786
+ addData(records: KanbanRecord[]): this;
787
+ /** @param {unknown} id Existing record id. @param {KanbanRecord} record Replacement. @returns {this} */
788
+ updateRecord(id: unknown, record: KanbanRecord): this;
789
+ /** @param {unknown} id Record id. @returns {this} */
790
+ removeRecord(id: unknown): this;
424
791
  /** Returns common plus board-specific serializable configuration. @returns {KanbanViewState} */
425
792
  getViewState(): KanbanViewState;
426
793
  /**
@@ -453,8 +820,52 @@ export declare class KanbanView extends RecordView {
453
820
  }[], hasLanes: boolean): HTMLElement;
454
821
  /** @param {KanbanRecord} record @param {number} index @param {string} column @param {string|null} lane @param {boolean} hasLanes @returns {HTMLLIElement} */
455
822
  _renderCard(record: KanbanRecord, index: number, column: string, lane: string | null, hasLanes: boolean): HTMLLIElement;
456
- /** @returns {HTMLElement} */
457
- _emptyContent(): HTMLElement;
823
+ /**
824
+ * Hands card content to `renderCard` while keeping the managed shell: the list item, its
825
+ * selection control, the action group, and the move handle are never given away, because they
826
+ * carry the drop, focus, and movement semantics the board is responsible for.
827
+ * @param {HTMLElement} card Card built from the shared anatomy.
828
+ * @param {KanbanCardContext} context Card context.
829
+ * @returns {void}
830
+ */
831
+ _applyCardContent(card: HTMLElement, context: KanbanCardContext): void;
832
+ /**
833
+ * Applies ordered card rules. The first match owns the marker tone; every match with a label adds
834
+ * a badge, and rule descriptions join the accessible description so the marker is never the only
835
+ * carrier of the meaning.
836
+ * @param {HTMLElement} card Card element.
837
+ * @param {KanbanRule[]} rules Matching rules.
838
+ * @returns {void}
839
+ */
840
+ _applyCardRules(card: HTMLElement, rules: KanbanRule[]): void;
841
+ /**
842
+ * Composes one card's accessible description from its selection state and its rules, so that
843
+ * refreshing selection cannot quietly drop what a rule contributed.
844
+ * @param {HTMLElement} card Card element.
845
+ * @param {unknown} id Record id.
846
+ * @returns {void}
847
+ */
848
+ _applyCardDescription(card: HTMLElement, id: unknown): void;
849
+ /** @param {string} text Board-level empty message. @returns {HTMLElement} */
850
+ _emptyContent(text: string): HTMLElement;
851
+ /** @returns {string} Per-column placeholder text, or an empty string for none. */
852
+ _placeholderText(): string;
853
+ /**
854
+ * @param {number} count Visible records.
855
+ * @param {number} total Records before search and filters.
856
+ * @param {number|null} limit Applicable limit.
857
+ * @returns {HTMLElement}
858
+ */
859
+ _countIndicator(count: number, total: number, limit: number | null): HTMLElement;
860
+ /**
861
+ * @param {number} count Visible records in this column and lane.
862
+ * @param {number} total Records in this column and lane before search and filters.
863
+ * @param {number} columnTotal Records across the whole column.
864
+ * @param {number|null} limit Applicable limit.
865
+ * @param {boolean} columnScoped Whether the limit counts the column rather than this lane.
866
+ * @returns {HTMLElement}
867
+ */
868
+ _columnIndicator(count: number, total: number, columnTotal: number, limit: number | null, columnScoped: boolean): HTMLElement;
458
869
  /** @returns {KanbanColumn[]} */
459
870
  _rawColumns(): KanbanColumn[];
460
871
  /** @returns {KanbanColumn[]} */
@@ -472,33 +883,204 @@ export declare class KanbanView extends RecordView {
472
883
  }[];
473
884
  /** @param {unknown} id @returns {KanbanMovePoint|null} */
474
885
  _recordLocation(id: unknown): KanbanMovePoint | null;
886
+ /**
887
+ * Recomputes which records survive the search and the filter predicate.
888
+ * @returns {void}
889
+ */
890
+ _resolveVisibility(): void;
891
+ /**
892
+ * Resolves what a search looks at, once. `searchFields` names them explicitly; otherwise the
893
+ * visible fields are searched, plus the title and subtitle sources — a card is findable by the
894
+ * name it shows even when that field is hidden from the metadata list.
895
+ * @returns {{fields:import('../view/record-view.js').ViewField[],readers:((record:KanbanRecord,index:number)=>unknown)[]}}
896
+ */
897
+ _searchSources(): {
898
+ fields: import('../view/record-view.js').ViewField[];
899
+ readers: ((record: KanbanRecord, index: number) => unknown)[];
900
+ };
901
+ /**
902
+ * Builds the text one record is searched through. Field `render` output is deliberately ignored:
903
+ * it may be a DOM node, and matching what a formatter produced rather than what the record holds
904
+ * would make the same query behave differently in two views over the same data.
905
+ * @param {KanbanRecord} record Record.
906
+ * @param {number} index Display index.
907
+ * @param {{fields:import('../view/record-view.js').ViewField[],readers:((record:KanbanRecord,index:number)=>unknown)[]}} sources Resolved sources.
908
+ * @returns {string} Searchable text.
909
+ */
910
+ _searchableText(record: KanbanRecord, index: number, sources: {
911
+ fields: import('../view/record-view.js').ViewField[];
912
+ readers: ((record: KanbanRecord, index: number) => unknown)[];
913
+ }): string;
914
+ /**
915
+ * Translates a position among rendered cards into a position among all records in one bucket.
916
+ * @param {string} column Column id.
917
+ * @param {string|null} lane Lane id.
918
+ * @param {number} visibleIndex Position among visible cards.
919
+ * @param {Set<unknown>} excluded Ids being moved, which the visible list already omits.
920
+ * @returns {number} Absolute position.
921
+ */
922
+ _absoluteIndex(column: string, lane: string | null, visibleIndex: number, excluded: Set<unknown>): number;
475
923
  /**
476
924
  * @param {KanbanRecord} record @param {KanbanMovePoint} from @param {KanbanMovePoint} to
477
925
  * @param {KanbanColumn} column @param {KanbanSwimlane|null} lane @returns {KanbanRecord|null}
478
926
  */
479
927
  _localMovedRecord(record: KanbanRecord, from: KanbanMovePoint, to: KanbanMovePoint, column: KanbanColumn, lane: KanbanSwimlane | null): KanbanRecord | null;
928
+ /**
929
+ * Runs transition, eligibility, and capacity policy for a whole move. Every distinct origin is
930
+ * checked, so a multi-card move drawn from two columns cannot slip past a transition rule that
931
+ * only the second origin violates.
932
+ * @param {KanbanRecord[]} records Moving records.
933
+ * @param {KanbanMovePoint[]} origins Their origins.
934
+ * @param {KanbanColumn} column Destination column.
935
+ * @param {KanbanSwimlane|null} lane Destination lane.
936
+ * @param {KanbanMovePoint} to Destination point.
937
+ * @param {KanbanMoveContext} context Predicate context.
938
+ * @param {Set<unknown>} excluded Moving ids, excluded from destination counts.
939
+ * @returns {KanbanMoveEvaluation} Decision.
940
+ */
941
+ _evaluateMove(records: KanbanRecord[], origins: KanbanMovePoint[], column: KanbanColumn, lane: KanbanSwimlane | null, to: KanbanMovePoint, context: KanbanMoveContext, excluded: Set<unknown>): KanbanMoveEvaluation;
942
+ /**
943
+ * Announces and reports a refused move. Nothing is written and no `recordmove` is emitted, so a
944
+ * listener can neither observe nor undo a move that never happened.
945
+ * @param {KanbanRejectReason} reason Refusal reason.
946
+ * @param {{ids:unknown[],records:KanbanRecord[],from:KanbanMovePoint|null,to:KanbanMovePoint|null,column:KanbanColumn|null,swimlane:KanbanSwimlane|null,evaluation?:KanbanMoveEvaluation}} detail Context.
947
+ * @returns {void}
948
+ * @fires KanbanView#movereject
949
+ */
950
+ _rejectMove(reason: KanbanRejectReason, detail: {
951
+ ids: unknown[];
952
+ records: KanbanRecord[];
953
+ from: KanbanMovePoint | null;
954
+ to: KanbanMovePoint | null;
955
+ column: KanbanColumn | null;
956
+ swimlane: KanbanSwimlane | null;
957
+ evaluation?: KanbanMoveEvaluation;
958
+ }): void;
959
+ /**
960
+ * Installs a committed local record order, remembering the previous one for undo.
961
+ * @param {KanbanRecord[]} records Next record order.
962
+ * @returns {void}
963
+ */
964
+ _commitLocalRecords(records: KanbanRecord[]): void;
965
+ /**
966
+ * @param {'undo'|'redo'} direction Travel direction.
967
+ * @returns {this}
968
+ */
969
+ _travelHistory(direction: 'undo' | 'redo'): this;
970
+ /** Drops both stacks without announcing, for host-driven data replacement. @returns {void} */
971
+ _resetHistory(): void;
972
+ /** @returns {void} */
973
+ _emitHistory(): void;
974
+ /** @returns {void} */
975
+ _syncHistoryControls(): void;
976
+ /** @returns {HTMLElement} Built-in search control. */
977
+ _createSearchControl(): HTMLElement;
978
+ /** @returns {HTMLElement} Built-in history controls. */
979
+ _createHistoryControls(): HTMLElement;
980
+ /** Attaches the card context menu. @returns {void} */
981
+ _createContextMenu(): void;
982
+ /**
983
+ * Builds the menu for one card: every destination the same policy path would accept, plus any
984
+ * application items. Destinations the board would refuse are shown disabled rather than hidden,
985
+ * so a rule is discoverable instead of silently removing a command.
986
+ * @param {Element|null} context Card element the menu opened on.
987
+ * @returns {(MenuItem|'-')[]} Menu entries.
988
+ */
989
+ _menuItems(context: Element | null): (MenuItem | '-')[];
990
+ /**
991
+ * @param {{value:unknown,item:MenuItem,context:Element|null}} detail Selection detail.
992
+ * @returns {void}
993
+ */
994
+ _handleMenuSelect(detail: {
995
+ value: unknown;
996
+ item: MenuItem;
997
+ context: Element | null;
998
+ }): void;
999
+ /**
1000
+ * Dry-runs the policy path for a destination without emitting or committing anything.
1001
+ * @param {unknown[]} ids Moving ids.
1002
+ * @param {KanbanDestination} destination Destination.
1003
+ * @returns {boolean} Whether the move would be allowed.
1004
+ */
1005
+ _canMove(ids: unknown[], destination: KanbanDestination): boolean;
1006
+ /**
1007
+ * Resolves the ids one gesture moves: the whole selection when the grabbed card belongs to it,
1008
+ * otherwise just that card. Picking up an unselected card never drags the selection along.
1009
+ * @param {unknown} id Grabbed record id.
1010
+ * @returns {unknown[]} Ids to move, in board order.
1011
+ */
1012
+ _movingIds(id: unknown): unknown[];
1013
+ /** @param {unknown[]} ids Requested ids. @returns {unknown[]} Existing ids in board order. */
1014
+ _orderedIds(ids: unknown[]): unknown[];
480
1015
  /** @param {MouseEvent} event @returns {void} */
481
1016
  _handleBoardClick(event: MouseEvent): void;
482
1017
  /** @param {MouseEvent} event @returns {void} */
483
1018
  _handleBoardDoubleClick(event: MouseEvent): void;
484
1019
  /** @param {KeyboardEvent} event @returns {void} */
485
1020
  _handleBoardKeydown(event: KeyboardEvent): void;
1021
+ /**
1022
+ * @param {KeyboardEvent} event Keyboard event.
1023
+ * @param {Element|null} target Event target.
1024
+ * @returns {boolean} Whether the event was handled as a history command.
1025
+ */
1026
+ _handleHistoryKeydown(event: KeyboardEvent, target: Element | null): boolean;
486
1027
  /** @param {KeyboardEvent} event @param {HTMLElement} handle @param {KanbanCardMeta} meta @returns {void} */
487
1028
  _handleMoveKeydown(event: KeyboardEvent, handle: HTMLElement, meta: KanbanCardMeta): void;
488
1029
  /** @param {Event} event @returns {void} */
489
1030
  _handlePreviewError(event: Event): void;
490
- /** @param {DragEvent} event @returns {void} */
491
- _handleDragStart(event: DragEvent): void;
492
- /** @param {DragEvent} event @returns {void} */
493
- _handleDragOver(event: DragEvent): void;
494
- /** @param {DragEvent} event @returns {void} */
495
- _handleDrop(event: DragEvent): void;
496
- /** @param {DragEvent} event @param {KanbanSectionMeta} section @returns {KanbanMovePoint} */
497
- _pointerDestination(event: DragEvent, section: KanbanSectionMeta): KanbanMovePoint;
1031
+ /**
1032
+ * Records a candidate drag. Nothing moves yet: a press that never travels stays a click, and a
1033
+ * touch on the card body stays a scroll until the long press elapses.
1034
+ * @param {PointerEvent} event Pointer event.
1035
+ * @returns {void}
1036
+ */
1037
+ _handlePointerDown(event: PointerEvent): void;
1038
+ /** @param {PointerEvent} event @returns {void} */
1039
+ _handlePointerMove(event: PointerEvent): void;
1040
+ /** @param {PointerEvent} event @returns {void} */
1041
+ _handlePointerUp(event: PointerEvent): void;
1042
+ /**
1043
+ * Starts a pointer drag: the floating preview appears, the board stops selecting text, and the
1044
+ * auto-scroll loop begins.
1045
+ * @param {number} x Pointer client x.
1046
+ * @param {number} y Pointer client y.
1047
+ * @returns {void}
1048
+ */
1049
+ _beginDrag(x: number, y: number): void;
1050
+ /** @returns {HTMLElement} Floating drag preview. */
1051
+ _createDragPreview(): HTMLElement;
1052
+ /** @returns {void} */
1053
+ _positionDragPreview(): void;
1054
+ /**
1055
+ * Resolves and marks the drop destination under the pointer. Pointer capture retargets events to
1056
+ * the board, so the element under the pointer is looked up rather than read off the event.
1057
+ * @param {number} x Pointer client x.
1058
+ * @param {number} y Pointer client y.
1059
+ * @returns {void}
1060
+ */
1061
+ _updateDropTarget(x: number, y: number): void;
1062
+ /** @returns {void} */
1063
+ _scheduleAutoScroll(): void;
1064
+ /** Scrolls the board horizontally and the hovered column vertically near their edges. @returns {void} */
1065
+ _autoScroll(): void;
1066
+ /**
1067
+ * Commits a move whose index came from rendered cards. Search and filters hide records without
1068
+ * removing them, so the visible position is translated back before anything is proposed.
1069
+ * @param {unknown[]} ids Moving ids.
1070
+ * @param {KanbanMovePoint} point Destination in visible-card space.
1071
+ * @returns {void}
1072
+ */
1073
+ _commitInteractionMove(ids: unknown[], point: KanbanMovePoint): void;
498
1074
  /** @returns {void} */
499
1075
  _clearDrag(): void;
500
1076
  /** @returns {void} */
501
1077
  _clearDropMarkers(): void;
1078
+ /** @param {HTMLElement} list Card list. @returns {HTMLElement[]} Cards excluding the ones in flight. */
1079
+ _draggableCards(list: HTMLElement): HTMLElement[];
1080
+ /** @param {string} column @param {string|null} lane @param {Set<unknown>} excluded @returns {number} */
1081
+ _visibleCount(column: string, lane: string | null, excluded: Set<unknown>): number;
1082
+ /** @param {unknown} id Record id. @returns {number} Position among visible cards in its bucket. */
1083
+ _visibleIndex(id: unknown): number;
502
1084
  /** @returns {void} */
503
1085
  _syncKeyboardTarget(): void;
504
1086
  /** @returns {void} */
@@ -507,10 +1089,39 @@ export declare class KanbanView extends RecordView {
507
1089
  _metaForTarget(target: EventTarget | null): KanbanCardMeta | null;
508
1090
  /** @param {unknown} id @returns {HTMLElement|null} */
509
1091
  _moveHandle(id: unknown): HTMLElement | null;
1092
+ /**
1093
+ * Resolves a collapse control after a board refresh, retaining the same lane copy for columns.
1094
+ * @param {'column'|'swimlane'} axis Collapse axis.
1095
+ * @param {string} id Descriptor id.
1096
+ * @param {string|null} [lane=null] Column copy's lane.
1097
+ * @returns {HTMLElement|null}
1098
+ */
1099
+ _collapseControl(axis: 'column' | 'swimlane', id: string, lane?: string | null): HTMLElement | null;
1100
+ /**
1101
+ * Runs one presentation hook, ignoring anything that is not a node so a hook cannot smuggle
1102
+ * markup in as a string.
1103
+ * @param {'renderCard'|'renderColumnHeader'|'renderSwimlaneHeader'|'renderColumnEmpty'|'renderDragPreview'} name Hook name.
1104
+ * @param {any} context Hook context.
1105
+ * @returns {Node|null} Node to adopt, or null for the default presentation.
1106
+ */
1107
+ _renderHook(name: 'renderCard' | 'renderColumnHeader' | 'renderSwimlaneHeader' | 'renderColumnEmpty' | 'renderDragPreview', context: any): Node | null;
1108
+ /** @param {KanbanColumn} column @param {KanbanSwimlane|null} lane @returns {boolean} */
1109
+ _allowsAdd(column: KanbanColumn, lane: KanbanSwimlane | null): boolean;
510
1110
  /** @param {KanbanRecord} record @param {number} index @returns {string} */
511
1111
  _recordName(record: KanbanRecord, index: number): string;
1112
+ /** @param {KanbanRecord[]} records Moving records. @returns {string} */
1113
+ _movingName(records: KanbanRecord[]): string;
512
1114
  /** @param {KanbanMovePoint} point @returns {string} */
513
1115
  _targetName(point: KanbanMovePoint): string;
1116
+ /** @param {KanbanMoveEvaluation} evaluation Capacity report. @returns {string} */
1117
+ _wipAnnouncement(evaluation: KanbanMoveEvaluation): string;
1118
+ /**
1119
+ * Resolves one localizable string.
1120
+ * @param {keyof typeof DEFAULT_LABELS|string} key Label key.
1121
+ * @param {Record<string, unknown>} [args] Placeholder values.
1122
+ * @returns {string} Resolved text.
1123
+ */
1124
+ _text(key: keyof typeof DEFAULT_LABELS | string, args?: Record<string, unknown>): string;
514
1125
  /** @param {string} message @returns {void} */
515
1126
  _announce(message: string): void;
516
1127
  /** @param {string} reason @returns {void} */
@@ -542,8 +1153,20 @@ export declare function reconcileKanbanOrder(descriptors: {
542
1153
  id: string;
543
1154
  }[], order: unknown): string[];
544
1155
  /**
545
- * Reorders records around an already-cloned moved record. The input array and every input record
546
- * remain untouched; destination index is relative to records in that one column/lane.
1156
+ * Reconciles the visible descriptor order while optionally retaining preferences for derived axes
1157
+ * that are absent from the current result set. This lets saved state be applied before an async
1158
+ * query supplies the records from which columns or lanes are derived.
1159
+ * @param {{id:string}[]} descriptors Available descriptors.
1160
+ * @param {unknown} order Preferred order.
1161
+ * @param {boolean} [preserveUnknown=false] Retain temporarily unavailable ids.
1162
+ * @returns {string[]}
1163
+ */
1164
+ export declare function reconcileKanbanPreference(descriptors: {
1165
+ id: string;
1166
+ }[], order: unknown, preserveUnknown?: boolean): string[];
1167
+ /**
1168
+ * Reorders records around one already-cloned moved record. Retained as the single-record form of
1169
+ * `reorderKanbanRecords`; the input array and every input record remain untouched.
547
1170
  * @param {KanbanRecord[]} records Records.
548
1171
  * @param {unknown} id Moving record id.
549
1172
  * @param {KanbanRecord} movedRecord Cloned record carrying its destination axis values.
@@ -560,13 +1183,35 @@ export declare function reorderKanbanData(records: KanbanRecord[], id: unknown,
560
1183
  from: KanbanMovePoint;
561
1184
  to: KanbanMovePoint;
562
1185
  } | null;
1186
+ export {};
563
1187
  /**
564
- * Record movement event emitted before a local commit or external request.
1188
+ * Record movement event emitted before a local commit or external request. `record`, `id`, and
1189
+ * `from` describe the primary record; `records`, `ids`, and `moves` describe the whole atomic move.
565
1190
  * @event KanbanView#recordmove
566
- * @type {CustomEvent<{record:KanbanRecord,id:unknown,from:KanbanMovePoint,to:KanbanMovePoint,column:KanbanColumn,swimlane:KanbanSwimlane|null,limitExceeded:boolean}>}
1191
+ * @type {CustomEvent<{record:KanbanRecord,id:unknown,records:KanbanRecord[],ids:unknown[],moves:{id:unknown,from:KanbanMovePoint}[],from:KanbanMovePoint,to:KanbanMovePoint,column:KanbanColumn,swimlane:KanbanSwimlane|null,limitExceeded:boolean}>}
1192
+ */
1193
+ /**
1194
+ * Refused movement event. Nothing was written and no `recordmove` was emitted.
1195
+ * @event KanbanView#movereject
1196
+ * @type {CustomEvent<{reason:KanbanRejectReason,ids:unknown[],records:KanbanRecord[],from:KanbanMovePoint|null,to:KanbanMovePoint|null,column:KanbanColumn|null,swimlane:KanbanSwimlane|null,limit:number|null,count:number,message:string}>}
567
1197
  */
568
1198
  /**
569
1199
  * Secondary card action event.
570
1200
  * @event KanbanView#recordaction
571
1201
  * @type {CustomEvent<{record:KanbanRecord,id:unknown,action:unknown,actionId:string|undefined,event:Event}>}
572
1202
  */
1203
+ /**
1204
+ * Request to create a record in one column and lane. The board never creates records itself.
1205
+ * @event KanbanView#recordadd
1206
+ * @type {CustomEvent<{column:KanbanColumn|null,swimlane:KanbanSwimlane|null,columnId:string,laneId:string|null,event:Event}>}
1207
+ */
1208
+ /**
1209
+ * Board search change from the built-in control.
1210
+ * @event KanbanView#searchchange
1211
+ * @type {CustomEvent<{search:string}>}
1212
+ */
1213
+ /**
1214
+ * Undo and redo availability after a committed, reverted, or discarded local move.
1215
+ * @event KanbanView#historychange
1216
+ * @type {CustomEvent<{canUndo:boolean,canRedo:boolean,depth:{undo:number,redo:number}}>}
1217
+ */