@plastic-js/tsumiki 0.1.27 → 0.1.29

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.
@@ -0,0 +1,99 @@
1
+ import { insert, setProp, template } from "@plastic-js/plastic/jsx-runtime";
2
+ import { css } from "@emotion/css";
3
+ import { createSignal, mergeProps as mergeProps$1, onCleanup, splitProps } from "@plastic-js/plastic";
4
+ //#region src/components/BackToTop.jsx
5
+ var _tmpl2 = template("<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\"><title>Back to top</title><path d=\"m18 15-6-6-6 6\"></path></svg>");
6
+ var _tmpl = template("<button type=\"button\"></button>");
7
+ var read = (v) => typeof v === "function" ? v() : v;
8
+ var baseClass = css({
9
+ position: "fixed",
10
+ right: "var(--tsu-spacing-lg)",
11
+ bottom: "var(--tsu-spacing-xl)",
12
+ display: "inline-flex",
13
+ alignItems: "center",
14
+ justifyContent: "center",
15
+ width: "48px",
16
+ height: "48px",
17
+ border: "none",
18
+ borderRadius: "50%",
19
+ backgroundColor: "var(--tsu-accent-9)",
20
+ color: "var(--tsu-bg)",
21
+ boxShadow: "var(--tsu-shadow-md)",
22
+ cursor: "pointer",
23
+ userSelect: "none",
24
+ touchAction: "manipulation",
25
+ zIndex: 90,
26
+ opacity: 0,
27
+ transform: "scale(0.8)",
28
+ pointerEvents: "none",
29
+ transition: "opacity var(--tsu-transition-normal), transform var(--tsu-transition-normal)",
30
+ "&:active": { transform: "scale(0.9)" },
31
+ "&:focus-visible": {
32
+ outline: "2px solid var(--tsu-accent-8)",
33
+ outlineOffset: "2px"
34
+ }
35
+ });
36
+ var visibleClass = css({
37
+ opacity: 1,
38
+ transform: "scale(1)",
39
+ pointerEvents: "auto"
40
+ });
41
+ var iconClass = css({
42
+ display: "block",
43
+ width: "22px",
44
+ height: "22px"
45
+ });
46
+ var BackToTop = (props = {}) => {
47
+ const [local] = splitProps(mergeProps$1({ threshold: 200 }, props), [
48
+ "className",
49
+ "threshold",
50
+ "target",
51
+ "icon",
52
+ "aria-label",
53
+ "onClick"
54
+ ]);
55
+ const visible = createSignal(false);
56
+ const getEl = () => typeof local.target === "function" ? local.target() : null;
57
+ const onScroll = () => {
58
+ const el = getEl();
59
+ visible((el ? el.scrollTop : window.scrollY) > read(local.threshold));
60
+ };
61
+ onCleanup(() => {
62
+ window.removeEventListener("scroll", onScroll, true);
63
+ });
64
+ window.addEventListener("scroll", onScroll, {
65
+ capture: true,
66
+ passive: true
67
+ });
68
+ const handleClick = () => {
69
+ const el = getEl();
70
+ if (el) el.scrollTo({
71
+ top: 0,
72
+ behavior: "smooth"
73
+ });
74
+ else window.scrollTo({
75
+ top: 0,
76
+ behavior: "smooth"
77
+ });
78
+ local.onClick?.();
79
+ };
80
+ return () => {
81
+ const _el0 = _tmpl.cloneNode(true);
82
+ insert(_el0, () => local.icon || (() => {
83
+ const _el0 = _tmpl2.cloneNode(true);
84
+ setProp(_el0, "className", () => iconClass);
85
+ return _el0;
86
+ })());
87
+ setProp(_el0, "className", () => [
88
+ baseClass,
89
+ visible() && visibleClass,
90
+ local.className
91
+ ].filter(Boolean).join(" "));
92
+ setProp(_el0, "onClick", () => handleClick);
93
+ setProp(_el0, "aria-label", () => local["aria-label"] || "Back to top");
94
+ return _el0;
95
+ };
96
+ };
97
+ BackToTop.origin = { Root: "button" };
98
+ //#endregion
99
+ export { BackToTop as default };
@@ -0,0 +1,98 @@
1
+ import { insert, jsx, mergeProps, setProp, template } from "@plastic-js/plastic/jsx-runtime";
2
+ import { css } from "@emotion/css";
3
+ import { Dynamic, mergeProps as mergeProps$1, splitProps } from "@plastic-js/plastic";
4
+ //#region src/components/BottomNavigation.jsx
5
+ var _tmpl = template("<span></span>");
6
+ var read = (v) => typeof v === "function" ? v() : v;
7
+ var rootClass = css({
8
+ position: "fixed",
9
+ insetInline: "0",
10
+ bottom: "0",
11
+ display: "flex",
12
+ justifyContent: "space-around",
13
+ alignItems: "center",
14
+ height: "56px",
15
+ paddingBottom: "env(safe-area-inset-bottom)",
16
+ backgroundColor: "var(--tsu-bg)",
17
+ borderTop: "1px solid var(--tsu-neutral-6)",
18
+ zIndex: "var(--tsu-z-base)"
19
+ });
20
+ var itemClass = css({
21
+ display: "flex",
22
+ flexDirection: "column",
23
+ alignItems: "center",
24
+ gap: "var(--tsu-spacing-xxs)",
25
+ padding: "4px 12px",
26
+ border: "none",
27
+ background: "none",
28
+ fontFamily: "inherit",
29
+ color: "var(--tsu-neutral-8)",
30
+ textDecoration: "none",
31
+ cursor: "pointer",
32
+ userSelect: "none",
33
+ touchAction: "manipulation",
34
+ transition: "color var(--tsu-transition-fast)",
35
+ "&[data-active]": { color: "var(--tsu-accent-11)" }
36
+ });
37
+ var iconClass = css({
38
+ display: "flex",
39
+ alignItems: "center",
40
+ justifyContent: "center",
41
+ width: "24px",
42
+ height: "24px",
43
+ "& svg": {
44
+ width: "100%",
45
+ height: "100%"
46
+ }
47
+ });
48
+ var labelClass = css({
49
+ fontSize: "11px",
50
+ lineHeight: "14px",
51
+ fontWeight: 500
52
+ });
53
+ var BottomNavigationItem = (props = {}) => {
54
+ const [local, rest] = splitProps(props, [
55
+ "icon",
56
+ "label",
57
+ "active",
58
+ "href",
59
+ "onClick",
60
+ "className"
61
+ ]);
62
+ return jsx(Dynamic, mergeProps({ component: local.href ? "a" : "button" }, () => local.href ? { href: local.href } : { type: "button" }, {
63
+ get className() {
64
+ return [itemClass, local.className].filter(Boolean).join(" ");
65
+ },
66
+ "data-active": () => read(local.active) ? "" : void 0,
67
+ get onClick() {
68
+ return local.onClick;
69
+ },
70
+ children: [() => {
71
+ const _el0 = _tmpl.cloneNode(true);
72
+ insert(_el0, () => local.icon);
73
+ setProp(_el0, "className", () => iconClass);
74
+ return _el0;
75
+ }, () => {
76
+ const _el0 = _tmpl.cloneNode(true);
77
+ insert(_el0, () => local.label);
78
+ setProp(_el0, "className", () => labelClass);
79
+ return _el0;
80
+ }]
81
+ }));
82
+ };
83
+ var BottomNavigation = (props = {}) => {
84
+ const [local, rest] = splitProps(mergeProps$1({}, props), ["className", "children"]);
85
+ return jsx("nav", mergeProps(rest, {
86
+ get className() {
87
+ return [rootClass, local.className].filter(Boolean).join(" ");
88
+ },
89
+ children: () => local.children
90
+ }));
91
+ };
92
+ BottomNavigation.Item = BottomNavigationItem;
93
+ BottomNavigation.origin = {
94
+ Root: "nav",
95
+ Item: BottomNavigationItem
96
+ };
97
+ //#endregion
98
+ export { BottomNavigationItem, BottomNavigation as default };
@@ -116,7 +116,10 @@ var loadingMaskClass = css({
116
116
  });
