kaze-design-system 0.8.0 → 0.8.1

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.
@@ -4,12 +4,12 @@ import { cn } from "../../lib/utils.js";
4
4
  const Avatar = forwardRef(
5
5
  ({ size = "md", src, alt, fallback, className, ...rest }, ref) => {
6
6
  const showFallback = !src && fallback;
7
+ const a11yProps = showFallback ? { role: "img", "aria-label": fallback } : {};
7
8
  return /* @__PURE__ */ jsx(
8
9
  "span",
9
10
  {
10
11
  ref,
11
- role: showFallback ? "img" : void 0,
12
- "aria-label": showFallback ? fallback : void 0,
12
+ ...a11yProps,
13
13
  className: cn("avatar", `avatar--${size}`, className),
14
14
  ...rest,
15
15
  children: src ? /* @__PURE__ */ jsx("img", { src, alt: alt ?? "" }) : /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: fallback })
@@ -7,7 +7,7 @@ const BarList = forwardRef(
7
7
  (a, b) => sortOrder === "ascending" ? a.value - b.value : b.value - a.value
8
8
  );
9
9
  const maxValue = Math.max(...sorted.map((d) => d.value), 1);
10
- return /* @__PURE__ */ jsx("div", { ref, className: cn("barlist", className), ...rest, children: sorted.map((item, i) => {
10
+ return /* @__PURE__ */ jsx("div", { ref, className: cn("barlist", className), ...rest, children: sorted.map((item) => {
11
11
  const pct = item.value / maxValue * 100;
12
12
  const display = item.displayValue ?? (valueFormatter ? valueFormatter(item.value) : String(item.value));
13
13
  const content = /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -27,7 +27,15 @@ const BarList = forwardRef(
27
27
  /* @__PURE__ */ jsx("span", { className: "barlist__value", children: display })
28
28
  ] });
29
29
  const LinkComponent = item.as ?? "a";
30
- return item.href ? /* @__PURE__ */ jsx(LinkComponent, { href: item.href, className: "barlist__row barlist__row--link", children: content }, i) : /* @__PURE__ */ jsx("div", { className: "barlist__row", children: content }, i);
30
+ return item.href ? /* @__PURE__ */ jsx(
31
+ LinkComponent,
32
+ {
33
+ href: item.href,
34
+ className: "barlist__row barlist__row--link",
35
+ children: content
36
+ },
37
+ item.label
38
+ ) : /* @__PURE__ */ jsx("div", { className: "barlist__row", children: content }, item.label);
31
39
  }) });
32
40
  }
33
41
  );
@@ -1,9 +1,10 @@
1
1
  "use client";
2
2
  import { jsxs, jsx } from "react/jsx-runtime";
3
- import { forwardRef, useRef, useState, useEffect, useCallback, useMemo } from "react";
3
+ import { forwardRef, useState, useCallback, useMemo } from "react";
4
4
  import { cn } from "../../lib/utils.js";
5
5
  import { CHART_COLORS } from "../../lib/chartColors.js";
6
6
  import { niceScale, linearScale, defaultFormat } from "../../lib/chartScale.js";
7
+ import { useChartWidth } from "../../lib/useChartWidth.js";
7
8
  import { ChartLegend } from "../ChartLegend/ChartLegend.js";
8
9
  const FALLBACK_WIDTH = 640;
9
10
  const PAD_TOP = 8;
@@ -43,23 +44,9 @@ const ColumnChart = forwardRef(
43
44
  onBlur,
44
45
  ...rest
45
46
  }, ref) => {
46
- const wrapRef = useRef(null);
47
- const [width, setWidth] = useState(FALLBACK_WIDTH);
47
+ const { ref: wrapRef, width, measured } = useChartWidth(FALLBACK_WIDTH);
48
48
  const [hidden, setHidden] = useState([]);
49
49
  const [active, setActive] = useState(null);
50
- useEffect(() => {
51
- const el = wrapRef.current;
52
- if (!el) return;
53
- const measure = () => {
54
- const w = el.clientWidth;
55
- if (w > 0) setWidth(w);
56
- };
57
- measure();
58
- if (typeof ResizeObserver === "undefined") return;
59
- const ro = new ResizeObserver(measure);
60
- ro.observe(el);
61
- return () => ro.disconnect();
62
- }, []);
63
50
  const toggle = useCallback((key) => {
64
51
  setHidden((prev) => prev.includes(key) ? prev.filter((k) => k !== key) : [...prev, key]);
65
52
  }, []);
@@ -177,9 +164,10 @@ const ColumnChart = forwardRef(
177
164
  "svg",
178
165
  {
179
166
  className: "chart__svg column-chart__svg",
180
- width,
167
+ width: measured ? width : "100%",
181
168
  height,
182
169
  viewBox: `0 0 ${width} ${height}`,
170
+ preserveAspectRatio: measured ? void 0 : "none",
183
171
  onPointerLeave: () => setActive(null),
184
172
  "aria-hidden": "true",
185
173
  children: [
@@ -37,10 +37,12 @@ const CommandPalette = forwardRef(
37
37
  const groups = /* @__PURE__ */ new Map();
38
38
  for (const cmd of filtered) {
39
39
  const group = cmd.group ?? "";
40
- if (!groups.has(group)) {
41
- groups.set(group, []);
40
+ const list = groups.get(group);
41
+ if (list) {
42
+ list.push(cmd);
43
+ } else {
44
+ groups.set(group, [cmd]);
42
45
  }
43
- groups.get(group).push(cmd);
44
46
  }
45
47
  return groups;
46
48
  }, [filtered]);
@@ -118,6 +120,8 @@ const CommandPalette = forwardRef(
118
120
  if (typeof document === "undefined") return null;
119
121
  let flatIndex = 0;
120
122
  return createPortal(
123
+ // biome-ignore lint/a11y/noStaticElementInteractions: the backdrop only handles click-outside-to-close; keyboard users close the palette with Escape, and the backdrop itself must not be focusable.
124
+ // biome-ignore lint/a11y/useKeyWithClickEvents: Escape is handled by the dialog's onKeyDown; the backdrop has no keyboard role of its own.
121
125
  /* @__PURE__ */ jsx("div", { className: "overlay", onClick: handleOverlayClick, children: /* @__PURE__ */ jsxs(
122
126
  "div",
123
127
  {
@@ -152,33 +156,37 @@ const CommandPalette = forwardRef(
152
156
  group && /* @__PURE__ */ jsx("div", { className: "command-palette__group", children: group }),
153
157
  cmds.map((cmd) => {
154
158
  const index = flatIndex++;
155
- return /* @__PURE__ */ jsxs(
156
- "div",
157
- {
158
- role: "option",
159
- "data-command-item": "",
160
- "aria-selected": index === highlightedIndex,
161
- className: cn(
162
- "command-palette__item",
163
- index === highlightedIndex && "command-palette__item--active"
164
- ),
165
- onClick: () => handleSelect(cmd),
166
- onMouseEnter: () => setHighlightedIndex(index),
167
- children: [
168
- cmd.icon && /* @__PURE__ */ jsx(
169
- "span",
170
- {
171
- style: {
172
- display: "inline-flex",
173
- flexShrink: 0
174
- },
175
- children: cmd.icon
176
- }
159
+ return (
160
+ // biome-ignore lint/a11y/useKeyWithClickEvents: Arrow/Enter selection is handled by the dialog's onKeyDown while focus stays on the search input (listbox pattern).
161
+ // biome-ignore lint/a11y/useFocusableInteractive: options must not take focus; focus stays on the search input and the highlighted option is tracked via aria-selected.
162
+ /* @__PURE__ */ jsxs(
163
+ "div",
164
+ {
165
+ role: "option",
166
+ "data-command-item": "",
167
+ "aria-selected": index === highlightedIndex,
168
+ className: cn(
169
+ "command-palette__item",
170
+ index === highlightedIndex && "command-palette__item--active"
177
171
  ),
178
- cmd.label
179
- ]
180
- },
181
- cmd.id
172
+ onClick: () => handleSelect(cmd),
173
+ onMouseEnter: () => setHighlightedIndex(index),
174
+ children: [
175
+ cmd.icon && /* @__PURE__ */ jsx(
176
+ "span",
177
+ {
178
+ style: {
179
+ display: "inline-flex",
180
+ flexShrink: 0
181
+ },
182
+ children: cmd.icon
183
+ }
184
+ ),
185
+ cmd.label
186
+ ]
187
+ },
188
+ cmd.id
189
+ )
182
190
  );
183
191
  })
184
192
  ] }, group || "__default"))
@@ -62,6 +62,7 @@ const Dialog = forwardRef(
62
62
  if (!visible) return null;
63
63
  if (typeof document === "undefined") return null;
64
64
  return createPortal(
65
+ // biome-ignore lint/a11y/noStaticElementInteractions: the backdrop only handles click-outside-to-close; keyboard users close the dialog with Escape, and the backdrop itself must not be focusable.
65
66
  /* @__PURE__ */ jsx(
66
67
  "div",
67
68
  {
@@ -11,15 +11,14 @@ const iconSvgProps = {
11
11
  strokeWidth: 2,
12
12
  strokeLinecap: "round",
13
13
  strokeLinejoin: "round",
14
- className: "disclosure__icon-svg",
15
- "aria-hidden": true
14
+ className: "disclosure__icon-svg"
16
15
  };
17
16
  function renderIcon(icon) {
18
17
  if (icon === "chevron" || icon == null) {
19
- return /* @__PURE__ */ jsx("svg", { ...iconSvgProps, children: /* @__PURE__ */ jsx("path", { d: "m9 18 6-6-6-6" }) });
18
+ return /* @__PURE__ */ jsx("svg", { ...iconSvgProps, "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "m9 18 6-6-6-6" }) });
20
19
  }
21
20
  if (icon === "plus") {
22
- return /* @__PURE__ */ jsxs("svg", { ...iconSvgProps, children: [
21
+ return /* @__PURE__ */ jsxs("svg", { ...iconSvgProps, "aria-hidden": "true", children: [
23
22
  /* @__PURE__ */ jsx("path", { d: "M5 12h14" }),
24
23
  /* @__PURE__ */ jsx("path", { d: "M12 5v14" })
25
24
  ] });
@@ -66,6 +65,7 @@ const Disclosure = forwardRef(
66
65
  [isOpen, setOpen]
67
66
  );
68
67
  const contentId = useId();
68
+ const triggerId = useId();
69
69
  const isBuiltInIcon = icon === "chevron" || icon === "plus";
70
70
  const markerNode = useMemo(() => renderIcon(icon), [icon]);
71
71
  return /* @__PURE__ */ jsxs(
@@ -85,6 +85,7 @@ const Disclosure = forwardRef(
85
85
  /* @__PURE__ */ jsxs(
86
86
  "button",
87
87
  {
88
+ id: triggerId,
88
89
  type: "button",
89
90
  className: cn(
90
91
  "disclosure__trigger",
@@ -122,10 +123,10 @@ const Disclosure = forwardRef(
122
123
  }
123
124
  ),
124
125
  /* @__PURE__ */ jsx(
125
- "div",
126
+ "section",
126
127
  {
127
128
  id: contentId,
128
- role: "region",
129
+ "aria-labelledby": triggerId,
129
130
  className: cn("disclosure__content", contentClassName),
130
131
  hidden: !isOpen,
131
132
  children: /* @__PURE__ */ jsx("div", { className: "disclosure__content-inner", children })
@@ -66,23 +66,26 @@ const DropdownTrigger = forwardRef(
66
66
  },
67
67
  [setOpen]
68
68
  );
69
- return /* @__PURE__ */ jsx(
70
- "span",
71
- {
72
- ref: (node) => {
73
- triggerRef.current = node;
74
- if (typeof ref === "function") ref(node);
75
- else if (ref) ref.current = node;
76
- },
77
- role: "button",
78
- tabIndex: 0,
79
- "aria-expanded": open ? "true" : "false",
80
- "aria-haspopup": "menu",
81
- onClick: handleClick,
82
- onKeyDown: handleKeyDown,
83
- style: { display: "inline-flex" },
84
- children
85
- }
69
+ return (
70
+ // biome-ignore lint/a11y/useSemanticElements: the trigger wraps arbitrary children (often a <Button>), and nesting a <button> inside a <button> is invalid HTML; the span gets role, tabIndex and keyboard handling instead.
71
+ /* @__PURE__ */ jsx(
72
+ "span",
73
+ {
74
+ ref: (node) => {
75
+ triggerRef.current = node;
76
+ if (typeof ref === "function") ref(node);
77
+ else if (ref) ref.current = node;
78
+ },
79
+ role: "button",
80
+ tabIndex: 0,
81
+ "aria-expanded": open ? "true" : "false",
82
+ "aria-haspopup": "menu",
83
+ onClick: handleClick,
84
+ onKeyDown: handleKeyDown,
85
+ style: { display: "inline-flex" },
86
+ children
87
+ }
88
+ )
86
89
  );
87
90
  }
88
91
  );
@@ -10,18 +10,8 @@ const Icon = forwardRef(
10
10
  lg: 20,
11
11
  xl: 24
12
12
  };
13
- return /* @__PURE__ */ jsx(
14
- "span",
15
- {
16
- ref,
17
- role: label ? "img" : void 0,
18
- "aria-label": label,
19
- "aria-hidden": label ? void 0 : true,
20
- className: cn("icon", `icon--${size}`, className),
21
- ...rest,
22
- children: /* @__PURE__ */ jsx(LucideComponent, { size: sizeMap[size], strokeWidth: 2 })
23
- }
24
- );
13
+ const a11yProps = label ? { role: "img", "aria-label": label } : { "aria-hidden": true };
14
+ return /* @__PURE__ */ jsx("span", { ref, ...a11yProps, className: cn("icon", `icon--${size}`, className), ...rest, children: /* @__PURE__ */ jsx(LucideComponent, { size: sizeMap[size], strokeWidth: 2 }) });
25
15
  }
26
16
  );
27
17
  Icon.displayName = "Icon";
@@ -1,9 +1,10 @@
1
1
  "use client";
2
2
  import { jsxs, jsx } from "react/jsx-runtime";
3
- import { forwardRef, useRef, useState, useEffect, useCallback } from "react";
3
+ import { forwardRef, useRef, useState, useCallback } from "react";
4
4
  import { cn } from "../../lib/utils.js";
5
5
  import { CHART_COLORS } from "../../lib/chartColors.js";
6
6
  import { niceScale, linearScale, defaultFormat } from "../../lib/chartScale.js";
7
+ import { useChartWidth } from "../../lib/useChartWidth.js";
7
8
  import { ChartLegend } from "../ChartLegend/ChartLegend.js";
8
9
  const PAD_TOP = 8;
9
10
  const PAD_BOTTOM = 24;
@@ -101,24 +102,10 @@ const LineChart = forwardRef(
101
102
  className,
102
103
  ...rest
103
104
  }, ref) => {
104
- const wrapRef = useRef(null);
105
+ const { ref: wrapRef, width, measured } = useChartWidth(INITIAL_WIDTH);
105
106
  const svgRef = useRef(null);
106
- const [width, setWidth] = useState(INITIAL_WIDTH);
107
107
  const [hidden, setHidden] = useState([]);
108
108
  const [active, setActive] = useState(null);
109
- useEffect(() => {
110
- const el = wrapRef.current;
111
- if (!el) return;
112
- const measure = () => {
113
- const w = Math.round(el.getBoundingClientRect().width);
114
- if (w > 0) setWidth(w);
115
- };
116
- measure();
117
- if (typeof ResizeObserver === "undefined") return;
118
- const ro = new ResizeObserver(measure);
119
- ro.observe(el);
120
- return () => ro.disconnect();
121
- }, []);
122
109
  const toggle = useCallback((key) => {
123
110
  setHidden((h) => h.includes(key) ? h.filter((k) => k !== key) : [...h, key]);
124
111
  }, []);
@@ -189,9 +176,10 @@ const LineChart = forwardRef(
189
176
  className: "chart__svg line-chart__svg",
190
177
  role: "img",
191
178
  "aria-label": ariaLabel,
192
- width,
179
+ width: measured ? width : "100%",
193
180
  height,
194
181
  viewBox: `0 0 ${width} ${height}`,
182
+ preserveAspectRatio: measured ? void 0 : "none",
195
183
  tabIndex: 0,
196
184
  onPointerMove: (e) => setActive(indexFromPointer(e)),
197
185
  onPointerLeave: () => setActive(null),
@@ -127,6 +127,7 @@ const NumberField = forwardRef(
127
127
  children: /* @__PURE__ */ jsx(
128
128
  "svg",
129
129
  {
130
+ "aria-hidden": "true",
130
131
  width: "12",
131
132
  height: "12",
132
133
  viewBox: "0 0 12 12",
@@ -171,6 +172,7 @@ const NumberField = forwardRef(
171
172
  children: /* @__PURE__ */ jsxs(
172
173
  "svg",
173
174
  {
175
+ "aria-hidden": "true",
174
176
  width: "12",
175
177
  height: "12",
176
178
  viewBox: "0 0 12 12",
@@ -6,9 +6,9 @@ function getPages(current, total, siblings) {
6
6
  const left = Math.max(2, current - siblings);
7
7
  const right = Math.min(total - 1, current + siblings);
8
8
  range.push(1);
9
- if (left > 2) range.push("...");
9
+ if (left > 2) range.push("ellipsis-start");
10
10
  for (let i = left; i <= right; i++) range.push(i);
11
- if (right < total - 1) range.push("...");
11
+ if (right < total - 1) range.push("ellipsis-end");
12
12
  if (total > 1) range.push(total);
13
13
  return range;
14
14
  }
@@ -38,7 +38,7 @@ const Pagination = forwardRef(
38
38
  }
39
39
  ),
40
40
  pages.map(
41
- (page, i) => page === "..." ? /* @__PURE__ */ jsx("span", { className: "pagination__ellipsis", children: "…" }, `e${i}`) : /* @__PURE__ */ jsx(
41
+ (page) => typeof page === "string" ? /* @__PURE__ */ jsx("span", { className: "pagination__ellipsis", children: "…" }, page) : /* @__PURE__ */ jsx(
42
42
  "button",
43
43
  {
44
44
  type: "button",
@@ -27,41 +27,45 @@ const Search = forwardRef(
27
27
  },
28
28
  [isControlled, onChangeProp, onSearch]
29
29
  );
30
- return /* @__PURE__ */ jsxs("div", { role: "search", className: cn("search", className), ...rest, children: [
31
- /* @__PURE__ */ jsxs(
32
- "svg",
33
- {
34
- className: "search__icon",
35
- "aria-hidden": "true",
36
- xmlns: "http://www.w3.org/2000/svg",
37
- width: "14",
38
- height: "14",
39
- viewBox: "0 0 24 24",
40
- fill: "none",
41
- stroke: "currentColor",
42
- strokeWidth: "2",
43
- strokeLinecap: "round",
44
- strokeLinejoin: "round",
45
- children: [
46
- /* @__PURE__ */ jsx("circle", { cx: "11", cy: "11", r: "8" }),
47
- /* @__PURE__ */ jsx("path", { d: "m21 21-4.3-4.3" })
48
- ]
49
- }
50
- ),
51
- /* @__PURE__ */ jsx(
52
- "input",
53
- {
54
- ref,
55
- type: "text",
56
- className: "search__input",
57
- placeholder,
58
- "aria-label": ariaLabel ?? placeholder,
59
- value: currentValue,
60
- onChange: handleChange
61
- }
62
- ),
63
- shortcut && /* @__PURE__ */ jsx("kbd", { className: "search__kbd", "aria-hidden": "true", children: shortcut })
64
- ] });
30
+ return (
31
+ // biome-ignore lint/a11y/useSemanticElements: the <search> element is not supported in all target browsers yet (Safari < 17); role="search" on a div exposes the same landmark.
32
+ /* @__PURE__ */ jsxs("div", { role: "search", className: cn("search", className), ...rest, children: [
33
+ /* @__PURE__ */ jsxs(
34
+ "svg",
35
+ {
36
+ className: "search__icon",
37
+ "aria-hidden": "true",
38
+ xmlns: "http://www.w3.org/2000/svg",
39
+ width: "14",
40
+ height: "14",
41
+ viewBox: "0 0 24 24",
42
+ fill: "none",
43
+ stroke: "currentColor",
44
+ strokeWidth: "2",
45
+ strokeLinecap: "round",
46
+ strokeLinejoin: "round",
47
+ children: [
48
+ /* @__PURE__ */ jsx("circle", { cx: "11", cy: "11", r: "8" }),
49
+ /* @__PURE__ */ jsx("path", { d: "m21 21-4.3-4.3" })
50
+ ]
51
+ }
52
+ ),
53
+ /* @__PURE__ */ jsx(
54
+ "input",
55
+ {
56
+ ref,
57
+ type: "text",
58
+ className: "search__input",
59
+ placeholder,
60
+ "aria-label": ariaLabel ?? placeholder,
61
+ value: currentValue,
62
+ onChange: handleChange
63
+ }
64
+ ),
65
+ shortcut && // biome-ignore lint/a11y/noAriaHiddenOnFocusable: <kbd> has no tabIndex and is not focusable; the shortcut hint is visual only.
66
+ /* @__PURE__ */ jsx("kbd", { className: "search__kbd", "aria-hidden": "true", children: shortcut })
67
+ ] })
68
+ );
65
69
  }
66
70
  );
67
71
  Search.displayName = "Search";
@@ -38,7 +38,10 @@ const SidebarGroupLabel = forwardRef(
38
38
  SidebarGroupLabel.displayName = "SidebarGroupLabel";
39
39
  const SidebarDivider = forwardRef(
40
40
  ({ className, ...rest }, ref) => {
41
- return /* @__PURE__ */ jsx("div", { ref, className: cn("sidebar__divider", className), role: "separator", ...rest });
41
+ return (
42
+ // biome-ignore lint/a11y/useSemanticElements: the forwarded ref and props are typed as HTMLDivElement (public API); switching to <hr> would change the exported types.
43
+ /* @__PURE__ */ jsx("div", { ref, className: cn("sidebar__divider", className), role: "separator", ...rest })
44
+ );
42
45
  }
43
46
  );
44
47
  SidebarDivider.displayName = "SidebarDivider";
@@ -20,28 +20,31 @@ const Stepper = forwardRef(
20
20
  ({ steps, currentStep, className, ...rest }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("stepper", className), ...rest, children: steps.map((step, i) => {
21
21
  const isCompleted = i < currentStep;
22
22
  const isActive = i === currentStep;
23
- return /* @__PURE__ */ jsxs("div", { style: { display: "contents" }, children: [
24
- /* @__PURE__ */ jsxs("div", { className: cn("stepper__step", isActive && "stepper__step--active"), children: [
25
- /* @__PURE__ */ jsx(
23
+ return (
24
+ // biome-ignore lint/suspicious/noArrayIndexKey: steps are ordinal (the index is rendered as the step number) and labels are not guaranteed unique.
25
+ /* @__PURE__ */ jsxs("div", { style: { display: "contents" }, children: [
26
+ /* @__PURE__ */ jsxs("div", { className: cn("stepper__step", isActive && "stepper__step--active"), children: [
27
+ /* @__PURE__ */ jsx(
28
+ "span",
29
+ {
30
+ className: cn(
31
+ "stepper__circle",
32
+ isActive && "stepper__circle--active",
33
+ isCompleted && "stepper__circle--completed"
34
+ ),
35
+ children: isCompleted ? /* @__PURE__ */ jsx(CheckIcon, {}) : i + 1
36
+ }
37
+ ),
38
+ /* @__PURE__ */ jsx("span", { className: "stepper__label", children: step.label })
39
+ ] }),
40
+ i < steps.length - 1 && /* @__PURE__ */ jsx(
26
41
  "span",
27
42
  {
28
- className: cn(
29
- "stepper__circle",
30
- isActive && "stepper__circle--active",
31
- isCompleted && "stepper__circle--completed"
32
- ),
33
- children: isCompleted ? /* @__PURE__ */ jsx(CheckIcon, {}) : i + 1
43
+ className: cn("stepper__connector", isCompleted && "stepper__connector--completed")
34
44
  }
35
- ),
36
- /* @__PURE__ */ jsx("span", { className: "stepper__label", children: step.label })
37
- ] }),
38
- i < steps.length - 1 && /* @__PURE__ */ jsx(
39
- "span",
40
- {
41
- className: cn("stepper__connector", isCompleted && "stepper__connector--completed")
42
- }
43
- )
44
- ] }, i);
45
+ )
46
+ ] }, i)
47
+ );
45
48
  }) })
46
49
  );
47
50
  Stepper.displayName = "Stepper";
@@ -3,16 +3,7 @@ import { forwardRef } from "react";
3
3
  import { cn } from "../../lib/utils.js";
4
4
  const Table = forwardRef(
5
5
  ({ compact, className, children, ...rest }, ref) => {
6
- return /* @__PURE__ */ jsx("div", { className: "table-wrapper", children: /* @__PURE__ */ jsx(
7
- "table",
8
- {
9
- ref,
10
- role: "table",
11
- className: cn("table", compact && "table--compact", className),
12
- ...rest,
13
- children
14
- }
15
- ) });
6
+ return /* @__PURE__ */ jsx("div", { className: "table-wrapper", children: /* @__PURE__ */ jsx("table", { ref, className: cn("table", compact && "table--compact", className), ...rest, children }) });
16
7
  }
17
8
  );
18
9
  Table.displayName = "Table";
@@ -37,6 +37,7 @@ function ToastItem(props) {
37
37
  data.variant === "positive" && /* @__PURE__ */ jsx(
38
38
  "svg",
39
39
  {
40
+ "aria-hidden": "true",
40
41
  xmlns: "http://www.w3.org/2000/svg",
41
42
  width: "16",
42
43
  height: "16",
@@ -52,6 +53,7 @@ function ToastItem(props) {
52
53
  data.variant === "negative" && /* @__PURE__ */ jsxs(
53
54
  "svg",
54
55
  {
56
+ "aria-hidden": "true",
55
57
  xmlns: "http://www.w3.org/2000/svg",
56
58
  width: "16",
57
59
  height: "16",
@@ -71,6 +73,7 @@ function ToastItem(props) {
71
73
  data.variant === "warning" && /* @__PURE__ */ jsxs(
72
74
  "svg",
73
75
  {
76
+ "aria-hidden": "true",
74
77
  xmlns: "http://www.w3.org/2000/svg",
75
78
  width: "16",
76
79
  height: "16",
@@ -90,6 +93,7 @@ function ToastItem(props) {
90
93
  data.variant === "info" && /* @__PURE__ */ jsxs(
91
94
  "svg",
92
95
  {
96
+ "aria-hidden": "true",
93
97
  xmlns: "http://www.w3.org/2000/svg",
94
98
  width: "16",
95
99
  height: "16",
@@ -114,6 +118,7 @@ function ToastItem(props) {
114
118
  /* @__PURE__ */ jsx("button", { type: "button", className: "toast__close", onClick: dismiss, "aria-label": closeLabel, children: /* @__PURE__ */ jsxs(
115
119
  "svg",
116
120
  {
121
+ "aria-hidden": "true",
117
122
  xmlns: "http://www.w3.org/2000/svg",
118
123
  width: "14",
119
124
  height: "14",
@@ -155,7 +160,7 @@ function ToastProvider({
155
160
  return /* @__PURE__ */ jsxs(ToastContext.Provider, { value: { toast }, children: [
156
161
  children,
157
162
  typeof document !== "undefined" && createPortal(
158
- /* @__PURE__ */ jsx("div", { className: "toast-container", "aria-label": containerLabel, "aria-live": "polite", children: toasts.map((t) => /* @__PURE__ */ jsx(ToastItem, { data: t, onRemove: removeToast, closeLabel }, t.id)) }),
163
+ /* @__PURE__ */ jsx("section", { className: "toast-container", "aria-label": containerLabel, "aria-live": "polite", children: toasts.map((t) => /* @__PURE__ */ jsx(ToastItem, { data: t, onRemove: removeToast, closeLabel }, t.id)) }),
159
164
  document.body
160
165
  )
161
166
  ] });
@@ -0,0 +1,28 @@
1
+ "use client";
2
+ import { useRef, useState, useLayoutEffect, useEffect } from "react";
3
+ const useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect;
4
+ function useChartWidth(fallback) {
5
+ const ref = useRef(null);
6
+ const [width, setWidth] = useState(fallback);
7
+ const [measured, setMeasured] = useState(false);
8
+ useIsomorphicLayoutEffect(() => {
9
+ const el = ref.current;
10
+ if (!el) return;
11
+ const measure = () => {
12
+ const w = Math.round(el.getBoundingClientRect().width);
13
+ if (w > 0) {
14
+ setWidth(w);
15
+ setMeasured(true);
16
+ }
17
+ };
18
+ measure();
19
+ if (typeof ResizeObserver === "undefined") return;
20
+ const ro = new ResizeObserver(measure);
21
+ ro.observe(el);
22
+ return () => ro.disconnect();
23
+ }, []);
24
+ return { ref, width, measured };
25
+ }
26
+ export {
27
+ useChartWidth
28
+ };
@@ -0,0 +1,14 @@
1
+ import { type RefObject } from "react";
2
+ export interface ChartWidth<T extends HTMLElement> {
3
+ ref: RefObject<T | null>;
4
+ /** Measured width in px, or `fallback` until the first measurement */
5
+ width: number;
6
+ /**
7
+ * False until the container has been measured (SSR HTML and the first client
8
+ * paint before layout). Charts should stretch the SVG to 100% while false so
9
+ * server-rendered output fits the container instead of jumping on hydration.
10
+ */
11
+ measured: boolean;
12
+ }
13
+ /** Track a container's width for SVG charts: measured before paint, then via ResizeObserver. */
14
+ export declare function useChartWidth<T extends HTMLElement>(fallback: number): ChartWidth<T>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kaze-design-system",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "license": "MIT",