@smwb/ui-solid 1.0.0 → 2.1.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 +115 -0
- package/dist/assets/styles/less/components/appBar/appBar.entry.css +1 -1
- package/dist/assets/styles/less/components/avatar/avatar.entry.css +1 -1
- package/dist/assets/styles/less/components/badge/badge.entry.css +1 -1
- package/dist/assets/styles/less/components/breadcrumbs/breadcrumbs.entry.css +1 -1
- package/dist/assets/styles/less/components/button/button.entry.css +1 -1
- package/dist/assets/styles/less/components/buttonGroup/buttonGroup.entry.css +1 -1
- package/dist/assets/styles/less/components/dagView/dagView.entry.css +1 -1
- package/dist/assets/styles/less/components/meter/meter.entry.css +1 -1
- package/dist/assets/styles/less/components/screenDivider/screenDivider.entry.css +1 -1
- package/dist/assets/styles/less/components/sheet/sheet.entry.css +1 -1
- package/dist/assets/styles/less/components/statusDot/statusDot.entry.css +1 -1
- package/dist/assets/styles/less/components/tabGroups/tabGroups.entry.css +1 -1
- package/dist/assets/styles/less/components/tabs/tab/tab.entry.css +1 -1
- package/dist/assets/styles/less/components/toggle/toggle.entry.css +1 -1
- package/dist/assets/styles/less/components/treeView/treeView.entry.css +1 -1
- package/dist/assets/styles/less/tokens.entry.css +1 -1
- package/dist/components/dataDisplay/avatar/avatar.d.ts +5 -2
- package/dist/components/dataDisplay/avatar/avatar.js +9 -9
- package/dist/components/dataDisplay/avatar/avatar.js.map +1 -1
- package/dist/components/dataDisplay/badge/badge.d.ts +2 -1
- package/dist/components/dataDisplay/badge/badge.js.map +1 -1
- package/dist/components/dataDisplay/chart/chart.d.ts +16 -8
- package/dist/components/dataDisplay/chart/chartFull.d.ts +14 -0
- package/dist/components/dataDisplay/chart/core.d.ts +19 -0
- package/dist/components/dataDisplay/chart/index.d.ts +1 -1
- package/dist/components/dataDisplay/dagView/dagView.d.ts +4 -0
- package/dist/components/dataDisplay/dagView/dagView.js +124 -123
- package/dist/components/dataDisplay/dagView/dagView.js.map +1 -1
- package/dist/components/dataDisplay/dagView/index.d.ts +1 -1
- package/dist/components/dataDisplay/meter/index.d.ts +1 -1
- package/dist/components/dataDisplay/meter/meter.d.ts +14 -0
- package/dist/components/dataDisplay/meter/meter.js +32 -30
- package/dist/components/dataDisplay/meter/meter.js.map +1 -1
- package/dist/components/dataDisplay/statusDot/index.d.ts +1 -1
- package/dist/components/dataDisplay/statusDot/statusDot.d.ts +10 -1
- package/dist/components/dataDisplay/statusDot/statusDot.js +20 -11
- package/dist/components/dataDisplay/statusDot/statusDot.js.map +1 -1
- package/dist/components/dataDisplay/treeView/treeView.d.ts +18 -2
- package/dist/components/dataDisplay/treeView/treeView.js +140 -121
- package/dist/components/dataDisplay/treeView/treeView.js.map +1 -1
- package/dist/components/inputs/button/button.d.ts +7 -0
- package/dist/components/inputs/button/button.js +57 -51
- package/dist/components/inputs/button/button.js.map +1 -1
- package/dist/components/inputs/toggle/toggle.d.ts +6 -0
- package/dist/components/inputs/toggle/toggle.js +23 -23
- package/dist/components/inputs/toggle/toggle.js.map +1 -1
- package/dist/components/layout/appLayout/appLayout.d.ts +6 -1
- package/dist/components/layout/appLayout/appLayout.js +49 -42
- package/dist/components/layout/appLayout/appLayout.js.map +1 -1
- package/dist/components/layout/screenDivider/screenDivider.d.ts +12 -0
- package/dist/components/layout/screenDivider/screenDivider.js +87 -46
- package/dist/components/layout/screenDivider/screenDivider.js.map +1 -1
- package/dist/components/layout/sheet/sheet.d.ts +11 -0
- package/dist/components/layout/sheet/sheet.js +122 -102
- package/dist/components/layout/sheet/sheet.js.map +1 -1
- package/dist/components/layout/tabGroups/index.d.ts +1 -1
- package/dist/components/layout/tabGroups/tabGroups.d.ts +44 -1
- package/dist/components/layout/tabGroups/tabGroups.js +500 -460
- package/dist/components/layout/tabGroups/tabGroups.js.map +1 -1
- package/dist/components/layout/tabGroups/tabGroupsFocus.d.ts +16 -0
- package/dist/components/layout/tabGroups/tabGroupsFocus.js +102 -70
- package/dist/components/layout/tabGroups/tabGroupsFocus.js.map +1 -1
- package/dist/components/navigation/appBar/appBar.d.ts +12 -0
- package/dist/components/navigation/appBar/appBar.js.map +1 -1
- package/dist/components/navigation/breadcrumbs/breadcrumbs.d.ts +9 -0
- package/dist/components/navigation/breadcrumbs/breadcrumbs.js +47 -31
- package/dist/components/navigation/breadcrumbs/breadcrumbs.js.map +1 -1
- package/dist/components/navigation/tabs/tab/tab.js +71 -68
- package/dist/components/navigation/tabs/tab/tab.js.map +1 -1
- package/dist/components/navigation/tabs/tabs.d.ts +15 -1
- package/dist/components/navigation/tabs/tabs.js +19 -16
- package/dist/components/navigation/tabs/tabs.js.map +1 -1
- package/dist/components/navigation/tabs/tabsContext.d.ts +2 -0
- package/dist/components/navigation/tabs/tabsContext.js.map +1 -1
- package/dist/headless/components/dataDisplay/avatar/avatar.js +9 -9
- package/dist/headless/components/dataDisplay/avatar/avatar.js.map +1 -1
- package/dist/headless/components/dataDisplay/badge/badge.js.map +1 -1
- package/dist/headless/components/dataDisplay/dagView/dagView.js +133 -132
- package/dist/headless/components/dataDisplay/dagView/dagView.js.map +1 -1
- package/dist/headless/components/dataDisplay/meter/meter.js +32 -30
- package/dist/headless/components/dataDisplay/meter/meter.js.map +1 -1
- package/dist/headless/components/dataDisplay/statusDot/statusDot.js +19 -10
- package/dist/headless/components/dataDisplay/statusDot/statusDot.js.map +1 -1
- package/dist/headless/components/dataDisplay/treeView/treeView.js +139 -120
- package/dist/headless/components/dataDisplay/treeView/treeView.js.map +1 -1
- package/dist/headless/components/inputs/button/button.js +57 -51
- package/dist/headless/components/inputs/button/button.js.map +1 -1
- package/dist/headless/components/inputs/toggle/toggle.js +23 -23
- package/dist/headless/components/inputs/toggle/toggle.js.map +1 -1
- package/dist/headless/components/layout/appLayout/appLayout.js +52 -45
- package/dist/headless/components/layout/appLayout/appLayout.js.map +1 -1
- package/dist/headless/components/layout/screenDivider/screenDivider.js +88 -47
- package/dist/headless/components/layout/screenDivider/screenDivider.js.map +1 -1
- package/dist/headless/components/layout/sheet/sheet.js +124 -104
- package/dist/headless/components/layout/sheet/sheet.js.map +1 -1
- package/dist/headless/components/layout/tabGroups/tabGroups.js +500 -460
- package/dist/headless/components/layout/tabGroups/tabGroups.js.map +1 -1
- package/dist/headless/components/layout/tabGroups/tabGroupsFocus.js +102 -70
- package/dist/headless/components/layout/tabGroups/tabGroupsFocus.js.map +1 -1
- package/dist/headless/components/navigation/appBar/appBar.js.map +1 -1
- package/dist/headless/components/navigation/breadcrumbs/breadcrumbs.js +46 -30
- package/dist/headless/components/navigation/breadcrumbs/breadcrumbs.js.map +1 -1
- package/dist/headless/components/navigation/tabs/tab/tab.js +70 -67
- package/dist/headless/components/navigation/tabs/tab/tab.js.map +1 -1
- package/dist/headless/components/navigation/tabs/tabs.js +19 -16
- package/dist/headless/components/navigation/tabs/tabs.js.map +1 -1
- package/dist/headless/components/navigation/tabs/tabsContext.js.map +1 -1
- package/dist/headless/primitives/publishHeaderOffset.js +19 -0
- package/dist/headless/primitives/publishHeaderOffset.js.map +1 -0
- package/dist/headless/source/components/dataDisplay/avatar/avatar.jsx +1 -0
- package/dist/headless/source/components/dataDisplay/avatar/avatar.jsx.map +1 -1
- package/dist/headless/source/components/dataDisplay/badge/badge.jsx.map +1 -1
- package/dist/headless/source/components/dataDisplay/chart/chart.jsx +14 -12
- package/dist/headless/source/components/dataDisplay/chart/chart.jsx.map +1 -1
- package/dist/headless/source/components/dataDisplay/chart/chartFull.js +12 -0
- package/dist/headless/source/components/dataDisplay/chart/chartFull.js.map +7 -0
- package/dist/headless/source/components/dataDisplay/chart/core.js +11 -0
- package/dist/headless/source/components/dataDisplay/chart/core.js.map +7 -0
- package/dist/headless/source/components/dataDisplay/chart/index.js +1 -1
- package/dist/headless/source/components/dataDisplay/chart/index.js.map +1 -1
- package/dist/headless/source/components/dataDisplay/dagView/dagView.jsx +2 -1
- package/dist/headless/source/components/dataDisplay/dagView/dagView.jsx.map +1 -1
- package/dist/headless/source/components/dataDisplay/dagView/index.js.map +2 -2
- package/dist/headless/source/components/dataDisplay/meter/index.js.map +1 -1
- package/dist/headless/source/components/dataDisplay/meter/meter.jsx +14 -2
- package/dist/headless/source/components/dataDisplay/meter/meter.jsx.map +1 -1
- package/dist/headless/source/components/dataDisplay/statusDot/index.js.map +1 -1
- package/dist/headless/source/components/dataDisplay/statusDot/statusDot.jsx +13 -1
- package/dist/headless/source/components/dataDisplay/statusDot/statusDot.jsx.map +1 -1
- package/dist/headless/source/components/dataDisplay/treeView/treeView.jsx +16 -3
- package/dist/headless/source/components/dataDisplay/treeView/treeView.jsx.map +1 -1
- package/dist/headless/source/components/inputs/button/button.jsx +26 -1
- package/dist/headless/source/components/inputs/button/button.jsx.map +1 -1
- package/dist/headless/source/components/inputs/toggle/toggle.jsx +9 -2
- package/dist/headless/source/components/inputs/toggle/toggle.jsx.map +1 -1
- package/dist/headless/source/components/layout/appLayout/appLayout.jsx +14 -2
- package/dist/headless/source/components/layout/appLayout/appLayout.jsx.map +1 -1
- package/dist/headless/source/components/layout/screenDivider/screenDivider.jsx +102 -18
- package/dist/headless/source/components/layout/screenDivider/screenDivider.jsx.map +1 -1
- package/dist/headless/source/components/layout/sheet/sheet.jsx +14 -1
- package/dist/headless/source/components/layout/sheet/sheet.jsx.map +1 -1
- package/dist/headless/source/components/layout/tabGroups/index.js.map +1 -1
- package/dist/headless/source/components/layout/tabGroups/tabGroups.jsx +55 -1
- package/dist/headless/source/components/layout/tabGroups/tabGroups.jsx.map +1 -1
- package/dist/headless/source/components/layout/tabGroups/tabGroupsFocus.js +42 -0
- package/dist/headless/source/components/layout/tabGroups/tabGroupsFocus.js.map +2 -2
- package/dist/headless/source/components/navigation/appBar/appBar.jsx.map +1 -1
- package/dist/headless/source/components/navigation/breadcrumbs/breadcrumbs.jsx +39 -4
- package/dist/headless/source/components/navigation/breadcrumbs/breadcrumbs.jsx.map +1 -1
- package/dist/headless/source/components/navigation/tabs/tab/tab.jsx +7 -1
- package/dist/headless/source/components/navigation/tabs/tab/tab.jsx.map +1 -1
- package/dist/headless/source/components/navigation/tabs/tabs.jsx +5 -1
- package/dist/headless/source/components/navigation/tabs/tabs.jsx.map +1 -1
- package/dist/headless/source/components/navigation/tabs/tabsContext.js.map +2 -2
- package/dist/headless/source/index.js +8 -1
- package/dist/headless/source/index.js.map +2 -2
- package/dist/headless/source/primitives/publishHeaderOffset.js +34 -0
- package/dist/headless/source/primitives/publishHeaderOffset.js.map +7 -0
- package/dist/index.d.ts +5 -5
- package/dist/primitives/publishHeaderOffset.d.ts +7 -0
- package/dist/primitives/publishHeaderOffset.js +19 -0
- package/dist/primitives/publishHeaderOffset.js.map +1 -0
- package/dist/source/components/dataDisplay/avatar/avatar.jsx +1 -0
- package/dist/source/components/dataDisplay/avatar/avatar.jsx.map +1 -1
- package/dist/source/components/dataDisplay/badge/badge.jsx.map +1 -1
- package/dist/source/components/dataDisplay/chart/chart.jsx +14 -12
- package/dist/source/components/dataDisplay/chart/chart.jsx.map +1 -1
- package/dist/source/components/dataDisplay/chart/chartFull.js +12 -0
- package/dist/source/components/dataDisplay/chart/chartFull.js.map +7 -0
- package/dist/source/components/dataDisplay/chart/core.js +11 -0
- package/dist/source/components/dataDisplay/chart/core.js.map +7 -0
- package/dist/source/components/dataDisplay/chart/index.js +1 -1
- package/dist/source/components/dataDisplay/chart/index.js.map +1 -1
- package/dist/source/components/dataDisplay/dagView/dagView.jsx +2 -1
- package/dist/source/components/dataDisplay/dagView/dagView.jsx.map +1 -1
- package/dist/source/components/dataDisplay/dagView/index.js.map +2 -2
- package/dist/source/components/dataDisplay/meter/index.js.map +1 -1
- package/dist/source/components/dataDisplay/meter/meter.jsx +14 -2
- package/dist/source/components/dataDisplay/meter/meter.jsx.map +1 -1
- package/dist/source/components/dataDisplay/statusDot/index.js.map +1 -1
- package/dist/source/components/dataDisplay/statusDot/statusDot.jsx +13 -1
- package/dist/source/components/dataDisplay/statusDot/statusDot.jsx.map +1 -1
- package/dist/source/components/dataDisplay/treeView/treeView.jsx +16 -3
- package/dist/source/components/dataDisplay/treeView/treeView.jsx.map +1 -1
- package/dist/source/components/inputs/button/button.jsx +26 -1
- package/dist/source/components/inputs/button/button.jsx.map +1 -1
- package/dist/source/components/inputs/toggle/toggle.jsx +9 -2
- package/dist/source/components/inputs/toggle/toggle.jsx.map +1 -1
- package/dist/source/components/layout/appLayout/appLayout.jsx +14 -2
- package/dist/source/components/layout/appLayout/appLayout.jsx.map +1 -1
- package/dist/source/components/layout/screenDivider/screenDivider.jsx +102 -18
- package/dist/source/components/layout/screenDivider/screenDivider.jsx.map +1 -1
- package/dist/source/components/layout/sheet/sheet.jsx +14 -1
- package/dist/source/components/layout/sheet/sheet.jsx.map +1 -1
- package/dist/source/components/layout/tabGroups/index.js.map +1 -1
- package/dist/source/components/layout/tabGroups/tabGroups.jsx +55 -1
- package/dist/source/components/layout/tabGroups/tabGroups.jsx.map +1 -1
- package/dist/source/components/layout/tabGroups/tabGroupsFocus.js +42 -0
- package/dist/source/components/layout/tabGroups/tabGroupsFocus.js.map +2 -2
- package/dist/source/components/navigation/appBar/appBar.jsx.map +1 -1
- package/dist/source/components/navigation/breadcrumbs/breadcrumbs.jsx +39 -4
- package/dist/source/components/navigation/breadcrumbs/breadcrumbs.jsx.map +1 -1
- package/dist/source/components/navigation/tabs/tab/tab.jsx +7 -1
- package/dist/source/components/navigation/tabs/tab/tab.jsx.map +1 -1
- package/dist/source/components/navigation/tabs/tabs.jsx +5 -1
- package/dist/source/components/navigation/tabs/tabs.jsx.map +1 -1
- package/dist/source/components/navigation/tabs/tabsContext.js.map +2 -2
- package/dist/source/index.js +8 -1
- package/dist/source/index.js.map +2 -2
- package/dist/source/primitives/publishHeaderOffset.js +34 -0
- package/dist/source/primitives/publishHeaderOffset.js.map +7 -0
- package/package.json +9 -3
|
@@ -12,6 +12,29 @@ import { isServer } from "solid-js/web";
|
|
|
12
12
|
import cn from "clsx";
|
|
13
13
|
import { throttle } from "../../../utils/throttle";
|
|
14
14
|
import { mergeRefs } from "../../../primitives/mergeRefs";
|
|
15
|
+
const MIN_SIZE = /^(\d*\.?\d+)(px|%)?$/i;
|
|
16
|
+
const parseMinSize = (min) => {
|
|
17
|
+
if (min === void 0) return null;
|
|
18
|
+
if (typeof min === "number") return Number.isFinite(min) && min > 0 ? { value: min, unit: "px" } : null;
|
|
19
|
+
const match = MIN_SIZE.exec(min.trim());
|
|
20
|
+
if (!match) return null;
|
|
21
|
+
const [, digits = "", unit] = match;
|
|
22
|
+
const value = parseFloat(digits);
|
|
23
|
+
return value > 0 ? { value, unit: unit === "%" ? "%" : "px" } : null;
|
|
24
|
+
};
|
|
25
|
+
const minToPercent = (min, available) => {
|
|
26
|
+
const parsed = parseMinSize(min);
|
|
27
|
+
if (!parsed) return 0;
|
|
28
|
+
if (parsed.unit === "%") return parsed.value;
|
|
29
|
+
return available > 0 ? parsed.value / available * 100 : 0;
|
|
30
|
+
};
|
|
31
|
+
const getBounds = (minFirst, minSecond, available) => {
|
|
32
|
+
const low = Math.min(100, minToPercent(minFirst, available));
|
|
33
|
+
const high = Math.max(low, 100 - minToPercent(minSecond, available));
|
|
34
|
+
return [low, high];
|
|
35
|
+
};
|
|
36
|
+
const minimumsClash = (minFirst, minSecond, available) => Math.min(100, minToPercent(minFirst, available)) + minToPercent(minSecond, available) > 100;
|
|
37
|
+
const clampDivider = (value, [low, high]) => Math.max(low, Math.min(high, value));
|
|
15
38
|
const getInitialDivider = (sizeFirst, sizeSecond) => {
|
|
16
39
|
if (!sizeFirst && sizeSecond) {
|
|
17
40
|
return 100 - sizeSecond;
|
|
@@ -35,28 +58,62 @@ function ScreenDivider(props) {
|
|
|
35
58
|
"onSizeChange",
|
|
36
59
|
"keyboardStep",
|
|
37
60
|
"resizerAriaLabel",
|
|
61
|
+
"minSizeFirst",
|
|
62
|
+
"minSizeSecond",
|
|
63
|
+
"collapsed",
|
|
38
64
|
"ref"
|
|
39
65
|
]);
|
|
40
66
|
let wrapperEl;
|
|
67
|
+
let resizerEl;
|
|
68
|
+
let lastBar = 0;
|
|
41
69
|
const [divider, setDivider] = createSignal(getInitialDivider(local.sizeFirst, local.sizeSecond));
|
|
42
70
|
const [isDragging, setIsDragging] = createSignal(false);
|
|
71
|
+
const [available, setAvailable] = createSignal(0);
|
|
72
|
+
const vertical = () => local.orientation === "vertical";
|
|
73
|
+
const measureAvailable = () => {
|
|
74
|
+
const main = (el) => {
|
|
75
|
+
const rect = el?.getBoundingClientRect();
|
|
76
|
+
return rect ? vertical() ? rect.height : rect.width : 0;
|
|
77
|
+
};
|
|
78
|
+
const bar = main(resizerEl);
|
|
79
|
+
if (bar > 0) lastBar = bar;
|
|
80
|
+
return Math.max(0, main(wrapperEl) - (bar > 0 ? bar : lastBar));
|
|
81
|
+
};
|
|
82
|
+
createEffect(() => {
|
|
83
|
+
if (isServer) return;
|
|
84
|
+
if (!parseMinSize(local.minSizeFirst) && !parseMinSize(local.minSizeSecond)) return;
|
|
85
|
+
void local.orientation;
|
|
86
|
+
void local.collapsed;
|
|
87
|
+
const update = () => {
|
|
88
|
+
setAvailable(measureAvailable());
|
|
89
|
+
};
|
|
90
|
+
update();
|
|
91
|
+
if (!wrapperEl || typeof ResizeObserver === "undefined") return;
|
|
92
|
+
const observer = new ResizeObserver(update);
|
|
93
|
+
observer.observe(wrapperEl);
|
|
94
|
+
onCleanup(() => observer.disconnect());
|
|
95
|
+
});
|
|
43
96
|
createEffect(() => {
|
|
44
97
|
setDivider(getInitialDivider(local.sizeFirst, local.sizeSecond));
|
|
45
98
|
});
|
|
99
|
+
const bounds = () => getBounds(local.minSizeFirst, local.minSizeSecond, available());
|
|
100
|
+
const shown = () => clampDivider(divider(), bounds());
|
|
46
101
|
createEffect(() => {
|
|
47
|
-
local.onSizeChange?.(
|
|
102
|
+
local.onSizeChange?.(shown());
|
|
48
103
|
});
|
|
49
104
|
const resolved = resolveChildren(() => local.children);
|
|
50
105
|
const child = () => resolved.toArray();
|
|
51
106
|
const dragOver = throttle((event) => {
|
|
52
|
-
if (!isDragging()) return;
|
|
107
|
+
if (!isDragging() || local.collapsed) return;
|
|
53
108
|
const wrapper = wrapperEl;
|
|
54
109
|
if (!wrapper) return;
|
|
55
110
|
const rect = wrapper.getBoundingClientRect();
|
|
56
|
-
const total =
|
|
57
|
-
const cursor =
|
|
111
|
+
const total = vertical() ? rect.height : rect.width;
|
|
112
|
+
const cursor = vertical() ? event.clientY - rect.top : event.clientX - rect.left;
|
|
58
113
|
const clamped = Math.max(5, Math.min(total - 5, cursor));
|
|
59
|
-
setDivider(
|
|
114
|
+
setDivider(
|
|
115
|
+
clampDivider(clamped / total * 100, getBounds(local.minSizeFirst, local.minSizeSecond, measureAvailable()))
|
|
116
|
+
);
|
|
60
117
|
}, 1e3 / 60);
|
|
61
118
|
const handleDragStart = () => {
|
|
62
119
|
setIsDragging(true);
|
|
@@ -79,46 +136,73 @@ function ScreenDivider(props) {
|
|
|
79
136
|
});
|
|
80
137
|
});
|
|
81
138
|
const onResizerKeyDown = (event) => {
|
|
82
|
-
const decreaseKey =
|
|
83
|
-
const increaseKey =
|
|
139
|
+
const decreaseKey = vertical() ? "ArrowUp" : "ArrowLeft";
|
|
140
|
+
const increaseKey = vertical() ? "ArrowDown" : "ArrowRight";
|
|
141
|
+
const keyBounds = getBounds(local.minSizeFirst, local.minSizeSecond, measureAvailable());
|
|
84
142
|
if (event.key === decreaseKey) {
|
|
85
143
|
event.preventDefault();
|
|
86
|
-
setDivider((
|
|
144
|
+
setDivider(clampDivider(Math.max(0, shown() - local.keyboardStep), keyBounds));
|
|
87
145
|
} else if (event.key === increaseKey) {
|
|
88
146
|
event.preventDefault();
|
|
89
|
-
setDivider((
|
|
147
|
+
setDivider(clampDivider(Math.min(100, shown() + local.keyboardStep), keyBounds));
|
|
90
148
|
} else if (event.key === "Home") {
|
|
91
149
|
event.preventDefault();
|
|
92
|
-
setDivider(0);
|
|
150
|
+
setDivider(clampDivider(0, keyBounds));
|
|
93
151
|
} else if (event.key === "End") {
|
|
94
152
|
event.preventDefault();
|
|
95
|
-
setDivider(100);
|
|
153
|
+
setDivider(clampDivider(100, keyBounds));
|
|
96
154
|
}
|
|
97
155
|
};
|
|
98
|
-
const
|
|
99
|
-
|
|
156
|
+
const sizeOf = (pane) => {
|
|
157
|
+
const share = pane === "first" ? shown() : 100 - shown();
|
|
158
|
+
return local.collapsed ? local.collapsed === pane ? 0 : 100 : share;
|
|
159
|
+
};
|
|
160
|
+
const paneStyle = (pane) => {
|
|
161
|
+
const style = vertical() ? { height: `${sizeOf(pane)}%` } : { width: `${sizeOf(pane)}%` };
|
|
162
|
+
const min = parseMinSize(pane === "first" ? local.minSizeFirst : local.minSizeSecond);
|
|
163
|
+
const gives = pane === "second" && minimumsClash(local.minSizeFirst, local.minSizeSecond, available());
|
|
164
|
+
if (!local.collapsed && !gives && min?.unit === "px")
|
|
165
|
+
style[vertical() ? "min-height" : "min-width"] = `${min.value}px`;
|
|
166
|
+
return style;
|
|
167
|
+
};
|
|
100
168
|
return <div
|
|
101
169
|
class={cn("smwb-screen-divider__wrapper", `smwb-${local.orientation}`, local.class)}
|
|
102
170
|
ref={mergeRefs(local.ref, (el) => wrapperEl = el)}
|
|
103
171
|
data-testid="screen-divider"
|
|
104
172
|
>
|
|
105
|
-
<div
|
|
173
|
+
<div
|
|
174
|
+
class={cn(
|
|
175
|
+
"smwb-screen-divider__first",
|
|
176
|
+
`smwb-${local.orientation}`,
|
|
177
|
+
local.collapsed === "first" && "smwb-collapsed"
|
|
178
|
+
)}
|
|
179
|
+
style={paneStyle("first")}
|
|
180
|
+
>
|
|
106
181
|
{child()[0] ?? ""}
|
|
107
182
|
</div>
|
|
108
183
|
<span
|
|
184
|
+
ref={(el) => resizerEl = el}
|
|
109
185
|
class="smwb-screen-divider__resizer"
|
|
186
|
+
hidden={local.collapsed !== void 0}
|
|
110
187
|
onPointerDown={handleDragStart}
|
|
111
188
|
draggable={false}
|
|
112
189
|
role="separator"
|
|
113
190
|
aria-orientation={local.orientation === "vertical" ? "horizontal" : "vertical"}
|
|
114
|
-
aria-valuenow={Math.round(
|
|
115
|
-
aria-valuemin={0}
|
|
116
|
-
aria-valuemax={
|
|
191
|
+
aria-valuenow={Math.round(shown())}
|
|
192
|
+
aria-valuemin={Math.round(bounds()[0])}
|
|
193
|
+
aria-valuemax={Math.round(bounds()[1])}
|
|
117
194
|
aria-label={local.resizerAriaLabel}
|
|
118
195
|
tabindex={0}
|
|
119
196
|
onKeyDown={onResizerKeyDown}
|
|
120
197
|
/>
|
|
121
|
-
<div
|
|
198
|
+
<div
|
|
199
|
+
class={cn(
|
|
200
|
+
"smwb-screen-divider__second",
|
|
201
|
+
`smwb-${local.orientation}`,
|
|
202
|
+
local.collapsed === "second" && "smwb-collapsed"
|
|
203
|
+
)}
|
|
204
|
+
style={paneStyle("second")}
|
|
205
|
+
>
|
|
122
206
|
{child()[1] ?? ""}
|
|
123
207
|
</div>
|
|
124
208
|
</div>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../src/components/layout/screenDivider/screenDivider.tsx"],"sourcesContent":["import \"@smwb/ui-styles/less/tokens.entry.less\";\nimport \"@smwb/ui-styles/less/components/screenDivider/screenDivider.entry.less\";\nimport {\n type JSX,\n type ParentProps,\n children as resolveChildren,\n createEffect,\n createSignal,\n mergeProps,\n onCleanup,\n splitProps,\n} from \"solid-js\";\nimport { isServer } from \"solid-js/web\";\nimport cn from \"clsx\";\nimport { throttle } from \"../../../utils/throttle\";\nimport { mergeRefs, type Ref } from \"../../../primitives/mergeRefs\";\n\nexport interface ScreenDividerProps {\n sizeFirst?: number;\n sizeSecond?: number;\n class?: string;\n orientation?: \"horizontal\" | \"vertical\";\n /** Called with the first pane size (percent) whenever it changes. */\n onSizeChange?: (sizeFirst: number) => void;\n /** Step (in percent) applied on arrow-key resize. */\n keyboardStep?: number;\n /** Accessible label for the resizer handle. */\n resizerAriaLabel?: string;\n ref?: Ref<HTMLDivElement>;\n}\n\nconst getInitialDivider = (sizeFirst?: number | null, sizeSecond?: number | null): number => {\n if (!sizeFirst && sizeSecond) {\n return 100 - sizeSecond;\n }\n if (sizeFirst) {\n return sizeFirst;\n }\n return 50;\n};\n\nexport function ScreenDivider(props: ParentProps<ScreenDividerProps>): JSX.Element {\n const merged = mergeProps(\n { orientation: \"horizontal\" as const, keyboardStep: 5, resizerAriaLabel: \"Resize panels\" },\n props\n );\n const [local] = splitProps(merged, [\n \"class\",\n \"orientation\",\n \"sizeFirst\",\n \"sizeSecond\",\n \"children\",\n \"onSizeChange\",\n \"keyboardStep\",\n \"resizerAriaLabel\",\n \"ref\",\n ]);\n\n let wrapperEl: HTMLDivElement | undefined;\n const [divider, setDivider] = createSignal(getInitialDivider(local.sizeFirst, local.sizeSecond));\n const [isDragging, setIsDragging] = createSignal(false);\n\n // Sync with controlled size props.\n createEffect(() => {\n setDivider(getInitialDivider(local.sizeFirst, local.sizeSecond));\n });\n\n // Report size changes.\n createEffect(() => {\n local.onSizeChange?.(divider());\n });\n\n const resolved = resolveChildren(() => local.children);\n const child = (): unknown[] => resolved.toArray();\n\n const dragOver = throttle((event: PointerEvent) => {\n if (!isDragging()) return;\n const wrapper = wrapperEl;\n if (!wrapper) return;\n // Viewport-relative rect — using offsetLeft/offsetTop here is wrong\n // because they are relative to the nearest positioned ancestor, not\n // the viewport that clientX/clientY live in (this is what made the\n // divider drift away from the cursor when the wrapper was nested).\n const rect = wrapper.getBoundingClientRect();\n const total = local.orientation === \"vertical\" ? rect.height : rect.width;\n const cursor = local.orientation === \"vertical\" ? event.clientY - rect.top : event.clientX - rect.left;\n // 5px margin on each side so neither pane fully collapses.\n const clamped = Math.max(5, Math.min(total - 5, cursor));\n setDivider((clamped / total) * 100);\n }, 1000 / 60);\n\n const handleDragStart = (): void => {\n setIsDragging(true);\n };\n const handleDragEnd = (): void => {\n setIsDragging(false);\n };\n\n createEffect(() => {\n if (isServer) return;\n if (isDragging()) {\n document.body.addEventListener(\"pointerup\", handleDragEnd);\n document.body.addEventListener(\"pointermove\", dragOver);\n document.body.style.cursor = local.orientation === \"horizontal\" ? \"e-resize\" : \"n-resize\";\n } else {\n document.body.style.cursor = \"default\";\n }\n onCleanup(() => {\n document.body.removeEventListener(\"pointerup\", handleDragEnd);\n document.body.removeEventListener(\"pointermove\", dragOver);\n });\n });\n\n const onResizerKeyDown: JSX.EventHandler<HTMLSpanElement, KeyboardEvent> = (event) => {\n const decreaseKey = local.orientation === \"vertical\" ? \"ArrowUp\" : \"ArrowLeft\";\n const increaseKey = local.orientation === \"vertical\" ? \"ArrowDown\" : \"ArrowRight\";\n if (event.key === decreaseKey) {\n event.preventDefault();\n setDivider((prev) => Math.max(0, prev - local.keyboardStep));\n } else if (event.key === increaseKey) {\n event.preventDefault();\n setDivider((prev) => Math.min(100, prev + local.keyboardStep));\n } else if (event.key === \"Home\") {\n event.preventDefault();\n setDivider(0);\n } else if (event.key === \"End\") {\n event.preventDefault();\n setDivider(100);\n }\n };\n\n const firstChildStyle = (): JSX.CSSProperties =>\n local.orientation === \"vertical\" ? { height: `${divider()}%` } : { width: `${divider()}%` };\n const secondChildStyle = (): JSX.CSSProperties =>\n local.orientation === \"vertical\" ? { height: `${100 - divider()}%` } : { width: `${100 - divider()}%` };\n\n return (\n <div\n class={cn(\"smwb-screen-divider__wrapper\", `smwb-${local.orientation}`, local.class)}\n ref={mergeRefs(local.ref, (el) => (wrapperEl = el))}\n data-testid=\"screen-divider\"\n >\n <div class={cn(\"smwb-screen-divider__first\", `smwb-${local.orientation}`)} style={firstChildStyle()}>\n {(child()[0] ?? \"\") as JSX.Element}\n </div>\n <span\n class=\"smwb-screen-divider__resizer\"\n onPointerDown={handleDragStart}\n draggable={false}\n role=\"separator\"\n aria-orientation={local.orientation === \"vertical\" ? \"horizontal\" : \"vertical\"}\n aria-valuenow={Math.round(divider())}\n aria-valuemin={0}\n aria-valuemax={100}\n aria-label={local.resizerAriaLabel}\n tabindex={0}\n onKeyDown={onResizerKeyDown}\n />\n <div class={cn(\"smwb-screen-divider__second\", `smwb-${local.orientation}`)} style={secondChildStyle()}>\n {(child()[1] ?? \"\") as JSX.Element}\n </div>\n </div>\n );\n}\n\nexport default ScreenDivider;\n"],"mappings":"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","names":[],"file":"screenDivider.jsx"}
|
|
1
|
+
{"version":3,"sources":["../../../../../src/components/layout/screenDivider/screenDivider.tsx"],"sourcesContent":["import \"@smwb/ui-styles/less/tokens.entry.less\";\nimport \"@smwb/ui-styles/less/components/screenDivider/screenDivider.entry.less\";\nimport {\n type JSX,\n type ParentProps,\n children as resolveChildren,\n createEffect,\n createSignal,\n mergeProps,\n onCleanup,\n splitProps,\n} from \"solid-js\";\nimport { isServer } from \"solid-js/web\";\nimport cn from \"clsx\";\nimport { throttle } from \"../../../utils/throttle\";\nimport { mergeRefs, type Ref } from \"../../../primitives/mergeRefs\";\n\nexport interface ScreenDividerProps {\n sizeFirst?: number;\n sizeSecond?: number;\n class?: string;\n orientation?: \"horizontal\" | \"vertical\";\n /** Called with the first pane size (percent) whenever it changes. */\n onSizeChange?: (sizeFirst: number) => void;\n /** Step (in percent) applied on arrow-key resize. */\n keyboardStep?: number;\n /** Accessible label for the resizer handle. */\n resizerAriaLabel?: string;\n /**\n * Smallest size of the first pane that dragging and the keyboard (arrows, Home, End) leave it:\n * a number is pixels, a string is `\"120px\"` or `\"20%\"`; any other unit is ignored. Unset means no minimum.\n */\n minSizeFirst?: number | string | undefined;\n /** Smallest size of the second pane, same units as `minSizeFirst`. If both minimums do not fit, the first one wins. */\n minSizeSecond?: number | string | undefined;\n /**\n * Folds one pane to zero and hides the resizer; the other pane takes the whole divider. Both panes stay mounted,\n * and clearing it brings back the split the divider had. `onSizeChange` is not called for the fold.\n */\n collapsed?: \"first\" | \"second\" | undefined;\n ref?: Ref<HTMLDivElement>;\n}\n\ntype ParsedMin = { value: number; unit: \"px\" | \"%\" } | null;\n\n// A bare number, or a number with `px` / `%`; any other unit (`rem`, `vh`, `calc(...)`) is no minimum.\nconst MIN_SIZE = /^(\\d*\\.?\\d+)(px|%)?$/i;\n\nconst parseMinSize = (min: number | string | undefined): ParsedMin => {\n if (min === undefined) return null;\n if (typeof min === \"number\") return Number.isFinite(min) && min > 0 ? { value: min, unit: \"px\" } : null;\n const match = MIN_SIZE.exec(min.trim());\n if (!match) return null;\n const [, digits = \"\", unit] = match;\n const value = parseFloat(digits);\n return value > 0 ? { value, unit: unit === \"%\" ? \"%\" : \"px\" } : null;\n};\n\n/** A minimum as a share (percent) of `available` pixels, the space the two panes split. */\nconst minToPercent = (min: number | string | undefined, available: number): number => {\n const parsed = parseMinSize(min);\n if (!parsed) return 0;\n if (parsed.unit === \"%\") return parsed.value;\n return available > 0 ? (parsed.value / available) * 100 : 0;\n};\n\n/** The range of the first pane's share: the second pane's minimum caps it, the first pane's minimum wins a clash. */\nconst getBounds = (\n minFirst: number | string | undefined,\n minSecond: number | string | undefined,\n available: number\n): [number, number] => {\n const low = Math.min(100, minToPercent(minFirst, available));\n const high = Math.max(low, 100 - minToPercent(minSecond, available));\n return [low, high];\n};\n\n/** Whether the first pane's minimum leaves the second less than its own: then the first one wins. */\nconst minimumsClash = (\n minFirst: number | string | undefined,\n minSecond: number | string | undefined,\n available: number\n): boolean => Math.min(100, minToPercent(minFirst, available)) + minToPercent(minSecond, available) > 100;\n\nconst clampDivider = (value: number, [low, high]: [number, number]) => Math.max(low, Math.min(high, value));\n\nconst getInitialDivider = (sizeFirst?: number | null, sizeSecond?: number | null): number => {\n if (!sizeFirst && sizeSecond) {\n return 100 - sizeSecond;\n }\n if (sizeFirst) {\n return sizeFirst;\n }\n return 50;\n};\n\nexport function ScreenDivider(props: ParentProps<ScreenDividerProps>): JSX.Element {\n const merged = mergeProps(\n { orientation: \"horizontal\" as const, keyboardStep: 5, resizerAriaLabel: \"Resize panels\" },\n props\n );\n const [local] = splitProps(merged, [\n \"class\",\n \"orientation\",\n \"sizeFirst\",\n \"sizeSecond\",\n \"children\",\n \"onSizeChange\",\n \"keyboardStep\",\n \"resizerAriaLabel\",\n \"minSizeFirst\",\n \"minSizeSecond\",\n \"collapsed\",\n \"ref\",\n ]);\n\n let wrapperEl: HTMLDivElement | undefined;\n let resizerEl: HTMLSpanElement | undefined;\n // The thickness the resizer had the last time it was shown.\n let lastBar = 0;\n const [divider, setDivider] = createSignal(getInitialDivider(local.sizeFirst, local.sizeSecond));\n const [isDragging, setIsDragging] = createSignal(false);\n const [available, setAvailable] = createSignal(0);\n const vertical = (): boolean => local.orientation === \"vertical\";\n\n // The pixels the two panes split: the wrapper along its main axis less the resizer. A hidden resizer measures 0, so\n // the thickness it had while shown is subtracted instead, and folding a pane leaves the minimums where they were.\n const measureAvailable = (): number => {\n const main = (el: HTMLElement | undefined): number => {\n const rect = el?.getBoundingClientRect();\n return rect ? (vertical() ? rect.height : rect.width) : 0;\n };\n const bar = main(resizerEl);\n if (bar > 0) lastBar = bar;\n return Math.max(0, main(wrapperEl) - (bar > 0 ? bar : lastBar));\n };\n\n createEffect(() => {\n if (isServer) return;\n if (!parseMinSize(local.minSizeFirst) && !parseMinSize(local.minSizeSecond)) return;\n // Measured again when the orientation or the folded pane changes, and when the wrapper resizes.\n void local.orientation;\n void local.collapsed;\n const update = (): void => {\n setAvailable(measureAvailable());\n };\n update();\n if (!wrapperEl || typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(update);\n observer.observe(wrapperEl);\n onCleanup(() => observer.disconnect());\n });\n\n // Sync with controlled size props.\n createEffect(() => {\n setDivider(getInitialDivider(local.sizeFirst, local.sizeSecond));\n });\n\n const bounds = (): [number, number] => getBounds(local.minSizeFirst, local.minSizeSecond, available());\n // What the panes show: the split held within the minimums, whatever the props or the last resize said.\n const shown = (): number => clampDivider(divider(), bounds());\n\n // Report size changes.\n createEffect(() => {\n local.onSizeChange?.(shown());\n });\n\n const resolved = resolveChildren(() => local.children);\n const child = (): unknown[] => resolved.toArray();\n\n const dragOver = throttle((event: PointerEvent) => {\n if (!isDragging() || local.collapsed) return;\n const wrapper = wrapperEl;\n if (!wrapper) return;\n // Viewport-relative rect — using offsetLeft/offsetTop here is wrong\n // because they are relative to the nearest positioned ancestor, not\n // the viewport that clientX/clientY live in (this is what made the\n // divider drift away from the cursor when the wrapper was nested).\n const rect = wrapper.getBoundingClientRect();\n const total = vertical() ? rect.height : rect.width;\n const cursor = vertical() ? event.clientY - rect.top : event.clientX - rect.left;\n // 5px margin on each side so neither pane fully collapses.\n const clamped = Math.max(5, Math.min(total - 5, cursor));\n setDivider(\n clampDivider((clamped / total) * 100, getBounds(local.minSizeFirst, local.minSizeSecond, measureAvailable()))\n );\n }, 1000 / 60);\n\n const handleDragStart = (): void => {\n setIsDragging(true);\n };\n const handleDragEnd = (): void => {\n setIsDragging(false);\n };\n\n createEffect(() => {\n if (isServer) return;\n if (isDragging()) {\n document.body.addEventListener(\"pointerup\", handleDragEnd);\n document.body.addEventListener(\"pointermove\", dragOver);\n document.body.style.cursor = local.orientation === \"horizontal\" ? \"e-resize\" : \"n-resize\";\n } else {\n document.body.style.cursor = \"default\";\n }\n onCleanup(() => {\n document.body.removeEventListener(\"pointerup\", handleDragEnd);\n document.body.removeEventListener(\"pointermove\", dragOver);\n });\n });\n\n const onResizerKeyDown: JSX.EventHandler<HTMLSpanElement, KeyboardEvent> = (event) => {\n const decreaseKey = vertical() ? \"ArrowUp\" : \"ArrowLeft\";\n const increaseKey = vertical() ? \"ArrowDown\" : \"ArrowRight\";\n // Measured now rather than read from the signal, so the minimums hold at the size the divider has this instant.\n const keyBounds = getBounds(local.minSizeFirst, local.minSizeSecond, measureAvailable());\n if (event.key === decreaseKey) {\n event.preventDefault();\n setDivider(clampDivider(Math.max(0, shown() - local.keyboardStep), keyBounds));\n } else if (event.key === increaseKey) {\n event.preventDefault();\n setDivider(clampDivider(Math.min(100, shown() + local.keyboardStep), keyBounds));\n } else if (event.key === \"Home\") {\n event.preventDefault();\n setDivider(clampDivider(0, keyBounds));\n } else if (event.key === \"End\") {\n event.preventDefault();\n setDivider(clampDivider(100, keyBounds));\n }\n };\n\n // A folded pane is 0 and the other 100; both stay in the tree, so nothing inside either remounts.\n const sizeOf = (pane: \"first\" | \"second\"): number => {\n const share = pane === \"first\" ? shown() : 100 - shown();\n return local.collapsed ? (local.collapsed === pane ? 0 : 100) : share;\n };\n const paneStyle = (pane: \"first\" | \"second\"): JSX.CSSProperties => {\n const style: JSX.CSSProperties = vertical() ? { height: `${sizeOf(pane)}%` } : { width: `${sizeOf(pane)}%` };\n // Pixel minimums also floor the pane in CSS, so it holds between a resize and the next measurement.\n const min = parseMinSize(pane === \"first\" ? local.minSizeFirst : local.minSizeSecond);\n // The second pane's floor would beat a first pane's minimum that wins the clash, so it gives way to it.\n const gives = pane === \"second\" && minimumsClash(local.minSizeFirst, local.minSizeSecond, available());\n if (!local.collapsed && !gives && min?.unit === \"px\")\n style[vertical() ? \"min-height\" : \"min-width\"] = `${min.value}px`;\n return style;\n };\n\n return (\n <div\n class={cn(\"smwb-screen-divider__wrapper\", `smwb-${local.orientation}`, local.class)}\n ref={mergeRefs(local.ref, (el) => (wrapperEl = el))}\n data-testid=\"screen-divider\"\n >\n <div\n class={cn(\n \"smwb-screen-divider__first\",\n `smwb-${local.orientation}`,\n local.collapsed === \"first\" && \"smwb-collapsed\"\n )}\n style={paneStyle(\"first\")}\n >\n {(child()[0] ?? \"\") as JSX.Element}\n </div>\n <span\n ref={(el) => (resizerEl = el)}\n class=\"smwb-screen-divider__resizer\"\n hidden={local.collapsed !== undefined}\n onPointerDown={handleDragStart}\n draggable={false}\n role=\"separator\"\n aria-orientation={local.orientation === \"vertical\" ? \"horizontal\" : \"vertical\"}\n aria-valuenow={Math.round(shown())}\n aria-valuemin={Math.round(bounds()[0])}\n aria-valuemax={Math.round(bounds()[1])}\n aria-label={local.resizerAriaLabel}\n tabindex={0}\n onKeyDown={onResizerKeyDown}\n />\n <div\n class={cn(\n \"smwb-screen-divider__second\",\n `smwb-${local.orientation}`,\n local.collapsed === \"second\" && \"smwb-collapsed\"\n )}\n style={paneStyle(\"second\")}\n >\n {(child()[1] ?? \"\") as JSX.Element}\n </div>\n </div>\n );\n}\n\nexport default ScreenDivider;\n"],"mappings":"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","names":[],"file":"screenDivider.jsx"}
|
|
@@ -11,6 +11,10 @@ import { isTopOverlayLayer } from "../../../primitives/overlayLayer";
|
|
|
11
11
|
import { restoreFocus } from "../../../primitives/restoreFocus";
|
|
12
12
|
import { mergeRefs } from "../../../primitives/mergeRefs";
|
|
13
13
|
import { mergeStyle } from "../../../primitives/mergeStyle";
|
|
14
|
+
const landedAboveScrim = (event, scrim) => {
|
|
15
|
+
const y = "changedTouches" in event ? event.changedTouches[0]?.clientY : event.clientY;
|
|
16
|
+
return scrim != null && y !== void 0 && y < scrim.getBoundingClientRect().top;
|
|
17
|
+
};
|
|
14
18
|
const isVerticalAxis = (placement) => placement === "bottom" || placement === "top";
|
|
15
19
|
function SheetBlock(props) {
|
|
16
20
|
const merged = mergeProps(
|
|
@@ -222,6 +226,7 @@ function Sheet(props) {
|
|
|
222
226
|
"placement",
|
|
223
227
|
"disableOutsideClick",
|
|
224
228
|
"closeOnEsc",
|
|
229
|
+
"offsetTop",
|
|
225
230
|
"ref"
|
|
226
231
|
]);
|
|
227
232
|
const [mode, setMode] = createSignal(null);
|
|
@@ -247,6 +252,10 @@ function Sheet(props) {
|
|
|
247
252
|
local.onClose?.();
|
|
248
253
|
setMode(null);
|
|
249
254
|
};
|
|
255
|
+
const wrapperStyle = () => {
|
|
256
|
+
const offset = local.offsetTop;
|
|
257
|
+
return offset === void 0 ? void 0 : { "--smui-sheet-offset-top": typeof offset === "number" ? `${offset}px` : offset };
|
|
258
|
+
};
|
|
250
259
|
const block = () => <SheetBlock
|
|
251
260
|
{...rest}
|
|
252
261
|
ref={local.ref}
|
|
@@ -267,9 +276,12 @@ function Sheet(props) {
|
|
|
267
276
|
fallback={<div
|
|
268
277
|
ref={(el) => wrapperEl = el}
|
|
269
278
|
data-smwb-overlay-layer
|
|
279
|
+
style={wrapperStyle()}
|
|
270
280
|
class={cn("smwb-sheet__overlay", "smwb-dark", `smwb-${local.placement}`, `smwb-sheet_${local.variant}`, outer.classes())}
|
|
271
281
|
>
|
|
272
|
-
<ClickAwayListener
|
|
282
|
+
<ClickAwayListener
|
|
283
|
+
onClickAway={(event) => !local.disableOutsideClick && !landedAboveScrim(event, wrapperEl) && handleClose()}
|
|
284
|
+
>
|
|
273
285
|
{block()}
|
|
274
286
|
</ClickAwayListener>
|
|
275
287
|
</div>}
|
|
@@ -277,6 +289,7 @@ function Sheet(props) {
|
|
|
277
289
|
<div
|
|
278
290
|
ref={(el) => wrapperEl = el}
|
|
279
291
|
data-smwb-overlay-layer
|
|
292
|
+
style={wrapperStyle()}
|
|
280
293
|
class={cn(local.disableOutsideClick ? "smwb-sheet__overlay" : "smwb-sheet__default", outer.classes())}
|
|
281
294
|
>
|
|
282
295
|
{block()}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../src/components/layout/sheet/sheet.tsx"],"sourcesContent":["import \"@smwb/ui-styles/less/tokens.entry.less\";\nimport \"@smwb/ui-styles/less/components/sheet/sheet.entry.less\";\nimport { type JSX, type ParentProps, Show, createEffect, createSignal, mergeProps, on, onCleanup, splitProps } from \"solid-js\";\nimport { Portal, isServer } from \"solid-js/web\";\nimport cn from \"clsx\";\nimport { Button } from \"../../inputs/button\";\nimport { Icon } from \"../../base/icon\";\nimport { ClickAwayListener } from \"../../clickAwayListener\";\nimport { createCssTransition } from \"../../../primitives/createCssTransition\";\nimport { isTopOverlayLayer } from \"../../../primitives/overlayLayer\";\nimport { restoreFocus } from \"../../../primitives/restoreFocus\";\nimport { mergeRefs, type Ref } from \"../../../primitives/mergeRefs\";\nimport { mergeStyle } from \"../../../primitives/mergeStyle\";\n\nexport interface SheetProps extends Omit<JSX.HTMLAttributes<HTMLDivElement>, \"onResize\"> {\n placement?: \"top\" | \"bottom\" | \"right\" | \"left\";\n isOpen: boolean;\n onClose?: (() => void) | undefined;\n variant?: \"default\" | \"modal\";\n withSizeToggle?: boolean;\n /** Hide built-in header actions; onClose still handles Esc and backdrop. */\n hideHeaderActions?: boolean;\n reverseHeaderActions?: boolean;\n /** Accessible name of the header close button (default \"Close sheet\"). */\n closeAriaLabel?: string;\n /**\n * Accessible name of the size toggle while the sheet is at its normal size (default \"Expand sheet\"). The toggle\n * cycles normal, expanded, collapsed and back, and its name always says what the next press does; the\n * name changing is how it exposes its state. It shows the `open_in_full` icon, and `close_fullscreen` while expanded.\n */\n expandAriaLabel?: string;\n /** Accessible name of the size toggle while the sheet is expanded (default \"Collapse sheet\"). */\n collapseAriaLabel?: string;\n /** Accessible name of the size toggle while the sheet is collapsed to its header (default \"Restore sheet size\"). */\n restoreAriaLabel?: string;\n size?: string;\n disableOutsideClick?: boolean;\n /** Close the sheet when Escape is pressed. */\n closeOnEsc?: boolean;\n /** Allow drag-to-resize (mobile bottom-sheet style). Off by default. */\n resizable?: boolean;\n /** Minimum size in px while resizing (default 80). */\n minSize?: number;\n /** Maximum size in px while resizing (default: viewport dimension). */\n maxSize?: number;\n /** Step in px for keyboard resizing (default 24). */\n resizeStep?: number;\n /** Accessible label for the resize handle. */\n resizeHandleAriaLabel?: string;\n /** Called with the new size in px while/after resizing. */\n onResize?: (size: number) => void;\n \"aria-label\"?: string;\n /** Test id of the sheet panel (the dialog); `sheet` unless given. */\n \"data-testid\"?: string | undefined;\n ref?: Ref<HTMLDivElement>;\n}\n\ntype Mode = \"small\" | \"expanded\" | null;\n\nconst isVerticalAxis = (placement: SheetProps[\"placement\"]): boolean => placement === \"bottom\" || placement === \"top\";\n\ninterface SheetBlockProps extends ParentProps<SheetProps> {\n mode: Mode;\n handleSizeButton: () => void;\n handleClose: () => void;\n}\n\nfunction SheetBlock(props: SheetBlockProps): JSX.Element {\n const merged = mergeProps(\n {\n size: \"60%\",\n withSizeToggle: true,\n hideHeaderActions: false,\n reverseHeaderActions: false,\n closeAriaLabel: \"Close sheet\",\n expandAriaLabel: \"Expand sheet\",\n collapseAriaLabel: \"Collapse sheet\",\n restoreAriaLabel: \"Restore sheet size\",\n resizable: false,\n minSize: 80,\n resizeStep: 24,\n resizeHandleAriaLabel: \"Resize sheet\",\n },\n props\n );\n const [local, rest] = splitProps(merged, [\n \"isOpen\",\n \"placement\",\n \"class\",\n \"mode\",\n \"size\",\n \"variant\",\n \"onClose\",\n \"withSizeToggle\",\n \"hideHeaderActions\",\n \"reverseHeaderActions\",\n \"closeAriaLabel\",\n \"expandAriaLabel\",\n \"collapseAriaLabel\",\n \"restoreAriaLabel\",\n \"resizable\",\n \"minSize\",\n \"maxSize\",\n \"resizeStep\",\n \"resizeHandleAriaLabel\",\n \"onResize\",\n \"style\",\n \"handleClose\",\n \"handleSizeButton\",\n \"children\",\n \"ref\",\n \"data-testid\",\n ]);\n\n let rootEl: HTMLDivElement | undefined;\n const [dragSize, setDragSize] = createSignal<number | null>(null);\n const vertical = (): boolean => isVerticalAxis(local.placement);\n\n const inner = createCssTransition({\n in: () => local.isOpen,\n timeout: { enter: 120, exit: 120 },\n classNames: { enterDone: \"smwb-sheet_entered\" },\n appear: true,\n });\n\n const clamp = (px: number): number => {\n const viewport = vertical() ? window.innerHeight : window.innerWidth;\n const max = local.maxSize ?? viewport;\n return Math.max(local.minSize, Math.min(max, px));\n };\n\n const currentSizePx = (): number => {\n const rect = rootEl?.getBoundingClientRect();\n if (!rect) return local.minSize;\n return vertical() ? rect.height : rect.width;\n };\n\n const applySize = (px: number): void => {\n const next = clamp(px);\n setDragSize(next);\n local.onResize?.(next);\n };\n\n let drag: { start: number; size: number } | null = null;\n\n const onPointerDown = (event: PointerEvent & { currentTarget: HTMLDivElement }): void => {\n event.preventDefault();\n drag = { start: vertical() ? event.clientY : event.clientX, size: currentSizePx() };\n try {\n event.currentTarget.setPointerCapture?.(event.pointerId);\n } catch {\n /* best-effort */\n }\n };\n\n const onPointerMove = (event: PointerEvent): void => {\n if (!drag) return;\n const pos = vertical() ? event.clientY : event.clientX;\n const delta = pos - drag.start;\n const grow = local.placement === \"bottom\" || local.placement === \"right\" ? -delta : delta;\n applySize(drag.size + grow);\n };\n\n const endDrag = (event: PointerEvent & { currentTarget: HTMLDivElement }): void => {\n drag = null;\n if (event.currentTarget.hasPointerCapture?.(event.pointerId)) {\n event.currentTarget.releasePointerCapture?.(event.pointerId);\n }\n };\n\n const onHandleKeyDown: JSX.EventHandler<HTMLDivElement, KeyboardEvent> = (event) => {\n const base = dragSize() ?? currentSizePx();\n const max = local.maxSize ?? (vertical() ? window.innerHeight : window.innerWidth);\n const incKey =\n local.placement === \"bottom\" ? \"ArrowUp\" : local.placement === \"top\" ? \"ArrowDown\" : local.placement === \"left\" ? \"ArrowRight\" : \"ArrowLeft\";\n const decKey =\n local.placement === \"bottom\" ? \"ArrowDown\" : local.placement === \"top\" ? \"ArrowUp\" : local.placement === \"left\" ? \"ArrowLeft\" : \"ArrowRight\";\n if (event.key === incKey) {\n event.preventDefault();\n applySize(base + local.resizeStep);\n } else if (event.key === decKey) {\n event.preventDefault();\n applySize(base - local.resizeStep);\n } else if (event.key === \"Home\") {\n event.preventDefault();\n applySize(local.minSize);\n } else if (event.key === \"End\") {\n event.preventDefault();\n applySize(max);\n }\n };\n\n // Keyed on `isOpen` alone: root.focus() fires focus handlers synchronously, and Solid runs those\n // without untrack, so any signal they read would otherwise re-run this effect and steal focus.\n createEffect(\n on(\n () => local.isOpen,\n (open) => {\n if (!open) {\n setDragSize(null);\n return;\n }\n const root = rootEl;\n // The element that had focus when the sheet opened gets it back when it closes (or unmounts\n // while open), unless the app has put focus somewhere else by then.\n const opener = root?.contains(document.activeElement) ? null : document.activeElement;\n root?.focus();\n onCleanup(() => restoreFocus(opener, root));\n },\n ),\n );\n\n const showActions = (): boolean => !local.hideHeaderActions && (!!local.onClose || local.withSizeToggle);\n const actionNodes = (): JSX.Element[] => {\n const nodes: JSX.Element[] = [];\n if (local.withSizeToggle) {\n nodes.push(\n <Button\n variant=\"outlined\"\n size=\"small\"\n onClick={local.handleSizeButton}\n aria-label={\n local.mode === \"expanded\" ? local.collapseAriaLabel : local.mode === \"small\" ? local.restoreAriaLabel : local.expandAriaLabel\n }\n >\n <Icon name={local.mode === \"expanded\" ? \"close_fullscreen\" : \"open_in_full\"} />\n </Button>\n );\n }\n if (local.onClose) {\n nodes.push(\n <Button variant=\"outlined\" size=\"small\" onClick={local.handleClose} aria-label={local.closeAriaLabel}>\n <Icon name=\"close\" />\n </Button>\n );\n }\n if (local.reverseHeaderActions) nodes.reverse();\n return nodes;\n };\n\n const sizeStyle = (): JSX.CSSProperties => {\n if (local.resizable && dragSize() !== null) {\n const px = `${dragSize()}px`;\n return { ...(vertical() ? { height: px } : { width: px }), transition: \"none\" };\n }\n return {};\n };\n\n return (\n <div\n {...rest}\n ref={mergeRefs(local.ref, (el) => (rootEl = el as HTMLDivElement))}\n data-testid={local[\"data-testid\"] ?? \"sheet\"}\n tabindex={-1}\n role=\"dialog\"\n aria-modal={local.variant === \"modal\" || undefined}\n class={cn(\"smwb-sheet\", local.class, `smwb-sheet_${local.variant}`, `smwb-${local.placement}`, inner.classes(), {\n \"smwb-expanded\": local.mode === \"expanded\",\n \"smwb-small\": local.mode === \"small\",\n \"smwb-sheet_resizable\": local.resizable,\n })}\n style={mergeStyle({ \"--sizeProp\": local.size, ...sizeStyle() }, local.style)}\n >\n <Show when={local.resizable}>\n <div\n class=\"smwb-sheet__drag-handle\"\n data-testid=\"sheet-drag-handle\"\n role=\"separator\"\n tabindex={0}\n aria-orientation={vertical() ? \"horizontal\" : \"vertical\"}\n aria-label={local.resizeHandleAriaLabel}\n aria-valuemin={local.minSize}\n aria-valuemax={Math.round(local.maxSize ?? (vertical() ? window.innerHeight : window.innerWidth))}\n aria-valuenow={Math.round(dragSize() ?? currentSizePx())}\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={endDrag}\n onPointerCancel={endDrag}\n onKeyDown={onHandleKeyDown}\n >\n <span class=\"smwb-sheet__drag-handle-bar\" />\n </div>\n </Show>\n <Show when={showActions()}>\n <div class=\"smwb-sheet__header\">{actionNodes()}</div>\n </Show>\n <Show when={local.isOpen && local.mode !== \"small\"}>\n <div class=\"smwb-sheet__inner\">{local.children}</div>\n </Show>\n </div>\n );\n}\n\nexport function Sheet(props: ParentProps<SheetProps>): JSX.Element {\n const merged = mergeProps({ variant: \"default\" as const, placement: \"bottom\" as const, closeOnEsc: true }, props);\n const [local, rest] = splitProps(merged, [\n \"isOpen\",\n \"onClose\",\n \"variant\",\n \"children\",\n \"placement\",\n \"disableOutsideClick\",\n \"closeOnEsc\",\n \"ref\",\n ]);\n\n const [mode, setMode] = createSignal<Mode>(null);\n let wrapperEl: HTMLDivElement | undefined;\n\n const outer = createCssTransition({\n in: () => local.isOpen,\n timeout: { enter: 70, exit: 70 },\n classNames: { enterDone: \"smwb-entered\" },\n appear: true,\n });\n\n createEffect(() => {\n if (!local.isOpen || !local.closeOnEsc || !local.onClose) return;\n const onKeyDown = (event: KeyboardEvent): void => {\n // Only the topmost overlay layer reacts to Escape, and not to one handled inside\n // it, e.g. by a menu closing on it.\n if (event.key === \"Escape\" && !event.defaultPrevented && isTopOverlayLayer(wrapperEl)) local.onClose?.();\n };\n document.addEventListener(\"keydown\", onKeyDown);\n onCleanup(() => document.removeEventListener(\"keydown\", onKeyDown));\n });\n\n const handleSizeButton = (): void => {\n setMode((prev) => (prev === \"expanded\" ? \"small\" : prev === null ? \"expanded\" : null));\n };\n const handleClose = (): void => {\n local.onClose?.();\n setMode(null);\n };\n\n const block = (): JSX.Element => (\n <SheetBlock\n {...rest}\n ref={local.ref}\n isOpen={local.isOpen}\n onClose={local.onClose}\n handleSizeButton={handleSizeButton}\n handleClose={handleClose}\n mode={mode()}\n placement={local.placement}\n variant={local.variant}\n >\n {local.children}\n </SheetBlock>\n );\n\n return (\n <Show when={outer.mounted()}>\n <Portal mount={(isServer ? undefined : document.body) as Node}>\n <Show\n when={local.variant === \"default\"}\n fallback={\n <div\n ref={(el) => (wrapperEl = el)}\n data-smwb-overlay-layer\n class={cn(\"smwb-sheet__overlay\", \"smwb-dark\", `smwb-${local.placement}`, `smwb-sheet_${local.variant}`, outer.classes())}\n >\n <ClickAwayListener onClickAway={() => !local.disableOutsideClick && handleClose()}>\n {block()}\n </ClickAwayListener>\n </div>\n }\n >\n <div\n ref={(el) => (wrapperEl = el)}\n data-smwb-overlay-layer\n class={cn(local.disableOutsideClick ? \"smwb-sheet__overlay\" : \"smwb-sheet__default\", outer.classes())}\n >\n {block()}\n </div>\n </Show>\n </Portal>\n </Show>\n );\n}\n\nexport default Sheet;\n"],"mappings":"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","names":[],"file":"sheet.jsx"}
|
|
1
|
+
{"version":3,"sources":["../../../../../src/components/layout/sheet/sheet.tsx"],"sourcesContent":["import \"@smwb/ui-styles/less/tokens.entry.less\";\nimport \"@smwb/ui-styles/less/components/sheet/sheet.entry.less\";\nimport { type JSX, type ParentProps, Show, createEffect, createSignal, mergeProps, on, onCleanup, splitProps } from \"solid-js\";\nimport { Portal, isServer } from \"solid-js/web\";\nimport cn from \"clsx\";\nimport { Button } from \"../../inputs/button\";\nimport { Icon } from \"../../base/icon\";\nimport { ClickAwayListener } from \"../../clickAwayListener\";\nimport { createCssTransition } from \"../../../primitives/createCssTransition\";\nimport { isTopOverlayLayer } from \"../../../primitives/overlayLayer\";\nimport { restoreFocus } from \"../../../primitives/restoreFocus\";\nimport { mergeRefs, type Ref } from \"../../../primitives/mergeRefs\";\nimport { mergeStyle } from \"../../../primitives/mergeStyle\";\n\nexport interface SheetProps extends Omit<JSX.HTMLAttributes<HTMLDivElement>, \"onResize\"> {\n placement?: \"top\" | \"bottom\" | \"right\" | \"left\";\n isOpen: boolean;\n onClose?: (() => void) | undefined;\n variant?: \"default\" | \"modal\";\n withSizeToggle?: boolean;\n /** Hide built-in header actions; onClose still handles Esc and backdrop. */\n hideHeaderActions?: boolean;\n reverseHeaderActions?: boolean;\n /** Accessible name of the header close button (default \"Close sheet\"). */\n closeAriaLabel?: string;\n /**\n * Accessible name of the size toggle while the sheet is at its normal size (default \"Expand sheet\"). The toggle\n * cycles normal, expanded, collapsed and back, and its name always says what the next press does; the\n * name changing is how it exposes its state. It shows the `open_in_full` icon, and `close_fullscreen` while expanded.\n */\n expandAriaLabel?: string;\n /** Accessible name of the size toggle while the sheet is expanded (default \"Collapse sheet\"). */\n collapseAriaLabel?: string;\n /** Accessible name of the size toggle while the sheet is collapsed to its header (default \"Restore sheet size\"). */\n restoreAriaLabel?: string;\n size?: string;\n /**\n * Gap between the top edge of the window and the top of the sheet and of its modal scrim (px, or any CSS length),\n * so a panel can start below a fixed app bar and leave it visible and clickable. It applies to the `top`, `left`\n * and `right` placements and to the scrim of every placement. Without it the sheet takes the page's\n * `--smui-sheet-offset-top`, which `AppLayout` sets to the height of its sticky header and which is 0 elsewhere;\n * `offsetTop={0}` covers the header anyway. A press in the strip above a modal scrim belongs to the page and does\n * not close the sheet, so a header button can toggle it. The modal sheet keeps `aria-modal`, though that strip can\n * still be reached by pointer and Tab; for a header that must stay available to assistive technology, use\n * `variant=\"default\"`.\n */\n offsetTop?: number | string;\n disableOutsideClick?: boolean;\n /** Close the sheet when Escape is pressed. */\n closeOnEsc?: boolean;\n /** Allow drag-to-resize (mobile bottom-sheet style). Off by default. */\n resizable?: boolean;\n /** Minimum size in px while resizing (default 80). */\n minSize?: number;\n /** Maximum size in px while resizing (default: viewport dimension). */\n maxSize?: number;\n /** Step in px for keyboard resizing (default 24). */\n resizeStep?: number;\n /** Accessible label for the resize handle. */\n resizeHandleAriaLabel?: string;\n /** Called with the new size in px while/after resizing. */\n onResize?: (size: number) => void;\n \"aria-label\"?: string;\n /** Test id of the sheet panel (the dialog); `sheet` unless given. */\n \"data-testid\"?: string | undefined;\n ref?: Ref<HTMLDivElement>;\n}\n\ntype Mode = \"small\" | \"expanded\" | null;\n\n/**\n * Whether a press ended above the top edge of the scrim, i.e. in the strip an `offsetTop` leaves uncovered. The page\n * owns that strip (a header button there, say), so it is not \"away\" from a modal sheet: a button that toggles the\n * sheet would otherwise see it close on the release and open again on the click.\n */\nconst landedAboveScrim = (event: MouseEvent | TouchEvent, scrim: HTMLElement | null | undefined): boolean => {\n const y = \"changedTouches\" in event ? event.changedTouches[0]?.clientY : event.clientY;\n return scrim != null && y !== undefined && y < scrim.getBoundingClientRect().top;\n};\n\nconst isVerticalAxis = (placement: SheetProps[\"placement\"]): boolean => placement === \"bottom\" || placement === \"top\";\n\ninterface SheetBlockProps extends ParentProps<SheetProps> {\n mode: Mode;\n handleSizeButton: () => void;\n handleClose: () => void;\n}\n\nfunction SheetBlock(props: SheetBlockProps): JSX.Element {\n const merged = mergeProps(\n {\n size: \"60%\",\n withSizeToggle: true,\n hideHeaderActions: false,\n reverseHeaderActions: false,\n closeAriaLabel: \"Close sheet\",\n expandAriaLabel: \"Expand sheet\",\n collapseAriaLabel: \"Collapse sheet\",\n restoreAriaLabel: \"Restore sheet size\",\n resizable: false,\n minSize: 80,\n resizeStep: 24,\n resizeHandleAriaLabel: \"Resize sheet\",\n },\n props\n );\n const [local, rest] = splitProps(merged, [\n \"isOpen\",\n \"placement\",\n \"class\",\n \"mode\",\n \"size\",\n \"variant\",\n \"onClose\",\n \"withSizeToggle\",\n \"hideHeaderActions\",\n \"reverseHeaderActions\",\n \"closeAriaLabel\",\n \"expandAriaLabel\",\n \"collapseAriaLabel\",\n \"restoreAriaLabel\",\n \"resizable\",\n \"minSize\",\n \"maxSize\",\n \"resizeStep\",\n \"resizeHandleAriaLabel\",\n \"onResize\",\n \"style\",\n \"handleClose\",\n \"handleSizeButton\",\n \"children\",\n \"ref\",\n \"data-testid\",\n ]);\n\n let rootEl: HTMLDivElement | undefined;\n const [dragSize, setDragSize] = createSignal<number | null>(null);\n const vertical = (): boolean => isVerticalAxis(local.placement);\n\n const inner = createCssTransition({\n in: () => local.isOpen,\n timeout: { enter: 120, exit: 120 },\n classNames: { enterDone: \"smwb-sheet_entered\" },\n appear: true,\n });\n\n const clamp = (px: number): number => {\n const viewport = vertical() ? window.innerHeight : window.innerWidth;\n const max = local.maxSize ?? viewport;\n return Math.max(local.minSize, Math.min(max, px));\n };\n\n const currentSizePx = (): number => {\n const rect = rootEl?.getBoundingClientRect();\n if (!rect) return local.minSize;\n return vertical() ? rect.height : rect.width;\n };\n\n const applySize = (px: number): void => {\n const next = clamp(px);\n setDragSize(next);\n local.onResize?.(next);\n };\n\n let drag: { start: number; size: number } | null = null;\n\n const onPointerDown = (event: PointerEvent & { currentTarget: HTMLDivElement }): void => {\n event.preventDefault();\n drag = { start: vertical() ? event.clientY : event.clientX, size: currentSizePx() };\n try {\n event.currentTarget.setPointerCapture?.(event.pointerId);\n } catch {\n /* best-effort */\n }\n };\n\n const onPointerMove = (event: PointerEvent): void => {\n if (!drag) return;\n const pos = vertical() ? event.clientY : event.clientX;\n const delta = pos - drag.start;\n const grow = local.placement === \"bottom\" || local.placement === \"right\" ? -delta : delta;\n applySize(drag.size + grow);\n };\n\n const endDrag = (event: PointerEvent & { currentTarget: HTMLDivElement }): void => {\n drag = null;\n if (event.currentTarget.hasPointerCapture?.(event.pointerId)) {\n event.currentTarget.releasePointerCapture?.(event.pointerId);\n }\n };\n\n const onHandleKeyDown: JSX.EventHandler<HTMLDivElement, KeyboardEvent> = (event) => {\n const base = dragSize() ?? currentSizePx();\n const max = local.maxSize ?? (vertical() ? window.innerHeight : window.innerWidth);\n const incKey =\n local.placement === \"bottom\" ? \"ArrowUp\" : local.placement === \"top\" ? \"ArrowDown\" : local.placement === \"left\" ? \"ArrowRight\" : \"ArrowLeft\";\n const decKey =\n local.placement === \"bottom\" ? \"ArrowDown\" : local.placement === \"top\" ? \"ArrowUp\" : local.placement === \"left\" ? \"ArrowLeft\" : \"ArrowRight\";\n if (event.key === incKey) {\n event.preventDefault();\n applySize(base + local.resizeStep);\n } else if (event.key === decKey) {\n event.preventDefault();\n applySize(base - local.resizeStep);\n } else if (event.key === \"Home\") {\n event.preventDefault();\n applySize(local.minSize);\n } else if (event.key === \"End\") {\n event.preventDefault();\n applySize(max);\n }\n };\n\n // Keyed on `isOpen` alone: root.focus() fires focus handlers synchronously, and Solid runs those\n // without untrack, so any signal they read would otherwise re-run this effect and steal focus.\n createEffect(\n on(\n () => local.isOpen,\n (open) => {\n if (!open) {\n setDragSize(null);\n return;\n }\n const root = rootEl;\n // The element that had focus when the sheet opened gets it back when it closes (or unmounts\n // while open), unless the app has put focus somewhere else by then.\n const opener = root?.contains(document.activeElement) ? null : document.activeElement;\n root?.focus();\n onCleanup(() => restoreFocus(opener, root));\n },\n ),\n );\n\n const showActions = (): boolean => !local.hideHeaderActions && (!!local.onClose || local.withSizeToggle);\n const actionNodes = (): JSX.Element[] => {\n const nodes: JSX.Element[] = [];\n if (local.withSizeToggle) {\n nodes.push(\n <Button\n variant=\"outlined\"\n size=\"small\"\n onClick={local.handleSizeButton}\n aria-label={\n local.mode === \"expanded\" ? local.collapseAriaLabel : local.mode === \"small\" ? local.restoreAriaLabel : local.expandAriaLabel\n }\n >\n <Icon name={local.mode === \"expanded\" ? \"close_fullscreen\" : \"open_in_full\"} />\n </Button>\n );\n }\n if (local.onClose) {\n nodes.push(\n <Button variant=\"outlined\" size=\"small\" onClick={local.handleClose} aria-label={local.closeAriaLabel}>\n <Icon name=\"close\" />\n </Button>\n );\n }\n if (local.reverseHeaderActions) nodes.reverse();\n return nodes;\n };\n\n const sizeStyle = (): JSX.CSSProperties => {\n if (local.resizable && dragSize() !== null) {\n const px = `${dragSize()}px`;\n return { ...(vertical() ? { height: px } : { width: px }), transition: \"none\" };\n }\n return {};\n };\n\n return (\n <div\n {...rest}\n ref={mergeRefs(local.ref, (el) => (rootEl = el as HTMLDivElement))}\n data-testid={local[\"data-testid\"] ?? \"sheet\"}\n tabindex={-1}\n role=\"dialog\"\n aria-modal={local.variant === \"modal\" || undefined}\n class={cn(\"smwb-sheet\", local.class, `smwb-sheet_${local.variant}`, `smwb-${local.placement}`, inner.classes(), {\n \"smwb-expanded\": local.mode === \"expanded\",\n \"smwb-small\": local.mode === \"small\",\n \"smwb-sheet_resizable\": local.resizable,\n })}\n style={mergeStyle({ \"--sizeProp\": local.size, ...sizeStyle() }, local.style)}\n >\n <Show when={local.resizable}>\n <div\n class=\"smwb-sheet__drag-handle\"\n data-testid=\"sheet-drag-handle\"\n role=\"separator\"\n tabindex={0}\n aria-orientation={vertical() ? \"horizontal\" : \"vertical\"}\n aria-label={local.resizeHandleAriaLabel}\n aria-valuemin={local.minSize}\n aria-valuemax={Math.round(local.maxSize ?? (vertical() ? window.innerHeight : window.innerWidth))}\n aria-valuenow={Math.round(dragSize() ?? currentSizePx())}\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={endDrag}\n onPointerCancel={endDrag}\n onKeyDown={onHandleKeyDown}\n >\n <span class=\"smwb-sheet__drag-handle-bar\" />\n </div>\n </Show>\n <Show when={showActions()}>\n <div class=\"smwb-sheet__header\">{actionNodes()}</div>\n </Show>\n <Show when={local.isOpen && local.mode !== \"small\"}>\n <div class=\"smwb-sheet__inner\">{local.children}</div>\n </Show>\n </div>\n );\n}\n\nexport function Sheet(props: ParentProps<SheetProps>): JSX.Element {\n const merged = mergeProps({ variant: \"default\" as const, placement: \"bottom\" as const, closeOnEsc: true }, props);\n const [local, rest] = splitProps(merged, [\n \"isOpen\",\n \"onClose\",\n \"variant\",\n \"children\",\n \"placement\",\n \"disableOutsideClick\",\n \"closeOnEsc\",\n \"offsetTop\",\n \"ref\",\n ]);\n\n const [mode, setMode] = createSignal<Mode>(null);\n let wrapperEl: HTMLDivElement | undefined;\n\n const outer = createCssTransition({\n in: () => local.isOpen,\n timeout: { enter: 70, exit: 70 },\n classNames: { enterDone: \"smwb-entered\" },\n appear: true,\n });\n\n createEffect(() => {\n if (!local.isOpen || !local.closeOnEsc || !local.onClose) return;\n const onKeyDown = (event: KeyboardEvent): void => {\n // Only the topmost overlay layer reacts to Escape, and not to one handled inside\n // it, e.g. by a menu closing on it.\n if (event.key === \"Escape\" && !event.defaultPrevented && isTopOverlayLayer(wrapperEl)) local.onClose?.();\n };\n document.addEventListener(\"keydown\", onKeyDown);\n onCleanup(() => document.removeEventListener(\"keydown\", onKeyDown));\n });\n\n const handleSizeButton = (): void => {\n setMode((prev) => (prev === \"expanded\" ? \"small\" : prev === null ? \"expanded\" : null));\n };\n const handleClose = (): void => {\n local.onClose?.();\n setMode(null);\n };\n\n // The variable goes on the wrapper, so the panel and the scrim both inherit it. Left off when not given: an\n // undefined style key would print `style=\"\"` on the server.\n const wrapperStyle = (): JSX.CSSProperties | undefined => {\n const offset = local.offsetTop;\n return offset === undefined ? undefined : { \"--smui-sheet-offset-top\": typeof offset === \"number\" ? `${offset}px` : offset };\n };\n\n const block = (): JSX.Element => (\n <SheetBlock\n {...rest}\n ref={local.ref}\n isOpen={local.isOpen}\n onClose={local.onClose}\n handleSizeButton={handleSizeButton}\n handleClose={handleClose}\n mode={mode()}\n placement={local.placement}\n variant={local.variant}\n >\n {local.children}\n </SheetBlock>\n );\n\n return (\n <Show when={outer.mounted()}>\n <Portal mount={(isServer ? undefined : document.body) as Node}>\n <Show\n when={local.variant === \"default\"}\n fallback={\n <div\n ref={(el) => (wrapperEl = el)}\n data-smwb-overlay-layer\n style={wrapperStyle()}\n class={cn(\"smwb-sheet__overlay\", \"smwb-dark\", `smwb-${local.placement}`, `smwb-sheet_${local.variant}`, outer.classes())}\n >\n <ClickAwayListener\n onClickAway={(event) => !local.disableOutsideClick && !landedAboveScrim(event, wrapperEl) && handleClose()}\n >\n {block()}\n </ClickAwayListener>\n </div>\n }\n >\n <div\n ref={(el) => (wrapperEl = el)}\n data-smwb-overlay-layer\n style={wrapperStyle()}\n class={cn(local.disableOutsideClick ? \"smwb-sheet__overlay\" : \"smwb-sheet__default\", outer.classes())}\n >\n {block()}\n </div>\n </Show>\n </Portal>\n </Show>\n );\n}\n\nexport default Sheet;\n"],"mappings":"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","names":[],"file":"sheet.jsx"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/components/layout/tabGroups/index.ts"],
|
|
4
|
-
"sourcesContent": ["export { TabGroups, default } from \"./tabGroups\";\nexport type { TabGroupsProps, TabGroupsTab } from \"./tabGroups\";\n// The pure model behind the component: `tabGroupsLayout.addTab`, `parseTabGroupsLayout`, \u2026\n// Generic names stay inside the namespace, so the barrel gains no ambiguous top-level functions.\nexport * as tabGroupsLayout from \"./tabGroupsLayout\";\nexport type {\n TabGroupsChange,\n TabGroupsDropZone,\n TabGroupsEdge,\n TabGroupsGroup,\n TabGroupsLabels,\n TabGroupsLayout,\n TabGroupsMoveTarget,\n TabGroupsNode,\n TabGroupsSplit,\n} from \"./tabGroupsLayout\";\n"],
|
|
4
|
+
"sourcesContent": ["export { TabGroups, default } from \"./tabGroups\";\nexport type { TabGroupsProps, TabGroupsStripGroup, TabGroupsTab } from \"./tabGroups\";\n// The pure model behind the component: `tabGroupsLayout.addTab`, `parseTabGroupsLayout`, \u2026\n// Generic names stay inside the namespace, so the barrel gains no ambiguous top-level functions.\nexport * as tabGroupsLayout from \"./tabGroupsLayout\";\nexport type {\n TabGroupsChange,\n TabGroupsDropZone,\n TabGroupsEdge,\n TabGroupsGroup,\n TabGroupsLabels,\n TabGroupsLayout,\n TabGroupsMoveTarget,\n TabGroupsNode,\n TabGroupsSplit,\n} from \"./tabGroupsLayout\";\n"],
|
|
5
5
|
"mappings": "AAAA,SAAS,WAAW,WAAAA,gBAAe;AAInC,YAAY,qBAAqB;",
|
|
6
6
|
"names": ["default"]
|
|
7
7
|
}
|
|
@@ -58,6 +58,8 @@ import {
|
|
|
58
58
|
lastStopOf,
|
|
59
59
|
ownGroupIdOf,
|
|
60
60
|
ownerRootOf,
|
|
61
|
+
slotFocusHeir,
|
|
62
|
+
slotFocusOf,
|
|
61
63
|
tabMove
|
|
62
64
|
} from "./tabGroupsFocus";
|
|
63
65
|
const CHORD_SHORTCUTS = [
|
|
@@ -76,6 +78,7 @@ const DIRECTION_CHORDS = [
|
|
|
76
78
|
["top", ["Alt", "Shift", "ArrowUp"]],
|
|
77
79
|
["bottom", ["Alt", "Shift", "ArrowDown"]]
|
|
78
80
|
];
|
|
81
|
+
const present = (node) => node != null && typeof node !== "boolean" && node !== "";
|
|
79
82
|
const FIRST_MENU_ITEM = '[role^="menuitem"]:not([aria-disabled="true"])';
|
|
80
83
|
function groupIdOf(node) {
|
|
81
84
|
let current = node ?? null;
|
|
@@ -183,7 +186,9 @@ function TabGroups(props) {
|
|
|
183
186
|
"minGroupSize",
|
|
184
187
|
"keyboardStep",
|
|
185
188
|
"groupActions",
|
|
189
|
+
"stripActions",
|
|
186
190
|
"dense",
|
|
191
|
+
"fit",
|
|
187
192
|
"labels",
|
|
188
193
|
// Wrapped so that the caller's handler still runs, first.
|
|
189
194
|
"onDragStart",
|
|
@@ -302,6 +307,7 @@ function TabGroups(props) {
|
|
|
302
307
|
let leaving = false;
|
|
303
308
|
let lastFocus = null;
|
|
304
309
|
let pointerFocus = false;
|
|
310
|
+
let slotFocus = null;
|
|
305
311
|
const announce = (next, change) => {
|
|
306
312
|
const text = describeTabGroupsChange(change, next, tabTitle, labels());
|
|
307
313
|
if (text === "") return;
|
|
@@ -342,6 +348,18 @@ function TabGroups(props) {
|
|
|
342
348
|
if (active !== null && active !== doc.body && active !== pending.from && !containsDeep(rootEl, active)) return;
|
|
343
349
|
element.focus();
|
|
344
350
|
};
|
|
351
|
+
const restoreSlotFocus = () => {
|
|
352
|
+
const lost = slotFocus;
|
|
353
|
+
if (!lost || !rootEl || lost.element.isConnected) return;
|
|
354
|
+
slotFocus = null;
|
|
355
|
+
const doc = rootEl.ownerDocument;
|
|
356
|
+
const active = deepActiveElement(doc);
|
|
357
|
+
if (active !== null && active !== doc.body) return;
|
|
358
|
+
slotFocusHeir(rootEl, lost, layout().activeGroup)?.focus();
|
|
359
|
+
};
|
|
360
|
+
const forgetGoneSlotFocus = () => {
|
|
361
|
+
if (slotFocus && !slotFocus.element.isConnected) slotFocus = null;
|
|
362
|
+
};
|
|
345
363
|
const commit = (next, change) => {
|
|
346
364
|
if (next === latest) return;
|
|
347
365
|
latest = next;
|
|
@@ -362,11 +380,16 @@ function TabGroups(props) {
|
|
|
362
380
|
latest = layout();
|
|
363
381
|
};
|
|
364
382
|
if (!isServer) {
|
|
383
|
+
createComputed(() => {
|
|
384
|
+
layout();
|
|
385
|
+
forgetGoneSlotFocus();
|
|
386
|
+
});
|
|
365
387
|
createEffect(
|
|
366
388
|
on(
|
|
367
389
|
layout,
|
|
368
390
|
() => {
|
|
369
391
|
focusPending();
|
|
392
|
+
restoreSlotFocus();
|
|
370
393
|
},
|
|
371
394
|
{ defer: true }
|
|
372
395
|
)
|
|
@@ -792,6 +815,7 @@ function TabGroups(props) {
|
|
|
792
815
|
if (!root || !(target instanceof HTMLElement)) return;
|
|
793
816
|
const previous = lastFocus;
|
|
794
817
|
lastFocus = ownerRootOf(target) === root ? target : null;
|
|
818
|
+
slotFocus = lastFocus === null ? null : slotFocusOf(root, target);
|
|
795
819
|
if (previous instanceof HTMLElement && previous.localName === "iframe" && previous !== target && !pointerFocus) {
|
|
796
820
|
const instead = frameExitTarget(root, previous, target);
|
|
797
821
|
if (instead !== null && instead === guardEl) {
|
|
@@ -843,7 +867,17 @@ function TabGroups(props) {
|
|
|
843
867
|
}, 0);
|
|
844
868
|
};
|
|
845
869
|
const onFocusOut = (event) => {
|
|
846
|
-
if (event.relatedTarget instanceof Node && !root.contains(event.relatedTarget))
|
|
870
|
+
if (event.relatedTarget instanceof Node && !root.contains(event.relatedTarget)) {
|
|
871
|
+
lastFocus = null;
|
|
872
|
+
slotFocus = null;
|
|
873
|
+
return;
|
|
874
|
+
}
|
|
875
|
+
const lost = slotFocus;
|
|
876
|
+
if (event.relatedTarget === null && lost?.element === event.target) {
|
|
877
|
+
queueMicrotask(() => {
|
|
878
|
+
if (slotFocus === lost && lost.element.isConnected) slotFocus = null;
|
|
879
|
+
});
|
|
880
|
+
}
|
|
847
881
|
};
|
|
848
882
|
const onWindowBlur = () => {
|
|
849
883
|
const active = root.ownerDocument.activeElement;
|
|
@@ -899,6 +933,7 @@ function TabGroups(props) {
|
|
|
899
933
|
if (given?.disabled) extra.disabled = true;
|
|
900
934
|
if (given?.closeLabel !== void 0) extra.closeLabel = given.closeLabel;
|
|
901
935
|
if (given?.actions !== void 0) extra.actions = given.actions;
|
|
936
|
+
if (local.fit === true && given?.title !== void 0) extra.title = given.title;
|
|
902
937
|
return extra;
|
|
903
938
|
});
|
|
904
939
|
const shortcuts = createMemo(() => meta()?.disabled ? void 0 : CHORD_SHORTCUTS);
|
|
@@ -924,6 +959,21 @@ function TabGroups(props) {
|
|
|
924
959
|
const index = tabs().indexOf(active);
|
|
925
960
|
return index < 0 ? 0 : index;
|
|
926
961
|
};
|
|
962
|
+
const stripIndex = createMemo(() => indexOfGroup(groupId));
|
|
963
|
+
const stripActive = createMemo(() => layout().activeGroup === groupId);
|
|
964
|
+
const stripGroup = {
|
|
965
|
+
get index() {
|
|
966
|
+
return stripIndex();
|
|
967
|
+
},
|
|
968
|
+
get count() {
|
|
969
|
+
return groupCount();
|
|
970
|
+
},
|
|
971
|
+
get active() {
|
|
972
|
+
return stripActive();
|
|
973
|
+
}
|
|
974
|
+
};
|
|
975
|
+
const strip = resolveChildren(() => local.stripActions?.(stripGroup));
|
|
976
|
+
const hasStrip = () => strip.toArray().some(present);
|
|
927
977
|
return <div
|
|
928
978
|
class={cn("smwb-tab-groups__header", {
|
|
929
979
|
"smwb-tab-groups__header--active": layout().activeGroup === groupId
|
|
@@ -942,9 +992,13 @@ function TabGroups(props) {
|
|
|
942
992
|
scrollable
|
|
943
993
|
overflowMenu
|
|
944
994
|
dense={local.dense === true}
|
|
995
|
+
fit={local.fit === true}
|
|
945
996
|
>
|
|
946
997
|
<For each={tabs()}>{(tabId) => renderTab(groupId, tabId)}</For>
|
|
947
998
|
</Tabs>
|
|
999
|
+
<Show when={hasStrip()}>
|
|
1000
|
+
<div class="smwb-tab-groups__strip-actions">{strip()}</div>
|
|
1001
|
+
</Show>
|
|
948
1002
|
<Show when={withGroupActions()}>
|
|
949
1003
|
<button
|
|
950
1004
|
type="button"
|