kaze-design-system 0.9.0 → 0.10.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 (66) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/components.css +2 -1
  3. package/dist/components/Alert/Alert.js +21 -7
  4. package/dist/components/Badge/Badge.js +5 -2
  5. package/dist/components/Button/Button.js +12 -3
  6. package/dist/components/CommandPalette/CommandPalette.js +1 -0
  7. package/dist/components/Dialog/Dialog.js +35 -15
  8. package/dist/components/Disclosure/Disclosure.js +2 -0
  9. package/dist/components/Dropdown/Dropdown.js +2 -0
  10. package/dist/components/FAB/FAB.js +17 -5
  11. package/dist/components/FilterPill/FilterPill.js +1 -0
  12. package/dist/components/HelpButton/HelpButton.js +2 -0
  13. package/dist/components/List/List.js +22 -18
  14. package/dist/components/Meter/Meter.js +5 -6
  15. package/dist/components/Navbar/Navbar.js +4 -1
  16. package/dist/components/NumberField/NumberField.js +11 -2
  17. package/dist/components/Pagination/Pagination.js +22 -5
  18. package/dist/components/Progress/Progress.js +5 -3
  19. package/dist/components/Search/Search.js +13 -3
  20. package/dist/components/SegmentedControl/SegmentedControl.js +1 -0
  21. package/dist/components/Sidebar/Sidebar.js +7 -6
  22. package/dist/components/StatusBadge/StatusBadge.js +6 -6
  23. package/dist/components/Stepper/Stepper.js +40 -36
  24. package/dist/components/Tabs/Tabs.js +46 -11
  25. package/dist/components/Timeline/Timeline.js +15 -14
  26. package/dist/components/Toast/Toast.js +12 -8
  27. package/dist/components/Tooltip/Tooltip.js +25 -6
  28. package/dist/components/Tracker/Tracker.js +18 -10
  29. package/dist/index.js +2 -0
  30. package/dist/lib/Slot.js +52 -0
  31. package/dist/lib/deprecate.js +26 -0
  32. package/dist/types/components/Alert/Alert.d.ts +6 -2
  33. package/dist/types/components/Avatar/Avatar.d.ts +2 -1
  34. package/dist/types/components/Badge/Badge.d.ts +6 -2
  35. package/dist/types/components/Button/Button.d.ts +4 -1
  36. package/dist/types/components/Dialog/Dialog.d.ts +4 -1
  37. package/dist/types/components/Disclosure/Disclosure.d.ts +2 -1
  38. package/dist/types/components/EmptyState/EmptyState.d.ts +2 -1
  39. package/dist/types/components/FAB/FAB.d.ts +4 -1
  40. package/dist/types/components/HelpButton/HelpButton.d.ts +2 -1
  41. package/dist/types/components/Icon/Icon.d.ts +2 -1
  42. package/dist/types/components/List/List.d.ts +2 -0
  43. package/dist/types/components/Logo/Logo.d.ts +3 -1
  44. package/dist/types/components/Logo/index.d.ts +1 -1
  45. package/dist/types/components/Meter/Meter.d.ts +9 -4
  46. package/dist/types/components/Meter/index.d.ts +1 -1
  47. package/dist/types/components/Navbar/Navbar.d.ts +2 -0
  48. package/dist/types/components/NumberField/NumberField.d.ts +3 -1
  49. package/dist/types/components/Pagination/Pagination.d.ts +6 -1
  50. package/dist/types/components/Progress/Progress.d.ts +7 -3
  51. package/dist/types/components/Search/Search.d.ts +5 -1
  52. package/dist/types/components/Section/Section.d.ts +2 -1
  53. package/dist/types/components/SegmentedControl/SegmentedControl.d.ts +2 -1
  54. package/dist/types/components/Sidebar/Sidebar.d.ts +2 -0
  55. package/dist/types/components/StatusBadge/StatusBadge.d.ts +1 -1
  56. package/dist/types/components/Stepper/Stepper.d.ts +4 -1
  57. package/dist/types/components/Tabs/Tabs.d.ts +18 -7
  58. package/dist/types/components/Timeline/Timeline.d.ts +6 -2
  59. package/dist/types/components/Toast/Toast.d.ts +9 -3
  60. package/dist/types/components/Tooltip/Tooltip.d.ts +5 -0
  61. package/dist/types/components/Tracker/Tracker.d.ts +7 -3
  62. package/dist/types/components/index.d.ts +5 -2
  63. package/dist/types/lib/Slot.d.ts +16 -0
  64. package/dist/types/lib/deprecate.d.ts +8 -0
  65. package/dist/types/lib/types.d.ts +4 -0
  66. package/package.json +2 -2
