@kouji-ui/core 0.9.2 → 0.11.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.
@@ -22122,6 +22122,26 @@ class KjTable {
22122
22122
  */
22123
22123
  kjGetRowId = input(undefined, /* @ts-ignore */
22124
22124
  ...(ngDevMode ? [{ debugName: "kjGetRowId" }] : /* istanbul ignore next */ []));
22125
+ /**
22126
+ * Tree rows: returns a row's children (TanStack `getSubRows`). Children are
22127
+ * real table rows with the same columns, shown under their parent while it
22128
+ * is expanded. `undefined` (default) keeps the table flat.
22129
+ */
22130
+ kjGetSubRows = input(undefined, /* @ts-ignore */
22131
+ ...(ngDevMode ? [{ debugName: "kjGetSubRows" }] : /* istanbul ignore next */ []));
22132
+ /**
22133
+ * Tree rows: whether selecting a parent also selects its children
22134
+ * (TanStack `enableSubRowSelection`). Defaults to `false` — every row is
22135
+ * selected on its own.
22136
+ */
22137
+ kjSelectSubRows = input(false, { ...(ngDevMode ? { debugName: "kjSelectSubRows" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
22138
+ /**
22139
+ * Tree rows: whether parents start expanded. Applies to every parent with
22140
+ * no explicit expanded state — on first render, to rows loaded later, and
22141
+ * again after {@link resetExpansion} (an infinite table's request change).
22142
+ * Defaults to `false`.
22143
+ */
22144
+ kjDefaultExpanded = input(false, { ...(ngDevMode ? { debugName: "kjDefaultExpanded" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
22125
22145
  _state = signal(DEFAULT_STATE, /* @ts-ignore */
22126
22146
  ...(ngDevMode ? [{ debugName: "_state" }] : /* istanbul ignore next */ []));
22127
22147
  /**
@@ -22156,6 +22176,85 @@ class KjTable {
22156
22176
  setRowCount(n) {
22157
22177
  this._rowCount.set(n);
22158
22178
  }
22179
+ /**
22180
+ * Server-side infinite mode. When on, `kjTableData` is index-aligned with
22181
+ * the server's result set and may hold holes (`undefined` / `null`) for
22182
+ * rows that are not loaded yet: the holes are dropped before the rows reach
22183
+ * TanStack, and {@link sourceIndex} maps each loaded row back to its
22184
+ * position in the full result set. Sorting, filtering and pagination are
22185
+ * left to the server (TanStack's `manual*` flags), so a header click only
22186
+ * changes `state.sorting()` — feed it into your loader's request.
22187
+ */
22188
+ _infinite = signal(false, /* @ts-ignore */
22189
+ ...(ngDevMode ? [{ debugName: "_infinite" }] : /* istanbul ignore next */ []));
22190
+ /** Whether server-side infinite mode is on — see {@link setInfinite}. */
22191
+ infinite = this._infinite.asReadonly();
22192
+ /**
22193
+ * Turn server-side infinite mode on or off.
22194
+ * @param on `true` while the data is a sparse window of a server result set.
22195
+ */
22196
+ setInfinite(on) {
22197
+ this._infinite.set(on);
22198
+ }
22199
+ /**
22200
+ * Whether a tree parent with no explicit entry in `state.expanded` is open.
22201
+ * Starts at `kjDefaultExpanded`; {@link expandAll} / {@link collapseAll}
22202
+ * flip it so rows loaded later follow the last bulk action.
22203
+ */
22204
+ _expandDefault = signal(false, /* @ts-ignore */
22205
+ ...(ngDevMode ? [{ debugName: "_expandDefault" }] : /* istanbul ignore next */ []));
22206
+ /**
22207
+ * `state.expanded` as TanStack sees it. Open-by-default with no exceptions
22208
+ * is handed over as `true`: TanStack skips expansion altogether for an
22209
+ * empty record, and `getIsRowExpanded` still keeps leaf rows closed.
22210
+ */
22211
+ tanstackExpanded = computed(() => {
22212
+ const expanded = this._state().expanded;
22213
+ if (expanded === true)
22214
+ return true;
22215
+ if (this._expandDefault() && Object.keys(expanded).length === 0)
22216
+ return true;
22217
+ return expanded;
22218
+ }, /* @ts-ignore */
22219
+ ...(ngDevMode ? [{ debugName: "tanstackExpanded" }] : /* istanbul ignore next */ []));
22220
+ /** Whether a row is expanded: its explicit state, else the tree default for parents. */
22221
+ isRowExpanded(row) {
22222
+ const expanded = this._state().expanded;
22223
+ if (expanded === true)
22224
+ return true;
22225
+ const own = expanded[row.id];
22226
+ if (own !== undefined)
22227
+ return own;
22228
+ return this._expandDefault() && (row.subRows?.length ?? 0) > 0;
22229
+ }
22230
+ /** Rows handed to TanStack plus, in infinite mode, their positions in the full result set. */
22231
+ compacted = computed(() => {
22232
+ const data = this.kjTableData();
22233
+ if (!this._infinite())
22234
+ return { rows: data, sourceIndex: null };
22235
+ const rows = [];
22236
+ const sourceIndex = [];
22237
+ // `forEach` skips true holes, so a `new Array(total)` window costs only
22238
+ // its loaded entries.
22239
+ data.forEach((row, i) => {
22240
+ if (row == null)
22241
+ return;
22242
+ rows.push(row);
22243
+ sourceIndex.push(i);
22244
+ });
22245
+ return { rows, sourceIndex };
22246
+ }, /* @ts-ignore */
22247
+ ...(ngDevMode ? [{ debugName: "compacted" }] : /* istanbul ignore next */ []));
22248
+ /**
22249
+ * In infinite mode, the result-set position of each loaded top-level row,
22250
+ * indexed by its TanStack `row.index`; `null` otherwise.
22251
+ */
22252
+ sourceIndex = computed(() => this.compacted().sourceIndex, /* @ts-ignore */
22253
+ ...(ngDevMode ? [{ debugName: "sourceIndex" }] : /* istanbul ignore next */ []));
22254
+ /** Position of a top-level row in the full result set (its `row.index` outside infinite mode). */
22255
+ sourceIndexOf(index) {
22256
+ return this.compacted().sourceIndex?.[index] ?? index;
22257
+ }
22159
22258
  /**
22160
22259
  * Apply a TanStack updater (value or function) to a single state slice.
22161
22260
  *
@@ -22198,11 +22297,15 @@ class KjTable {
22198
22297
  ...(ngDevMode ? [{ debugName: "boundColumns" }] : /* istanbul ignore next */ []));
22199
22298
  /** The TanStack table instance. Access rows, headers, and state here. */
22200
22299
  table = createAngularTable(() => ({
22201
- data: this.kjTableData(),
22300
+ data: this.compacted().rows,
22202
22301
  columns: this.boundColumns(),
22302
+ // In infinite mode a top-level row's index is its result-set position,
22303
+ // so default ids stay stable while pages load around them.
22203
22304
  getRowId: this.kjGetRowId()
22204
- ? (row, index) => this.kjGetRowId()(row, index)
22205
- : undefined,
22305
+ ? (row, index, parent) => this.kjGetRowId()(row, parent ? index : this.sourceIndexOf(index))
22306
+ : this._infinite()
22307
+ ? (_row, index, parent) => (parent ? `${parent.id}.${index}` : String(this.sourceIndexOf(index)))
22308
+ : undefined,
22206
22309
  // `columnResizeMode: 'onEnd'` defers the actual column width write until
22207
22310
  // the user releases the resize handle. While dragging, the wrapper reads
22208
22311
  // `state.columnSizingInfo().deltaOffset` to render a vertical preview
@@ -22212,9 +22315,21 @@ class KjTable {
22212
22315
  // TanStack to manual pagination so the loader-page becomes the
22213
22316
  // visible page (no client-side slicing) and pagination controls
22214
22317
  // derive their totals from the remote count.
22215
- manualPagination: this._rowCount() !== null,
22318
+ manualPagination: this._rowCount() !== null || this._infinite(),
22319
+ manualSorting: this._infinite(),
22320
+ manualFiltering: this._infinite(),
22216
22321
  rowCount: this._rowCount() ?? undefined,
22217
- state: { ...this._state(), columnSizingInfo: this._columnSizingInfo() },
22322
+ getSubRows: this.kjGetSubRows(),
22323
+ getIsRowExpanded: row => this.isRowExpanded(row),
22324
+ enableSubRowSelection: this.kjSelectSubRows(),
22325
+ // Expansion is reset by the directive itself (see the constructor): an
22326
+ // infinite table's page loads must not wipe it.
22327
+ autoResetExpanded: false,
22328
+ state: {
22329
+ ...this._state(),
22330
+ expanded: this.tanstackExpanded(),
22331
+ columnSizingInfo: this._columnSizingInfo(),
22332
+ },
22218
22333
  onSortingChange: u => this.patch('sorting', u),
22219
22334
  onColumnFiltersChange: u => this.patch('columnFilters', u),
22220
22335
  onGlobalFilterChange: u => this.patch('globalFilter', u),
@@ -22239,6 +22354,91 @@ class KjTable {
22239
22354
  enableRowSelection: true,
22240
22355
  enableMultiRowSelection: true,
22241
22356
  }));
22357
+ constructor() {
22358
+ // New rows reset the expansion to `kjDefaultExpanded` (TanStack's own
22359
+ // auto-reset, kept), except in infinite mode where data changes are page
22360
+ // loads — the infinite wrapper calls `resetExpansion()` on a real reset.
22361
+ let seeded = false;
22362
+ effect(() => {
22363
+ this.kjTableData();
22364
+ if (!seeded) {
22365
+ seeded = true;
22366
+ return;
22367
+ }
22368
+ untracked(() => {
22369
+ if (!this._infinite())
22370
+ this.resetExpansion();
22371
+ });
22372
+ });
22373
+ // A new `kjDefaultExpanded` restarts the tree from that default.
22374
+ effect(() => {
22375
+ const open = this.kjDefaultExpanded();
22376
+ untracked(() => {
22377
+ if (this._expandDefault() === open)
22378
+ return;
22379
+ this._expandDefault.set(open);
22380
+ this.patch('expanded', {});
22381
+ });
22382
+ });
22383
+ }
22384
+ // ── Tree rows ───────────────────────────────────────────────────────────
22385
+ /**
22386
+ * Whether every loaded parent row is expanded (`false` when no row has
22387
+ * children). Rows of an infinite table that are not loaded yet do not count.
22388
+ */
22389
+ isAllExpanded = computed(() => {
22390
+ this._state();
22391
+ this._expandDefault();
22392
+ let parents = 0;
22393
+ for (const row of this.table().getCoreRowModel().flatRows) {
22394
+ if (!row.subRows?.length)
22395
+ continue;
22396
+ parents++;
22397
+ if (!this.isRowExpanded(row))
22398
+ return false;
22399
+ }
22400
+ return parents > 0;
22401
+ }, /* @ts-ignore */
22402
+ ...(ngDevMode ? [{ debugName: "isAllExpanded" }] : /* istanbul ignore next */ []));
22403
+ /**
22404
+ * Whether a row is expanded — its own choice, else `kjDefaultExpanded`
22405
+ * (or the last expand-all / collapse-all) for parents.
22406
+ * @param row The row.
22407
+ */
22408
+ getRowExpanded(row) {
22409
+ return this.isRowExpanded(row);
22410
+ }
22411
+ /**
22412
+ * Expand or collapse one row. Unlike TanStack's `row.toggleExpanded()`, the
22413
+ * choice is stored explicitly, so it holds against `kjDefaultExpanded`.
22414
+ * @param row The row.
22415
+ * @param expanded The new state; omitted, the current one flips.
22416
+ * @returns The row's new expanded state.
22417
+ */
22418
+ setRowExpanded(row, expanded) {
22419
+ const next = expanded ?? !this.isRowExpanded(row);
22420
+ const prev = this._state().expanded;
22421
+ // `true` ("every row") becomes "open by default" plus this exception.
22422
+ if (prev === true)
22423
+ this._expandDefault.set(true);
22424
+ this.patch('expanded', { ...(prev === true ? {} : prev), [row.id]: next });
22425
+ return next;
22426
+ }
22427
+ /** Expand every parent row, including the ones loaded later. */
22428
+ expandAll() {
22429
+ this._expandDefault.set(true);
22430
+ this.patch('expanded', {});
22431
+ }
22432
+ /** Collapse every parent row, including the ones loaded later. */
22433
+ collapseAll() {
22434
+ this._expandDefault.set(false);
22435
+ this.patch('expanded', {});
22436
+ }
22437
+ /** Forget per-row expansion and go back to `kjDefaultExpanded` — e.g. when the rows are replaced. */
22438
+ resetExpansion() {
22439
+ this._expandDefault.set(untracked(this.kjDefaultExpanded));
22440
+ this.patch('expanded', {});
22441
+ }
22242
22442
  /** Merge a partial state. Unspecified slices are left untouched. */
22243
22443
  setState(partial) {
22244
22444
  this._state.update(s => ({ ...s, ...partial }));
@@ -22273,7 +22473,7 @@ class KjTable {
22273
22473
  */
22274
22474
  get columnApi() { return this.buildApi().columnApi; }
22275
22475
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: KjTable, deps: [], target: i0.ɵɵFactoryTarget.Directive });
22276
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.5", type: KjTable, isStandalone: true, selector: "[kjTable]", inputs: { kjTable: { classPropertyName: "kjTable", publicName: "kjTable", isSignal: true, isRequired: true, transformFunction: null }, kjTableData: { classPropertyName: "kjTableData", publicName: "kjTableData", isSignal: true, isRequired: false, transformFunction: null }, kjGetRowId: { classPropertyName: "kjGetRowId", publicName: "kjGetRowId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { kjTableData: "kjTableDataChange" }, providers: [{ provide: KJ_TABLE, useExisting: KjTable }], exportAs: ["kjTable"], ngImport: i0 });
22476
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.5", type: KjTable, isStandalone: true, selector: "[kjTable]", inputs: { kjTable: { classPropertyName: "kjTable", publicName: "kjTable", isSignal: true, isRequired: true, transformFunction: null }, kjTableData: { classPropertyName: "kjTableData", publicName: "kjTableData", isSignal: true, isRequired: false, transformFunction: null }, kjGetRowId: { classPropertyName: "kjGetRowId", publicName: "kjGetRowId", isSignal: true, isRequired: false, transformFunction: null }, kjGetSubRows: { classPropertyName: "kjGetSubRows", publicName: "kjGetSubRows", isSignal: true, isRequired: false, transformFunction: null }, kjSelectSubRows: { classPropertyName: "kjSelectSubRows", publicName: "kjSelectSubRows", isSignal: true, isRequired: false, transformFunction: null }, kjDefaultExpanded: { classPropertyName: "kjDefaultExpanded", publicName: "kjDefaultExpanded", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { kjTableData: "kjTableDataChange" }, providers: [{ provide: KJ_TABLE, useExisting: KjTable }], exportAs: ["kjTable"], ngImport: i0 });
22277
22477
  }
22278
22478
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: KjTable, decorators: [{
22279
22479
  type: Directive,
@@ -22283,7 +22483,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
22283
22483
  exportAs: 'kjTable',
22284
22484
  providers: [{ provide: KJ_TABLE, useExisting: KjTable }],
22285
22485
  }]
22286
- }], propDecorators: { kjTable: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjTable", required: true }] }], kjTableData: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjTableData", required: false }] }, { type: i0.Output, args: ["kjTableDataChange"] }], kjGetRowId: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjGetRowId", required: false }] }] } });
22486
+ }], ctorParameters: () => [], propDecorators: { kjTable: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjTable", required: true }] }], kjTableData: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjTableData", required: false }] }, { type: i0.Output, args: ["kjTableDataChange"] }], kjGetRowId: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjGetRowId", required: false }] }], kjGetSubRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjGetSubRows", required: false }] }], kjSelectSubRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjSelectSubRows", required: false }] }], kjDefaultExpanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjDefaultExpanded", required: false }] }] } });
22287
22487
  /** Angular's NG0950 — a required input read before its first binding. */
