forty-cdk 0.24.1 → 0.25.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 (64) hide show
  1. package/accordion/README.md +7 -0
  2. package/aspect-ratio/README.md +6 -0
  3. package/avatar/README.md +6 -0
  4. package/breadcrumbs/README.md +7 -0
  5. package/breakpoints/README.md +6 -0
  6. package/button/README.md +8 -1
  7. package/calendar/README.md +10 -1
  8. package/carousel/README.md +7 -0
  9. package/checkbox/README.md +7 -0
  10. package/combobox/README.md +8 -1
  11. package/context-menu/README.md +7 -0
  12. package/date-field/README.md +9 -2
  13. package/date-picker/README.md +7 -0
  14. package/dialog/README.md +7 -0
  15. package/disclosure/README.md +7 -0
  16. package/drag-drop/README.md +6 -0
  17. package/drawer/README.md +7 -0
  18. package/dropdown-menu/README.md +7 -0
  19. package/fesm2022/forty-cdk-table-virtualization.mjs +20 -3
  20. package/fesm2022/forty-cdk-table-virtualization.mjs.map +1 -1
  21. package/fesm2022/forty-cdk-table.mjs +74 -11
  22. package/fesm2022/forty-cdk-table.mjs.map +1 -1
  23. package/field/README.md +7 -1
  24. package/fieldset/README.md +7 -1
  25. package/file-upload/README.md +7 -1
  26. package/hover-card/README.md +6 -0
  27. package/input/README.md +7 -1
  28. package/internationalized-date/README.md +7 -1
  29. package/listbox/README.md +7 -0
  30. package/menu/README.md +7 -0
  31. package/menubar/README.md +7 -0
  32. package/meter/README.md +7 -0
  33. package/navigation-menu/README.md +7 -0
  34. package/number-input/README.md +7 -0
  35. package/otp-input/README.md +6 -0
  36. package/package.json +1 -1
  37. package/pagination/README.md +7 -0
  38. package/pane-resizer/README.md +7 -0
  39. package/popover/README.md +7 -0
  40. package/progress/README.md +7 -0
  41. package/radio-group/README.md +7 -0
  42. package/scroll-area/README.md +6 -0
  43. package/search/README.md +6 -0
  44. package/select/README.md +7 -0
  45. package/separator/README.md +6 -0
  46. package/shared/README.md +6 -0
  47. package/slider/README.md +7 -0
  48. package/stepper/README.md +7 -0
  49. package/switch/README.md +7 -0
  50. package/table/README.md +89 -16
  51. package/table-virtualization/README.md +38 -10
  52. package/tabs/README.md +7 -0
  53. package/time-field/README.md +9 -2
  54. package/time-picker/README.md +7 -0
  55. package/toast/README.md +7 -0
  56. package/toggle/README.md +7 -0
  57. package/toolbar/README.md +7 -0
  58. package/tooltip/README.md +7 -0
  59. package/tree/README.md +8 -1
  60. package/types/forty-cdk-table-virtualization.d.ts +18 -3
  61. package/types/forty-cdk-table.d.ts +78 -25
  62. package/virtual-reorder/README.md +7 -0
  63. package/virtualization/README.md +6 -0
  64. package/visually-hidden/README.md +6 -0
@@ -1,3 +1,10 @@
1
+ ---
2
+ title: Accordion
3
+ group: primitives
4
+ archetype: [composable-ui]
5
+ apgUrl: https://www.w3.org/WAI/ARIA/apg/patterns/accordion/
6
+ ---
7
+
1
8
  # Accordion
2
9
 
3
10
  A stack of collapsible sections, optionally allowing multiple panels open at once.
@@ -1,3 +1,9 @@
1
+ ---
2
+ title: Aspect Ratio
3
+ group: primitives
4
+ archetype: [composable-ui]
5
+ ---
6
+
1
7
  # AspectRatio
2
8
 
3
9
  A container that keeps its content at a fixed width-to-height ratio.
package/avatar/README.md CHANGED
@@ -1,3 +1,9 @@
1
+ ---
2
+ title: Avatar
3
+ group: primitives
4
+ archetype: [composable-ui]
5
+ ---
6
+
1
7
  # Avatar
2
8
 
3
9
  A user image with a graceful fallback across its loading lifecycle.
@@ -1,3 +1,10 @@
1
+ ---
2
+ title: Breadcrumbs
3
+ group: primitives
4
+ archetype: [composable-ui]
5
+ apgUrl: https://www.w3.org/WAI/ARIA/apg/patterns/breadcrumb/
6
+ ---
7
+
1
8
  # Breadcrumbs
2
9
 
3
10
  A labelled navigation landmark for a breadcrumb trail: links with aria-current='page' on the current page and decorative separators hidden from assistive technology.
@@ -1,3 +1,9 @@
1
+ ---
2
+ title: Breakpoints
3
+ group: utilities
4
+ archetype: [headless-utility]
5
+ ---
6
+
1
7
  # Breakpoints
2
8
 
3
9
  A signal-first, zoneless, SSR-safe viewport breakpoint observer (injectBreakpoints). Configure the breakpoint map once via provideForBreakpointsDefaults — or use the Tailwind scale by default — then read up / down / between / only / active or any arbitrary media query, each as a live Signal<boolean>.
package/button/README.md CHANGED
@@ -1,6 +1,13 @@
1
+ ---
2
+ title: Button
3
+ group: primitives
4
+ archetype: [composable-ui]
5
+ apgUrl: https://www.w3.org/WAI/ARIA/apg/patterns/button/
6
+ ---
7
+
1
8
  # ForButton
2
9
 
3
- Turns any element — a native <button> or a custom host like <div> / <span> — into an accessible button with keyboard activation. Disabled stays focusable (aria-disabled, never the native attribute) and pressed / hovered / focus-visible are reflected as data-\* hooks.
10
+ Turns any element — a native `<button>` or a custom host like `<div>` / `<span>` — into an accessible button with keyboard activation. Disabled stays focusable (aria-disabled, never the native attribute) and pressed / hovered / focus-visible are reflected as data-\* hooks.
4
11
 
5
12
  A single `[forButton]` directive does all of this. On a native `<button>` host the platform owns Enter/Space activation and `type` handling; on any non-button host the directive adds `role="button"`, `tabindex="0"`, and keyboard activation so the contract matches.
6
13
 
@@ -1,3 +1,10 @@
1
+ ---
2
+ title: Calendar
3
+ group: primitives
4
+ archetype: [composable-ui, form-control]
5
+ apgUrl: https://www.w3.org/WAI/ARIA/apg/patterns/grid/
6
+ ---
7
+
1
8
  # Calendar
2
9
 
3
10
  A single-date calendar grid implementing the APG Grid pattern over a pluggable date adapter: roving-tabindex day navigation, month / year paging, and min / max / per-date availability.