@@ -1,6 +1,7 @@
1
- import { jsxs, jsx } from "react/jsx-runtime";
1
+ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
2
  import { forwardRef } from "react";
3
3
  import { cn } from "../../lib/utils.js";
4
+ import { Slot } from "../../lib/Slot.js";
4
5
  const Sidebar = forwardRef(
5
6
  ({ children, logo, footer, navLabel = "メインナビゲーション", className, ...rest }, ref) => {
6
7
  return /* @__PURE__ */ jsxs("aside", { ref, className: cn("sidebar", className), ...rest, children: [
@@ -12,10 +13,10 @@ const Sidebar = forwardRef(
12
13
  );
13
14
  Sidebar.displayName = "Sidebar";
14
15
  const NavItem = forwardRef(
15
- ({ icon, active, as: asProp, href, className, children, ...rest }, ref) => {
16
- const Component = asProp ?? (href ? "a" : "button");
16
+ ({ icon, active, as: asProp, href, asChild, className, children, ...rest }, ref) => {
17
+ const Component = asChild ? Slot : asProp ?? (href ? "a" : "button");
17
18
  const typeAttr = Component === "button" ? { type: "button" } : {};
18
- return /* @__PURE__ */ jsxs(
19
+ return /* @__PURE__ */ jsx(
19
20
  Component,
20
21
  {
21
22
  ref,
@@ -24,10 +25,10 @@ const NavItem = forwardRef(
24
25
  href,
25
26
  ...typeAttr,
26
27
  ...rest,
27
- children: [
28
+ children: asChild ? children : /* @__PURE__ */ jsxs(Fragment, { children: [
28
29
  icon && /* @__PURE__ */ jsx("span", { className: "nav-item__icon", children: icon }),
29
30
  children
30
- ]
31
+ ] })
31
32
  }
32
33
  );
33
34
  }
@@ -2,17 +2,17 @@ import { jsx } from "react/jsx-runtime";
2
2
  import { forwardRef } from "react";
3
3
  import { Badge } from "../Badge/Badge.js";
4
4
  const STATUS_CONFIG = {
5
- live: { variant: "positive", label: "自動更新" },
6
- stale: { variant: "warning", label: "要更新" },
7
- missing: { variant: "negative", label: "欠損" },
8
- manual: { variant: "default", label: "手動" },
9
- loading: { variant: "info", label: "取得中" }
5
+ live: { tone: "positive", label: "自動更新" },
6
+ stale: { tone: "warning", label: "要更新" },
7
+ missing: { tone: "negative", label: "欠損" },
8
+ manual: { tone: "default", label: "手動" },
9
+ loading: { tone: "info", label: "取得中" }
10
10
  };
11
11
  const StatusBadge = forwardRef(
12
12
  ({ status, label, dot = true, live, ...rest }, ref) => {
13
13
  const config = STATUS_CONFIG[status];
14
14
  const isLive = live ?? (status === "live" || status === "loading");
15
- return /* @__PURE__ */ jsx(Badge, { ref, variant: config.variant, dot, live: isLive, ...rest, children: label ?? config.label });
15
+ return /* @__PURE__ */ jsx(Badge, { ref, tone: config.tone, dot, live: isLive, ...rest, children: label ?? config.label });
16
16
  }
17
17
  );
18
18
  StatusBadge.displayName = "StatusBadge";
@@ -1,6 +1,7 @@
1
1
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
2
  import { forwardRef } from "react";
3
3
  import { cn } from "../../lib/utils.js";
4
+ import { renamedProp } from "../../lib/deprecate.js";
4
5
  const CheckIcon = () => /* @__PURE__ */ jsx(
5
6
  "svg",
6
7
  {
@@ -17,46 +18,49 @@ const CheckIcon = () => /* @__PURE__ */ jsx(
17
18
  }
18
19
  );
19
20
  const Stepper = forwardRef(
20
- ({ steps, currentStep, className, ...rest }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("stepper", className), ...rest, children: /* @__PURE__ */ jsx("ol", { className: "stepper__list", children: steps.map((step, i) => {
21
- const isCompleted = i < currentStep;
22
- const isActive = i === currentStep;
23
- return /* @__PURE__ */ jsxs(
24
- "li",
25
- {
26
- className: "stepper__item",
27
- "aria-current": isActive ? "step" : void 0,
28
- children: [
29
- /* @__PURE__ */ jsxs("div", { className: cn("stepper__step", isActive && "stepper__step--active"), children: [
30
- /* @__PURE__ */ jsx(
21
+ ({ steps, step: stepProp, currentStep: currentStepProp, className, ...rest }, ref) => {
22
+ const currentStep = renamedProp("Stepper", "currentStep", "step", currentStepProp, stepProp) ?? 0;
23
+ return /* @__PURE__ */ jsx("div", { ref, className: cn("stepper", className), ...rest, children: /* @__PURE__ */ jsx("ol", { className: "stepper__list", children: steps.map((step, i) => {
24
+ const isCompleted = i < currentStep;
25
+ const isActive = i === currentStep;
26
+ return /* @__PURE__ */ jsxs(
27
+ "li",
28
+ {
29
+ className: "stepper__item",
30
+ "aria-current": isActive ? "step" : void 0,
31
+ children: [
32
+ /* @__PURE__ */ jsxs("div", { className: cn("stepper__step", isActive && "stepper__step--active"), children: [
33
+ /* @__PURE__ */ jsx(
34
+ "span",
35
+ {
36
+ className: cn(
37
+ "stepper__circle",
38
+ isActive && "stepper__circle--active",
39
+ isCompleted && "stepper__circle--completed"
40
+ ),
41
+ children: isCompleted ? /* @__PURE__ */ jsxs(Fragment, { children: [
42
+ /* @__PURE__ */ jsx(CheckIcon, {}),
43
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: "完了" })
44
+ ] }) : i + 1
45
+ }
46
+ ),
47
+ /* @__PURE__ */ jsx("span", { className: "stepper__label", children: step.label })
48
+ ] }),
49
+ i < steps.length - 1 && /* @__PURE__ */ jsx(
31
50
  "span",
32
51
  {
33
52
  className: cn(
34
- "stepper__circle",
35
- isActive && "stepper__circle--active",
36
- isCompleted && "stepper__circle--completed"
37
- ),
38
- children: isCompleted ? /* @__PURE__ */ jsxs(Fragment, { children: [
39
- /* @__PURE__ */ jsx(CheckIcon, {}),
40
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: "完了" })
41
- ] }) : i + 1
53
+ "stepper__connector",
54
+ isCompleted && "stepper__connector--completed"
55
+ )
42
56
  }
43
- ),
44
- /* @__PURE__ */ jsx("span", { className: "stepper__label", children: step.label })
45
- ] }),
46
- i < steps.length - 1 && /* @__PURE__ */ jsx(
47
- "span",
48
- {
49
- className: cn(
50
- "stepper__connector",
51
- isCompleted && "stepper__connector--completed"
52
- )
53
- }
54
- )
55
- ]
56
- },
57
- i
58
- );
59
- }) }) })
57
+ )
58
+ ]
59
+ },
60
+ i
61
+ );
62
+ }) }) });
63
+ }
60
64
  );
61
65
  Stepper.displayName = "Stepper";
62
66
  export {
@@ -1,13 +1,26 @@
1
1
  "use client";
2
2
  import { jsx } from "react/jsx-runtime";
3
- import { forwardRef, useContext, useId, useRef, useEffect, useCallback, createContext } from "react";
3
+ import { forwardRef, useContext, useId, useState, useCallback, useMemo, useRef, useEffect, createContext } from "react";
4
4
  import { cn } from "../../lib/utils.js";
5
5
  import { composeEventHandlers } from "../../lib/composeEventHandlers.js";
6
- const TabsIdContext = createContext(void 0);
6
+ import { warnDeprecated } from "../../lib/deprecate.js";
7
+ import { Slot } from "../../lib/Slot.js";
8
+ const TabsContext = createContext(void 0);
7
9
  const TabGroup = forwardRef(
8
- ({ children, className, ...rest }, ref) => {
10
+ ({ value: valueProp, defaultValue, onValueChange, children, className, ...rest }, ref) => {
9
11
  const prefix = useId();
10
- return /* @__PURE__ */ jsx(TabsIdContext.Provider, { value: prefix, children: /* @__PURE__ */ jsx("div", { ref, className, ...rest, children }) });
12
+ const [internalValue, setInternalValue] = useState(defaultValue);
13
+ const isControlled = valueProp !== void 0;
14
+ const value = isControlled ? valueProp : internalValue;
15
+ const select = useCallback(
16
+ (next) => {
17
+ if (!isControlled) setInternalValue(next);
18
+ onValueChange?.(next);
19
+ },
20
+ [isControlled, onValueChange]
21
+ );
22
+ const context = useMemo(() => ({ prefix, value, select }), [prefix, value, select]);
23
+ return /* @__PURE__ */ jsx(TabsContext.Provider, { value: context, children: /* @__PURE__ */ jsx("div", { ref, className, ...rest, children }) });
11
24
  }
12
25
  );
13
26
  TabGroup.displayName = "TabGroup";
@@ -80,25 +93,39 @@ const Tabs = forwardRef(
80
93
  );
81
94
  Tabs.displayName = "Tabs";
82
95
  const Tab = forwardRef(
83
- ({ active, id, value, className, children, ...rest }, ref) => {
84
- const contextPrefix = useContext(TabsIdContext);
96
+ ({ active: activeProp, id, value, asChild, className, children, onClick, ...rest }, ref) => {
97
+ const context = useContext(TabsContext);
85
98
  const fallbackPrefix = useId();
86
- const prefix = contextPrefix ?? fallbackPrefix;
99
+ const prefix = context?.prefix ?? fallbackPrefix;
100
+ if (activeProp !== void 0) {
101
+ warnDeprecated(
102
+ "Tab.active",
103
+ "Tab の `active` は `TabGroup` の `value` / `defaultValue` に置き換わりました。次のマイナーバージョンで削除します"
104
+ );
105
+ }
106
+ const active = context?.value !== void 0 ? value !== void 0 && value === context.value : activeProp;
87
107
  const tabId = id ?? (value ? `${prefix}-tab-${value}` : void 0);
88
108
  const panelId = value ? `${prefix}-panel-${value}` : void 0;
89
109
  const text = typeof children === "string" ? children : void 0;
110
+ const handleClick = () => {
111
+ if (value !== void 0) context?.select(value);
112
+ };
113
+ const Comp = asChild ? Slot : "button";
90
114
  return /* @__PURE__ */ jsx(
91
- "button",
115
+ Comp,
92
116
  {
93
117
  ref,
94
118
  id: tabId,
95
119
  role: "tab",
120
+ type: asChild ? void 0 : "button",
96
121
  "aria-selected": active,
97
122
  "aria-controls": panelId,
98
123
  tabIndex: active ? 0 : -1,
99
124
  className: cn("tab", active && "tab--active", className),
125
+ "data-state": active ? "active" : "inactive",
100
126
  "data-text": text,
101
127
  ...rest,
128
+ onClick: composeEventHandlers(onClick, handleClick),
102
129
  children
103
130
  }
104
131
  );
@@ -106,12 +133,19 @@ const Tab = forwardRef(
106
133
  );
107
134
  Tab.displayName = "Tab";
108
135
  const TabPanel = forwardRef(
109
- ({ value, selected, children, className, ...rest }, ref) => {
110
- const contextPrefix = useContext(TabsIdContext);
136
+ ({ value, selected: selectedProp, children, className, ...rest }, ref) => {
137
+ const context = useContext(TabsContext);
111
138
  const fallbackPrefix = useId();
112
- const prefix = contextPrefix ?? fallbackPrefix;
139
+ const prefix = context?.prefix ?? fallbackPrefix;
113
140
  const panelId = `${prefix}-panel-${value}`;
114
141
  const tabId = `${prefix}-tab-${value}`;
142
+ if (selectedProp !== void 0) {
143
+ warnDeprecated(
144
+ "TabPanel.selected",
145
+ "TabPanel の `selected` は `TabGroup` の `value` / `defaultValue` に置き換わりました。次のマイナーバージョンで削除します"
146
+ );
147
+ }
148
+ const selected = context?.value !== void 0 ? context.value : selectedProp;
115
149
  if (value !== selected) return null;
116
150
  return /* @__PURE__ */ jsx(
117
151
  "div",
@@ -122,6 +156,7 @@ const TabPanel = forwardRef(
122
156
  tabIndex: 0,
123
157
  "aria-labelledby": tabId,
124
158
  className: cn("tab-panel", className),
159
+ "data-state": "active",
125
160
  ...rest,
126
161
  children
127
162
  }
@@ -1,25 +1,26 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { forwardRef } from "react";
3
+ import { renamedProp } from "../../lib/deprecate.js";
3
4
  import { cn } from "../../lib/utils.js";
4
5
  const Timeline = forwardRef(
5
6
  ({ className, children, ...rest }, ref) => /* @__PURE__ */ jsx("ul", { ref, className: cn("timeline", className), ...rest, children })
6
7
  );
7
8
  Timeline.displayName = "Timeline";
8
9
  const TimelineItem = forwardRef(
9
- ({ time, title, description, status = "default", className, ...rest }, ref) => /* @__PURE__ */ jsxs("li", { ref, className: cn("timeline-item", className), ...rest, children: [
10
- /* @__PURE__ */ jsx(
11
- "span",
12
- {
13
- className: cn(
14
- "timeline-item__dot",
15
- status !== "default" && `timeline-item__dot--${status}`
16
- )
17
- }
18
- ),
19
- time && /* @__PURE__ */ jsx("span", { className: "timeline-item__time", children: time }),
20
- /* @__PURE__ */ jsx("span", { className: "timeline-item__title", children: title }),
21
- description && /* @__PURE__ */ jsx("span", { className: "timeline-item__description", children: description })
22
- ] })
10
+ ({ time, title, description, tone: toneProp, status, className, ...rest }, ref) => {
11
+ const tone = renamedProp("TimelineItem", "status", "tone", status, toneProp) ?? "default";
12
+ return /* @__PURE__ */ jsxs("li", { ref, "data-tone": tone, className: cn("timeline-item", className), ...rest, children: [
13
+ /* @__PURE__ */ jsx(
14
+ "span",
15
+ {
16
+ className: cn("timeline-item__dot", tone !== "default" && `timeline-item__dot--${tone}`)
17
+ }
18
+ ),
19
+ time && /* @__PURE__ */ jsx("span", { className: "timeline-item__time", children: time }),
20
+ /* @__PURE__ */ jsx("span", { className: "timeline-item__title", children: title }),
21
+ description && /* @__PURE__ */ jsx("span", { className: "timeline-item__description", children: description })
22
+ ] });
23
+ }
23
24
  );
24
25
  TimelineItem.displayName = "TimelineItem";
25
26
  export {
@@ -2,6 +2,7 @@
2
2
  import { jsxs, jsx } from "react/jsx-runtime";
3
3
  import { useState, useRef, useCallback, useMemo, useContext, createContext, forwardRef, useEffect } from "react";
4
4
  import { createPortal } from "react-dom";
5
+ import { renamedProp } from "../../lib/deprecate.js";
5
6
  import { cn } from "../../lib/utils.js";
6
7
  const ToastContext = createContext(null);
7
8
  function useToast() {
@@ -60,7 +61,8 @@ const ToastItem = forwardRef(function ToastItem2(props, ref) {
60
61
  else focusRef.current = false;
61
62
  if (!hoverRef.current && !focusRef.current && !removing) startTimer();
62
63
  };
63
- const variantClass = data.variant && data.variant !== "default" ? `toast--${data.variant}` : void 0;
64
+ const tone = data.tone ?? data.variant ?? "default";
65
+ const variantClass = tone !== "default" ? `toast--${tone}` : void 0;
64
66
  return (
65
67
  // biome-ignore lint/a11y/noStaticElementInteractions: hover/focus handlers only pause the auto-dismiss timer (WCAG 2.2.1); the element is a live region, not a control.
66
68
  /* @__PURE__ */ jsxs(
@@ -68,8 +70,10 @@ const ToastItem = forwardRef(function ToastItem2(props, ref) {
68
70
  {
69
71
  ref,
70
72
  className: cn("toast", variantClass),
71
- role: data.variant === "negative" || data.variant === "warning" ? "alert" : "status",
73
+ role: tone === "negative" || tone === "warning" ? "alert" : "status",
72
74
  "aria-atomic": "true",
75
+ "data-tone": tone,
76
+ "data-state": removing ? "closing" : "open",
73
77
  "data-removing": removing || void 0,
74
78
  onMouseEnter: () => pause("hover"),
75
79
  onMouseLeave: () => resume("hover"),
@@ -78,8 +82,8 @@ const ToastItem = forwardRef(function ToastItem2(props, ref) {
78
82
  if (!e.currentTarget.contains(e.relatedTarget)) resume("focus");
79
83
  },
80
84
  children: [
81
- data.variant && data.variant !== "default" && /* @__PURE__ */ jsxs("span", { className: "toast__icon", "aria-hidden": "true", children: [
82
- data.variant === "positive" && /* @__PURE__ */ jsx(
85
+ tone !== "default" && /* @__PURE__ */ jsxs("span", { className: "toast__icon", "aria-hidden": "true", children: [
86
+ tone === "positive" && /* @__PURE__ */ jsx(
83
87
  "svg",
84
88
  {
85
89
  "aria-hidden": "true",
@@ -95,7 +99,7 @@ const ToastItem = forwardRef(function ToastItem2(props, ref) {
95
99
  children: /* @__PURE__ */ jsx("path", { d: "M20 6 9 17l-5-5" })
96
100
  }
97
101
  ),
98
- data.variant === "negative" && /* @__PURE__ */ jsxs(
102
+ tone === "negative" && /* @__PURE__ */ jsxs(
99
103
  "svg",
100
104
  {
101
105
  "aria-hidden": "true",
@@ -115,7 +119,7 @@ const ToastItem = forwardRef(function ToastItem2(props, ref) {
115
119
  ]
116
120
  }
117
121
  ),
118
- data.variant === "warning" && /* @__PURE__ */ jsxs(
122
+ tone === "warning" && /* @__PURE__ */ jsxs(
119
123
  "svg",
120
124
  {
121
125
  "aria-hidden": "true",
@@ -135,7 +139,7 @@ const ToastItem = forwardRef(function ToastItem2(props, ref) {
135
139
  ]
136
140
  }
137
141
  ),
138
- data.variant === "info" && /* @__PURE__ */ jsxs(
142
+ tone === "info" && /* @__PURE__ */ jsxs(
139
143
  "svg",
140
144
  {
141
145
  "aria-hidden": "true",
@@ -206,7 +210,7 @@ function ToastProvider({
206
210
  id,
207
211
  title: options.title,
208
212
  description: options.description,
209
- variant: options.variant ?? "default",
213
+ tone: renamedProp("Toast", "variant", "tone", options.variant, options.tone) ?? "default",
210
214
  duration: options.duration ?? 4e3
211
215
  };
212
216
  setToasts((prev) => [...prev, newToast]);
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { jsxs, Fragment, jsx } from "react/jsx-runtime";
3
- import { forwardRef, useState, useRef, useId, useCallback, useEffect, useLayoutEffect, cloneElement, version } from "react";
3
+ import { forwardRef, useState, useCallback, useRef, useId, useEffect, useLayoutEffect, cloneElement, version } from "react";
4
4
  import { createPortal } from "react-dom";
5
5
  import { cn } from "../../lib/utils.js";
6
6
  const GAP = 8;
@@ -57,8 +57,27 @@ function getChildRef(child) {
57
57
  return child.ref;
58
58
  }
59
59
  const Tooltip = forwardRef(
60
- ({ content, children, side = "top", delay = 200, className, style }, ref) => {
61
- const [open, setOpen] = useState(false);
60
+ ({
61
+ content,
62
+ children,
63
+ open: openProp,
64
+ defaultOpen = false,
65
+ onOpenChange,
66
+ side = "top",
67
+ delay = 200,
68
+ className,
69
+ style
70
+ }, ref) => {
71
+ const [internalOpen, setInternalOpen] = useState(defaultOpen);
72
+ const isControlled = openProp !== void 0;
73
+ const open = isControlled ? openProp : internalOpen;
74
+ const setOpen = useCallback(
75
+ (next) => {
76
+ if (!isControlled) setInternalOpen(next);
77
+ onOpenChange?.(next);
78
+ },
79
+ [isControlled, onOpenChange]
80
+ );
62
81
  const [mounted, setMounted] = useState(false);
63
82
  const [position, setPosition] = useState({ top: 0, left: 0 });
64
83
  const triggerRef = useRef(null);
@@ -99,11 +118,11 @@ const Tooltip = forwardRef(
99
118
  const show = useCallback(() => {
100
119
  clearTimers();
101
120
  showTimerRef.current = setTimeout(() => setOpen(true), delay);
102
- }, [delay, clearTimers]);
121
+ }, [delay, clearTimers, setOpen]);
103
122
  const hide = useCallback(() => {
104
123
  clearTimers();
105
124
  hideTimerRef.current = setTimeout(() => setOpen(false), HIDE_GRACE_MS);
106
- }, [clearTimers]);
125
+ }, [clearTimers, setOpen]);
107
126
  const keepOpen = useCallback(() => {
108
127
  if (hideTimerRef.current) clearTimeout(hideTimerRef.current);
109
128
  hideTimerRef.current = null;
@@ -118,7 +137,7 @@ const Tooltip = forwardRef(
118
137
  };
119
138
  document.addEventListener("keydown", onKeyDown);
120
139
  return () => document.removeEventListener("keydown", onKeyDown);
121
- }, [open, clearTimers]);
140
+ }, [open, clearTimers, setOpen]);
122
141
  const childProps = children.props;
123
142
  const childRef = getChildRef(children);
124
143
  const describedBy = [childProps["aria-describedby"], tooltipId].filter(Boolean).join(" ");
@@ -1,5 +1,6 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
2
  import { forwardRef } from "react";
3
+ import { renamedProp } from "../../lib/deprecate.js";
3
4
  import { cn } from "../../lib/utils.js";
4
5
  const STATUS_LABELS = {
5
6
  positive: "正常",
@@ -8,8 +9,11 @@ const STATUS_LABELS = {
8
9
  info: "情報",
9
10
  default: "データなし"
10
11
  };
12
+ function resolveTone(item) {
13
+ return renamedProp("Tracker", "status", "tone", item.status, item.tone) ?? "default";
14
+ }
11
15
  function segmentText(item) {
12
- const status = STATUS_LABELS[item.status];
16
+ const status = STATUS_LABELS[resolveTone(item)];
13
17
  if (!item.tooltip || item.tooltip === status) return status;
14
18
  return `${item.tooltip}(${status})`;
15
19
  }
@@ -25,15 +29,19 @@ const Tracker = forwardRef(
25
29
  role: "figure",
26
30
  "aria-label": label ?? `${data.length}区間`,
27
31
  ...rest,
28
- children: /* @__PURE__ */ jsx("ol", { className: "tracker__list", children: data.map((item, i) => /* @__PURE__ */ jsx(
29
- "li",
30
- {
31
- className: cn("tracker__segment", `tracker__segment--${item.status}`),
32
- title: item.tooltip,
33
- children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: segmentText(item) })
34
- },
35
- i
36
- )) })
32
+ children: /* @__PURE__ */ jsx("ol", { className: "tracker__list", children: data.map((item, i) => {
33
+ const tone = resolveTone(item);
34
+ return /* @__PURE__ */ jsx(
35
+ "li",
36
+ {
37
+ "data-tone": tone,
38
+ className: cn("tracker__segment", `tracker__segment--${tone}`),
39
+ title: item.tooltip,
40
+ children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: segmentText(item) })
41
+ },
42
+ i
43
+ );
44
+ }) })
37
45
  }
38
46
  )
39
47
  );
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
  import { ThemeProvider, useTheme } from "./hooks/useTheme.js";
3
+ import { Slot } from "./lib/Slot.js";
3
4
  import { Alert } from "./components/Alert/Alert.js";
4
5
  import { AppLayout } from "./components/Layout/Layout.js";
5
6
  import { Avatar } from "./components/Avatar/Avatar.js";
@@ -147,6 +148,7 @@ export {
147
148
  SidebarDivider,
148
149
  SidebarGroupLabel,
149
150
  Skeleton,
151
+ Slot,
150
152
  Sparkline,
151
153
  SplitSection,
152
154
  StatItem,
@@ -0,0 +1,52 @@
1
+ "use client";
2
+ import { version, forwardRef, Children, isValidElement, cloneElement } from "react";
3
+ import { composeEventHandlers } from "./composeEventHandlers.js";
4
+ import { cn } from "./utils.js";
5
+ const REACT_MAJOR = Number.parseInt(version.split(".")[0] ?? "0", 10);
6
+ function assignRef(ref, node) {
7
+ if (typeof ref === "function") ref(node);
8
+ else if (ref) ref.current = node;
9
+ }
10
+ function getElementRef(element) {
11
+ if (REACT_MAJOR >= 19) return element.props.ref;
12
+ return element.ref;
13
+ }
14
+ const Slot = forwardRef(
15
+ ({ children, ...slotProps }, forwardedRef) => {
16
+ const child = Children.only(children);
17
+ if (!isValidElement(child)) {
18
+ throw new Error("[kaze] Slot (asChild) には React 要素を 1 つだけ渡してください");
19
+ }
20
+ const merged = mergeProps(slotProps, child.props);
21
+ const childRef = getElementRef(child);
22
+ merged.ref = (node) => {
23
+ assignRef(childRef, node);
24
+ assignRef(forwardedRef, node);
25
+ };
26
+ return cloneElement(child, merged);
27
+ }
28
+ );
29
+ Slot.displayName = "Slot";
30
+ function mergeProps(slotProps, childProps) {
31
+ const merged = { ...slotProps };
32
+ for (const [key, childValue] of Object.entries(childProps)) {
33
+ if (childValue === void 0) continue;
34
+ const slotValue = slotProps[key];
35
+ if (key === "className") {
36
+ merged.className = cn(slotValue, childValue);
37
+ } else if (key === "style") {
38
+ merged.style = {
39
+ ...slotValue,
40
+ ...childValue
41
+ };
42
+ } else if (/^on[A-Z]/.test(key) && typeof slotValue === "function" && typeof childValue === "function") {
43
+ merged[key] = composeEventHandlers(childValue, slotValue);
44
+ } else {
45
+ merged[key] = childValue;
46
+ }
47
+ }
48
+ return merged;
49
+ }
50
+ export {
51
+ Slot
52
+ };
@@ -0,0 +1,26 @@
1
+ const warned = /* @__PURE__ */ new Set();
2
+ function isProduction() {
3
+ try {
4
+ return typeof process !== "undefined" && process.env.NODE_ENV === "production";
5
+ } catch {
6
+ return false;
7
+ }
8
+ }
9
+ function warnDeprecated(key, message) {
10
+ if (isProduction() || warned.has(key)) return;
11
+ warned.add(key);
12
+ console.warn(`[kaze] ${message}`);
13
+ }
14
+ function renamedProp(component, oldName, newName, oldValue, newValue) {
15
+ if (newValue !== void 0) return newValue;
16
+ if (oldValue === void 0) return void 0;
17
+ warnDeprecated(
18
+ `${component}.${oldName}`,
19
+ `${component} の \`${oldName}\` は \`${newName}\` に改名しました。次のマイナーバージョンで削除します`
20
+ );
21
+ return oldValue;
22
+ }
23
+ export {
24
+ renamedProp,
25
+ warnDeprecated
26
+ };
@@ -1,7 +1,11 @@
1
1
  import { type ReactNode, type HTMLAttributes } from "react";
2
- export type AlertVariant = "default" | "positive" | "negative" | "warning" | "info";
2
+ import type { Tone } from "../../lib/types";
3
+ /** @deprecated `Tone` を使う */
4
+ export type AlertVariant = Tone;
3
5
  export interface AlertProps extends HTMLAttributes<HTMLDivElement> {
4
- variant?: AlertVariant;
6
+ tone?: Tone;
7
+ /** @deprecated `tone` に改名。次のマイナーで削除 */
8
+ variant?: Tone;
5
9
  icon?: ReactNode;
6
10
  title?: string;
7
11
  children: ReactNode;
@@ -1,5 +1,6 @@
1
1
  import { type HTMLAttributes } from "react";
2
- export type AvatarSize = "xs" | "sm" | "md" | "lg";
2
+ import type { Size } from "../../lib/types";
3
+ export type AvatarSize = Extract<Size, "xs" | "sm" | "md" | "lg">;
3
4
  export interface AvatarProps extends HTMLAttributes<HTMLSpanElement> {
4
5
  size?: AvatarSize;
5
6
  src?: string;
@@ -1,7 +1,11 @@
1
1
  import { type HTMLAttributes } from "react";
2
- export type BadgeVariant = "default" | "positive" | "negative" | "warning" | "info";
2
+ import type { Tone } from "../../lib/types";
3
+ /** @deprecated `Tone` を使う */
4
+ export type BadgeVariant = Tone;
3
5
  export interface BadgeProps extends HTMLAttributes<HTMLSpanElement> {
4
- variant?: BadgeVariant;
6
+ tone?: Tone;
7
+ /** @deprecated `tone` に改名。次のマイナーで削除 */
8
+ variant?: Tone;
5
9
  dot?: boolean;
6
10
  /** Solid (inverted) style: filled background with white text */
7
11
  solid?: boolean;
@@ -1,10 +1,13 @@
1
1
  import { type ButtonHTMLAttributes } from "react";
2
+ import type { Size } from "../../lib/types";
2
3
  export type ButtonVariant = "primary" | "secondary" | "outline" | "ghost" | "destructive";
3
- export type ButtonSize = "xs" | "sm" | "md" | "lg";
4
+ export type ButtonSize = Extract<Size, "xs" | "sm" | "md" | "lg">;
4
5
  export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
5
6
  variant?: ButtonVariant;
6
7
  size?: ButtonSize;
7
8
  fullWidth?: boolean;
8
9
  iconOnly?: boolean;
10
+ /** 子要素にスタイルと振る舞いを委ねる(Next.js の Link など) */
11
+ asChild?: boolean;
9
12
  }
10
13
  export declare const Button: import("react").ForwardRefExoticComponent<ButtonProps & import("react").RefAttributes<HTMLButtonElement>>;
@@ -1,7 +1,10 @@
1
1
  import { type HTMLAttributes, type RefObject } from "react";
2
2
  export interface DialogProps extends HTMLAttributes<HTMLDivElement> {
3
3
  open: boolean;
4
- onClose: () => void;
4
+ /** Escape / 背景クリック / 閉じる操作で呼ばれる。`open` を false にするのは呼び出し側 */
5
+ onOpenChange?: (open: boolean) => void;
6
+ /** @deprecated `onOpenChange` に改名。次のマイナーで削除 */
7
+ onClose?: () => void;
5
8
  /** @deprecated useFocusTrap handles focus restoration automatically. Kept for backward compat: ignored. */
6
9
  returnFocusRef?: RefObject<HTMLElement | null>;
7
10
  }