@rozie-ui/data-table-lit 0.3.2 → 0.5.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.
- package/README.md +129 -76
- package/dist/index.cjs +502 -120
- package/dist/index.d.cts +69 -25
- package/dist/index.d.mts +69 -25
- package/dist/index.mjs +502 -120
- package/package.json +2 -2
- package/src/Column.ts +2 -2
- package/src/DataTable.ts +534 -124
- package/src/themes/base.css +38 -25
- package/src/themes/bootstrap.css +11 -0
- package/src/themes/material.css +11 -0
- package/src/themes/shadcn.css +11 -0
package/src/DataTable.ts
CHANGED
|
@@ -52,13 +52,13 @@ interface RozieSelectAllSlotCtx {
|
|
|
52
52
|
toggle: any;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
interface
|
|
55
|
+
interface RozieDynamicColHeaderSlotCtx {
|
|
56
56
|
columnId: any;
|
|
57
57
|
column: any;
|
|
58
58
|
label: any;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
interface
|
|
61
|
+
interface RozieDynamicFilterSlotCtx {
|
|
62
62
|
columnId: any;
|
|
63
63
|
value: any;
|
|
64
64
|
uniqueValues: any;
|
|
@@ -72,14 +72,14 @@ interface RozieSelectCellSlotCtx {
|
|
|
72
72
|
toggle: any;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
interface
|
|
75
|
+
interface RozieDynamicCellSlotCtx {
|
|
76
76
|
columnId: any;
|
|
77
77
|
column: any;
|
|
78
78
|
row: any;
|
|
79
79
|
value: any;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
interface
|
|
82
|
+
interface RozieDynamicEditorSlotCtx {
|
|
83
83
|
columnId: any;
|
|
84
84
|
column: any;
|
|
85
85
|
row: any;
|
|
@@ -93,12 +93,71 @@ interface RozieDetailSlotCtx {
|
|
|
93
93
|
row: any;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
interface RozieColHeaderSlotCtx {
|
|
97
|
+
columnId: any;
|
|
98
|
+
column: any;
|
|
99
|
+
label: any;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
interface RozieFilterSlotCtx {
|
|
103
|
+
columnId: any;
|
|
104
|
+
value: any;
|
|
105
|
+
uniqueValues: any;
|
|
106
|
+
minMax: any;
|
|
107
|
+
setFilter: any;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
interface RozieCellSlotCtx {
|
|
111
|
+
columnId: any;
|
|
112
|
+
column: any;
|
|
113
|
+
row: any;
|
|
114
|
+
value: any;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
interface RozieEditorSlotCtx {
|
|
118
|
+
columnId: any;
|
|
119
|
+
column: any;
|
|
120
|
+
row: any;
|
|
121
|
+
value: any;
|
|
122
|
+
commit: any;
|
|
123
|
+
cancel: any;
|
|
124
|
+
autofocus: any;
|
|
125
|
+
}
|
|
126
|
+
|
|
96
127
|
@customElement('rozie-data-table')
|
|
97
128
|
export default class DataTable extends SignalWatcher(LitElement) {
|
|
98
129
|
static shadowRootOptions: ShadowRootInit = { ...LitElement.shadowRootOptions, slotAssignment: 'manual' };
|
|
99
130
|
|
|
100
131
|
static styles = css`
|
|
101
132
|
:host{display:contents}
|
|
133
|
+
:host {
|
|
134
|
+
--rdt-font: var(--rozie-data-table-font);
|
|
135
|
+
--rdt-color: var(--rozie-data-table-fg);
|
|
136
|
+
--rdt-cell-padding: var(--rozie-data-table-cell-padding);
|
|
137
|
+
--rdt-border: 1px solid var(--rozie-data-table-border-color);
|
|
138
|
+
--rdt-header-bg: var(--rozie-data-table-header-bg);
|
|
139
|
+
--rdt-header-weight: var(--rozie-data-table-header-weight);
|
|
140
|
+
--rdt-sort-ind-opacity: var(--rozie-data-table-sort-indicator-opacity);
|
|
141
|
+
--rdt-filter-border: 1px solid var(--rozie-data-table-control-border-color);
|
|
142
|
+
--rdt-filter-radius: var(--rozie-data-table-radius);
|
|
143
|
+
--rdt-page-btn-border: 1px solid var(--rozie-data-table-control-border-color);
|
|
144
|
+
--rdt-page-btn-radius: var(--rozie-data-table-radius);
|
|
145
|
+
--rdt-page-btn-disabled-opacity: var(--rozie-data-table-disabled-opacity);
|
|
146
|
+
--rdt-page-size-border: 1px solid var(--rozie-data-table-control-border-color);
|
|
147
|
+
--rdt-page-size-radius: var(--rozie-data-table-radius);
|
|
148
|
+
--rdt-resize-grip-color: var(--rozie-data-table-resize-grip-color);
|
|
149
|
+
--rdt-resize-grip-active: var(--rozie-data-table-resize-grip-active);
|
|
150
|
+
--rdt-pin-btn-border: 1px solid var(--rozie-data-table-control-border-color);
|
|
151
|
+
--rdt-pin-btn-radius: var(--rozie-data-table-radius);
|
|
152
|
+
--rdt-pin-btn-active-bg: var(--rozie-data-table-pin-active-bg);
|
|
153
|
+
--rdt-colvis-summary-border: 1px solid var(--rozie-data-table-control-border-color);
|
|
154
|
+
--rdt-colvis-summary-radius: var(--rozie-data-table-radius);
|
|
155
|
+
--rdt-colvis-menu-border: 1px solid var(--rozie-data-table-border-color);
|
|
156
|
+
--rdt-colvis-menu-radius: var(--rozie-data-table-radius);
|
|
157
|
+
--rdt-colvis-menu-bg: var(--rozie-data-table-menu-bg);
|
|
158
|
+
--rdt-colvis-menu-shadow: var(--rozie-data-table-menu-shadow);
|
|
159
|
+
--rdt-select-accent: var(--rozie-data-table-select-accent);
|
|
160
|
+
}
|
|
102
161
|
.rozie-data-table[data-rozie-s-d5dcab4c] {
|
|
103
162
|
border-collapse: collapse;
|
|
104
163
|
width: 100%;
|
|
@@ -436,14 +495,14 @@ export default class DataTable extends SignalWatcher(LitElement) {
|
|
|
436
495
|
`;
|
|
437
496
|
|
|
438
497
|
/**
|
|
439
|
-
* The row data — `model: true`, so a committed cell/row edit writes a **fresh** array back through `
|
|
498
|
+
* The row data — `model: true`, so a committed cell/row edit writes a **fresh** array back through the two-way `data` binding (uncontrolled fallback `dataDefault`). A stable reference per Rozie's setup-once model — fed directly into table-core (never map/cloned in the watcher).
|
|
440
499
|
* @example
|
|
441
500
|
* <rozie-data-table .data=${rows} @data-change=${…} .columns=${cols}></rozie-data-table>
|
|
442
501
|
*/
|
|
443
502
|
@property({ type: Array, attribute: 'data' }) _data_attr!: any[];
|
|
444
503
|
private _dataControllable = createLitControllableProperty<any[]>({ host: this, eventName: 'data-change', defaultValue: [], initialControlledValue: undefined });
|
|
445
504
|
/**
|
|
446
|
-
* Config-array column fallback (lower precedence than `<Column>` children). Each entry: `{ id?, field, header?, sortable?, filterable?, pinned?, width? }`. Columns may come from this array, from `<Column>` children, or both (id-keyed last-write-wins union).
|
|
505
|
+
* Config-array column fallback (lower precedence than `<Column>` children). Each entry: `{ id?, field, header?, sortable?, filterable?, pinned?, width? }`. Columns may come from this array, from `<Column>` children, or both (id-keyed last-write-wins union). A `pinned` value of `'left'` or `'right'` is applied once as the table's initial `columnPinning` state — identical to `<Column pinned>` — which also REORDERS that column, since table-core orders visible cells `[left-pinned, center, right-pinned]`. A consumer who has already expressed a pin (an initial two-way `columnPinning` model value or an interactive pin) owns the slice and this declaration is ignored.
|
|
447
506
|
*/
|
|
448
507
|
@property({ type: Array }) columns: any[] = [];
|
|
449
508
|
/**
|
|
@@ -489,7 +548,7 @@ export default class DataTable extends SignalWatcher(LitElement) {
|
|
|
489
548
|
*/
|
|
490
549
|
@property({ type: Number, reflect: true }) pageCount: number | null = null;
|
|
491
550
|
/**
|
|
492
|
-
* Opt-in **expandable rows**. When `true`, a leading chevron expander column auto-injects (after the select column) and `getExpandedRowModel` activates; default `false` is byte-identical-off. Every row can expand to reveal a `#detail` panel unless `getSubRows` is supplied (then only rows with children expand).
|
|
551
|
+
* Opt-in **expandable rows**. When `true`, a leading chevron expander column auto-injects (after the select column) and `getExpandedRowModel` activates; default `false` is byte-identical-off. Every row can expand to reveal a `#detail` panel unless `getSubRows` is supplied (then only rows with children expand). Pass `expandable` as a real boolean — a valueless attribute only coerces to `true` on Vue and Lit.
|
|
493
552
|
*/
|
|
494
553
|
@property({ type: Boolean, reflect: true }) expandable: boolean = false;
|
|
495
554
|
/**
|
|
@@ -506,7 +565,7 @@ export default class DataTable extends SignalWatcher(LitElement) {
|
|
|
506
565
|
*/
|
|
507
566
|
@property({ type: Boolean, reflect: true }) groupable: boolean = false;
|
|
508
567
|
/**
|
|
509
|
-
* `GroupingState` — an ordered `string[]` of column ids (multi-column → nested groups, e.g. `['region','category']`). An empty/unbound list is ungrouped (byte-identical-off). Group-header rows are collapsible (they ride the expand model). Surfaces through `group-change`; uncontrolled fallback (`$data.groupingDefault`, default `[]`) when unbound — the default is `null` (mirroring `expanded`) so the uncontrolled fallback is reachable and the grouping auto-expand default can activate when a consumer applies grouping without binding `
|
|
568
|
+
* `GroupingState` — an ordered `string[]` of column ids (multi-column → nested groups, e.g. `['region','category']`). An empty/unbound list is ungrouped (byte-identical-off). Group-header rows are collapsible (they ride the expand model). Surfaces through `group-change`; uncontrolled fallback (`$data.groupingDefault`, default `[]`) when unbound — the default is `null` (mirroring `expanded`) so the uncontrolled fallback is reachable and the grouping auto-expand default can activate when a consumer applies grouping without binding the two-way `grouping` model (a non-null `[]` default would short-circuit it). All reads are null-guarded, so table-core still receives an array.
|
|
510
569
|
*/
|
|
511
570
|
@property({ type: Array, attribute: 'grouping' }) _grouping_attr: any[] | null = null;
|
|
512
571
|
private _groupingControllable = createLitControllableProperty<any[]>({ host: this, eventName: 'grouping-change', defaultValue: null, initialControlledValue: undefined });
|
|
@@ -546,7 +605,7 @@ export default class DataTable extends SignalWatcher(LitElement) {
|
|
|
546
605
|
*/
|
|
547
606
|
@property({ type: Boolean, reflect: true }) stickyHeader: boolean = false;
|
|
548
607
|
/**
|
|
549
|
-
* `'table'` (default, row-oriented, byte-behaviorally identical to a plain accessible table) | `'grid'`
|
|
608
|
+
* `'table'` (default, row-oriented, byte-behaviorally identical to a plain accessible table) | `'grid'` — lights up the full WAI-ARIA **[grid interaction mode](/components/data-table-grid-mode)**: `role="grid"`, a roving single tab-stop, 2-D APG arrow-key cell navigation, range selection, and clipboard support.
|
|
550
609
|
*/
|
|
551
610
|
@property({ type: String, reflect: true }) interactionMode: string = 'table';
|
|
552
611
|
/**
|
|
@@ -658,24 +717,32 @@ private __rozieCtxProvider_data_table_columns = new ContextProvider(this, { cont
|
|
|
658
717
|
@state() private _hasSlotSelectAll = false;
|
|
659
718
|
@queryAssignedElements({ slot: 'selectAll', flatten: true }) private _slotSelectAllElements!: Element[];
|
|
660
719
|
@property({ attribute: false }) selectAll?: (scope: { checked: any; indeterminate: any; toggle: any }) => unknown;
|
|
720
|
+
@state() private _hasSlotDynamicColHeader = false;
|
|
721
|
+
@queryAssignedElements({ flatten: true }) private _slotDynamicColHeaderElements!: Element[];
|
|
722
|
+
@state() private _hasSlotDynamicFilter = false;
|
|
723
|
+
@queryAssignedElements({ flatten: true }) private _slotDynamicFilterElements!: Element[];
|
|
724
|
+
@state() private _hasSlotSelectCell = false;
|
|
725
|
+
@queryAssignedElements({ slot: 'selectCell', flatten: true }) private _slotSelectCellElements!: Element[];
|
|
726
|
+
@property({ attribute: false }) selectCell?: (scope: { row: any; checked: any; toggle: any }) => unknown;
|
|
727
|
+
@state() private _hasSlotDynamicCell = false;
|
|
728
|
+
@queryAssignedElements({ flatten: true }) private _slotDynamicCellElements!: Element[];
|
|
729
|
+
@state() private _hasSlotDynamicEditor = false;
|
|
730
|
+
@queryAssignedElements({ flatten: true }) private _slotDynamicEditorElements!: Element[];
|
|
731
|
+
@state() private _hasSlotDetail = false;
|
|
732
|
+
@queryAssignedElements({ slot: 'detail', flatten: true }) private _slotDetailElements!: Element[];
|
|
733
|
+
@property({ attribute: false }) detail?: (scope: { row: any }) => unknown;
|
|
661
734
|
@state() private _hasSlotColHeader = false;
|
|
662
735
|
@queryAssignedElements({ slot: 'colHeader', flatten: true }) private _slotColHeaderElements!: Element[];
|
|
663
736
|
@property({ attribute: false }) colHeader?: (scope: { columnId: any; column: any; label: any }) => unknown;
|
|
664
737
|
@state() private _hasSlotFilter = false;
|
|
665
738
|
@queryAssignedElements({ slot: 'filter', flatten: true }) private _slotFilterElements!: Element[];
|
|
666
739
|
@property({ attribute: false }) filter?: (scope: { columnId: any; value: any; uniqueValues: any; minMax: any; setFilter: any }) => unknown;
|
|
667
|
-
@state() private _hasSlotSelectCell = false;
|
|
668
|
-
@queryAssignedElements({ slot: 'selectCell', flatten: true }) private _slotSelectCellElements!: Element[];
|
|
669
|
-
@property({ attribute: false }) selectCell?: (scope: { row: any; checked: any; toggle: any }) => unknown;
|
|
670
740
|
@state() private _hasSlotCell = false;
|
|
671
741
|
@queryAssignedElements({ slot: 'cell', flatten: true }) private _slotCellElements!: Element[];
|
|
672
742
|
@property({ attribute: false }) cell?: (scope: { columnId: any; column: any; row: any; value: any }) => unknown;
|
|
673
743
|
@state() private _hasSlotEditor = false;
|
|
674
744
|
@queryAssignedElements({ slot: 'editor', flatten: true }) private _slotEditorElements!: Element[];
|
|
675
745
|
@property({ attribute: false }) editor?: (scope: { columnId: any; column: any; row: any; value: any; commit: any; cancel: any; autofocus: any }) => unknown;
|
|
676
|
-
@state() private _hasSlotDetail = false;
|
|
677
|
-
@queryAssignedElements({ slot: 'detail', flatten: true }) private _slotDetailElements!: Element[];
|
|
678
|
-
@property({ attribute: false }) detail?: (scope: { row: any }) => unknown;
|
|
679
746
|
// Phase 79 Plan 08 (R4) contract for 79-09: the record intake for
|
|
680
747
|
// record-routed slot fills. 79-09's consumer-side emitSlotFiller
|
|
681
748
|
// accumulates an object literal onto the SAME `.rozieSlots=${{ ... }}`
|
|
@@ -685,7 +752,7 @@ private __rozieCtxProvider_data_table_columns = new ContextProvider(this, { cont
|
|
|
685
752
|
// named function-prop / <slot> fallback (AC-9). Attribute
|
|
686
753
|
// deserialization is disabled — this is a function-valued record,
|
|
687
754
|
// never reflected to/from an HTML attribute.
|
|
688
|
-
@property({ attribute: false }) rozieSlots?: Record<string, (scope: any) => unknown>;
|
|
755
|
+
@property({ attribute: false }) rozieSlots?: { [key: `colHeader-${string}`]: (scope: { columnId: any; column: any; label: any }) => unknown; [key: `filter-${string}`]: (scope: { columnId: any; value: any; uniqueValues: any; minMax: any; setFilter: any }) => unknown; [key: `cell-${string}`]: (scope: { columnId: any; column: any; row: any; value: any }) => unknown; [key: `editor-${string}`]: (scope: { columnId: any; column: any; row: any; value: any; commit: any; cancel: any; autofocus: any }) => unknown; } & Record<string, (scope: any) => unknown>;
|
|
689
756
|
|
|
690
757
|
private _disconnectCleanups: Array<() => void> = [];
|
|
691
758
|
// Re-parenting guard: set true once the deferred teardown has actually
|
|
@@ -727,9 +794,9 @@ private __rozieCtxProvider_data_table_columns = new ContextProvider(this, { cont
|
|
|
727
794
|
}
|
|
728
795
|
|
|
729
796
|
{
|
|
730
|
-
const slotEl = this.shadowRoot?.querySelector('slot[name
|
|
797
|
+
const slotEl = this.shadowRoot?.querySelector('slot:not([name])');
|
|
731
798
|
if (slotEl !== null && slotEl !== undefined) {
|
|
732
|
-
const update = () => { this.
|
|
799
|
+
const update = () => { this._hasSlotDynamicColHeader = this._slotDynamicColHeaderElements.length > 0; };
|
|
733
800
|
slotEl.addEventListener('slotchange', update);
|
|
734
801
|
// CR-05 fix: push cleanup so the listener is removed on disconnectedCallback.
|
|
735
802
|
this._disconnectCleanups.push(() => slotEl.removeEventListener('slotchange', update));
|
|
@@ -738,9 +805,9 @@ private __rozieCtxProvider_data_table_columns = new ContextProvider(this, { cont
|
|
|
738
805
|
}
|
|
739
806
|
|
|
740
807
|
{
|
|
741
|
-
const slotEl = this.shadowRoot?.querySelector('slot[name
|
|
808
|
+
const slotEl = this.shadowRoot?.querySelector('slot:not([name])');
|
|
742
809
|
if (slotEl !== null && slotEl !== undefined) {
|
|
743
|
-
const update = () => { this.
|
|
810
|
+
const update = () => { this._hasSlotDynamicFilter = this._slotDynamicFilterElements.length > 0; };
|
|
744
811
|
slotEl.addEventListener('slotchange', update);
|
|
745
812
|
// CR-05 fix: push cleanup so the listener is removed on disconnectedCallback.
|
|
746
813
|
this._disconnectCleanups.push(() => slotEl.removeEventListener('slotchange', update));
|
|
@@ -760,9 +827,9 @@ private __rozieCtxProvider_data_table_columns = new ContextProvider(this, { cont
|
|
|
760
827
|
}
|
|
761
828
|
|
|
762
829
|
{
|
|
763
|
-
const slotEl = this.shadowRoot?.querySelector('slot[name
|
|
830
|
+
const slotEl = this.shadowRoot?.querySelector('slot:not([name])');
|
|
764
831
|
if (slotEl !== null && slotEl !== undefined) {
|
|
765
|
-
const update = () => { this.
|
|
832
|
+
const update = () => { this._hasSlotDynamicCell = this._slotDynamicCellElements.length > 0; };
|
|
766
833
|
slotEl.addEventListener('slotchange', update);
|
|
767
834
|
// CR-05 fix: push cleanup so the listener is removed on disconnectedCallback.
|
|
768
835
|
this._disconnectCleanups.push(() => slotEl.removeEventListener('slotchange', update));
|
|
@@ -771,9 +838,9 @@ private __rozieCtxProvider_data_table_columns = new ContextProvider(this, { cont
|
|
|
771
838
|
}
|
|
772
839
|
|
|
773
840
|
{
|
|
774
|
-
const slotEl = this.shadowRoot?.querySelector('slot[name
|
|
841
|
+
const slotEl = this.shadowRoot?.querySelector('slot:not([name])');
|
|
775
842
|
if (slotEl !== null && slotEl !== undefined) {
|
|
776
|
-
const update = () => { this.
|
|
843
|
+
const update = () => { this._hasSlotDynamicEditor = this._slotDynamicEditorElements.length > 0; };
|
|
777
844
|
slotEl.addEventListener('slotchange', update);
|
|
778
845
|
// CR-05 fix: push cleanup so the listener is removed on disconnectedCallback.
|
|
779
846
|
this._disconnectCleanups.push(() => slotEl.removeEventListener('slotchange', update));
|
|
@@ -791,6 +858,50 @@ private __rozieCtxProvider_data_table_columns = new ContextProvider(this, { cont
|
|
|
791
858
|
update();
|
|
792
859
|
}
|
|
793
860
|
}
|
|
861
|
+
|
|
862
|
+
{
|
|
863
|
+
const slotEl = this.shadowRoot?.querySelector('slot[name="colHeader"]');
|
|
864
|
+
if (slotEl !== null && slotEl !== undefined) {
|
|
865
|
+
const update = () => { this._hasSlotColHeader = this._slotColHeaderElements.length > 0; };
|
|
866
|
+
slotEl.addEventListener('slotchange', update);
|
|
867
|
+
// CR-05 fix: push cleanup so the listener is removed on disconnectedCallback.
|
|
868
|
+
this._disconnectCleanups.push(() => slotEl.removeEventListener('slotchange', update));
|
|
869
|
+
update();
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
{
|
|
874
|
+
const slotEl = this.shadowRoot?.querySelector('slot[name="filter"]');
|
|
875
|
+
if (slotEl !== null && slotEl !== undefined) {
|
|
876
|
+
const update = () => { this._hasSlotFilter = this._slotFilterElements.length > 0; };
|
|
877
|
+
slotEl.addEventListener('slotchange', update);
|
|
878
|
+
// CR-05 fix: push cleanup so the listener is removed on disconnectedCallback.
|
|
879
|
+
this._disconnectCleanups.push(() => slotEl.removeEventListener('slotchange', update));
|
|
880
|
+
update();
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
{
|
|
885
|
+
const slotEl = this.shadowRoot?.querySelector('slot[name="cell"]');
|
|
886
|
+
if (slotEl !== null && slotEl !== undefined) {
|
|
887
|
+
const update = () => { this._hasSlotCell = this._slotCellElements.length > 0; };
|
|
888
|
+
slotEl.addEventListener('slotchange', update);
|
|
889
|
+
// CR-05 fix: push cleanup so the listener is removed on disconnectedCallback.
|
|
890
|
+
this._disconnectCleanups.push(() => slotEl.removeEventListener('slotchange', update));
|
|
891
|
+
update();
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
{
|
|
896
|
+
const slotEl = this.shadowRoot?.querySelector('slot[name="editor"]');
|
|
897
|
+
if (slotEl !== null && slotEl !== undefined) {
|
|
898
|
+
const update = () => { this._hasSlotEditor = this._slotEditorElements.length > 0; };
|
|
899
|
+
slotEl.addEventListener('slotchange', update);
|
|
900
|
+
// CR-05 fix: push cleanup so the listener is removed on disconnectedCallback.
|
|
901
|
+
this._disconnectCleanups.push(() => slotEl.removeEventListener('slotchange', update));
|
|
902
|
+
update();
|
|
903
|
+
}
|
|
904
|
+
}
|
|
794
905
|
}
|
|
795
906
|
|
|
796
907
|
connectedCallback(): void {
|
|
@@ -798,12 +909,16 @@ private __rozieCtxProvider_data_table_columns = new ContextProvider(this, { cont
|
|
|
798
909
|
this._hasSlotDefault = Array.from(this.children).some((el) => !el.hasAttribute('slot') && (el.nodeType !== 3 || (el.textContent?.trim().length ?? 0) > 0));
|
|
799
910
|
this._hasSlotGroupBar = Array.from(this.children).some((el) => el.getAttribute('slot') === 'groupBar');
|
|
800
911
|
this._hasSlotSelectAll = Array.from(this.children).some((el) => el.getAttribute('slot') === 'selectAll');
|
|
912
|
+
this._hasSlotDynamicColHeader = Array.from(this.children).some((el) => !el.hasAttribute('slot') && (el.nodeType !== 3 || (el.textContent?.trim().length ?? 0) > 0));
|
|
913
|
+
this._hasSlotDynamicFilter = Array.from(this.children).some((el) => !el.hasAttribute('slot') && (el.nodeType !== 3 || (el.textContent?.trim().length ?? 0) > 0));
|
|
914
|
+
this._hasSlotSelectCell = Array.from(this.children).some((el) => el.getAttribute('slot') === 'selectCell');
|
|
915
|
+
this._hasSlotDynamicCell = Array.from(this.children).some((el) => !el.hasAttribute('slot') && (el.nodeType !== 3 || (el.textContent?.trim().length ?? 0) > 0));
|
|
916
|
+
this._hasSlotDynamicEditor = Array.from(this.children).some((el) => !el.hasAttribute('slot') && (el.nodeType !== 3 || (el.textContent?.trim().length ?? 0) > 0));
|
|
917
|
+
this._hasSlotDetail = Array.from(this.children).some((el) => el.getAttribute('slot') === 'detail');
|
|
801
918
|
this._hasSlotColHeader = Array.from(this.children).some((el) => el.getAttribute('slot') === 'colHeader');
|
|
802
919
|
this._hasSlotFilter = Array.from(this.children).some((el) => el.getAttribute('slot') === 'filter');
|
|
803
|
-
this._hasSlotSelectCell = Array.from(this.children).some((el) => el.getAttribute('slot') === 'selectCell');
|
|
804
920
|
this._hasSlotCell = Array.from(this.children).some((el) => el.getAttribute('slot') === 'cell');
|
|
805
921
|
this._hasSlotEditor = Array.from(this.children).some((el) => el.getAttribute('slot') === 'editor');
|
|
806
|
-
this._hasSlotDetail = Array.from(this.children).some((el) => el.getAttribute('slot') === 'detail');
|
|
807
922
|
super.connectedCallback();
|
|
808
923
|
if (this.hasUpdated && this._rozieTornDown) { this._rozieTornDown = false; this._armListeners(); }
|
|
809
924
|
}
|
|
@@ -825,6 +940,12 @@ private __rozieCtxProvider_data_table_columns = new ContextProvider(this, { cont
|
|
|
825
940
|
// (Consumers memoize the array as with $props.data/$props.sorting; the uncontrolled
|
|
826
941
|
// <Column>-children path leaves $props.columns undefined — a stable no-op getter.)
|
|
827
942
|
this.columns, this._colReg.value])(); untracked(() => { if (this.__rozieWatchInitial_0) { this.__rozieWatchInitial_0 = false; return; } (() => {
|
|
943
|
+
// Seed BEFORE the re-feed so the pinning this call may write is part of the state object
|
|
944
|
+
// table-core receives on this same tick (rather than landing a frame late and re-ordering
|
|
945
|
+
// getVisibleCells() a render after the columns appear). One-shot and self-latching — see
|
|
946
|
+
// seedColumnPinning() in stateAssembly.rzts. This is the `<Column>`-children entry point:
|
|
947
|
+
// those register during mount, so the $onMount call below can find zero columns.
|
|
948
|
+
this.seedColumnPinning();
|
|
828
949
|
this.reFeed();
|
|
829
950
|
this.maybeClearHistoryOnExternalSwap();
|
|
830
951
|
})(); }); }));
|
|
@@ -858,6 +979,19 @@ private __rozieCtxProvider_data_table_columns = new ContextProvider(this, { cont
|
|
|
858
979
|
// a one-way `:data`) has a base array to whole-array-replace. currentData() then sources
|
|
859
980
|
// the bound prop when controlled, this fallback otherwise.
|
|
860
981
|
this._dataDefault.value = this.data || [];
|
|
982
|
+
// Apply any per-column `pinned` declaration as the INITIAL pinning state BEFORE createTable
|
|
983
|
+
// reads currentState() below, so the first row model table-core builds is already correctly
|
|
984
|
+
// ordered (getVisibleCells() returns [left-pinned, center, right-pinned]) rather than
|
|
985
|
+
// re-ordering a render later. Covers the `:columns` config-array form, which is available
|
|
986
|
+
// now; `<Column>` children register during mount and are covered by the re-feed watch's own
|
|
987
|
+
// call. One-shot and self-latching — see seedColumnPinning() in stateAssembly.rzts.
|
|
988
|
+
// Apply any per-column `pinned` declaration as the INITIAL pinning state BEFORE createTable
|
|
989
|
+
// reads currentState() below, so the first row model table-core builds is already correctly
|
|
990
|
+
// ordered (getVisibleCells() returns [left-pinned, center, right-pinned]) rather than
|
|
991
|
+
// re-ordering a render later. Covers the `:columns` config-array form, which is available
|
|
992
|
+
// now; `<Column>` children register during mount and are covered by the re-feed watch's own
|
|
993
|
+
// call. One-shot and self-latching — see seedColumnPinning() in stateAssembly.rzts.
|
|
994
|
+
this.seedColumnPinning();
|
|
861
995
|
// Build the table instance HERE so the closures below capture the live `table`.
|
|
862
996
|
// Build the table instance HERE so the closures below capture the live `table`.
|
|
863
997
|
this.table = createTable({
|
|
@@ -1180,6 +1314,31 @@ private __rozieCtxProvider_data_table_columns = new ContextProvider(this, { cont
|
|
|
1180
1314
|
if (!w) {
|
|
1181
1315
|
console.warn('[rozie-data-table] virtual is on for columns but the scroll container has no bounded width; set a CSS width on an ancestor so the column window can be measured');
|
|
1182
1316
|
}
|
|
1317
|
+
// COLUMN-AXIS FIRST-WINDOW BUMP (quick 260908-vcy). The mount-kick above fires right
|
|
1318
|
+
// after `new Virtualizer(...)` — BEFORE colVirtualizer has measured, and before the
|
|
1319
|
+
// spacer effects even exist. Any fine-grained subscriber created inside that measure
|
|
1320
|
+
// gap takes colPadLeft()/colPadRight()'s `if (!items.length) return 0` early return,
|
|
1321
|
+
// which reads ONLY windowVer + editVer and never reaches the live path's reads
|
|
1322
|
+
// (forcedColumns()/columnSize()/column state). Its dependency set is therefore those
|
|
1323
|
+
// two counters alone — and without this bump nothing ever touches them again, so the
|
|
1324
|
+
// subscriber stays frozen at 0 for the lifetime of the component. Measured on Svelte:
|
|
1325
|
+
// the FIRST header row's trailing spacer froze at 0px while its siblings (created
|
|
1326
|
+
// after measurement, on the live path) tracked correctly; under table-layout:fixed the
|
|
1327
|
+
// first row alone establishes column widths, so that one cell collapsed the whole
|
|
1328
|
+
// table's scrollWidth from 9000 to 1350. The five other targets re-render wholesale
|
|
1329
|
+
// (Solid re-creates these rows), which masked it.
|
|
1330
|
+
//
|
|
1331
|
+
// The ROW axis has always been safe here only by accident: remeasureWindow() above
|
|
1332
|
+
// already routes through bumpWindowVer() on this same post-first-frame callback. This
|
|
1333
|
+
// is the column axis's missing equivalent — not a Svelte workaround (the emitted
|
|
1334
|
+
// Svelte binding is a correct reactive template_effect; the dependency set is what was
|
|
1335
|
+
// wrong), so it is fixed once, in the shared engine's consumer, for all six targets.
|
|
1336
|
+
//
|
|
1337
|
+
// Routed through bumpWindowVer() rather than a raw `$data.windowVer = $data.windowVer + 1`
|
|
1338
|
+
// to keep 87-10's microtask coalescing AND to avoid manufacturing the ROZ138
|
|
1339
|
+
// false positive that a second in-body counter self-read would trip (see the
|
|
1340
|
+
// mount-kick's own comment above).
|
|
1341
|
+
this.bumpWindowVer();
|
|
1183
1342
|
}
|
|
1184
1343
|
};
|
|
1185
1344
|
if (typeof requestAnimationFrame === 'function') requestAnimationFrame(() => requestAnimationFrame(afterFirstFrame));else setTimeout(afterFirstFrame, 0);
|
|
@@ -1200,12 +1359,12 @@ private __rozieCtxProvider_data_table_columns = new ContextProvider(this, { cont
|
|
|
1200
1359
|
this.maybeClearHistoryOnExternalSwap();
|
|
1201
1360
|
if (!this.table) return;
|
|
1202
1361
|
// Phase 51 req-4: track currentData() (the bound prop OR the uncontrolled
|
|
1203
|
-
// $data.dataDefault) so a committed edit re-feeds on Lit whether or not
|
|
1204
|
-
// bound. Compare by reference AND length so a same-length single-cell edit (fresh array,
|
|
1362
|
+
// $data.dataDefault) so a committed edit re-feeds on Lit whether or not the two-way
|
|
1363
|
+
// `data` model is bound. Compare by reference AND length so a same-length single-cell edit (fresh array,
|
|
1205
1364
|
// identical length) still re-feeds.
|
|
1206
1365
|
// Phase 51 req-4: track currentData() (the bound prop OR the uncontrolled
|
|
1207
|
-
// $data.dataDefault) so a committed edit re-feeds on Lit whether or not
|
|
1208
|
-
// bound. Compare by reference AND length so a same-length single-cell edit (fresh array,
|
|
1366
|
+
// $data.dataDefault) so a committed edit re-feeds on Lit whether or not the two-way
|
|
1367
|
+
// `data` model is bound. Compare by reference AND length so a same-length single-cell edit (fresh array,
|
|
1209
1368
|
// identical length) still re-feeds.
|
|
1210
1369
|
const d = this.currentData() || [];
|
|
1211
1370
|
if (d === this.lastData && d.length === this.lastDataLen) return;
|
|
@@ -1288,19 +1447,23 @@ ${this.groupable ? html`<div class="rdt-group-bar-host" data-rozie-s-d5dcab4c>
|
|
|
1288
1447
|
<thead class="rdt-thead" role="rowgroup" data-rozie-s-d5dcab4c>
|
|
1289
1448
|
${repeat<any>(this._headerGroups.value, (hg, hgLevel) => hg.id, (hg, hgLevel) => html`<tr class="rdt-tr" role="row" aria-rowindex=${rozieAttr(hgLevel + 1)} data-rozie-s-d5dcab4c>
|
|
1290
1449
|
|
|
1291
|
-
${this.colsWindowed() ? html`<th class="rdt-col-spacer" aria-hidden="true" style=${rozieStyle('width:' + this.colPadLeft() + 'px;padding:0;border:0')} data-rozie-s-d5dcab4c></th>` : nothing}${repeat<any>(this.windowedHeadersFor(hg, hgLevel), (wh, _idx) => wh.header.id, (wh, _idx) => html`<th class="${Object.entries({ "rdt-th": true, 'rdt-select-th': this.isSelectColumn(wh.header.column.id), 'rdt-expander-th': this.isExpanderColumn(wh.header.column.id), 'rdt-th-resizing': this.columnIsResizing(wh.header.column.id), 'rdt-cell-active': this.isActiveCell('__header', this.headerColIndexOf(hg, wh.header), hgLevel) }).filter(([, v]) => v).map(([k]) => k).join(' ')}" role="columnheader" data-col=${rozieAttr(wh.header.column.id)} data-grid-cell="" data-row="__header" data-header-level=${rozieAttr(hgLevel)} colspan=${rozieAttr(wh.span > 1 ? wh.span : null)} data-col-index=${rozieAttr(this.headerColIndexOf(hg, wh.header))} tabindex=${rozieAttr(this.cellTabindex('__header', this.headerColIndexOf(hg, wh.header), hgLevel))} aria-sort=${rozieAttr(this.ariaSortFor(wh.header.column.id))} style=${rozieStyle(this.thStyle(wh.header
|
|
1450
|
+
${this.colsWindowed() ? html`<th class="rdt-col-spacer" aria-hidden="true" style=${rozieStyle('width:' + this.colPadLeft() + 'px;padding:0;border:0')} data-rozie-s-d5dcab4c></th>` : nothing}${repeat<any>(this.windowedHeadersFor(hg, hgLevel), (wh, _idx) => wh.header.id, (wh, _idx) => html`<th class="${Object.entries({ "rdt-th": true, 'rdt-select-th': this.isSelectColumn(wh.header.column.id), 'rdt-expander-th': this.isExpanderColumn(wh.header.column.id), 'rdt-th-resizing': this.columnIsResizing(wh.header.column.id), 'rdt-cell-active': this.isActiveCell('__header', this.headerColIndexOf(hg, wh.header), hgLevel) }).filter(([, v]) => v).map(([k]) => k).join(' ')}" role="columnheader" data-col=${rozieAttr(wh.header.column.id)} data-grid-cell="" data-row="__header" data-header-level=${rozieAttr(hgLevel)} colspan=${rozieAttr(wh.span > 1 ? wh.span : null)} data-col-index=${rozieAttr(this.headerColIndexOf(hg, wh.header))} tabindex=${rozieAttr(this.cellTabindex('__header', this.headerColIndexOf(hg, wh.header), hgLevel))} aria-sort=${rozieAttr(this.ariaSortFor(wh.header.column.id))} style=${rozieStyle(this.thStyle(wh.header))} data-rozie-s-d5dcab4c>
|
|
1292
1451
|
${this.isSelectColumn(wh.header.column.id) ? html`<span style="display:contents" data-rozie-s-d5dcab4c>
|
|
1293
1452
|
${this.selectAll !== undefined ? this.selectAll({checked: this.isAllRowsSelected(), indeterminate: this.isSomeRowsSelected(), toggle: this.onToggleAllRows}) : html`<slot name="selectAll" data-rozie-params=${(() => { try { return JSON.stringify({checked: this.isAllRowsSelected(), indeterminate: this.isSomeRowsSelected()}); } catch { return '{}'; } })()} @rozie-select-all-toggle=${($event: CustomEvent) => ((this.onToggleAllRows) as (...args: any[]) => any)($event.detail)}>
|
|
1294
1453
|
${this.selectionMode === 'multiple' ? html`<input class="rdt-select-all" type="checkbox" aria-label="Select all rows" ?checked=${this.isAllRowsSelected()} @change=${($event: Event & { currentTarget: HTMLInputElement; target: HTMLInputElement }) => { this.onToggleAllRows($event); }} data-rozie-s-d5dcab4c />` : nothing}</slot>`}
|
|
1295
1454
|
</span>` : this.isExpanderColumn(wh.header.column.id) ? html`<span style="display:contents" data-rozie-s-d5dcab4c></span>` : html`<span style="display:contents" data-rozie-s-d5dcab4c>
|
|
1296
1455
|
${wh.header.column.getCanSort && wh.header.column.getCanSort() ? html`<button class="rdt-sort-btn" type="button" @click=${($event: MouseEvent & { currentTarget: HTMLButtonElement; target: HTMLButtonElement }) => { this.onHeaderSort(wh.header.column.id, $event); }} data-rozie-s-d5dcab4c>
|
|
1297
1456
|
<span class="rdt-header-label" data-rozie-s-d5dcab4c>
|
|
1298
|
-
${this.colHeader !== undefined ? this.colHeader({columnId: wh.header.column.id, column: wh.header.column, label: this.headerLabel(wh.header.column.id)}) : html`<slot name="colHeader" data-rozie-params=${(() => { try { return JSON.stringify({columnId: wh.header.column.id, column: wh.header.column, label: this.headerLabel(wh.header.column.id)}); } catch { return '{}'; } })()}
|
|
1457
|
+
${this.rozieSlots?.[`colHeader-${wh.header.column.id}`] !== undefined ? this.rozieSlots?.[`colHeader-${wh.header.column.id}`]!({columnId: wh.header.column.id, column: wh.header.column, label: this.headerLabel(wh.header.column.id)}) : html`<slot name="${`colHeader-${wh.header.column.id}`}" data-rozie-params=${(() => { try { return JSON.stringify({columnId: wh.header.column.id, column: wh.header.column, label: this.headerLabel(wh.header.column.id)}); } catch { return '{}'; } })()}>
|
|
1458
|
+
${this.colHeader !== undefined ? this.colHeader({columnId: wh.header.column.id, column: wh.header.column, label: this.headerLabel(wh.header.column.id)}) : html`<slot name="colHeader" data-rozie-params=${(() => { try { return JSON.stringify({columnId: wh.header.column.id, column: wh.header.column, label: this.headerLabel(wh.header.column.id)}); } catch { return '{}'; } })()}>${rozieDisplay(this.headerLabel(wh.header.column.id))}</slot>`}
|
|
1459
|
+
</slot>`}
|
|
1299
1460
|
</span>
|
|
1300
1461
|
<span class="rdt-sort-ind" aria-hidden="true" data-rozie-s-d5dcab4c>${rozieDisplay(this.sortIndicator(wh.header.column.id))}</span>
|
|
1301
1462
|
</button>` : html`<span style="display:contents" data-rozie-s-d5dcab4c>
|
|
1302
1463
|
<span class="rdt-header-label" data-rozie-s-d5dcab4c>
|
|
1303
|
-
${this.colHeader !== undefined ? this.colHeader({columnId: wh.header.column.id, column: wh.header.column, label: this.headerLabel(wh.header.column.id)}) : html`<slot name="colHeader" data-rozie-params=${(() => { try { return JSON.stringify({columnId: wh.header.column.id, column: wh.header.column, label: this.headerLabel(wh.header.column.id)}); } catch { return '{}'; } })()}
|
|
1464
|
+
${this.rozieSlots?.[`colHeader-${wh.header.column.id}`] !== undefined ? this.rozieSlots?.[`colHeader-${wh.header.column.id}`]!({columnId: wh.header.column.id, column: wh.header.column, label: this.headerLabel(wh.header.column.id)}) : html`<slot name="${`colHeader-${wh.header.column.id}`}" data-rozie-params=${(() => { try { return JSON.stringify({columnId: wh.header.column.id, column: wh.header.column, label: this.headerLabel(wh.header.column.id)}); } catch { return '{}'; } })()}>
|
|
1465
|
+
${this.colHeader !== undefined ? this.colHeader({columnId: wh.header.column.id, column: wh.header.column, label: this.headerLabel(wh.header.column.id)}) : html`<slot name="colHeader" data-rozie-params=${(() => { try { return JSON.stringify({columnId: wh.header.column.id, column: wh.header.column, label: this.headerLabel(wh.header.column.id)}); } catch { return '{}'; } })()}>${rozieDisplay(this.headerLabel(wh.header.column.id))}</slot>`}
|
|
1466
|
+
</slot>`}
|
|
1304
1467
|
</span>
|
|
1305
1468
|
</span>`}<rozie-popover trigger="click" placement="bottom-end" strategy="fixed" .offset=${4} data-rozie-s-d5dcab4c><button class="rdt-col-menu-trigger" type="button" aria-label=${rozieAttr('Column options for ' + this.headerLabel(wh.header.column.id))} data-rozie-s-d5dcab4c slot="anchor">⋯</button><div class="rdt-col-menu" role="menu" data-rozie-s-d5dcab4c>
|
|
1306
1469
|
<button class="rdt-col-menu-item" type="button" role="menuitem" aria-pressed=${this.columnPinSide(wh.header.column.id) === 'left'} @click=${($event: MouseEvent & { currentTarget: HTMLButtonElement; target: HTMLButtonElement }) => { this.onPinColumn(wh.header.column.id, 'left', $event); }} data-rozie-s-d5dcab4c>Pin left</button>
|
|
@@ -1317,8 +1480,12 @@ ${this.groupable ? html`<div class="rdt-group-bar-host" data-rozie-s-d5dcab4c>
|
|
|
1317
1480
|
${this.hasAnyFilterableColumn() ? html`<tr class="rdt-filter-row" data-rozie-s-d5dcab4c>
|
|
1318
1481
|
${this.colsWindowed() ? html`<th class="rdt-col-spacer" aria-hidden="true" style=${rozieStyle('width:' + this.colPadLeft() + 'px;padding:0;border:0')} data-rozie-s-d5dcab4c></th>` : nothing}${repeat<any>(this.windowedHeadersFor(this._headerGroups.value[this._headerGroups.value.length - 1], this._headerGroups.value.length - 1), (wh, _idx) => wh.header.id, (wh, _idx) => html`<th class="rdt-filter-cell" role="presentation" data-col=${rozieAttr(wh.header.column.id)} style=${rozieStyle(this.pinStyle(wh.header.column.id))} data-rozie-s-d5dcab4c>
|
|
1319
1482
|
${this.isSelectColumn(wh.header.column.id) ? html`<span style="display:contents" data-rozie-s-d5dcab4c></span>` : this.isExpanderColumn(wh.header.column.id) ? html`<span style="display:contents" data-rozie-s-d5dcab4c></span>` : html`<span style="display:contents" data-rozie-s-d5dcab4c>
|
|
1320
|
-
${this.columnIsFilterable(wh.header.column.id)
|
|
1321
|
-
${this.filter !== undefined ? this.filter({columnId: wh.header.column.id, value: this.columnFilterValue(wh.header.column.id), uniqueValues: this.getFacetedUniqueValues(wh.header.column.id), minMax: this.getFacetedMinMaxValues(wh.header.column.id), setFilter: this.setColumnFilter}) : html`<slot name="filter" data-rozie-params=${(() => { try { return JSON.stringify({columnId: wh.header.column.id, value: this.columnFilterValue(wh.header.column.id), uniqueValues: this.getFacetedUniqueValues(wh.header.column.id), minMax: this.getFacetedMinMaxValues(wh.header.column.id)}); } catch { return '{}'; } })()} @rozie-
|
|
1483
|
+
${this.columnIsFilterable(wh.header.column.id) ? html`<span style="display:contents" data-rozie-s-d5dcab4c>
|
|
1484
|
+
${this.rozieSlots?.[`filter-${wh.header.column.id}`] !== undefined ? this.rozieSlots?.[`filter-${wh.header.column.id}`]!({columnId: wh.header.column.id, value: this.columnFilterValue(wh.header.column.id), uniqueValues: this.getFacetedUniqueValues(wh.header.column.id), minMax: this.getFacetedMinMaxValues(wh.header.column.id), setFilter: this.setColumnFilter}) : html`<slot name="${`filter-${wh.header.column.id}`}" data-rozie-params=${(() => { try { return JSON.stringify({columnId: wh.header.column.id, value: this.columnFilterValue(wh.header.column.id), uniqueValues: this.getFacetedUniqueValues(wh.header.column.id), minMax: this.getFacetedMinMaxValues(wh.header.column.id)}); } catch { return '{}'; } })()} @rozie-default-set-filter=${($event: CustomEvent) => ((this.setColumnFilter) as (...args: any[]) => any)($event.detail)}>
|
|
1485
|
+
${this.filter !== undefined ? this.filter({columnId: wh.header.column.id, value: this.columnFilterValue(wh.header.column.id), uniqueValues: this.getFacetedUniqueValues(wh.header.column.id), minMax: this.getFacetedMinMaxValues(wh.header.column.id), setFilter: this.setColumnFilter}) : html`<slot name="filter" data-rozie-params=${(() => { try { return JSON.stringify({columnId: wh.header.column.id, value: this.columnFilterValue(wh.header.column.id), uniqueValues: this.getFacetedUniqueValues(wh.header.column.id), minMax: this.getFacetedMinMaxValues(wh.header.column.id)}); } catch { return '{}'; } })()} @rozie-filter-set-filter=${($event: CustomEvent) => ((this.setColumnFilter) as (...args: any[]) => any)($event.detail)}>
|
|
1486
|
+
<input class="rdt-col-filter" type="text" aria-label=${rozieAttr('Filter ' + this.headerLabel(wh.header.column.id))} .value=${this.columnFilterValue(wh.header.column.id)} @input=${($event: InputEvent & { currentTarget: HTMLInputElement; target: HTMLInputElement }) => { this.onColumnFilterInput(wh.header.column.id, $event); }} @click=${($event: MouseEvent & { currentTarget: HTMLInputElement; target: HTMLInputElement }) => { this.stopEvent($event); }} data-rozie-s-d5dcab4c />
|
|
1487
|
+
</slot>`}
|
|
1488
|
+
</slot>`}
|
|
1322
1489
|
</span>` : nothing}</span>`}</th>`)}
|
|
1323
1490
|
${this.colsWindowed() ? html`<th class="rdt-col-spacer" aria-hidden="true" style=${rozieStyle('width:' + this.colPadRight() + 'px;padding:0;border:0')} data-rozie-s-d5dcab4c></th>` : nothing}</tr>` : nothing}</thead>
|
|
1324
1491
|
|
|
@@ -1341,16 +1508,24 @@ ${this.groupable ? html`<div class="rdt-group-bar-host" data-rozie-s-d5dcab4c>
|
|
|
1341
1508
|
</span>` : this.cellIsGrouped(cell) ? html`<span style="display:contents" data-rozie-s-d5dcab4c>
|
|
1342
1509
|
<button class="rdt-expander rdt-group-toggle" type="button" data-expander="" aria-expanded=${!!this.rowIsExpanded(wr.row)} aria-label=${rozieAttr(this.rowIsExpanded(wr.row) ? 'Collapse group' : 'Expand group')} @click=${($event: MouseEvent & { currentTarget: HTMLButtonElement; target: HTMLButtonElement }) => { this.onToggleExpand(wr.row, $event); }} data-rozie-s-d5dcab4c>${rozieDisplay(this.rowIsExpanded(wr.row) ? '▾' : '▸')}</button>
|
|
1343
1510
|
<span class="rdt-group-value" data-rozie-s-d5dcab4c>
|
|
1344
|
-
${this.cell !== undefined ? this.cell({columnId: cell.column.id, column: cell.column, row: this.cellSlotRow(wr.row), value: cell.getValue()}) : html`<slot name="cell" data-rozie-params=${(() => { try { return JSON.stringify({columnId: cell.column.id, column: cell.column, row: this.cellSlotRow(wr.row), value: cell.getValue()}); } catch { return '{}'; } })()}
|
|
1511
|
+
${this.rozieSlots?.[`cell-${cell.column.id}`] !== undefined ? this.rozieSlots?.[`cell-${cell.column.id}`]!({columnId: cell.column.id, column: cell.column, row: this.cellSlotRow(wr.row), value: cell.getValue()}) : html`<slot name="${`cell-${cell.column.id}`}" data-rozie-params=${(() => { try { return JSON.stringify({columnId: cell.column.id, column: cell.column, row: this.cellSlotRow(wr.row), value: cell.getValue()}); } catch { return '{}'; } })()}>
|
|
1512
|
+
${this.cell !== undefined ? this.cell({columnId: cell.column.id, column: cell.column, row: this.cellSlotRow(wr.row), value: cell.getValue()}) : html`<slot name="cell" data-rozie-params=${(() => { try { return JSON.stringify({columnId: cell.column.id, column: cell.column, row: this.cellSlotRow(wr.row), value: cell.getValue()}); } catch { return '{}'; } })()}>${rozieDisplay(cell.getValue())}</slot>`}
|
|
1513
|
+
</slot>`}
|
|
1345
1514
|
</span>
|
|
1346
1515
|
<span class="rdt-group-count" data-rozie-s-d5dcab4c>${rozieDisplay('(' + this.groupSubRowCount(wr.row) + ')')}</span>
|
|
1347
1516
|
</span>` : this.isEditing(wr.vi.index, this.colIndexOf(wr.row, cell)) ? html`<span style="display:contents" data-rozie-s-d5dcab4c>
|
|
1348
|
-
${this.
|
|
1349
|
-
${this.editor !== undefined ? this.editor({columnId: cell.column.id, column: cell.column, row: this.cellSlotRow(wr.row), value: this.editorValueFor(cell.column.id), commit: this.editorCommitFor(cell.column.id), cancel: this.editorCancelFor(), autofocus: this.editorAutofocusFor(cell.column.id, wr.vi.index)}) : html`<slot name="editor" data-rozie-params=${(() => { try { return JSON.stringify({columnId: cell.column.id, column: cell.column, row: this.cellSlotRow(wr.row), value: this.editorValueFor(cell.column.id), commit: this.editorCommitFor(cell.column.id), cancel: this.editorCancelFor(), autofocus: this.editorAutofocusFor(cell.column.id, wr.vi.index)}); } catch { return '{}'; } })()}></slot>`}
|
|
1350
|
-
</span>` : this.editorTypeOf(cell.column.id) === 'number' ? html`<input class="rdt-cell-editor" type="number" data-editing-cell="" .value=${this.editorValueFor(cell.column.id)} @input=${($event: InputEvent & { currentTarget: HTMLInputElement; target: HTMLInputElement }) => { this.onCellEditorInput(cell.column.id, $event); }} @keydown=${($event: KeyboardEvent & { currentTarget: HTMLInputElement; target: HTMLInputElement }) => { this.onEditorKeyDown($event); }} @blur=${($event: FocusEvent & { currentTarget: HTMLInputElement; target: HTMLInputElement }) => { this.onEditorBlur($event); }} data-rozie-s-d5dcab4c />` : this.editorTypeOf(cell.column.id) === 'select' ? html`<select class="rdt-cell-editor" data-editing-cell="" .value=${this.editorValueFor(cell.column.id)} @change=${($event: Event & { currentTarget: HTMLSelectElement; target: HTMLSelectElement }) => { this.onCellEditorInput(cell.column.id, $event); }} @keydown=${($event: KeyboardEvent & { currentTarget: HTMLSelectElement; target: HTMLSelectElement }) => { this.onEditorKeyDown($event); }} @blur=${($event: FocusEvent & { currentTarget: HTMLSelectElement; target: HTMLSelectElement }) => { this.onEditorBlur($event); }} data-rozie-s-d5dcab4c>
|
|
1517
|
+
${this.editorTypeOf(cell.column.id) === 'number' ? html`<input class="rdt-cell-editor" type="number" data-editing-cell="" data-builtin-editor="" .value=${this.editorValueFor(cell.column.id)} @input=${($event: InputEvent & { currentTarget: HTMLInputElement; target: HTMLInputElement }) => { this.onCellEditorInput(cell.column.id, $event); }} @keydown=${($event: KeyboardEvent & { currentTarget: HTMLInputElement; target: HTMLInputElement }) => { this.onEditorKeyDown($event); }} @blur=${($event: FocusEvent & { currentTarget: HTMLInputElement; target: HTMLInputElement }) => { this.onEditorBlur($event); }} data-rozie-s-d5dcab4c />` : this.editorTypeOf(cell.column.id) === 'select' ? html`<select class="rdt-cell-editor" data-editing-cell="" data-builtin-editor="" .value=${this.editorValueFor(cell.column.id)} @change=${($event: Event & { currentTarget: HTMLSelectElement; target: HTMLSelectElement }) => { this.onCellEditorInput(cell.column.id, $event); }} @keydown=${($event: KeyboardEvent & { currentTarget: HTMLSelectElement; target: HTMLSelectElement }) => { this.onEditorKeyDown($event); }} @blur=${($event: FocusEvent & { currentTarget: HTMLSelectElement; target: HTMLSelectElement }) => { this.onEditorBlur($event); }} data-rozie-s-d5dcab4c>
|
|
1351
1518
|
${repeat<any>(this.editorOptionsOf(cell.column.id), (opt, _idx) => opt.value, (opt, _idx) => html`<option value=${rozieAttr(opt.value)} data-rozie-s-d5dcab4c>${rozieDisplay(opt.label)}</option>`)}
|
|
1352
|
-
</select>` : this.editorTypeOf(cell.column.id) === 'checkbox' ? html`<input class="rdt-cell-editor" type="checkbox" data-editing-cell="" ?checked=${this.editorCheckedFor(cell.column.id)} @change=${($event: Event & { currentTarget: HTMLInputElement; target: HTMLInputElement }) => { this.onCellEditorCheckbox(cell.column.id, $event); }} @keydown=${($event: KeyboardEvent & { currentTarget: HTMLInputElement; target: HTMLInputElement }) => { this.onEditorKeyDown($event); }} @blur=${($event: FocusEvent & { currentTarget: HTMLInputElement; target: HTMLInputElement }) => { this.onEditorBlur($event); }} data-rozie-s-d5dcab4c />` :
|
|
1353
|
-
|
|
1519
|
+
</select>` : this.editorTypeOf(cell.column.id) === 'checkbox' ? html`<input class="rdt-cell-editor" type="checkbox" data-editing-cell="" data-builtin-editor="" ?checked=${this.editorCheckedFor(cell.column.id)} @change=${($event: Event & { currentTarget: HTMLInputElement; target: HTMLInputElement }) => { this.onCellEditorCheckbox(cell.column.id, $event); }} @keydown=${($event: KeyboardEvent & { currentTarget: HTMLInputElement; target: HTMLInputElement }) => { this.onEditorKeyDown($event); }} @blur=${($event: FocusEvent & { currentTarget: HTMLInputElement; target: HTMLInputElement }) => { this.onEditorBlur($event); }} data-rozie-s-d5dcab4c />` : this.editorTypeOf(cell.column.id) === 'custom' ? html`<span style="display:contents" data-rozie-s-d5dcab4c>
|
|
1520
|
+
${this.rozieSlots?.[`editor-${cell.column.id}`] !== undefined ? this.rozieSlots?.[`editor-${cell.column.id}`]!({columnId: cell.column.id, column: cell.column, row: this.cellSlotRow(wr.row), value: this.editorValueFor(cell.column.id), commit: this.editorCommitFor(cell.column.id), cancel: this.editorCancelFor(), autofocus: this.editorAutofocusFor(cell.column.id, wr.vi.index)}) : html`<slot name="${`editor-${cell.column.id}`}" data-rozie-params=${(() => { try { return JSON.stringify({columnId: cell.column.id, column: cell.column, row: this.cellSlotRow(wr.row), value: this.editorValueFor(cell.column.id), commit: this.editorCommitFor(cell.column.id), cancel: this.editorCancelFor(), autofocus: this.editorAutofocusFor(cell.column.id, wr.vi.index)}); } catch { return '{}'; } })()}>
|
|
1521
|
+
${this.editor !== undefined ? this.editor({columnId: cell.column.id, column: cell.column, row: this.cellSlotRow(wr.row), value: this.editorValueFor(cell.column.id), commit: this.editorCommitFor(cell.column.id), cancel: this.editorCancelFor(), autofocus: this.editorAutofocusFor(cell.column.id, wr.vi.index)}) : html`<slot name="editor" data-rozie-params=${(() => { try { return JSON.stringify({columnId: cell.column.id, column: cell.column, row: this.cellSlotRow(wr.row), value: this.editorValueFor(cell.column.id), commit: this.editorCommitFor(cell.column.id), cancel: this.editorCancelFor(), autofocus: this.editorAutofocusFor(cell.column.id, wr.vi.index)}); } catch { return '{}'; } })()}>
|
|
1522
|
+
<input class="rdt-cell-editor" type="text" data-editing-cell="" data-builtin-editor="" .value=${this.editorValueFor(cell.column.id)} @input=${($event: InputEvent & { currentTarget: HTMLInputElement; target: HTMLInputElement }) => { this.onCellEditorInput(cell.column.id, $event); }} @keydown=${($event: KeyboardEvent & { currentTarget: HTMLInputElement; target: HTMLInputElement }) => { this.onEditorKeyDown($event); }} @blur=${($event: FocusEvent & { currentTarget: HTMLInputElement; target: HTMLInputElement }) => { this.onEditorBlur($event); }} data-rozie-s-d5dcab4c />
|
|
1523
|
+
</slot>`}
|
|
1524
|
+
</slot>`}
|
|
1525
|
+
</span>` : html`<input class="rdt-cell-editor" type="text" data-editing-cell="" data-builtin-editor="" .value=${this.editorValueFor(cell.column.id)} @input=${($event: InputEvent & { currentTarget: HTMLInputElement; target: HTMLInputElement }) => { this.onCellEditorInput(cell.column.id, $event); }} @keydown=${($event: KeyboardEvent & { currentTarget: HTMLInputElement; target: HTMLInputElement }) => { this.onEditorKeyDown($event); }} @blur=${($event: FocusEvent & { currentTarget: HTMLInputElement; target: HTMLInputElement }) => { this.onEditorBlur($event); }} data-rozie-s-d5dcab4c />`}</span>` : this.cellIsPlaceholder(cell) ? html`<span style="display:contents" data-rozie-s-d5dcab4c></span>` : html`<span class="rdt-cell-value" data-rozie-s-d5dcab4c>
|
|
1526
|
+
${this.rozieSlots?.[`cell-${cell.column.id}`] !== undefined ? this.rozieSlots?.[`cell-${cell.column.id}`]!({columnId: cell.column.id, column: cell.column, row: this.cellSlotRow(wr.row), value: cell.getValue()}) : html`<slot name="${`cell-${cell.column.id}`}" data-rozie-params=${(() => { try { return JSON.stringify({columnId: cell.column.id, column: cell.column, row: this.cellSlotRow(wr.row), value: cell.getValue()}); } catch { return '{}'; } })()}>
|
|
1527
|
+
${this.cell !== undefined ? this.cell({columnId: cell.column.id, column: cell.column, row: this.cellSlotRow(wr.row), value: cell.getValue()}) : html`<slot name="cell" data-rozie-params=${(() => { try { return JSON.stringify({columnId: cell.column.id, column: cell.column, row: this.cellSlotRow(wr.row), value: cell.getValue()}); } catch { return '{}'; } })()}>${rozieDisplay(cell.getValue())}</slot>`}
|
|
1528
|
+
</slot>`}
|
|
1354
1529
|
</span>`}${this.isFillHandleCell(wr.vi.index, this.colIndexOf(wr.row, cell)) ? html`<span class="rdt-fill-handle" data-fill-handle="" data-testid="fill-handle" aria-hidden="true" @pointerdown=${($event: PointerEvent & { currentTarget: HTMLSpanElement; target: HTMLSpanElement }) => { this.onFillHandlePointerDown($event); }} data-rozie-s-d5dcab4c></span>` : nothing}</td>`)}
|
|
1355
1530
|
|
|
1356
1531
|
${this.colsWindowed() ? html`<td class="rdt-col-spacer" aria-hidden="true" style=${rozieStyle('width:' + this.colPadRight() + 'px;padding:0;border:0')} data-rozie-s-d5dcab4c></td>` : nothing}</tr>
|
|
@@ -1369,7 +1544,7 @@ ${this.groupable ? html`<div class="rdt-group-bar-host" data-rozie-s-d5dcab4c>
|
|
|
1369
1544
|
</div>` : html`<table class="${Object.entries({ "rozie-data-table": true, 'rdt-sticky': this.stickyHeader }).filter(([, v]) => v).map(([k]) => k).join(' ')}" role=${rozieAttr(this.tableRole())} aria-rowcount=${rozieAttr(this.gridAriaRowCount())} @keydown=${($event: KeyboardEvent & { currentTarget: HTMLTableElement; target: HTMLTableElement }) => { this.onGridKeyDown($event); }} @focusin=${($event: Event & { currentTarget: HTMLTableElement; target: HTMLTableElement }) => { this.syncActiveFromEvent($event); }} @focusout=${($event: Event & { currentTarget: HTMLTableElement; target: HTMLTableElement }) => { this.onGridFocusOut($event); }} @mousedown=${($event: MouseEvent & { currentTarget: HTMLTableElement; target: HTMLTableElement }) => { this.onGridMouseDown($event); }} @dblclick=${($event: Event & { currentTarget: HTMLTableElement; target: HTMLTableElement }) => { this.onGridDblClick($event); }} @click=${($event: MouseEvent & { currentTarget: HTMLTableElement; target: HTMLTableElement }) => { this.onGridClick($event); }} data-rozie-s-d5dcab4c>
|
|
1370
1545
|
<thead class="rdt-thead" role="rowgroup" data-rozie-s-d5dcab4c>
|
|
1371
1546
|
${repeat<any>(this._headerGroups.value, (hg, hgLevel) => hg.id, (hg, hgLevel) => html`<tr class="rdt-tr" role="row" aria-rowindex=${rozieAttr(hgLevel + 1)} data-rozie-s-d5dcab4c>
|
|
1372
|
-
${repeat<any>(hg.headers, (header, _idx) => header.id, (header, _idx) => html`<th class="${Object.entries({ "rdt-th": true, 'rdt-select-th': this.isSelectColumn(header.column.id), 'rdt-expander-th': this.isExpanderColumn(header.column.id), 'rdt-th-resizing': this.columnIsResizing(header.column.id), 'rdt-cell-active': this.isActiveCell('__header', this.headerColIndexOf(hg, header), hgLevel) }).filter(([, v]) => v).map(([k]) => k).join(' ')}" role="columnheader" data-col=${rozieAttr(header.column.id)} data-grid-cell="" data-row="__header" data-header-level=${rozieAttr(hgLevel)} colspan=${rozieAttr(header.colSpan > 1 ? header.colSpan : null)} data-col-index=${rozieAttr(this.headerColIndexOf(hg, header))} tabindex=${rozieAttr(this.cellTabindex('__header', this.headerColIndexOf(hg, header), hgLevel))} aria-sort=${rozieAttr(this.ariaSortFor(header.column.id))} style=${rozieStyle(this.thStyle(header
|
|
1547
|
+
${repeat<any>(hg.headers, (header, _idx) => header.id, (header, _idx) => html`<th class="${Object.entries({ "rdt-th": true, 'rdt-select-th': this.isSelectColumn(header.column.id), 'rdt-expander-th': this.isExpanderColumn(header.column.id), 'rdt-th-resizing': this.columnIsResizing(header.column.id), 'rdt-cell-active': this.isActiveCell('__header', this.headerColIndexOf(hg, header), hgLevel) }).filter(([, v]) => v).map(([k]) => k).join(' ')}" role="columnheader" data-col=${rozieAttr(header.column.id)} data-grid-cell="" data-row="__header" data-header-level=${rozieAttr(hgLevel)} colspan=${rozieAttr(header.colSpan > 1 ? header.colSpan : null)} data-col-index=${rozieAttr(this.headerColIndexOf(hg, header))} tabindex=${rozieAttr(this.cellTabindex('__header', this.headerColIndexOf(hg, header), hgLevel))} aria-sort=${rozieAttr(this.ariaSortFor(header.column.id))} style=${rozieStyle(this.thStyle(header))} data-rozie-s-d5dcab4c>
|
|
1373
1548
|
|
|
1374
1549
|
|
|
1375
1550
|
${this.isSelectColumn(header.column.id) ? html`<span style="display:contents" data-rozie-s-d5dcab4c>
|
|
@@ -1381,12 +1556,16 @@ ${this.groupable ? html`<div class="rdt-group-bar-host" data-rozie-s-d5dcab4c>
|
|
|
1381
1556
|
${header.column.getCanSort && header.column.getCanSort() ? html`<button class="rdt-sort-btn" type="button" @click=${($event: MouseEvent & { currentTarget: HTMLButtonElement; target: HTMLButtonElement }) => { this.onHeaderSort(header.column.id, $event); }} data-rozie-s-d5dcab4c>
|
|
1382
1557
|
|
|
1383
1558
|
<span class="rdt-header-label" data-rozie-s-d5dcab4c>
|
|
1384
|
-
${this.colHeader !== undefined ? this.colHeader({columnId: header.column.id, column: header.column, label: this.headerLabel(header.column.id)}) : html`<slot name="colHeader" data-rozie-params=${(() => { try { return JSON.stringify({columnId: header.column.id, column: header.column, label: this.headerLabel(header.column.id)}); } catch { return '{}'; } })()}
|
|
1559
|
+
${this.rozieSlots?.[`colHeader-${header.column.id}`] !== undefined ? this.rozieSlots?.[`colHeader-${header.column.id}`]!({columnId: header.column.id, column: header.column, label: this.headerLabel(header.column.id)}) : html`<slot name="${`colHeader-${header.column.id}`}" data-rozie-params=${(() => { try { return JSON.stringify({columnId: header.column.id, column: header.column, label: this.headerLabel(header.column.id)}); } catch { return '{}'; } })()}>
|
|
1560
|
+
${this.colHeader !== undefined ? this.colHeader({columnId: header.column.id, column: header.column, label: this.headerLabel(header.column.id)}) : html`<slot name="colHeader" data-rozie-params=${(() => { try { return JSON.stringify({columnId: header.column.id, column: header.column, label: this.headerLabel(header.column.id)}); } catch { return '{}'; } })()}>${rozieDisplay(this.headerLabel(header.column.id))}</slot>`}
|
|
1561
|
+
</slot>`}
|
|
1385
1562
|
</span>
|
|
1386
1563
|
<span class="rdt-sort-ind" aria-hidden="true" data-rozie-s-d5dcab4c>${rozieDisplay(this.sortIndicator(header.column.id))}</span>
|
|
1387
1564
|
</button>` : html`<span style="display:contents" data-rozie-s-d5dcab4c>
|
|
1388
1565
|
<span class="rdt-header-label" data-rozie-s-d5dcab4c>
|
|
1389
|
-
${this.colHeader !== undefined ? this.colHeader({columnId: header.column.id, column: header.column, label: this.headerLabel(header.column.id)}) : html`<slot name="colHeader" data-rozie-params=${(() => { try { return JSON.stringify({columnId: header.column.id, column: header.column, label: this.headerLabel(header.column.id)}); } catch { return '{}'; } })()}
|
|
1566
|
+
${this.rozieSlots?.[`colHeader-${header.column.id}`] !== undefined ? this.rozieSlots?.[`colHeader-${header.column.id}`]!({columnId: header.column.id, column: header.column, label: this.headerLabel(header.column.id)}) : html`<slot name="${`colHeader-${header.column.id}`}" data-rozie-params=${(() => { try { return JSON.stringify({columnId: header.column.id, column: header.column, label: this.headerLabel(header.column.id)}); } catch { return '{}'; } })()}>
|
|
1567
|
+
${this.colHeader !== undefined ? this.colHeader({columnId: header.column.id, column: header.column, label: this.headerLabel(header.column.id)}) : html`<slot name="colHeader" data-rozie-params=${(() => { try { return JSON.stringify({columnId: header.column.id, column: header.column, label: this.headerLabel(header.column.id)}); } catch { return '{}'; } })()}>${rozieDisplay(this.headerLabel(header.column.id))}</slot>`}
|
|
1568
|
+
</slot>`}
|
|
1390
1569
|
</span>
|
|
1391
1570
|
</span>`}<rozie-popover trigger="click" placement="bottom-end" strategy="fixed" .offset=${4} data-rozie-s-d5dcab4c><button class="rdt-col-menu-trigger" type="button" aria-label=${rozieAttr('Column options for ' + this.headerLabel(header.column.id))} data-rozie-s-d5dcab4c slot="anchor">⋯</button><div class="rdt-col-menu" role="menu" data-rozie-s-d5dcab4c>
|
|
1392
1571
|
<button class="rdt-col-menu-item" type="button" role="menuitem" aria-pressed=${this.columnPinSide(header.column.id) === 'left'} @click=${($event: MouseEvent & { currentTarget: HTMLButtonElement; target: HTMLButtonElement }) => { this.onPinColumn(header.column.id, 'left', $event); }} data-rozie-s-d5dcab4c>Pin left</button>
|
|
@@ -1403,8 +1582,12 @@ ${this.groupable ? html`<div class="rdt-group-bar-host" data-rozie-s-d5dcab4c>
|
|
|
1403
1582
|
${this.hasAnyFilterableColumn() ? html`<tr class="rdt-filter-row" data-rozie-s-d5dcab4c>
|
|
1404
1583
|
${repeat<any>(this._headerGroups.value[this._headerGroups.value.length - 1].headers, (header, _idx) => header.id, (header, _idx) => html`<th class="rdt-filter-cell" role="presentation" style=${rozieStyle(this.pinStyle(header.column.id))} data-rozie-s-d5dcab4c>
|
|
1405
1584
|
${this.isSelectColumn(header.column.id) ? html`<span style="display:contents" data-rozie-s-d5dcab4c></span>` : this.isExpanderColumn(header.column.id) ? html`<span style="display:contents" data-rozie-s-d5dcab4c></span>` : html`<span style="display:contents" data-rozie-s-d5dcab4c>
|
|
1406
|
-
${this.columnIsFilterable(header.column.id)
|
|
1407
|
-
${this.filter !== undefined ? this.filter({columnId: header.column.id, value: this.columnFilterValue(header.column.id), uniqueValues: this.getFacetedUniqueValues(header.column.id), minMax: this.getFacetedMinMaxValues(header.column.id), setFilter: this.setColumnFilter}) : html`<slot name="filter" data-rozie-params=${(() => { try { return JSON.stringify({columnId: header.column.id, value: this.columnFilterValue(header.column.id), uniqueValues: this.getFacetedUniqueValues(header.column.id), minMax: this.getFacetedMinMaxValues(header.column.id)}); } catch { return '{}'; } })()} @rozie-
|
|
1585
|
+
${this.columnIsFilterable(header.column.id) ? html`<span style="display:contents" data-rozie-s-d5dcab4c>
|
|
1586
|
+
${this.rozieSlots?.[`filter-${header.column.id}`] !== undefined ? this.rozieSlots?.[`filter-${header.column.id}`]!({columnId: header.column.id, value: this.columnFilterValue(header.column.id), uniqueValues: this.getFacetedUniqueValues(header.column.id), minMax: this.getFacetedMinMaxValues(header.column.id), setFilter: this.setColumnFilter}) : html`<slot name="${`filter-${header.column.id}`}" data-rozie-params=${(() => { try { return JSON.stringify({columnId: header.column.id, value: this.columnFilterValue(header.column.id), uniqueValues: this.getFacetedUniqueValues(header.column.id), minMax: this.getFacetedMinMaxValues(header.column.id)}); } catch { return '{}'; } })()} @rozie-default-set-filter=${($event: CustomEvent) => ((this.setColumnFilter) as (...args: any[]) => any)($event.detail)}>
|
|
1587
|
+
${this.filter !== undefined ? this.filter({columnId: header.column.id, value: this.columnFilterValue(header.column.id), uniqueValues: this.getFacetedUniqueValues(header.column.id), minMax: this.getFacetedMinMaxValues(header.column.id), setFilter: this.setColumnFilter}) : html`<slot name="filter" data-rozie-params=${(() => { try { return JSON.stringify({columnId: header.column.id, value: this.columnFilterValue(header.column.id), uniqueValues: this.getFacetedUniqueValues(header.column.id), minMax: this.getFacetedMinMaxValues(header.column.id)}); } catch { return '{}'; } })()} @rozie-filter-set-filter=${($event: CustomEvent) => ((this.setColumnFilter) as (...args: any[]) => any)($event.detail)}>
|
|
1588
|
+
<input class="rdt-col-filter" type="text" aria-label=${rozieAttr('Filter ' + this.headerLabel(header.column.id))} .value=${this.columnFilterValue(header.column.id)} @input=${($event: InputEvent & { currentTarget: HTMLInputElement; target: HTMLInputElement }) => { this.onColumnFilterInput(header.column.id, $event); }} @click=${($event: MouseEvent & { currentTarget: HTMLInputElement; target: HTMLInputElement }) => { this.stopEvent($event); }} data-rozie-s-d5dcab4c />
|
|
1589
|
+
</slot>`}
|
|
1590
|
+
</slot>`}
|
|
1408
1591
|
</span>` : nothing}</span>`}</th>`)}
|
|
1409
1592
|
</tr>` : nothing}</thead>
|
|
1410
1593
|
|
|
@@ -1422,16 +1605,24 @@ ${this.groupable ? html`<div class="rdt-group-bar-host" data-rozie-s-d5dcab4c>
|
|
|
1422
1605
|
</span>` : this.cellIsGrouped(cell) ? html`<span style="display:contents" data-rozie-s-d5dcab4c>
|
|
1423
1606
|
<button class="rdt-expander rdt-group-toggle" type="button" data-expander="" aria-expanded=${!!this.rowIsExpanded(row)} aria-label=${rozieAttr(this.rowIsExpanded(row) ? 'Collapse group' : 'Expand group')} @click=${($event: MouseEvent & { currentTarget: HTMLButtonElement; target: HTMLButtonElement }) => { this.onToggleExpand(row, $event); }} data-rozie-s-d5dcab4c>${rozieDisplay(this.rowIsExpanded(row) ? '▾' : '▸')}</button>
|
|
1424
1607
|
<span class="rdt-group-value" data-rozie-s-d5dcab4c>
|
|
1425
|
-
${this.cell !== undefined ? this.cell({columnId: cell.column.id, column: cell.column, row: this.cellSlotRow(row), value: cell.getValue()}) : html`<slot name="cell" data-rozie-params=${(() => { try { return JSON.stringify({columnId: cell.column.id, column: cell.column, row: this.cellSlotRow(row), value: cell.getValue()}); } catch { return '{}'; } })()}
|
|
1608
|
+
${this.rozieSlots?.[`cell-${cell.column.id}`] !== undefined ? this.rozieSlots?.[`cell-${cell.column.id}`]!({columnId: cell.column.id, column: cell.column, row: this.cellSlotRow(row), value: cell.getValue()}) : html`<slot name="${`cell-${cell.column.id}`}" data-rozie-params=${(() => { try { return JSON.stringify({columnId: cell.column.id, column: cell.column, row: this.cellSlotRow(row), value: cell.getValue()}); } catch { return '{}'; } })()}>
|
|
1609
|
+
${this.cell !== undefined ? this.cell({columnId: cell.column.id, column: cell.column, row: this.cellSlotRow(row), value: cell.getValue()}) : html`<slot name="cell" data-rozie-params=${(() => { try { return JSON.stringify({columnId: cell.column.id, column: cell.column, row: this.cellSlotRow(row), value: cell.getValue()}); } catch { return '{}'; } })()}>${rozieDisplay(cell.getValue())}</slot>`}
|
|
1610
|
+
</slot>`}
|
|
1426
1611
|
</span>
|
|
1427
1612
|
<span class="rdt-group-count" data-rozie-s-d5dcab4c>${rozieDisplay('(' + this.groupSubRowCount(row) + ')')}</span>
|
|
1428
1613
|
</span>` : this.isEditing(this.rowIndexOf(row), this.colIndexOf(row, cell)) ? html`<span style="display:contents" data-rozie-s-d5dcab4c>
|
|
1429
|
-
${this.
|
|
1430
|
-
${this.editor !== undefined ? this.editor({columnId: cell.column.id, column: cell.column, row: this.cellSlotRow(row), value: this.editorValueFor(cell.column.id), commit: this.editorCommitFor(cell.column.id), cancel: this.editorCancelFor(), autofocus: this.editorAutofocusFor(cell.column.id, this.rowIndexOf(row))}) : html`<slot name="editor" data-rozie-params=${(() => { try { return JSON.stringify({columnId: cell.column.id, column: cell.column, row: this.cellSlotRow(row), value: this.editorValueFor(cell.column.id), commit: this.editorCommitFor(cell.column.id), cancel: this.editorCancelFor(), autofocus: this.editorAutofocusFor(cell.column.id, this.rowIndexOf(row))}); } catch { return '{}'; } })()}></slot>`}
|
|
1431
|
-
</span>` : this.editorTypeOf(cell.column.id) === 'number' ? html`<input class="rdt-cell-editor" type="number" data-editing-cell="" .value=${this.editorValueFor(cell.column.id)} @input=${($event: InputEvent & { currentTarget: HTMLInputElement; target: HTMLInputElement }) => { this.onCellEditorInput(cell.column.id, $event); }} @keydown=${($event: KeyboardEvent & { currentTarget: HTMLInputElement; target: HTMLInputElement }) => { this.onEditorKeyDown($event); }} @blur=${($event: FocusEvent & { currentTarget: HTMLInputElement; target: HTMLInputElement }) => { this.onEditorBlur($event); }} data-rozie-s-d5dcab4c />` : this.editorTypeOf(cell.column.id) === 'select' ? html`<select class="rdt-cell-editor" data-editing-cell="" .value=${this.editorValueFor(cell.column.id)} @change=${($event: Event & { currentTarget: HTMLSelectElement; target: HTMLSelectElement }) => { this.onCellEditorInput(cell.column.id, $event); }} @keydown=${($event: KeyboardEvent & { currentTarget: HTMLSelectElement; target: HTMLSelectElement }) => { this.onEditorKeyDown($event); }} @blur=${($event: FocusEvent & { currentTarget: HTMLSelectElement; target: HTMLSelectElement }) => { this.onEditorBlur($event); }} data-rozie-s-d5dcab4c>
|
|
1614
|
+
${this.editorTypeOf(cell.column.id) === 'number' ? html`<input class="rdt-cell-editor" type="number" data-editing-cell="" data-builtin-editor="" .value=${this.editorValueFor(cell.column.id)} @input=${($event: InputEvent & { currentTarget: HTMLInputElement; target: HTMLInputElement }) => { this.onCellEditorInput(cell.column.id, $event); }} @keydown=${($event: KeyboardEvent & { currentTarget: HTMLInputElement; target: HTMLInputElement }) => { this.onEditorKeyDown($event); }} @blur=${($event: FocusEvent & { currentTarget: HTMLInputElement; target: HTMLInputElement }) => { this.onEditorBlur($event); }} data-rozie-s-d5dcab4c />` : this.editorTypeOf(cell.column.id) === 'select' ? html`<select class="rdt-cell-editor" data-editing-cell="" data-builtin-editor="" .value=${this.editorValueFor(cell.column.id)} @change=${($event: Event & { currentTarget: HTMLSelectElement; target: HTMLSelectElement }) => { this.onCellEditorInput(cell.column.id, $event); }} @keydown=${($event: KeyboardEvent & { currentTarget: HTMLSelectElement; target: HTMLSelectElement }) => { this.onEditorKeyDown($event); }} @blur=${($event: FocusEvent & { currentTarget: HTMLSelectElement; target: HTMLSelectElement }) => { this.onEditorBlur($event); }} data-rozie-s-d5dcab4c>
|
|
1432
1615
|
${repeat<any>(this.editorOptionsOf(cell.column.id), (opt, _idx) => opt.value, (opt, _idx) => html`<option value=${rozieAttr(opt.value)} data-rozie-s-d5dcab4c>${rozieDisplay(opt.label)}</option>`)}
|
|
1433
|
-
</select>` : this.editorTypeOf(cell.column.id) === 'checkbox' ? html`<input class="rdt-cell-editor" type="checkbox" data-editing-cell="" ?checked=${this.editorCheckedFor(cell.column.id)} @change=${($event: Event & { currentTarget: HTMLInputElement; target: HTMLInputElement }) => { this.onCellEditorCheckbox(cell.column.id, $event); }} @keydown=${($event: KeyboardEvent & { currentTarget: HTMLInputElement; target: HTMLInputElement }) => { this.onEditorKeyDown($event); }} @blur=${($event: FocusEvent & { currentTarget: HTMLInputElement; target: HTMLInputElement }) => { this.onEditorBlur($event); }} data-rozie-s-d5dcab4c />` :
|
|
1434
|
-
|
|
1616
|
+
</select>` : this.editorTypeOf(cell.column.id) === 'checkbox' ? html`<input class="rdt-cell-editor" type="checkbox" data-editing-cell="" data-builtin-editor="" ?checked=${this.editorCheckedFor(cell.column.id)} @change=${($event: Event & { currentTarget: HTMLInputElement; target: HTMLInputElement }) => { this.onCellEditorCheckbox(cell.column.id, $event); }} @keydown=${($event: KeyboardEvent & { currentTarget: HTMLInputElement; target: HTMLInputElement }) => { this.onEditorKeyDown($event); }} @blur=${($event: FocusEvent & { currentTarget: HTMLInputElement; target: HTMLInputElement }) => { this.onEditorBlur($event); }} data-rozie-s-d5dcab4c />` : this.editorTypeOf(cell.column.id) === 'custom' ? html`<span style="display:contents" data-rozie-s-d5dcab4c>
|
|
1617
|
+
${this.rozieSlots?.[`editor-${cell.column.id}`] !== undefined ? this.rozieSlots?.[`editor-${cell.column.id}`]!({columnId: cell.column.id, column: cell.column, row: this.cellSlotRow(row), value: this.editorValueFor(cell.column.id), commit: this.editorCommitFor(cell.column.id), cancel: this.editorCancelFor(), autofocus: this.editorAutofocusFor(cell.column.id, this.rowIndexOf(row))}) : html`<slot name="${`editor-${cell.column.id}`}" data-rozie-params=${(() => { try { return JSON.stringify({columnId: cell.column.id, column: cell.column, row: this.cellSlotRow(row), value: this.editorValueFor(cell.column.id), commit: this.editorCommitFor(cell.column.id), cancel: this.editorCancelFor(), autofocus: this.editorAutofocusFor(cell.column.id, this.rowIndexOf(row))}); } catch { return '{}'; } })()}>
|
|
1618
|
+
${this.editor !== undefined ? this.editor({columnId: cell.column.id, column: cell.column, row: this.cellSlotRow(row), value: this.editorValueFor(cell.column.id), commit: this.editorCommitFor(cell.column.id), cancel: this.editorCancelFor(), autofocus: this.editorAutofocusFor(cell.column.id, this.rowIndexOf(row))}) : html`<slot name="editor" data-rozie-params=${(() => { try { return JSON.stringify({columnId: cell.column.id, column: cell.column, row: this.cellSlotRow(row), value: this.editorValueFor(cell.column.id), commit: this.editorCommitFor(cell.column.id), cancel: this.editorCancelFor(), autofocus: this.editorAutofocusFor(cell.column.id, this.rowIndexOf(row))}); } catch { return '{}'; } })()}>
|
|
1619
|
+
<input class="rdt-cell-editor" type="text" data-editing-cell="" data-builtin-editor="" .value=${this.editorValueFor(cell.column.id)} @input=${($event: InputEvent & { currentTarget: HTMLInputElement; target: HTMLInputElement }) => { this.onCellEditorInput(cell.column.id, $event); }} @keydown=${($event: KeyboardEvent & { currentTarget: HTMLInputElement; target: HTMLInputElement }) => { this.onEditorKeyDown($event); }} @blur=${($event: FocusEvent & { currentTarget: HTMLInputElement; target: HTMLInputElement }) => { this.onEditorBlur($event); }} data-rozie-s-d5dcab4c />
|
|
1620
|
+
</slot>`}
|
|
1621
|
+
</slot>`}
|
|
1622
|
+
</span>` : html`<input class="rdt-cell-editor" type="text" data-editing-cell="" data-builtin-editor="" .value=${this.editorValueFor(cell.column.id)} @input=${($event: InputEvent & { currentTarget: HTMLInputElement; target: HTMLInputElement }) => { this.onCellEditorInput(cell.column.id, $event); }} @keydown=${($event: KeyboardEvent & { currentTarget: HTMLInputElement; target: HTMLInputElement }) => { this.onEditorKeyDown($event); }} @blur=${($event: FocusEvent & { currentTarget: HTMLInputElement; target: HTMLInputElement }) => { this.onEditorBlur($event); }} data-rozie-s-d5dcab4c />`}</span>` : this.cellIsPlaceholder(cell) ? html`<span style="display:contents" data-rozie-s-d5dcab4c></span>` : html`<span class="rdt-cell-value" data-rozie-s-d5dcab4c>
|
|
1623
|
+
${this.rozieSlots?.[`cell-${cell.column.id}`] !== undefined ? this.rozieSlots?.[`cell-${cell.column.id}`]!({columnId: cell.column.id, column: cell.column, row: this.cellSlotRow(row), value: cell.getValue()}) : html`<slot name="${`cell-${cell.column.id}`}" data-rozie-params=${(() => { try { return JSON.stringify({columnId: cell.column.id, column: cell.column, row: this.cellSlotRow(row), value: cell.getValue()}); } catch { return '{}'; } })()}>
|
|
1624
|
+
${this.cell !== undefined ? this.cell({columnId: cell.column.id, column: cell.column, row: this.cellSlotRow(row), value: cell.getValue()}) : html`<slot name="cell" data-rozie-params=${(() => { try { return JSON.stringify({columnId: cell.column.id, column: cell.column, row: this.cellSlotRow(row), value: cell.getValue()}); } catch { return '{}'; } })()}>${rozieDisplay(cell.getValue())}</slot>`}
|
|
1625
|
+
</slot>`}
|
|
1435
1626
|
</span>`}${this.isFillHandleCell(this.rowIndexOf(row), this.colIndexOf(row, cell)) ? html`<span class="rdt-fill-handle" data-fill-handle="" data-testid="fill-handle" aria-hidden="true" @pointerdown=${($event: PointerEvent & { currentTarget: HTMLSpanElement; target: HTMLSpanElement }) => { this.onFillHandlePointerDown($event); }} data-rozie-s-d5dcab4c></span>` : nothing}</td>`)}
|
|
1436
1627
|
</tr>
|
|
1437
1628
|
|
|
@@ -1614,6 +1805,96 @@ ${this.groupable ? html`<div class="rdt-group-bar-host" data-rozie-s-d5dcab4c>
|
|
|
1614
1805
|
};
|
|
1615
1806
|
};
|
|
1616
1807
|
|
|
1808
|
+
// seedColumnPinning(): apply the per-column `pinned` declaration ('left' | 'right') — from the
|
|
1809
|
+
// `:columns` config array or a `<Column pinned="...">` child — as the INITIAL columnPinning
|
|
1810
|
+
// state, exactly once.
|
|
1811
|
+
//
|
|
1812
|
+
// Until now that field was carried onto every ColumnDef by columnBuilders.rzts and then read by
|
|
1813
|
+
// nobody: `column.getIsPinned()` returned false for a column declared `pinned="left"`, so the
|
|
1814
|
+
// documented per-column API was inert while `r-model:columnPinning` was the only mechanism that
|
|
1815
|
+
// actually pinned anything.
|
|
1816
|
+
//
|
|
1817
|
+
// Three invariants, each load-bearing:
|
|
1818
|
+
// 1. ONE-SHOT, LATCHED ON THE OUTCOME — never merely on "we ran". The latch flips only when
|
|
1819
|
+
// a seed is actually applied, or when a consumer pin is observed; a call that finds no
|
|
1820
|
+
// declared pins leaves it DOWN and will look again on the next re-feed. Latching on the
|
|
1821
|
+
// first call that saw any column at all was wrong, and Lit proved it: `<Column>` children
|
|
1822
|
+
// register INCREMENTALLY there, one re-feed per child, so the first call sees only
|
|
1823
|
+
// `name` — no pins declared yet — and a run-once latch would disable the seed
|
|
1824
|
+
// permanently before `amount pinned="right"` ever registered. The other five targets
|
|
1825
|
+
// batch all children into a single flush, which hid the race entirely. Once a seed HAS
|
|
1826
|
+
// been applied the latch holds, so a subsequent interactive UNPIN is never re-seeded —
|
|
1827
|
+
// which is the whole reason this is not folded into effectiveColumnPinning().
|
|
1828
|
+
// 2. ONLY WHEN NOTHING IS PINNED YET. Note this is NOT an `$props.columnPinning == null`
|
|
1829
|
+
// check: the prop carries `default: () => ({ left: [], right: [] })`, so it is never null
|
|
1830
|
+
// and a bound-vs-unbound test is impossible here. The meaningful question is whether the
|
|
1831
|
+
// consumer has actually expressed a pin — if they have, they own the slice and the
|
|
1832
|
+
// per-column declaration must not fight it; if the state is empty, `pinned` IS the
|
|
1833
|
+
// initial state and applying it is the whole point.
|
|
1834
|
+
// 3. NO CHROME IDS. The select/expander rail is injected by effectiveColumnPinning() on read
|
|
1835
|
+
// and stripped by writeColumnPinning() on write; the persisted state must never contain
|
|
1836
|
+
// them. Seeding only real column ids keeps that discipline.
|
|
1837
|
+
//
|
|
1838
|
+
// Writes BOTH the uncontrolled holder and `$model.columnPinning` so a consumer with a two-way
|
|
1839
|
+
// binding sees the pinning that is actually in effect rather than a stale empty object — but
|
|
1840
|
+
// does NOT emit `pin-change`: this is initial state derived from the consumer's own markup, not
|
|
1841
|
+
// a user action, and firing a change event for it on mount would be noise.
|
|
1842
|
+
//
|
|
1843
|
+
// Called from $onMount (covers the `:columns` config-array form, known at mount) AND from the
|
|
1844
|
+
// re-feed watch (covers `<Column>` children, which register during mount, and a runtime
|
|
1845
|
+
// `:columns` swap that arrives before any pin interaction). The `!defs.length` bail is what
|
|
1846
|
+
// makes the mount call harmless when columns are not known yet — the latch stays down and the
|
|
1847
|
+
// next re-feed seeds instead.
|
|
1848
|
+
pinSeedApplied = false;
|
|
1849
|
+
|
|
1850
|
+
seedColumnPinning = (): void => {
|
|
1851
|
+
if (this.pinSeedApplied) return;
|
|
1852
|
+
// Invariant 2: bail if the consumer already pinned something (either an initial bound value
|
|
1853
|
+
// or an interaction that beat us here). Read the same source order effectiveColumnPinning()
|
|
1854
|
+
// does, minus the chrome rail it injects on read — the rail is structural, not a consumer pin.
|
|
1855
|
+
const live = this.columnPinning != null ? this.columnPinning : this._columnPinningDefault.value;
|
|
1856
|
+
const isRealPin = (id: string) => id !== this.SELECT_COL_ID && id !== this.EXPANDER_COL_ID;
|
|
1857
|
+
const pinnedAlready = (live && live.left ? live.left : []).filter(isRealPin).length > 0 || (live && live.right ? live.right : []).filter(isRealPin).length > 0;
|
|
1858
|
+
if (pinnedAlready) {
|
|
1859
|
+
this.pinSeedApplied = true;
|
|
1860
|
+
return;
|
|
1861
|
+
}
|
|
1862
|
+
const defs = this.columnDefs();
|
|
1863
|
+
if (!defs.length) return;
|
|
1864
|
+
const left: string[] = [];
|
|
1865
|
+
const right: string[] = [];
|
|
1866
|
+
for (let i = 0; i < defs.length; i++) {
|
|
1867
|
+
const def = defs[i];
|
|
1868
|
+
if (!def) continue;
|
|
1869
|
+
if (def.pinned === 'left') left.push(def.id);else if (def.pinned === 'right') right.push(def.id);
|
|
1870
|
+
}
|
|
1871
|
+
// Invariant 1: no declared pins in the column set we can currently SEE is not evidence there
|
|
1872
|
+
// are none — on Lit the set is still filling in. Leave the latch down and try again on the
|
|
1873
|
+
// next re-feed; the only exits are "seeded" (below) and "consumer already pinned" (above).
|
|
1874
|
+
if (!left.length && !right.length) return;
|
|
1875
|
+
this.pinSeedApplied = true;
|
|
1876
|
+
// Nothing was pinned (invariant 2 above), so this is a plain assignment, not a merge — the
|
|
1877
|
+
// only ids that could be in `live` are the structural chrome rail, which must never be
|
|
1878
|
+
// persisted (invariant 3). Writes the uncontrolled holder AND the two-way model so both
|
|
1879
|
+
// binding modes observe the pinning that is actually in effect; deliberately does NOT route
|
|
1880
|
+
// through writeColumnPinning(), whose job is to emit `pin-change` for real user actions.
|
|
1881
|
+
// Annotated `any` for the same reason currentState() is (see its comment below): the
|
|
1882
|
+
// uncontrolled holder's initial value is the empty literal `{ left: [], right: [] }`, so the
|
|
1883
|
+
// emitted per-target state holder infers `{ left: never[]; right: never[] }` and a genuinely
|
|
1884
|
+
// populated `{ left: string[]; right: string[] }` will not assign to it under the strict
|
|
1885
|
+
// bundled-leaf tsc (react TS2345 on setColumnPinningDefault, solid TS2769, lit TS2322 — one
|
|
1886
|
+
// per target, all the same never[] inference). The runtime shape is correct; this is the
|
|
1887
|
+
// established idiom here rather than widening three per-leaf strict baselines for it.
|
|
1888
|
+
// writeColumnPinning() never hits this because its payload flows through `strip()`, which
|
|
1889
|
+
// returns `any[]`.
|
|
1890
|
+
const seeded: any = {
|
|
1891
|
+
left,
|
|
1892
|
+
right
|
|
1893
|
+
};
|
|
1894
|
+
this._columnPinningDefault.value = seeded;
|
|
1895
|
+
this._columnPinningControllable.write(seeded);
|
|
1896
|
+
};
|
|
1897
|
+
|
|
1617
1898
|
// Assemble the live state object from bound r-model slices (?? uncontrolled fallback).
|
|
1618
1899
|
// All NINE slices are wired (each ?? its own $data.<slice>Default). table-core reads
|
|
1619
1900
|
// this whole object as `state`. Return type annotated `any`: the inferred object-literal
|
|
@@ -2348,9 +2629,43 @@ ${this.groupable ? html`<div class="rdt-group-bar-host" data-rozie-s-d5dcab4c>
|
|
|
2348
2629
|
this.writeColumnPinning(applyUpdater(updater, this.currentState().columnPinning));
|
|
2349
2630
|
};
|
|
2350
2631
|
|
|
2632
|
+
// columnSizingInfo is the ONLY slice table-core drives as a rapid, multi-call gesture: a resize
|
|
2633
|
+
// drag fires this updater once on pointerdown (seeding startOffset/startSize/isResizingColumn)
|
|
2634
|
+
// and then again on EVERY mousemove, each call reading the values the previous one wrote.
|
|
2635
|
+
//
|
|
2636
|
+
// That read-your-own-last-write requirement is what makes a plain `$data` read wrong here on
|
|
2637
|
+
// React. These callbacks are handed to createTable in $onMount, so on React the closure binds a
|
|
2638
|
+
// MOUNT-TIME `$data.columnSizingInfo` and never sees its own writes. Measured on the shipped
|
|
2639
|
+
// leaf (quick 260909, instrumented build): pointerdown correctly wrote
|
|
2640
|
+
// `{ startOffset: 163, isResizingColumn: 'col0' }`, and then every single mousemove still saw
|
|
2641
|
+
// `startOffset: null, isResizingColumn: false` — so it computed deltaOffset from 0 instead of
|
|
2642
|
+
// 163 (179, 195, 211… instead of 16, 32, 48…) AND wrote `isResizingColumn: false` back, wiping
|
|
2643
|
+
// the gesture on the first move. Net effect: column resizing did nothing at all on React while
|
|
2644
|
+
// working on the other five. It was masked because the one VR case that drives a real drag was
|
|
2645
|
+
// `test.fixme`'d on React as a presumed harness limitation (87-05 Task 3) — a real CDP drag
|
|
2646
|
+
// with intermediate steps fails there too, which is what proved it a product bug.
|
|
2647
|
+
//
|
|
2648
|
+
// Why not just read `currentState().columnSizingInfo` like the eleven siblings above: that
|
|
2649
|
+
// idiom relies on the re-feed $watch re-passing fresh callbacks into setOptions, and that watch
|
|
2650
|
+
// fires on data/column changes — NOT on every render, and never mid-gesture. It is sufficient
|
|
2651
|
+
// for one-shot updaters (a sort toggle, a selection click) and insufficient here, where
|
|
2652
|
+
// table-core issues a burst of updaters between any two re-feeds.
|
|
2653
|
+
//
|
|
2654
|
+
// Fix: a synchronous module-scope mirror, written on the same tick as the state. Reads prefer
|
|
2655
|
+
// the mirror, so each updater in a gesture composes on the previous one's result regardless of
|
|
2656
|
+
// when the host framework commits. Behaviourally identical on the five targets whose state read
|
|
2657
|
+
// was already synchronous (mirror and state agree), and the slice is transient gesture state
|
|
2658
|
+
// owned entirely by table-core — nothing else writes it — so the mirror cannot drift.
|
|
2659
|
+
// Same trick as the `committedThisSession` commit latch (editCellLifecycle.rzts), for the same
|
|
2660
|
+
// reason: a sync latch survives React's async window.
|
|
2661
|
+
columnSizingInfoSync: any = null;
|
|
2662
|
+
|
|
2351
2663
|
onColumnSizingInfoChangeCb = (updater: any) => {
|
|
2352
|
-
const
|
|
2353
|
-
|
|
2664
|
+
const base = this.columnSizingInfoSync != null ? this.columnSizingInfoSync : this._columnSizingInfo.value;
|
|
2665
|
+
const next = applyUpdater(updater, base);
|
|
2666
|
+
if (next == null) return;
|
|
2667
|
+
this.columnSizingInfoSync = next;
|
|
2668
|
+
this._columnSizingInfo.value = next;
|
|
2354
2669
|
};
|
|
2355
2670
|
|
|
2356
2671
|
// ══ Vertical row windowing (phase 53, req-1/2/3/6/9/10) — the virtual-core bridge ════════
|
|
@@ -3632,17 +3947,6 @@ ${this.groupable ? html`<div class="rdt-group-bar-host" data-rozie-s-d5dcab4c>
|
|
|
3632
3947
|
return m && m.editorOptions != null ? m.editorOptions : [];
|
|
3633
3948
|
};
|
|
3634
3949
|
|
|
3635
|
-
// hasEditorSlot: this column routes through the consumer's #editor scoped slot (req-2)
|
|
3636
|
-
// — true only when the column declared editor='custom' AND the consumer actually
|
|
3637
|
-
// provided an #editor slot. Falls through to the built-in editor otherwise (e.g. a
|
|
3638
|
-
// column marked 'custom' with no slot supplied degrades to the text editor, never blank).
|
|
3639
|
-
hasEditorSlot = (colId: any) => this.editorTypeOf(colId) === 'custom' && !!(this._hasSlotEditor || this.editor !== undefined);
|
|
3640
|
-
|
|
3641
|
-
// hasFilterSlot: the consumer supplied a #filter scoped slot, so it OWNS the per-column
|
|
3642
|
-
// filter UI (re-added in 72-05 alongside the dedicated filter row's `<slot name="filter">`
|
|
3643
|
-
// host — see the 72-03 removal note in that plan's SUMMARY for why this was briefly gone).
|
|
3644
|
-
hasFilterSlot = () => !!(this._hasSlotFilter || this.filter !== undefined);
|
|
3645
|
-
|
|
3646
3950
|
columnIsFilterable = (colId: any) => {
|
|
3647
3951
|
const d = this.defFor(colId);
|
|
3648
3952
|
return !!(d && d.filterable);
|
|
@@ -3654,13 +3958,27 @@ ${this.groupable ? html`<div class="rdt-group-bar-host" data-rozie-s-d5dcab4c>
|
|
|
3654
3958
|
};
|
|
3655
3959
|
|
|
3656
3960
|
// ── Column-management chrome (req-8/9/10/11) ────────────────────────────────────────
|
|
3657
|
-
// Live header width (px) for a
|
|
3658
|
-
//
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3961
|
+
// Live header width (px) for a <th> — drives the :style width binding. Takes the table-core
|
|
3962
|
+
// HEADER object (not a column id / Column lookup) and reads header.getSize() — deliberately,
|
|
3963
|
+
// NOT column.getSize(). table-core's own ColumnSizing feature (createHeader, always present —
|
|
3964
|
+
// it is in table-core's builtInFeatures, never opt-in) gives Header.getSize() a recursive
|
|
3965
|
+
// implementation: a LEAF header returns its own column's getSize() (identical to the old
|
|
3966
|
+
// column.getSize() path — byte-identical output there), but a GROUP header (one with
|
|
3967
|
+
// subHeaders, e.g. a multi-level `columns:` entry from buildConfigDef) SUMS every descendant
|
|
3968
|
+
// leaf column's getSize() instead of returning the group's own meaningless single-column
|
|
3969
|
+
// default (150px, table-core's defaultColumnSizing.size — group ColumnDefs never set `size`).
|
|
3970
|
+
// Before this fix, a colspan-N grouped <th> asked the browser for ONE column's worth of width
|
|
3971
|
+
// (150px) while spanning N columns; under `table-layout:fixed` the browser then divides that
|
|
3972
|
+
// single declared width ACROSS all N spanned <th>s (150/5 = 30px for this codebase's 5-column
|
|
3973
|
+
// "Group A" fixture) — a 5x collapse of the actual rendered column width, independent of (and
|
|
3974
|
+
// invisible to) columnSize()/the column virtualizer's windowing math, which reads
|
|
3975
|
+
// table.getVisibleLeafColumns()[i].getSize() directly and was never affected. Root cause +
|
|
3976
|
+
// fix confirmed via .planning/debug/datatable-d13-column-axis-all-targets.md and previously
|
|
3977
|
+
// documented as "Gap 1" in .planning/phases/87-.../deferred-items.md (the identical grouped-
|
|
3978
|
+
// header/colspan mismatch, found independently during 87-05/87-06 authoring and deferred).
|
|
3979
|
+
headerWidth = (header: any) => {
|
|
3980
|
+
if (this.tick() < 0 || !header || typeof header.getSize !== 'function') return null;
|
|
3981
|
+
const w = header.getSize();
|
|
3664
3982
|
return w != null && w > 0 ? w + 'px' : null;
|
|
3665
3983
|
};
|
|
3666
3984
|
|
|
@@ -3784,15 +4102,18 @@ ${this.groupable ? html`<div class="rdt-group-bar-host" data-rozie-s-d5dcab4c>
|
|
|
3784
4102
|
return '';
|
|
3785
4103
|
};
|
|
3786
4104
|
|
|
3787
|
-
// Combined inline style for a <th> (width + pin)
|
|
3788
|
-
//
|
|
3789
|
-
//
|
|
3790
|
-
//
|
|
3791
|
-
|
|
4105
|
+
// Combined inline style for a <th> (width + pin). Plain string concat — uniform on all 6, no
|
|
4106
|
+
// bound-object trap. zIndex=2 (see pinStyle) so a pinned header cell — which hosts the ⋯ menu's
|
|
4107
|
+
// floating content — always stacks above the pinned filter-row cell for the same column
|
|
4108
|
+
// (zIndex=1, its own default). Takes the HEADER object (not a bare colId) so headerWidth() can
|
|
4109
|
+
// read header.getSize() — the group-aware size (see headerWidth's own comment). pinStyle still
|
|
4110
|
+
// keys off the plain column id (pinning is a leaf-column-only concern; a grouped header never
|
|
4111
|
+
// pins as a unit in this codebase's model).
|
|
4112
|
+
thStyle = (header: any) => {
|
|
3792
4113
|
let s = '';
|
|
3793
|
-
const w = this.headerWidth(
|
|
4114
|
+
const w = this.headerWidth(header);
|
|
3794
4115
|
if (w) s += 'width:' + w + ';';
|
|
3795
|
-
s += this.pinStyle(
|
|
4116
|
+
s += this.pinStyle(header && header.column ? header.column.id : null, 2);
|
|
3796
4117
|
return s;
|
|
3797
4118
|
};
|
|
3798
4119
|
|
|
@@ -6268,8 +6589,26 @@ ${this.groupable ? html`<div class="rdt-group-bar-host" data-rozie-s-d5dcab4c>
|
|
|
6268
6589
|
}
|
|
6269
6590
|
};
|
|
6270
6591
|
|
|
6271
|
-
|
|
6272
|
-
|
|
6592
|
+
// FILL_HITTEST_PROBE_RADIUS_PX / FILL_HITTEST_PROBE_STEP_PX (hardening, authorized alongside the
|
|
6593
|
+
// D-13 grouped-header width fix — .planning/debug/datatable-d13-column-axis-all-targets.md):
|
|
6594
|
+
// resolveCellAt() below hit-tests document.elementFromPoint at ONE exact pixel — structurally
|
|
6595
|
+
// fragile against a scrolling DOM by construction: any narrow column transiting past that fixed
|
|
6596
|
+
// screen point (the edge-auto-scroll rAF loop in fillDrag re-probes the SAME fixed point every
|
|
6597
|
+
// frame while gridScrollEl's content slides underneath it) has only a single-frame-scale window
|
|
6598
|
+
// to be caught, and a probe pixel landing exactly on a border/rounding edge between two adjacent
|
|
6599
|
+
// `<td>`/`<th>`s can miss both. cellIndexFromPoint widens the miss case ONLY: it tries the exact
|
|
6600
|
+
// point first (byte-identical to the pre-hardening behavior on a hit — this is not a behavior
|
|
6601
|
+
// change to the common case) and, only if that misses, fans out a small nearest-first horizontal
|
|
6602
|
+
// search before giving up. Vertical-only misses are not probed (rows are >=24px tall by design —
|
|
6603
|
+
// FILL_EDGE_SCROLL_PX itself — so a row is never narrower than a single scroll step; only COLUMN
|
|
6604
|
+
// width has no such floor, hence a horizontal-only widening).
|
|
6605
|
+
FILL_HITTEST_PROBE_RADIUS_PX = 8;
|
|
6606
|
+
|
|
6607
|
+
FILL_HITTEST_PROBE_STEP_PX = 2;
|
|
6608
|
+
|
|
6609
|
+
// resolveCellAt: the exact-point hit-test (identical logic to the pre-hardening
|
|
6610
|
+
// cellIndexFromPoint body) extracted so it can be re-tried at nearby x-offsets below.
|
|
6611
|
+
resolveCellAt = (clientX: any, clientY: any) => {
|
|
6273
6612
|
let el = document.elementFromPoint(clientX, clientY);
|
|
6274
6613
|
// Pierce OPEN shadow roots (Lit): document.elementFromPoint retargets to the shadow HOST, so
|
|
6275
6614
|
// a drag over the Lit data-table's shadow content would otherwise resolve the host (no cell)
|
|
@@ -6295,6 +6634,23 @@ ${this.groupable ? html`<div class="rdt-group-bar-host" data-rozie-s-d5dcab4c>
|
|
|
6295
6634
|
};
|
|
6296
6635
|
};
|
|
6297
6636
|
|
|
6637
|
+
cellIndexFromPoint = (clientX: any, clientY: any) => {
|
|
6638
|
+
if (typeof document === 'undefined' || !document.elementFromPoint) return null;
|
|
6639
|
+
const direct = this.resolveCellAt(clientX, clientY);
|
|
6640
|
+
if (direct) return direct;
|
|
6641
|
+
// Widened nearest-cell search: only reached when the exact point misses. Fans outward
|
|
6642
|
+
// (nearest-first, alternating +/-x) up to FILL_HITTEST_PROBE_RADIUS_PX before giving up —
|
|
6643
|
+
// catches a narrow column that transited past the fixed probe point on this exact frame
|
|
6644
|
+
// without changing which cell wins on an ordinary (non-edge-case) hit.
|
|
6645
|
+
for (let d = this.FILL_HITTEST_PROBE_STEP_PX; d <= this.FILL_HITTEST_PROBE_RADIUS_PX; d += this.FILL_HITTEST_PROBE_STEP_PX) {
|
|
6646
|
+
const right = this.resolveCellAt(clientX + d, clientY);
|
|
6647
|
+
if (right) return right;
|
|
6648
|
+
const left = this.resolveCellAt(clientX - d, clientY);
|
|
6649
|
+
if (left) return left;
|
|
6650
|
+
}
|
|
6651
|
+
return null;
|
|
6652
|
+
};
|
|
6653
|
+
|
|
6298
6654
|
onFillHandlePointerDown = (e: any) => {
|
|
6299
6655
|
if (!e) return;
|
|
6300
6656
|
if (e.preventDefault) e.preventDefault();
|
|
@@ -6624,39 +6980,73 @@ ${this.groupable ? html`<div class="rdt-group-bar-host" data-rozie-s-d5dcab4c>
|
|
|
6624
6980
|
return row ? row.id : null;
|
|
6625
6981
|
};
|
|
6626
6982
|
|
|
6983
|
+
// resolveEditFocusCellEl(rowIndex, colIndex): the DOM cell element for an edit-focus
|
|
6984
|
+
// target, given an EXPLICIT index pair — NEVER a $data re-read inside this function or its
|
|
6985
|
+
// caller's poll closure. Every call site already knows this pair SYNCHRONOUSLY at the
|
|
6986
|
+
// moment it calls (beginEdit's own rowIndex/colIndex params; commitEdit's stable, already-
|
|
6987
|
+
// settled $data.editingRow/editingCol; beginRowEdit's rowIndex + the target column's
|
|
6988
|
+
// editable[].colIndex from editableColumnsForRow) — threading them through as PARAMETERS,
|
|
6989
|
+
// mirroring focusRowEditorAt's already-proven resolveCellEl(String(rowIndex), colIndex)
|
|
6990
|
+
// shape, closes the React batched-setState staleness class 88-05-SUMMARY.md documented
|
|
6991
|
+
// (REACT_STALE_GATE_SINGLE_CELL_BUG): the OLD gate read $data.editFocusColId SYNCHRONOUSLY,
|
|
6992
|
+
// in the SAME tick beginEdit's own setState call set it, and React's batching meant that
|
|
6993
|
+
// read saw the PRE-edit (null) value. A plain function argument threaded through — never a
|
|
6994
|
+
// reactive re-read — is not subject to that race, uniformly on all 6 targets.
|
|
6995
|
+
resolveEditFocusCellEl = (rowIndex: any, colIndex: any) => {
|
|
6996
|
+
if (rowIndex == null || colIndex == null || rowIndex < 0 || colIndex < 0) return null;
|
|
6997
|
+
return this.resolveCellEl(String(rowIndex), colIndex);
|
|
6998
|
+
};
|
|
6999
|
+
|
|
6627
7000
|
// Focus the freshly-mounted editor (Pitfall 1, ROZ123): after beginEdit flips the editing
|
|
6628
7001
|
// state, the editor <input> does not exist until the framework commits the r-if branch
|
|
6629
|
-
// (React setState async; Solid/Lit/Svelte next reactive tick). Poll for the
|
|
6630
|
-
//
|
|
6631
|
-
//
|
|
7002
|
+
// (React setState async; Solid/Lit/Svelte next reactive tick). Poll for the target CELL's
|
|
7003
|
+
// marked built-in editor for ~30 frames — the five fast targets resolve on attempt 1, React
|
|
7004
|
+
// retries across its async commit. NEVER read $refs eagerly.
|
|
6632
7005
|
// B2: selectAll gates the post-focus el.select(). Select-all is right when entering
|
|
6633
7006
|
// edit IN PLACE (F2/Enter/click/row-edit/validation-reject — no seeded char, the user
|
|
6634
7007
|
// retypes), but WRONG on a type-to-edit entry where a printable key already seeded the
|
|
6635
7008
|
// draft (selecting the seeded char makes the next keystroke replace it: Zeta → eta).
|
|
6636
7009
|
// beginEdit threads `seed == null` so a seeded entry skips the select and the caret sits
|
|
6637
7010
|
// AFTER the seeded char; every other caller keeps the default select-all.
|
|
6638
|
-
//
|
|
6639
|
-
//
|
|
6640
|
-
//
|
|
6641
|
-
//
|
|
6642
|
-
//
|
|
6643
|
-
//
|
|
6644
|
-
//
|
|
6645
|
-
|
|
7011
|
+
// D-01/D-02 (88-07): REPLACES the deleted presence-check early-return gate AND the
|
|
7012
|
+
// gridRoot-wide `[data-editing-cell]` first-match query with a column-scoped, marker-driven
|
|
7013
|
+
// poll. Resolves the TARGET cell first (resolveEditFocusCellEl, mirroring
|
|
7014
|
+
// focusRowEditorAt's already-shipped two-step resolve), then queries for an editor carrying
|
|
7015
|
+
// BOTH `data-editing-cell` and the host-owned `data-builtin-editor` marker WITHIN that one
|
|
7016
|
+
// cell — "no marker found" (a drop-in target, D-01) bails immediately rather than reaching
|
|
7017
|
+
// into DOM the host does not own; "no editor at all yet" keeps polling (the framework hasn't
|
|
7018
|
+
// committed the r-if branch). This closes the D-02 wrong-column hole the old grid-wide query
|
|
7019
|
+
// left open (every editable cell mounts an editor at once in row-edit mode) AND fixes the
|
|
7020
|
+
// React staleness race documented above, as a side effect of no longer reading
|
|
7021
|
+
// $data.editFocusColId anywhere in this function.
|
|
7022
|
+
focusEditorWhenReady = (rowIndex: any, colIndex: any, selectAll = true) => {
|
|
6646
7023
|
if (!this.gridRoot) return;
|
|
6647
|
-
// Editor-owns-focus contract: when the CURRENT focus target is a #editor drop-in, the host
|
|
6648
|
-
// does NOT reach into its DOM — the drop-in self-focuses via its own autofocus prop.
|
|
6649
|
-
if (this._editFocusColId.value != null && this.hasEditorSlot(this._editFocusColId.value)) return;
|
|
6650
7024
|
let attempts = 0;
|
|
6651
7025
|
const tryFocus = () => {
|
|
6652
|
-
const
|
|
7026
|
+
const cellEl = this.resolveEditFocusCellEl(rowIndex, colIndex);
|
|
7027
|
+
const el = cellEl && cellEl.querySelector ? cellEl.querySelector('[data-editing-cell][data-builtin-editor]') : null;
|
|
7028
|
+
if (!el) {
|
|
7029
|
+
// Editor-owns-focus contract (quick 260711-i5m, D-01): if the cell ALREADY holds an
|
|
7030
|
+
// editor that carries only the base marker, it's a consumer drop-in — it owns its own
|
|
7031
|
+
// focus (EditorText's own $onMount + reactive `autofocus`). Bail immediately, zero
|
|
7032
|
+
// wasted frames, rather than burning the retry ceiling on DOM the host does not own.
|
|
7033
|
+
const anyEditor = cellEl && cellEl.querySelector ? cellEl.querySelector('[data-editing-cell]') : null;
|
|
7034
|
+
if (anyEditor) return;
|
|
7035
|
+
attempts = attempts + 1;
|
|
7036
|
+
if (attempts >= 30) return;
|
|
7037
|
+
if (typeof requestAnimationFrame === 'function') requestAnimationFrame(tryFocus);else setTimeout(tryFocus, 16);
|
|
7038
|
+
return;
|
|
7039
|
+
}
|
|
6653
7040
|
// Do NOT stomp focus a later interaction already placed in a DIFFERENT column's editor of
|
|
6654
7041
|
// this row: focusEditorWhenReady only needs to get focus INTO the (first) freshly-mounted
|
|
6655
7042
|
// editor; if focus already sits in another editable cell, a late rAF re-focus would steal it
|
|
6656
7043
|
// back to the first editor and break row-mode Tab containment (the non-deterministic B21
|
|
6657
7044
|
// focus-theft). Compare the OWNING cell's data-col-index (NOT node identity) so a stale
|
|
6658
7045
|
// SAME-column editor node on Solid's node-replacing re-render still resolves as the target —
|
|
6659
|
-
// a genuinely dropped focus is still recovered.
|
|
7046
|
+
// a genuinely dropped focus is still recovered. Stays BROAD on `data-editing-cell` (NOT
|
|
7047
|
+
// narrowed to the builtin marker, D-02): focus validly placed inside a drop-in must still
|
|
7048
|
+
// count as "already placed" — narrowing this check would let a late rAF poll steal focus
|
|
7049
|
+
// back from a drop-in that legitimately holds it.
|
|
6660
7050
|
const ae = this.gridRoot && this.gridRoot.getRootNode ? this.gridRoot.getRootNode().activeElement : null;
|
|
6661
7051
|
if (ae && el && ae !== el && ae.closest && this.gridRoot.contains(ae) && ae.hasAttribute && ae.hasAttribute('data-editing-cell')) {
|
|
6662
7052
|
const aeCell = ae.closest('[data-grid-cell]');
|
|
@@ -6665,18 +7055,12 @@ ${this.groupable ? html`<div class="rdt-group-bar-host" data-rozie-s-d5dcab4c>
|
|
|
6665
7055
|
const elCol = elCell ? elCell.getAttribute('data-col-index') : null;
|
|
6666
7056
|
if (aeCol != null && aeCol !== elCol) return;
|
|
6667
7057
|
}
|
|
6668
|
-
|
|
6669
|
-
|
|
6670
|
-
|
|
6671
|
-
|
|
6672
|
-
|
|
6673
|
-
} catch (e: any) {}
|
|
6674
|
-
}
|
|
6675
|
-
return;
|
|
7058
|
+
el.focus();
|
|
7059
|
+
if (selectAll && el.select) {
|
|
7060
|
+
try {
|
|
7061
|
+
el.select();
|
|
7062
|
+
} catch (e: any) {}
|
|
6676
7063
|
}
|
|
6677
|
-
attempts = attempts + 1;
|
|
6678
|
-
if (attempts >= 30) return;
|
|
6679
|
-
if (typeof requestAnimationFrame === 'function') requestAnimationFrame(tryFocus);else setTimeout(tryFocus, 16);
|
|
6680
7064
|
};
|
|
6681
7065
|
if (typeof requestAnimationFrame === 'function') requestAnimationFrame(tryFocus);else setTimeout(tryFocus, 0);
|
|
6682
7066
|
};
|
|
@@ -6732,8 +7116,10 @@ ${this.groupable ? html`<div class="rdt-group-bar-host" data-rozie-s-d5dcab4c>
|
|
|
6732
7116
|
// from it. Cleared on endEdit.
|
|
6733
7117
|
this._editFocusColId.value = colId;
|
|
6734
7118
|
// B2: a seeded (type-to-edit) entry must NOT select-all — keep the caret after the
|
|
6735
|
-
// seeded char so subsequent typing appends instead of replacing it.
|
|
6736
|
-
|
|
7119
|
+
// seeded char so subsequent typing appends instead of replacing it. rowIndex/colIndex
|
|
7120
|
+
// are THIS call's own params — passed through directly (D-02), never re-read off
|
|
7121
|
+
// $data.editingRow/editingCol inside the poll.
|
|
7122
|
+
this.focusEditorWhenReady(rowIndex, colIndex, seed == null);
|
|
6737
7123
|
};
|
|
6738
7124
|
|
|
6739
7125
|
// Return focus to a body cell AFTER the editor unmounts (commit/cancel). The display↔
|
|
@@ -6748,6 +7134,21 @@ ${this.groupable ? html`<div class="rdt-group-bar-host" data-rozie-s-d5dcab4c>
|
|
|
6748
7134
|
const tryFocus = () => {
|
|
6749
7135
|
const el = this.resolveCellEl(String(row), col);
|
|
6750
7136
|
if (el) {
|
|
7137
|
+
// B-05 / F-07: do NOT stomp focus a later interaction has already placed on a DIFFERENT
|
|
7138
|
+
// grid cell. This poll runs up to 30 frames after commit/cancel, so without this guard a
|
|
7139
|
+
// user (or a test) who moves to the next cell in that window gets yanked BACK to the cell
|
|
7140
|
+
// they just left — and because the steal depends on rAF timing it presents as a flake that
|
|
7141
|
+
// rotates across targets rather than as a reproducible bug. Measured 2026-09-12: the
|
|
7142
|
+
// data-table-dropins editor case failed with the active cell pinned to the PREVIOUS column
|
|
7143
|
+
// (want 0/2, active 0/1; want 0/1, active 0/0) with no editor open.
|
|
7144
|
+
// focusEditorWhenReady already carries the equivalent guard against the same class of
|
|
7145
|
+
// theft; this is its missing twin. Compare the OWNING CELL (not node identity) so a
|
|
7146
|
+
// node-replacing re-render on Solid still resolves a genuinely dropped focus as recoverable.
|
|
7147
|
+
const ae = this.gridRoot && this.gridRoot.getRootNode ? this.gridRoot.getRootNode().activeElement : null;
|
|
7148
|
+
if (ae && ae !== el && this.gridRoot.contains && this.gridRoot.contains(ae) && ae.closest) {
|
|
7149
|
+
const aeCell = ae.closest('[data-grid-cell]');
|
|
7150
|
+
if (aeCell && aeCell !== el) return;
|
|
7151
|
+
}
|
|
6751
7152
|
el.focus();
|
|
6752
7153
|
return;
|
|
6753
7154
|
}
|
|
@@ -6847,8 +7248,10 @@ ${this.groupable ? html`<div class="rdt-group-bar-host" data-rozie-s-d5dcab4c>
|
|
|
6847
7248
|
const err = this.runValidator(colId, newValue, rowOriginal);
|
|
6848
7249
|
if (err !== true) {
|
|
6849
7250
|
// D-01: reject — keep the editor open, announce, re-trap focus, NEVER write the model.
|
|
7251
|
+
// $data.editingRow/editingCol are STABLE here (set by a prior beginEdit call, not this
|
|
7252
|
+
// same tick) — safe to read directly, unlike the deleted $data.editFocusColId gate.
|
|
6850
7253
|
this.setInvalid(err);
|
|
6851
|
-
this.focusEditorWhenReady();
|
|
7254
|
+
this.focusEditorWhenReady(this._editingRow.value, this._editingCol.value);
|
|
6852
7255
|
return false;
|
|
6853
7256
|
}
|
|
6854
7257
|
this.setInvalid('');
|
|
@@ -7017,22 +7420,22 @@ ${this.groupable ? html`<div class="rdt-group-bar-host" data-rozie-s-d5dcab4c>
|
|
|
7017
7420
|
|
|
7018
7421
|
// B21/B22: focus the row-mode editor at a given VISIBLE col index. In full-row edit every
|
|
7019
7422
|
// editable cell is already mounted as an editor, so this resolves the cell off gridRoot and
|
|
7020
|
-
// focuses its [data-editing-cell] control. Bounded rAF-poll (mirrors
|
|
7021
|
-
// so a React re-render that recreates the input across the focus call
|
|
7022
|
-
// all on text/number editors (a no-op try/catch on select/checkbox).
|
|
7023
|
-
//
|
|
7024
|
-
//
|
|
7025
|
-
//
|
|
7026
|
-
//
|
|
7027
|
-
//
|
|
7423
|
+
// focuses its [data-editing-cell][data-builtin-editor] control. Bounded rAF-poll (mirrors
|
|
7424
|
+
// focusEditorWhenReady) so a React re-render that recreates the input across the focus call
|
|
7425
|
+
// still lands it. select-all on text/number editors (a no-op try/catch on select/checkbox).
|
|
7426
|
+
// D-01/D-02 (88-07): the old columnIdAt + presence-check early-return gate is DELETED — the
|
|
7427
|
+
// host-owned data-builtin-editor marker replaces it. If the resolved cell already holds an
|
|
7428
|
+
// editor carrying ONLY the base data-editing-cell marker (no builtin marker), that's a
|
|
7429
|
+
// consumer drop-in — bail immediately, it owns its own focus (EditorText's own $onMount +
|
|
7430
|
+
// reactive `autofocus`, which the caller already flips via $data.editFocusColId). This
|
|
7431
|
+
// two-step resolve (resolveCellEl THEN a scoped query within that one cell) was ALREADY
|
|
7432
|
+
// column-scoped and correct — it is the shape focusEditorWhenReady's D-02 rework adopts.
|
|
7028
7433
|
focusRowEditorAt = (rowIndex: any, colIndex: any) => {
|
|
7029
7434
|
if (!this.gridRoot) return;
|
|
7030
|
-
const colId = this.columnIdAt(rowIndex, colIndex);
|
|
7031
|
-
if (colId != null && this.hasEditorSlot(colId)) return;
|
|
7032
7435
|
let attempts = 0;
|
|
7033
7436
|
const tryFocus = () => {
|
|
7034
7437
|
const cellEl = this.resolveCellEl(String(rowIndex), colIndex);
|
|
7035
|
-
const ed = cellEl && cellEl.querySelector ? cellEl.querySelector('[data-editing-cell]') : null;
|
|
7438
|
+
const ed = cellEl && cellEl.querySelector ? cellEl.querySelector('[data-editing-cell][data-builtin-editor]') : null;
|
|
7036
7439
|
if (ed) {
|
|
7037
7440
|
ed.focus();
|
|
7038
7441
|
if (ed.select) {
|
|
@@ -7042,6 +7445,11 @@ ${this.groupable ? html`<div class="rdt-group-bar-host" data-rozie-s-d5dcab4c>
|
|
|
7042
7445
|
}
|
|
7043
7446
|
return;
|
|
7044
7447
|
}
|
|
7448
|
+
// No marked built-in editor yet in this cell: a drop-in mounted there owns its own
|
|
7449
|
+
// focus — bail immediately (D-02) rather than burning the retry ceiling on DOM the
|
|
7450
|
+
// host does not own. No editor at all yet just means "keep polling."
|
|
7451
|
+
const any = cellEl && cellEl.querySelector ? cellEl.querySelector('[data-editing-cell]') : null;
|
|
7452
|
+
if (any) return;
|
|
7045
7453
|
attempts = attempts + 1;
|
|
7046
7454
|
if (attempts >= 30) return;
|
|
7047
7455
|
if (typeof requestAnimationFrame === 'function') requestAnimationFrame(tryFocus);else setTimeout(tryFocus, 16);
|
|
@@ -7088,9 +7496,11 @@ ${this.groupable ? html`<div class="rdt-group-bar-host" data-rozie-s-d5dcab4c>
|
|
|
7088
7496
|
// Editor-owns-focus contract (quick 260711-i5m): the row's FIRST editable column is the
|
|
7089
7497
|
// initial focus target — editorAutofocusFor derives the reactive `autofocus` #editor scope
|
|
7090
7498
|
// prop from it (a built-in column is also host-focused below via focusEditorWhenReady; a
|
|
7091
|
-
// drop-in column self-focuses via its own $onMount, gated off the host reach-in
|
|
7499
|
+
// drop-in column self-focuses via its own $onMount, gated off the host reach-in via D-01's
|
|
7500
|
+
// marker). editable[0].colIndex is THIS call's own already-resolved value (D-02) — passed
|
|
7501
|
+
// through directly, never re-read off $data.editFocusColId inside the poll.
|
|
7092
7502
|
this._editFocusColId.value = editable[0].colId;
|
|
7093
|
-
this.focusEditorWhenReady();
|
|
7503
|
+
this.focusEditorWhenReady(rowIndex, editable[0].colIndex);
|
|
7094
7504
|
};
|
|
7095
7505
|
|
|
7096
7506
|
// commitRow(): validate EVERY edited column (D-01 — keep the row open if ANY fails: set
|