@@ -26,6 +33,8 @@ bootstrapApplication(App, {
26
33
 
27
34
  `@internationalized/date` is a widely-used immutable date primitive; it works in every browser today with no polyfill, and its reference-equality-on-mutation makes it signal-friendly.
28
35
 
36
+ → **[Date adapters](../../../docs/date-adapters.md)** — picking one, the optional peer dependency, and writing your own.
37
+
29
38
  **Calendar system (Gregorian).** The adapter seam abstracts the date _library_ and locale-aware _formatting_, not the calendar _system_'s month structure. Both `@internationalized/date` adapters build Gregorian dates, so the grid stays Gregorian regardless of the runtime locale, and the grid, the month picker and the date field all assume a Gregorian-structured year — exactly twelve months, `month` **1-12**, the year ending at month 12. Adapters over calendars with a different month structure (e.g. a 13-month year) are not supported. The optional `compareDate` hook overrides day-only _ordering_ only — it does not make the grid non-Gregorian.
30
39
 
31
40
  ## Anatomy
@@ -401,7 +410,7 @@ Auto-disabled when the entire previous / next page would be outside `[min, max]`
401
410
  | `[forCalendarYearCell]` | `data-highlighted` | present \| absent |
402
411
  | `[forCalendarYearCell]` | `data-disabled` | present \| absent |
403
412
 
404
- ## Scope defaults
413
+ ## Scoped defaults
405
414
 
406
415
  ```ts
407
416
  import { provideForCalendarDefaults } from 'forty-cdk/calendar';
@@ -1,3 +1,10 @@
1
+ ---
2
+ title: Carousel
3
+ group: primitives
4
+ archetype: [composable-ui]
5
+ apgUrl: https://www.w3.org/WAI/ARIA/apg/patterns/carousel/
6
+ ---
7
+
1
8
  # ForCarousel
2
9
 
3
10
  A slideshow of content panels with previous / next controls, an indicator group, optional looping and multi-slide views, and an accessible autoplay mode with a pause control.
@@ -1,3 +1,10 @@
1
+ ---
2
+ title: Checkbox
3
+ group: primitives
4
+ archetype: [form-control]
5
+ apgUrl: https://www.w3.org/WAI/ARIA/apg/patterns/checkbox/
6
+ ---
7
+
1
8
  # Checkbox
2
9
 
3
10
  A checkbox supporting the three states checked, unchecked and indeterminate.
@@ -1,3 +1,10 @@
1
+ ---
2
+ title: Combobox
3
+ group: primitives
4
+ archetype: [overlay, form-control]
5
+ apgUrl: https://www.w3.org/WAI/ARIA/apg/patterns/combobox/
6
+ ---
7
+
1
8
  # Combobox
2
9
 
3
10
  An editable input paired with a filterable listbox popup, supporting single or multi selection with chips.
@@ -259,7 +266,7 @@ An action:
259
266
 
260
267
  Use `[forComboboxAction]` for a pinned side-effect. For an entry that _does_
261
268
  select (an "Add new" row that adds an item and commits it to `value`), use a
262
- plain `[forComboboxOption]` — see [Static options alongside the `@for`](#static-options-alongside-the-for).
269
+ plain `[forComboboxOption]` — see the static options under [Examples](#examples).
263
270
 
264
271
  ### Focus & keyboard (model A)
265
272
 
@@ -1,3 +1,10 @@
1
+ ---
2
+ title: Context Menu
3
+ group: primitives
4
+ archetype: [overlay]
5
+ apgUrl: https://www.w3.org/WAI/ARIA/apg/patterns/menu/
6
+ ---
7
+
1
8
  # ContextMenu
2
9
 
3
10
  A menu opened by right-click or long-press, anchored to the pointer position.
@@ -1,3 +1,10 @@
1
+ ---
2
+ title: Date Field
3
+ group: primitives
4
+ archetype: [form-control]
5
+ apgUrl: https://www.w3.org/WAI/ARIA/apg/patterns/spinbutton/
6
+ ---
7
+
1
8
  # DateField
2
9
 
3
10
  A segmented date (and optional time) input over a pluggable date adapter — each part a spinbutton with keyboard stepping, locale-driven segment order and min / max clamping.
@@ -171,7 +178,7 @@ Set `granularity` to `'hour'`, `'minute'`, or `'second'` (`granularity > 'day'`)
171
178
 
172
179
  On the AM/PM segment, `a` / `p` set the period and ArrowUp / ArrowDown toggle it; the period is derived from the entered hour, so clearing it is a no-op (clear or step the hour instead). The value stays `null` until every visible segment — date **and** time — is filled.
173
180
 
174
- ## Scope defaults
181
+ ## Scoped defaults
175
182
 
176
183
  ```ts
177
184
  import { provideForDateFieldDefaults } from 'forty-cdk/date-field';
@@ -256,7 +263,7 @@ The two endpoints are typed independently, so order is not guaranteed by constru
256
263
 
257
264
  Each endpoint is its own tab stop, so `Tab` moves start group → end group → next control; arrows move between segments **within** an endpoint. Every other key behaves as in the [Keyboard](#keyboard) table below. Roving tabindex is per endpoint, and `aria-invalid="true"` is reflected on the root when the form marks it invalid **or** when two complete endpoints are out of order; everything else matches the [Accessibility](#accessibility) notes below.
258
265
 
259
- ### Range scope defaults
266
+ ### Range scoped defaults
260
267
 
261
268
  `provideForDateRangeFieldDefaults` mirrors `provideForDateFieldDefaults` and adds `startLabel` / `endLabel` for the two endpoint group `aria-label`s (`'Start date'` / `'End date'` by default). Both wrapper patterns work via `FOR_DATE_RANGE_FIELD_HOST_DIRECTIVE_INPUTS` / `FOR_DATE_RANGE_FIELD_HOST_DIRECTIVE_OUTPUTS` — see [Wrapping form primitives](../../../docs/wrapping-form-primitives.md).
262
269
 
@@ -1,3 +1,10 @@
1
+ ---
2
+ title: Date Picker
3
+ group: primitives
4
+ archetype: [overlay, form-control]
5
+ apgUrl: https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/examples/datepicker-dialog/
6
+ ---
7
+
1
8
  # DatePicker
2
9
 
3
10
  A trigger that opens a floating calendar to pick a date, composing ForCalendar inside a dismissible popover with min / max bounds and per-date availability.
package/dialog/README.md CHANGED
@@ -1,3 +1,10 @@
1
+ ---
2
+ title: Dialog
3
+ group: primitives
4
+ archetype: [overlay]
5
+ apgUrl: https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/
6
+ ---
7
+
1
8
  # Dialog
2
9
 
3
10
  A modal window overlaid on the page, with a focus trap, scroll lock and Escape / dismiss handling. Also openable imperatively through ForDialogManager.
@@ -1,3 +1,10 @@
1
+ ---
2
+ title: Disclosure
3
+ group: primitives
4
+ archetype: [composable-ui]
5
+ apgUrl: https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/
6
+ ---
7
+
1
8
  # Disclosure
2
9
 
3
10
  A single trigger that shows or hides a related region of content.
@@ -1,3 +1,9 @@
1
+ ---
2
+ title: Drag & Drop
3
+ group: utilities
4
+ archetype: [headless-utility]
5
+ ---
6
+
1
7
  # Drag & Drop
2
8
 
3
9
  Headless, accessible drag-and-drop for sortable lists and cross-list transfers, driven by both keyboard and pointer. Ships drag handles, custom preview / placeholder templates, live-sort, FLIP reorder animations, auto-scroll, and boundary / axis-lock constraints.
package/drawer/README.md CHANGED
@@ -1,3 +1,10 @@
1
+ ---
2
+ title: Drawer
3
+ group: primitives
4
+ archetype: [overlay]
5
+ apgUrl: https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/
6
+ ---
7
+
1
8
  # Drawer
2
9
 
3
10
  A side or bottom sheet built on the modal dialog pattern, adding pointer-driven swipe-to-dismiss and snap points.
@@ -1,3 +1,10 @@
1
+ ---
2
+ title: Dropdown Menu
3
+ group: primitives
4
+ archetype: [overlay]
5
+ apgUrl: https://www.w3.org/WAI/ARIA/apg/patterns/menu-button/
6
+ ---
7
+
1
8
  # DropdownMenu
2
9
 
3
10
  A button that opens a menu of actions, with full keyboard navigation, typeahead and submenus.
@@ -37,6 +37,7 @@ import { injectVirtualizer } from 'forty-cdk/virtualization';
37
37
 
38
38
 
39
39
 
40
+
40
41
 
41
42
 
42
43
  class TableVirtualizedNavigator {
@@ -177,6 +178,7 @@ function injectTableRegistration() {
177
178
 
178
179
 
179
180
 
181
+
180
182
  class ForTableVirtualized {
181
183
  #ctx = injectTableContext();
182
184
  #registration = injectTableRegistration();
@@ -193,9 +195,24 @@ class ForTableVirtualized {
193
195
 
194
196
  scrollElement = input(null, /* @ts-ignore */
195
197
  ...(ngDevMode ? [{ debugName: "scrollElement" }] : /* istanbul ignore next */ []));
198
+
199
+
200
+
201
+
202
+
203
+
204
+
205
+
206
+
207
+
208
+
209
+
210
+
211
+ virtualRowCount = input(undefined, /* @ts-ignore */
212
+ ...(ngDevMode ? [{ debugName: "virtualRowCount" }] : /* istanbul ignore next */ []));
196
213
  #scrollElement = computed(() => this.scrollElement() ?? this.#rootEl, /* @ts-ignore */
197
214
  ...(ngDevMode ? [{ debugName: "#scrollElement" }] : /* istanbul ignore next */ []));
198
- #rowCount = computed(() => this.#ctx.rowCount() ?? 0, /* @ts-ignore */
215
+ #rowCount = computed(() => this.virtualRowCount() ?? this.#ctx.rowCount() ?? 0, /* @ts-ignore */
199
216
  ...(ngDevMode ? [{ debugName: "#rowCount" }] : /* istanbul ignore next */ []));
200
217
  #virtualizer = injectVirtualizer({
201
218
  count: this.#rowCount,
@@ -279,7 +296,7 @@ class ForTableVirtualized {
279
296
  this.#virtualizer.measureElement(element);
280
297
  }
281
298
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTableVirtualized, deps: [], target: i0.ɵɵFactoryTarget.Directive });
282
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: ForTableVirtualized, isStandalone: true, selector: "[forTableVirtualized]", inputs: { estimateRowSize: { classPropertyName: "estimateRowSize", publicName: "estimateRowSize", isSignal: true, isRequired: false, transformFunction: null }, scrollElement: { classPropertyName: "scrollElement", publicName: "scrollElement", isSignal: true, isRequired: false, transformFunction: null } }, exportAs: ["forTableVirtualized"], ngImport: i0 });
299
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: ForTableVirtualized, isStandalone: true, selector: "[forTableVirtualized]", inputs: { estimateRowSize: { classPropertyName: "estimateRowSize", publicName: "estimateRowSize", isSignal: true, isRequired: false, transformFunction: null }, scrollElement: { classPropertyName: "scrollElement", publicName: "scrollElement", isSignal: true, isRequired: false, transformFunction: null }, virtualRowCount: { classPropertyName: "virtualRowCount", publicName: "virtualRowCount", isSignal: true, isRequired: false, transformFunction: null } }, exportAs: ["forTableVirtualized"], ngImport: i0 });
283
300
  }
