@smwb/ui-solid 2.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.
Files changed (207) hide show
  1. package/README.md +115 -0
  2. package/dist/assets/styles/less/components/appBar/appBar.entry.css +1 -1
  3. package/dist/assets/styles/less/components/avatar/avatar.entry.css +1 -1
  4. package/dist/assets/styles/less/components/breadcrumbs/breadcrumbs.entry.css +1 -1
  5. package/dist/assets/styles/less/components/button/button.entry.css +1 -1
  6. package/dist/assets/styles/less/components/buttonGroup/buttonGroup.entry.css +1 -1
  7. package/dist/assets/styles/less/components/dagView/dagView.entry.css +1 -1
  8. package/dist/assets/styles/less/components/meter/meter.entry.css +1 -1
  9. package/dist/assets/styles/less/components/screenDivider/screenDivider.entry.css +1 -1
  10. package/dist/assets/styles/less/components/sheet/sheet.entry.css +1 -1
  11. package/dist/assets/styles/less/components/statusDot/statusDot.entry.css +1 -1
  12. package/dist/assets/styles/less/components/tabGroups/tabGroups.entry.css +1 -1
  13. package/dist/assets/styles/less/components/tabs/tab/tab.entry.css +1 -1
  14. package/dist/assets/styles/less/components/toggle/toggle.entry.css +1 -1
  15. package/dist/assets/styles/less/components/treeView/treeView.entry.css +1 -1
  16. package/dist/assets/styles/less/tokens.entry.css +1 -1
  17. package/dist/components/dataDisplay/avatar/avatar.d.ts +5 -2
  18. package/dist/components/dataDisplay/avatar/avatar.js +9 -9
  19. package/dist/components/dataDisplay/avatar/avatar.js.map +1 -1
  20. package/dist/components/dataDisplay/chart/chart.d.ts +16 -8
  21. package/dist/components/dataDisplay/chart/chartFull.d.ts +14 -0
  22. package/dist/components/dataDisplay/chart/core.d.ts +19 -0
  23. package/dist/components/dataDisplay/chart/index.d.ts +1 -1
  24. package/dist/components/dataDisplay/dagView/dagView.d.ts +4 -0
  25. package/dist/components/dataDisplay/dagView/dagView.js +124 -123
  26. package/dist/components/dataDisplay/dagView/dagView.js.map +1 -1
  27. package/dist/components/dataDisplay/dagView/index.d.ts +1 -1
  28. package/dist/components/dataDisplay/meter/index.d.ts +1 -1
  29. package/dist/components/dataDisplay/meter/meter.d.ts +14 -0
  30. package/dist/components/dataDisplay/meter/meter.js +32 -30
  31. package/dist/components/dataDisplay/meter/meter.js.map +1 -1
  32. package/dist/components/dataDisplay/statusDot/index.d.ts +1 -1
  33. package/dist/components/dataDisplay/statusDot/statusDot.d.ts +10 -1
  34. package/dist/components/dataDisplay/statusDot/statusDot.js +20 -11
  35. package/dist/components/dataDisplay/statusDot/statusDot.js.map +1 -1
  36. package/dist/components/dataDisplay/treeView/treeView.d.ts +18 -2
  37. package/dist/components/dataDisplay/treeView/treeView.js +140 -121
  38. package/dist/components/dataDisplay/treeView/treeView.js.map +1 -1
  39. package/dist/components/inputs/button/button.d.ts +7 -0
  40. package/dist/components/inputs/button/button.js +57 -51
  41. package/dist/components/inputs/button/button.js.map +1 -1
  42. package/dist/components/inputs/toggle/toggle.d.ts +6 -0
  43. package/dist/components/inputs/toggle/toggle.js +23 -23
  44. package/dist/components/inputs/toggle/toggle.js.map +1 -1
  45. package/dist/components/layout/appLayout/appLayout.d.ts +6 -1
  46. package/dist/components/layout/appLayout/appLayout.js +49 -42
  47. package/dist/components/layout/appLayout/appLayout.js.map +1 -1
  48. package/dist/components/layout/screenDivider/screenDivider.d.ts +12 -0
  49. package/dist/components/layout/screenDivider/screenDivider.js +87 -46
  50. package/dist/components/layout/screenDivider/screenDivider.js.map +1 -1
  51. package/dist/components/layout/sheet/sheet.d.ts +11 -0
  52. package/dist/components/layout/sheet/sheet.js +122 -102
  53. package/dist/components/layout/sheet/sheet.js.map +1 -1
  54. package/dist/components/layout/tabGroups/index.d.ts +1 -1
  55. package/dist/components/layout/tabGroups/tabGroups.d.ts +44 -1
  56. package/dist/components/layout/tabGroups/tabGroups.js +500 -460
  57. package/dist/components/layout/tabGroups/tabGroups.js.map +1 -1
  58. package/dist/components/layout/tabGroups/tabGroupsFocus.d.ts +16 -0
  59. package/dist/components/layout/tabGroups/tabGroupsFocus.js +102 -70
  60. package/dist/components/layout/tabGroups/tabGroupsFocus.js.map +1 -1
  61. package/dist/components/navigation/appBar/appBar.d.ts +12 -0
  62. package/dist/components/navigation/appBar/appBar.js.map +1 -1
  63. package/dist/components/navigation/breadcrumbs/breadcrumbs.d.ts +9 -0
  64. package/dist/components/navigation/breadcrumbs/breadcrumbs.js +47 -31
  65. package/dist/components/navigation/breadcrumbs/breadcrumbs.js.map +1 -1
  66. package/dist/components/navigation/tabs/tab/tab.js +71 -68
  67. package/dist/components/navigation/tabs/tab/tab.js.map +1 -1
  68. package/dist/components/navigation/tabs/tabs.d.ts +11 -0
  69. package/dist/components/navigation/tabs/tabs.js +19 -16
  70. package/dist/components/navigation/tabs/tabs.js.map +1 -1
  71. package/dist/components/navigation/tabs/tabsContext.d.ts +2 -0
  72. package/dist/components/navigation/tabs/tabsContext.js.map +1 -1
  73. package/dist/headless/components/dataDisplay/avatar/avatar.js +9 -9
  74. package/dist/headless/components/dataDisplay/avatar/avatar.js.map +1 -1
  75. package/dist/headless/components/dataDisplay/dagView/dagView.js +133 -132
  76. package/dist/headless/components/dataDisplay/dagView/dagView.js.map +1 -1
  77. package/dist/headless/components/dataDisplay/meter/meter.js +32 -30
  78. package/dist/headless/components/dataDisplay/meter/meter.js.map +1 -1
  79. package/dist/headless/components/dataDisplay/statusDot/statusDot.js +19 -10
  80. package/dist/headless/components/dataDisplay/statusDot/statusDot.js.map +1 -1
  81. package/dist/headless/components/dataDisplay/treeView/treeView.js +139 -120
  82. package/dist/headless/components/dataDisplay/treeView/treeView.js.map +1 -1
  83. package/dist/headless/components/inputs/button/button.js +57 -51
  84. package/dist/headless/components/inputs/button/button.js.map +1 -1
  85. package/dist/headless/components/inputs/toggle/toggle.js +23 -23
  86. package/dist/headless/components/inputs/toggle/toggle.js.map +1 -1
  87. package/dist/headless/components/layout/appLayout/appLayout.js +52 -45
  88. package/dist/headless/components/layout/appLayout/appLayout.js.map +1 -1
  89. package/dist/headless/components/layout/screenDivider/screenDivider.js +88 -47
  90. package/dist/headless/components/layout/screenDivider/screenDivider.js.map +1 -1
  91. package/dist/headless/components/layout/sheet/sheet.js +124 -104
  92. package/dist/headless/components/layout/sheet/sheet.js.map +1 -1
  93. package/dist/headless/components/layout/tabGroups/tabGroups.js +500 -460
  94. package/dist/headless/components/layout/tabGroups/tabGroups.js.map +1 -1
  95. package/dist/headless/components/layout/tabGroups/tabGroupsFocus.js +102 -70
  96. package/dist/headless/components/layout/tabGroups/tabGroupsFocus.js.map +1 -1
  97. package/dist/headless/components/navigation/appBar/appBar.js.map +1 -1
  98. package/dist/headless/components/navigation/breadcrumbs/breadcrumbs.js +46 -30
  99. package/dist/headless/components/navigation/breadcrumbs/breadcrumbs.js.map +1 -1
  100. package/dist/headless/components/navigation/tabs/tab/tab.js +70 -67
  101. package/dist/headless/components/navigation/tabs/tab/tab.js.map +1 -1
  102. package/dist/headless/components/navigation/tabs/tabs.js +19 -16
  103. package/dist/headless/components/navigation/tabs/tabs.js.map +1 -1
  104. package/dist/headless/components/navigation/tabs/tabsContext.js.map +1 -1
  105. package/dist/headless/primitives/publishHeaderOffset.js +19 -0
  106. package/dist/headless/primitives/publishHeaderOffset.js.map +1 -0
  107. package/dist/headless/source/components/dataDisplay/avatar/avatar.jsx +1 -0
  108. package/dist/headless/source/components/dataDisplay/avatar/avatar.jsx.map +1 -1
  109. package/dist/headless/source/components/dataDisplay/chart/chart.jsx +14 -12
  110. package/dist/headless/source/components/dataDisplay/chart/chart.jsx.map +1 -1
  111. package/dist/headless/source/components/dataDisplay/chart/chartFull.js +12 -0
  112. package/dist/headless/source/components/dataDisplay/chart/chartFull.js.map +7 -0
  113. package/dist/headless/source/components/dataDisplay/chart/core.js +11 -0
  114. package/dist/headless/source/components/dataDisplay/chart/core.js.map +7 -0
  115. package/dist/headless/source/components/dataDisplay/chart/index.js +1 -1
  116. package/dist/headless/source/components/dataDisplay/chart/index.js.map +1 -1
  117. package/dist/headless/source/components/dataDisplay/dagView/dagView.jsx +2 -1
  118. package/dist/headless/source/components/dataDisplay/dagView/dagView.jsx.map +1 -1
  119. package/dist/headless/source/components/dataDisplay/dagView/index.js.map +2 -2
  120. package/dist/headless/source/components/dataDisplay/meter/index.js.map +1 -1
  121. package/dist/headless/source/components/dataDisplay/meter/meter.jsx +14 -2
  122. package/dist/headless/source/components/dataDisplay/meter/meter.jsx.map +1 -1
  123. package/dist/headless/source/components/dataDisplay/statusDot/index.js.map +1 -1
  124. package/dist/headless/source/components/dataDisplay/statusDot/statusDot.jsx +13 -1
  125. package/dist/headless/source/components/dataDisplay/statusDot/statusDot.jsx.map +1 -1
  126. package/dist/headless/source/components/dataDisplay/treeView/treeView.jsx +16 -3
  127. package/dist/headless/source/components/dataDisplay/treeView/treeView.jsx.map +1 -1
  128. package/dist/headless/source/components/inputs/button/button.jsx +26 -1
  129. package/dist/headless/source/components/inputs/button/button.jsx.map +1 -1
  130. package/dist/headless/source/components/inputs/toggle/toggle.jsx +9 -2
  131. package/dist/headless/source/components/inputs/toggle/toggle.jsx.map +1 -1
  132. package/dist/headless/source/components/layout/appLayout/appLayout.jsx +14 -2
  133. package/dist/headless/source/components/layout/appLayout/appLayout.jsx.map +1 -1
  134. package/dist/headless/source/components/layout/screenDivider/screenDivider.jsx +102 -18
  135. package/dist/headless/source/components/layout/screenDivider/screenDivider.jsx.map +1 -1
  136. package/dist/headless/source/components/layout/sheet/sheet.jsx +14 -1
  137. package/dist/headless/source/components/layout/sheet/sheet.jsx.map +1 -1
  138. package/dist/headless/source/components/layout/tabGroups/index.js.map +1 -1
  139. package/dist/headless/source/components/layout/tabGroups/tabGroups.jsx +55 -1
  140. package/dist/headless/source/components/layout/tabGroups/tabGroups.jsx.map +1 -1
  141. package/dist/headless/source/components/layout/tabGroups/tabGroupsFocus.js +42 -0
  142. package/dist/headless/source/components/layout/tabGroups/tabGroupsFocus.js.map +2 -2
  143. package/dist/headless/source/components/navigation/appBar/appBar.jsx.map +1 -1
  144. package/dist/headless/source/components/navigation/breadcrumbs/breadcrumbs.jsx +39 -4
  145. package/dist/headless/source/components/navigation/breadcrumbs/breadcrumbs.jsx.map +1 -1
  146. package/dist/headless/source/components/navigation/tabs/tab/tab.jsx +7 -1
  147. package/dist/headless/source/components/navigation/tabs/tab/tab.jsx.map +1 -1
  148. package/dist/headless/source/components/navigation/tabs/tabs.jsx +5 -1
  149. package/dist/headless/source/components/navigation/tabs/tabs.jsx.map +1 -1
  150. package/dist/headless/source/components/navigation/tabs/tabsContext.js.map +2 -2
  151. package/dist/headless/source/index.js +8 -1
  152. package/dist/headless/source/index.js.map +2 -2
  153. package/dist/headless/source/primitives/publishHeaderOffset.js +34 -0
  154. package/dist/headless/source/primitives/publishHeaderOffset.js.map +7 -0
  155. package/dist/index.d.ts +5 -5
  156. package/dist/primitives/publishHeaderOffset.d.ts +7 -0
  157. package/dist/primitives/publishHeaderOffset.js +19 -0
  158. package/dist/primitives/publishHeaderOffset.js.map +1 -0
  159. package/dist/source/components/dataDisplay/avatar/avatar.jsx +1 -0
  160. package/dist/source/components/dataDisplay/avatar/avatar.jsx.map +1 -1
  161. package/dist/source/components/dataDisplay/chart/chart.jsx +14 -12
  162. package/dist/source/components/dataDisplay/chart/chart.jsx.map +1 -1
  163. package/dist/source/components/dataDisplay/chart/chartFull.js +12 -0
  164. package/dist/source/components/dataDisplay/chart/chartFull.js.map +7 -0
  165. package/dist/source/components/dataDisplay/chart/core.js +11 -0
  166. package/dist/source/components/dataDisplay/chart/core.js.map +7 -0
  167. package/dist/source/components/dataDisplay/chart/index.js +1 -1
  168. package/dist/source/components/dataDisplay/chart/index.js.map +1 -1
  169. package/dist/source/components/dataDisplay/dagView/dagView.jsx +2 -1
  170. package/dist/source/components/dataDisplay/dagView/dagView.jsx.map +1 -1
  171. package/dist/source/components/dataDisplay/dagView/index.js.map +2 -2
  172. package/dist/source/components/dataDisplay/meter/index.js.map +1 -1
  173. package/dist/source/components/dataDisplay/meter/meter.jsx +14 -2
  174. package/dist/source/components/dataDisplay/meter/meter.jsx.map +1 -1
  175. package/dist/source/components/dataDisplay/statusDot/index.js.map +1 -1
  176. package/dist/source/components/dataDisplay/statusDot/statusDot.jsx +13 -1
  177. package/dist/source/components/dataDisplay/statusDot/statusDot.jsx.map +1 -1
  178. package/dist/source/components/dataDisplay/treeView/treeView.jsx +16 -3
  179. package/dist/source/components/dataDisplay/treeView/treeView.jsx.map +1 -1
  180. package/dist/source/components/inputs/button/button.jsx +26 -1
  181. package/dist/source/components/inputs/button/button.jsx.map +1 -1
  182. package/dist/source/components/inputs/toggle/toggle.jsx +9 -2
  183. package/dist/source/components/inputs/toggle/toggle.jsx.map +1 -1
  184. package/dist/source/components/layout/appLayout/appLayout.jsx +14 -2
  185. package/dist/source/components/layout/appLayout/appLayout.jsx.map +1 -1
  186. package/dist/source/components/layout/screenDivider/screenDivider.jsx +102 -18
  187. package/dist/source/components/layout/screenDivider/screenDivider.jsx.map +1 -1
  188. package/dist/source/components/layout/sheet/sheet.jsx +14 -1
  189. package/dist/source/components/layout/sheet/sheet.jsx.map +1 -1
  190. package/dist/source/components/layout/tabGroups/index.js.map +1 -1
  191. package/dist/source/components/layout/tabGroups/tabGroups.jsx +55 -1
  192. package/dist/source/components/layout/tabGroups/tabGroups.jsx.map +1 -1
  193. package/dist/source/components/layout/tabGroups/tabGroupsFocus.js +42 -0
  194. package/dist/source/components/layout/tabGroups/tabGroupsFocus.js.map +2 -2
  195. package/dist/source/components/navigation/appBar/appBar.jsx.map +1 -1
  196. package/dist/source/components/navigation/breadcrumbs/breadcrumbs.jsx +39 -4
  197. package/dist/source/components/navigation/breadcrumbs/breadcrumbs.jsx.map +1 -1
  198. package/dist/source/components/navigation/tabs/tab/tab.jsx +7 -1
  199. package/dist/source/components/navigation/tabs/tab/tab.jsx.map +1 -1
  200. package/dist/source/components/navigation/tabs/tabs.jsx +5 -1
  201. package/dist/source/components/navigation/tabs/tabs.jsx.map +1 -1
  202. package/dist/source/components/navigation/tabs/tabsContext.js.map +2 -2
  203. package/dist/source/index.js +8 -1
  204. package/dist/source/index.js.map +2 -2
  205. package/dist/source/primitives/publishHeaderOffset.js +34 -0
  206. package/dist/source/primitives/publishHeaderOffset.js.map +7 -0
  207. 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?.(divider());
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 = local.orientation === "vertical" ? rect.height : rect.width;
57
- const cursor = local.orientation === "vertical" ? event.clientY - rect.top : event.clientX - rect.left;
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(clamped / total * 100);
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 = local.orientation === "vertical" ? "ArrowUp" : "ArrowLeft";
83
- const increaseKey = local.orientation === "vertical" ? "ArrowDown" : "ArrowRight";
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((prev) => Math.max(0, prev - local.keyboardStep));
144
+ setDivider(clampDivider(Math.max(0, shown() - local.keyboardStep), keyBounds));
87
145
  } else if (event.key === increaseKey) {
88
146
  event.preventDefault();
89
- setDivider((prev) => Math.min(100, prev + local.keyboardStep));
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 firstChildStyle = () => local.orientation === "vertical" ? { height: `${divider()}%` } : { width: `${divider()}%` };
99
- const secondChildStyle = () => local.orientation === "vertical" ? { height: `${100 - divider()}%` } : { width: `${100 - divider()}%` };
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 class={cn("smwb-screen-divider__first", `smwb-${local.orientation}`)} style={firstChildStyle()}>
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(divider())}
115
- aria-valuemin={0}
116
- aria-valuemax={100}
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 class={cn("smwb-screen-divider__second", `smwb-${local.orientation}`)} style={secondChildStyle()}>
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 onClickAway={() => !local.disableOutsideClick && handleClose()}>
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)) lastFocus = null;
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"