117
117
  var loadingSpinnerClass = css({
118
118
  position: "relative",
119
- zIndex: 1
119
+ zIndex: 1,
120
+ display: "flex",
121
+ alignItems: "center",
122
+ justifyContent: "center"
120
123
  });
121
124
  var loadingClass = css({ pointerEvents: "none" });
122
125
  var read = (v) => typeof v === "function" ? v() : v;
@@ -0,0 +1,20 @@
1
+ import { jsx, mergeProps } from "@plastic-js/plastic/jsx-runtime";
2
+ import { css } from "@emotion/css";
3
+ import { mergeProps as mergeProps$1, splitProps } from "@plastic-js/plastic";
4
+ //#region src/components/Card.jsx
5
+ var baseClass = css({
6
+ backgroundColor: "var(--tsu-bg)",
7
+ borderRadius: "var(--tsu-radius-l2-md)"
8
+ });
9
+ var Card = (props = {}) => {
10
+ const [local, rest] = splitProps(mergeProps$1({}, props), ["className", "children"]);
11
+ return jsx("div", mergeProps(rest, {
12
+ get className() {
13
+ return [baseClass, local.className].filter(Boolean).join(" ");
14
+ },
15
+ children: () => local.children
16
+ }));
17
+ };
18
+ Card.origin = { Root: "div" };
19
+ //#endregion
20
+ export { Card as default };
@@ -1,12 +1,45 @@
1
- import { jsx, mergeProps } from "@plastic-js/plastic/jsx-runtime";
1
+ import { jsx, mergeProps, template } from "@plastic-js/plastic/jsx-runtime";
2
2
  import { css } from "@emotion/css";
3
3
  import { Collapsible } from "@plastic-js/ark";
4
4
  import { splitProps } from "@plastic-js/plastic";
5
5
  //#region src/components/Collapsible.jsx
6
+ var _tmpl = template("<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\"><path d=\"m6 9 6 6 6-6\"></path></svg>");
6
7
  var rootClass = css({
7
8
  display: "flex",
8
9
  flexDirection: "column"
9
10
  });