22288
22488
  function isUnsetRequiredInput(error) {
22289
22489
  return typeof error === 'object' && error !== null && error.code === -950;
@@ -22512,9 +22712,24 @@ class KjTableRow {
22512
22712
  /** TanStack row instance — pass from getRowModel().rows. */
22513
22713
  kjRow = input.required(/* @ts-ignore */
22514
22714
  ...(ngDevMode ? [{ debugName: "kjRow" }] : /* istanbul ignore next */ []));
22715
+ /**
22716
+ * Overrides the 1-based `aria-rowindex` — for rows whose place in the grid
22717
+ * is not their `row.index`, like the children of a tree row. `null`
22718
+ * (default) derives it from the row.
22719
+ */
22720
+ kjAriaRowIndex = input(null, /* @ts-ignore */
22721
+ ...(ngDevMode ? [{ debugName: "kjAriaRowIndex" }] : /* istanbul ignore next */ []));
22515
22722
  table = injectParent(KJ_TABLE, { child: 'KjTableRow', parent: '[kjTable]' });
22516
22723
  /** 1-based ARIA index — accounts for header row(s). */
22517
- ariaRowIndex = computed(() => this.kjRow().index + 2, /* @ts-ignore */
22724
+ ariaRowIndex = computed(() => {
22725
+ const override = this.kjAriaRowIndex();
22726
+ if (override != null)
22727
+ return override;
22728
+ const row = this.kjRow();
22729
+ // Infinite mode: the row's place in the whole result set, not in the loaded window.
22730
+ const source = row.depth === 0 ? this.table.sourceIndex()?.[row.index] : undefined;
22731
+ return (source ?? row.index) + 2;
22732
+ }, /* @ts-ignore */
22518
22733
  ...(ngDevMode ? [{ debugName: "ariaRowIndex" }] : /* istanbul ignore next */ []));
22519
22734
  isSelectable = computed(() => Object.keys(this.table.state.rowSelection()).length >= 0, /* @ts-ignore */
22520
22735
  ...(ngDevMode ? [{ debugName: "isSelectable" }] : /* istanbul ignore next */ [])); // always true if selection enabled
@@ -22525,7 +22740,7 @@ class KjTableRow {
22525
22740
  }, /* @ts-ignore */
22526
22741
  ...(ngDevMode ? [{ debugName: "isSelected" }] : /* istanbul ignore next */ []));
22527
22742
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: KjTableRow, deps: [], target: i0.ɵɵFactoryTarget.Directive });
22528
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.5", type: KjTableRow, isStandalone: true, selector: "[kjTableRow]", inputs: { kjRow: { classPropertyName: "kjRow", publicName: "kjRow", isSignal: true, isRequired: true, transformFunction: null } }, host: { attributes: { "role": "row" }, properties: { "attr.aria-rowindex": "ariaRowIndex()", "attr.aria-selected": "isSelectable() ? isSelected() : null", "attr.data-selected": "isSelected() ? \"\" : null" } }, ngImport: i0 });
22743
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.5", type: KjTableRow, isStandalone: true, selector: "[kjTableRow]", inputs: { kjRow: { classPropertyName: "kjRow", publicName: "kjRow", isSignal: true, isRequired: true, transformFunction: null }, kjAriaRowIndex: { classPropertyName: "kjAriaRowIndex", publicName: "kjAriaRowIndex", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "row" }, properties: { "attr.aria-rowindex": "ariaRowIndex()", "attr.aria-selected": "isSelectable() ? isSelected() : null", "attr.data-selected": "isSelected() ? \"\" : null" } }, ngImport: i0 });
22529
22744
  }
22530
22745
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: KjTableRow, decorators: [{
22531
22746
  type: Directive,
@@ -22539,7 +22754,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
22539
22754
  '[attr.data-selected]': 'isSelected() ? "" : null',
22540
22755
  },
22541
22756
  }]
22542
- }], propDecorators: { kjRow: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjRow", required: true }] }] } });
22757
+ }], propDecorators: { kjRow: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjRow", required: true }] }], kjAriaRowIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjAriaRowIndex", required: false }] }] } });
22543
22758
 
22544
22759
  /** Context token provided by `KjTableKeyboardNav` and injected by `KjTableCell`. */
22545
22760
  const KJ_TABLE_KEYBOARD_NAV = new InjectionToken('KjTableKeyboardNav');