@plastic-js/tsumiki 0.1.39 → 0.1.40
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/dist/components/BottomSheet.js +12 -55
- package/dist/components/Dialog.js +17 -12
- package/dist/components/Select.js +8 -2
- package/docs/api.md +16 -4
- package/package.json +1 -1
|
@@ -3,10 +3,9 @@ import { insert, jsx, mergeProps, setProp, template } from "@plastic-js/plastic/
|
|
|
3
3
|
import { css } from "@emotion/css";
|
|
4
4
|
import { createEffect, createSignal, mergeProps as mergeProps$1, splitProps } from "@plastic-js/plastic";
|
|
5
5
|
//#region src/components/BottomSheet.jsx
|
|
6
|
-
var
|
|
7
|
-
var
|
|
8
|
-
var
|
|
9
|
-
var _tmpl = template("<div></div>");
|
|
6
|
+
var _tmpl3 = template("<div data-part=\"grabber\"><div></div></div>");
|
|
7
|
+
var _tmpl2 = template("<div role=\"dialog\" aria-modal=\"true\" aria-label=\"BottomSheet\"><!><div data-part=\"content\"></div></div>");
|
|
8
|
+
var _tmpl = template("<div data-part=\"backdrop\"></div>");
|
|
10
9
|
var CLOSE_DISTANCE_RATIO = .3;
|
|
11
10
|
var CLOSE_VELOCITY = .5;
|
|
12
11
|
var RUBBER_BAND_FACTOR = 3;
|
|
@@ -37,6 +36,7 @@ var sheetClass = css({
|
|
|
37
36
|
borderRadius: "var(--tsu-radius-l3-md) var(--tsu-radius-l3-md) 0 0",
|
|
38
37
|
boxShadow: "var(--tsu-shadow-lg)",
|
|
39
38
|
maxHeight: "70vh",
|
|
39
|
+
"@supports (height: 100dvh)": { maxHeight: "min(70dvh, 70vh)" },
|
|
40
40
|
display: "flex",
|
|
41
41
|
flexDirection: "column",
|
|
42
42
|
transform: "translateY(100%)",
|
|
@@ -62,7 +62,7 @@ var grabberAreaClass = css({
|
|
|
62
62
|
display: "flex",
|
|
63
63
|
alignItems: "center",
|
|
64
64
|
justifyContent: "center",
|
|
65
|
-
|
|
65
|
+
height: "24px",
|
|
66
66
|
flexShrink: 0,
|
|
67
67
|
cursor: "grab",
|
|
68
68
|
touchAction: "none",
|
|
@@ -74,35 +74,12 @@ var grabberClass = css({
|
|
|
74
74
|
borderRadius: "2.5px",
|
|
75
75
|
background: "var(--tsu-neutral-alpha-6)"
|
|
76
76
|
});
|
|
77
|
-
var
|
|
77
|
+
var contentClass = css({
|
|
78
78
|
display: "flex",
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
padding: "var(--tsu-container-padding-lg)",
|
|
82
|
-
borderBottom: "1px solid var(--tsu-neutral-7)",
|
|
83
|
-
flexShrink: 0
|
|
84
|
-
});
|
|
85
|
-
var titleClass = css({
|
|
86
|
-
margin: 0,
|
|
87
|
-
fontSize: "var(--tsu-text-title-size)",
|
|
88
|
-
fontWeight: "var(--tsu-text-title-weight)",
|
|
89
|
-
lineHeight: "var(--tsu-text-title-line-height)",
|
|
90
|
-
color: "var(--tsu-fg)",
|
|
91
|
-
textAlign: "center"
|
|
92
|
-
});
|
|
93
|
-
var bodyClass = css({
|
|
94
|
-
overflowY: "auto",
|
|
95
|
-
padding: "var(--tsu-container-padding-lg)",
|
|
79
|
+
flexDirection: "column",
|
|
80
|
+
padding: "0 var(--tsu-container-padding-lg)",
|
|
96
81
|
flex: 1,
|
|
97
|
-
|
|
98
|
-
});
|
|
99
|
-
var bodyNoHeaderClass = css({ paddingTop: "calc(var(--tsu-container-padding-lg) * 2)" });
|
|
100
|
-
var footerClass = css({
|
|
101
|
-
display: "flex",
|
|
102
|
-
gap: "var(--tsu-spacing-sm)",
|
|
103
|
-
padding: "0 var(--tsu-container-padding-lg) var(--tsu-container-padding-lg)",
|
|
104
|
-
flexShrink: 0,
|
|
105
|
-
paddingTop: "var(--tsu-spacing-sm)"
|
|
82
|
+
minHeight: 0
|
|
106
83
|
});
|
|
107
84
|
var read = (v) => typeof v === "function" ? v() : v;
|
|
108
85
|
var BottomSheet = (props) => {
|
|
@@ -115,15 +92,12 @@ var BottomSheet = (props) => {
|
|
|
115
92
|
"onOpen",
|
|
116
93
|
"onClose",
|
|
117
94
|
"onOpenChange",
|
|
118
|
-
"title",
|
|
119
95
|
"dismissible",
|
|
120
96
|
"draggable",
|
|
121
97
|
"backdropBlur",
|
|
122
98
|
"backdropClassName",
|
|
123
99
|
"backdropStyle",
|
|
124
|
-
"
|
|
125
|
-
"footer",
|
|
126
|
-
"footerClass",
|
|
100
|
+
"contentClassName",
|
|
127
101
|
"class",
|
|
128
102
|
"children"
|
|
129
103
|
]);
|
|
@@ -197,8 +171,6 @@ var BottomSheet = (props) => {
|
|
|
197
171
|
const _el0 = _tmpl2.cloneNode(true);
|
|
198
172
|
const _el1 = _el0.firstChild;
|
|
199
173
|
const _el2 = _el0.firstChild.nextSibling;
|
|
200
|
-
const _el3 = _el0.firstChild.nextSibling.nextSibling;
|
|
201
|
-
const _el4 = _el0.firstChild.nextSibling.nextSibling.nextSibling;
|
|
202
174
|
insert(_el0, () => showGrabber && (() => {
|
|
203
175
|
const _el0 = _tmpl3.cloneNode(true);
|
|
204
176
|
const _el1 = _el0.firstChild;
|
|
@@ -210,22 +182,8 @@ var BottomSheet = (props) => {
|
|
|
210
182
|
setProp(_el0, "onPointerCancel", () => onGrabberPointerCancel);
|
|
211
183
|
return _el0;
|
|
212
184
|
})(), _el1);
|
|
213
|
-
insert(
|
|
214
|
-
|
|
215
|
-
const _el1 = _el0.firstChild;
|
|
216
|
-
insert(_el1, () => local.title);
|
|
217
|
-
setProp(_el1, "className", () => titleClass);
|
|
218
|
-
setProp(_el0, "className", () => headerClass);
|
|
219
|
-
return _el0;
|
|
220
|
-
}), _el2);
|
|
221
|
-
insert(_el3, () => local.children);
|
|
222
|
-
setProp(_el3, "className", () => [!local.title && !showGrabber ? `${bodyClass} ${bodyNoHeaderClass}` : bodyClass, local.bodyClassName].filter(Boolean).join(" "));
|
|
223
|
-
insert(_el0, () => local.footer && (() => {
|
|
224
|
-
const _el0 = _tmpl.cloneNode(true);
|
|
225
|
-
insert(_el0, () => local.footer);
|
|
226
|
-
setProp(_el0, "className", () => local.footerClass ? `${footerClass} ${local.footerClass}` : footerClass);
|
|
227
|
-
return _el0;
|
|
228
|
-
}), _el4);
|
|
185
|
+
insert(_el2, () => local.children);
|
|
186
|
+
setProp(_el2, "className", () => [contentClass, local.contentClassName].filter(Boolean).join(" "));
|
|
229
187
|
setProp(_el0, "ref", (el) => {
|
|
230
188
|
sheetEl = el;
|
|
231
189
|
});
|
|
@@ -235,7 +193,6 @@ var BottomSheet = (props) => {
|
|
|
235
193
|
local.class
|
|
236
194
|
].filter(Boolean).join(" "));
|
|
237
195
|
setProp(_el0, "data-open", () => read(local.open) ? "" : void 0);
|
|
238
|
-
setProp(_el0, "aria-label", () => local.title || "BottomSheet");
|
|
239
196
|
return _el0;
|
|
240
197
|
}] }));
|
|
241
198
|
};
|
|
@@ -60,7 +60,8 @@ var contentModalClass = css({
|
|
|
60
60
|
borderRadius: "var(--tsu-radius-l3-md)",
|
|
61
61
|
width: "100%",
|
|
62
62
|
maxWidth: "440px",
|
|
63
|
-
maxHeight: "calc(100vh - var(--tsu-spacing-lg) * 2)",
|
|
63
|
+
maxHeight: "min(calc(100vh - var(--tsu-spacing-lg) * 2), 560px)",
|
|
64
|
+
"@supports (height: 100dvh)": { maxHeight: "min(calc(100dvh - var(--tsu-spacing-lg) * 2), 560px)" },
|
|
64
65
|
display: "flex",
|
|
65
66
|
flexDirection: "column",
|
|
66
67
|
color: "var(--tsu-fg)",
|
|
@@ -82,7 +83,8 @@ var headerClass = css({
|
|
|
82
83
|
display: "flex",
|
|
83
84
|
alignItems: "center",
|
|
84
85
|
justifyContent: "space-between",
|
|
85
|
-
padding: "18px var(--tsu-container-padding-lg)",
|
|
86
|
+
padding: "18px var(--tsu-container-padding-lg) var(--tsu-spacing-lg)",
|
|
87
|
+
marginBottom: "var(--tsu-spacing-sm)",
|
|
86
88
|
borderBottom: "1px solid var(--tsu-neutral-7)",
|
|
87
89
|
flexShrink: 0
|
|
88
90
|
});
|
|
@@ -117,16 +119,16 @@ var closeBtnClass = css({
|
|
|
117
119
|
flexShrink: 0
|
|
118
120
|
});
|
|
119
121
|
var bodyClass = css({
|
|
120
|
-
padding: "var(--tsu-container-padding-lg)",
|
|
122
|
+
padding: "0 var(--tsu-container-padding-lg)",
|
|
121
123
|
flex: 1,
|
|
124
|
+
minHeight: 0,
|
|
122
125
|
overflowY: "auto",
|
|
123
126
|
WebkitOverflowScrolling: "touch"
|
|
124
127
|
});
|
|
125
|
-
var bodySheetNoHeaderClass = css({ paddingTop: "calc(var(--tsu-container-padding-lg) * 2)" });
|
|
126
128
|
var footerClass = css({
|
|
127
129
|
display: "flex",
|
|
128
130
|
gap: "var(--tsu-spacing-md)",
|
|
129
|
-
padding: "var(--tsu-
|
|
131
|
+
padding: "var(--tsu-container-padding-lg)",
|
|
130
132
|
flexShrink: 0
|
|
131
133
|
});
|
|
132
134
|
var footerBtnClass = css({
|
|
@@ -167,6 +169,7 @@ var btnDangerClass = css({
|
|
|
167
169
|
}
|
|
168
170
|
});
|
|
169
171
|
var read = (v) => typeof v === "function" ? v() : v;
|
|
172
|
+
var warnedSheetNoTitle = false;
|
|
170
173
|
var Dialog$1 = (props) => {
|
|
171
174
|
const [local, rest] = splitProps(mergeProps$2({
|
|
172
175
|
mode: "sheet",
|
|
@@ -250,8 +253,10 @@ var Dialog$1 = (props) => {
|
|
|
250
253
|
setProp(_el0, "className", () => onlyOneBtn ? `${footerBtnClass} tsu-dialog-footer--center` : footerBtnClass);
|
|
251
254
|
return _el0;
|
|
252
255
|
} : void 0;
|
|
253
|
-
|
|
254
|
-
|
|
256
|
+
if (isSheet && !local.title && !warnedSheetNoTitle) {
|
|
257
|
+
warnedSheetNoTitle = true;
|
|
258
|
+
console.warn("[Dialog] sheet mode requires a title; use BottomSheet for header-less content.");
|
|
259
|
+
}
|
|
255
260
|
return jsx(Dialog.Root, mergeProps({
|
|
256
261
|
get open() {
|
|
257
262
|
return read(local.open);
|
|
@@ -280,7 +285,7 @@ var Dialog$1 = (props) => {
|
|
|
280
285
|
return local.class ? `${isSheet ? contentSheetClass : contentModalClass} ${local.class}` : isSheet ? contentSheetClass : contentModalClass;
|
|
281
286
|
},
|
|
282
287
|
children: [
|
|
283
|
-
() =>
|
|
288
|
+
() => {
|
|
284
289
|
const _el0 = _tmpl4.cloneNode(true);
|
|
285
290
|
const _el1 = _el0.firstChild;
|
|
286
291
|
const _el2 = _el1.firstChild;
|
|
@@ -308,19 +313,19 @@ var Dialog$1 = (props) => {
|
|
|
308
313
|
})), _el4);
|
|
309
314
|
setProp(_el0, "className", () => headerClass);
|
|
310
315
|
return _el0;
|
|
311
|
-
}
|
|
316
|
+
},
|
|
312
317
|
() => {
|
|
313
318
|
const _el0 = _tmpl5.cloneNode(true);
|
|
314
319
|
insert(_el0, () => local.children);
|
|
315
|
-
setProp(_el0, "className", () =>
|
|
320
|
+
setProp(_el0, "className", () => bodyClass);
|
|
316
321
|
return _el0;
|
|
317
322
|
},
|
|
318
|
-
() =>
|
|
323
|
+
() => {
|
|
319
324
|
const _el0 = _tmpl6.cloneNode(true);
|
|
320
325
|
insert(_el0, () => footerContent);
|
|
321
326
|
setProp(_el0, "className", () => footerClass);
|
|
322
327
|
return _el0;
|
|
323
|
-
}
|
|
328
|
+
}
|
|
324
329
|
]
|
|
325
330
|
}))
|
|
326
331
|
}))] })) }));
|
|
@@ -59,7 +59,13 @@ var triggerIndicatorClass = css({
|
|
|
59
59
|
}
|
|
60
60
|
});
|
|
61
61
|
var listBodyClass = css({ padding: 0 });
|
|
62
|
-
var listClass = css({
|
|
62
|
+
var listClass = css({
|
|
63
|
+
flex: 1,
|
|
64
|
+
minHeight: 0,
|
|
65
|
+
overflowY: "auto",
|
|
66
|
+
WebkitOverflowScrolling: "touch",
|
|
67
|
+
padding: "0 8px calc(8px + env(safe-area-inset-bottom, 0px))"
|
|
68
|
+
});
|
|
63
69
|
var itemClass = css({
|
|
64
70
|
width: "100%",
|
|
65
71
|
display: "flex",
|
|
@@ -308,7 +314,7 @@ var Content = () => {
|
|
|
308
314
|
get backdropStyle() {
|
|
309
315
|
return ctx.backdropStyle ?? { background: "rgba(0,0,0,0.5)" };
|
|
310
316
|
},
|
|
311
|
-
|
|
317
|
+
contentClassName: listBodyClass,
|
|
312
318
|
children: jsx("div", mergeProps(() => part("list"), {
|
|
313
319
|
className: listClass,
|
|
314
320
|
children: [() => ctx.filter && (() => {
|
package/docs/api.md
CHANGED
|
@@ -641,6 +641,8 @@ Passthrough: Ark `Root` props (`value`, `min`, `max`, …).
|
|
|
641
641
|
|
|
642
642
|
**Rendered DOM parts:** content is `[data-scope="dialog"][data-part="content"]`; tsumiki adds `data-part="header"`, `data-part="body"`, `data-part="footer"`, and the action buttons `data-part="negative-trigger"` / `data-part="positive-trigger"`. `title`, `description`, and `closeTrigger` carry `data-part` from Ark.
|
|
643
643
|
|
|
644
|
+
> **Note:** Dialog never renders a grabber / drag handle in either mode (`modal` or `sheet`). Drag-to-dismiss and the grabber are exclusive to [`BottomSheet`](#bottomsheet) — this is a deliberate difference between the two components.
|
|
645
|
+
|
|
644
646
|
| Prop | Type | Default | Description |
|
|
645
647
|
|---|---|---|---|
|
|
646
648
|
| `mode` | `string` | `'sheet'` | `'modal'` (centered) or `'sheet'` (bottom sheet). |
|
|
@@ -747,16 +749,26 @@ Passthrough: Ark `Root` props (`open`, `onOpenChange`, `positioning`, …).
|
|
|
747
749
|
|
|
748
750
|
### BottomSheet
|
|
749
751
|
|
|
750
|
-
|
|
752
|
+
A free-form presentation layer: a backdrop, an optional drag grabber, and a scrollable content area (`children`). The content is fully consumer-owned — headers, titles, and footers are just regular content.
|
|
751
753
|
|
|
752
754
|
**Exports:** `BottomSheet` · no `.origin`
|
|
753
755
|
|
|
756
|
+
**Rendered DOM parts:** exactly three — `data-part="backdrop"`, `data-part="grabber"` (only when `draggable`), and `data-part="content"`. BottomSheet has no focus trap or Escape-to-close — use [`Dialog`](#dialog) when those are required.
|
|
757
|
+
|
|
758
|
+
> **Design decision:** `content` is a pure container — `display: flex; flex-direction: column` with `padding: 0 24px` only. It has **no vertical padding, no margin, and no scrolling**. Spacing and scrolling are entirely consumer-owned: build your own layout inside `children` (the sheet caps height at `70vh`, so a scrollable section can be a `flex: 1; min-height: 0; overflow-y: auto` child).
|
|
759
|
+
|
|
754
760
|
| Prop | Type | Default | Description |
|
|
755
761
|
|---|---|---|---|
|
|
756
|
-
| `
|
|
762
|
+
| `open` | `boolean \| (() => boolean)` | — | **Required** controlled open state. |
|
|
763
|
+
| `onOpenChange` | `({ open }) => void` | — | Fired on open/close; sync your state here. |
|
|
764
|
+
| `dismissible` | `boolean` | `true` | Allow dismiss via backdrop / drag. |
|
|
765
|
+
| `draggable` | `boolean` | `true` | Show the grabber and enable drag-to-dismiss. |
|
|
757
766
|
| `backdropBlur` | `boolean` | `true` | Blur the backdrop. |
|
|
758
|
-
|
|
759
|
-
|
|
767
|
+
| `backdropClassName` | `string` | — | Extra class for the backdrop. |
|
|
768
|
+
| `backdropStyle` | `object` | — | Extra style for the backdrop. |
|
|
769
|
+
| `contentClassName` | `string` | — | Extra class for the content area. |
|
|
770
|
+
| `class` | `string` | — | Extra class for the sheet. |
|
|
771
|
+
| `children` | `node` | — | **Required** body content. |
|
|
760
772
|
|
|
761
773
|
---
|
|
762
774
|
|