@rozie-ui/data-table-lit 0.1.1 → 0.2.1
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 +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.d.cts +20 -21
- package/dist/index.d.mts +20 -21
- package/dist/index.mjs +2 -2
- package/package.json +7 -9
- package/src/Column.ts +2 -2
- package/src/DataTable.ts +4 -15
- package/src/EditorCheckbox.ts +2 -2
- package/src/EditorDate.ts +2 -2
- package/src/EditorNumber.ts +2 -2
- package/src/EditorSelect.ts +2 -2
- package/src/EditorText.ts +2 -2
- package/src/FilterNumberRange.ts +1 -1
- package/src/FilterSelect.ts +1 -1
- package/src/FilterText.ts +1 -1
- package/src/GroupBar.ts +2 -2
- package/src/Popover.ts +0 -446
- package/src/internal/middleware.ts +0 -54
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ Idiomatic **lit** `DataTable` — a headless, fully-accessible (WAI-ARIA) data t
|
|
|
8
8
|
npm i @rozie-ui/data-table-lit
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
Peer dependencies: `lit + @lit/context + @lit-labs/preact-signals + @preact/signals-core + @tanstack/table-core + @tanstack/virtual-core + @
|
|
11
|
+
Peer dependencies: `lit + @lit/context + @lit-labs/preact-signals + @preact/signals-core + @tanstack/table-core + @tanstack/virtual-core + @rozie-ui/popover-lit`. Install them alongside this package.
|
|
12
12
|
|
|
13
13
|
## Usage
|
|
14
14
|
|
package/dist/index.cjs
CHANGED
|
@@ -8,6 +8,7 @@ let _lit_labs_preact_signals = require("@lit-labs/preact-signals");
|
|
|
8
8
|
let _rozie_runtime_lit = require("@rozie/runtime-lit");
|
|
9
9
|
let _lit_context = require("@lit/context");
|
|
10
10
|
let lit_directives_repeat_js = require("lit/directives/repeat.js");
|
|
11
|
+
require("@rozie-ui/popover-lit");
|
|
11
12
|
let _tanstack_table_core = require("@tanstack/table-core");
|
|
12
13
|
let _tanstack_virtual_core = require("@tanstack/virtual-core");
|
|
13
14
|
//#region \0@oxc-project+runtime@0.127.0/helpers/decorate.js
|
|
@@ -1055,8 +1056,7 @@ let DataTable = class DataTable extends (0, _lit_labs_preact_signals.SignalWatch
|
|
|
1055
1056
|
const base = this.pinStyle(colId);
|
|
1056
1057
|
if (this.isExpanderColumn(colId) && row && row.depth) {
|
|
1057
1058
|
const pad = "padding-left:" + (.5 + row.depth * 1.25) + "rem";
|
|
1058
|
-
|
|
1059
|
-
return base ? base + sep + pad : pad;
|
|
1059
|
+
return base ? base + pad : pad;
|
|
1060
1060
|
}
|
|
1061
1061
|
return base;
|
|
1062
1062
|
};
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import * as _$lit from "lit";
|
|
2
2
|
import { LitElement } from "lit";
|
|
3
|
-
|
|
4
3
|
//#region src/DataTable.d.ts
|
|
5
4
|
declare const DataTable_base: typeof LitElement;
|
|
6
5
|
declare class DataTable extends DataTable_base {
|
|
@@ -64,7 +63,7 @@ declare class DataTable extends DataTable_base {
|
|
|
64
63
|
/**
|
|
65
64
|
* Table-level child-row accessor `(originalRow, index) => TData[] | undefined` that drives nested sub-rows. When supplied (with `expandable`), table-core flattens the hierarchy and the expand seam reveals depth-indented child rows. Null → the `#detail` scoped slot is the expand mode.
|
|
66
65
|
*/
|
|
67
|
-
getSubRows: ((...args:
|
|
66
|
+
getSubRows: ((...args: any[]) => any) | null;
|
|
68
67
|
/**
|
|
69
68
|
* Opt-in gate for the **headless `#groupBar`** host region. Default `false` is byte-identical-off. `getGroupedRowModel` is wired unconditionally (inert when `grouping` is empty), so grouping is driven by the `grouping` model; this flag only gates the consumer-facing group-bar surface (the component ships **no** built-in drag UI).
|
|
70
69
|
*/
|
|
@@ -706,7 +705,7 @@ declare class Column extends Column_base {
|
|
|
706
705
|
/**
|
|
707
706
|
* The table-core aggregation for this column inside a group-header cell. Either a built-in name string — `'sum'` | `'min'` | `'max'` | `'extent'` | `'mean'` | `'median'` | `'unique'` | `'uniqueCount'` | `'count'` — or a custom function `(columnId, leafRows, childRows) => any` (defensively wrapped by the parent so a throw cannot crash grouping). Null → no aggregation (the group-header cell renders as a placeholder).
|
|
708
707
|
*/
|
|
709
|
-
aggregationFn: string | (((...args:
|
|
708
|
+
aggregationFn: string | (((...args: any[]) => any) | null);
|
|
710
709
|
/**
|
|
711
710
|
* Whether this column's cells are editable (opt-in). Default `false` → the column is read-only and the display↔editor branch never mounts an editor. Bind `:editable="true"` (a bare attr only coerces on Vue+Lit).
|
|
712
711
|
*/
|
|
@@ -722,7 +721,7 @@ declare class Column extends Column_base {
|
|
|
722
721
|
/**
|
|
723
722
|
* Synchronous per-column validator `(value, row) => true | string`. A string return is the error message (the editor stays open and the aria-live region announces it). Null → no validation. The parent wraps it defensively against a thrown/non-bool/non-string return.
|
|
724
723
|
*/
|
|
725
|
-
validate: ((...args:
|
|
724
|
+
validate: ((...args: any[]) => any) | null;
|
|
726
725
|
private __rozieFirstUpdateDone;
|
|
727
726
|
private __rozieCtxConsumer_data_table_columns;
|
|
728
727
|
private get registry();
|
|
@@ -745,11 +744,11 @@ declare class Column extends Column_base {
|
|
|
745
744
|
width: string | number;
|
|
746
745
|
expandable: boolean;
|
|
747
746
|
groupable: boolean;
|
|
748
|
-
aggregationFn: string | ((...args:
|
|
747
|
+
aggregationFn: string | ((...args: any[]) => any);
|
|
749
748
|
editable: boolean;
|
|
750
749
|
editor: string;
|
|
751
750
|
editorOptions: any[];
|
|
752
|
-
validate: (...args:
|
|
751
|
+
validate: (...args: any[]) => any;
|
|
753
752
|
};
|
|
754
753
|
}
|
|
755
754
|
//#endregion
|
|
@@ -776,11 +775,11 @@ declare class EditorText extends EditorText_base {
|
|
|
776
775
|
/**
|
|
777
776
|
* `(value) => void` — commit the edited cell value (from the `#editor` slot scope). Null-guarded at call sites.
|
|
778
777
|
*/
|
|
779
|
-
commit: ((...args:
|
|
778
|
+
commit: ((...args: any[]) => any) | null;
|
|
780
779
|
/**
|
|
781
780
|
* `() => void` — revert the edit and close the editor (from the `#editor` slot scope). Null-guarded at call sites.
|
|
782
781
|
*/
|
|
783
|
-
cancel: ((...args:
|
|
782
|
+
cancel: ((...args: any[]) => any) | null;
|
|
784
783
|
/**
|
|
785
784
|
* Focus this editor's primary input when true — the host sets it for the one editor that should hold focus; reactive.
|
|
786
785
|
*/
|
|
@@ -824,11 +823,11 @@ declare class EditorNumber extends EditorNumber_base {
|
|
|
824
823
|
/**
|
|
825
824
|
* `(value) => void` — commit the cell. The draft is coerced with `Number()` at commit time; an empty/whitespace or non-numeric draft commits `null` (never `NaN`). Null-guarded at call sites.
|
|
826
825
|
*/
|
|
827
|
-
commit: ((...args:
|
|
826
|
+
commit: ((...args: any[]) => any) | null;
|
|
828
827
|
/**
|
|
829
828
|
* `() => void` — revert the edit (Escape). Null-guarded at call sites.
|
|
830
829
|
*/
|
|
831
|
-
cancel: ((...args:
|
|
830
|
+
cancel: ((...args: any[]) => any) | null;
|
|
832
831
|
private _draft;
|
|
833
832
|
private _disconnectCleanups;
|
|
834
833
|
private _rozieTornDown;
|
|
@@ -865,11 +864,11 @@ declare class EditorSelect extends EditorSelect_base {
|
|
|
865
864
|
/**
|
|
866
865
|
* `(value) => void` — commit the cell with the selected value (Enter / blur). Null-guarded at call sites.
|
|
867
866
|
*/
|
|
868
|
-
commit: ((...args:
|
|
867
|
+
commit: ((...args: any[]) => any) | null;
|
|
869
868
|
/**
|
|
870
869
|
* `() => void` — revert the edit (Escape). Null-guarded at call sites.
|
|
871
870
|
*/
|
|
872
|
-
cancel: ((...args:
|
|
871
|
+
cancel: ((...args: any[]) => any) | null;
|
|
873
872
|
/**
|
|
874
873
|
* The select options — `[{ value, label }]`. Mirrors `<Column editorOptions>`.
|
|
875
874
|
*/
|
|
@@ -910,11 +909,11 @@ declare class EditorCheckbox extends EditorCheckbox_base {
|
|
|
910
909
|
/**
|
|
911
910
|
* `(value) => void` — commit the cell. This editor immediately commits the boolean checked state on `@change`. Null-guarded at call sites.
|
|
912
911
|
*/
|
|
913
|
-
commit: ((...args:
|
|
912
|
+
commit: ((...args: any[]) => any) | null;
|
|
914
913
|
/**
|
|
915
914
|
* `() => void` — revert the edit (Escape). Null-guarded at call sites.
|
|
916
915
|
*/
|
|
917
|
-
cancel: ((...args:
|
|
916
|
+
cancel: ((...args: any[]) => any) | null;
|
|
918
917
|
private _disconnectCleanups;
|
|
919
918
|
private _rozieTornDown;
|
|
920
919
|
disconnectedCallback(): void;
|
|
@@ -946,11 +945,11 @@ declare class EditorDate extends EditorDate_base {
|
|
|
946
945
|
/**
|
|
947
946
|
* `(value) => void` — commit the cell with the ISO `YYYY-MM-DD` string (Enter / blur). Null-guarded at call sites.
|
|
948
947
|
*/
|
|
949
|
-
commit: ((...args:
|
|
948
|
+
commit: ((...args: any[]) => any) | null;
|
|
950
949
|
/**
|
|
951
950
|
* `() => void` — revert the edit (Escape). Null-guarded at call sites.
|
|
952
951
|
*/
|
|
953
|
-
cancel: ((...args:
|
|
952
|
+
cancel: ((...args: any[]) => any) | null;
|
|
954
953
|
private _draft;
|
|
955
954
|
private _disconnectCleanups;
|
|
956
955
|
private _rozieTornDown;
|
|
@@ -984,7 +983,7 @@ declare class FilterText extends FilterText_base {
|
|
|
984
983
|
/**
|
|
985
984
|
* `(columnId, value) => void` — apply the column filter (Enter / blur applies, Escape clears). Null-guarded at call sites.
|
|
986
985
|
*/
|
|
987
|
-
setFilter: ((...args:
|
|
986
|
+
setFilter: ((...args: any[]) => any) | null;
|
|
988
987
|
private _draft;
|
|
989
988
|
private _disconnectCleanups;
|
|
990
989
|
private _rozieTornDown;
|
|
@@ -1017,7 +1016,7 @@ declare class FilterNumberRange extends FilterNumberRange_base {
|
|
|
1017
1016
|
/**
|
|
1018
1017
|
* `(columnId, value) => void` — apply the column filter as a `[min, max]` tuple (each side coerced to a Number or `undefined`, so a one-sided range works); both empty clears the filter. Null-guarded at call sites.
|
|
1019
1018
|
*/
|
|
1020
|
-
setFilter: ((...args:
|
|
1019
|
+
setFilter: ((...args: any[]) => any) | null;
|
|
1021
1020
|
/**
|
|
1022
1021
|
* The faceted `[min, max]` bounds for this column (`[number, number]` or null) — drives the input placeholders only.
|
|
1023
1022
|
*/
|
|
@@ -1055,7 +1054,7 @@ declare class FilterSelect extends FilterSelect_base {
|
|
|
1055
1054
|
/**
|
|
1056
1055
|
* `(columnId, value) => void` — apply the column filter on change; the leading empty "All" option clears it. Null-guarded at call sites.
|
|
1057
1056
|
*/
|
|
1058
|
-
setFilter: ((...args:
|
|
1057
|
+
setFilter: ((...args: any[]) => any) | null;
|
|
1059
1058
|
/**
|
|
1060
1059
|
* The faceted distinct keys for this column (cross-filtered, keys only — no occurrence counts) used to build the `<option>` list.
|
|
1061
1060
|
*/
|
|
@@ -1083,11 +1082,11 @@ declare class GroupBar extends GroupBar_base {
|
|
|
1083
1082
|
/**
|
|
1084
1083
|
* `(cols: string[]) => void` — the only add/reorder writer for the grouping order. Null-guarded at call sites.
|
|
1085
1084
|
*/
|
|
1086
|
-
applyGrouping: ((...args:
|
|
1085
|
+
applyGrouping: ((...args: any[]) => any) | null;
|
|
1087
1086
|
/**
|
|
1088
1087
|
* `() => void` — the only clear writer; resets grouping to empty. Null-guarded at call sites.
|
|
1089
1088
|
*/
|
|
1090
|
-
clearGrouping: ((...args:
|
|
1089
|
+
clearGrouping: ((...args: any[]) => any) | null;
|
|
1091
1090
|
private _draggingId;
|
|
1092
1091
|
private _isOver;
|
|
1093
1092
|
private _dragKind;
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import * as _$lit from "lit";
|
|
2
2
|
import { LitElement } from "lit";
|
|
3
|
-
|
|
4
3
|
//#region src/DataTable.d.ts
|
|
5
4
|
declare const DataTable_base: typeof LitElement;
|
|
6
5
|
declare class DataTable extends DataTable_base {
|
|
@@ -64,7 +63,7 @@ declare class DataTable extends DataTable_base {
|
|
|
64
63
|
/**
|
|
65
64
|
* Table-level child-row accessor `(originalRow, index) => TData[] | undefined` that drives nested sub-rows. When supplied (with `expandable`), table-core flattens the hierarchy and the expand seam reveals depth-indented child rows. Null → the `#detail` scoped slot is the expand mode.
|
|
66
65
|
*/
|
|
67
|
-
getSubRows: ((...args:
|
|
66
|
+
getSubRows: ((...args: any[]) => any) | null;
|
|
68
67
|
/**
|
|
69
68
|
* Opt-in gate for the **headless `#groupBar`** host region. Default `false` is byte-identical-off. `getGroupedRowModel` is wired unconditionally (inert when `grouping` is empty), so grouping is driven by the `grouping` model; this flag only gates the consumer-facing group-bar surface (the component ships **no** built-in drag UI).
|
|
70
69
|
*/
|
|
@@ -706,7 +705,7 @@ declare class Column extends Column_base {
|
|
|
706
705
|
/**
|
|
707
706
|
* The table-core aggregation for this column inside a group-header cell. Either a built-in name string — `'sum'` | `'min'` | `'max'` | `'extent'` | `'mean'` | `'median'` | `'unique'` | `'uniqueCount'` | `'count'` — or a custom function `(columnId, leafRows, childRows) => any` (defensively wrapped by the parent so a throw cannot crash grouping). Null → no aggregation (the group-header cell renders as a placeholder).
|
|
708
707
|
*/
|
|
709
|
-
aggregationFn: string | (((...args:
|
|
708
|
+
aggregationFn: string | (((...args: any[]) => any) | null);
|
|
710
709
|
/**
|
|
711
710
|
* Whether this column's cells are editable (opt-in). Default `false` → the column is read-only and the display↔editor branch never mounts an editor. Bind `:editable="true"` (a bare attr only coerces on Vue+Lit).
|
|
712
711
|
*/
|
|
@@ -722,7 +721,7 @@ declare class Column extends Column_base {
|
|
|
722
721
|
/**
|
|
723
722
|
* Synchronous per-column validator `(value, row) => true | string`. A string return is the error message (the editor stays open and the aria-live region announces it). Null → no validation. The parent wraps it defensively against a thrown/non-bool/non-string return.
|
|
724
723
|
*/
|
|
725
|
-
validate: ((...args:
|
|
724
|
+
validate: ((...args: any[]) => any) | null;
|
|
726
725
|
private __rozieFirstUpdateDone;
|
|
727
726
|
private __rozieCtxConsumer_data_table_columns;
|
|
728
727
|
private get registry();
|
|
@@ -745,11 +744,11 @@ declare class Column extends Column_base {
|
|
|
745
744
|
width: string | number;
|
|
746
745
|
expandable: boolean;
|
|
747
746
|
groupable: boolean;
|
|
748
|
-
aggregationFn: string | ((...args:
|
|
747
|
+
aggregationFn: string | ((...args: any[]) => any);
|
|
749
748
|
editable: boolean;
|
|
750
749
|
editor: string;
|
|
751
750
|
editorOptions: any[];
|
|
752
|
-
validate: (...args:
|
|
751
|
+
validate: (...args: any[]) => any;
|
|
753
752
|
};
|
|
754
753
|
}
|
|
755
754
|
//#endregion
|
|
@@ -776,11 +775,11 @@ declare class EditorText extends EditorText_base {
|
|
|
776
775
|
/**
|
|
777
776
|
* `(value) => void` — commit the edited cell value (from the `#editor` slot scope). Null-guarded at call sites.
|
|
778
777
|
*/
|
|
779
|
-
commit: ((...args:
|
|
778
|
+
commit: ((...args: any[]) => any) | null;
|
|
780
779
|
/**
|
|
781
780
|
* `() => void` — revert the edit and close the editor (from the `#editor` slot scope). Null-guarded at call sites.
|
|
782
781
|
*/
|
|
783
|
-
cancel: ((...args:
|
|
782
|
+
cancel: ((...args: any[]) => any) | null;
|
|
784
783
|
/**
|
|
785
784
|
* Focus this editor's primary input when true — the host sets it for the one editor that should hold focus; reactive.
|
|
786
785
|
*/
|
|
@@ -824,11 +823,11 @@ declare class EditorNumber extends EditorNumber_base {
|
|
|
824
823
|
/**
|
|
825
824
|
* `(value) => void` — commit the cell. The draft is coerced with `Number()` at commit time; an empty/whitespace or non-numeric draft commits `null` (never `NaN`). Null-guarded at call sites.
|
|
826
825
|
*/
|
|
827
|
-
commit: ((...args:
|
|
826
|
+
commit: ((...args: any[]) => any) | null;
|
|
828
827
|
/**
|
|
829
828
|
* `() => void` — revert the edit (Escape). Null-guarded at call sites.
|
|
830
829
|
*/
|
|
831
|
-
cancel: ((...args:
|
|
830
|
+
cancel: ((...args: any[]) => any) | null;
|
|
832
831
|
private _draft;
|
|
833
832
|
private _disconnectCleanups;
|
|
834
833
|
private _rozieTornDown;
|
|
@@ -865,11 +864,11 @@ declare class EditorSelect extends EditorSelect_base {
|
|
|
865
864
|
/**
|
|
866
865
|
* `(value) => void` — commit the cell with the selected value (Enter / blur). Null-guarded at call sites.
|
|
867
866
|
*/
|
|
868
|
-
commit: ((...args:
|
|
867
|
+
commit: ((...args: any[]) => any) | null;
|
|
869
868
|
/**
|
|
870
869
|
* `() => void` — revert the edit (Escape). Null-guarded at call sites.
|
|
871
870
|
*/
|
|
872
|
-
cancel: ((...args:
|
|
871
|
+
cancel: ((...args: any[]) => any) | null;
|
|
873
872
|
/**
|
|
874
873
|
* The select options — `[{ value, label }]`. Mirrors `<Column editorOptions>`.
|
|
875
874
|
*/
|
|
@@ -910,11 +909,11 @@ declare class EditorCheckbox extends EditorCheckbox_base {
|
|
|
910
909
|
/**
|
|
911
910
|
* `(value) => void` — commit the cell. This editor immediately commits the boolean checked state on `@change`. Null-guarded at call sites.
|
|
912
911
|
*/
|
|
913
|
-
commit: ((...args:
|
|
912
|
+
commit: ((...args: any[]) => any) | null;
|
|
914
913
|
/**
|
|
915
914
|
* `() => void` — revert the edit (Escape). Null-guarded at call sites.
|
|
916
915
|
*/
|
|
917
|
-
cancel: ((...args:
|
|
916
|
+
cancel: ((...args: any[]) => any) | null;
|
|
918
917
|
private _disconnectCleanups;
|
|
919
918
|
private _rozieTornDown;
|
|
920
919
|
disconnectedCallback(): void;
|
|
@@ -946,11 +945,11 @@ declare class EditorDate extends EditorDate_base {
|
|
|
946
945
|
/**
|
|
947
946
|
* `(value) => void` — commit the cell with the ISO `YYYY-MM-DD` string (Enter / blur). Null-guarded at call sites.
|
|
948
947
|
*/
|
|
949
|
-
commit: ((...args:
|
|
948
|
+
commit: ((...args: any[]) => any) | null;
|
|
950
949
|
/**
|
|
951
950
|
* `() => void` — revert the edit (Escape). Null-guarded at call sites.
|
|
952
951
|
*/
|
|
953
|
-
cancel: ((...args:
|
|
952
|
+
cancel: ((...args: any[]) => any) | null;
|
|
954
953
|
private _draft;
|
|
955
954
|
private _disconnectCleanups;
|
|
956
955
|
private _rozieTornDown;
|
|
@@ -984,7 +983,7 @@ declare class FilterText extends FilterText_base {
|
|
|
984
983
|
/**
|
|
985
984
|
* `(columnId, value) => void` — apply the column filter (Enter / blur applies, Escape clears). Null-guarded at call sites.
|
|
986
985
|
*/
|
|
987
|
-
setFilter: ((...args:
|
|
986
|
+
setFilter: ((...args: any[]) => any) | null;
|
|
988
987
|
private _draft;
|
|
989
988
|
private _disconnectCleanups;
|
|
990
989
|
private _rozieTornDown;
|
|
@@ -1017,7 +1016,7 @@ declare class FilterNumberRange extends FilterNumberRange_base {
|
|
|
1017
1016
|
/**
|
|
1018
1017
|
* `(columnId, value) => void` — apply the column filter as a `[min, max]` tuple (each side coerced to a Number or `undefined`, so a one-sided range works); both empty clears the filter. Null-guarded at call sites.
|
|
1019
1018
|
*/
|
|
1020
|
-
setFilter: ((...args:
|
|
1019
|
+
setFilter: ((...args: any[]) => any) | null;
|
|
1021
1020
|
/**
|
|
1022
1021
|
* The faceted `[min, max]` bounds for this column (`[number, number]` or null) — drives the input placeholders only.
|
|
1023
1022
|
*/
|
|
@@ -1055,7 +1054,7 @@ declare class FilterSelect extends FilterSelect_base {
|
|
|
1055
1054
|
/**
|
|
1056
1055
|
* `(columnId, value) => void` — apply the column filter on change; the leading empty "All" option clears it. Null-guarded at call sites.
|
|
1057
1056
|
*/
|
|
1058
|
-
setFilter: ((...args:
|
|
1057
|
+
setFilter: ((...args: any[]) => any) | null;
|
|
1059
1058
|
/**
|
|
1060
1059
|
* The faceted distinct keys for this column (cross-filtered, keys only — no occurrence counts) used to build the `<option>` list.
|
|
1061
1060
|
*/
|
|
@@ -1083,11 +1082,11 @@ declare class GroupBar extends GroupBar_base {
|
|
|
1083
1082
|
/**
|
|
1084
1083
|
* `(cols: string[]) => void` — the only add/reorder writer for the grouping order. Null-guarded at call sites.
|
|
1085
1084
|
*/
|
|
1086
|
-
applyGrouping: ((...args:
|
|
1085
|
+
applyGrouping: ((...args: any[]) => any) | null;
|
|
1087
1086
|
/**
|
|
1088
1087
|
* `() => void` — the only clear writer; resets grouping to empty. Null-guarded at call sites.
|
|
1089
1088
|
*/
|
|
1090
|
-
clearGrouping: ((...args:
|
|
1089
|
+
clearGrouping: ((...args: any[]) => any) | null;
|
|
1091
1090
|
private _draggingId;
|
|
1092
1091
|
private _isOver;
|
|
1093
1092
|
private _dragKind;
|
package/dist/index.mjs
CHANGED
|
@@ -4,6 +4,7 @@ import { SignalWatcher, effect, signal, untracked } from "@lit-labs/preact-signa
|
|
|
4
4
|
import { createLitControllableProperty, rozieAttr, rozieDisplay, rozieStyle } from "@rozie/runtime-lit";
|
|
5
5
|
import { ContextConsumer, ContextProvider, createContext } from "@lit/context";
|
|
6
6
|
import { repeat } from "lit/directives/repeat.js";
|
|
7
|
+
import "@rozie-ui/popover-lit";
|
|
7
8
|
import { createTable, getCoreRowModel, getExpandedRowModel, getFacetedMinMaxValues, getFacetedRowModel, getFacetedUniqueValues, getFilteredRowModel, getGroupedRowModel, getPaginationRowModel, getSortedRowModel } from "@tanstack/table-core";
|
|
8
9
|
import { Virtualizer, elementScroll, measureElement, observeElementOffset, observeElementRect } from "@tanstack/virtual-core";
|
|
9
10
|
//#region \0@oxc-project+runtime@0.127.0/helpers/decorate.js
|
|
@@ -1051,8 +1052,7 @@ let DataTable = class DataTable extends SignalWatcher(LitElement) {
|
|
|
1051
1052
|
const base = this.pinStyle(colId);
|
|
1052
1053
|
if (this.isExpanderColumn(colId) && row && row.depth) {
|
|
1053
1054
|
const pad = "padding-left:" + (.5 + row.depth * 1.25) + "rem";
|
|
1054
|
-
|
|
1055
|
-
return base ? base + sep + pad : pad;
|
|
1055
|
+
return base ? base + pad : pad;
|
|
1056
1056
|
}
|
|
1057
1057
|
return base;
|
|
1058
1058
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rozie-ui/data-table-lit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "MIT",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"src"
|
|
46
46
|
],
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@rozie/runtime-lit": "0.
|
|
48
|
+
"@rozie/runtime-lit": "0.2.0"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
51
|
"lit": "^3.2",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"@preact/signals-core": "^1.3.0",
|
|
55
55
|
"@tanstack/table-core": "^8.21",
|
|
56
56
|
"@tanstack/virtual-core": "^3",
|
|
57
|
-
"@
|
|
57
|
+
"@rozie-ui/popover-lit": "^0.1.0"
|
|
58
58
|
},
|
|
59
59
|
"peerDependenciesMeta": {
|
|
60
60
|
"lit": {
|
|
@@ -75,19 +75,17 @@
|
|
|
75
75
|
"@tanstack/virtual-core": {
|
|
76
76
|
"optional": false
|
|
77
77
|
},
|
|
78
|
-
"@
|
|
78
|
+
"@rozie-ui/popover-lit": {
|
|
79
79
|
"optional": false
|
|
80
80
|
}
|
|
81
81
|
},
|
|
82
|
-
"devDependencies": {
|
|
83
|
-
"@floating-ui/dom": "^1.7.2"
|
|
84
|
-
},
|
|
82
|
+
"devDependencies": {},
|
|
85
83
|
"publishConfig": {
|
|
86
84
|
"access": "public"
|
|
87
85
|
},
|
|
88
86
|
"sideEffects": [
|
|
89
|
-
"
|
|
90
|
-
"
|
|
87
|
+
"./dist/index.mjs",
|
|
88
|
+
"./dist/index.cjs"
|
|
91
89
|
],
|
|
92
90
|
"scripts": {
|
|
93
91
|
"build": "tsdown",
|
package/src/Column.ts
CHANGED
|
@@ -52,7 +52,7 @@ export default class Column extends SignalWatcher(LitElement) {
|
|
|
52
52
|
/**
|
|
53
53
|
* The table-core aggregation for this column inside a group-header cell. Either a built-in name string — `'sum'` | `'min'` | `'max'` | `'extent'` | `'mean'` | `'median'` | `'unique'` | `'uniqueCount'` | `'count'` — or a custom function `(columnId, leafRows, childRows) => any` (defensively wrapped by the parent so a throw cannot crash grouping). Null → no aggregation (the group-header cell renders as a placeholder).
|
|
54
54
|
*/
|
|
55
|
-
@property({ type: String }) aggregationFn: string | (((...args:
|
|
55
|
+
@property({ type: String }) aggregationFn: string | (((...args: any[]) => any) | null) = null;
|
|
56
56
|
/**
|
|
57
57
|
* Whether this column's cells are editable (opt-in). Default `false` → the column is read-only and the display↔editor branch never mounts an editor. Bind `:editable="true"` (a bare attr only coerces on Vue+Lit).
|
|
58
58
|
*/
|
|
@@ -68,7 +68,7 @@ export default class Column extends SignalWatcher(LitElement) {
|
|
|
68
68
|
/**
|
|
69
69
|
* Synchronous per-column validator `(value, row) => true | string`. A string return is the error message (the editor stays open and the aria-live region announces it). Null → no validation. The parent wraps it defensively against a thrown/non-bool/non-string return.
|
|
70
70
|
*/
|
|
71
|
-
@property({ type: Function }) validate: ((...args:
|
|
71
|
+
@property({ type: Function }) validate: ((...args: any[]) => any) | null = null;
|
|
72
72
|
private __rozieFirstUpdateDone = false;
|
|
73
73
|
private __rozieCtxConsumer_data_table_columns = new ContextConsumer(this, { context: __rozieCtx_data_table_columns, subscribe: true });
|
|
74
74
|
private get registry() { return this.__rozieCtxConsumer_data_table_columns.value; }
|
package/src/DataTable.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { SignalWatcher, effect, signal, untracked } from '@lit-labs/preact-signa
|
|
|
4
4
|
import { createLitControllableProperty, rozieAttr, rozieDisplay, rozieStyle } from '@rozie/runtime-lit';
|
|
5
5
|
import { ContextProvider, createContext } from '@lit/context';
|
|
6
6
|
import { repeat } from 'lit/directives/repeat.js';
|
|
7
|
-
import '
|
|
7
|
+
import '@rozie-ui/popover-lit';
|
|
8
8
|
import { createTable, getCoreRowModel, getSortedRowModel, getFilteredRowModel, getPaginationRowModel, getExpandedRowModel, getGroupedRowModel,
|
|
9
9
|
// Faceted filtering (phase 50 reqs 8-9, D-03). All three are supplied UNCONDITIONALLY
|
|
10
10
|
// (mirrors the expand/group models) — inert until a consumer READS a column facet via the
|
|
@@ -25,11 +25,6 @@ getFacetedUniqueValues as makeFacetedUniqueValues, getFacetedMinMaxValues as mak
|
|
|
25
25
|
// by ALL virtual-core RUNTIME references sitting behind `if ($props.virtual)` / a `virtualizer`
|
|
26
26
|
// guard so they never execute when off — the import token is the only static virtual-core
|
|
27
27
|
// presence. NO per-framework adapter (the codegen guard forbids @tanstack/<fw>-virtual).
|
|
28
|
-
// Vertical row windowing (phase 53). A3: this static import line is emitted UNCONDITIONALLY
|
|
29
|
-
// (virtual-core is a peer dep the consumer installs); byte-identical-off (req-1) is satisfied
|
|
30
|
-
// by ALL virtual-core RUNTIME references sitting behind `if ($props.virtual)` / a `virtualizer`
|
|
31
|
-
// guard so they never execute when off — the import token is the only static virtual-core
|
|
32
|
-
// presence. NO per-framework adapter (the codegen guard forbids @tanstack/<fw>-virtual).
|
|
33
28
|
import { Virtualizer, elementScroll, observeElementRect, observeElementOffset, measureElement } from '@tanstack/virtual-core';
|
|
34
29
|
|
|
35
30
|
// table-core instance — top-level `let` referenced from hooks → React hoists to
|
|
@@ -492,7 +487,7 @@ export default class DataTable extends SignalWatcher(LitElement) {
|
|
|
492
487
|
/**
|
|
493
488
|
* Table-level child-row accessor `(originalRow, index) => TData[] | undefined` that drives nested sub-rows. When supplied (with `expandable`), table-core flattens the hierarchy and the expand seam reveals depth-indented child rows. Null → the `#detail` scoped slot is the expand mode.
|
|
494
489
|
*/
|
|
495
|
-
@property({ type: Function }) getSubRows: ((...args:
|
|
490
|
+
@property({ type: Function }) getSubRows: ((...args: any[]) => any) | null = null;
|
|
496
491
|
/**
|
|
497
492
|
* Opt-in gate for the **headless `#groupBar`** host region. Default `false` is byte-identical-off. `getGroupedRowModel` is wired unconditionally (inert when `grouping` is empty), so grouping is driven by the `grouping` model; this flag only gates the consumer-facing group-bar surface (the component ships **no** built-in drag UI).
|
|
498
493
|
*/
|
|
@@ -2515,15 +2510,9 @@ ${this.groupable ? html`<div class="rdt-group-bar-host" data-rozie-s-d5dcab4c>
|
|
|
2515
2510
|
bodyCellStyle = (row: any, colId: any) => {
|
|
2516
2511
|
const base = this.pinStyle(colId);
|
|
2517
2512
|
if (this.isExpanderColumn(colId) && row && row.depth) {
|
|
2513
|
+
// Only the expander column indents (the tree affordance lives here).
|
|
2518
2514
|
const pad = 'padding-left:' + (0.5 + row.depth * 1.25) + 'rem';
|
|
2519
|
-
|
|
2520
|
-
// second ';' produces a `;;` empty declaration. Browsers tolerate that in a
|
|
2521
|
-
// raw style attribute (React/Vue/Svelte/Lit) and Solid's cssText path does
|
|
2522
|
-
// too, but Angular's [style] binding parses the string via ɵɵstyleMap and
|
|
2523
|
-
// DROPS the declaration following the empty one — so the depth padding-left
|
|
2524
|
-
// silently vanished on Angular only. Emit a single, well-formed separator.
|
|
2525
|
-
const sep = base.endsWith(';') ? '' : ';';
|
|
2526
|
-
return base ? base + sep + pad : pad;
|
|
2515
|
+
return base ? base + pad : pad;
|
|
2527
2516
|
}
|
|
2528
2517
|
return base;
|
|
2529
2518
|
};
|
package/src/EditorCheckbox.ts
CHANGED
|
@@ -27,11 +27,11 @@ export default class EditorCheckbox extends SignalWatcher(LitElement) {
|
|
|
27
27
|
/**
|
|
28
28
|
* `(value) => void` — commit the cell. This editor immediately commits the boolean checked state on `@change`. Null-guarded at call sites.
|
|
29
29
|
*/
|
|
30
|
-
@property({ type: Function }) commit: ((...args:
|
|
30
|
+
@property({ type: Function }) commit: ((...args: any[]) => any) | null = null;
|
|
31
31
|
/**
|
|
32
32
|
* `() => void` — revert the edit (Escape). Null-guarded at call sites.
|
|
33
33
|
*/
|
|
34
|
-
@property({ type: Function }) cancel: ((...args:
|
|
34
|
+
@property({ type: Function }) cancel: ((...args: any[]) => any) | null = null;
|
|
35
35
|
|
|
36
36
|
private _disconnectCleanups: Array<() => void> = [];
|
|
37
37
|
// Re-parenting guard: set true once the deferred teardown has actually
|
package/src/EditorDate.ts
CHANGED
|
@@ -27,11 +27,11 @@ export default class EditorDate extends SignalWatcher(LitElement) {
|
|
|
27
27
|
/**
|
|
28
28
|
* `(value) => void` — commit the cell with the ISO `YYYY-MM-DD` string (Enter / blur). Null-guarded at call sites.
|
|
29
29
|
*/
|
|
30
|
-
@property({ type: Function }) commit: ((...args:
|
|
30
|
+
@property({ type: Function }) commit: ((...args: any[]) => any) | null = null;
|
|
31
31
|
/**
|
|
32
32
|
* `() => void` — revert the edit (Escape). Null-guarded at call sites.
|
|
33
33
|
*/
|
|
34
|
-
@property({ type: Function }) cancel: ((...args:
|
|
34
|
+
@property({ type: Function }) cancel: ((...args: any[]) => any) | null = null;
|
|
35
35
|
private _draft = signal('');
|
|
36
36
|
|
|
37
37
|
private _disconnectCleanups: Array<() => void> = [];
|
package/src/EditorNumber.ts
CHANGED
|
@@ -27,11 +27,11 @@ export default class EditorNumber extends SignalWatcher(LitElement) {
|
|
|
27
27
|
/**
|
|
28
28
|
* `(value) => void` — commit the cell. The draft is coerced with `Number()` at commit time; an empty/whitespace or non-numeric draft commits `null` (never `NaN`). Null-guarded at call sites.
|
|
29
29
|
*/
|
|
30
|
-
@property({ type: Function }) commit: ((...args:
|
|
30
|
+
@property({ type: Function }) commit: ((...args: any[]) => any) | null = null;
|
|
31
31
|
/**
|
|
32
32
|
* `() => void` — revert the edit (Escape). Null-guarded at call sites.
|
|
33
33
|
*/
|
|
34
|
-
@property({ type: Function }) cancel: ((...args:
|
|
34
|
+
@property({ type: Function }) cancel: ((...args: any[]) => any) | null = null;
|
|
35
35
|
private _draft = signal('');
|
|
36
36
|
|
|
37
37
|
private _disconnectCleanups: Array<() => void> = [];
|
package/src/EditorSelect.ts
CHANGED
|
@@ -29,11 +29,11 @@ export default class EditorSelect extends SignalWatcher(LitElement) {
|
|
|
29
29
|
/**
|
|
30
30
|
* `(value) => void` — commit the cell with the selected value (Enter / blur). Null-guarded at call sites.
|
|
31
31
|
*/
|
|
32
|
-
@property({ type: Function }) commit: ((...args:
|
|
32
|
+
@property({ type: Function }) commit: ((...args: any[]) => any) | null = null;
|
|
33
33
|
/**
|
|
34
34
|
* `() => void` — revert the edit (Escape). Null-guarded at call sites.
|
|
35
35
|
*/
|
|
36
|
-
@property({ type: Function }) cancel: ((...args:
|
|
36
|
+
@property({ type: Function }) cancel: ((...args: any[]) => any) | null = null;
|
|
37
37
|
/**
|
|
38
38
|
* The select options — `[{ value, label }]`. Mirrors `<Column editorOptions>`.
|
|
39
39
|
*/
|
package/src/EditorText.ts
CHANGED
|
@@ -27,11 +27,11 @@ export default class EditorText extends SignalWatcher(LitElement) {
|
|
|
27
27
|
/**
|
|
28
28
|
* `(value) => void` — commit the edited cell value (from the `#editor` slot scope). Null-guarded at call sites.
|
|
29
29
|
*/
|
|
30
|
-
@property({ type: Function }) commit: ((...args:
|
|
30
|
+
@property({ type: Function }) commit: ((...args: any[]) => any) | null = null;
|
|
31
31
|
/**
|
|
32
32
|
* `() => void` — revert the edit and close the editor (from the `#editor` slot scope). Null-guarded at call sites.
|
|
33
33
|
*/
|
|
34
|
-
@property({ type: Function }) cancel: ((...args:
|
|
34
|
+
@property({ type: Function }) cancel: ((...args: any[]) => any) | null = null;
|
|
35
35
|
/**
|
|
36
36
|
* Focus this editor's primary input when true — the host sets it for the one editor that should hold focus; reactive.
|
|
37
37
|
*/
|
package/src/FilterNumberRange.ts
CHANGED
|
@@ -24,7 +24,7 @@ export default class FilterNumberRange extends SignalWatcher(LitElement) {
|
|
|
24
24
|
/**
|
|
25
25
|
* `(columnId, value) => void` — apply the column filter as a `[min, max]` tuple (each side coerced to a Number or `undefined`, so a one-sided range works); both empty clears the filter. Null-guarded at call sites.
|
|
26
26
|
*/
|
|
27
|
-
@property({ type: Function }) setFilter: ((...args:
|
|
27
|
+
@property({ type: Function }) setFilter: ((...args: any[]) => any) | null = null;
|
|
28
28
|
/**
|
|
29
29
|
* The faceted `[min, max]` bounds for this column (`[number, number]` or null) — drives the input placeholders only.
|
|
30
30
|
*/
|
package/src/FilterSelect.ts
CHANGED
|
@@ -25,7 +25,7 @@ export default class FilterSelect extends SignalWatcher(LitElement) {
|
|
|
25
25
|
/**
|
|
26
26
|
* `(columnId, value) => void` — apply the column filter on change; the leading empty "All" option clears it. Null-guarded at call sites.
|
|
27
27
|
*/
|
|
28
|
-
@property({ type: Function }) setFilter: ((...args:
|
|
28
|
+
@property({ type: Function }) setFilter: ((...args: any[]) => any) | null = null;
|
|
29
29
|
/**
|
|
30
30
|
* The faceted distinct keys for this column (cross-filtered, keys only — no occurrence counts) used to build the `<option>` list.
|
|
31
31
|
*/
|
package/src/FilterText.ts
CHANGED
|
@@ -23,7 +23,7 @@ export default class FilterText extends SignalWatcher(LitElement) {
|
|
|
23
23
|
/**
|
|
24
24
|
* `(columnId, value) => void` — apply the column filter (Enter / blur applies, Escape clears). Null-guarded at call sites.
|
|
25
25
|
*/
|
|
26
|
-
@property({ type: Function }) setFilter: ((...args:
|
|
26
|
+
@property({ type: Function }) setFilter: ((...args: any[]) => any) | null = null;
|
|
27
27
|
private _draft = signal('');
|
|
28
28
|
|
|
29
29
|
private _disconnectCleanups: Array<() => void> = [];
|
package/src/GroupBar.ts
CHANGED
|
@@ -78,11 +78,11 @@ export default class GroupBar extends SignalWatcher(LitElement) {
|
|
|
78
78
|
/**
|
|
79
79
|
* `(cols: string[]) => void` — the only add/reorder writer for the grouping order. Null-guarded at call sites.
|
|
80
80
|
*/
|
|
81
|
-
@property({ type: Function }) applyGrouping: ((...args:
|
|
81
|
+
@property({ type: Function }) applyGrouping: ((...args: any[]) => any) | null = null;
|
|
82
82
|
/**
|
|
83
83
|
* `() => void` — the only clear writer; resets grouping to empty. Null-guarded at call sites.
|
|
84
84
|
*/
|
|
85
|
-
@property({ type: Function }) clearGrouping: ((...args:
|
|
85
|
+
@property({ type: Function }) clearGrouping: ((...args: any[]) => any) | null = null;
|
|
86
86
|
private _draggingId = signal('');
|
|
87
87
|
private _isOver = signal(false);
|
|
88
88
|
private _dragKind = signal('');
|
package/src/Popover.ts
DELETED
|
@@ -1,446 +0,0 @@
|
|
|
1
|
-
import { LitElement, css, html, nothing } from 'lit';
|
|
2
|
-
import { customElement, property, query, queryAssignedElements, state } from 'lit/decorators.js';
|
|
3
|
-
import { SignalWatcher, effect, untracked } from '@lit-labs/preact-signals';
|
|
4
|
-
import { attachOutsideClickListener, createLitControllableProperty, rozieAttr, rozieListeners, rozieSpread } from '@rozie/runtime-lit';
|
|
5
|
-
// The `offset` AND `arrow` middleware factories are ALIASED on import: both are
|
|
6
|
-
// ALSO author PROP names (`offset`, `arrow`). A bare `offset`/`arrow` shorthand in
|
|
7
|
-
// the buildMiddleware factories object resolves to the PROP — on Vue/Svelte the
|
|
8
|
-
// destructured prop local shadows the import, and on Angular the emitter rewrites
|
|
9
|
-
// the bare shorthand to the prop signal (`offset: this.offset()`, a number) instead
|
|
10
|
-
// of the middleware function (TS2322). Aliasing both severs the import↔prop clash.
|
|
11
|
-
// (The Cropper import-name==component-name class, applied to imports vs PROP names —
|
|
12
|
-
// two collisions, not one.) computePosition/autoUpdate/flip/shift carry no clash.
|
|
13
|
-
import { computePosition, autoUpdate, offset as offsetMiddleware, flip, shift, arrow as arrowMiddleware } from '@floating-ui/dom';
|
|
14
|
-
import { buildMiddleware } from './internal/middleware';
|
|
15
|
-
|
|
16
|
-
// null-lets so the bundled-leaf typeNeutralize pass annotates them `any`:
|
|
17
|
-
// anchorNode/floatingNode/arrowNode hold the resolved ref ELEMENTS (read ONLY in
|
|
18
|
-
// $onMount/handlers, ROZ123). They are deliberately named DIFFERENTLY from the
|
|
19
|
-
// `ref="anchorEl"` / `ref="floatingEl"` / `ref="arrowEl"` template ref names: the
|
|
20
|
-
// React/Svelte emitters declare a `const anchorEl = useRef(...)` for the ref, and a
|
|
21
|
-
// top-level `let anchorEl` hoisted to its own `useRef` would REDECLARE it (TS2451 —
|
|
22
|
-
// the local-name==ref-name self-shadow class, here in its `let X = null; X = $refs.X`
|
|
23
|
-
// variant, which deconflictRefShadows does NOT auto-rewrite since it only fires on the
|
|
24
|
-
// `const X = $refs.X` init shape).
|
|
25
|
-
// stopAutoUpdate is the autoUpdate teardown handle — a TOP-LEVEL `let` so the Solid
|
|
26
|
-
// onMount→onCleanup split (teardown is a separate closure) can still see it.
|
|
27
|
-
// lastFocusedEl (phase 72-06b) holds whatever had DOM focus at the moment a
|
|
28
|
-
// `trigger="click"` popover opened (natively the clicked trigger element itself,
|
|
29
|
-
// since a mousedown focuses a native `<button>` before its `click` fires) —
|
|
30
|
-
// restored on dismissal so Escape/click-outside don't drop focus to `<body>`.
|
|
31
|
-
// Same null-let convention as the others: read/written only in handlers, `any`
|
|
32
|
-
// via typeNeutralize.
|
|
33
|
-
|
|
34
|
-
interface RozieAnchorSlotCtx {
|
|
35
|
-
open: unknown;
|
|
36
|
-
toggle: unknown;
|
|
37
|
-
show: unknown;
|
|
38
|
-
hide: unknown;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
@customElement('rozie-popover')
|
|
42
|
-
export default class Popover extends SignalWatcher(LitElement) {
|
|
43
|
-
static styles = css`
|
|
44
|
-
:host{display:contents}
|
|
45
|
-
.rozie-popover[data-rozie-s-c6cf02ea] {
|
|
46
|
-
display: contents;
|
|
47
|
-
}
|
|
48
|
-
.rozie-popover-anchor[data-rozie-s-c6cf02ea] {
|
|
49
|
-
display: inline-block;
|
|
50
|
-
}
|
|
51
|
-
.rozie-popover-floating[data-rozie-s-c6cf02ea] {
|
|
52
|
-
position: absolute;
|
|
53
|
-
left: 0;
|
|
54
|
-
top: 0;
|
|
55
|
-
z-index: var(--rozie-popover-z, 1000);
|
|
56
|
-
width: max-content;
|
|
57
|
-
max-width: var(--rozie-popover-max-width, calc(100vw - 16px));
|
|
58
|
-
background: var(--rozie-popover-bg, #fff);
|
|
59
|
-
color: var(--rozie-popover-color, inherit);
|
|
60
|
-
border: var(--rozie-popover-border, 1px solid rgba(0, 0, 0, 0.12));
|
|
61
|
-
border-radius: var(--rozie-popover-radius, 8px);
|
|
62
|
-
box-shadow: var(--rozie-popover-shadow, 0 8px 24px rgba(0, 0, 0, 0.12));
|
|
63
|
-
padding: var(--rozie-popover-padding, 8px 12px);
|
|
64
|
-
}
|
|
65
|
-
.rozie-popover-arrow[data-rozie-s-c6cf02ea] {
|
|
66
|
-
position: absolute;
|
|
67
|
-
width: var(--rozie-popover-arrow-size, 8px);
|
|
68
|
-
height: var(--rozie-popover-arrow-size, 8px);
|
|
69
|
-
background: var(--rozie-popover-bg, #fff);
|
|
70
|
-
border: var(--rozie-popover-border, 1px solid rgba(0, 0, 0, 0.12));
|
|
71
|
-
transform: rotate(45deg);
|
|
72
|
-
}
|
|
73
|
-
`;
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Whether the floating content is open. The sole `model: true` prop — two-way bind it (`r-model:open` / `v-model:open` / `bind:open` / `[(open)]`) and Popover writes the new state back whenever the trigger or a dismissal toggles it. Left unbound it falls back to an uncontrolled default.
|
|
77
|
-
*/
|
|
78
|
-
@property({ type: Boolean, attribute: 'open' }) _open_attr: boolean = false;
|
|
79
|
-
private _openControllable = createLitControllableProperty<boolean>({ host: this, eventName: 'open-change', defaultValue: false, initialControlledValue: undefined });
|
|
80
|
-
/**
|
|
81
|
-
* Floating UI placement of the content relative to the anchor — one of `top`/`right`/`bottom`/`left`, each optionally suffixed `-start`/`-end` (e.g. `bottom-start`). With `disableFlip` off, the content may flip to the opposite side when it would overflow the viewport. Reconciled at runtime.
|
|
82
|
-
*/
|
|
83
|
-
@property({ type: String, reflect: true }) placement: string = 'bottom';
|
|
84
|
-
/**
|
|
85
|
-
* How the anchor opens the content: `'click'` toggles on click, `'hover'` opens on pointer-enter and closes on pointer-leave (tooltip-style), `'focus'` opens on focus and closes on blur. Drives both the gesture handlers and the ARIA role (`'hover'`/`'focus'` → tooltip, `'click'` → popover dialog).
|
|
86
|
-
*/
|
|
87
|
-
@property({ type: String, reflect: true }) trigger: string = 'click';
|
|
88
|
-
/**
|
|
89
|
-
* Distance in pixels between the anchor and the floating content (the Floating UI `offset` middleware). Reconciled at runtime.
|
|
90
|
-
*/
|
|
91
|
-
@property({ type: Number, reflect: true }) offset: number = 8;
|
|
92
|
-
/**
|
|
93
|
-
* Disable the Floating UI `flip` middleware. By default the content flips to the opposite side of the anchor when it would overflow the viewport; set this to keep it pinned to `placement` regardless.
|
|
94
|
-
*/
|
|
95
|
-
@property({ type: Boolean, reflect: true }) disableFlip: boolean = false;
|
|
96
|
-
/**
|
|
97
|
-
* Disable the Floating UI `shift` middleware. By default the content shifts along its axis to stay within the viewport; set this to keep it strictly aligned to the anchor.
|
|
98
|
-
*/
|
|
99
|
-
@property({ type: Boolean, reflect: true }) disableShift: boolean = false;
|
|
100
|
-
/**
|
|
101
|
-
* Opt in to a positioned arrow element. When set, Popover renders an arrow `<div>` and runs the Floating UI `arrow` middleware against it so it points at the anchor. Style it via the `--rozie-popover-*` arrow CSS custom properties.
|
|
102
|
-
*/
|
|
103
|
-
@property({ type: Boolean, reflect: true }) arrow: boolean = false;
|
|
104
|
-
/**
|
|
105
|
-
* Disable the control entirely: the trigger no longer opens the content and any open content is suppressed.
|
|
106
|
-
*/
|
|
107
|
-
@property({ type: Boolean, reflect: true }) disabled: boolean = false;
|
|
108
|
-
/**
|
|
109
|
-
* Opt in to modal dialog semantics for a `click` popover. **Off by default:** a click popover is a non-modal, click-outside-dismissable layer, so its panel is rendered role-neutral (the slot content owns its own ARIA role — e.g. a `role="menu"`) and carries NO `aria-modal`. Set `modal` for a genuinely modal dialog popover: the panel then gets `role="dialog"` + `aria-modal="true"`. **Note:** Popover ships no focus trap (it stays a minimal headless primitive); if you set `modal`, provide your own focus containment so the `aria-modal` claim holds. Ignored for `hover`/`focus` triggers (always tooltip-flavored).
|
|
110
|
-
*/
|
|
111
|
-
@property({ type: Boolean, reflect: true }) modal: boolean = false;
|
|
112
|
-
/**
|
|
113
|
-
* Floating UI positioning strategy — 'absolute' (default) or 'fixed'. Use 'fixed' to escape a scrollable/overflow-clipping ancestor (e.g. a sticky table header). Reconciled at runtime.
|
|
114
|
-
*/
|
|
115
|
-
@property({ type: String, reflect: true }) strategy: string = 'absolute';
|
|
116
|
-
@query('[data-rozie-ref="anchorEl"]') private _refAnchorEl!: HTMLElement;
|
|
117
|
-
@query('[data-rozie-ref="floatingEl"]') private _refFloatingEl!: HTMLElement;
|
|
118
|
-
@query('[data-rozie-ref="arrowEl"]') private _refArrowEl!: HTMLElement;
|
|
119
|
-
private __rozieWatchInitial_0 = true;
|
|
120
|
-
private __rozieFirstUpdateDone = false;
|
|
121
|
-
|
|
122
|
-
@state() private _hasSlotAnchor = false;
|
|
123
|
-
@queryAssignedElements({ slot: 'anchor', flatten: true }) private _slotAnchorElements!: Element[];
|
|
124
|
-
@property({ attribute: false }) anchor?: (scope: { open: unknown; toggle: unknown; show: unknown; hide: unknown }) => unknown;
|
|
125
|
-
@state() private _hasSlotDefault = false;
|
|
126
|
-
@queryAssignedElements({ flatten: true }) private _slotDefaultElements!: Element[];
|
|
127
|
-
|
|
128
|
-
private _disconnectCleanups: Array<() => void> = [];
|
|
129
|
-
// Re-parenting guard: set true once the deferred teardown has actually
|
|
130
|
-
// run (a genuine un-mount), so a subsequent reconnect knows to re-arm.
|
|
131
|
-
private _rozieTornDown = false;
|
|
132
|
-
|
|
133
|
-
private _armListeners(): void {
|
|
134
|
-
const _lh0 = ($event: KeyboardEvent) => { if (!(this.open)) return; if ($event.key !== 'Escape') return; ((this.dismiss) as (...args: any[]) => any)($event); };
|
|
135
|
-
document.addEventListener('keydown', _lh0, undefined);
|
|
136
|
-
this._disconnectCleanups.push(() => document.removeEventListener('keydown', _lh0, undefined));
|
|
137
|
-
|
|
138
|
-
const _u1 = attachOutsideClickListener([() => this._refAnchorEl, () => this._refFloatingEl], ($event) => { ((this.dismiss) as (...args: any[]) => any)($event); }, () => (this.open));
|
|
139
|
-
this._disconnectCleanups.push(_u1);
|
|
140
|
-
|
|
141
|
-
{
|
|
142
|
-
const slotEl = this.shadowRoot?.querySelector('slot[name="anchor"]');
|
|
143
|
-
if (slotEl !== null && slotEl !== undefined) {
|
|
144
|
-
const update = () => { this._hasSlotAnchor = this._slotAnchorElements.length > 0; };
|
|
145
|
-
slotEl.addEventListener('slotchange', update);
|
|
146
|
-
// CR-05 fix: push cleanup so the listener is removed on disconnectedCallback.
|
|
147
|
-
this._disconnectCleanups.push(() => slotEl.removeEventListener('slotchange', update));
|
|
148
|
-
update();
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
{
|
|
153
|
-
const slotEl = this.shadowRoot?.querySelector('slot:not([name])');
|
|
154
|
-
if (slotEl !== null && slotEl !== undefined) {
|
|
155
|
-
const update = () => { this._hasSlotDefault = this._slotDefaultElements.length > 0; };
|
|
156
|
-
slotEl.addEventListener('slotchange', update);
|
|
157
|
-
// CR-05 fix: push cleanup so the listener is removed on disconnectedCallback.
|
|
158
|
-
this._disconnectCleanups.push(() => slotEl.removeEventListener('slotchange', update));
|
|
159
|
-
update();
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
connectedCallback(): void {
|
|
165
|
-
// Phase 07.3.1 D-LIT-15 — pre-seed _hasSlot<X> from light DOM so first render isn't deadlocked.
|
|
166
|
-
this._hasSlotAnchor = Array.from(this.children).some((el) => el.getAttribute('slot') === 'anchor');
|
|
167
|
-
this._hasSlotDefault = Array.from(this.children).some((el) => !el.hasAttribute('slot') && (el.nodeType !== 3 || (el.textContent?.trim().length ?? 0) > 0));
|
|
168
|
-
super.connectedCallback();
|
|
169
|
-
if (this.hasUpdated && this._rozieTornDown) { this._rozieTornDown = false; this._armListeners(); }
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
firstUpdated(): void {
|
|
173
|
-
this._armListeners();
|
|
174
|
-
|
|
175
|
-
this._disconnectCleanups.push((() => {
|
|
176
|
-
this.stopTracking();
|
|
177
|
-
}));
|
|
178
|
-
|
|
179
|
-
this._disconnectCleanups.push(effect(() => { const __watchVal = (() => this.open)(); untracked(() => { if (this.__rozieWatchInitial_0) { this.__rozieWatchInitial_0 = false; return; } ((isOpen: any) => {
|
|
180
|
-
if (isOpen && !this.disabled) {
|
|
181
|
-
queueMicrotask(() => {
|
|
182
|
-
if (!this.open || this.disabled) return;
|
|
183
|
-
this.floatingNode = this._refFloatingEl;
|
|
184
|
-
this.arrowNode = this._refArrowEl;
|
|
185
|
-
this.startTracking();
|
|
186
|
-
});
|
|
187
|
-
} else {
|
|
188
|
-
this.stopTracking();
|
|
189
|
-
}
|
|
190
|
-
})(__watchVal); }); }));
|
|
191
|
-
|
|
192
|
-
// $refs read ONLY here (ROZ123). The floating + arrow elements live behind r-if
|
|
193
|
-
// and may be null until open; startTracking re-reads via the watch path.
|
|
194
|
-
this.anchorNode = this._refAnchorEl;
|
|
195
|
-
if (this.open && !this.disabled) {
|
|
196
|
-
// floatingNode is populated by its r-if having rendered; read it lazily inside
|
|
197
|
-
// the watch/handlers too. Position on next tick when it exists.
|
|
198
|
-
this.floatingNode = this._refFloatingEl;
|
|
199
|
-
this.arrowNode = this._refArrowEl;
|
|
200
|
-
this.startTracking();
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
updated(changedProperties: Map<string, unknown>): void {
|
|
205
|
-
if (this.__rozieFirstUpdateDone && (changedProperties.has('placement'))) { const __watchVal = (() => this.placement)(); (() => {
|
|
206
|
-
if (this.open) this.position();
|
|
207
|
-
})(); }
|
|
208
|
-
if (this.__rozieFirstUpdateDone && (changedProperties.has('offset'))) { const __watchVal = (() => this.offset)(); (() => {
|
|
209
|
-
if (this.open) this.position();
|
|
210
|
-
})(); }
|
|
211
|
-
if (this.__rozieFirstUpdateDone && (changedProperties.has('disableFlip'))) { const __watchVal = (() => this.disableFlip)(); (() => {
|
|
212
|
-
if (this.open) this.position();
|
|
213
|
-
})(); }
|
|
214
|
-
if (this.__rozieFirstUpdateDone && (changedProperties.has('disableShift'))) { const __watchVal = (() => this.disableShift)(); (() => {
|
|
215
|
-
if (this.open) this.position();
|
|
216
|
-
})(); }
|
|
217
|
-
if (this.__rozieFirstUpdateDone && (changedProperties.has('strategy'))) { const __watchVal = (() => this.strategy)(); (() => {
|
|
218
|
-
if (this.open) this.position();
|
|
219
|
-
})(); }
|
|
220
|
-
this.__rozieFirstUpdateDone = true;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
disconnectedCallback(): void {
|
|
224
|
-
super.disconnectedCallback();
|
|
225
|
-
queueMicrotask(() => {
|
|
226
|
-
if (this.isConnected || this._rozieTornDown) return;
|
|
227
|
-
this._rozieTornDown = true;
|
|
228
|
-
for (const fn of this._disconnectCleanups) fn();
|
|
229
|
-
this._disconnectCleanups = [];
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
attributeChangedCallback(name: string, old: string | null, value: string | null): void {
|
|
234
|
-
super.attributeChangedCallback(name, old, value);
|
|
235
|
-
if (name === 'open') this._openControllable.notifyAttributeChange(value !== null);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
render() {
|
|
239
|
-
return html`
|
|
240
|
-
<div class="rozie-popover" ${rozieSpread(this.$attrs)} ${rozieListeners(this.$listeners)} data-rozie-s-c6cf02ea>
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
<div class="rozie-popover-anchor" aria-haspopup="dialog" aria-expanded=${!!this.open} aria-describedby=${rozieAttr(this.isTooltip() && this.open ? 'rozie-popover-floating' : null)} @click=${($event: MouseEvent & { currentTarget: HTMLDivElement; target: HTMLDivElement }) => { this.trigger === 'click' && this.onAnchorClick(); }} @pointerenter=${($event: Event & { currentTarget: HTMLDivElement; target: HTMLDivElement }) => { this.trigger === 'hover' && this.onAnchorPointerEnter(); }} @pointerleave=${($event: Event & { currentTarget: HTMLDivElement; target: HTMLDivElement }) => { this.trigger === 'hover' && this.onAnchorPointerLeave(); }} @focusin=${($event: Event & { currentTarget: HTMLDivElement; target: HTMLDivElement }) => { this.trigger === 'focus' && this.onAnchorFocus(); }} @focusout=${($event: Event & { currentTarget: HTMLDivElement; target: HTMLDivElement }) => { this.trigger === 'focus' && this.onAnchorBlur(); }} data-rozie-ref="anchorEl" data-rozie-s-c6cf02ea>
|
|
244
|
-
${this.anchor !== undefined ? this.anchor({open: this.open, toggle: this.toggle, show: this.show, hide: this.hide}) : html`<slot name="anchor" data-rozie-params=${(() => { try { return JSON.stringify({open: this.open}); } catch { return '{}'; } })()} @rozie-anchor-toggle=${($event: CustomEvent) => ((this.toggle) as (...args: any[]) => any)($event.detail)} @rozie-anchor-show=${($event: CustomEvent) => ((this.show) as (...args: any[]) => any)($event.detail)} @rozie-anchor-hide=${($event: CustomEvent) => ((this.hide) as (...args: any[]) => any)($event.detail)}></slot>`}
|
|
245
|
-
</div>
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
${this.open && !this.disabled ? html`<div class="rozie-popover-floating" id="rozie-popover-floating" role=${rozieAttr(this.floatingRole())} aria-modal=${!!(this.floatingRole() === 'dialog')} data-rozie-ref="floatingEl" data-rozie-s-c6cf02ea>
|
|
249
|
-
${this.arrow ? html`<div class="rozie-popover-arrow" data-rozie-ref="arrowEl" data-rozie-s-c6cf02ea></div>` : nothing}<slot></slot>
|
|
250
|
-
</div>` : nothing}</div>
|
|
251
|
-
`;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
anchorNode: any = null;
|
|
255
|
-
|
|
256
|
-
floatingNode: any = null;
|
|
257
|
-
|
|
258
|
-
arrowNode: any = null;
|
|
259
|
-
|
|
260
|
-
stopAutoUpdate: any = null;
|
|
261
|
-
|
|
262
|
-
lastFocusedEl: any = null;
|
|
263
|
-
|
|
264
|
-
deepActiveElement = () => {
|
|
265
|
-
let el = document.activeElement;
|
|
266
|
-
while (el && el.shadowRoot && el.shadowRoot.activeElement) {
|
|
267
|
-
el = el.shadowRoot.activeElement;
|
|
268
|
-
}
|
|
269
|
-
return el;
|
|
270
|
-
};
|
|
271
|
-
|
|
272
|
-
requestOpen = (next: any) => {
|
|
273
|
-
if (this.open === next) return;
|
|
274
|
-
if (next && this.trigger === 'click') {
|
|
275
|
-
this.lastFocusedEl = this.deepActiveElement();
|
|
276
|
-
}
|
|
277
|
-
this._openControllable.write(next);
|
|
278
|
-
this.dispatchEvent(new CustomEvent("change", {
|
|
279
|
-
detail: next,
|
|
280
|
-
bubbles: true,
|
|
281
|
-
composed: true
|
|
282
|
-
}));
|
|
283
|
-
if (!next && this.trigger === 'click' && this.lastFocusedEl && this.lastFocusedEl.isConnected && typeof this.lastFocusedEl.focus === 'function') {
|
|
284
|
-
this.lastFocusedEl.focus();
|
|
285
|
-
}
|
|
286
|
-
if (!next) {
|
|
287
|
-
this.lastFocusedEl = null;
|
|
288
|
-
}
|
|
289
|
-
};
|
|
290
|
-
|
|
291
|
-
applyPosition = (x: any, y: any, middlewareData: any) => {
|
|
292
|
-
if (!this.floatingNode) return;
|
|
293
|
-
this.floatingNode.style.left = x + 'px';
|
|
294
|
-
this.floatingNode.style.top = y + 'px';
|
|
295
|
-
if (this.arrowNode && middlewareData && middlewareData.arrow) {
|
|
296
|
-
const ax = middlewareData.arrow.x;
|
|
297
|
-
const ay = middlewareData.arrow.y;
|
|
298
|
-
this.arrowNode.style.left = ax == null ? '' : ax + 'px';
|
|
299
|
-
this.arrowNode.style.top = ay == null ? '' : ay + 'px';
|
|
300
|
-
}
|
|
301
|
-
};
|
|
302
|
-
|
|
303
|
-
position = () => {
|
|
304
|
-
if (!this.anchorNode || !this.floatingNode) return;
|
|
305
|
-
const middleware = buildMiddleware({
|
|
306
|
-
offset: offsetMiddleware,
|
|
307
|
-
flip,
|
|
308
|
-
shift,
|
|
309
|
-
arrow: arrowMiddleware
|
|
310
|
-
}, {
|
|
311
|
-
offset: this.offset,
|
|
312
|
-
disableFlip: this.disableFlip,
|
|
313
|
-
disableShift: this.disableShift,
|
|
314
|
-
arrow: this.arrow,
|
|
315
|
-
arrowEl: this.arrowNode
|
|
316
|
-
});
|
|
317
|
-
// 'fixed' inline position MUST be written before computePosition measures the
|
|
318
|
-
// floating element's offset parent (fixed vs absolute changes the containing
|
|
319
|
-
// block). Default 'absolute' explicitly CLEARS any inline position instead of
|
|
320
|
-
// writing `position: absolute` — so a never-fixed popover still writes no
|
|
321
|
-
// visible inline position (byte-identical-off preserved: `style.position = ''`
|
|
322
|
-
// is a no-op when the property was never set), while a live `strategy`
|
|
323
|
-
// reconcile (fixed → absolute, see the $watch below) correctly resets the
|
|
324
|
-
// stale inline `fixed` so the stylesheet's `position: absolute` rule re-takes
|
|
325
|
-
// over instead of positioning `fixed` with absolute-computed coordinates
|
|
326
|
-
// (72-REVIEW.md WR-01).
|
|
327
|
-
if (this.strategy === 'fixed') {
|
|
328
|
-
this.floatingNode.style.position = 'fixed';
|
|
329
|
-
} else {
|
|
330
|
-
this.floatingNode.style.position = '';
|
|
331
|
-
}
|
|
332
|
-
let opts: any = null;
|
|
333
|
-
opts = {
|
|
334
|
-
placement: this.placement,
|
|
335
|
-
strategy: this.strategy,
|
|
336
|
-
middleware
|
|
337
|
-
};
|
|
338
|
-
computePosition(this.anchorNode, this.floatingNode, opts).then((result: any) => {
|
|
339
|
-
this.applyPosition(result.x, result.y, result.middlewareData);
|
|
340
|
-
});
|
|
341
|
-
};
|
|
342
|
-
|
|
343
|
-
startTracking = () => {
|
|
344
|
-
if (!this.anchorNode || !this.floatingNode) return;
|
|
345
|
-
if (this.stopAutoUpdate) {
|
|
346
|
-
this.stopAutoUpdate();
|
|
347
|
-
this.stopAutoUpdate = null;
|
|
348
|
-
}
|
|
349
|
-
this.stopAutoUpdate = autoUpdate(this.anchorNode, this.floatingNode, this.position);
|
|
350
|
-
};
|
|
351
|
-
|
|
352
|
-
stopTracking = () => {
|
|
353
|
-
if (this.stopAutoUpdate) {
|
|
354
|
-
this.stopAutoUpdate();
|
|
355
|
-
this.stopAutoUpdate = null;
|
|
356
|
-
}
|
|
357
|
-
};
|
|
358
|
-
|
|
359
|
-
onAnchorClick = () => {
|
|
360
|
-
if (this.disabled) return;
|
|
361
|
-
this.requestOpen(!this.open);
|
|
362
|
-
};
|
|
363
|
-
|
|
364
|
-
onAnchorPointerEnter = () => {
|
|
365
|
-
if (this.disabled) return;
|
|
366
|
-
this.requestOpen(true);
|
|
367
|
-
};
|
|
368
|
-
|
|
369
|
-
onAnchorPointerLeave = () => {
|
|
370
|
-
if (this.disabled) return;
|
|
371
|
-
this.requestOpen(false);
|
|
372
|
-
};
|
|
373
|
-
|
|
374
|
-
onAnchorFocus = () => {
|
|
375
|
-
if (this.disabled) return;
|
|
376
|
-
this.requestOpen(true);
|
|
377
|
-
};
|
|
378
|
-
|
|
379
|
-
onAnchorBlur = () => {
|
|
380
|
-
if (this.disabled) return;
|
|
381
|
-
this.requestOpen(false);
|
|
382
|
-
};
|
|
383
|
-
|
|
384
|
-
dismiss = () => {
|
|
385
|
-
this.requestOpen(false);
|
|
386
|
-
};
|
|
387
|
-
|
|
388
|
-
isTooltip = () => this.trigger === 'hover' || this.trigger === 'focus';
|
|
389
|
-
|
|
390
|
-
floatingRole = () => this.isTooltip() ? 'tooltip' : this.modal ? 'dialog' : undefined;
|
|
391
|
-
|
|
392
|
-
show() {
|
|
393
|
-
if (!this.disabled) this.requestOpen(true);
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
hide() {
|
|
397
|
-
this.requestOpen(false);
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
toggle() {
|
|
401
|
-
if (!this.disabled) this.requestOpen(!this.open);
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
reposition() {
|
|
405
|
-
this.position();
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
get open(): boolean { return this._openControllable.read(); }
|
|
409
|
-
set open(v: boolean) { this._openControllable.notifyPropertyWrite(v); }
|
|
410
|
-
|
|
411
|
-
/**
|
|
412
|
-
* Plan 14-05 — cross-framework attribute fallthrough source. Reads the
|
|
413
|
-
* host custom element's attributes on each call so a consumer-side bound
|
|
414
|
-
* attribute flows through on every render. The `rozieSpread` directive
|
|
415
|
-
* (D-02) does the cross-render diff downstream.
|
|
416
|
-
*
|
|
417
|
-
* Phase 15 follow-up Bug A — declared-prop attribute names are filtered
|
|
418
|
-
* out so `$attrs` returns "rest after declared props" (semantic parity
|
|
419
|
-
* with React/Vue/Svelte/Solid/Angular). Both Lit attribute-naming
|
|
420
|
-
* forms are folded into the skip set: kebab-case for model props
|
|
421
|
-
* (explicit `attribute:`) AND lowercased property name (Lit's default).
|
|
422
|
-
*/
|
|
423
|
-
private get $attrs(): Record<string, string> {
|
|
424
|
-
const __skip = new Set<string>(['open', 'placement', 'trigger', 'offset', 'disable-flip', 'disableflip', 'disable-shift', 'disableshift', 'arrow', 'disabled', 'modal', 'strategy']);
|
|
425
|
-
const out: Record<string, string> = {};
|
|
426
|
-
for (const a of Array.from(this.attributes)) {
|
|
427
|
-
if (__skip.has(a.name)) continue;
|
|
428
|
-
out[a.name] = a.value;
|
|
429
|
-
}
|
|
430
|
-
return out;
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
/**
|
|
434
|
-
* Phase 15 D-19 — consumer-passed listener cluster placeholder.
|
|
435
|
-
* Lit attaches event listeners directly on the host element via
|
|
436
|
-
* `addEventListener` (no per-instance prop rest binding), so the
|
|
437
|
-
* runtime value is undefined; the `rozieListeners` directive's
|
|
438
|
-
* nullish coercion (`obj ?? {}`) handles the no-op cleanly.
|
|
439
|
-
* The declaration exists to satisfy `tsc --noEmit` on consumer
|
|
440
|
-
* projects with strict mode — bare `$listeners` in `render()`
|
|
441
|
-
* would otherwise raise TS2304 (Cannot find name).
|
|
442
|
-
*/
|
|
443
|
-
private get $listeners(): Record<string, EventListener> | undefined {
|
|
444
|
-
return undefined;
|
|
445
|
-
}
|
|
446
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* middleware.ts — pure construction of the Floating UI middleware stack from the
|
|
3
|
-
* Popover's first-class props.
|
|
4
|
-
*
|
|
5
|
-
* This is the one piece of BRANCHY engine glue in Popover.rozie (offset always on,
|
|
6
|
-
* flip/shift opt-out via `disableFlip`/`disableShift`, arrow opt-in via `arrow` +
|
|
7
|
-
* an element). Extracted to `src/internal/` so it can be unit-tested in isolation
|
|
8
|
-
* (codegen vendors `src/internal/` into every leaf via copyInternal, excluding
|
|
9
|
-
* `*.test.ts`).
|
|
10
|
-
*
|
|
11
|
-
* It takes the Floating UI middleware FACTORIES as arguments (rather than importing
|
|
12
|
-
* `@floating-ui/dom` itself) so the unit test can pass lightweight stand-ins and
|
|
13
|
-
* assert ORDER + presence without pulling the real engine — and so the vendored
|
|
14
|
-
* copy in each leaf has zero engine import of its own (the leaf's Popover.* owns
|
|
15
|
-
* the single engine import).
|
|
16
|
-
*
|
|
17
|
-
* Floating UI ordering contract: offset → flip → shift → arrow. `offset` first so
|
|
18
|
-
* the gap is measured before collision detection; `arrow` last so it reads the
|
|
19
|
-
* final resolved placement.
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
|
-
export interface MiddlewareFactories {
|
|
23
|
-
offset: (value: number) => unknown;
|
|
24
|
-
flip: () => unknown;
|
|
25
|
-
shift: () => unknown;
|
|
26
|
-
arrow: (opts: { element: Element }) => unknown;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface MiddlewareConfig {
|
|
30
|
-
offset: number;
|
|
31
|
-
disableFlip: boolean;
|
|
32
|
-
disableShift: boolean;
|
|
33
|
-
arrow: boolean;
|
|
34
|
-
arrowEl: Element | null;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Build the ordered middleware array for `computePosition`. Pure — no engine
|
|
39
|
-
* import, no DOM read beyond the passed-in arrow element.
|
|
40
|
-
*/
|
|
41
|
-
export function buildMiddleware(
|
|
42
|
-
factories: MiddlewareFactories,
|
|
43
|
-
config: MiddlewareConfig,
|
|
44
|
-
): unknown[] {
|
|
45
|
-
const mw: unknown[] = [factories.offset(config.offset)];
|
|
46
|
-
if (!config.disableFlip) mw.push(factories.flip());
|
|
47
|
-
if (!config.disableShift) mw.push(factories.shift());
|
|
48
|
-
// The arrow middleware needs a real element to position; opt-in only when both
|
|
49
|
-
// the `arrow` prop is set AND the arrow element has mounted.
|
|
50
|
-
if (config.arrow && config.arrowEl) {
|
|
51
|
-
mw.push(factories.arrow({ element: config.arrowEl }));
|
|
52
|
-
}
|
|
53
|
-
return mw;
|
|
54
|
-
}
|