@rozie-ui/data-table-solid 0.1.1 → 0.2.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 +1 -1
- package/dist/index.cjs +29 -281
- package/dist/index.mjs +4 -279
- package/package.json +4 -6
- package/src/Column.tsx +1 -1
- package/src/DataTable.tsx +6 -12
- package/src/EditorSelect.tsx +1 -1
- package/src/FilterSelect.tsx +1 -1
- package/src/GroupBar.tsx +1 -1
- package/src/Popover.tsx +0 -416
- package/src/internal/middleware.ts +0 -54
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ Idiomatic **solid** `DataTable` — a headless, fully-accessible (WAI-ARIA) data
|
|
|
8
8
|
npm i @rozie-ui/data-table-solid
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
Peer dependencies: `solid-js + @tanstack/table-core + @tanstack/virtual-core + @
|
|
11
|
+
Peer dependencies: `solid-js + @tanstack/table-core + @tanstack/virtual-core + @rozie-ui/popover-solid`. Install them alongside this package.
|
|
12
12
|
|
|
13
13
|
## Usage
|
|
14
14
|
|
package/dist/index.cjs
CHANGED
|
@@ -2,286 +2,35 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
+
//#region \0rolldown/runtime.js
|
|
6
|
+
var __create = Object.create;
|
|
7
|
+
var __defProp = Object.defineProperty;
|
|
8
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
9
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
10
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
11
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
14
|
+
key = keys[i];
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
16
|
+
get: ((k) => from[k]).bind(null, key),
|
|
17
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
return to;
|
|
21
|
+
};
|
|
22
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
23
|
+
value: mod,
|
|
24
|
+
enumerable: true
|
|
25
|
+
}) : target, mod));
|
|
26
|
+
//#endregion
|
|
5
27
|
let Solid_js = require("solid-js");
|
|
6
28
|
let _solid_primitives_keyed = require("@solid-primitives/keyed");
|
|
7
29
|
let _rozie_runtime_solid = require("@rozie/runtime-solid");
|
|
8
|
-
let
|
|
30
|
+
let _rozie_ui_popover_solid = require("@rozie-ui/popover-solid");
|
|
31
|
+
_rozie_ui_popover_solid = __toESM(_rozie_ui_popover_solid);
|
|
9
32
|
let _tanstack_table_core = require("@tanstack/table-core");
|
|
10
33
|
let _tanstack_virtual_core = require("@tanstack/virtual-core");
|
|
11
|
-
//#region src/internal/middleware.ts
|
|
12
|
-
/**
|
|
13
|
-
* Build the ordered middleware array for `computePosition`. Pure — no engine
|
|
14
|
-
* import, no DOM read beyond the passed-in arrow element.
|
|
15
|
-
*/
|
|
16
|
-
function buildMiddleware(factories, config) {
|
|
17
|
-
const mw = [factories.offset(config.offset)];
|
|
18
|
-
if (!config.disableFlip) mw.push(factories.flip());
|
|
19
|
-
if (!config.disableShift) mw.push(factories.shift());
|
|
20
|
-
if (config.arrow && config.arrowEl) mw.push(factories.arrow({ element: config.arrowEl }));
|
|
21
|
-
return mw;
|
|
22
|
-
}
|
|
23
|
-
//#endregion
|
|
24
|
-
//#region src/Popover.tsx
|
|
25
|
-
(0, _rozie_runtime_solid.__rozieInjectStyle)("Popover-c6cf02ea", `.rozie-popover[data-rozie-s-c6cf02ea] {
|
|
26
|
-
display: contents;
|
|
27
|
-
}
|
|
28
|
-
.rozie-popover-anchor[data-rozie-s-c6cf02ea] {
|
|
29
|
-
display: inline-block;
|
|
30
|
-
}
|
|
31
|
-
.rozie-popover-floating[data-rozie-s-c6cf02ea] {
|
|
32
|
-
position: absolute;
|
|
33
|
-
left: 0;
|
|
34
|
-
top: 0;
|
|
35
|
-
z-index: var(--rozie-popover-z, 1000);
|
|
36
|
-
width: max-content;
|
|
37
|
-
max-width: var(--rozie-popover-max-width, calc(100vw - 16px));
|
|
38
|
-
background: var(--rozie-popover-bg, #fff);
|
|
39
|
-
color: var(--rozie-popover-color, inherit);
|
|
40
|
-
border: var(--rozie-popover-border, 1px solid rgba(0, 0, 0, 0.12));
|
|
41
|
-
border-radius: var(--rozie-popover-radius, 8px);
|
|
42
|
-
box-shadow: var(--rozie-popover-shadow, 0 8px 24px rgba(0, 0, 0, 0.12));
|
|
43
|
-
padding: var(--rozie-popover-padding, 8px 12px);
|
|
44
|
-
}
|
|
45
|
-
.rozie-popover-arrow[data-rozie-s-c6cf02ea] {
|
|
46
|
-
position: absolute;
|
|
47
|
-
width: var(--rozie-popover-arrow-size, 8px);
|
|
48
|
-
height: var(--rozie-popover-arrow-size, 8px);
|
|
49
|
-
background: var(--rozie-popover-bg, #fff);
|
|
50
|
-
border: var(--rozie-popover-border, 1px solid rgba(0, 0, 0, 0.12));
|
|
51
|
-
transform: rotate(45deg);
|
|
52
|
-
}`);
|
|
53
|
-
function Popover(_props) {
|
|
54
|
-
const [local, attrs] = (0, Solid_js.splitProps)((0, Solid_js.mergeProps)({
|
|
55
|
-
placement: "bottom",
|
|
56
|
-
trigger: "click",
|
|
57
|
-
offset: 8,
|
|
58
|
-
disableFlip: false,
|
|
59
|
-
disableShift: false,
|
|
60
|
-
arrow: false,
|
|
61
|
-
disabled: false,
|
|
62
|
-
modal: false,
|
|
63
|
-
strategy: "absolute"
|
|
64
|
-
}, _props), [
|
|
65
|
-
"open",
|
|
66
|
-
"placement",
|
|
67
|
-
"trigger",
|
|
68
|
-
"offset",
|
|
69
|
-
"disableFlip",
|
|
70
|
-
"disableShift",
|
|
71
|
-
"arrow",
|
|
72
|
-
"disabled",
|
|
73
|
-
"modal",
|
|
74
|
-
"strategy",
|
|
75
|
-
"children",
|
|
76
|
-
"ref"
|
|
77
|
-
]);
|
|
78
|
-
const resolved = (0, Solid_js.children)(() => local.children);
|
|
79
|
-
(0, Solid_js.onMount)(() => {
|
|
80
|
-
local.ref?.({
|
|
81
|
-
show,
|
|
82
|
-
hide,
|
|
83
|
-
toggle,
|
|
84
|
-
reposition
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
const [open, setOpen] = (0, _rozie_runtime_solid.createControllableSignal)(_props, "open", false);
|
|
88
|
-
(0, Solid_js.onMount)(() => {
|
|
89
|
-
const _cleanup = (() => {
|
|
90
|
-
anchorNode = anchorElRef;
|
|
91
|
-
if (open() && !local.disabled) {
|
|
92
|
-
floatingNode = floatingElRef;
|
|
93
|
-
arrowNode = arrowElRef;
|
|
94
|
-
startTracking();
|
|
95
|
-
}
|
|
96
|
-
})();
|
|
97
|
-
if (_cleanup) (0, Solid_js.onCleanup)(_cleanup);
|
|
98
|
-
(0, Solid_js.onCleanup)(() => {
|
|
99
|
-
stopTracking();
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
(0, Solid_js.createEffect)((0, Solid_js.on)(() => open(), (v) => (0, Solid_js.untrack)(() => ((isOpen) => {
|
|
103
|
-
if (isOpen && !local.disabled) queueMicrotask(() => {
|
|
104
|
-
if (!open() || local.disabled) return;
|
|
105
|
-
floatingNode = floatingElRef;
|
|
106
|
-
arrowNode = arrowElRef;
|
|
107
|
-
startTracking();
|
|
108
|
-
});
|
|
109
|
-
else stopTracking();
|
|
110
|
-
})(v)), { defer: true }));
|
|
111
|
-
(0, Solid_js.createEffect)((0, Solid_js.on)(() => local.placement, (v) => (0, Solid_js.untrack)(() => (() => {
|
|
112
|
-
if (open()) position();
|
|
113
|
-
})()), { defer: true }));
|
|
114
|
-
(0, Solid_js.createEffect)((0, Solid_js.on)(() => local.offset, (v) => (0, Solid_js.untrack)(() => (() => {
|
|
115
|
-
if (open()) position();
|
|
116
|
-
})()), { defer: true }));
|
|
117
|
-
(0, Solid_js.createEffect)((0, Solid_js.on)(() => local.disableFlip, (v) => (0, Solid_js.untrack)(() => (() => {
|
|
118
|
-
if (open()) position();
|
|
119
|
-
})()), { defer: true }));
|
|
120
|
-
(0, Solid_js.createEffect)((0, Solid_js.on)(() => local.disableShift, (v) => (0, Solid_js.untrack)(() => (() => {
|
|
121
|
-
if (open()) position();
|
|
122
|
-
})()), { defer: true }));
|
|
123
|
-
(0, Solid_js.createEffect)((0, Solid_js.on)(() => local.strategy, (v) => (0, Solid_js.untrack)(() => (() => {
|
|
124
|
-
if (open()) position();
|
|
125
|
-
})()), { defer: true }));
|
|
126
|
-
let anchorElRef = null;
|
|
127
|
-
let floatingElRef = null;
|
|
128
|
-
let arrowElRef = null;
|
|
129
|
-
let anchorNode = null;
|
|
130
|
-
let floatingNode = null;
|
|
131
|
-
let arrowNode = null;
|
|
132
|
-
let stopAutoUpdate = null;
|
|
133
|
-
let lastFocusedEl = null;
|
|
134
|
-
function deepActiveElement() {
|
|
135
|
-
let el = document.activeElement;
|
|
136
|
-
while (el && el.shadowRoot && el.shadowRoot.activeElement) el = el.shadowRoot.activeElement;
|
|
137
|
-
return el;
|
|
138
|
-
}
|
|
139
|
-
function requestOpen(next) {
|
|
140
|
-
if (open() === next) return;
|
|
141
|
-
if (next && local.trigger === "click") lastFocusedEl = deepActiveElement();
|
|
142
|
-
setOpen(next);
|
|
143
|
-
_props.onChange?.(next);
|
|
144
|
-
if (!next && local.trigger === "click" && lastFocusedEl && lastFocusedEl.isConnected && typeof lastFocusedEl.focus === "function") lastFocusedEl.focus();
|
|
145
|
-
if (!next) lastFocusedEl = null;
|
|
146
|
-
}
|
|
147
|
-
function applyPosition(x, y, middlewareData) {
|
|
148
|
-
if (!floatingNode) return;
|
|
149
|
-
floatingNode.style.left = x + "px";
|
|
150
|
-
floatingNode.style.top = y + "px";
|
|
151
|
-
if (arrowNode && middlewareData && middlewareData.arrow) {
|
|
152
|
-
const ax = middlewareData.arrow.x;
|
|
153
|
-
const ay = middlewareData.arrow.y;
|
|
154
|
-
arrowNode.style.left = ax == null ? "" : ax + "px";
|
|
155
|
-
arrowNode.style.top = ay == null ? "" : ay + "px";
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
function position() {
|
|
159
|
-
if (!anchorNode || !floatingNode) return;
|
|
160
|
-
const middleware = buildMiddleware({
|
|
161
|
-
offset: _floating_ui_dom.offset,
|
|
162
|
-
flip: _floating_ui_dom.flip,
|
|
163
|
-
shift: _floating_ui_dom.shift,
|
|
164
|
-
arrow: _floating_ui_dom.arrow
|
|
165
|
-
}, {
|
|
166
|
-
offset: local.offset,
|
|
167
|
-
disableFlip: local.disableFlip,
|
|
168
|
-
disableShift: local.disableShift,
|
|
169
|
-
arrow: local.arrow,
|
|
170
|
-
arrowEl: arrowNode
|
|
171
|
-
});
|
|
172
|
-
if (local.strategy === "fixed") floatingNode.style.position = "fixed";
|
|
173
|
-
else floatingNode.style.position = "";
|
|
174
|
-
let opts = null;
|
|
175
|
-
opts = {
|
|
176
|
-
placement: local.placement,
|
|
177
|
-
strategy: local.strategy,
|
|
178
|
-
middleware
|
|
179
|
-
};
|
|
180
|
-
(0, _floating_ui_dom.computePosition)(anchorNode, floatingNode, opts).then((result) => {
|
|
181
|
-
applyPosition(result.x, result.y, result.middlewareData);
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
function startTracking() {
|
|
185
|
-
if (!anchorNode || !floatingNode) return;
|
|
186
|
-
if (stopAutoUpdate) {
|
|
187
|
-
stopAutoUpdate();
|
|
188
|
-
stopAutoUpdate = null;
|
|
189
|
-
}
|
|
190
|
-
stopAutoUpdate = (0, _floating_ui_dom.autoUpdate)(anchorNode, floatingNode, position);
|
|
191
|
-
}
|
|
192
|
-
function stopTracking() {
|
|
193
|
-
if (stopAutoUpdate) {
|
|
194
|
-
stopAutoUpdate();
|
|
195
|
-
stopAutoUpdate = null;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
function onAnchorClick() {
|
|
199
|
-
if (local.disabled) return;
|
|
200
|
-
requestOpen(!open());
|
|
201
|
-
}
|
|
202
|
-
function onAnchorPointerEnter() {
|
|
203
|
-
if (local.disabled) return;
|
|
204
|
-
requestOpen(true);
|
|
205
|
-
}
|
|
206
|
-
function onAnchorPointerLeave() {
|
|
207
|
-
if (local.disabled) return;
|
|
208
|
-
requestOpen(false);
|
|
209
|
-
}
|
|
210
|
-
function onAnchorFocus() {
|
|
211
|
-
if (local.disabled) return;
|
|
212
|
-
requestOpen(true);
|
|
213
|
-
}
|
|
214
|
-
function onAnchorBlur() {
|
|
215
|
-
if (local.disabled) return;
|
|
216
|
-
requestOpen(false);
|
|
217
|
-
}
|
|
218
|
-
function dismiss() {
|
|
219
|
-
requestOpen(false);
|
|
220
|
-
}
|
|
221
|
-
function isTooltip() {
|
|
222
|
-
return local.trigger === "hover" || local.trigger === "focus";
|
|
223
|
-
}
|
|
224
|
-
function floatingRole() {
|
|
225
|
-
return isTooltip() ? "tooltip" : local.modal ? "dialog" : void 0;
|
|
226
|
-
}
|
|
227
|
-
function show() {
|
|
228
|
-
if (!local.disabled) requestOpen(true);
|
|
229
|
-
}
|
|
230
|
-
function hide() {
|
|
231
|
-
requestOpen(false);
|
|
232
|
-
}
|
|
233
|
-
function toggle() {
|
|
234
|
-
if (!local.disabled) requestOpen(!open());
|
|
235
|
-
}
|
|
236
|
-
function reposition() {
|
|
237
|
-
position();
|
|
238
|
-
}
|
|
239
|
-
(0, Solid_js.createEffect)(() => {
|
|
240
|
-
if (!open()) return;
|
|
241
|
-
const _rozieHandler = ($event) => {
|
|
242
|
-
if ($event.key !== "Escape") return;
|
|
243
|
-
dismiss();
|
|
244
|
-
};
|
|
245
|
-
document.addEventListener("keydown", _rozieHandler);
|
|
246
|
-
(0, Solid_js.onCleanup)(() => document.removeEventListener("keydown", _rozieHandler));
|
|
247
|
-
});
|
|
248
|
-
(0, _rozie_runtime_solid.createOutsideClick)([() => anchorElRef, () => floatingElRef], dismiss, () => open());
|
|
249
|
-
return <>
|
|
250
|
-
<div {...attrs} class={"rozie-popover" + (attrs.class ? " " + attrs.class : "")} data-rozie-s-c6cf02ea="">
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
<div aria-haspopup="dialog" aria-expanded={!!open()} aria-describedby={(0, _rozie_runtime_solid.rozieAttr)(isTooltip() && open() ? "rozie-popover-floating" : null)} class={"rozie-popover-anchor"} ref={(el) => {
|
|
254
|
-
anchorElRef = el;
|
|
255
|
-
}} onClick={($event) => {
|
|
256
|
-
local.trigger === "click" && onAnchorClick();
|
|
257
|
-
}} onPointerEnter={($event) => {
|
|
258
|
-
local.trigger === "hover" && onAnchorPointerEnter();
|
|
259
|
-
}} onPointerLeave={($event) => {
|
|
260
|
-
local.trigger === "hover" && onAnchorPointerLeave();
|
|
261
|
-
}} onFocusIn={($event) => {
|
|
262
|
-
local.trigger === "focus" && onAnchorFocus();
|
|
263
|
-
}} onFocusOut={($event) => {
|
|
264
|
-
local.trigger === "focus" && onAnchorBlur();
|
|
265
|
-
}} data-rozie-s-c6cf02ea="">
|
|
266
|
-
{(_props.anchorSlot ?? _props.slots?.["anchor"])?.({
|
|
267
|
-
open: open(),
|
|
268
|
-
toggle,
|
|
269
|
-
show,
|
|
270
|
-
hide
|
|
271
|
-
})}
|
|
272
|
-
</div>
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
{<Solid_js.Show when={open() && !local.disabled}><div class={"rozie-popover-floating"} ref={(el) => {
|
|
276
|
-
floatingElRef = el;
|
|
277
|
-
}} id="rozie-popover-floating" role={(0, _rozie_runtime_solid.rozieAttr)(floatingRole())} aria-modal={!!(floatingRole() === "dialog")} data-rozie-s-c6cf02ea="">
|
|
278
|
-
{<Solid_js.Show when={local.arrow}><div class={"rozie-popover-arrow"} ref={(el) => {
|
|
279
|
-
arrowElRef = el;
|
|
280
|
-
}} data-rozie-s-c6cf02ea="" /></Solid_js.Show>}{resolved()}
|
|
281
|
-
</div></Solid_js.Show>}</div>
|
|
282
|
-
</>;
|
|
283
|
-
}
|
|
284
|
-
//#endregion
|
|
285
34
|
//#region src/DataTable.tsx
|
|
286
35
|
(0, _rozie_runtime_solid.__rozieInjectStyle)("DataTable-d5dcab4c", `.rozie-data-table[data-rozie-s-d5dcab4c] {
|
|
287
36
|
border-collapse: collapse;
|
|
@@ -1723,8 +1472,7 @@ function DataTable(_props) {
|
|
|
1723
1472
|
const base = pinStyle(colId);
|
|
1724
1473
|
if (isExpanderColumn(colId) && row && row.depth) {
|
|
1725
1474
|
const pad = "padding-left:" + (.5 + row.depth * 1.25) + "rem";
|
|
1726
|
-
|
|
1727
|
-
return base ? base + sep + pad : pad;
|
|
1475
|
+
return base ? base + pad : pad;
|
|
1728
1476
|
}
|
|
1729
1477
|
return base;
|
|
1730
1478
|
}
|
|
@@ -3958,7 +3706,7 @@ function DataTable(_props) {
|
|
|
3958
3706
|
}) ?? (0, _rozie_runtime_solid.rozieDisplay)(headerLabel(header().column.id))}
|
|
3959
3707
|
</span>
|
|
3960
3708
|
<span class={"rdt-sort-ind"} aria-hidden="true" data-rozie-s-d5dcab4c="">{(0, _rozie_runtime_solid.rozieDisplay)(sortIndicator(header().column.id))}</span>
|
|
3961
|
-
</button></Solid_js.Show>}<
|
|
3709
|
+
</button></Solid_js.Show>}<_rozie_ui_popover_solid.default trigger="click" placement="bottom-end" strategy="fixed" offset={4} data-rozie-s-d5dcab4c="" anchorSlot={() => <>
|
|
3962
3710
|
<button type="button" class={"rdt-col-menu-trigger"} aria-label={(0, _rozie_runtime_solid.rozieAttr)("Column options for " + headerLabel(header().column.id))} data-rozie-s-d5dcab4c="">⋯</button>
|
|
3963
3711
|
</>}><div class={"rdt-col-menu"} role="menu" data-rozie-s-d5dcab4c="">
|
|
3964
3712
|
<button type="button" role="menuitem" aria-pressed={columnPinSide(header().column.id) === "left"} class={"rdt-col-menu-item"} onClick={($event) => {
|
|
@@ -3974,7 +3722,7 @@ function DataTable(_props) {
|
|
|
3974
3722
|
<button type="button" role="menuitem" class={"rdt-col-menu-item"} onClick={($event) => {
|
|
3975
3723
|
onHideColumn(header().column.id, $event);
|
|
3976
3724
|
}} data-rozie-s-d5dcab4c="">Hide column</button>
|
|
3977
|
-
</div></
|
|
3725
|
+
</div></_rozie_ui_popover_solid.default>
|
|
3978
3726
|
|
|
3979
3727
|
<button type="button" aria-label={(0, _rozie_runtime_solid.rozieAttr)("Resize " + headerLabel(header().column.id))} class={"rdt-resize-handle"} onPointerDown={($event) => {
|
|
3980
3728
|
onResizeStart(header().column.id, $event);
|
|
@@ -4142,7 +3890,7 @@ function DataTable(_props) {
|
|
|
4142
3890
|
}) ?? (0, _rozie_runtime_solid.rozieDisplay)(headerLabel(header().column.id))}
|
|
4143
3891
|
</span>
|
|
4144
3892
|
<span class={"rdt-sort-ind"} aria-hidden="true" data-rozie-s-d5dcab4c="">{(0, _rozie_runtime_solid.rozieDisplay)(sortIndicator(header().column.id))}</span>
|
|
4145
|
-
</button></Solid_js.Show>}<
|
|
3893
|
+
</button></Solid_js.Show>}<_rozie_ui_popover_solid.default trigger="click" placement="bottom-end" strategy="fixed" offset={4} data-rozie-s-d5dcab4c="" anchorSlot={() => <>
|
|
4146
3894
|
<button type="button" class={"rdt-col-menu-trigger"} aria-label={(0, _rozie_runtime_solid.rozieAttr)("Column options for " + headerLabel(header().column.id))} data-rozie-s-d5dcab4c="">⋯</button>
|
|
4147
3895
|
</>}><div class={"rdt-col-menu"} role="menu" data-rozie-s-d5dcab4c="">
|
|
4148
3896
|
<button type="button" role="menuitem" aria-pressed={columnPinSide(header().column.id) === "left"} class={"rdt-col-menu-item"} onClick={($event) => {
|
|
@@ -4158,7 +3906,7 @@ function DataTable(_props) {
|
|
|
4158
3906
|
<button type="button" role="menuitem" class={"rdt-col-menu-item"} onClick={($event) => {
|
|
4159
3907
|
onHideColumn(header().column.id, $event);
|
|
4160
3908
|
}} data-rozie-s-d5dcab4c="">Hide column</button>
|
|
4161
|
-
</div></
|
|
3909
|
+
</div></_rozie_ui_popover_solid.default>
|
|
4162
3910
|
<button type="button" aria-label={(0, _rozie_runtime_solid.rozieAttr)("Resize " + headerLabel(header().column.id))} class={"rdt-resize-handle"} onPointerDown={($event) => {
|
|
4163
3911
|
onResizeStart(header().column.id, $event);
|
|
4164
3912
|
}} onTouchStart={($event) => {
|
package/dist/index.mjs
CHANGED
|
@@ -1,283 +1,9 @@
|
|
|
1
|
-
import { Show,
|
|
1
|
+
import { Show, createEffect, createSignal, mergeProps, on, onCleanup, onMount, splitProps, untrack, useContext } from "solid-js";
|
|
2
2
|
import { Key } from "@solid-primitives/keyed";
|
|
3
|
-
import { __rozieInjectStyle, createControllableSignal,
|
|
4
|
-
import
|
|
3
|
+
import { __rozieInjectStyle, createControllableSignal, parseInlineStyle, rozieAttr, rozieClass, rozieContext, rozieDisplay } from "@rozie/runtime-solid";
|
|
4
|
+
import Popover from "@rozie-ui/popover-solid";
|
|
5
5
|
import { createTable, getCoreRowModel, getExpandedRowModel, getFacetedMinMaxValues, getFacetedRowModel, getFacetedUniqueValues, getFilteredRowModel, getGroupedRowModel, getPaginationRowModel, getSortedRowModel } from "@tanstack/table-core";
|
|
6
6
|
import { Virtualizer, elementScroll, measureElement, observeElementOffset, observeElementRect } from "@tanstack/virtual-core";
|
|
7
|
-
//#region src/internal/middleware.ts
|
|
8
|
-
/**
|
|
9
|
-
* Build the ordered middleware array for `computePosition`. Pure — no engine
|
|
10
|
-
* import, no DOM read beyond the passed-in arrow element.
|
|
11
|
-
*/
|
|
12
|
-
function buildMiddleware(factories, config) {
|
|
13
|
-
const mw = [factories.offset(config.offset)];
|
|
14
|
-
if (!config.disableFlip) mw.push(factories.flip());
|
|
15
|
-
if (!config.disableShift) mw.push(factories.shift());
|
|
16
|
-
if (config.arrow && config.arrowEl) mw.push(factories.arrow({ element: config.arrowEl }));
|
|
17
|
-
return mw;
|
|
18
|
-
}
|
|
19
|
-
//#endregion
|
|
20
|
-
//#region src/Popover.tsx
|
|
21
|
-
__rozieInjectStyle("Popover-c6cf02ea", `.rozie-popover[data-rozie-s-c6cf02ea] {
|
|
22
|
-
display: contents;
|
|
23
|
-
}
|
|
24
|
-
.rozie-popover-anchor[data-rozie-s-c6cf02ea] {
|
|
25
|
-
display: inline-block;
|
|
26
|
-
}
|
|
27
|
-
.rozie-popover-floating[data-rozie-s-c6cf02ea] {
|
|
28
|
-
position: absolute;
|
|
29
|
-
left: 0;
|
|
30
|
-
top: 0;
|
|
31
|
-
z-index: var(--rozie-popover-z, 1000);
|
|
32
|
-
width: max-content;
|
|
33
|
-
max-width: var(--rozie-popover-max-width, calc(100vw - 16px));
|
|
34
|
-
background: var(--rozie-popover-bg, #fff);
|
|
35
|
-
color: var(--rozie-popover-color, inherit);
|
|
36
|
-
border: var(--rozie-popover-border, 1px solid rgba(0, 0, 0, 0.12));
|
|
37
|
-
border-radius: var(--rozie-popover-radius, 8px);
|
|
38
|
-
box-shadow: var(--rozie-popover-shadow, 0 8px 24px rgba(0, 0, 0, 0.12));
|
|
39
|
-
padding: var(--rozie-popover-padding, 8px 12px);
|
|
40
|
-
}
|
|
41
|
-
.rozie-popover-arrow[data-rozie-s-c6cf02ea] {
|
|
42
|
-
position: absolute;
|
|
43
|
-
width: var(--rozie-popover-arrow-size, 8px);
|
|
44
|
-
height: var(--rozie-popover-arrow-size, 8px);
|
|
45
|
-
background: var(--rozie-popover-bg, #fff);
|
|
46
|
-
border: var(--rozie-popover-border, 1px solid rgba(0, 0, 0, 0.12));
|
|
47
|
-
transform: rotate(45deg);
|
|
48
|
-
}`);
|
|
49
|
-
function Popover(_props) {
|
|
50
|
-
const [local, attrs] = splitProps(mergeProps({
|
|
51
|
-
placement: "bottom",
|
|
52
|
-
trigger: "click",
|
|
53
|
-
offset: 8,
|
|
54
|
-
disableFlip: false,
|
|
55
|
-
disableShift: false,
|
|
56
|
-
arrow: false,
|
|
57
|
-
disabled: false,
|
|
58
|
-
modal: false,
|
|
59
|
-
strategy: "absolute"
|
|
60
|
-
}, _props), [
|
|
61
|
-
"open",
|
|
62
|
-
"placement",
|
|
63
|
-
"trigger",
|
|
64
|
-
"offset",
|
|
65
|
-
"disableFlip",
|
|
66
|
-
"disableShift",
|
|
67
|
-
"arrow",
|
|
68
|
-
"disabled",
|
|
69
|
-
"modal",
|
|
70
|
-
"strategy",
|
|
71
|
-
"children",
|
|
72
|
-
"ref"
|
|
73
|
-
]);
|
|
74
|
-
const resolved = children(() => local.children);
|
|
75
|
-
onMount(() => {
|
|
76
|
-
local.ref?.({
|
|
77
|
-
show,
|
|
78
|
-
hide,
|
|
79
|
-
toggle,
|
|
80
|
-
reposition
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
const [open, setOpen] = createControllableSignal(_props, "open", false);
|
|
84
|
-
onMount(() => {
|
|
85
|
-
const _cleanup = (() => {
|
|
86
|
-
anchorNode = anchorElRef;
|
|
87
|
-
if (open() && !local.disabled) {
|
|
88
|
-
floatingNode = floatingElRef;
|
|
89
|
-
arrowNode = arrowElRef;
|
|
90
|
-
startTracking();
|
|
91
|
-
}
|
|
92
|
-
})();
|
|
93
|
-
if (_cleanup) onCleanup(_cleanup);
|
|
94
|
-
onCleanup(() => {
|
|
95
|
-
stopTracking();
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
createEffect(on(() => open(), (v) => untrack(() => ((isOpen) => {
|
|
99
|
-
if (isOpen && !local.disabled) queueMicrotask(() => {
|
|
100
|
-
if (!open() || local.disabled) return;
|
|
101
|
-
floatingNode = floatingElRef;
|
|
102
|
-
arrowNode = arrowElRef;
|
|
103
|
-
startTracking();
|
|
104
|
-
});
|
|
105
|
-
else stopTracking();
|
|
106
|
-
})(v)), { defer: true }));
|
|
107
|
-
createEffect(on(() => local.placement, (v) => untrack(() => (() => {
|
|
108
|
-
if (open()) position();
|
|
109
|
-
})()), { defer: true }));
|
|
110
|
-
createEffect(on(() => local.offset, (v) => untrack(() => (() => {
|
|
111
|
-
if (open()) position();
|
|
112
|
-
})()), { defer: true }));
|
|
113
|
-
createEffect(on(() => local.disableFlip, (v) => untrack(() => (() => {
|
|
114
|
-
if (open()) position();
|
|
115
|
-
})()), { defer: true }));
|
|
116
|
-
createEffect(on(() => local.disableShift, (v) => untrack(() => (() => {
|
|
117
|
-
if (open()) position();
|
|
118
|
-
})()), { defer: true }));
|
|
119
|
-
createEffect(on(() => local.strategy, (v) => untrack(() => (() => {
|
|
120
|
-
if (open()) position();
|
|
121
|
-
})()), { defer: true }));
|
|
122
|
-
let anchorElRef = null;
|
|
123
|
-
let floatingElRef = null;
|
|
124
|
-
let arrowElRef = null;
|
|
125
|
-
let anchorNode = null;
|
|
126
|
-
let floatingNode = null;
|
|
127
|
-
let arrowNode = null;
|
|
128
|
-
let stopAutoUpdate = null;
|
|
129
|
-
let lastFocusedEl = null;
|
|
130
|
-
function deepActiveElement() {
|
|
131
|
-
let el = document.activeElement;
|
|
132
|
-
while (el && el.shadowRoot && el.shadowRoot.activeElement) el = el.shadowRoot.activeElement;
|
|
133
|
-
return el;
|
|
134
|
-
}
|
|
135
|
-
function requestOpen(next) {
|
|
136
|
-
if (open() === next) return;
|
|
137
|
-
if (next && local.trigger === "click") lastFocusedEl = deepActiveElement();
|
|
138
|
-
setOpen(next);
|
|
139
|
-
_props.onChange?.(next);
|
|
140
|
-
if (!next && local.trigger === "click" && lastFocusedEl && lastFocusedEl.isConnected && typeof lastFocusedEl.focus === "function") lastFocusedEl.focus();
|
|
141
|
-
if (!next) lastFocusedEl = null;
|
|
142
|
-
}
|
|
143
|
-
function applyPosition(x, y, middlewareData) {
|
|
144
|
-
if (!floatingNode) return;
|
|
145
|
-
floatingNode.style.left = x + "px";
|
|
146
|
-
floatingNode.style.top = y + "px";
|
|
147
|
-
if (arrowNode && middlewareData && middlewareData.arrow) {
|
|
148
|
-
const ax = middlewareData.arrow.x;
|
|
149
|
-
const ay = middlewareData.arrow.y;
|
|
150
|
-
arrowNode.style.left = ax == null ? "" : ax + "px";
|
|
151
|
-
arrowNode.style.top = ay == null ? "" : ay + "px";
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
function position() {
|
|
155
|
-
if (!anchorNode || !floatingNode) return;
|
|
156
|
-
const middleware = buildMiddleware({
|
|
157
|
-
offset,
|
|
158
|
-
flip,
|
|
159
|
-
shift,
|
|
160
|
-
arrow
|
|
161
|
-
}, {
|
|
162
|
-
offset: local.offset,
|
|
163
|
-
disableFlip: local.disableFlip,
|
|
164
|
-
disableShift: local.disableShift,
|
|
165
|
-
arrow: local.arrow,
|
|
166
|
-
arrowEl: arrowNode
|
|
167
|
-
});
|
|
168
|
-
if (local.strategy === "fixed") floatingNode.style.position = "fixed";
|
|
169
|
-
else floatingNode.style.position = "";
|
|
170
|
-
let opts = null;
|
|
171
|
-
opts = {
|
|
172
|
-
placement: local.placement,
|
|
173
|
-
strategy: local.strategy,
|
|
174
|
-
middleware
|
|
175
|
-
};
|
|
176
|
-
computePosition(anchorNode, floatingNode, opts).then((result) => {
|
|
177
|
-
applyPosition(result.x, result.y, result.middlewareData);
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
function startTracking() {
|
|
181
|
-
if (!anchorNode || !floatingNode) return;
|
|
182
|
-
if (stopAutoUpdate) {
|
|
183
|
-
stopAutoUpdate();
|
|
184
|
-
stopAutoUpdate = null;
|
|
185
|
-
}
|
|
186
|
-
stopAutoUpdate = autoUpdate(anchorNode, floatingNode, position);
|
|
187
|
-
}
|
|
188
|
-
function stopTracking() {
|
|
189
|
-
if (stopAutoUpdate) {
|
|
190
|
-
stopAutoUpdate();
|
|
191
|
-
stopAutoUpdate = null;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
function onAnchorClick() {
|
|
195
|
-
if (local.disabled) return;
|
|
196
|
-
requestOpen(!open());
|
|
197
|
-
}
|
|
198
|
-
function onAnchorPointerEnter() {
|
|
199
|
-
if (local.disabled) return;
|
|
200
|
-
requestOpen(true);
|
|
201
|
-
}
|
|
202
|
-
function onAnchorPointerLeave() {
|
|
203
|
-
if (local.disabled) return;
|
|
204
|
-
requestOpen(false);
|
|
205
|
-
}
|
|
206
|
-
function onAnchorFocus() {
|
|
207
|
-
if (local.disabled) return;
|
|
208
|
-
requestOpen(true);
|
|
209
|
-
}
|
|
210
|
-
function onAnchorBlur() {
|
|
211
|
-
if (local.disabled) return;
|
|
212
|
-
requestOpen(false);
|
|
213
|
-
}
|
|
214
|
-
function dismiss() {
|
|
215
|
-
requestOpen(false);
|
|
216
|
-
}
|
|
217
|
-
function isTooltip() {
|
|
218
|
-
return local.trigger === "hover" || local.trigger === "focus";
|
|
219
|
-
}
|
|
220
|
-
function floatingRole() {
|
|
221
|
-
return isTooltip() ? "tooltip" : local.modal ? "dialog" : void 0;
|
|
222
|
-
}
|
|
223
|
-
function show() {
|
|
224
|
-
if (!local.disabled) requestOpen(true);
|
|
225
|
-
}
|
|
226
|
-
function hide() {
|
|
227
|
-
requestOpen(false);
|
|
228
|
-
}
|
|
229
|
-
function toggle() {
|
|
230
|
-
if (!local.disabled) requestOpen(!open());
|
|
231
|
-
}
|
|
232
|
-
function reposition() {
|
|
233
|
-
position();
|
|
234
|
-
}
|
|
235
|
-
createEffect(() => {
|
|
236
|
-
if (!open()) return;
|
|
237
|
-
const _rozieHandler = ($event) => {
|
|
238
|
-
if ($event.key !== "Escape") return;
|
|
239
|
-
dismiss();
|
|
240
|
-
};
|
|
241
|
-
document.addEventListener("keydown", _rozieHandler);
|
|
242
|
-
onCleanup(() => document.removeEventListener("keydown", _rozieHandler));
|
|
243
|
-
});
|
|
244
|
-
createOutsideClick([() => anchorElRef, () => floatingElRef], dismiss, () => open());
|
|
245
|
-
return <>
|
|
246
|
-
<div {...attrs} class={"rozie-popover" + (attrs.class ? " " + attrs.class : "")} data-rozie-s-c6cf02ea="">
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
<div aria-haspopup="dialog" aria-expanded={!!open()} aria-describedby={rozieAttr(isTooltip() && open() ? "rozie-popover-floating" : null)} class={"rozie-popover-anchor"} ref={(el) => {
|
|
250
|
-
anchorElRef = el;
|
|
251
|
-
}} onClick={($event) => {
|
|
252
|
-
local.trigger === "click" && onAnchorClick();
|
|
253
|
-
}} onPointerEnter={($event) => {
|
|
254
|
-
local.trigger === "hover" && onAnchorPointerEnter();
|
|
255
|
-
}} onPointerLeave={($event) => {
|
|
256
|
-
local.trigger === "hover" && onAnchorPointerLeave();
|
|
257
|
-
}} onFocusIn={($event) => {
|
|
258
|
-
local.trigger === "focus" && onAnchorFocus();
|
|
259
|
-
}} onFocusOut={($event) => {
|
|
260
|
-
local.trigger === "focus" && onAnchorBlur();
|
|
261
|
-
}} data-rozie-s-c6cf02ea="">
|
|
262
|
-
{(_props.anchorSlot ?? _props.slots?.["anchor"])?.({
|
|
263
|
-
open: open(),
|
|
264
|
-
toggle,
|
|
265
|
-
show,
|
|
266
|
-
hide
|
|
267
|
-
})}
|
|
268
|
-
</div>
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
{<Show when={open() && !local.disabled}><div class={"rozie-popover-floating"} ref={(el) => {
|
|
272
|
-
floatingElRef = el;
|
|
273
|
-
}} id="rozie-popover-floating" role={rozieAttr(floatingRole())} aria-modal={!!(floatingRole() === "dialog")} data-rozie-s-c6cf02ea="">
|
|
274
|
-
{<Show when={local.arrow}><div class={"rozie-popover-arrow"} ref={(el) => {
|
|
275
|
-
arrowElRef = el;
|
|
276
|
-
}} data-rozie-s-c6cf02ea="" /></Show>}{resolved()}
|
|
277
|
-
</div></Show>}</div>
|
|
278
|
-
</>;
|
|
279
|
-
}
|
|
280
|
-
//#endregion
|
|
281
7
|
//#region src/DataTable.tsx
|
|
282
8
|
__rozieInjectStyle("DataTable-d5dcab4c", `.rozie-data-table[data-rozie-s-d5dcab4c] {
|
|
283
9
|
border-collapse: collapse;
|
|
@@ -1719,8 +1445,7 @@ function DataTable(_props) {
|
|
|
1719
1445
|
const base = pinStyle(colId);
|
|
1720
1446
|
if (isExpanderColumn(colId) && row && row.depth) {
|
|
1721
1447
|
const pad = "padding-left:" + (.5 + row.depth * 1.25) + "rem";
|
|
1722
|
-
|
|
1723
|
-
return base ? base + sep + pad : pad;
|
|
1448
|
+
return base ? base + pad : pad;
|
|
1724
1449
|
}
|
|
1725
1450
|
return base;
|
|
1726
1451
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rozie-ui/data-table-solid",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "MIT",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"solid-js": "^1.8",
|
|
53
53
|
"@tanstack/table-core": "^8.21",
|
|
54
54
|
"@tanstack/virtual-core": "^3",
|
|
55
|
-
"@
|
|
55
|
+
"@rozie-ui/popover-solid": "^0.1.0"
|
|
56
56
|
},
|
|
57
57
|
"peerDependenciesMeta": {
|
|
58
58
|
"solid-js": {
|
|
@@ -64,13 +64,11 @@
|
|
|
64
64
|
"@tanstack/virtual-core": {
|
|
65
65
|
"optional": false
|
|
66
66
|
},
|
|
67
|
-
"@
|
|
67
|
+
"@rozie-ui/popover-solid": {
|
|
68
68
|
"optional": false
|
|
69
69
|
}
|
|
70
70
|
},
|
|
71
|
-
"devDependencies": {
|
|
72
|
-
"@floating-ui/dom": "^1.7.2"
|
|
73
|
-
},
|
|
71
|
+
"devDependencies": {},
|
|
74
72
|
"publishConfig": {
|
|
75
73
|
"access": "public"
|
|
76
74
|
},
|
package/src/Column.tsx
CHANGED
|
@@ -64,7 +64,7 @@ interface ColumnProps {
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
export default function Column(_props: ColumnProps): JSX.Element {
|
|
67
|
-
const _merged = mergeProps({ id: '', field: '', header: '', sortable: false, filterable: false, pinned: '', width: '', expandable: false, groupable: true, aggregationFn: null, editable: false, editor: 'text', editorOptions: (() => [])(), validate: null }, _props);
|
|
67
|
+
const _merged = mergeProps({ id: '', field: '', header: '', sortable: false, filterable: false, pinned: '', width: '', expandable: false, groupable: true, aggregationFn: null, editable: false, editor: 'text', editorOptions: (() => [])() as any[], validate: null }, _props);
|
|
68
68
|
const [local, attrs] = splitProps(_merged, ['id', 'field', 'header', 'sortable', 'filterable', 'pinned', 'width', 'expandable', 'groupable', 'aggregationFn', 'editable', 'editor', 'editorOptions', 'validate']);
|
|
69
69
|
|
|
70
70
|
const registry = useContext(rozieContext("data-table:columns"));
|
package/src/DataTable.tsx
CHANGED
|
@@ -2,7 +2,7 @@ import type { JSX } from 'solid-js';
|
|
|
2
2
|
import { Show, createEffect, createSignal, mergeProps, on, onCleanup, onMount, splitProps, untrack } from 'solid-js';
|
|
3
3
|
import { Key } from '@solid-primitives/keyed';
|
|
4
4
|
import { __rozieInjectStyle, createControllableSignal, parseInlineStyle, rozieAttr, rozieClass, rozieContext, rozieDisplay } from '@rozie/runtime-solid';
|
|
5
|
-
import Popover from '
|
|
5
|
+
import Popover from '@rozie-ui/popover-solid';
|
|
6
6
|
import { createTable, getCoreRowModel, getSortedRowModel, getFilteredRowModel, getPaginationRowModel, getExpandedRowModel, getGroupedRowModel,
|
|
7
7
|
// Faceted filtering (phase 50 reqs 8-9, D-03). All three are supplied UNCONDITIONALLY
|
|
8
8
|
// (mirrors the expand/group models) — inert until a consumer READS a column facet via the
|
|
@@ -565,7 +565,7 @@ export interface DataTableHandle {
|
|
|
565
565
|
}
|
|
566
566
|
|
|
567
567
|
export default function DataTable(_props: DataTableProps): JSX.Element {
|
|
568
|
-
const _merged = mergeProps({ columns: (() => [])(), selectionMode: 'none', manual: false, rowCount: null, pageCount: null, expandable: false, getSubRows: null, groupable: false, stickyHeader: false, interactionMode: 'table', singleClickEdit: false, undoable: false, undoLimit: 100, virtual: false, estimateRowHeight: 40, maxHeight: '' }, _props);
|
|
568
|
+
const _merged = mergeProps({ columns: (() => [])() as any[], selectionMode: 'none', manual: false, rowCount: null, pageCount: null, expandable: false, getSubRows: null, groupable: false, stickyHeader: false, interactionMode: 'table', singleClickEdit: false, undoable: false, undoLimit: 100, virtual: false, estimateRowHeight: 40, maxHeight: '' }, _props);
|
|
569
569
|
const [local, attrs] = splitProps(_merged, ['data', 'columns', 'selectionMode', 'sorting', 'globalFilter', 'columnFilters', 'pagination', 'manual', 'rowCount', 'pageCount', 'expandable', 'expanded', 'getSubRows', 'groupable', 'grouping', 'rowSelection', 'columnVisibility', 'columnSizing', 'columnOrder', 'columnPinning', 'stickyHeader', 'interactionMode', 'singleClickEdit', 'undoable', 'undoLimit', 'virtual', 'estimateRowHeight', 'maxHeight', 'children', 'ref']);
|
|
570
570
|
const resolved = () => local.children;
|
|
571
571
|
onMount(() => { local.ref?.({ sortColumn, clearSorting, toggleRowExpanded, expandAll, collapseAll, getExpandedRows, applyGrouping, clearGrouping, getFacetedUniqueValues, getFacetedMinMaxValues, getColumnDefs, toggleAllRows, clearSelection, getSelectedRows, setPage, setRowsPerPage, toggleColumnVisibility, applyColumnOrder, resetColumnSizing, pinColumn, focusCell, getActiveCell, clearActiveCell, getRowIndexRelativeToPage, editCell, commitEditing, editRow, getSelectedRange, cut, undo, redo, canUndo, canRedo, clearHistory }); });
|
|
@@ -579,8 +579,8 @@ export default function DataTable(_props: DataTableProps): JSX.Element {
|
|
|
579
579
|
pageIndex: 0,
|
|
580
580
|
pageSize: 10
|
|
581
581
|
}))());
|
|
582
|
-
const [expanded, setExpanded] = createControllableSignal<Record<string, any> | boolean>(_props as unknown as Record<string, unknown>, 'expanded', null);
|
|
583
|
-
const [grouping, setGrouping] = createControllableSignal<any[]>(_props as unknown as Record<string, unknown>, 'grouping', null);
|
|
582
|
+
const [expanded, setExpanded] = createControllableSignal<Record<string, any> | boolean | null>(_props as unknown as Record<string, unknown>, 'expanded', null);
|
|
583
|
+
const [grouping, setGrouping] = createControllableSignal<any[] | null>(_props as unknown as Record<string, unknown>, 'grouping', null);
|
|
584
584
|
const [rowSelection, setRowSelection] = createControllableSignal<Record<string, any>>(_props as unknown as Record<string, unknown>, 'rowSelection', (() => ({}))());
|
|
585
585
|
const [columnVisibility, setColumnVisibility] = createControllableSignal<Record<string, any>>(_props as unknown as Record<string, unknown>, 'columnVisibility', (() => ({}))());
|
|
586
586
|
const [columnSizing, setColumnSizing] = createControllableSignal<Record<string, any>>(_props as unknown as Record<string, unknown>, 'columnSizing', (() => ({}))());
|
|
@@ -2606,15 +2606,9 @@ export default function DataTable(_props: DataTableProps): JSX.Element {
|
|
|
2606
2606
|
function bodyCellStyle(row: any, colId: any) {
|
|
2607
2607
|
const base = pinStyle(colId);
|
|
2608
2608
|
if (isExpanderColumn(colId) && row && row.depth) {
|
|
2609
|
+
// Only the expander column indents (the tree affordance lives here).
|
|
2609
2610
|
const pad = 'padding-left:' + (0.5 + row.depth * 1.25) + 'rem';
|
|
2610
|
-
|
|
2611
|
-
// second ';' produces a `;;` empty declaration. Browsers tolerate that in a
|
|
2612
|
-
// raw style attribute (React/Vue/Svelte/Lit) and Solid's cssText path does
|
|
2613
|
-
// too, but Angular's [style] binding parses the string via ɵɵstyleMap and
|
|
2614
|
-
// DROPS the declaration following the empty one — so the depth padding-left
|
|
2615
|
-
// silently vanished on Angular only. Emit a single, well-formed separator.
|
|
2616
|
-
const sep = base.endsWith(';') ? '' : ';';
|
|
2617
|
-
return base ? base + sep + pad : pad;
|
|
2611
|
+
return base ? base + pad : pad;
|
|
2618
2612
|
}
|
|
2619
2613
|
return base;
|
|
2620
2614
|
}
|
package/src/EditorSelect.tsx
CHANGED
|
@@ -35,7 +35,7 @@ interface EditorSelectProps {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
export default function EditorSelect(_props: EditorSelectProps): JSX.Element {
|
|
38
|
-
const _merged = mergeProps({ columnId: '', column: null, row: null, value: null, commit: null, cancel: null, options: (() => [])() }, _props);
|
|
38
|
+
const _merged = mergeProps({ columnId: '', column: null, row: null, value: null, commit: null, cancel: null, options: (() => [])() as any[] }, _props);
|
|
39
39
|
const [local, attrs] = splitProps(_merged, ['columnId', 'column', 'row', 'value', 'commit', 'cancel', 'options']);
|
|
40
40
|
|
|
41
41
|
const [draft, setDraft] = createSignal('');
|
package/src/FilterSelect.tsx
CHANGED
|
@@ -27,7 +27,7 @@ interface FilterSelectProps {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
export default function FilterSelect(_props: FilterSelectProps): JSX.Element {
|
|
30
|
-
const _merged = mergeProps({ columnId: '', column: null, value: null, setFilter: null, uniqueValues: (() => [])() }, _props);
|
|
30
|
+
const _merged = mergeProps({ columnId: '', column: null, value: null, setFilter: null, uniqueValues: (() => [])() as any[] }, _props);
|
|
31
31
|
const [local, attrs] = splitProps(_merged, ['columnId', 'column', 'value', 'setFilter', 'uniqueValues']);
|
|
32
32
|
|
|
33
33
|
// The <select> value binding coerced to a string. $props.value is typed `unknown`
|
package/src/GroupBar.tsx
CHANGED
|
@@ -81,7 +81,7 @@ interface GroupBarProps {
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
export default function GroupBar(_props: GroupBarProps): JSX.Element {
|
|
84
|
-
const _merged = mergeProps({ grouping: (() => [])(), groupableColumns: (() => [])(), applyGrouping: null, clearGrouping: null }, _props);
|
|
84
|
+
const _merged = mergeProps({ grouping: (() => [])() as any[], groupableColumns: (() => [])() as any[], applyGrouping: null, clearGrouping: null }, _props);
|
|
85
85
|
const [local, attrs] = splitProps(_merged, ['grouping', 'groupableColumns', 'applyGrouping', 'clearGrouping']);
|
|
86
86
|
|
|
87
87
|
const [draggingId, setDraggingId] = createSignal('');
|
package/src/Popover.tsx
DELETED
|
@@ -1,416 +0,0 @@
|
|
|
1
|
-
import type { JSX } from 'solid-js';
|
|
2
|
-
import { Show, children, createEffect, mergeProps, on, onCleanup, onMount, splitProps, untrack } from 'solid-js';
|
|
3
|
-
import { __rozieInjectStyle, createControllableSignal, createOutsideClick, rozieAttr } from '@rozie/runtime-solid';
|
|
4
|
-
// The `offset` AND `arrow` middleware factories are ALIASED on import: both are
|
|
5
|
-
// ALSO author PROP names (`offset`, `arrow`). A bare `offset`/`arrow` shorthand in
|
|
6
|
-
// the buildMiddleware factories object resolves to the PROP — on Vue/Svelte the
|
|
7
|
-
// destructured prop local shadows the import, and on Angular the emitter rewrites
|
|
8
|
-
// the bare shorthand to the prop signal (`offset: this.offset()`, a number) instead
|
|
9
|
-
// of the middleware function (TS2322). Aliasing both severs the import↔prop clash.
|
|
10
|
-
// (The Cropper import-name==component-name class, applied to imports vs PROP names —
|
|
11
|
-
// two collisions, not one.) computePosition/autoUpdate/flip/shift carry no clash.
|
|
12
|
-
import { computePosition, autoUpdate, offset as offsetMiddleware, flip, shift, arrow as arrowMiddleware } from '@floating-ui/dom';
|
|
13
|
-
import { buildMiddleware } from './internal/middleware';
|
|
14
|
-
|
|
15
|
-
// null-lets so the bundled-leaf typeNeutralize pass annotates them `any`:
|
|
16
|
-
// anchorNode/floatingNode/arrowNode hold the resolved ref ELEMENTS (read ONLY in
|
|
17
|
-
// $onMount/handlers, ROZ123). They are deliberately named DIFFERENTLY from the
|
|
18
|
-
// `ref="anchorEl"` / `ref="floatingEl"` / `ref="arrowEl"` template ref names: the
|
|
19
|
-
// React/Svelte emitters declare a `const anchorEl = useRef(...)` for the ref, and a
|
|
20
|
-
// top-level `let anchorEl` hoisted to its own `useRef` would REDECLARE it (TS2451 —
|
|
21
|
-
// the local-name==ref-name self-shadow class, here in its `let X = null; X = $refs.X`
|
|
22
|
-
// variant, which deconflictRefShadows does NOT auto-rewrite since it only fires on the
|
|
23
|
-
// `const X = $refs.X` init shape).
|
|
24
|
-
// stopAutoUpdate is the autoUpdate teardown handle — a TOP-LEVEL `let` so the Solid
|
|
25
|
-
// onMount→onCleanup split (teardown is a separate closure) can still see it.
|
|
26
|
-
// lastFocusedEl (phase 72-06b) holds whatever had DOM focus at the moment a
|
|
27
|
-
// `trigger="click"` popover opened (natively the clicked trigger element itself,
|
|
28
|
-
// since a mousedown focuses a native `<button>` before its `click` fires) —
|
|
29
|
-
// restored on dismissal so Escape/click-outside don't drop focus to `<body>`.
|
|
30
|
-
// Same null-let convention as the others: read/written only in handlers, `any`
|
|
31
|
-
// via typeNeutralize.
|
|
32
|
-
|
|
33
|
-
__rozieInjectStyle('Popover-c6cf02ea', `.rozie-popover[data-rozie-s-c6cf02ea] {
|
|
34
|
-
display: contents;
|
|
35
|
-
}
|
|
36
|
-
.rozie-popover-anchor[data-rozie-s-c6cf02ea] {
|
|
37
|
-
display: inline-block;
|
|
38
|
-
}
|
|
39
|
-
.rozie-popover-floating[data-rozie-s-c6cf02ea] {
|
|
40
|
-
position: absolute;
|
|
41
|
-
left: 0;
|
|
42
|
-
top: 0;
|
|
43
|
-
z-index: var(--rozie-popover-z, 1000);
|
|
44
|
-
width: max-content;
|
|
45
|
-
max-width: var(--rozie-popover-max-width, calc(100vw - 16px));
|
|
46
|
-
background: var(--rozie-popover-bg, #fff);
|
|
47
|
-
color: var(--rozie-popover-color, inherit);
|
|
48
|
-
border: var(--rozie-popover-border, 1px solid rgba(0, 0, 0, 0.12));
|
|
49
|
-
border-radius: var(--rozie-popover-radius, 8px);
|
|
50
|
-
box-shadow: var(--rozie-popover-shadow, 0 8px 24px rgba(0, 0, 0, 0.12));
|
|
51
|
-
padding: var(--rozie-popover-padding, 8px 12px);
|
|
52
|
-
}
|
|
53
|
-
.rozie-popover-arrow[data-rozie-s-c6cf02ea] {
|
|
54
|
-
position: absolute;
|
|
55
|
-
width: var(--rozie-popover-arrow-size, 8px);
|
|
56
|
-
height: var(--rozie-popover-arrow-size, 8px);
|
|
57
|
-
background: var(--rozie-popover-bg, #fff);
|
|
58
|
-
border: var(--rozie-popover-border, 1px solid rgba(0, 0, 0, 0.12));
|
|
59
|
-
transform: rotate(45deg);
|
|
60
|
-
}`);
|
|
61
|
-
|
|
62
|
-
interface AnchorSlotCtx { open: any; toggle: any; show: any; hide: any; }
|
|
63
|
-
|
|
64
|
-
interface PopoverProps {
|
|
65
|
-
/**
|
|
66
|
-
* 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.
|
|
67
|
-
*/
|
|
68
|
-
open?: boolean;
|
|
69
|
-
defaultOpen?: boolean;
|
|
70
|
-
onOpenChange?: (open: boolean) => void;
|
|
71
|
-
/**
|
|
72
|
-
* 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.
|
|
73
|
-
*/
|
|
74
|
-
placement?: string;
|
|
75
|
-
/**
|
|
76
|
-
* 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).
|
|
77
|
-
*/
|
|
78
|
-
trigger?: string;
|
|
79
|
-
/**
|
|
80
|
-
* Distance in pixels between the anchor and the floating content (the Floating UI `offset` middleware). Reconciled at runtime.
|
|
81
|
-
*/
|
|
82
|
-
offset?: number;
|
|
83
|
-
/**
|
|
84
|
-
* 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.
|
|
85
|
-
*/
|
|
86
|
-
disableFlip?: boolean;
|
|
87
|
-
/**
|
|
88
|
-
* 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.
|
|
89
|
-
*/
|
|
90
|
-
disableShift?: boolean;
|
|
91
|
-
/**
|
|
92
|
-
* 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.
|
|
93
|
-
*/
|
|
94
|
-
arrow?: boolean;
|
|
95
|
-
/**
|
|
96
|
-
* Disable the control entirely: the trigger no longer opens the content and any open content is suppressed.
|
|
97
|
-
*/
|
|
98
|
-
disabled?: boolean;
|
|
99
|
-
/**
|
|
100
|
-
* 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).
|
|
101
|
-
*/
|
|
102
|
-
modal?: boolean;
|
|
103
|
-
/**
|
|
104
|
-
* 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.
|
|
105
|
-
*/
|
|
106
|
-
strategy?: string;
|
|
107
|
-
onChange?: (...args: unknown[]) => void;
|
|
108
|
-
anchorSlot?: (ctx: AnchorSlotCtx) => JSX.Element;
|
|
109
|
-
// D-131: default slot resolved via children() at body top
|
|
110
|
-
children?: JSX.Element;
|
|
111
|
-
slots?: Record<string, (ctx: any) => JSX.Element>;
|
|
112
|
-
ref?: (h: PopoverHandle) => void;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
export interface PopoverHandle {
|
|
116
|
-
show: (...args: any[]) => any;
|
|
117
|
-
hide: (...args: any[]) => any;
|
|
118
|
-
toggle: (...args: any[]) => any;
|
|
119
|
-
reposition: (...args: any[]) => any;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
export default function Popover(_props: PopoverProps): JSX.Element {
|
|
123
|
-
const _merged = mergeProps({ placement: 'bottom', trigger: 'click', offset: 8, disableFlip: false, disableShift: false, arrow: false, disabled: false, modal: false, strategy: 'absolute' }, _props);
|
|
124
|
-
const [local, attrs] = splitProps(_merged, ['open', 'placement', 'trigger', 'offset', 'disableFlip', 'disableShift', 'arrow', 'disabled', 'modal', 'strategy', 'children', 'ref']);
|
|
125
|
-
const resolved = children(() => local.children);
|
|
126
|
-
onMount(() => { local.ref?.({ show, hide, toggle, reposition }); });
|
|
127
|
-
|
|
128
|
-
const [open, setOpen] = createControllableSignal<boolean>(_props as unknown as Record<string, unknown>, 'open', false);
|
|
129
|
-
onMount(() => {
|
|
130
|
-
const _cleanup = (() => {
|
|
131
|
-
// $refs read ONLY here (ROZ123). The floating + arrow elements live behind r-if
|
|
132
|
-
// and may be null until open; startTracking re-reads via the watch path.
|
|
133
|
-
anchorNode = anchorElRef;
|
|
134
|
-
if (open() && !local.disabled) {
|
|
135
|
-
// floatingNode is populated by its r-if having rendered; read it lazily inside
|
|
136
|
-
// the watch/handlers too. Position on next tick when it exists.
|
|
137
|
-
floatingNode = floatingElRef;
|
|
138
|
-
arrowNode = arrowElRef;
|
|
139
|
-
startTracking();
|
|
140
|
-
}
|
|
141
|
-
})() as unknown;
|
|
142
|
-
if (_cleanup) onCleanup(_cleanup as () => void);
|
|
143
|
-
onCleanup(() => {
|
|
144
|
-
stopTracking();
|
|
145
|
-
});
|
|
146
|
-
});
|
|
147
|
-
createEffect(on(() => (() => open())(), (v) => untrack(() => ((isOpen: any) => {
|
|
148
|
-
if (isOpen && !local.disabled) {
|
|
149
|
-
queueMicrotask(() => {
|
|
150
|
-
if (!open() || local.disabled) return;
|
|
151
|
-
floatingNode = floatingElRef;
|
|
152
|
-
arrowNode = arrowElRef;
|
|
153
|
-
startTracking();
|
|
154
|
-
});
|
|
155
|
-
} else {
|
|
156
|
-
stopTracking();
|
|
157
|
-
}
|
|
158
|
-
})(v)), { defer: true }));
|
|
159
|
-
createEffect(on(() => (() => local.placement)(), (v) => untrack(() => (() => {
|
|
160
|
-
if (open()) position();
|
|
161
|
-
})()), { defer: true }));
|
|
162
|
-
createEffect(on(() => (() => local.offset)(), (v) => untrack(() => (() => {
|
|
163
|
-
if (open()) position();
|
|
164
|
-
})()), { defer: true }));
|
|
165
|
-
createEffect(on(() => (() => local.disableFlip)(), (v) => untrack(() => (() => {
|
|
166
|
-
if (open()) position();
|
|
167
|
-
})()), { defer: true }));
|
|
168
|
-
createEffect(on(() => (() => local.disableShift)(), (v) => untrack(() => (() => {
|
|
169
|
-
if (open()) position();
|
|
170
|
-
})()), { defer: true }));
|
|
171
|
-
createEffect(on(() => (() => local.strategy)(), (v) => untrack(() => (() => {
|
|
172
|
-
if (open()) position();
|
|
173
|
-
})()), { defer: true }));
|
|
174
|
-
let anchorElRef: HTMLElement | null = null;
|
|
175
|
-
let floatingElRef: HTMLElement | null = null;
|
|
176
|
-
let arrowElRef: HTMLElement | null = null;
|
|
177
|
-
|
|
178
|
-
// null-lets so the bundled-leaf typeNeutralize pass annotates them `any`:
|
|
179
|
-
// anchorNode/floatingNode/arrowNode hold the resolved ref ELEMENTS (read ONLY in
|
|
180
|
-
// $onMount/handlers, ROZ123). They are deliberately named DIFFERENTLY from the
|
|
181
|
-
// `ref="anchorEl"` / `ref="floatingEl"` / `ref="arrowEl"` template ref names: the
|
|
182
|
-
// React/Svelte emitters declare a `const anchorEl = useRef(...)` for the ref, and a
|
|
183
|
-
// top-level `let anchorEl` hoisted to its own `useRef` would REDECLARE it (TS2451 —
|
|
184
|
-
// the local-name==ref-name self-shadow class, here in its `let X = null; X = $refs.X`
|
|
185
|
-
// variant, which deconflictRefShadows does NOT auto-rewrite since it only fires on the
|
|
186
|
-
// `const X = $refs.X` init shape).
|
|
187
|
-
// stopAutoUpdate is the autoUpdate teardown handle — a TOP-LEVEL `let` so the Solid
|
|
188
|
-
// onMount→onCleanup split (teardown is a separate closure) can still see it.
|
|
189
|
-
// lastFocusedEl (phase 72-06b) holds whatever had DOM focus at the moment a
|
|
190
|
-
// `trigger="click"` popover opened (natively the clicked trigger element itself,
|
|
191
|
-
// since a mousedown focuses a native `<button>` before its `click` fires) —
|
|
192
|
-
// restored on dismissal so Escape/click-outside don't drop focus to `<body>`.
|
|
193
|
-
// Same null-let convention as the others: read/written only in handlers, `any`
|
|
194
|
-
// via typeNeutralize.
|
|
195
|
-
let anchorNode: any = null;
|
|
196
|
-
let floatingNode: any = null;
|
|
197
|
-
let arrowNode: any = null;
|
|
198
|
-
let stopAutoUpdate: any = null;
|
|
199
|
-
let lastFocusedEl: any = null;
|
|
200
|
-
|
|
201
|
-
// `document.activeElement` stops at the OUTERMOST shadow-DOM host when focus
|
|
202
|
-
// lives inside a NESTED shadow tree — e.g. a Lit consumer that composes
|
|
203
|
-
// `<rozie-popover>` inside its own shadow root (data-table's vendored copy):
|
|
204
|
-
// clicking the trigger focuses a real element several shadow boundaries deep,
|
|
205
|
-
// but `document.activeElement` only resolves as far as the outermost custom
|
|
206
|
-
// element (`<rozie-data-table>`), not the actual focused node. Walking
|
|
207
|
-
// `.shadowRoot.activeElement` recursively drills to the true focused element.
|
|
208
|
-
// On the other 5 targets (no shadow DOM) `el.shadowRoot` is always
|
|
209
|
-
// null/undefined, so the loop is a no-op and this degrades to a plain
|
|
210
|
-
// `document.activeElement` read — one implementation, safe on every target.
|
|
211
|
-
function deepActiveElement() {
|
|
212
|
-
let el = document.activeElement;
|
|
213
|
-
while (el && el.shadowRoot && el.shadowRoot.activeElement) {
|
|
214
|
-
el = el.shadowRoot.activeElement;
|
|
215
|
-
}
|
|
216
|
-
return el;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// Drive the two-way model + emit in one place. Named `requestOpen` (NOT `setOpen`)
|
|
220
|
-
// to dodge the React generated `setOpen` setter for the `open` model (ROZ524).
|
|
221
|
-
//
|
|
222
|
-
// Focus-return (phase 72-06b, D-08 a11y finding): scoped to `trigger === 'click'`
|
|
223
|
-
// only — click-triggered popovers are genuinely interactive (a real dialog the
|
|
224
|
-
// user tabs/clicks into), so restoring focus to the trigger on dismissal matches
|
|
225
|
-
// standard disclosure-widget a11y practice. Deliberately NOT applied to
|
|
226
|
-
// `hover`/`focus` triggers (tooltip-flavored, see `isTooltip()`): those close on
|
|
227
|
-
// pointerleave/blur constantly during normal mouse/keyboard traversal, and
|
|
228
|
-
// forcing a focus() call on every such close would fight the user's own focus
|
|
229
|
-
// movement rather than restore anything lost.
|
|
230
|
-
function requestOpen(next: any) {
|
|
231
|
-
if (open() === next) return;
|
|
232
|
-
if (next && local.trigger === 'click') {
|
|
233
|
-
lastFocusedEl = deepActiveElement();
|
|
234
|
-
}
|
|
235
|
-
setOpen(next);
|
|
236
|
-
_props.onChange?.(next);
|
|
237
|
-
if (!next && local.trigger === 'click' && lastFocusedEl && lastFocusedEl.isConnected && typeof lastFocusedEl.focus === 'function') {
|
|
238
|
-
lastFocusedEl.focus();
|
|
239
|
-
}
|
|
240
|
-
if (!next) {
|
|
241
|
-
lastFocusedEl = null;
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// Apply the resolved x/y (and arrow offset, when present) onto the floating element.
|
|
246
|
-
function applyPosition(x: any, y: any, middlewareData: any) {
|
|
247
|
-
if (!floatingNode) return;
|
|
248
|
-
floatingNode.style.left = x + 'px';
|
|
249
|
-
floatingNode.style.top = y + 'px';
|
|
250
|
-
if (arrowNode && middlewareData && middlewareData.arrow) {
|
|
251
|
-
const ax = middlewareData.arrow.x;
|
|
252
|
-
const ay = middlewareData.arrow.y;
|
|
253
|
-
arrowNode.style.left = ax == null ? '' : ax + 'px';
|
|
254
|
-
arrowNode.style.top = ay == null ? '' : ay + 'px';
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// Recompute the position once. Pure engine call; safe to invoke whenever both
|
|
259
|
-
// elements exist and the content is open. `opts` is a null-let (→ `any`) so the
|
|
260
|
-
// loosely-typed `<props>` placement (string) + the `unknown[]` middleware array don't
|
|
261
|
-
// fail the strict leaf tsc against Floating UI's `Placement` / `Middleware[]` types
|
|
262
|
-
// (the cropper `let cfg = null` constructor-args idiom).
|
|
263
|
-
function position() {
|
|
264
|
-
if (!anchorNode || !floatingNode) return;
|
|
265
|
-
const middleware = buildMiddleware({
|
|
266
|
-
offset: offsetMiddleware,
|
|
267
|
-
flip,
|
|
268
|
-
shift,
|
|
269
|
-
arrow: arrowMiddleware
|
|
270
|
-
}, {
|
|
271
|
-
offset: local.offset,
|
|
272
|
-
disableFlip: local.disableFlip,
|
|
273
|
-
disableShift: local.disableShift,
|
|
274
|
-
arrow: local.arrow,
|
|
275
|
-
arrowEl: arrowNode
|
|
276
|
-
});
|
|
277
|
-
// 'fixed' inline position MUST be written before computePosition measures the
|
|
278
|
-
// floating element's offset parent (fixed vs absolute changes the containing
|
|
279
|
-
// block). Default 'absolute' explicitly CLEARS any inline position instead of
|
|
280
|
-
// writing `position: absolute` — so a never-fixed popover still writes no
|
|
281
|
-
// visible inline position (byte-identical-off preserved: `style.position = ''`
|
|
282
|
-
// is a no-op when the property was never set), while a live `strategy`
|
|
283
|
-
// reconcile (fixed → absolute, see the $watch below) correctly resets the
|
|
284
|
-
// stale inline `fixed` so the stylesheet's `position: absolute` rule re-takes
|
|
285
|
-
// over instead of positioning `fixed` with absolute-computed coordinates
|
|
286
|
-
// (72-REVIEW.md WR-01).
|
|
287
|
-
if (local.strategy === 'fixed') {
|
|
288
|
-
floatingNode.style.position = 'fixed';
|
|
289
|
-
} else {
|
|
290
|
-
floatingNode.style.position = '';
|
|
291
|
-
}
|
|
292
|
-
let opts: any = null;
|
|
293
|
-
opts = {
|
|
294
|
-
placement: local.placement,
|
|
295
|
-
strategy: local.strategy,
|
|
296
|
-
middleware
|
|
297
|
-
};
|
|
298
|
-
computePosition(anchorNode, floatingNode, opts).then((result: any) => {
|
|
299
|
-
applyPosition(result.x, result.y, result.middlewareData);
|
|
300
|
-
});
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
// Start autoUpdate (idempotent — stop any prior subscription first) and do an
|
|
304
|
-
// initial position. Floating UI's autoUpdate keeps the position fresh on scroll/
|
|
305
|
-
// resize/ancestor-layout changes and returns its own teardown.
|
|
306
|
-
function startTracking() {
|
|
307
|
-
if (!anchorNode || !floatingNode) return;
|
|
308
|
-
if (stopAutoUpdate) {
|
|
309
|
-
stopAutoUpdate();
|
|
310
|
-
stopAutoUpdate = null;
|
|
311
|
-
}
|
|
312
|
-
stopAutoUpdate = autoUpdate(anchorNode, floatingNode, position);
|
|
313
|
-
}
|
|
314
|
-
function stopTracking() {
|
|
315
|
-
if (stopAutoUpdate) {
|
|
316
|
-
stopAutoUpdate();
|
|
317
|
-
stopAutoUpdate = null;
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
// ─── trigger gesture handlers (wired conditionally on the anchor by `trigger`) ──
|
|
321
|
-
function onAnchorClick() {
|
|
322
|
-
if (local.disabled) return;
|
|
323
|
-
requestOpen(!open());
|
|
324
|
-
}
|
|
325
|
-
function onAnchorPointerEnter() {
|
|
326
|
-
if (local.disabled) return;
|
|
327
|
-
requestOpen(true);
|
|
328
|
-
}
|
|
329
|
-
function onAnchorPointerLeave() {
|
|
330
|
-
if (local.disabled) return;
|
|
331
|
-
requestOpen(false);
|
|
332
|
-
}
|
|
333
|
-
function onAnchorFocus() {
|
|
334
|
-
if (local.disabled) return;
|
|
335
|
-
requestOpen(true);
|
|
336
|
-
}
|
|
337
|
-
function onAnchorBlur() {
|
|
338
|
-
if (local.disabled) return;
|
|
339
|
-
requestOpen(false);
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
// Dismissal handler — method reference for the <listeners> block (an inline
|
|
343
|
-
// handler referencing $event leaks into React's useEffect deps → TS2552; every
|
|
344
|
-
// corpus <listener> uses a method-ref + modifiers).
|
|
345
|
-
function dismiss() {
|
|
346
|
-
requestOpen(false);
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
// ─── role helpers (plain functions; tooltip vs popover-dialog by trigger) ───────
|
|
350
|
-
// hover/focus triggers are tooltip-flavored; click is an interactive popover.
|
|
351
|
-
function isTooltip() {
|
|
352
|
-
return local.trigger === 'hover' || local.trigger === 'focus';
|
|
353
|
-
}
|
|
354
|
-
// Role: hover/focus → 'tooltip'; a click popover is 'dialog' ONLY when the consumer
|
|
355
|
-
// opts into `modal` (which is what also emits aria-modal). A default (non-modal)
|
|
356
|
-
// click popover returns `undefined` — a role-NEUTRAL positioned container, so the slot
|
|
357
|
-
// content owns its own semantics (e.g. the data-table ⋯ menu declares role="menu").
|
|
358
|
-
// Emitting role="dialog" + aria-modal="true" on a click-outside-dismissable panel
|
|
359
|
-
// with no focus trap wrongly tells assistive tech that sibling content is inert (IN-03).
|
|
360
|
-
// `undefined` (not `null`) for the neutral case: the Vue `:role` binding target is
|
|
361
|
-
// `string | undefined`, and under strict vue-tsc `null` is not assignable to it —
|
|
362
|
-
// `undefined` drops the attribute identically (Vue/Solid nullish-attr drop treats both
|
|
363
|
-
// alike) while keeping the emitted leaf's inferred type a clean `'tooltip' | 'dialog' | undefined`.
|
|
364
|
-
function floatingRole() {
|
|
365
|
-
return isTooltip() ? 'tooltip' : local.modal ? 'dialog' : undefined;
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
// ─── imperative handle ($expose) ────────────────────────────────────────────────
|
|
369
|
-
// Verbs: show/hide/toggle/reposition. NOT `update` (reserved Lit lifecycle) → the
|
|
370
|
-
// reposition verb is `reposition`. None collide with the `change` emit, the `open`
|
|
371
|
-
// model, or its React `setOpen` setter, nor with inherited HTMLElement members.
|
|
372
|
-
function show() {
|
|
373
|
-
if (!local.disabled) requestOpen(true);
|
|
374
|
-
}
|
|
375
|
-
function hide() {
|
|
376
|
-
requestOpen(false);
|
|
377
|
-
}
|
|
378
|
-
function toggle() {
|
|
379
|
-
if (!local.disabled) requestOpen(!open());
|
|
380
|
-
}
|
|
381
|
-
function reposition() {
|
|
382
|
-
position();
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
createEffect(() => {
|
|
386
|
-
if (!(open())) return;
|
|
387
|
-
const _rozieHandler = ($event: KeyboardEvent) => {
|
|
388
|
-
if ($event.key !== 'Escape') return;
|
|
389
|
-
dismiss();
|
|
390
|
-
};
|
|
391
|
-
document.addEventListener('keydown', _rozieHandler);
|
|
392
|
-
onCleanup(() => document.removeEventListener('keydown', _rozieHandler));
|
|
393
|
-
});
|
|
394
|
-
|
|
395
|
-
createOutsideClick(
|
|
396
|
-
[() => anchorElRef, () => floatingElRef],
|
|
397
|
-
dismiss,
|
|
398
|
-
() => open(),
|
|
399
|
-
);
|
|
400
|
-
|
|
401
|
-
return (
|
|
402
|
-
<>
|
|
403
|
-
<div {...attrs} class={"rozie-popover" + (((attrs as unknown as Record<string, unknown>).class as string | undefined) ? " " + ((attrs as unknown as Record<string, unknown>).class as string | undefined) : "")} data-rozie-s-c6cf02ea="">
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
<div aria-haspopup="dialog" aria-expanded={!!open()} aria-describedby={rozieAttr(isTooltip() && open() ? 'rozie-popover-floating' : null)} class={"rozie-popover-anchor"} ref={(el) => { anchorElRef = el as HTMLElement; }} onClick={($event: MouseEvent & { currentTarget: HTMLDivElement; target: Element }) => { local.trigger === 'click' && onAnchorClick(); }} onPointerEnter={($event: PointerEvent & { currentTarget: HTMLDivElement; target: Element }) => { local.trigger === 'hover' && onAnchorPointerEnter(); }} onPointerLeave={($event: PointerEvent & { currentTarget: HTMLDivElement; target: Element }) => { local.trigger === 'hover' && onAnchorPointerLeave(); }} onFocusIn={($event: FocusEvent & { currentTarget: HTMLDivElement; target: Element }) => { local.trigger === 'focus' && onAnchorFocus(); }} onFocusOut={($event: FocusEvent & { currentTarget: HTMLDivElement; target: Element }) => { local.trigger === 'focus' && onAnchorBlur(); }} data-rozie-s-c6cf02ea="">
|
|
407
|
-
{(_props.anchorSlot ?? _props.slots?.['anchor'])?.({ open: open(), toggle, show, hide })}
|
|
408
|
-
</div>
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
{<Show when={open() && !local.disabled}><div class={"rozie-popover-floating"} ref={(el) => { floatingElRef = el as HTMLElement; }} id="rozie-popover-floating" role={rozieAttr(floatingRole())} aria-modal={!!(floatingRole() === 'dialog')} data-rozie-s-c6cf02ea="">
|
|
412
|
-
{<Show when={local.arrow}><div class={"rozie-popover-arrow"} ref={(el) => { arrowElRef = el as HTMLElement; }} data-rozie-s-c6cf02ea="" /></Show>}{resolved()}
|
|
413
|
-
</div></Show>}</div>
|
|
414
|
-
</>
|
|
415
|
-
);
|
|
416
|
-
}
|
|
@@ -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
|
-
}
|