284
301
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTableVirtualized, decorators: [{
285
302
  type: Directive,
@@ -287,7 +304,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
287
304
  selector: '[forTableVirtualized]',
288
305
  exportAs: 'forTableVirtualized',
289
306
  }]
290
- }], ctorParameters: () => [], propDecorators: { estimateRowSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "estimateRowSize", required: false }] }], scrollElement: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollElement", required: false }] }] } });
307
+ }], ctorParameters: () => [], propDecorators: { estimateRowSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "estimateRowSize", required: false }] }], scrollElement: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollElement", required: false }] }], virtualRowCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "virtualRowCount", required: false }] }] } });
291
308
 
292
309
 
293
310
 
@@ -1 +1 @@
1
- {"version":3,"file":"forty-cdk-table-virtualization.mjs","sources":["../../../projects/forty-cdk/table-virtualization/src/table-virtualized-navigator.ts","../../../projects/forty-cdk/table-virtualization/src/table-virtualized.ts","../../../projects/forty-cdk/table-virtualization/src/public-api.ts","../../../projects/forty-cdk/table-virtualization/src/forty-cdk-table-virtualization.ts"],"sourcesContent":["import { signal, type Signal } from '@angular/core';\n\nimport { type ForTableRowHandle } from 'forty-cdk/core';\n\n/**\n * An absolute `(rowIndex, 0-based column)` target awaiting the row to mount,\n * plus the travel `direction` used to step over full-span variant rows (which\n * register no cells and so cannot receive roving focus).\n */\ninterface PendingTarget {\n readonly row: number;\n readonly col: number;\n readonly direction: 1 | -1;\n}\n\ntype ProbeResult = 'focused' | 'variant' | 'disabled' | 'unmounted';\n\n/**\n * Dependencies for `TableVirtualizedNavigator`. Wires the bridge to the table's\n * live row registry and the companion's scroll method.\n */\nexport interface TableVirtualizedNavigatorDeps {\n /** Live registered data rows, so the bridge can resolve a pending target once it mounts. */\n readonly rows: Signal<readonly ForTableRowHandle[]>;\n /** Scroll the virtualizer so the row at the absolute index mounts. */\n readonly scrollToRow: (index: number) => void;\n /** The scroll container's bounding rect, or `null` before it is available. */\n readonly scrollViewportRect: () => DOMRect | null;\n /** The true total data-row count, used to clamp when stepping over variant rows. */\n readonly rowCount: () => number;\n /** The count of currently loaded data rows (the body dataset length), or `undefined` when unknown (raw-primitive rendering). Cross-window targets are clamped to this so an out-of-prefix row never stashes a pending focus move that can only resolve when a far page later loads. */\n readonly loadedRowCount?: () => number | undefined;\n}\n\n/**\n * Cross-window keyboard-navigation bridge for a virtualized `[forTable]` grid,\n * owned by `[forTableVirtualized]`. The grid keeps roving-tabindex and renders\n * only a window of rows, so a navigation target can land on a row that is not\n * currently mounted. This bridge resolves that:\n *\n * - **Move** — `navigateTo(row, col, direction)` focuses the cell at the\n * absolute `(row, col)` when its row is already rendered; otherwise it stashes\n * the target and calls `scrollToRow(row)` to mount it.\n * - **Resolve** — once the freshly-mounted row registers, the companion's bridge\n * effect calls `tryResolvePending`, which moves roving focus onto the target\n * cell (its `(focus)` handler promotes it to the active roving cell) and clears\n * the pending target.\n *\n * Full-span **variant rows** (group headers / separators / summary rows) mount\n * as a single presentational cell and register no cell handles, so they cannot\n * hold roving focus; likewise a mounted row whose target-column cell is\n * `disabled` (e.g. skeleton / placeholder rows) cannot receive focus. When a\n * target lands on either, the bridge steps `row` by `direction` (clamped to the\n * loaded prefix) to the adjacent enabled data cell — scrolling it in when it is\n * outside the window — and clears the target if the dataset bound is reached\n * with no landable data row in that direction, so a stale target can never\n * later steal focus.\n *\n * Off-prefix targets are clamped to the last loaded row: when `loadedRowCount`\n * is smaller than the total `rowCount` (a server-paged grid whose far pages are\n * not yet loaded), a target beyond the loaded prefix restarts at the last loaded\n * row searching upward, so a Ctrl+End / Page / Arrow move can never stash a\n * pending focus that only resolves — and steals focus — when a far page later\n * mounts.\n *\n * Mirrors the 1D `ListboxVirtualizedNavigator` precedent, adapted to the table's\n * roving + focused-row-retention model. Internal — not re-exported from\n * `table/index.ts` or `public-api.ts`.\n */\nexport class TableVirtualizedNavigator {\n readonly #deps: TableVirtualizedNavigatorDeps;\n\n readonly #pending = signal<PendingTarget | null>(null);\n\n constructor(deps: TableVirtualizedNavigatorDeps) {\n this.#deps = deps;\n }\n\n /**\n * Move roving focus to the data cell at the absolute `(row, col)`, travelling\n * in `direction` (`+1` down / `-1` up) so full-span variant rows are stepped\n * over. When the target row is already rendered as a data row, focuses\n * immediately. Otherwise stashes the target and scrolls it into the window;\n * the bridge effect resolves it once the row mounts.\n */\n navigateTo(row: number, col: number, direction: 1 | -1): void {\n this.#resolve(row, col, direction);\n }\n\n /** Scroll the virtualizer so the row at the absolute `index` is in the window. */\n scrollToRow(index: number): void {\n this.#deps.scrollToRow(index);\n }\n\n /** The scroll container's bounding rect, or `null` before it is available. */\n scrollViewportRect(): DOMRect | null {\n return this.#deps.scrollViewportRect();\n }\n\n /**\n * Resolve a pending cross-window navigation: once the row carrying the pending\n * absolute index mounts, focus its cell and clear the pending target. Returns\n * `true` when a pending request was resolved, `false` otherwise. Called from\n * the companion's bridge effect whenever the rendered rows change.\n */\n tryResolvePending(): boolean {\n const pending = this.#pending();\n if (pending === null) {\n return false;\n }\n return this.#resolve(pending.row, pending.col, pending.direction);\n }\n\n /**\n * Drop any stashed cross-window target. `ForTable` calls this on the next\n * keyboard interaction that reaches the grid, so a pending move set by an\n * earlier Ctrl+End / Page / Arrow is superseded rather than teleporting focus\n * when a far page later mounts.\n */\n clearPending(): void {\n this.#pending.set(null);\n }\n\n #resolve(row: number, col: number, direction: 1 | -1): boolean {\n const count = this.#deps.rowCount();\n const bound = Math.min(this.#deps.loadedRowCount?.() ?? count, count);\n let target = row;\n let dir = direction;\n if (bound > 0 && target >= bound) {\n target = bound - 1;\n dir = -1;\n }\n while (target >= 0 && target < bound) {\n const result = this.#probeCell(target, col);\n if (result === 'focused') {\n this.#pending.set(null);\n return true;\n }\n if (result === 'unmounted') {\n const current = this.#pending();\n const unchanged =\n current !== null &&\n current.row === target &&\n current.col === col &&\n current.direction === dir;\n if (!unchanged) {\n this.#pending.set({ row: target, col, direction: dir });\n this.#deps.scrollToRow(target);\n }\n return false;\n }\n target += dir;\n }\n this.#pending.set(null);\n return false;\n }\n\n #probeCell(row: number, col: number): ProbeResult {\n const handle = this.#deps.rows().find((r) => r.virtualIndex() === row);\n if (!handle) {\n return 'unmounted';\n }\n const cells = handle.cells();\n const cell = cells[col] ?? cells[cells.length - 1];\n if (!cell) {\n return 'variant';\n }\n if (cell.disabled()) {\n return 'disabled';\n }\n cell.host.focus();\n return 'focused';\n }\n}\n","import {\n computed,\n DestroyRef,\n Directive,\n effect,\n ElementRef,\n inject,\n input,\n numberAttribute,\n} from '@angular/core';\n\nimport {\n orphanContextError,\n TABLE_REGISTRATION_CONTEXT,\n type TableRegistrationContext,\n} from 'forty-cdk/core';\nimport { FOR_TABLE_CONTEXT, type ForTableContext } from 'forty-cdk/table';\nimport { injectVirtualizer, type VirtualItem } from 'forty-cdk/virtualization';\n\nimport { TableVirtualizedNavigator } from './table-virtualized-navigator';\n\nfunction injectTableContext(): ForTableContext {\n const ctx = inject(FOR_TABLE_CONTEXT, { optional: true });\n if (!ctx) {\n throw orphanContextError({\n code: 'FORCDK-TABLE-VIRTUALIZATION-001',\n piece: 'ForTableVirtualized',\n root: '[forTable]',\n token: 'FOR_TABLE_CONTEXT',\n });\n }\n return ctx;\n}\n\nfunction injectTableRegistration(): TableRegistrationContext {\n const registration = inject(TABLE_REGISTRATION_CONTEXT, { optional: true });\n if (!registration) {\n throw orphanContextError({\n code: 'FORCDK-TABLE-VIRTUALIZATION-002',\n piece: 'ForTableVirtualized',\n root: '[forTable]',\n token: 'TABLE_REGISTRATION_CONTEXT',\n });\n }\n return registration;\n}\n\n/**\n * Opt-in row-virtualization companion for `[forTable]` in `<div role>` grid mode. Place it on the\n * same element as `[forTable]`; it builds the windowing core from the table's `[rowCount]` and\n * exposes the visible window for the consumer to render with their own `@for` + position transform.\n *\n * Tree-shakeable: `ForTable` never imports the virtualization core — only consumers that import\n * `ForTableVirtualized` bundle `@tanstack/virtual-core`.\n *\n * The focused row is kept mounted even when scrolled out of the window so the roving-focused\n * `gridcell` is never unmounted. SSR-safe: off-browser the window is empty and\n * `totalSize` is the estimate-based total.\n *\n * Also drives cross-window keyboard navigation: when an Arrow / Page / Ctrl+Home / Ctrl+End grid\n * action resolves a row outside the rendered window, it scrolls that row into view and moves roving\n * focus onto the target cell once it mounts (preserving the current column). `ForTable` stays\n * unaware of virtualization — it delegates the row-crossing move through the table context.\n */\n@Directive({\n selector: '[forTableVirtualized]',\n exportAs: 'forTableVirtualized',\n})\nexport class ForTableVirtualized {\n readonly #ctx = injectTableContext();\n readonly #registration = injectTableRegistration();\n readonly #rootEl = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n\n /** Estimated row size in px along the scroll axis (the fixed-size fast path). Read for the size estimate. */\n readonly estimateRowSize = input(44, { transform: numberAttribute });\n\n /**\n * Scroll container. Defaults to the table root element (the scroll container in `<div>` grid\n * mode). Bind it explicitly when the scroll container is an **ancestor** of the table — e.g. an\n * app-shell viewport that scrolls projected content — since the table cannot inject an ancestor\n * it does not own. A design-system wrapper can re-expose or rename this input through\n * `hostDirectives` input aliasing (`inputs: ['scrollElement: scrollContainer']`) with no bridging\n * effect required.\n */\n readonly scrollElement = input<HTMLElement | null>(null);\n\n readonly #scrollElement = computed(() => this.scrollElement() ?? this.#rootEl);\n readonly #rowCount = computed(() => this.#ctx.rowCount() ?? 0);\n\n readonly #virtualizer = injectVirtualizer({\n count: this.#rowCount,\n estimateSize: () => this.estimateRowSize(),\n scrollElement: this.#scrollElement,\n });\n\n readonly #navigator = new TableVirtualizedNavigator({\n rows: this.#registration.rows,\n scrollToRow: (index) => this.scrollToRow(index),\n scrollViewportRect: () => this.#scrollElement().getBoundingClientRect(),\n rowCount: this.#rowCount,\n loadedRowCount: () => this.#ctx.loadedRowCount(),\n });\n\n constructor() {\n this.#registration.registerVirtualNavigation(this.#navigator);\n this.#registration.registerVirtualWindow({\n rows: this.virtualRows,\n totalSize: this.totalSize,\n measureRow: (element) => this.measureRow(element),\n });\n inject(DestroyRef).onDestroy(() => {\n this.#registration.registerVirtualNavigation(null);\n this.#registration.registerVirtualWindow(null);\n });\n effect(() => {\n this.#registration.rows();\n this.#navigator.tryResolvePending();\n });\n }\n\n /**\n * The rows in the visible window plus overscan, augmented to always include the focused row and\n * the row being reordered even when they are scrolled out of view (so the roving-focused cell\n * stays mounted, and a pointer reorder drag never unmounts the lifted row). Render these with\n * `@for (vrow of v.virtualRows(); track vrow.index)` and position each row absolutely with\n * `transform: translateY(vrow.start + 'px')`. Bind each row's `[virtualIndex]=\"vrow.index\"`.\n */\n readonly virtualRows = computed<readonly VirtualItem[]>(() => {\n const items = this.#virtualizer.virtualItems();\n const retain = new Set<number>();\n const focused = this.#ctx.focusedRowIndex();\n if (focused !== null) {\n retain.add(focused);\n }\n const reordering = this.#registration.reorderingRowIndex();\n if (reordering !== null) {\n retain.add(reordering);\n }\n for (const it of items) {\n retain.delete(it.index);\n }\n if (retain.size === 0) {\n return items;\n }\n const size = this.estimateRowSize();\n const retained: VirtualItem[] = [...retain].map(\n (index) =>\n this.#virtualizer.measurementFor(index) ?? { index, key: index, start: index * size, size },\n );\n return [...items, ...retained].sort((a, b) => a.index - b.index);\n });\n\n /** Total scroll size of all rows in px. Bind to the body container's height to size the scroll range. */\n readonly totalSize = this.#virtualizer.totalSize;\n\n /**\n * The rendered window as the inclusive-exclusive `[firstIndex, lastIndex + 1)` index range,\n * sourced from the underlying virtualizer (the true window) — not from {@link virtualRows},\n * which is augmented with the focused / reordering rows. So a row retained out of the window\n * never widens this range. Plugs straight into `injectInfiniteScroll({ range, count, onLoadMore })`.\n */\n readonly range = this.#virtualizer.range;\n\n /**\n * Scroll the container so the row at `index` is in view. Cross-window keyboard\n * navigation calls this internally; consumers may also call it to scroll\n * programmatically.\n */\n scrollToRow(index: number, options?: { align?: 'start' | 'center' | 'end' | 'auto' }): void {\n this.#virtualizer.scrollToIndex(index, options);\n }\n\n /**\n * Record the measured size of a rendered row element (dynamic / measured row heights).\n * Passing `null` sweeps evicted rows recycled out of the window from the measurement cache.\n */\n measureRow(element: HTMLElement | null): void {\n this.#virtualizer.measureElement(element);\n }\n}\n","/*\n * Public API surface of forty-cdk/table-virtualization.\n *\n * The `[forTableVirtualized]` adapter ships from its own secondary entry point\n * because it composes two primitives: it reads the table's context and\n * registration surface from `forty-cdk/table` and builds its window with\n * `forty-cdk/virtualization`. Keeping it here is what lets `forty-cdk/virtualization`\n * stay free of any other primitive's module graph, so a consumer virtualizing a\n * plain list never resolves the table entry point.\n */\n\nexport { ForTableVirtualized } from './table-virtualized';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"forty-cdk-table-virtualization.mjs","sources":["../../../projects/forty-cdk/table-virtualization/src/table-virtualized-navigator.ts","../../../projects/forty-cdk/table-virtualization/src/table-virtualized.ts","../../../projects/forty-cdk/table-virtualization/src/public-api.ts","../../../projects/forty-cdk/table-virtualization/src/forty-cdk-table-virtualization.ts"],"sourcesContent":["import { signal, type Signal } from '@angular/core';\n\nimport { type ForTableRowHandle } from 'forty-cdk/core';\n\n/**\n * An absolute `(rowIndex, 0-based column)` target awaiting the row to mount,\n * plus the travel `direction` used to step over full-span variant rows (which\n * register no cells and so cannot receive roving focus).\n */\ninterface PendingTarget {\n readonly row: number;\n readonly col: number;\n readonly direction: 1 | -1;\n}\n\ntype ProbeResult = 'focused' | 'variant' | 'disabled' | 'unmounted';\n\n/**\n * Dependencies for `TableVirtualizedNavigator`. Wires the bridge to the table's\n * live row registry and the companion's scroll method.\n */\nexport interface TableVirtualizedNavigatorDeps {\n /** Live registered data rows, so the bridge can resolve a pending target once it mounts. */\n readonly rows: Signal<readonly ForTableRowHandle[]>;\n /** Scroll the virtualizer so the row at the absolute index mounts. */\n readonly scrollToRow: (index: number) => void;\n /** The scroll container's bounding rect, or `null` before it is available. */\n readonly scrollViewportRect: () => DOMRect | null;\n /** The count of rows the virtualizer can place (`[virtualRowCount]`, else the table's `[rowCount]`), used to clamp when stepping over variant rows. */\n readonly rowCount: () => number;\n /** The count of currently loaded data rows (the body dataset length), or `undefined` when unknown (raw-primitive rendering). Cross-window targets are clamped to this so an out-of-prefix row never stashes a pending focus move that can only resolve when a far page later loads. */\n readonly loadedRowCount?: () => number | undefined;\n}\n\n/**\n * Cross-window keyboard-navigation bridge for a virtualized `[forTable]` grid,\n * owned by `[forTableVirtualized]`. The grid keeps roving-tabindex and renders\n * only a window of rows, so a navigation target can land on a row that is not\n * currently mounted. This bridge resolves that:\n *\n * - **Move** — `navigateTo(row, col, direction)` focuses the cell at the\n * absolute `(row, col)` when its row is already rendered; otherwise it stashes\n * the target and calls `scrollToRow(row)` to mount it.\n * - **Resolve** — once the freshly-mounted row registers, the companion's bridge\n * effect calls `tryResolvePending`, which moves roving focus onto the target\n * cell (its `(focus)` handler promotes it to the active roving cell) and clears\n * the pending target.\n *\n * Full-span **variant rows** (group headers / separators / summary rows) mount\n * as a single presentational cell and register no cell handles, so they cannot\n * hold roving focus; likewise a mounted row whose target-column cell is\n * `disabled` (e.g. skeleton / placeholder rows) cannot receive focus. When a\n * target lands on either, the bridge steps `row` by `direction` (clamped to the\n * loaded prefix) to the adjacent enabled data cell — scrolling it in when it is\n * outside the window — and clears the target if the dataset bound is reached\n * with no landable data row in that direction, so a stale target can never\n * later steal focus.\n *\n * Off-prefix targets are clamped to the last loaded row: when `loadedRowCount`\n * is smaller than the placeable `rowCount` (a server-paged grid whose far pages\n * are not yet loaded), a target beyond the loaded prefix restarts at the last\n * loaded row searching upward, so a Ctrl+End / Page / Arrow move can never stash\n * a pending focus that only resolves — and steals focus — when a far page later\n * mounts. A raw-primitive grid registers no body dataset, so there the same bound\n * arrives through `[virtualRowCount]` instead.\n *\n * Mirrors the 1D `ListboxVirtualizedNavigator` precedent, adapted to the table's\n * roving + focused-row-retention model. Internal — not re-exported from\n * `table/index.ts` or `public-api.ts`.\n */\nexport class TableVirtualizedNavigator {\n readonly #deps: TableVirtualizedNavigatorDeps;\n\n readonly #pending = signal<PendingTarget | null>(null);\n\n constructor(deps: TableVirtualizedNavigatorDeps) {\n this.#deps = deps;\n }\n\n /**\n * Move roving focus to the data cell at the absolute `(row, col)`, travelling\n * in `direction` (`+1` down / `-1` up) so full-span variant rows are stepped\n * over. When the target row is already rendered as a data row, focuses\n * immediately. Otherwise stashes the target and scrolls it into the window;\n * the bridge effect resolves it once the row mounts.\n */\n navigateTo(row: number, col: number, direction: 1 | -1): void {\n this.#resolve(row, col, direction);\n }\n\n /** Scroll the virtualizer so the row at the absolute `index` is in the window. */\n scrollToRow(index: number): void {\n this.#deps.scrollToRow(index);\n }\n\n /** The scroll container's bounding rect, or `null` before it is available. */\n scrollViewportRect(): DOMRect | null {\n return this.#deps.scrollViewportRect();\n }\n\n /**\n * Resolve a pending cross-window navigation: once the row carrying the pending\n * absolute index mounts, focus its cell and clear the pending target. Returns\n * `true` when a pending request was resolved, `false` otherwise. Called from\n * the companion's bridge effect whenever the rendered rows change.\n */\n tryResolvePending(): boolean {\n const pending = this.#pending();\n if (pending === null) {\n return false;\n }\n return this.#resolve(pending.row, pending.col, pending.direction);\n }\n\n /**\n * Drop any stashed cross-window target. `ForTable` calls this on the next\n * keyboard interaction that reaches the grid, so a pending move set by an\n * earlier Ctrl+End / Page / Arrow is superseded rather than teleporting focus\n * when a far page later mounts.\n */\n clearPending(): void {\n this.#pending.set(null);\n }\n\n #resolve(row: number, col: number, direction: 1 | -1): boolean {\n const count = this.#deps.rowCount();\n const bound = Math.min(this.#deps.loadedRowCount?.() ?? count, count);\n let target = row;\n let dir = direction;\n if (bound > 0 && target >= bound) {\n target = bound - 1;\n dir = -1;\n }\n while (target >= 0 && target < bound) {\n const result = this.#probeCell(target, col);\n if (result === 'focused') {\n this.#pending.set(null);\n return true;\n }\n if (result === 'unmounted') {\n const current = this.#pending();\n const unchanged =\n current !== null &&\n current.row === target &&\n current.col === col &&\n current.direction === dir;\n if (!unchanged) {\n this.#pending.set({ row: target, col, direction: dir });\n this.#deps.scrollToRow(target);\n }\n return false;\n }\n target += dir;\n }\n this.#pending.set(null);\n return false;\n }\n\n #probeCell(row: number, col: number): ProbeResult {\n const handle = this.#deps.rows().find((r) => r.virtualIndex() === row);\n if (!handle) {\n return 'unmounted';\n }\n const cells = handle.cells();\n const cell = cells[col] ?? cells[cells.length - 1];\n if (!cell) {\n return 'variant';\n }\n if (cell.disabled()) {\n return 'disabled';\n }\n cell.host.focus();\n return 'focused';\n }\n}\n","import {\n computed,\n DestroyRef,\n Directive,\n effect,\n ElementRef,\n inject,\n input,\n numberAttribute,\n} from '@angular/core';\n\nimport {\n orphanContextError,\n TABLE_REGISTRATION_CONTEXT,\n type TableRegistrationContext,\n} from 'forty-cdk/core';\nimport { FOR_TABLE_CONTEXT, type ForTableContext } from 'forty-cdk/table';\nimport { injectVirtualizer, type VirtualItem } from 'forty-cdk/virtualization';\n\nimport { TableVirtualizedNavigator } from './table-virtualized-navigator';\n\nfunction injectTableContext(): ForTableContext {\n const ctx = inject(FOR_TABLE_CONTEXT, { optional: true });\n if (!ctx) {\n throw orphanContextError({\n code: 'FORCDK-TABLE-VIRTUALIZATION-001',\n piece: 'ForTableVirtualized',\n root: '[forTable]',\n token: 'FOR_TABLE_CONTEXT',\n });\n }\n return ctx;\n}\n\nfunction injectTableRegistration(): TableRegistrationContext {\n const registration = inject(TABLE_REGISTRATION_CONTEXT, { optional: true });\n if (!registration) {\n throw orphanContextError({\n code: 'FORCDK-TABLE-VIRTUALIZATION-002',\n piece: 'ForTableVirtualized',\n root: '[forTable]',\n token: 'TABLE_REGISTRATION_CONTEXT',\n });\n }\n return registration;\n}\n\n/**\n * Opt-in row-virtualization companion for `[forTable]` in `<div role>` grid mode. Place it on the\n * same element as `[forTable]`; it builds the windowing core from `[virtualRowCount]` — which\n * defaults to the table's `[rowCount]` — and exposes the visible window for the consumer to render\n * with their own `@for` + position transform.\n *\n * Tree-shakeable: `ForTable` never imports the virtualization core — only consumers that import\n * `ForTableVirtualized` bundle `@tanstack/virtual-core`.\n *\n * The focused row is kept mounted even when scrolled out of the window so the roving-focused\n * `gridcell` is never unmounted. SSR-safe: off-browser the window is empty and\n * `totalSize` is the estimate-based total.\n *\n * Also drives cross-window keyboard navigation: when an Arrow / Page / Ctrl+Home / Ctrl+End grid\n * action resolves a row outside the rendered window, it scrolls that row into view and moves roving\n * focus onto the target cell once it mounts (preserving the current column). `ForTable` stays\n * unaware of virtualization — it delegates the row-crossing move through the table context.\n */\n@Directive({\n selector: '[forTableVirtualized]',\n exportAs: 'forTableVirtualized',\n})\nexport class ForTableVirtualized {\n readonly #ctx = injectTableContext();\n readonly #registration = injectTableRegistration();\n readonly #rootEl = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n\n /** Estimated row size in px along the scroll axis (the fixed-size fast path). Read for the size estimate. */\n readonly estimateRowSize = input(44, { transform: numberAttribute });\n\n /**\n * Scroll container. Defaults to the table root element (the scroll container in `<div>` grid\n * mode). Bind it explicitly when the scroll container is an **ancestor** of the table — e.g. an\n * app-shell viewport that scrolls projected content — since the table cannot inject an ancestor\n * it does not own. A design-system wrapper can re-expose or rename this input through\n * `hostDirectives` input aliasing (`inputs: ['scrollElement: scrollContainer']`) with no bridging\n * effect required.\n */\n readonly scrollElement = input<HTMLElement | null>(null);\n\n /**\n * Count of rows the virtualizer can place: it sizes the scroll range and bounds\n * cross-window keyboard navigation. Defaults to the table's `[rowCount]`, which is also\n * the placeable range for an **index-addressable** dataset — any absolute index is\n * fetchable, so the window can travel the whole server-known total.\n *\n * Bind it for an **append-style** infinite list, whose loaded prefix is the only range\n * that can be rendered: leave `[rowCount]` at the server-known total `aria-rowcount`\n * reports and set this to the loaded count, so the scroll range covers exactly the rows\n * the virtualizer can place instead of stretching over pages only an append can reach.\n * It is the one channel the raw-primitive shape has for that count, since\n * `<for-table-body>` is what otherwise derives it from its dataset.\n */\n readonly virtualRowCount = input<number | undefined>(undefined);\n\n readonly #scrollElement = computed(() => this.scrollElement() ?? this.#rootEl);\n readonly #rowCount = computed(() => this.virtualRowCount() ?? this.#ctx.rowCount() ?? 0);\n\n readonly #virtualizer = injectVirtualizer({\n count: this.#rowCount,\n estimateSize: () => this.estimateRowSize(),\n scrollElement: this.#scrollElement,\n });\n\n readonly #navigator = new TableVirtualizedNavigator({\n rows: this.#registration.rows,\n scrollToRow: (index) => this.scrollToRow(index),\n scrollViewportRect: () => this.#scrollElement().getBoundingClientRect(),\n rowCount: this.#rowCount,\n loadedRowCount: () => this.#ctx.loadedRowCount(),\n });\n\n constructor() {\n this.#registration.registerVirtualNavigation(this.#navigator);\n this.#registration.registerVirtualWindow({\n rows: this.virtualRows,\n totalSize: this.totalSize,\n measureRow: (element) => this.measureRow(element),\n });\n inject(DestroyRef).onDestroy(() => {\n this.#registration.registerVirtualNavigation(null);\n this.#registration.registerVirtualWindow(null);\n });\n effect(() => {\n this.#registration.rows();\n this.#navigator.tryResolvePending();\n });\n }\n\n /**\n * The rows in the visible window plus overscan, augmented to always include the focused row and\n * the row being reordered even when they are scrolled out of view (so the roving-focused cell\n * stays mounted, and a pointer reorder drag never unmounts the lifted row). Render these with\n * `@for (vrow of v.virtualRows(); track vrow.index)` and position each row absolutely with\n * `transform: translateY(vrow.start + 'px')`. Bind each row's `[virtualIndex]=\"vrow.index\"`.\n */\n readonly virtualRows = computed<readonly VirtualItem[]>(() => {\n const items = this.#virtualizer.virtualItems();\n const retain = new Set<number>();\n const focused = this.#ctx.focusedRowIndex();\n if (focused !== null) {\n retain.add(focused);\n }\n const reordering = this.#registration.reorderingRowIndex();\n if (reordering !== null) {\n retain.add(reordering);\n }\n for (const it of items) {\n retain.delete(it.index);\n }\n if (retain.size === 0) {\n return items;\n }\n const size = this.estimateRowSize();\n const retained: VirtualItem[] = [...retain].map(\n (index) =>\n this.#virtualizer.measurementFor(index) ?? { index, key: index, start: index * size, size },\n );\n return [...items, ...retained].sort((a, b) => a.index - b.index);\n });\n\n /** Total scroll size of all rows in px. Bind to the body container's height to size the scroll range. */\n readonly totalSize = this.#virtualizer.totalSize;\n\n /**\n * The rendered window as the inclusive-exclusive `[firstIndex, lastIndex + 1)` index range,\n * sourced from the underlying virtualizer (the true window) — not from {@link virtualRows},\n * which is augmented with the focused / reordering rows. So a row retained out of the window\n * never widens this range. Plugs straight into `injectInfiniteScroll({ range, count, onLoadMore })`.\n */\n readonly range = this.#virtualizer.range;\n\n /**\n * Scroll the container so the row at `index` is in view. Cross-window keyboard\n * navigation calls this internally; consumers may also call it to scroll\n * programmatically.\n */\n scrollToRow(index: number, options?: { align?: 'start' | 'center' | 'end' | 'auto' }): void {\n this.#virtualizer.scrollToIndex(index, options);\n }\n\n /**\n * Record the measured size of a rendered row element (dynamic / measured row heights).\n * Passing `null` sweeps evicted rows recycled out of the window from the measurement cache.\n */\n measureRow(element: HTMLElement | null): void {\n this.#virtualizer.measureElement(element);\n }\n}\n","/*\n * Public API surface of forty-cdk/table-virtualization.\n *\n * The `[forTableVirtualized]` adapter ships from its own secondary entry point\n * because it composes two primitives: it reads the table's context and\n * registration surface from `forty-cdk/table` and builds its window with\n * `forty-cdk/virtualization`. Keeping it here is what lets `forty-cdk/virtualization`\n * stay free of any other primitive's module graph, so a consumer virtualizing a\n * plain list never resolves the table entry point.\n */\n\nexport { ForTableVirtualized } from './table-virtualized';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -474,12 +474,15 @@ class TableRowSelection {
474
474
 
475
475
 
476
476
 
477
+
478
+
477
479
  select(value, modifiers) {
478
480
  const mode = this.#selectionMode();
479
- if (mode === 'none') {
481
+ const behavior = this.#selectionBehavior();
482
+ if (mode === 'none' || behavior === 'none') {
480
483
  return;
481
484
  }
482
- if (this.#selectionBehavior() === 'toggle') {
485
+ if (behavior === 'toggle') {
483
486
  this.#model.toggle(value);
484
487
  this.#anchor.set(value);
485
488
  return;
@@ -642,6 +645,7 @@ class ForTable {
642
645
 
643
646
 
644
647
 
648
+
645
649
  _rowCountInput = input(undefined, { ...(ngDevMode ? { debugName: "_rowCountInput" } : /* istanbul ignore next */ {}), alias: 'rowCount' });
646
650
  #registry = inject(TableRegistry);
647
651
 
@@ -650,6 +654,7 @@ class ForTable {
650
654
 
651
655
 
652
656
 
657
+
653
658
  rowCount = computed(() => this._rowCountInput() ?? this.#registry.bodyRowCount()?.(), /* @ts-ignore */
654
659
  ...(ngDevMode ? [{ debugName: "rowCount" }] : /* istanbul ignore next */ []));
655
660
 
@@ -690,6 +695,10 @@ class ForTable {
690
695
 
691
696
 
692
697
 
698
+
699
+
700
+
701
+
693
702
  selectionBehavior = input('toggle', /* @ts-ignore */
694
703
  ...(ngDevMode ? [{ debugName: "selectionBehavior" }] : /* istanbul ignore next */ []));
695
704
 
@@ -771,6 +780,7 @@ class ForTable {
771
780
  return dataCols > 0 ? dataCols : this.#headerCellHosts().length;
772
781
  }, /* @ts-ignore */
773
782
  ...(ngDevMode ? [{ debugName: "#cols" }] : /* istanbul ignore next */ []));
783
+ columnCount = this.#cols;
774
784
 
775
785
 
776
786
 
@@ -3316,6 +3326,13 @@ class ForTableBody {
3316
3326
 
3317
3327
 
3318
3328
 
3329
+
3330
+
3331
+
3332
+
3333
+
3334
+
3335
+
3319
3336
 
3320
3337
 
3321
3338
 
@@ -3334,7 +3351,6 @@ class ForTableBody {
3334
3351
 
3335
3352
 
3336
3353
 
3337
-
3338
3354
  rowContextMenu = output();
3339
3355
 
3340
3356
 
@@ -3528,11 +3544,15 @@ class ForTableBody {
3528
3544
  return descriptor && descriptor.column === column ? descriptor.direction : 'none';
3529
3545
  }
3530
3546
 
3531
- rowsInteractive = computed(() => this.interactiveRows() && this.#ctx.mode() === 'table', /* @ts-ignore */
3532
- ...(ngDevMode ? [{ debugName: "rowsInteractive" }] : /* istanbul ignore next */ []));
3547
+
3548
+
3549
+
3550
+
3551
+ rowsKeyboardInteractive = computed(() => this.interactiveRows() && this.#ctx.mode() === 'table', /* @ts-ignore */
3552
+ ...(ngDevMode ? [{ debugName: "rowsKeyboardInteractive" }] : /* istanbul ignore next */ []));
3533
3553
 
3534
3554
  rowTabIndex(row) {
3535
- return this.rowsInteractive() && !row.variant ? 0 : null;
3555
+ return this.rowsKeyboardInteractive() && !row.variant ? 0 : null;
3536
3556
  }
3537
3557
 
3538
3558
  rowClassFor(row) {
@@ -3570,21 +3590,23 @@ class ForTableBody {
3570
3590
  this.onColumnWidthChange(descriptor.column, descriptor.width);
3571
3591
  };
3572
3592
  onRowClick(row, event) {
3573
- this.#activateRow(row, event);
3593
+ if (this.interactiveRows()) {
3594
+ this.#activateRow(row, event);
3595
+ }
3574
3596
  }
3575
3597
  onRowEnter(row, event) {
3576
- if (this.#activateRow(row, event)) {
3598
+ if (this.rowsKeyboardInteractive() && this.#activateRow(row, event)) {
3577
3599
  event.preventDefault();
3578
3600
  }
3579
3601
  }
3580
3602
  onRowContextMenu(row, event) {
3581
- if (!this.rowsInteractive() || row.variant) {
3603
+ if (!this.interactiveRows() || row.variant) {
3582
3604
  return;
3583
3605
  }
3584
3606
  this.rowContextMenu.emit({ row: row.datum, index: row.index, event });
3585
3607
  }
3586
3608
  #activateRow(row, event) {
3587
- if (!this.rowsInteractive() || row.variant || eventFromInteractiveDescendant(event)) {
3609
+ if (row.variant || eventFromInteractiveDescendant(event)) {
3588
3610
  return false;
3589
3611
  }
3590
3612
  this.rowActivate.emit({ row: row.datum, index: row.index, event });
@@ -4044,6 +4066,47 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
4044
4066
 
4045
4067
 
4046
4068
 
4069
+ class ForTableVariantCell {
4070
+ #ctx = injectTableContext('ForTableVariantCell');
4071
+ role = computed(() => (this.#ctx.mode() === 'table' ? 'cell' : 'gridcell'), /* @ts-ignore */
4072
+ ...(ngDevMode ? [{ debugName: "role" }] : /* istanbul ignore next */ []));
4073
+ colSpan = computed(() => {
4074
+ const cols = this.#ctx.columnCount();
4075
+ return cols > 0 ? cols : null;
4076
+ }, /* @ts-ignore */
4077
+ ...(ngDevMode ? [{ debugName: "colSpan" }] : /* istanbul ignore next */ []));
4078
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTableVariantCell, deps: [], target: i0.ɵɵFactoryTarget.Directive });
4079
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: ForTableVariantCell, isStandalone: true, selector: "[forTableVariantCell]", host: { attributes: { "data-row-variant": "" }, properties: { "attr.role": "role()", "attr.aria-colindex": "1", "attr.aria-colspan": "colSpan()" } }, exportAs: ["forTableVariantCell"], ngImport: i0 });
4080
+ }
4081
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTableVariantCell, decorators: [{
4082
+ type: Directive,
4083
+ args: [{
4084
+ selector: '[forTableVariantCell]',
4085
+ exportAs: 'forTableVariantCell',
4086
+ host: {
4087
+ 'data-row-variant': '',
4088
+ '[attr.role]': 'role()',
4089
+ '[attr.aria-colindex]': '1',
4090
+ '[attr.aria-colspan]': 'colSpan()',
4091
+ },
4092
+ }]
4093
+ }] });
4094
+
4095
+
4096
+
4097
+
4098
+
4099
+
4100
+
4101
+
4102
+
4103
+
4104
+
4105
+
4106
+
4107
+
4108
+
4109
+
4047
4110
  class ForTableRowSelector {
4048
4111
  ctx = injectTableContext('ForTableRowSelector');
4049
4112
  row = injectTableRowContext('ForTableRowSelector');
@@ -4673,5 +4736,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
4673
4736
 
4674
4737
 
4675
4738
 
4676
- export { FOR_TABLE_CONTEXT, FOR_TABLE_DEF_REGISTRY, ForTable, ForTableBody, ForTableCell, ForTableCellDef, ForTableColumnDef, ForTableColumnDragPlaceholder, ForTableColumnLabel, ForTableColumnReorder, ForTableColumnResizer, ForTableHeaderCell, ForTableHeaderCellDef, ForTableHeaderRow, ForTablePlaceholderCellDef, ForTablePlaceholderCellDefault, ForTableRow, ForTableRowCellDef, ForTableRowDef, ForTableRowReorder, ForTableRowSelector, ForTableSelectAll, ForTableSortHeader, provideForTable, provideForTableDefRegistry };
4739
+ export { FOR_TABLE_CONTEXT, FOR_TABLE_DEF_REGISTRY, ForTable, ForTableBody, ForTableCell, ForTableCellDef, ForTableColumnDef, ForTableColumnDragPlaceholder, ForTableColumnLabel, ForTableColumnReorder, ForTableColumnResizer, ForTableHeaderCell, ForTableHeaderCellDef, ForTableHeaderRow, ForTablePlaceholderCellDef, ForTablePlaceholderCellDefault, ForTableRow, ForTableRowCellDef, ForTableRowDef, ForTableRowReorder, ForTableRowSelector, ForTableSelectAll, ForTableSortHeader, ForTableVariantCell, provideForTable, provideForTableDefRegistry };
4677
4740
  //# sourceMappingURL=forty-cdk-table.mjs.map