@geckou/ui-react 0.7.0 → 0.9.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.
package/README.md CHANGED
@@ -108,6 +108,31 @@ props は Vue 版(`@geckou/ui-vue`)と揃えている。`v-model` にあた
108
108
  yarn workspace @geckou/ui-react test
109
109
  ```
110
110
 
111
+ ## 0.9.0 の変更
112
+
113
+ - `DropdownUi` が Escape で閉じ、トリガーへフォーカスを戻す。処理したときは
114
+ `preventDefault()` するので、`ModalBox` の中に置いてもダイアログまで閉じない
115
+ - `DropdownUi` / `SlideDownUi` のトリガーに `aria-controls` が付き、
116
+ `DropdownUi` には `aria-haspopup="true"` も付く
117
+
118
+ ## 0.8.0 の変更
119
+
120
+ - `BasicButton` はローディング中に `disabled` にしない(押した瞬間にフォーカスが
121
+ `body` へ落ちるため)。代わりに `aria-disabled` / `aria-busy` を付け、
122
+ `pointer-events` と `onClick` ガードで押せなくする。**`:disabled` を前提に
123
+ スタイルを当てている場合は `[aria-disabled]` の追従が要る**
124
+ - `BasicButton` のローディング中もアクセシブル名を保つ(`invisible` → `opacity-0`)。
125
+ `LoadingSpinner` は `aria-hidden="true"`
126
+ - `BasicButton` の hover 色が `color-mix()` になった。`${色}cc` の文字列連結だったため、
127
+ 3 桁 hex(`'#fff'`)・`rgb()`・名前色・`var()` では不正値になっていた
128
+ - `PopupBox` は**表示している間だけ**子要素を描画する。常時 DOM にあると
129
+ `opacity` の切り替えでは支援技術に通知されず、非表示中も文言が読めていた
130
+ - `ModalBox` は開いたままアンマウントされてもフォーカスを戻す
131
+ - `ModalBox` の背景クリック判定が「押し始めも背景だったとき」だけになった。
132
+ ダイアログ内でテキスト選択を始めて背景で離すと閉じていた
133
+ - `RadioButtons` が `role="radiogroup"` になり、`ariaLabel` / `ariaLabelledBy` を受ける。
134
+ エラーは `aria-describedby` で結び付く
135
+ - `ErrorMessage` が `id` を受ける(`aria-describedby` から参照するため)
111
136
  ## 0.7.0 の変更
112
137
 
113
138
  - `CheckBox` / `LabeledCheckbox` に `value` を追加。`CheckBoxes` は各選択肢の
@@ -1,57 +1,72 @@
1
1
  "use client";
2
- import { jsxs as z, jsx as t } from "react/jsx-runtime";
3
- import { LoadingSpinner as $ } from "./LoadingSpinner.js";
4
- import { COLOR as n } from "@geckou/ui-core";
5
- function B({
2
+ import { jsxs as j, jsx as i } from "react/jsx-runtime";
3
+ import { LoadingSpinner as I } from "./LoadingSpinner.js";
4
+ import { COLOR as d } from "@geckou/ui-core";
5
+ function O({
6
6
  cssStyle: r,
7
- duration: m = 0.3,
8
- isLoading: d,
9
- isDisabled: a,
7
+ duration: p = 0.3,
8
+ isLoading: a,
9
+ isDisabled: n,
10
10
  buttonType: v = "button",
11
- onClick: f,
12
- loading: k,
13
- children: p
11
+ onClick: b,
12
+ loading: w,
13
+ children: k
14
14
  }) {
15
- var b, u, i, l, c, s, h, x, g;
16
- const w = a ? r == null ? void 0 : r.disabled : r == null ? void 0 : r.default, o = {
17
- textColor: n.white,
18
- backgroundColor: a ? n.darkGray : n.blue,
15
+ var u, l, s, c, h, f, x, m, g;
16
+ const z = n ? r == null ? void 0 : r.disabled : r == null ? void 0 : r.default, e = {
17
+ textColor: d.white,
18
+ backgroundColor: n ? d.darkGray : d.blue,
19
19
  backgroundImage: "none",
20
20
  border: {
21
- color: a ? n.darkGray : n.blue,
21
+ color: n ? d.darkGray : d.blue,
22
22
  size: "1px",
23
23
  radius: ".25rem"
24
24
  },
25
25
  boxShadow: "0 0 0 0 transparent",
26
- ...w
27
- }, e = (r == null ? void 0 : r.hover) || {}, C = {
28
- "--text-color": o.textColor,
29
- "--background-color": o.backgroundColor,
30
- "--background-image": o.backgroundImage,
31
- "--radius-size": (b = o.border) == null ? void 0 : b.radius,
32
- "--button-shadow": `0 0 0 ${(u = o.border) == null ? void 0 : u.size} ${(i = o.border) == null ? void 0 : i.color} inset, ${o.boxShadow}`,
33
- "--hover-text-color": e.textColor || o.textColor,
34
- "--hover-background-color": e.backgroundColor || o.backgroundColor && `${o.backgroundColor}cc`,
35
- "--hover-background-image": e.backgroundImage || o.backgroundImage,
36
- "--hover-radius-size": ((l = e.border) == null ? void 0 : l.radius) || ((c = o.border) == null ? void 0 : c.radius),
37
- "--hover-button-shadow": `0 0 0 ${((s = e.border) == null ? void 0 : s.size) || ((h = o.border) == null ? void 0 : h.size)} ${((x = e.border) == null ? void 0 : x.color) || ((g = o.border) == null ? void 0 : g.color)} inset, ${e.boxShadow || o.boxShadow}`,
38
- "--duration": `${m}s`
26
+ ...z
27
+ }, o = (r == null ? void 0 : r.hover) || {}, $ = (t) => t && `color-mix(in srgb, ${t} 80%, transparent)`, C = {
28
+ "--text-color": e.textColor,
29
+ "--background-color": e.backgroundColor,
30
+ "--background-image": e.backgroundImage,
31
+ "--radius-size": (u = e.border) == null ? void 0 : u.radius,
32
+ "--button-shadow": `0 0 0 ${(l = e.border) == null ? void 0 : l.size} ${(s = e.border) == null ? void 0 : s.color} inset, ${e.boxShadow}`,
33
+ "--hover-text-color": o.textColor || e.textColor,
34
+ "--hover-background-color": o.backgroundColor || $(e.backgroundColor),
35
+ "--hover-background-image": o.backgroundImage || e.backgroundImage,
36
+ "--hover-radius-size": ((c = o.border) == null ? void 0 : c.radius) || ((h = e.border) == null ? void 0 : h.radius),
37
+ "--hover-button-shadow": `0 0 0 ${((f = o.border) == null ? void 0 : f.size) || ((x = e.border) == null ? void 0 : x.size)} ${((m = o.border) == null ? void 0 : m.color) || ((g = e.border) == null ? void 0 : g.color)} inset, ${o.boxShadow || e.boxShadow}`,
38
+ "--duration": `${p}s`
39
39
  };
40
- return /* @__PURE__ */ z(
40
+ return /* @__PURE__ */ j(
41
41
  "button",
42
42
  {
43
43
  type: v,
44
- disabled: a || d,
45
- onClick: f,
44
+ disabled: n,
45
+ "aria-disabled": n || a || void 0,
46
+ "aria-busy": a || void 0,
47
+ onClick: (t) => {
48
+ if (a) {
49
+ t.preventDefault();
50
+ return;
51
+ }
52
+ b == null || b(t);
53
+ },
46
54
  style: C,
47
- className: "relative inline-flex w-max cursor-pointer items-center justify-center rounded-(--radius-size) border-none bg-(--background-color) px-8 py-4 leading-none text-(--text-color) shadow-(--button-shadow) transition-all duration-(--duration) enabled:hover:rounded-(--hover-radius-size) enabled:hover:bg-(--hover-background-color) enabled:hover:text-(--hover-text-color) enabled:hover:shadow-(--hover-button-shadow) disabled:pointer-events-none disabled:before:pointer-events-auto disabled:before:absolute disabled:before:inset-0 disabled:before:cursor-not-allowed disabled:before:content-['']",
55
+ className: "relative inline-flex w-max cursor-pointer items-center justify-center rounded-(--radius-size) border-none bg-(--background-color) px-8 py-4 leading-none text-(--text-color) shadow-(--button-shadow) transition-all duration-(--duration) enabled:hover:rounded-(--hover-radius-size) enabled:hover:bg-(--hover-background-color) enabled:hover:text-(--hover-text-color) enabled:hover:shadow-(--hover-button-shadow) disabled:pointer-events-none disabled:before:pointer-events-auto disabled:before:absolute disabled:before:inset-0 disabled:before:cursor-not-allowed disabled:before:content-[''] aria-disabled:pointer-events-none aria-disabled:before:pointer-events-auto aria-disabled:before:absolute aria-disabled:before:inset-0 aria-disabled:before:cursor-not-allowed aria-disabled:before:content-['']",
48
56
  children: [
49
- d && /* @__PURE__ */ t("span", { className: "absolute inset-0 m-auto flex max-h-[calc(100%-1.5rem)] items-center justify-center fill-current text-current [&_*]:max-h-full [&_*]:fill-current", children: k ?? /* @__PURE__ */ t($, {}) }),
50
- /* @__PURE__ */ t("span", { className: d ? "invisible" : void 0, children: p })
57
+ a && /* @__PURE__ */ i(
58
+ "span",
59
+ {
60
+ "aria-hidden": "true",
61
+ className: "absolute inset-0 m-auto flex max-h-[calc(100%-1.5rem)] items-center justify-center fill-current text-current [&_*]:max-h-full [&_*]:fill-current",
62
+ children: w ?? /* @__PURE__ */ i(I, {})
63
+ }
64
+ ),
65
+ /* @__PURE__ */ i("span", { className: a ? "opacity-0" : void 0, children: k })
51
66
  ]
52
67
  }
53
68
  );
54
69
  }
55
70
  export {
56
- B as BasicButton
71
+ O as BasicButton
57
72
  };
@@ -1,88 +1,105 @@
1
1
  "use client";
2
- import { jsxs as f, jsx as a } from "react/jsx-runtime";
3
- import { useState as v, useRef as p, useImperativeHandle as I, useEffect as b } from "react";
4
- import { KeyboardArrowDownIcon as N } from "./icons/KeyboardArrowDownIcon.js";
5
- import { COLOR as h } from "@geckou/ui-core";
6
- function L({
7
- isHiddenArrow: g = !1,
8
- contentAlignment: i = "left",
9
- isDisabled: s = !1,
10
- contentsWidth: x = "auto",
11
- trigger: w,
12
- contents: o,
13
- ref: C
2
+ import { jsxs as b, jsx as l } from "react/jsx-runtime";
3
+ import { useState as g, useRef as i, useId as H, useImperativeHandle as N, useEffect as h } from "react";
4
+ import { KeyboardArrowDownIcon as S } from "./icons/KeyboardArrowDownIcon.js";
5
+ import { COLOR as w } from "@geckou/ui-core";
6
+ function K({
7
+ isHiddenArrow: x = !1,
8
+ contentAlignment: c = "left",
9
+ isDisabled: a = !1,
10
+ contentsWidth: C = "auto",
11
+ trigger: y,
12
+ contents: t,
13
+ ref: k
14
14
  }) {
15
- const [t, c] = v(!1), [z, O] = v(0), u = p(null), d = p(null), k = () => c((e) => !e), l = () => c(!1);
16
- I(C, () => ({
17
- isContentsOpened: () => t,
18
- close: l
19
- })), b(() => {
20
- const e = (r) => {
21
- const n = u.current;
22
- n && !n.contains(r.target) && l();
15
+ const [n, u] = g(!1), [z, I] = g(0), d = i(null), f = i(null), m = i(null), p = `${H()}-panel`, O = () => u((e) => !e), r = () => u(!1), R = () => {
16
+ var e;
17
+ r(), (e = m.current) == null || e.focus();
18
+ };
19
+ N(k, () => ({
20
+ isContentsOpened: () => n,
21
+ close: r
22
+ })), h(() => {
23
+ const e = (s) => {
24
+ const o = d.current;
25
+ o && !o.contains(s.target) && r();
23
26
  };
24
27
  return document.addEventListener("pointerdown", e), () => document.removeEventListener("pointerdown", e);
25
- }, []), b(() => {
26
- const e = d.current;
28
+ }, []), h(() => {
29
+ const e = f.current;
27
30
  if (!e)
28
31
  return;
29
- const r = () => O(e.clientHeight);
30
- r();
31
- const n = new ResizeObserver(r);
32
- return n.observe(e), () => n.disconnect();
33
- }, [!!o]);
34
- const m = s || !o, y = {
35
- "--trigger-color": m ? h.black : h.blue
36
- }, H = {
37
- boxShadow: t ? "0 0 6px #33333333" : "none",
38
- blockSize: t ? `${z}px` : 0,
39
- inlineSize: x,
40
- right: i === "right" ? "calc(var(--bv, 0.375rem) * -0.5)" : "auto",
41
- left: i === "left" ? "calc(var(--bv, 0.375rem) * -0.5)" : "auto",
32
+ const s = () => I(e.clientHeight);
33
+ s();
34
+ const o = new ResizeObserver(s);
35
+ return o.observe(e), () => o.disconnect();
36
+ }, [!!t]);
37
+ const v = a || !t, D = {
38
+ "--trigger-color": v ? w.black : w.blue
39
+ }, E = {
40
+ boxShadow: n ? "0 0 6px #33333333" : "none",
41
+ blockSize: n ? `${z}px` : 0,
42
+ inlineSize: C,
43
+ right: c === "right" ? "calc(var(--bv, 0.375rem) * -0.5)" : "auto",
44
+ left: c === "left" ? "calc(var(--bv, 0.375rem) * -0.5)" : "auto",
42
45
  zIndex: 2
43
46
  };
44
- return /* @__PURE__ */ f("div", { ref: u, className: "relative", children: [
45
- /* @__PURE__ */ f(
46
- "button",
47
- {
48
- type: "button",
49
- disabled: s,
50
- "aria-expanded": t,
51
- style: { ...y, cursor: m ? "auto" : "pointer" },
52
- className: "flex size-full items-center gap-[var(--sp-small,0.375rem)] text-(--trigger-color)",
53
- onClick: (e) => {
54
- e.preventDefault(), k();
55
- },
56
- children: [
57
- w,
58
- !(g || !o || s) && /* @__PURE__ */ a(
59
- N,
60
- {
61
- className: `size-[var(--icon-small,0.9375rem)] flex-none transition-all duration-100 ${t ? "rotate-180" : ""}`
62
- }
63
- )
64
- ]
65
- }
66
- ),
67
- o && /* @__PURE__ */ a(
68
- "div",
69
- {
70
- style: H,
71
- inert: !t,
72
- className: "absolute top-[calc(100%-var(--sp-min,0.1875rem))] cursor-pointer overflow-hidden rounded-[var(--radius-small,0.1875rem)] transition-[block-size] duration-100",
73
- children: /* @__PURE__ */ a(
47
+ return /* @__PURE__ */ b(
48
+ "div",
49
+ {
50
+ ref: d,
51
+ className: "relative",
52
+ onKeyDown: (e) => {
53
+ e.key !== "Escape" || !n || (e.preventDefault(), R());
54
+ },
55
+ children: [
56
+ /* @__PURE__ */ b(
57
+ "button",
58
+ {
59
+ ref: m,
60
+ type: "button",
61
+ disabled: a,
62
+ "aria-expanded": n,
63
+ "aria-haspopup": t ? !0 : void 0,
64
+ "aria-controls": t ? p : void 0,
65
+ style: { ...D, cursor: v ? "auto" : "pointer" },
66
+ className: "flex size-full items-center gap-[var(--sp-small,0.375rem)] text-(--trigger-color)",
67
+ onClick: (e) => {
68
+ e.preventDefault(), O();
69
+ },
70
+ children: [
71
+ y,
72
+ !(x || !t || a) && /* @__PURE__ */ l(
73
+ S,
74
+ {
75
+ className: `size-[var(--icon-small,0.9375rem)] flex-none transition-all duration-100 ${n ? "rotate-180" : ""}`
76
+ }
77
+ )
78
+ ]
79
+ }
80
+ ),
81
+ t && /* @__PURE__ */ l(
74
82
  "div",
75
83
  {
76
- ref: d,
77
- className: "max-h-[calc(var(--bv,0.375rem)*56)] min-w-[calc(var(--bv,0.375rem)*20)] overflow-auto bg-white",
78
- onClick: l,
79
- children: o
84
+ id: p,
85
+ style: E,
86
+ inert: !n,
87
+ className: "absolute top-[calc(100%-var(--sp-min,0.1875rem))] cursor-pointer overflow-hidden rounded-[var(--radius-small,0.1875rem)] transition-[block-size] duration-100",
88
+ children: /* @__PURE__ */ l(
89
+ "div",
90
+ {
91
+ ref: f,
92
+ className: "max-h-[calc(var(--bv,0.375rem)*56)] min-w-[calc(var(--bv,0.375rem)*20)] overflow-auto bg-white",
93
+ onClick: r,
94
+ children: t
95
+ }
96
+ )
80
97
  }
81
98
  )
82
- }
83
- )
84
- ] });
99
+ ]
100
+ }
101
+ );
85
102
  }
86
103
  export {
87
- L as DropdownUi
104
+ K as DropdownUi
88
105
  };
@@ -1,9 +1,11 @@
1
1
  type Props = {
2
+ /** aria-describedby から参照させたいときに渡す */
3
+ id?: string;
2
4
  errorMessages?: string[];
3
5
  cssStyle?: {
4
6
  textColor?: string;
5
7
  backgroundColor?: string;
6
8
  };
7
9
  };
8
- export declare function ErrorMessage({ errorMessages, cssStyle }: Props): import("react").JSX.Element | null;
10
+ export declare function ErrorMessage({ id, errorMessages, cssStyle }: Props): import("react").JSX.Element | null;
9
11
  export {};
@@ -1,22 +1,23 @@
1
1
  import { jsx as e } from "react/jsx-runtime";
2
2
  import { COLOR as n } from "@geckou/ui-core";
3
- function m({ errorMessages: o, cssStyle: r }) {
3
+ function c({ id: t, errorMessages: o, cssStyle: r }) {
4
4
  if (!o || !o.length)
5
5
  return null;
6
- const t = {
6
+ const b = {
7
7
  "--error-text-color": (r == null ? void 0 : r.textColor) || n.white,
8
8
  "--error-background-color": (r == null ? void 0 : r.backgroundColor) || n.red
9
9
  };
10
10
  return /* @__PURE__ */ e(
11
11
  "div",
12
12
  {
13
+ id: t,
13
14
  role: "alert",
14
15
  className: "absolute top-[calc(100%+0.25rem)] flex w-max flex-col gap-2 rounded-sm bg-(--error-background-color) px-3 py-2 text-sm leading-none text-(--error-text-color) shadow-[0_0_0.5rem_0.25rem_#33333322] before:absolute before:bottom-[calc(100%-1px)] before:left-[0.85rem] before:block before:h-[0.53rem] before:w-[0.85rem] before:bg-(--error-background-color) before:content-[''] before:[clip-path:polygon(50%_0%,100%_100%,0%_100%)]",
15
- style: t,
16
- children: o.map((b, l) => /* @__PURE__ */ e("span", { children: b }, l))
16
+ style: b,
17
+ children: o.map((l, a) => /* @__PURE__ */ e("span", { children: l }, a))
17
18
  }
18
19
  );
19
20
  }
20
21
  export {
21
- m as ErrorMessage
22
+ c as ErrorMessage
22
23
  };
@@ -1,25 +1,37 @@
1
1
  import { jsx as c, jsxs as r } from "react/jsx-runtime";
2
2
  function i() {
3
- return /* @__PURE__ */ c("svg", { viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ r("g", { children: [
4
- /* @__PURE__ */ c("circle", { cx: "12", cy: "2.5", r: "1.5", opacity: ".14" }),
5
- /* @__PURE__ */ c("circle", { cx: "16.75", cy: "3.77", r: "1.5", opacity: ".29" }),
6
- /* @__PURE__ */ c("circle", { cx: "20.23", cy: "7.25", r: "1.5", opacity: ".43" }),
7
- /* @__PURE__ */ c("circle", { cx: "21.50", cy: "12.00", r: "1.5", opacity: ".57" }),
8
- /* @__PURE__ */ c("circle", { cx: "20.23", cy: "16.75", r: "1.5", opacity: ".71" }),
9
- /* @__PURE__ */ c("circle", { cx: "16.75", cy: "20.23", r: "1.5", opacity: ".86" }),
10
- /* @__PURE__ */ c("circle", { cx: "12", cy: "21.5", r: "1.5" }),
3
+ return (
4
+ // 装飾。読み上げ対象にしない(状態は呼び出し側の aria-busy で伝える)
11
5
  /* @__PURE__ */ c(
12
- "animateTransform",
6
+ "svg",
13
7
  {
14
- attributeName: "transform",
15
- type: "rotate",
16
- calcMode: "discrete",
17
- dur: "0.75s",
18
- values: "0 12 12;30 12 12;60 12 12;90 12 12;120 12 12;150 12 12;180 12 12;210 12 12;240 12 12;270 12 12;300 12 12;330 12 12;360 12 12",
19
- repeatCount: "indefinite"
8
+ viewBox: "0 0 24 24",
9
+ xmlns: "http://www.w3.org/2000/svg",
10
+ "aria-hidden": "true",
11
+ focusable: "false",
12
+ children: /* @__PURE__ */ r("g", { children: [
13
+ /* @__PURE__ */ c("circle", { cx: "12", cy: "2.5", r: "1.5", opacity: ".14" }),
14
+ /* @__PURE__ */ c("circle", { cx: "16.75", cy: "3.77", r: "1.5", opacity: ".29" }),
15
+ /* @__PURE__ */ c("circle", { cx: "20.23", cy: "7.25", r: "1.5", opacity: ".43" }),
16
+ /* @__PURE__ */ c("circle", { cx: "21.50", cy: "12.00", r: "1.5", opacity: ".57" }),
17
+ /* @__PURE__ */ c("circle", { cx: "20.23", cy: "16.75", r: "1.5", opacity: ".71" }),
18
+ /* @__PURE__ */ c("circle", { cx: "16.75", cy: "20.23", r: "1.5", opacity: ".86" }),
19
+ /* @__PURE__ */ c("circle", { cx: "12", cy: "21.5", r: "1.5" }),
20
+ /* @__PURE__ */ c(
21
+ "animateTransform",
22
+ {
23
+ attributeName: "transform",
24
+ type: "rotate",
25
+ calcMode: "discrete",
26
+ dur: "0.75s",
27
+ values: "0 12 12;30 12 12;60 12 12;90 12 12;120 12 12;150 12 12;180 12 12;210 12 12;240 12 12;270 12 12;300 12 12;330 12 12;360 12 12",
28
+ repeatCount: "indefinite"
29
+ }
30
+ )
31
+ ] })
20
32
  }
21
33
  )
22
- ] }) });
34
+ );
23
35
  }
24
36
  export {
25
37
  i as LoadingSpinner
@@ -1,71 +1,75 @@
1
1
  "use client";
2
- import { jsx as t, jsxs as b } from "react/jsx-runtime";
3
- import { useId as g, useRef as i, useEffect as u } from "react";
4
- import { createScrollLock as y, createModalLayer as w, handleTabKey as h } from "@geckou/ui-core";
5
- const k = {
2
+ import { jsx as a, jsxs as b } from "react/jsx-runtime";
3
+ import { useId as y, useRef as o, useEffect as i } from "react";
4
+ import { createScrollLock as w, createModalLayer as h, handleTabKey as k } from "@geckou/ui-core";
5
+ const E = {
6
6
  small: "max-w-[var(--mobile-lower-width,430px)]",
7
7
  medium: "max-w-[var(--desktop-lower-width,1025px)]",
8
8
  large: "max-w-[var(--contents-max-width,1440px)]"
9
9
  };
10
- function E() {
11
- return /* @__PURE__ */ t(
10
+ function L() {
11
+ return /* @__PURE__ */ a(
12
12
  "svg",
13
13
  {
14
14
  xmlns: "http://www.w3.org/2000/svg",
15
15
  viewBox: "0 -960 960 960",
16
16
  fill: "currentColor",
17
- children: /* @__PURE__ */ t("path", { d: "m256-200-56-56 224-224-224-224 56-56 224 224 224-224 56 56-224 224 224 224-56 56-224-224-224 224Z" })
17
+ children: /* @__PURE__ */ a("path", { d: "m256-200-56-56 224-224-224-224 56-56 224 224 224-224 56 56-224 224 224 224-56 56-224-224-224 224Z" })
18
18
  }
19
19
  );
20
20
  }
21
21
  function _({
22
22
  isShown: r,
23
- size: p = "medium",
24
- onClose: o,
25
- header: n,
23
+ size: f = "medium",
24
+ onClose: n,
25
+ header: u,
26
26
  footer: d,
27
- children: f,
28
- ariaLabel: v
27
+ children: v,
28
+ ariaLabel: x
29
29
  }) {
30
- const c = g(), m = i(null), x = i(y());
31
- u(() => {
32
- const e = x.current;
30
+ const s = y(), m = o(null), g = o(w());
31
+ i(() => {
32
+ const e = g.current;
33
33
  return e.toggle(r), () => e.release();
34
34
  }, [r]);
35
- const s = i(w());
36
- u(() => {
37
- const e = s.current;
35
+ const p = o(h());
36
+ i(() => {
37
+ const e = p.current;
38
38
  return e.toggle(r, m.current), () => e.release();
39
39
  }, [r]);
40
- const l = i(null);
41
- return u(() => {
42
- var e, a;
43
- if (r) {
44
- l.current ?? (l.current = document.activeElement), (e = m.current) == null || e.focus();
45
- return;
46
- }
47
- (a = l.current) == null || a.focus(), l.current = null;
48
- }, [r]), u(() => {
40
+ const l = o(null), c = o(!1);
41
+ return i(() => {
42
+ var e;
43
+ if (r)
44
+ return l.current ?? (l.current = document.activeElement), (e = m.current) == null || e.focus(), () => {
45
+ var t;
46
+ (t = l.current) == null || t.focus(), l.current = null;
47
+ };
48
+ }, [r]), i(() => {
49
49
  if (!r)
50
50
  return;
51
- const e = (a) => {
52
- if (s.current.isTopmost() && !a.defaultPrevented) {
53
- if (a.key === "Escape") {
54
- a.preventDefault(), o();
51
+ const e = (t) => {
52
+ if (p.current.isTopmost() && !t.defaultPrevented) {
53
+ if (t.key === "Escape") {
54
+ t.preventDefault(), n();
55
55
  return;
56
56
  }
57
- h(m.current, a, document.activeElement);
57
+ k(m.current, t, document.activeElement);
58
58
  }
59
59
  };
60
60
  return document.addEventListener("keydown", e), () => document.removeEventListener("keydown", e);
61
- }, [r, o]), /* @__PURE__ */ t(
61
+ }, [r, n]), /* @__PURE__ */ a(
62
62
  "div",
63
63
  {
64
64
  className: `fixed top-0 left-0 z-50 flex h-dvh w-dvw cursor-pointer items-center justify-center overflow-hidden bg-[#33333380] p-[var(--sp-larger,3rem)] backdrop-blur-sm transition-opacity duration-100 max-md:px-[var(--sp-large,1.5rem)] ${r ? "pointer-events-auto opacity-100" : "pointer-events-none opacity-0"}`,
65
65
  "aria-hidden": !r,
66
66
  inert: !r,
67
+ onPointerDown: (e) => {
68
+ c.current = e.target === e.currentTarget;
69
+ },
67
70
  onClick: (e) => {
68
- e.target === e.currentTarget && o();
71
+ const t = c.current;
72
+ c.current = !1, t && e.target === e.currentTarget && n();
69
73
  },
70
74
  children: /* @__PURE__ */ b(
71
75
  "div",
@@ -73,29 +77,29 @@ function _({
73
77
  ref: m,
74
78
  role: "dialog",
75
79
  "aria-modal": "true",
76
- "aria-labelledby": n ? c : void 0,
77
- "aria-label": n ? void 0 : v ?? "ダイアログ",
80
+ "aria-labelledby": u ? s : void 0,
81
+ "aria-label": u ? void 0 : x ?? "ダイアログ",
78
82
  tabIndex: -1,
79
- className: `relative flex max-h-full w-full cursor-auto flex-col rounded-[var(--radius-small,0.1875rem)] bg-white drop-shadow-[0_0_6px_#33333355] ${k[p]}`,
83
+ className: `relative flex max-h-full w-full cursor-auto flex-col rounded-[var(--radius-small,0.1875rem)] bg-white drop-shadow-[0_0_6px_#33333355] ${E[f]}`,
80
84
  children: [
81
- n && /* @__PURE__ */ t(
85
+ u && /* @__PURE__ */ a(
82
86
  "header",
83
87
  {
84
- id: c,
88
+ id: s,
85
89
  className: "border-b border-[#eee] px-[var(--sp-large,1.5rem)] py-[var(--sp-medium,0.75rem)] max-md:p-[var(--sp-medium,0.75rem)] [&>h2]:font-bold [&>h2]:text-[var(--fs-large,0.875rem)]",
86
- children: n
90
+ children: u
87
91
  }
88
92
  ),
89
- /* @__PURE__ */ t("div", { className: "flex-auto overflow-auto p-[var(--sp-large,1.5rem)] max-md:p-[var(--sp-medium,0.75rem)]", children: f }),
90
- d && /* @__PURE__ */ t("footer", { className: "flex justify-center gap-[var(--sp-large,1.5rem)] border-t border-[#eee] px-[var(--sp-large,1.5rem)] py-[var(--sp-medium,0.75rem)] max-md:gap-[var(--sp-medium,0.75rem)] max-md:p-[var(--sp-medium,0.75rem)] max-md:[&>*]:flex-auto", children: d }),
91
- /* @__PURE__ */ t(
93
+ /* @__PURE__ */ a("div", { className: "flex-auto overflow-auto p-[var(--sp-large,1.5rem)] max-md:p-[var(--sp-medium,0.75rem)]", children: v }),
94
+ d && /* @__PURE__ */ a("footer", { className: "flex justify-center gap-[var(--sp-large,1.5rem)] border-t border-[#eee] px-[var(--sp-large,1.5rem)] py-[var(--sp-medium,0.75rem)] max-md:gap-[var(--sp-medium,0.75rem)] max-md:p-[var(--sp-medium,0.75rem)] max-md:[&>*]:flex-auto", children: d }),
95
+ /* @__PURE__ */ a(
92
96
  "button",
93
97
  {
94
98
  type: "button",
95
99
  "aria-label": "閉じる",
96
100
  className: "absolute bottom-full left-full size-[var(--icon-medium,1.125rem)] cursor-pointer leading-none text-white focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white [&>*]:size-full",
97
- onClick: o,
98
- children: /* @__PURE__ */ t(E, {})
101
+ onClick: n,
102
+ children: /* @__PURE__ */ a(L, {})
99
103
  }
100
104
  )
101
105
  ]
@@ -1,28 +1,28 @@
1
1
  "use client";
2
2
  import { jsx as i } from "react/jsx-runtime";
3
- import { useState as o, useRef as c, useEffect as l, useImperativeHandle as f } from "react";
3
+ import { useState as n, useRef as c, useEffect as l, useImperativeHandle as f } from "react";
4
4
  import { createPortal as p } from "react-dom";
5
5
  import { COLOR as d } from "@geckou/ui-core";
6
6
  const x = {
7
7
  left: "left-[max(calc((100vw-64rem)/2),1rem)]",
8
8
  right: "right-[max(calc((100vw-64rem)/2),1rem)]",
9
9
  center: "inset-0 m-auto"
10
- }, S = {
10
+ }, h = {
11
11
  top: "top-14",
12
12
  bottom: "bottom-4",
13
13
  center: "inset-0 m-auto"
14
14
  };
15
15
  function y({
16
16
  position: t = { x: "right", y: "top" },
17
- ref: n,
18
- children: m
17
+ ref: m,
18
+ children: s
19
19
  }) {
20
- const [s, r] = o(!1), [u, a] = o(!1), e = c(null);
20
+ const [r, o] = n(!1), [u, a] = n(!1), e = c(null);
21
21
  return l(() => (a(!0), () => {
22
22
  e.current && clearTimeout(e.current);
23
- }), []), f(n, () => ({
23
+ }), []), f(m, () => ({
24
24
  showPopup: () => {
25
- r(!0), e.current && clearTimeout(e.current), e.current = setTimeout(() => r(!1), 3e3);
25
+ o(!0), e.current && clearTimeout(e.current), e.current = setTimeout(() => o(!1), 3e3);
26
26
  }
27
27
  })), u ? p(
28
28
  /* @__PURE__ */ i(
@@ -30,8 +30,8 @@ function y({
30
30
  {
31
31
  role: "status",
32
32
  style: { borderColor: d.blue },
33
- className: `pointer-events-none fixed z-50 h-max w-max max-w-40 rounded-[var(--radius-small,0.1875rem)] border bg-white p-[var(--sp-medium,0.75rem)] transition-[transform,opacity] duration-300 ${x[t.x]} ${S[t.y]} ${s ? "opacity-100" : "opacity-0"}`,
34
- children: m
33
+ className: `pointer-events-none fixed z-50 h-max w-max max-w-40 rounded-[var(--radius-small,0.1875rem)] border bg-white p-[var(--sp-medium,0.75rem)] transition-[transform,opacity] duration-300 ${x[t.x]} ${h[t.y]} ${r ? "opacity-100" : "opacity-0"}`,
34
+ children: r && s
35
35
  }
36
36
  ),
37
37
  document.body
@@ -8,6 +8,12 @@ type Props = {
8
8
  isRequired?: boolean;
9
9
  cssStyle?: RadioButtonStyleForEachStatus;
10
10
  isDisableAnimation?: boolean;
11
+ /**
12
+ * ラジオグループ自体のアクセシブル名。
13
+ * 可視ラベル(見出し等)があるなら ariaLabelledBy でその要素を指すこと
14
+ */
15
+ ariaLabel?: string;
16
+ ariaLabelledBy?: string;
11
17
  };
12
- export declare function RadioButtons({ value, onChange, options, name, isDisabled, isRequired, cssStyle, isDisableAnimation, }: Props): import("react").JSX.Element;
18
+ export declare function RadioButtons({ value, onChange, options, name, isDisabled, isRequired, cssStyle, isDisableAnimation, ariaLabel, ariaLabelledBy, }: Props): import("react").JSX.Element;
13
19
  export {};
@@ -1,70 +1,83 @@
1
1
  "use client";
2
- import { jsxs as f, jsx as u } from "react/jsx-runtime";
3
- import { useState as N, useRef as G, useEffect as R, useId as z } from "react";
4
- import { isEmptyValue as M, MESSAGES as V, COLOR as o } from "@geckou/ui-core";
5
- import { ErrorMessage as j } from "./ErrorMessage.js";
6
- function A({
7
- value: p,
8
- onChange: s,
9
- options: m,
10
- name: _,
2
+ import { jsxs as _, jsx as d } from "react/jsx-runtime";
3
+ import { useState as M, useRef as R, useEffect as V, useId as g } from "react";
4
+ import { isEmptyValue as j, MESSAGES as q, COLOR as o } from "@geckou/ui-core";
5
+ import { ErrorMessage as I } from "./ErrorMessage.js";
6
+ function $({
7
+ value: v,
8
+ onChange: n,
9
+ options: x,
10
+ name: h,
11
11
  isDisabled: a,
12
- isRequired: n,
12
+ isRequired: u,
13
13
  cssStyle: e,
14
- isDisableAnimation: v
14
+ isDisableAnimation: k,
15
+ ariaLabel: w,
16
+ ariaLabelledBy: i
15
17
  }) {
16
- var i, d;
17
- const r = p ?? "", [g, x] = N(!1), c = G(r);
18
- R(() => {
19
- c.current !== r && (c.current = r, x(!0));
18
+ var f, p;
19
+ const r = v ?? "", [C, E] = M(!1), s = R(r);
20
+ V(() => {
21
+ s.current !== r && (s.current = r, E(!0));
20
22
  }, [r]);
21
- const h = g && n && M(r) ? [V.required] : void 0, k = z(), w = _ ?? k, C = a ? e == null ? void 0 : e.disabled : e == null ? void 0 : e.default, l = {
23
+ const c = C && u && j(r) ? [q.required] : void 0, N = g(), G = h ?? N, b = g(), y = a ? e == null ? void 0 : e.disabled : e == null ? void 0 : e.default, l = {
22
24
  textColor: a ? o.darkGray : o.black,
23
25
  backgroundColor: a ? o.lightGray : o.white,
24
26
  border: {
25
27
  color: a ? o.darkGray : o.blue,
26
28
  size: "1px"
27
29
  },
28
- ...C ?? {}
29
- }, E = {
30
+ ...y ?? {}
31
+ }, z = {
30
32
  "--text-color": l.textColor,
31
- "--border-color": (i = l.border) == null ? void 0 : i.color,
32
- "--border-size": (d = l.border) == null ? void 0 : d.size,
33
+ "--border-color": (f = l.border) == null ? void 0 : f.color,
34
+ "--border-size": (p = l.border) == null ? void 0 : p.size,
33
35
  "--background-color": l.backgroundColor,
34
- "--duration": v ? "0s" : ".3s"
36
+ "--duration": k ? "0s" : ".3s"
35
37
  };
36
- return /* @__PURE__ */ f("div", { className: "relative flex flex-wrap items-center gap-4", children: [
37
- /* @__PURE__ */ u("style", { children: "@keyframes uiRadioPop{0%{scale:1}10%{scale:.8}50%{scale:1.2}100%{scale:1}}" }),
38
- m.map((t) => {
39
- const b = t.value === r;
40
- return /* @__PURE__ */ f(
41
- "label",
42
- {
43
- style: E,
44
- className: `relative grid cursor-pointer grid-cols-[auto_1fr] items-center gap-2 before:inline-block before:aspect-square before:w-4 before:rounded-full before:transition-all before:duration-(--duration) before:ease-linear before:content-[''] has-[input:disabled]:cursor-not-allowed has-[input:focus-visible]:outline-2 has-[input:focus-visible]:outline-offset-2 has-[input:focus-visible]:outline-(--border-color) ${b ? "text-(--text-color) before:animate-[uiRadioPop_var(--duration)_ease-out] before:bg-(--border-color) before:shadow-[0_0_0_2px_var(--background-color)_inset,0_0_0_1px_var(--border-color)]" : "text-(--border-color) before:bg-(--background-color) before:shadow-[0_0_0_1px_var(--border-color)_inset]"}`,
45
- children: [
46
- /* @__PURE__ */ u(
47
- "input",
48
- {
49
- type: "radio",
50
- name: w,
51
- value: t.value,
52
- disabled: a,
53
- required: n,
54
- checked: b,
55
- onChange: () => s == null ? void 0 : s(t.value),
56
- className: "sr-only"
57
- }
58
- ),
59
- /* @__PURE__ */ u("span", { children: t.label })
60
- ]
61
- },
62
- t.value
63
- );
64
- }),
65
- /* @__PURE__ */ u(j, { errorMessages: h })
66
- ] });
38
+ return /* @__PURE__ */ _(
39
+ "div",
40
+ {
41
+ role: "radiogroup",
42
+ "aria-label": i ? void 0 : w,
43
+ "aria-labelledby": i,
44
+ "aria-required": u || void 0,
45
+ "aria-describedby": c ? b : void 0,
46
+ className: "relative flex flex-wrap items-center gap-4",
47
+ children: [
48
+ /* @__PURE__ */ d("style", { children: "@keyframes uiRadioPop{0%{scale:1}10%{scale:.8}50%{scale:1.2}100%{scale:1}}" }),
49
+ x.map((t) => {
50
+ const m = t.value === r;
51
+ return /* @__PURE__ */ _(
52
+ "label",
53
+ {
54
+ style: z,
55
+ className: `relative grid cursor-pointer grid-cols-[auto_1fr] items-center gap-2 before:inline-block before:aspect-square before:w-4 before:rounded-full before:transition-all before:duration-(--duration) before:ease-linear before:content-[''] has-[input:disabled]:cursor-not-allowed has-[input:focus-visible]:outline-2 has-[input:focus-visible]:outline-offset-2 has-[input:focus-visible]:outline-(--border-color) ${m ? "text-(--text-color) before:animate-[uiRadioPop_var(--duration)_ease-out] before:bg-(--border-color) before:shadow-[0_0_0_2px_var(--background-color)_inset,0_0_0_1px_var(--border-color)]" : "text-(--border-color) before:bg-(--background-color) before:shadow-[0_0_0_1px_var(--border-color)_inset]"}`,
56
+ children: [
57
+ /* @__PURE__ */ d(
58
+ "input",
59
+ {
60
+ type: "radio",
61
+ name: G,
62
+ value: t.value,
63
+ disabled: a,
64
+ required: u,
65
+ checked: m,
66
+ onChange: () => n == null ? void 0 : n(t.value),
67
+ className: "sr-only"
68
+ }
69
+ ),
70
+ /* @__PURE__ */ d("span", { children: t.label })
71
+ ]
72
+ },
73
+ t.value
74
+ );
75
+ }),
76
+ /* @__PURE__ */ d(I, { id: b, errorMessages: c })
77
+ ]
78
+ }
79
+ );
67
80
  }
68
81
  export {
69
- A as RadioButtons
82
+ $ as RadioButtons
70
83
  };
@@ -1,20 +1,20 @@
1
1
  "use client";
2
2
  import { jsxs as v, jsx as i } from "react/jsx-runtime";
3
- import { useState as a, useRef as p, useImperativeHandle as E, useEffect as l } from "react";
4
- import { KeyboardArrowDownIcon as I } from "./icons/KeyboardArrowDownIcon.js";
5
- import { COLOR as N } from "@geckou/ui-core";
6
- function $({
3
+ import { useState as a, useRef as h, useId as E, useImperativeHandle as N, useEffect as l } from "react";
4
+ import { KeyboardArrowDownIcon as O } from "./icons/KeyboardArrowDownIcon.js";
5
+ import { COLOR as $ } from "@geckou/ui-core";
6
+ function S({
7
7
  isOpened: n = null,
8
- isHiddenArrow: h = !1,
9
- isDisabled: b = !1,
8
+ isHiddenArrow: b = !1,
9
+ isDisabled: g = !1,
10
10
  isDisableClickOutside: f = !1,
11
11
  duration: c = 0.3,
12
- trigger: g,
13
- children: w,
14
- ref: x
12
+ trigger: w,
13
+ children: x,
14
+ ref: C
15
15
  }) {
16
- const [t, r] = a(n || !1), [C, u] = a(n || !1), [y, k] = a(0), d = p(null), m = p(null), H = () => r((e) => !e);
17
- E(x, () => ({
16
+ const [t, r] = a(n || !1), [y, u] = a(n || !1), [I, k] = a(0), d = h(null), m = h(null), p = `${E()}-panel`, H = () => r((e) => !e);
17
+ N(C, () => ({
18
18
  isOpenedContents: () => t,
19
19
  close: () => r(!1)
20
20
  })), l(() => {
@@ -43,22 +43,23 @@ function $({
43
43
  const e = setTimeout(() => u(!0), c * 1e3);
44
44
  return () => clearTimeout(e);
45
45
  }, [t, c]);
46
- const R = { "--link-color": N.blue };
46
+ const R = { "--link-color": $.blue };
47
47
  return /* @__PURE__ */ v("div", { ref: d, style: R, children: [
48
48
  /* @__PURE__ */ v(
49
49
  "button",
50
50
  {
51
51
  type: "button",
52
- disabled: b,
52
+ disabled: g,
53
53
  "aria-expanded": t,
54
+ "aria-controls": p,
54
55
  className: "relative grid w-full cursor-pointer grid-cols-[1fr_auto] items-center justify-items-start text-(--link-color)",
55
56
  onClick: (e) => {
56
57
  e.preventDefault(), H();
57
58
  },
58
59
  children: [
59
- /* @__PURE__ */ i("span", { className: "block w-full text-left", children: g }),
60
- !h && /* @__PURE__ */ i(
61
- I,
60
+ /* @__PURE__ */ i("span", { className: "block w-full text-left", children: w }),
61
+ !b && /* @__PURE__ */ i(
62
+ O,
62
63
  {
63
64
  className: `size-[var(--icon-medium,1.125rem)] flex-none text-(--link-color) transition-all duration-100 ${t ? "rotate-180" : ""}`
64
65
  }
@@ -69,18 +70,19 @@ function $({
69
70
  /* @__PURE__ */ i(
70
71
  "div",
71
72
  {
73
+ id: p,
72
74
  style: {
73
- height: t ? `${y}px` : 0,
75
+ height: t ? `${I}px` : 0,
74
76
  transitionDuration: `${c}s`,
75
- overflow: C ? "visible" : "hidden"
77
+ overflow: y ? "visible" : "hidden"
76
78
  },
77
79
  inert: !t,
78
80
  className: "transition-[height]",
79
- children: /* @__PURE__ */ i("div", { ref: m, children: w })
81
+ children: /* @__PURE__ */ i("div", { ref: m, children: x })
80
82
  }
81
83
  )
82
84
  ] });
83
85
  }
84
86
  export {
85
- $ as SlideDownUi
87
+ S as SlideDownUi
86
88
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geckou/ui-react",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
4
4
  "description": "A set of reusable React UI components (forms) by Geckou",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -40,7 +40,7 @@
40
40
  "react-dom": "^19.0.0"
41
41
  },
42
42
  "dependencies": {
43
- "@geckou/ui-core": "^0.7.0"
43
+ "@geckou/ui-core": "^0.8.0"
44
44
  },
45
45
  "scripts": {
46
46
  "build": "vite build && node scripts/copy-assets.mjs",