11
+ var triggerClass = css({
12
+ display: "flex",
13
+ alignItems: "center",
14
+ justifyContent: "space-between",
15
+ gap: "var(--tsu-spacing-sm)",
16
+ width: "100%",
17
+ padding: "var(--tsu-comp-padding-y-sm) var(--tsu-comp-padding-x-sm)",
18
+ border: "none",
19
+ background: "var(--tsu-neutral-2)",
20
+ borderRadius: "var(--tsu-radius-l1-md)",
21
+ color: "var(--tsu-fg)",
22
+ fontSize: "var(--tsu-comp-font-size-sm)",
23
+ fontWeight: 500,
24
+ fontFamily: "inherit",
25
+ cursor: "pointer",
26
+ textAlign: "left",
27
+ touchAction: "manipulation",
28
+ transition: "background-color var(--tsu-transition-fast)",
29
+ "& svg": {
30
+ flexShrink: 0,
31
+ width: "16px",
32
+ height: "16px",
33
+ color: "var(--tsu-neutral-11)",
34
+ transition: "transform var(--tsu-transition-normal)"
35
+ },
36
+ "&[aria-expanded=\"true\"] svg": { transform: "rotate(180deg)" },
37
+ "&:disabled": {
38
+ opacity: .5,
39
+ cursor: "not-allowed"
40
+ }
41
+ });
42
+ var contentClass = css({ padding: "var(--tsu-spacing-sm) var(--tsu-spacing-md)" });
10
43
  var Collapsible$1 = (props = {}) => {
11
44
  const [local, rest] = splitProps(props, ["className", "children"]);
12
45
  return jsx(Collapsible.Root, mergeProps(rest, {
@@ -16,10 +49,25 @@ var Collapsible$1 = (props = {}) => {
16
49
  children: () => local.children
17
50
  }));
18
51
  };
52
+ var CollapsibleTrigger = (props = {}) => {
53
+ const [local, rest] = splitProps(props, ["className", "children"]);
54
+ return jsx(Collapsible.Trigger, mergeProps(rest, {
55
+ get className() {
56
+ return [triggerClass, local.className].filter(Boolean).join(" ");
57
+ },
58
+ children: [() => local.children, _tmpl.cloneNode(true)]
59
+ }));
60
+ };
61
+ var CollapsibleContent = (props = {}) => {
62
+ const [local, rest] = splitProps(props, ["className"]);
63
+ return jsx(Collapsible.Content, mergeProps(rest, { get className() {
64
+ return [contentClass, local.className].filter(Boolean).join(" ");
65
+ } }));
66
+ };
19
67
  Collapsible$1.origin = {
20
68
  Root: Collapsible.Root,
21
69
  Trigger: Collapsible.Trigger,
22
70
  Content: Collapsible.Content
23
71
  };
24
72
  //#endregion
25
- export { Collapsible$1 as default };
73
+ export { CollapsibleContent, CollapsibleTrigger, Collapsible$1 as default };
@@ -1,11 +1,9 @@
1
1
  import { insert, jsx, mergeProps, setProp, template } from "@plastic-js/plastic/jsx-runtime";
2
2
  import { css } from "@emotion/css";
3
- import { mergeProps as mergeProps$1, splitProps } from "@plastic-js/plastic";
3
+ import { Field } from "@plastic-js/ark";
4
+ import { mergeProps as mergeProps$2, splitProps } from "@plastic-js/plastic";
4
5
  //#region src/components/Field.jsx
5
- var _tmpl4 = template("<p></p>");
6
- var _tmpl3 = template("<div></div>");
7
- var _tmpl2 = template("<span>*</span>");
8
- var _tmpl = template("<span><!><!></span>");
6
+ var _tmpl = template("<div></div>");
9
7
  var rootClass = css({
10
8
  display: "flex",
11
9
  flexDirection: "column",
@@ -51,8 +49,8 @@ var helperTextClass = css({
51
49
  flexBasis: "100%"
52
50
  });
53
51
  var read = (v) => typeof v === "function" ? v() : v;
54
- var Field = (props = {}) => {
55
- const [local, rest] = splitProps(mergeProps$1({
52
+ var Field$1 = (props = {}) => {
53
+ const [local, rest] = splitProps(mergeProps$2({
56
54
  label: "",
57
55
  error: "",
58
56
  helper: "",
@@ -64,6 +62,9 @@ var Field = (props = {}) => {
64
62
  "helper",
65
63
  "required",
66
64
  "compact",
65
+ "invalid",
66
+ "disabled",
67
+ "readOnly",
67
68
  "className",
68
69
  "children"
69
70
  ]);
@@ -72,52 +73,61 @@ var Field = (props = {}) => {
72
73
  const hasError = () => !!error();
73
74
  const hasHelper = () => !!helper();
74
75
  const isCompact = () => read(local.compact);
75
- return jsx("div", mergeProps(rest, {
76
- className: () => [isCompact() ? compactRootClass : rootClass, local.className].filter(Boolean).join(" "),
77
- get "data-invalid"() {
78
- return hasError() ? "" : void 0;
76
+ const required = () => read(local.required);
77
+ const invalid = () => {
78
+ if (hasError()) return true;
79
+ return local.invalid === void 0 ? void 0 : read(local.invalid);
80
+ };
81
+ const disabled = () => local.disabled === void 0 ? void 0 : read(local.disabled);
82
+ const readOnly = () => local.readOnly === void 0 ? void 0 : read(local.readOnly);
83
+ return jsx(Field.Root, mergeProps(rest, {
84
+ get invalid() {
85
+ return invalid();
86
+ },
87
+ get disabled() {
88
+ return disabled();
89
+ },
90
+ get readOnly() {
91
+ return readOnly();
79
92
  },
93
+ get required() {
94
+ return required();
95
+ },
96
+ className: () => [isCompact() ? compactRootClass : rootClass, local.className].filter(Boolean).join(" "),
80
97
  children: [
81
- () => {
82
- const label = read(local.label);
83
- if (!label) return null;
84
- return () => {
85
- const _el0 = _tmpl.cloneNode(true);
86
- const _el1 = _el0.firstChild;
87
- const _el2 = _el0.firstChild.nextSibling;
88
- insert(_el0, () => label, _el1);
89
- insert(_el0, () => read(local.required) && (() => {
90
- const _el0 = _tmpl2.cloneNode(true);
91
- setProp(_el0, "className", () => requiredClass);
92
- return _el0;
93
- })(), _el2);
94
- setProp(_el0, "className", () => [labelClass, isCompact() && compactLabelClass].filter(Boolean).join(" "));
95
- return _el0;
96
- };
97
- },
98
+ () => read(local.label) ? jsx(Field.Label, mergeProps({
99
+ className: () => [labelClass, isCompact() && compactLabelClass].filter(Boolean).join(" "),
100
+ children: [() => read(local.label), () => required() && jsx(Field.RequiredIndicator, mergeProps({
101
+ className: requiredClass,
102
+ children: "*"
103
+ }))]
104
+ })) : null,
98
105
  () => isCompact() ? () => {
99
- const _el0 = _tmpl3.cloneNode(true);
106
+ const _el0 = _tmpl.cloneNode(true);
100
107
  insert(_el0, () => local.children);
101
108
  setProp(_el0, "className", () => inputWrapClass);
102
109
  return _el0;
103
110
  } : local.children,
104
- () => {
105
- if (hasError()) return () => {
106
- const _el0 = _tmpl4.cloneNode(true);
107
- insert(_el0, () => error());
108
- setProp(_el0, "className", () => errorTextClass);
109
- return _el0;
110
- };
111
- if (hasHelper()) return () => {
112
- const _el0 = _tmpl4.cloneNode(true);
113
- insert(_el0, () => helper());
114
- setProp(_el0, "className", () => helperTextClass);
115
- return _el0;
116
- };
117
- return null;
118
- }
111
+ () => hasError() ? jsx(Field.ErrorText, mergeProps({
112
+ className: errorTextClass,
113
+ children: () => error()
114
+ })) : hasHelper() ? jsx(Field.HelperText, mergeProps({
115
+ className: helperTextClass,
116
+ children: () => helper()
117
+ })) : null
119
118
  ]
120
119
  }));
121
120
  };
121
+ Field$1.origin = {
122
+ Root: Field.Root,
123
+ Label: Field.Label,
124
+ Input: Field.Input,
125
+ Textarea: Field.Textarea,
126
+ Select: Field.Select,
127
+ HelperText: Field.HelperText,
128
+ ErrorText: Field.ErrorText,
129
+ RequiredIndicator: Field.RequiredIndicator,
130
+ Item: Field.Item
131
+ };
122
132
  //#endregion
123
- export { Field as default };
133
+ export { Field$1 as default };
@@ -1,9 +1,8 @@
1
- import { insert, jsx, mergeProps, setProp, template } from "@plastic-js/plastic/jsx-runtime";
1
+ import { jsx, mergeProps } from "@plastic-js/plastic/jsx-runtime";
2
2
  import { css } from "@emotion/css";
3
- import { mergeProps as mergeProps$1, splitProps } from "@plastic-js/plastic";
3
+ import { Fieldset } from "@plastic-js/ark";
4
+ import { mergeProps as mergeProps$2, splitProps } from "@plastic-js/plastic";
4
5
  //#region src/components/Fieldset.jsx
5
- var _tmpl2 = template("<p></p>");
6
- var _tmpl = template("<div></div>");
7
6
  var rootClass = css({
8
7
  display: "flex",
9
8
  flexDirection: "column",
@@ -25,8 +24,8 @@ var helperClass = css({
25
24
  margin: 0
26
25
  });
27
26
  var read = (v) => typeof v === "function" ? v() : v;
28
- var Fieldset = (props = {}) => {
29
- const [local, rest] = splitProps(mergeProps$1({
27
+ var Fieldset$1 = (props = {}) => {
28
+ const [local, rest] = splitProps(mergeProps$2({
30
29
  legend: "",
31
30
  helper: ""
32
31
  }, props), [
@@ -35,32 +34,26 @@ var Fieldset = (props = {}) => {
35
34
  "className",
36
35
  "children"
37
36
  ]);
38
- return jsx("div", mergeProps(rest, {
37
+ return jsx(Fieldset.Root, mergeProps(rest, {
39
38
  className: () => [rootClass, local.className].filter(Boolean).join(" "),
40
39
  children: [
41
- () => {
42
- const legend = read(local.legend);
43
- if (!legend) return null;
44
- return () => {
45
- const _el0 = _tmpl.cloneNode(true);
46
- insert(_el0, () => legend);
47
- setProp(_el0, "className", () => legendClass);
48
- return _el0;
49
- };
50
- },
40
+ () => read(local.legend) ? jsx(Fieldset.Legend, mergeProps({
41
+ className: legendClass,
42
+ children: () => read(local.legend)
43
+ })) : null,
51
44
  () => local.children,
52
- () => {
53
- const helper = read(local.helper);
54
- if (!helper) return null;
55
- return () => {
56
- const _el0 = _tmpl2.cloneNode(true);
57
- insert(_el0, () => helper);
58
- setProp(_el0, "className", () => helperClass);
59
- return _el0;
60
- };
61
- }
45
+ () => read(local.helper) ? jsx(Fieldset.HelperText, mergeProps({
46
+ className: helperClass,
47
+ children: () => read(local.helper)
48
+ })) : null
62
49
  ]
63
50
  }));
64
51
  };
52
+ Fieldset$1.origin = {
53
+ Root: Fieldset.Root,
54
+ Legend: Fieldset.Legend,
55
+ HelperText: Fieldset.HelperText,
56
+ ErrorText: Fieldset.ErrorText
57
+ };
65
58
  //#endregion
66
- export { Fieldset as default };
59
+ export { Fieldset$1 as default };
@@ -0,0 +1,150 @@
1
+ import ToggleGroup, { ToggleGroupItem } from "./ToggleGroup.js";
2
+ import { insert, jsx, mergeProps, setProp, template } from "@plastic-js/plastic/jsx-runtime";
3
+ import { css } from "@emotion/css";
4
+ import { mergeProps as mergeProps$1, splitProps } from "@plastic-js/plastic";
5
+ //#region src/components/FilterGroup.jsx
6
+ var _tmpl = template("<button type=\"button\"></button>");
7
+ var access = (v) => typeof v === "function" ? v() : v;
8
+ var sizeVars = {
9
+ xs: {
10
+ "--_tg-padding-y": "var(--tsu-comp-padding-y-xs)",
11
+ "--_tg-padding-x": "var(--tsu-comp-padding-x-xs)",
12
+ "--_tg-font-size": "var(--tsu-comp-font-size-xs)",
13
+ "--_tg-radius": "var(--tsu-radius-l1-xs)"
14
+ },
15
+ sm: {
16
+ "--_tg-padding-y": "var(--tsu-comp-padding-y-sm)",
17
+ "--_tg-padding-x": "var(--tsu-comp-padding-x-sm)",
18
+ "--_tg-font-size": "var(--tsu-comp-font-size-sm)",
19
+ "--_tg-radius": "var(--tsu-radius-l1-sm)"
20
+ },
21
+ md: {
22
+ "--_tg-padding-y": "var(--tsu-comp-padding-y-md)",
23
+ "--_tg-padding-x": "var(--tsu-comp-padding-x-md)",
24
+ "--_tg-font-size": "var(--tsu-comp-font-size-md)",
25
+ "--_tg-radius": "var(--tsu-radius-l1-md)"
26
+ },
27
+ lg: {
28
+ "--_tg-padding-y": "var(--tsu-comp-padding-y-lg)",
29
+ "--_tg-padding-x": "var(--tsu-comp-padding-x-lg)",
30
+ "--_tg-font-size": "var(--tsu-comp-font-size-lg)",
31
+ "--_tg-radius": "var(--tsu-radius-l1-lg)"
32
+ },
33
+ xl: {
34
+ "--_tg-padding-y": "var(--tsu-comp-padding-y-xl)",
35
+ "--_tg-padding-x": "var(--tsu-comp-padding-x-xl)",
36
+ "--_tg-font-size": "var(--tsu-comp-font-size-xl)",
37
+ "--_tg-radius": "var(--tsu-radius-l1-xl)"
38
+ }
39
+ };
40
+ var defaultStyle = { "--_tg-border": "var(--tsu-accent-9)" };
41
+ var rootClass = css({ display: "inline-flex" });
42
+ var allClass = css({
43
+ display: "flex",
44
+ alignItems: "center",
45
+ justifyContent: "center",
46
+ padding: "var(--_tg-padding-y, var(--tsu-comp-padding-y-md)) var(--_tg-padding-x, var(--tsu-comp-padding-x-md))",
47
+ border: "1px solid var(--_tg-border)",
48
+ borderRight: "none",
49
+ borderTopLeftRadius: "var(--_tg-radius, var(--tsu-radius-l1-md))",
50
+ borderBottomLeftRadius: "var(--_tg-radius, var(--tsu-radius-l1-md))",
51
+ background: "transparent",
52
+ color: "var(--tsu-neutral-11)",
53
+ fontSize: "var(--_tg-font-size, var(--tsu-comp-font-size-md))",
54
+ fontWeight: 500,
55
+ fontFamily: "inherit",
56
+ cursor: "pointer",
57
+ touchAction: "manipulation",
58
+ transition: "background-color var(--tsu-transition-fast), color var(--tsu-transition-fast)",
59
+ "&[data-state=\"on\"]": {
60
+ backgroundColor: "var(--tsu-accent-9)",
61
+ color: "var(--tsu-bg)"
62
+ },
63
+ "&:disabled": {
64
+ opacity: .5,
65
+ cursor: "not-allowed"
66
+ }
67
+ });
68
+ var tgStyle = {
69
+ borderTopLeftRadius: "0",
70
+ borderBottomLeftRadius: "0"
71
+ };
72
+ var FilterGroup = (props = {}) => {
73
+ const [local, rest] = splitProps(mergeProps$1({
74
+ size: "md",
75
+ allLabel: "All",
76
+ items: []
77
+ }, props), [
78
+ "className",
79
+ "items",
80
+ "value",
81
+ "onValueChange",
82
+ "allLabel",
83
+ "size",
84
+ "buttonClass",
85
+ "groupClass"
86
+ ]);
87
+ const value = () => access(local.value) ?? [];
88
+ const allActive = () => value().length === 0;
89
+ const emit = (val) => {
90
+ local.onValueChange?.(val);
91
+ };
92
+ const handleAllClick = () => {
93
+ if (allActive()) return;
94
+ emit([]);
95
+ };
96
+ const handleChange = (value) => emit(value);
97
+ return jsx("div", mergeProps(rest, {
98
+ get className() {
99
+ return [rootClass, local.className].filter(Boolean).join(" ");
100
+ },
101
+ get style() {
102
+ return {
103
+ ...defaultStyle,
104
+ ...sizeVars[local.size]
105
+ };
106
+ },
107
+ children: [() => {
108
+ const _el0 = _tmpl.cloneNode(true);
109
+ insert(_el0, () => local.allLabel);
110
+ setProp(_el0, "className", () => [allClass, local.buttonClass].filter(Boolean).join(" "));
111
+ setProp(_el0, "data-state", () => allActive() ? "on" : "off");
112
+ setProp(_el0, "onClick", () => handleAllClick);
113
+ return _el0;
114
+ }, jsx(ToggleGroup, mergeProps({
115
+ multiple: true,
116
+ get size() {
117
+ return local.size;
118
+ },
119
+ get value() {
120
+ return allActive() ? [] : value();
121
+ },
122
+ onValueChange: handleChange,
123
+ get className() {
124
+ return local.groupClass;
125
+ },
126
+ style: tgStyle,
127
+ children: () => local.items.map((item) => jsx(ToggleGroupItem, mergeProps({
128
+ get key() {
129
+ return item.value;
130
+ },
131
+ get value() {
132
+ return item.value;
133
+ },
134
+ get disabled() {
135
+ return item.disabled;
136
+ },
137
+ get className() {
138
+ return local.buttonClass;
139
+ },
140
+ children: () => item.label
141
+ })))
142
+ }))]
143
+ }));
144
+ };
145
+ FilterGroup.origin = {
146
+ ToggleGroup,
147
+ ToggleGroupItem
148
+ };
149
+ //#endregion
150
+ export { FilterGroup as default };
@@ -1,7 +1,8 @@
1
1
  import { insert, jsx, mergeProps, setProp, template } from "@plastic-js/plastic/jsx-runtime";
2
2
  import { css } from "@emotion/css";
3
- import { createSignal, mergeProps as mergeProps$1, splitProps } from "@plastic-js/plastic";
4
- import { ark } from "@plastic-js/ark/factory";
3
+ import { useFieldContext } from "@plastic-js/ark";
4
+ import { createSignal, mergeProps as mergeProps$2, splitProps } from "@plastic-js/plastic";
5
+ import { ark as ark$1 } from "@plastic-js/ark/factory";
5
6
  //#region src/components/Input.jsx
6
7
  var _tmpl5 = template("<svg fill=\"none\" stroke=\"currentColor\" strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth=\"2\" viewBox=\"0 0 24 24\"><path d=\"M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z\"></path><circle cx=\"12\" cy=\"12\" r=\"3\"></circle></svg>");
7
8
  var _tmpl4 = template("<svg fill=\"none\" stroke=\"currentColor\" strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth=\"2\" viewBox=\"0 0 24 24\"><path d=\"M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94\"></path><path d=\"M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19\"></path><line x1=\"1\" y1=\"1\" x2=\"23\" y2=\"23\"></line></svg>");
@@ -128,7 +129,7 @@ var eyeButtonClass = css({
128
129
  });
129
130
  var read = (v) => typeof v === "function" ? v() : v;
130
131
  var Input = (props = {}) => {
131
- const [local, rest] = splitProps(mergeProps$1({
132
+ const [local, rest] = splitProps(mergeProps$2({
132
133
  solid: false,
133
134
  size: "md",
134
135
  disabled: false,
@@ -147,12 +148,17 @@ var Input = (props = {}) => {
147
148
  ]);
148
149
  const showPassword = createSignal(false);
149
150
  const isPassword = read(local.type) === "password";
151
+ const field = useFieldContext();
152
+ const fieldInvalid = () => field?.invalid?.() ?? false;
153
+ const fieldDisabled = () => field?.disabled?.() ?? false;
154
+ const effectiveInvalid = () => read(local.invalid) || fieldInvalid();
155
+ const effectiveDisabled = () => read(local.disabled) || fieldDisabled();
150
156
  const wrapperClassName = () => [
151
157
  wrapperClass,
152
158
  sizeClasses[read(local.size)],
153
159
  read(local.solid) ? variantClasses.solid : variantClasses.outline,
154
- read(local.invalid) && errorClass,
155
- read(local.disabled) && disabledClass,
160
+ effectiveInvalid() && errorClass,
161
+ effectiveDisabled() && disabledClass,
156
162
  read(local.className)
157
163
  ].filter(Boolean).join(" ");
158
164
  const inputType = () => {
@@ -178,11 +184,13 @@ var Input = (props = {}) => {
178
184
  if (p == null) return null;
179
185
  return renderAffix(p);
180
186
  }, _el1);
181
- insert(_el0, () => jsx(ark.input, mergeProps({ get ref() {
187
+ insert(_el0, () => jsx(ark$1.input, mergeProps({ get ref() {
182
188
  return local.ref;
183
- } }, rest, {
189
+ } }, () => field ? field.getInputProps() : {}, rest, {
184
190
  type: inputType,
185
- disabled: () => read(local.disabled),
191
+ "aria-invalid": () => effectiveInvalid() ? "true" : "false",
192
+ "data-invalid": () => effectiveInvalid() ? "" : void 0,
193
+ disabled: () => effectiveDisabled(),
186
194
  className: () => [inputClass, read(local.inputClassName)].filter(Boolean).join(" ")
187
195
  })), _el2);
188
196
  insert(_el0, () => {
@@ -206,6 +214,6 @@ var Input = (props = {}) => {
206
214
  return _el0;
207
215
  };
208
216
  };
209
- Input.origin = { Root: ark.input };
217
+ Input.origin = { Root: ark$1.input };
210
218
  //#endregion
211
219
  export { Input as default };
@@ -0,0 +1,32 @@
1
+ import { jsx, mergeProps } from "@plastic-js/plastic/jsx-runtime";
2
+ import { css } from "@emotion/css";
3
+ import { mergeProps as mergeProps$1, splitProps } from "@plastic-js/plastic";
4
+ //#region src/components/Link.jsx
5
+ var baseClass = css({
6
+ color: "var(--tsu-accent-11)",
7
+ textDecoration: "none",
8
+ cursor: "pointer",
9
+ fontWeight: 500,
10
+ transition: "color var(--tsu-transition-fast), text-decoration-color var(--tsu-transition-fast)",
11
+ "&:hover": {
12
+ textDecoration: "underline",
13
+ textDecorationColor: "var(--tsu-accent-6)"
14
+ },
15
+ "&:focus-visible": {
16
+ outline: "2px solid var(--tsu-focus-border)",
17
+ outlineOffset: "2px",
18
+ borderRadius: "2px"
19
+ }
20
+ });
21
+ var Link = (props = {}) => {
22
+ const [local, rest] = splitProps(mergeProps$1({}, props), ["className", "children"]);
23
+ return jsx("a", mergeProps(rest, {
24
+ get className() {
25
+ return [baseClass, local.className].filter(Boolean).join(" ");
26
+ },
27
+ children: () => local.children
28
+ }));
29
+ };
30
+ Link.origin = { Root: "a" };
31
+ //#endregion
32
+ export { Link as default };
@@ -0,0 +1,30 @@
1
+ import { jsx, mergeProps } from "@plastic-js/plastic/jsx-runtime";
2
+ import { css } from "@emotion/css";
3
+ import { mergeProps as mergeProps$1, splitProps } from "@plastic-js/plastic";
4
+ //#region src/components/SafeArea.jsx
5
+ var read = (v) => typeof v === "function" ? v() : v;
6
+ var baseClass = css({ boxSizing: "border-box" });
7
+ var positionClasses = {
8
+ top: css({ paddingTop: "env(safe-area-inset-top)" }),
9
+ bottom: css({ paddingBottom: "env(safe-area-inset-bottom)" }),
10
+ left: css({ paddingLeft: "env(safe-area-inset-left)" }),
11
+ right: css({ paddingRight: "env(safe-area-inset-right)" })
12
+ };
13
+ var SafeArea = (props = {}) => {
14
+ const [local, rest] = splitProps(mergeProps$1({ position: "top" }, props), [
15
+ "className",
16
+ "children",
17
+ "position"
18
+ ]);
19
+ return jsx("div", mergeProps(rest, {
20
+ className: () => [
21
+ baseClass,
22
+ positionClasses[read(local.position)],
23
+ local.className
24
+ ].filter(Boolean).join(" "),
25
+ children: () => local.children
26
+ }));
27
+ };
28
+ SafeArea.origin = { Root: "div" };
29
+ //#endregion
30
+ export { SafeArea as default };
@@ -352,9 +352,7 @@ var Content = () => {
352
352
  };
353
353
  return jsx("div", mergeProps(() => part("positioner"), {
354
354
  "aria-hidden": () => !ctx.open(),
355
- get className() {
356
- return `${overlayClass} ${ctx.open() ? openClass : ""}`;
357
- },
355
+ className: () => `${overlayClass} ${ctx.open() ? openClass : ""}`,
358
356
  children: [jsx("div", mergeProps(() => part("backdrop"), {
359
357
  get className() {
360
358
  return `${backdropClass} ${ctx.backdropClassName ?? ""}`;
@@ -236,4 +236,4 @@ SelectPc.origin = {
236
236
  HiddenSelect: Select.HiddenSelect
237
237
  };
238
238
  //#endregion
239
- export { SelectPc as default };
239
+ export { SelectItem, SelectPc as default };
@@ -0,0 +1,35 @@
1
+ import { jsx, mergeProps } from "@plastic-js/plastic/jsx-runtime";
2
+ import { css } from "@emotion/css";
3
+ import { mergeProps as mergeProps$1, splitProps } from "@plastic-js/plastic";
4
+ //#region src/components/StickyHeader.jsx
5
+ var read = (v) => typeof v === "function" ? v() : v;
6
+ var baseClass = css({
7
+ position: "sticky",
8
+ zIndex: "var(--tsu-z-dropdown)",
9
+ backgroundColor: "var(--tsu-bg)"
10
+ });
11
+ var shadowClass = css({ boxShadow: "var(--tsu-shadow-sm)" });
12
+ var StickyHeader = (props = {}) => {
13
+ const [local, rest] = splitProps(mergeProps$1({ offsetTop: 0 }, props), [
14
+ "className",
15
+ "children",
16
+ "offsetTop",
17
+ "shadow",
18
+ "style"
19
+ ]);
20
+ return jsx("div", mergeProps(rest, {
21
+ className: () => [
22
+ baseClass,
23
+ read(local.shadow) && shadowClass,
24
+ local.className
25
+ ].filter(Boolean).join(" "),
26
+ style: () => ({
27
+ top: `${read(local.offsetTop)}px`,
28
+ ...typeof local.style === "object" && local.style ? local.style : void 0
29
+ }),
30
+ children: () => local.children
31
+ }));
32
+ };
33
+ StickyHeader.origin = { Root: "div" };
34
+ //#endregion
35
+ export { StickyHeader as default };
@@ -112,5 +112,11 @@ TreeView$1.origin = {
112
112
  Label: TreeView.Label,
113
113
  Tree: TreeView.Tree
114
114
  };
115
+ TreeView$1.treeViewPart = TreeView.Root.treeViewPart;
116
+ TreeViewBranch.treeViewPart = TreeView.Branch.treeViewPart;
117
+ TreeViewBranchTrigger.treeViewPart = TreeView.BranchTrigger.treeViewPart;
118
+ TreeViewBranchContent.treeViewPart = TreeView.BranchContent.treeViewPart;
119
+ TreeViewBranchControl.treeViewPart = TreeView.BranchControl.treeViewPart;
120
+ TreeViewItem.treeViewPart = TreeView.Item.treeViewPart;
115
121
  //#endregion
116
122
  export { TreeViewBranch, TreeViewBranchContent, TreeViewBranchControl, TreeViewBranchTrigger, TreeViewItem, TreeView$1 as default };
package/dist/index.js CHANGED
@@ -1,16 +1,19 @@
1
1
  import AccordionRoot, { AccordionContent, AccordionItem, AccordionTrigger } from "./components/Accordion.js";
2
2
  import Avatar, { AvatarFallback, AvatarImage } from "./components/Avatar.js";
3
+ import BackToTop from "./components/BackToTop.js";
4
+ import BottomNavigation, { BottomNavigationItem } from "./components/BottomNavigation.js";
3
5
  import Portal_default from "./components/Portal.js";
4
6
  import BottomSheet from "./components/BottomSheet.js";
5
7
  import Spinner from "./components/Spinner.js";
6
8
  import Button from "./components/Button.js";
9
+ import Card from "./components/Card.js";
7
10
  import Input$1 from "./components/Input.js";
8
11
  import CardNumberInput from "./components/CardNumberInput.js";
9
12
  import Carousel_default, { CarouselAutoplayTrigger, CarouselControl, CarouselIndicator, CarouselIndicatorGroup, CarouselItem, CarouselItemGroup, CarouselNextTrigger, CarouselPrevTrigger, CarouselProgressText } from "./components/Carousel.js";
10
13
  import Checkbox from "./components/Checkbox.js";
11
14
  import Clipboard from "./components/Clipboard.js";
12
15
  import CloseButton from "./components/CloseButton.js";
13
- import Collapsible from "./components/Collapsible.js";
16
+ import Collapsible, { CollapsibleContent, CollapsibleTrigger } from "./components/Collapsible.js";
14
17
  import ColorPicker from "./components/ColorPicker.js";
15
18
  import Root, { ComboboxContent, ComboboxItem, Input } from "./components/Combobox.js";
16
19
  import Icon from "./components/Icon.js";
@@ -23,6 +26,7 @@ import Fieldset from "./components/Fieldset.js";
23
26
  import FileUpload, { FileUploadClearTrigger, FileUploadItem, FileUploadItemGroup } from "./components/FileUpload.js";
24
27
  import FocusTrap_default from "./components/FocusTrap.js";
25
28
  import Listbox from "./components/Listbox.js";
29
+ import Link from "./components/Link.js";
26
30
  import Menu, { MenuContent, MenuItem, MenuSeparator, MenuTrigger } from "./components/Menu.js";
27
31
  import MoneyInput from "./components/MoneyInput.js";
28
32
  import NumberInput from "./components/NumberInput.js";
@@ -32,14 +36,16 @@ import Presence_default from "./components/Presence.js";
32
36
  import Progress, { ProgressLabel } from "./components/Progress.js";
33
37
  import RadioGroup from "./components/RadioGroup.js";
34
38
  import RatingGroup from "./components/RatingGroup.js";
39
+ import SafeArea from "./components/SafeArea.js";
35
40
  import SearchInput from "./components/SearchInput.js";
36
41
  import Select, { SelectTrigger } from "./components/Select.js";
37
- import SelectPc from "./components/SelectPc.js";
42
+ import SelectPc, { SelectItem } from "./components/SelectPc.js";
38
43
  import SignaturePad, { ClearTrigger } from "./components/SignaturePad.js";
39
44
  import Skeleton from "./components/Skeleton.js";
40
45
  import Slider from "./components/Slider.js";
41
46
  import Splitter, { SplitterPanel, SplitterResizeTrigger, SplitterResizeTriggerIndicator } from "./components/Splitter.js";
42
47
  import Steps from "./components/Steps.js";
48
+ import StickyHeader from "./components/StickyHeader.js";
43
49
  import SwipeReveal from "./components/SwipeReveal.js";
44
50
  import Switch from "./components/Switch.js";
45
51
  import Tabs from "./components/Tabs.js";
@@ -47,6 +53,7 @@ import TagsInput from "./components/TagsInput.js";
47
53
  import Toast, { ToastCloseTrigger, ToastDescription, ToastIcon, ToastTitle, ToastToaster, createToaster } from "./components/Toast.js";
48
54
  import Toggle from "./components/Toggle.js";
49
55
  import ToggleGroup, { ToggleGroupItem } from "./components/ToggleGroup.js";
56
+ import FilterGroup from "./components/FilterGroup.js";
50
57
  import Tour, { TourCloseTrigger, TourContent } from "./components/Tour.js";
51
58
  import TreeView, { TreeViewBranch, TreeViewBranchContent, TreeViewBranchControl, TreeViewBranchTrigger, TreeViewItem } from "./components/TreeView.js";
52
- export { AccordionRoot as Accordion, AccordionContent, AccordionItem, AccordionTrigger, Avatar, AvatarFallback, AvatarImage, BottomSheet, Button, CardNumberInput, Carousel_default as Carousel, CarouselAutoplayTrigger, CarouselControl, CarouselIndicator, CarouselIndicatorGroup, CarouselItem, CarouselItemGroup, CarouselNextTrigger, CarouselPrevTrigger, CarouselProgressText, Checkbox, ClearTrigger, Clipboard, CloseButton, Collapsible, ColorPicker, Root as Combobox, ComboboxContent, Input as ComboboxInput, ComboboxItem, ConfirmDialog, DateInput, Dialog, Root$1 as Drawer, DrawerContent, DrawerTrigger, Field, Fieldset, FileUpload, FileUploadClearTrigger, FileUploadItem, FileUploadItemGroup, FocusTrap_default as FocusTrap, Icon, Input$1 as Input, Listbox, Menu, MenuContent, MenuItem, MenuSeparator, MenuTrigger, MoneyInput, NumberInput, Pagination, Popover, Portal_default as Portal, Presence_default as Presence, Progress, ProgressLabel, RadioGroup, RatingGroup, SearchInput, Select, SelectPc, SelectTrigger, SignaturePad, Skeleton, Slider, Spinner, Splitter, SplitterPanel, SplitterResizeTrigger, SplitterResizeTriggerIndicator, Steps, SwipeReveal, Switch, Tabs, TagsInput, Toast, ToastCloseTrigger, ToastDescription, ToastIcon, ToastTitle, ToastToaster, Toggle, ToggleGroup, ToggleGroupItem, Tour, TourCloseTrigger, TourContent, TreeView, TreeViewBranch, TreeViewBranchContent, TreeViewBranchControl, TreeViewBranchTrigger, TreeViewItem, confirm, createToaster };
59
+ export { AccordionRoot as Accordion, AccordionContent, AccordionItem, AccordionTrigger, Avatar, AvatarFallback, AvatarImage, BackToTop, BottomNavigation, BottomNavigationItem, BottomSheet, Button, Card, CardNumberInput, Carousel_default as Carousel, CarouselAutoplayTrigger, CarouselControl, CarouselIndicator, CarouselIndicatorGroup, CarouselItem, CarouselItemGroup, CarouselNextTrigger, CarouselPrevTrigger, CarouselProgressText, Checkbox, ClearTrigger, Clipboard, CloseButton, Collapsible, CollapsibleContent, CollapsibleTrigger, ColorPicker, Root as Combobox, ComboboxContent, Input as ComboboxInput, ComboboxItem, ConfirmDialog, DateInput, Dialog, Root$1 as Drawer, DrawerContent, DrawerTrigger, Field, Fieldset, FileUpload, FileUploadClearTrigger, FileUploadItem, FileUploadItemGroup, FilterGroup, FocusTrap_default as FocusTrap, Icon, Input$1 as Input, Link, Listbox, Menu, MenuContent, MenuItem, MenuSeparator, MenuTrigger, MoneyInput, NumberInput, Pagination, Popover, Portal_default as Portal, Presence_default as Presence, Progress, ProgressLabel, RadioGroup, RatingGroup, SafeArea, SearchInput, Select, SelectItem, SelectPc, SelectTrigger, SignaturePad, Skeleton, Slider, Spinner, Splitter, SplitterPanel, SplitterResizeTrigger, SplitterResizeTriggerIndicator, Steps, StickyHeader, SwipeReveal, Switch, Tabs, TagsInput, Toast, ToastCloseTrigger, ToastDescription, ToastIcon, ToastTitle, ToastToaster, Toggle, ToggleGroup, ToggleGroupItem, Tour, TourCloseTrigger, TourContent, TreeView, TreeViewBranch, TreeViewBranchContent, TreeViewBranchControl, TreeViewBranchTrigger, TreeViewItem, confirm, createToaster };
package/docs/api.md CHANGED
@@ -52,31 +52,38 @@ The shared `size` prop accepts: `xs | sm | md | lg | xl | xxl` (default `md`).
52
52
 
53
53
  ### Field
54
54
 
55
- A labeled field wrapper combining a label, an error message and helper text.
55
+ A labeled field wrapper combining a label, an error message and helper text, backed by `ArkField`.
56
56
 
57
- **Exports:** `Field`
57
+ **Exports:** `Field` · Escape hatch: `Field.origin.{Root, Label, Input, Textarea, Select, HelperText, ErrorText, RequiredIndicator, Item}`
58
58
 
59
59
  | Prop | Type | Default | Description |
60
- |---|---|---|---|
61
- | `label` | `string` | `''` | Field label text. |
62
- | `error` | `string` | `''` | Error message; when non-empty the field renders in its error state. |
63
- | `helper` | `string` | `''` | Helper text below the control. |
60
+ |---|---|---|---|---|
61
+ | `label` | `string \| node` | `''` | Label content — any text or JSX. Styling is fixed. |
62
+ | `error` | `string \| node` | `''` | Error content; when non-empty the field renders in its error state. Styling is fixed. |
63
+ | `helper` | `string \| node` | `''` | Helper content below the control. Styling is fixed. |
64
64
  | `required` | `boolean` | `false` | Marks the label with a required indicator. |
65
65
  | `compact` | `boolean` | `false` | Renders in a compact (smaller) layout. |
66
+ | `invalid` | `boolean` | `false` | Error state (also implied by a non-empty `error`). |
67
+ | `disabled` | `boolean` | — | Disables the control; inherits from an enclosing `Fieldset`. |
68
+ | `readOnly` | `boolean` | — | Read-only state. |
66
69
  | `children` | `node` | — | The input control (e.g. `<Input />`). |
67
70
 
71
+ Passthrough: Ark `Field.Root` props (`id`, `ids`, …).
72
+
68
73
  ### Fieldset
69
74
 
70
- Groups several `Field`s with a legend.
75
+ Groups several `Field`s with a legend, backed by `ArkFieldset` (native `<fieldset>`).
71
76
 
72
- **Exports:** `Fieldset`
77
+ **Exports:** `Fieldset` · Escape hatch: `Fieldset.origin.{Root, Legend, HelperText, ErrorText}`
73
78
 
74
79
  | Prop | Type | Default | Description |
75
- |---|---|---|---|
76
- | `legend` | `string` | `''` | Fieldset legend text. |
77
- | `helper` | `string` | `''` | Helper text below the fields. |
80
+ |---|---|---|---|---|
81
+ | `legend` | `string \| node` | `''` | Legend content — any text or JSX. Styling is fixed. |
82
+ | `helper` | `string \| node` | `''` | Helper content below the fields. Styling is fixed. |
78
83
  | `children` | `node` | — | `Field` components to group. |
79
84
 
85
+ Passthrough: Ark `Fieldset.Root` props (`disabled`, `invalid`, …).
86
+
80
87
  ### Input
81
88
 
82
89
  **Exports:** `Input` · Escape hatch: `Input.origin.Root`
@@ -260,6 +267,23 @@ Passthrough: Ark `Root` props (`pressed`, `onPressedChange`, …).
260
267
  | `size` | `string` | `md` | See size values. |
261
268
  | `children` | `node` | — | `ToggleGroupItem` elements. |
262
269
 
270
+ ### FilterGroup
271
+
272
+ Multi-select filter bar built on `ToggleGroup`. Renders an "All" toggle; selecting any item clears it, and an empty selection maps back to "All".
273
+
274
+ **Exports:** `FilterGroup`
275
+
276
+ | Prop | Type | Default | Description |
277
+ |---|---|---|---|
278
+ | `value` | `array \| (() => array)` | — | Selected item values (controlled). |
279
+ | `onValueChange` | `(value) => void` | — | Called with the new selection array. |
280
+ | `items` | `array` | `[]` | Filter items (`{ value, label, disabled? }`). |
281
+ | `allLabel` | `string` | `'All'` | Label for the clear/All toggle. |
282
+ | `size` | `string` | `md` | See size values. |
283
+ | `buttonClass` | `string` | — | Class applied to every button. |
284
+ | `groupClass` | `string` | — | Class applied to the inner `ToggleGroup`. |
285
+ | `className` | `string` | — | Root class. |
286
+
263
287
  ### RadioGroup
264
288
 
265
289
  **Exports:** `RadioGroup` · Escape hatch: `RadioGroup.origin.{Root, Label, Item, ItemControl, ItemText, ItemHiddenInput, Indicator}`
@@ -316,7 +340,7 @@ Mobile-optimized bottom-sheet select (draggable sheet overlay).
316
340
 
317
341
  Desktop (popover-style) select built on Ark `Select`.
318
342
 
319
- **Exports:** `SelectPc` · Escape hatch: `SelectPc.origin.{Root, Trigger, Indicator, Positioner, Content, List, Item, ItemText, ValueText, HiddenSelect}`
343
+ **Exports:** `SelectPc`, `SelectItem` · Escape hatch: `SelectPc.origin.{Root, Trigger, Indicator, Positioner, Content, List, Item, ItemText, ValueText, HiddenSelect}`
320
344
 
321
345
  | Prop | Type | Default | Description |
322
346
  |---|---|---|---|
@@ -325,7 +349,7 @@ Desktop (popover-style) select built on Ark `Select`.
325
349
  | `disabled` | `boolean` | `false` | Disabled. |
326
350
  | `invalid` | `boolean` | `false` | Error state. |
327
351
  | `positioning` | `object` | `{ sameWidth: true }` | Positioning options. |
328
- | `children` | `node` | — | `SelectPc.origin.Item` elements or custom content. |
352
+ | `children` | `node` | — | `SelectItem` elements or custom content. |
329
353
 
330
354
  Passthrough: Ark `Root` props (`value`, `onValueChange`, `collection`, …). Note: unlike `Select`, it is **not** controlled-only at the tsumiki level — state flows through the Ark props.
331
355
 
@@ -404,18 +428,105 @@ Passthrough: Ark `Root` props (`value`, `onValueChange`, `orientation`, …).
404
428
 
405
429
  Passthrough: Ark `Root` props (`count`, `pageSize`, `page`, `onPageChange`, …).
406
430
 
431
+ ### BottomNavigation
432
+
433
+ Fixed bottom navigation bar with icons and labels.
434
+
435
+ **Exports:** `BottomNavigation`, `BottomNavigationItem`
436
+
437
+ | Prop | Type | Default | Description |
438
+ |---|---|---|---|
439
+ | `className` | `string` | — | Root class. |
440
+ | `children` | `node` | — | `BottomNavigationItem` elements. |
441
+
442
+ `BottomNavigationItem`:
443
+
444
+ | Prop | Type | Default | Description |
445
+ |---|---|---|---|
446
+ | `icon` | `node` | — | Icon content. |
447
+ | `label` | `string` | — | Item label. |
448
+ | `active` | `boolean` | `false` | Active state. |
449
+ | `href` | `string` | — | When set, renders an `<a>` instead of a `<button>`. |
450
+ | `onClick` | `() => void` | — | Click handler. |
451
+ | `className` | `string` | — | Item class. |
452
+
453
+ ### BackToTop
454
+
455
+ Floating scroll-to-top button that appears after scrolling past a threshold.
456
+
457
+ **Exports:** `BackToTop`
458
+
459
+ | Prop | Type | Default | Description |
460
+ |---|---|---|---|
461
+ | `threshold` | `number` | `200` | Scroll offset (px) at which the button appears. |
462
+ | `target` | `() => Element` | — | Scroll container. When omitted, uses the window. |
463
+ | `icon` | `node` | — | Custom button icon. |
464
+ | `onClick` | `() => void` | — | Click callback. |
465
+ | `className` | `string` | — | Button class. |
466
+
407
467
  ---
408
468
 
409
469
  ## Content & Layout
410
470
 
411
471
  ### Collapsible
412
472
 
413
- **Exports:** `Collapsible` · Escape hatch: `Collapsible.origin.{Root, Trigger, Content}`
473
+ **Exports:** `Collapsible`, `CollapsibleTrigger`, `CollapsibleContent` · Escape hatch: `Collapsible.origin.{Root, Trigger, Content}`
414
474
 
415
475
  | Prop | Type | Default | Description |
416
476
  |---|---|---|---|
417
477
  | `className` | `string` | — | Root class. |
418
- | `children` | `node` | — | `Trigger` / `Content` elements. |
478
+ | `children` | `node` | — | `CollapsibleTrigger` / `CollapsibleContent` elements. |
479
+
480
+ ### Card
481
+
482
+ Container with themed background and border radius.
483
+
484
+ **Exports:** `Card`
485
+
486
+ | Prop | Type | Default | Description |
487
+ |---|---|---|---|
488
+ | `className` | `string` | — | Root class. |
489
+ | `children` | `node` | — | Content. |
490
+
491
+ Passthrough: `div` DOM props (`style`, `onClick`, …).
492
+
493
+ ### Link
494
+
495
+ Styled anchor.
496
+
497
+ **Exports:** `Link`
498
+
499
+ | Prop | Type | Default | Description |
500
+ |---|---|---|---|
501
+ | `className` | `string` | — | Root class. |
502
+ | `children` | `node` | — | Content. |
503
+
504
+ Passthrough: native `<a>` props (`href`, `target`, `rel`, …).
505
+
506
+ ### SafeArea
507
+
508
+ Adds device safe-area inset padding.
509
+
510
+ **Exports:** `SafeArea`
511
+
512
+ | Prop | Type | Default | Description |
513
+ |---|---|---|---|
514
+ | `position` | `string` | `'top'` | `'top' \| 'bottom' \| 'left' \| 'right'`. |
515
+ | `className` | `string` | — | Root class. |
516
+ | `children` | `node` | — | Content. |
517
+
518
+ ### StickyHeader
519
+
520
+ Header that sticks to the top while scrolling.
521
+
522
+ **Exports:** `StickyHeader`
523
+
524
+ | Prop | Type | Default | Description |
525
+ |---|---|---|---|
526
+ | `offsetTop` | `number` | `0` | Sticky top offset (px). |
527
+ | `shadow` | `boolean` | `false` | Add a drop shadow when stuck. |
528
+ | `className` | `string` | — | Root class. |
529
+ | `children` | `node` | — | Content. |
419
530
 
420
531
  ### Splitter
421
532
 
@@ -28,7 +28,7 @@ Whether the component is built on top of a same-named Ark primitive, or implemen
28
28
 
29
29
  ### Component Matrix
30
30
 
31
- #### Completed · Ark-Backed (30)
31
+ #### Completed · Ark-Backed (32)
32
32
 
33
33
  | Component | Ark Primitive |
34
34
  |-----------|---------------|
@@ -39,6 +39,8 @@ Whether the component is built on top of a same-named Ark primitive, or implemen
39
39
  | ColorPicker | `ArkColorPicker` |
40
40
  | Dialog | `ArkDialog` |
41
41
  | Drawer | `ArkDrawer` |
42
+ | Field | `ArkField` |
43
+ | Fieldset | `ArkFieldset` |
42
44
  | FileUpload | `ArkFileUpload` |
43
45
  | FocusTrap | `ArkFocusTrap` |
44
46
  | Input | `ark("input")` via factory |
@@ -63,7 +65,7 @@ Whether the component is built on top of a same-named Ark primitive, or implemen
63
65
  | Toggle | `ArkToggle` |
64
66
  | ToggleGroup | `ArkToggleGroup` |
65
67
 
66
- #### Completed · Custom-Built (15)
68
+ #### Completed · Custom-Built (13)
67
69
 
68
70
  | Component | Built With |
69
71
  |-----------|------------|
@@ -73,8 +75,6 @@ Whether the component is built on top of a same-named Ark primitive, or implemen
73
75
  | CloseButton | `@emotion/css` |
74
76
  | ConfirmDialog | Composes `Dialog` |
75
77
  | DateInput | `DateWheel` utility |
76
- | Field | `@emotion/css` |
77
- | Fieldset | `@emotion/css` |
78
78
  | Icon | `@emotion/css` |
79
79
  | MoneyInput | Composes `Input` |
80
80
  | SearchInput | Composes `Input` |
@@ -103,11 +103,11 @@ Whether the component is built on top of a same-named Ark primitive, or implemen
103
103
  ```
104
104
  Ark-Backed Custom-Built
105
105
  ┌─────────────────┬─────────────────┐
106
- Completed │ 3015 │ 45
106
+ Completed │ 3213 │ 45
107
107
  ├─────────────────┼─────────────────┤
108
108
  In Development │ 6 │ 0 │ 6
109
109
  └─────────────────┴─────────────────┘
110
- 36 15 51
110
+ 38 13 51
111
111
  ```
112
112
 
113
113
  ---
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plastic-js/tsumiki",
3
- "version": "0.1.27",
3
+ "version": "0.1.29",
4
4
  "description": "A UI component library for Plastic JS.",
5
5
  "license": "MIT",
6
6
  "author": "tigre",