@ecatchup/basercms-ui 0.1.0 → 0.1.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.
- package/README.md +30 -0
- package/dist/index.d.ts +37 -4
- package/dist/index.js +169 -163
- package/dist/style.css +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -93,6 +93,36 @@ type SelectOption = {
|
|
|
93
93
|
/>
|
|
94
94
|
```
|
|
95
95
|
|
|
96
|
+
## 入力欄のクラス・属性(既定で baserCMS の input スタイルが当たります)
|
|
97
|
+
|
|
98
|
+
`SearchSelect` の `triggerProps` / `searchInputProps`、`MultiSelectPicker` の
|
|
99
|
+
`searchInputProps` は、既定で以下の値になっています(baserCMS の
|
|
100
|
+
`.bca-textbox__input` を前提にしています)。
|
|
101
|
+
|
|
102
|
+
| prop | 既定値 |
|
|
103
|
+
| --- | --- |
|
|
104
|
+
| `triggerProps`(SearchSelect) | `{ className: 'bca-textbox__input' }` |
|
|
105
|
+
| `searchInputProps`(SearchSelect) | `{ className: 'bca-textbox__input' }` |
|
|
106
|
+
| `searchInputProps`(MultiSelectPicker) | `{ className: 'bca-textbox__input' }` |
|
|
107
|
+
|
|
108
|
+
**既定値は「置き換え」です。マージではありません。** 何か1つでも渡すと、既定値は使われず渡した内容がそのまま使われます。既定のクラス・属性が不要な場合は空オブジェクト(`{}`)を渡してください。ただしこれで置き換わるのは追加のクラス・属性だけで、部品自身の BEM クラス(`bca-search-select__trigger` 等)は常に付くため、`{}` を渡しても部品同梱の CSS は効き続けます(baserCMS の input スタイルだけ外したい場合に使えます)。`MultiSelectDialog` / `MultiSelectField` に渡した `searchInputProps` は、内部の `MultiSelectPicker` の検索欄へそのまま転送されます。`type` / `value` / `onChange` / `placeholder` / `disabled`(`triggerProps` では加えて `role` / `tabIndex` / `onClick` / `onKeyDown` / ARIA 属性)は部品側が制御するため渡せません。
|
|
109
|
+
|
|
110
|
+
```tsx
|
|
111
|
+
// baserCMS 以外の見た目にしたい場合
|
|
112
|
+
<SearchSelect
|
|
113
|
+
triggerProps={{ className: 'my-trigger' }}
|
|
114
|
+
searchInputProps={{ className: 'my-search-input' }}
|
|
115
|
+
...
|
|
116
|
+
/>
|
|
117
|
+
|
|
118
|
+
// 何も付けたくない場合
|
|
119
|
+
<SearchSelect
|
|
120
|
+
triggerProps={{}}
|
|
121
|
+
searchInputProps={{}}
|
|
122
|
+
...
|
|
123
|
+
/>
|
|
124
|
+
```
|
|
125
|
+
|
|
96
126
|
## フォーム連携
|
|
97
127
|
|
|
98
128
|
`name` を渡すと hidden input を描画します。
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { ButtonHTMLAttributes } from 'react';
|
|
2
|
+
import { HTMLAttributes } from 'react';
|
|
3
|
+
import { InputHTMLAttributes } from 'react';
|
|
2
4
|
import { JSX } from 'react';
|
|
3
5
|
|
|
4
6
|
/**
|
|
@@ -13,9 +15,20 @@ export declare type ButtonPassthroughProps = Omit<ButtonHTMLAttributes<HTMLButto
|
|
|
13
15
|
[key: `data-${string}`]: string | number | boolean | undefined;
|
|
14
16
|
};
|
|
15
17
|
|
|
18
|
+
/**
|
|
19
|
+
* 部品内の検索欄(input)へ、利用側から任意のクラス・data 属性等を渡すための型。
|
|
20
|
+
*
|
|
21
|
+
* `type` / `value` / `onChange` / `placeholder` / `disabled` は部品側が制御する
|
|
22
|
+
* ため除外している。baserCMS 等、既存のデザインシステムの input スタイル
|
|
23
|
+
* (クラス+ data 属性)を当てたい場合に使う。
|
|
24
|
+
*/
|
|
25
|
+
export declare type InputPassthroughProps = Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'value' | 'onChange' | 'placeholder' | 'disabled'> & {
|
|
26
|
+
[key: `data-${string}`]: string | number | boolean | undefined;
|
|
27
|
+
};
|
|
28
|
+
|
|
16
29
|
export declare const MultiSelectDialog: ({ open, title, options, initialValue, onSubmit, onCancel, submitLabel, cancelLabel, requireSelection, submitButtonProps, cancelButtonProps, className, ...pickerProps }: MultiSelectDialogProps) => JSX.Element | null;
|
|
17
30
|
|
|
18
|
-
export declare type MultiSelectDialogProps = Pick<MultiSelectPickerProps, 'options' | 'searchPlaceholder' | 'noResultsText' | 'listHeight' | 'maxSelected' | 'className'> & {
|
|
31
|
+
export declare type MultiSelectDialogProps = Pick<MultiSelectPickerProps, 'options' | 'searchPlaceholder' | 'noResultsText' | 'listHeight' | 'maxSelected' | 'className' | 'searchInputProps'> & {
|
|
19
32
|
open: boolean;
|
|
20
33
|
title?: string;
|
|
21
34
|
initialValue?: SelectOption[];
|
|
@@ -37,7 +50,7 @@ export declare type MultiSelectDialogProps = Pick<MultiSelectPickerProps, 'optio
|
|
|
37
50
|
*/
|
|
38
51
|
export declare const MultiSelectField: ({ options, value, onChange, name, addButtonLabel, dialogTitle, emptyText, disabled, className, maxSelected, submitLabel, cancelLabel, requireSelection, addButtonProps, submitButtonProps, cancelButtonProps, ...pickerProps }: MultiSelectFieldProps) => JSX.Element;
|
|
39
52
|
|
|
40
|
-
export declare type MultiSelectFieldProps = Pick<MultiSelectPickerProps, 'options' | 'searchPlaceholder' | 'noResultsText' | 'listHeight' | 'maxSelected' | 'className'> & {
|
|
53
|
+
export declare type MultiSelectFieldProps = Pick<MultiSelectPickerProps, 'options' | 'searchPlaceholder' | 'noResultsText' | 'listHeight' | 'maxSelected' | 'className' | 'searchInputProps'> & {
|
|
41
54
|
value: SelectOption[];
|
|
42
55
|
onChange: (selected: SelectOption[]) => void;
|
|
43
56
|
/** 指定時、選択件数分の hidden input を描画する */
|
|
@@ -57,7 +70,7 @@ export declare type MultiSelectFieldProps = Pick<MultiSelectPickerProps, 'option
|
|
|
57
70
|
cancelButtonProps?: ButtonPassthroughProps;
|
|
58
71
|
};
|
|
59
72
|
|
|
60
|
-
export declare const MultiSelectPicker: ({ options, value, onChange, searchPlaceholder, noResultsText, listHeight, maxSelected, className, }: MultiSelectPickerProps) => JSX.Element;
|
|
73
|
+
export declare const MultiSelectPicker: ({ options, value, onChange, searchPlaceholder, noResultsText, listHeight, maxSelected, className, searchInputProps, }: MultiSelectPickerProps) => JSX.Element;
|
|
61
74
|
|
|
62
75
|
export declare type MultiSelectPickerProps = {
|
|
63
76
|
options: SelectOption[];
|
|
@@ -68,9 +81,11 @@ export declare type MultiSelectPickerProps = {
|
|
|
68
81
|
listHeight?: number | string;
|
|
69
82
|
maxSelected?: number;
|
|
70
83
|
className?: string;
|
|
84
|
+
/** 検索欄へ渡す任意のクラス・属性(baserCMS 等の input スタイル用) */
|
|
85
|
+
searchInputProps?: InputPassthroughProps;
|
|
71
86
|
};
|
|
72
87
|
|
|
73
|
-
export declare const SearchSelect: ({ options, value, onChange, name, placeholder, emptyLabel, clearable, disabled, noResultsText, searchPlaceholder, dropdownPlacement, className, }: SearchSelectProps) => JSX.Element;
|
|
88
|
+
export declare const SearchSelect: ({ options, value, onChange, name, placeholder, emptyLabel, clearable, disabled, noResultsText, searchPlaceholder, dropdownPlacement, className, triggerProps, searchInputProps, }: SearchSelectProps) => JSX.Element;
|
|
74
89
|
|
|
75
90
|
export declare type SearchSelectProps = {
|
|
76
91
|
options: SelectOption[];
|
|
@@ -88,6 +103,10 @@ export declare type SearchSelectProps = {
|
|
|
88
103
|
searchPlaceholder?: string;
|
|
89
104
|
dropdownPlacement?: 'auto' | 'top' | 'bottom';
|
|
90
105
|
className?: string;
|
|
106
|
+
/** トリガー(閉じた状態の表示部)へ渡す任意のクラス・属性(baserCMS 等の input スタイル用) */
|
|
107
|
+
triggerProps?: TriggerPassthroughProps;
|
|
108
|
+
/** 検索欄へ渡す任意のクラス・属性(baserCMS 等の input スタイル用) */
|
|
109
|
+
searchInputProps?: InputPassthroughProps;
|
|
91
110
|
};
|
|
92
111
|
|
|
93
112
|
/**
|
|
@@ -103,4 +122,18 @@ export declare type SelectOption = {
|
|
|
103
122
|
disabled?: boolean;
|
|
104
123
|
};
|
|
105
124
|
|
|
125
|
+
/**
|
|
126
|
+
* `SearchSelect` のトリガー(閉じた状態の div)へ、利用側から任意のクラス・
|
|
127
|
+
* data 属性等を渡すための型。
|
|
128
|
+
*
|
|
129
|
+
* `role` / `tabIndex` / `onClick` / `onKeyDown` / `aria-expanded` /
|
|
130
|
+
* `aria-haspopup` / `aria-disabled` / `aria-controls` / `aria-activedescendant`
|
|
131
|
+
* は部品側が制御する(開閉のハンドラ、ARIA 属性の同期)ため除外している。
|
|
132
|
+
* baserCMS 等、既存のデザインシステムの input スタイル(クラス+ data 属性)を
|
|
133
|
+
* 当てたい場合に使う。
|
|
134
|
+
*/
|
|
135
|
+
export declare type TriggerPassthroughProps = Omit<HTMLAttributes<HTMLDivElement>, 'role' | 'tabIndex' | 'onClick' | 'onKeyDown' | 'aria-expanded' | 'aria-haspopup' | 'aria-disabled' | 'aria-controls' | 'aria-activedescendant'> & {
|
|
136
|
+
[key: `data-${string}`]: string | number | boolean | undefined;
|
|
137
|
+
};
|
|
138
|
+
|
|
106
139
|
export { }
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { jsxs as
|
|
2
|
-
import { useMemo as
|
|
3
|
-
const
|
|
1
|
+
import { jsxs as N, jsx as c } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo as q, useEffect as C, useState as D, useLayoutEffect as Q, useRef as L, useId as H } from "react";
|
|
3
|
+
const Y = (t, e) => {
|
|
4
4
|
const s = e.trim().toLowerCase();
|
|
5
5
|
return s ? t.filter(
|
|
6
6
|
(i) => i.label.toLowerCase().includes(s) || (i.sublabel ?? "").toLowerCase().includes(s)
|
|
7
7
|
) : t;
|
|
8
|
-
},
|
|
8
|
+
}, K = (t, e) => q(() => Y(t, e), [t, e]), z = (t, e, s) => {
|
|
9
9
|
C(() => {
|
|
10
10
|
if (!e) return;
|
|
11
11
|
const i = (n) => {
|
|
@@ -13,83 +13,86 @@ const q = (t, e) => {
|
|
|
13
13
|
};
|
|
14
14
|
return document.addEventListener("mousedown", i), () => document.removeEventListener("mousedown", i);
|
|
15
15
|
}, [t, e, s]);
|
|
16
|
-
},
|
|
17
|
-
const [n,
|
|
18
|
-
return
|
|
16
|
+
}, B = (t, e, s, i = 240) => {
|
|
17
|
+
const [n, o] = D(s === "top" ? "top" : "bottom");
|
|
18
|
+
return Q(() => {
|
|
19
19
|
if (!e) return;
|
|
20
20
|
if (s !== "auto") {
|
|
21
|
-
|
|
21
|
+
o(s);
|
|
22
22
|
return;
|
|
23
23
|
}
|
|
24
|
-
const
|
|
25
|
-
if (!
|
|
26
|
-
const
|
|
27
|
-
|
|
24
|
+
const b = t.current;
|
|
25
|
+
if (!b) return;
|
|
26
|
+
const d = b.getBoundingClientRect(), y = window.innerHeight - d.bottom;
|
|
27
|
+
o(y < i && d.top > y ? "top" : "bottom");
|
|
28
28
|
}, [t, e, s, i]), n;
|
|
29
|
-
},
|
|
29
|
+
}, O = "", te = ({
|
|
30
30
|
options: t,
|
|
31
31
|
value: e,
|
|
32
32
|
onChange: s,
|
|
33
33
|
name: i,
|
|
34
34
|
placeholder: n = "選択してください",
|
|
35
|
-
emptyLabel:
|
|
36
|
-
clearable:
|
|
37
|
-
disabled:
|
|
38
|
-
noResultsText:
|
|
39
|
-
searchPlaceholder:
|
|
35
|
+
emptyLabel: o,
|
|
36
|
+
clearable: b = !0,
|
|
37
|
+
disabled: d = !1,
|
|
38
|
+
noResultsText: y = "一致する項目がありません",
|
|
39
|
+
searchPlaceholder: m = "検索...",
|
|
40
40
|
dropdownPlacement: k = "auto",
|
|
41
|
-
className:
|
|
41
|
+
className: x = "",
|
|
42
|
+
triggerProps: w = { className: "bca-textbox__input" },
|
|
43
|
+
searchInputProps: f = { className: "bca-textbox__input" }
|
|
42
44
|
}) => {
|
|
43
|
-
const [
|
|
44
|
-
|
|
45
|
-
const
|
|
45
|
+
const [r, _] = D(!1), [l, p] = D(""), [u, g] = D(0), $ = L(null), R = L(null), S = L(null), M = H(), I = (a) => `${M}-option-${a || "__empty__"}`, h = K(t, l), E = o ? [{ id: O, label: o }, ...h] : h, A = t.find((a) => a.id === e) ?? null;
|
|
46
|
+
z($, r, () => _(!1));
|
|
47
|
+
const U = B($, r, k);
|
|
46
48
|
C(() => {
|
|
47
|
-
|
|
48
|
-
}, [
|
|
49
|
-
|
|
50
|
-
}, [
|
|
51
|
-
const
|
|
52
|
-
s(a ===
|
|
53
|
-
},
|
|
54
|
-
if (!
|
|
49
|
+
r || (p(""), g(0));
|
|
50
|
+
}, [r]), C(() => {
|
|
51
|
+
r && R.current?.focus();
|
|
52
|
+
}, [r]);
|
|
53
|
+
const F = (a) => {
|
|
54
|
+
s(a === O ? null : a), _(!1), S.current?.focus();
|
|
55
|
+
}, j = (a) => {
|
|
56
|
+
if (!d && !a.nativeEvent.isComposing && !(a.key !== "Escape" && a.target.closest(".bca-search-select__clear"))) {
|
|
55
57
|
if (a.key === "Escape") {
|
|
56
|
-
|
|
58
|
+
_(!1), S.current?.focus();
|
|
57
59
|
return;
|
|
58
60
|
}
|
|
59
|
-
if (!
|
|
60
|
-
(a.key === "ArrowDown" || a.key === "Enter") && (a.preventDefault(),
|
|
61
|
+
if (!r) {
|
|
62
|
+
(a.key === "ArrowDown" || a.key === "Enter") && (a.preventDefault(), _(!0));
|
|
61
63
|
return;
|
|
62
64
|
}
|
|
63
65
|
if (a.key === "ArrowDown")
|
|
64
|
-
a.preventDefault(),
|
|
66
|
+
a.preventDefault(), g((v) => Math.min(v + 1, E.length - 1));
|
|
65
67
|
else if (a.key === "ArrowUp")
|
|
66
|
-
a.preventDefault(),
|
|
68
|
+
a.preventDefault(), g((v) => Math.max(v - 1, 0));
|
|
67
69
|
else if (a.key === "Enter") {
|
|
68
70
|
a.preventDefault();
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
+
const v = E[u];
|
|
72
|
+
v && !v.disabled && F(v.id);
|
|
71
73
|
}
|
|
72
74
|
}
|
|
73
75
|
};
|
|
74
|
-
return /* @__PURE__ */
|
|
76
|
+
return /* @__PURE__ */ N("div", { ref: $, className: `bca-search-select ${x}`.trim(), onKeyDown: j, children: [
|
|
75
77
|
i && /* @__PURE__ */ c("input", { type: "hidden", name: i, value: e ?? "" }),
|
|
76
|
-
/* @__PURE__ */
|
|
78
|
+
/* @__PURE__ */ N(
|
|
77
79
|
"div",
|
|
78
80
|
{
|
|
81
|
+
...w,
|
|
79
82
|
ref: S,
|
|
80
83
|
role: "combobox",
|
|
81
|
-
"aria-expanded":
|
|
84
|
+
"aria-expanded": r,
|
|
82
85
|
"aria-haspopup": "listbox",
|
|
83
|
-
"aria-disabled":
|
|
84
|
-
"aria-controls":
|
|
85
|
-
"aria-activedescendant":
|
|
86
|
-
tabIndex:
|
|
87
|
-
className:
|
|
88
|
-
"data-disabled":
|
|
89
|
-
onClick: () => !
|
|
86
|
+
"aria-disabled": d,
|
|
87
|
+
"aria-controls": r ? M : void 0,
|
|
88
|
+
"aria-activedescendant": r && E[u] ? I(E[u].id) : void 0,
|
|
89
|
+
tabIndex: d ? -1 : 0,
|
|
90
|
+
className: `bca-search-select__trigger ${w?.className ?? ""}`.trim(),
|
|
91
|
+
"data-disabled": d || void 0,
|
|
92
|
+
onClick: () => !d && _(!r),
|
|
90
93
|
children: [
|
|
91
|
-
/* @__PURE__ */ c("span", { className:
|
|
92
|
-
!
|
|
94
|
+
/* @__PURE__ */ c("span", { className: A ? "bca-search-select__value" : "bca-search-select__placeholder", children: A ? A.label : n }),
|
|
95
|
+
!d && b && e ? /* @__PURE__ */ c(
|
|
93
96
|
"button",
|
|
94
97
|
{
|
|
95
98
|
type: "button",
|
|
@@ -104,44 +107,45 @@ const q = (t, e) => {
|
|
|
104
107
|
]
|
|
105
108
|
}
|
|
106
109
|
),
|
|
107
|
-
|
|
110
|
+
r && /* @__PURE__ */ N("div", { className: "bca-search-select__dropdown", "data-placement": U, children: [
|
|
108
111
|
/* @__PURE__ */ c("div", { className: "bca-search-select__search", children: /* @__PURE__ */ c(
|
|
109
112
|
"input",
|
|
110
113
|
{
|
|
111
|
-
|
|
114
|
+
...f,
|
|
115
|
+
ref: R,
|
|
112
116
|
type: "text",
|
|
113
|
-
className:
|
|
114
|
-
placeholder:
|
|
115
|
-
value:
|
|
117
|
+
className: `bca-search-select__search-input ${f?.className ?? ""}`.trim(),
|
|
118
|
+
placeholder: m,
|
|
119
|
+
value: l,
|
|
116
120
|
onChange: (a) => {
|
|
117
|
-
|
|
121
|
+
p(a.target.value), g(0);
|
|
118
122
|
},
|
|
119
123
|
onClick: (a) => a.stopPropagation()
|
|
120
124
|
}
|
|
121
125
|
) }),
|
|
122
|
-
/* @__PURE__ */ c("ul", { role: "listbox", id: M, className: "bca-search-select__list", children: E.length > 0 ? E.map((a,
|
|
126
|
+
/* @__PURE__ */ c("ul", { role: "listbox", id: M, className: "bca-search-select__list", children: E.length > 0 ? E.map((a, v) => /* @__PURE__ */ N(
|
|
123
127
|
"li",
|
|
124
128
|
{
|
|
125
129
|
id: I(a.id),
|
|
126
130
|
role: "option",
|
|
127
|
-
"aria-selected": a.id === (e ??
|
|
131
|
+
"aria-selected": a.id === (e ?? O),
|
|
128
132
|
"aria-disabled": a.disabled || void 0,
|
|
129
133
|
className: "bca-search-select__option",
|
|
130
|
-
"data-active":
|
|
131
|
-
"data-selected": a.id === (e ??
|
|
134
|
+
"data-active": v === u || void 0,
|
|
135
|
+
"data-selected": a.id === (e ?? O) || void 0,
|
|
132
136
|
"data-disabled": a.disabled || void 0,
|
|
133
|
-
onMouseEnter: () => !a.disabled &&
|
|
134
|
-
onClick: () => !a.disabled &&
|
|
137
|
+
onMouseEnter: () => !a.disabled && g(v),
|
|
138
|
+
onClick: () => !a.disabled && F(a.id),
|
|
135
139
|
children: [
|
|
136
140
|
/* @__PURE__ */ c("span", { className: "bca-search-select__label", children: a.label }),
|
|
137
141
|
a.sublabel && /* @__PURE__ */ c("span", { className: "bca-search-select__sublabel", children: `(${a.sublabel})` })
|
|
138
142
|
]
|
|
139
143
|
},
|
|
140
144
|
a.id || "__empty__"
|
|
141
|
-
)) : /* @__PURE__ */ c("li", { className: "bca-search-select__no-results", children:
|
|
145
|
+
)) : /* @__PURE__ */ c("li", { className: "bca-search-select__no-results", children: y }) })
|
|
142
146
|
] })
|
|
143
147
|
] });
|
|
144
|
-
},
|
|
148
|
+
}, T = ({ items: t, onRemove: e, emptyText: s = "選択されていません", disabled: i = !1 }) => t.length === 0 ? /* @__PURE__ */ c("span", { className: "bca-selected-tags__empty", children: s }) : /* @__PURE__ */ c("ul", { className: "bca-selected-tags", children: t.map((n) => /* @__PURE__ */ N("li", { className: "bca-selected-tags__item", children: [
|
|
145
149
|
/* @__PURE__ */ c("span", { className: "bca-selected-tags__label", children: n.label }),
|
|
146
150
|
/* @__PURE__ */ c(
|
|
147
151
|
"button",
|
|
@@ -154,58 +158,60 @@ const q = (t, e) => {
|
|
|
154
158
|
children: "×"
|
|
155
159
|
}
|
|
156
160
|
)
|
|
157
|
-
] }, n.id)) }),
|
|
161
|
+
] }, n.id)) }), G = ({
|
|
158
162
|
options: t,
|
|
159
163
|
value: e,
|
|
160
164
|
onChange: s,
|
|
161
165
|
searchPlaceholder: i = "検索...",
|
|
162
166
|
noResultsText: n = "選択可能な項目はありません",
|
|
163
|
-
listHeight:
|
|
164
|
-
maxSelected:
|
|
165
|
-
className:
|
|
167
|
+
listHeight: o = 350,
|
|
168
|
+
maxSelected: b,
|
|
169
|
+
className: d = "",
|
|
170
|
+
searchInputProps: y = { className: "bca-textbox__input" }
|
|
166
171
|
}) => {
|
|
167
|
-
const [
|
|
168
|
-
|
|
169
|
-
},
|
|
170
|
-
s(e.filter((
|
|
172
|
+
const [m, k] = D(""), x = new Set(e.map((l) => l.id)), w = K(t, m).filter((l) => !x.has(l.id)), f = b !== void 0 && e.length >= b, r = (l) => {
|
|
173
|
+
f || l.disabled || s([...e, l]);
|
|
174
|
+
}, _ = (l) => {
|
|
175
|
+
s(e.filter((p) => p.id !== l));
|
|
171
176
|
};
|
|
172
|
-
return /* @__PURE__ */
|
|
173
|
-
/* @__PURE__ */
|
|
177
|
+
return /* @__PURE__ */ N("div", { className: `bca-multi-select-picker ${d}`.trim(), children: [
|
|
178
|
+
/* @__PURE__ */ N("div", { className: "bca-multi-select-picker__search", children: [
|
|
174
179
|
/* @__PURE__ */ c(
|
|
175
180
|
"input",
|
|
176
181
|
{
|
|
182
|
+
...y,
|
|
177
183
|
type: "text",
|
|
178
|
-
className:
|
|
184
|
+
className: `bca-multi-select-picker__search-input ${y?.className ?? ""}`.trim(),
|
|
179
185
|
placeholder: i,
|
|
180
|
-
value:
|
|
181
|
-
onChange: (l) =>
|
|
186
|
+
value: m,
|
|
187
|
+
onChange: (l) => k(l.target.value)
|
|
182
188
|
}
|
|
183
189
|
),
|
|
184
|
-
|
|
190
|
+
m && /* @__PURE__ */ c(
|
|
185
191
|
"button",
|
|
186
192
|
{
|
|
187
193
|
type: "button",
|
|
188
194
|
className: "bca-multi-select-picker__search-clear",
|
|
189
195
|
"aria-label": "検索語をクリア",
|
|
190
|
-
onClick: () =>
|
|
196
|
+
onClick: () => k(""),
|
|
191
197
|
children: "×"
|
|
192
198
|
}
|
|
193
199
|
)
|
|
194
200
|
] }),
|
|
195
|
-
/* @__PURE__ */ c("div", { className: "bca-multi-select-picker__list-wrapper", style: { maxHeight:
|
|
196
|
-
const
|
|
197
|
-
return /* @__PURE__ */
|
|
201
|
+
/* @__PURE__ */ c("div", { className: "bca-multi-select-picker__list-wrapper", style: { maxHeight: o }, children: /* @__PURE__ */ c("ul", { className: "bca-multi-select-picker__list", role: "listbox", "aria-multiselectable": "true", "aria-label": "選択可能な項目", children: w.length > 0 ? w.map((l) => {
|
|
202
|
+
const p = l.disabled || f;
|
|
203
|
+
return /* @__PURE__ */ N(
|
|
198
204
|
"li",
|
|
199
205
|
{
|
|
200
206
|
className: "bca-multi-select-picker__option",
|
|
201
207
|
role: "option",
|
|
202
208
|
"aria-selected": !1,
|
|
203
|
-
"aria-disabled":
|
|
204
|
-
"data-disabled":
|
|
205
|
-
tabIndex:
|
|
206
|
-
onClick: () =>
|
|
207
|
-
onKeyDown: (
|
|
208
|
-
|
|
209
|
+
"aria-disabled": p || void 0,
|
|
210
|
+
"data-disabled": p || void 0,
|
|
211
|
+
tabIndex: p ? -1 : 0,
|
|
212
|
+
onClick: () => r(l),
|
|
213
|
+
onKeyDown: (u) => {
|
|
214
|
+
u.nativeEvent.isComposing || (u.key === "Enter" || u.key === " ") && (u.preventDefault(), r(l));
|
|
209
215
|
},
|
|
210
216
|
children: [
|
|
211
217
|
/* @__PURE__ */ c("span", { className: "bca-multi-select-picker__label", children: l.label }),
|
|
@@ -215,151 +221,151 @@ const q = (t, e) => {
|
|
|
215
221
|
l.id
|
|
216
222
|
);
|
|
217
223
|
}) : /* @__PURE__ */ c("li", { className: "bca-multi-select-picker__no-results", children: n }) }) }),
|
|
218
|
-
/* @__PURE__ */ c("div", { className: "bca-multi-select-picker__selected", children: /* @__PURE__ */ c(
|
|
224
|
+
/* @__PURE__ */ c("div", { className: "bca-multi-select-picker__selected", children: /* @__PURE__ */ c(T, { items: e, onRemove: _ }) })
|
|
219
225
|
] });
|
|
220
|
-
},
|
|
226
|
+
}, J = 'a[href], button, input:not([type="hidden"]), select, textarea, [tabindex]:not([tabindex="-1"])', P = (t) => "disabled" in t && t.disabled, W = (t) => Array.from(t.querySelectorAll(J)).filter((e) => !P(e)), X = (t, e) => {
|
|
221
227
|
C(() => {
|
|
222
228
|
if (!e) return;
|
|
223
229
|
const s = t.current;
|
|
224
230
|
if (!s) return;
|
|
225
231
|
const i = (n) => {
|
|
226
232
|
if (n.key !== "Tab") return;
|
|
227
|
-
const
|
|
228
|
-
if (
|
|
229
|
-
const
|
|
230
|
-
n.shiftKey ? (document.activeElement ===
|
|
233
|
+
const o = W(s);
|
|
234
|
+
if (o.length === 0) return;
|
|
235
|
+
const b = o[0], d = o[o.length - 1];
|
|
236
|
+
n.shiftKey ? (document.activeElement === b || !s.contains(document.activeElement)) && (n.preventDefault(), d.focus()) : (document.activeElement === d || !s.contains(document.activeElement)) && (n.preventDefault(), b.focus());
|
|
231
237
|
};
|
|
232
238
|
return s.addEventListener("keydown", i), () => s.removeEventListener("keydown", i);
|
|
233
239
|
}, [t, e]);
|
|
234
|
-
},
|
|
240
|
+
}, Z = ({
|
|
235
241
|
open: t,
|
|
236
242
|
title: e = "選択",
|
|
237
243
|
options: s,
|
|
238
244
|
initialValue: i,
|
|
239
245
|
onSubmit: n,
|
|
240
|
-
onCancel:
|
|
241
|
-
submitLabel:
|
|
242
|
-
cancelLabel:
|
|
243
|
-
requireSelection:
|
|
244
|
-
submitButtonProps:
|
|
246
|
+
onCancel: o,
|
|
247
|
+
submitLabel: b = "決定",
|
|
248
|
+
cancelLabel: d = "キャンセル",
|
|
249
|
+
requireSelection: y = !0,
|
|
250
|
+
submitButtonProps: m = { className: "bca-btn", "data-bca-btn-type": "save" },
|
|
245
251
|
cancelButtonProps: k = { className: "bca-btn" },
|
|
246
|
-
className:
|
|
247
|
-
...
|
|
252
|
+
className: x = "",
|
|
253
|
+
...w
|
|
248
254
|
}) => {
|
|
249
|
-
const [
|
|
255
|
+
const [f, r] = D(i ?? []), _ = L(null), l = L(null);
|
|
250
256
|
if (C(() => {
|
|
251
|
-
t &&
|
|
257
|
+
t && r(i ?? []);
|
|
252
258
|
}, [t]), C(() => {
|
|
253
259
|
if (!t) return;
|
|
254
|
-
const
|
|
255
|
-
|
|
260
|
+
const u = (g) => {
|
|
261
|
+
g.isComposing || g.key === "Escape" && o();
|
|
256
262
|
};
|
|
257
|
-
return document.addEventListener("keydown",
|
|
258
|
-
}, [t,
|
|
259
|
-
t ? (
|
|
260
|
-
}, [t]),
|
|
261
|
-
const
|
|
262
|
-
return /* @__PURE__ */ c("div", { className: "bca-multi-select-dialog__overlay", children: /* @__PURE__ */
|
|
263
|
+
return document.addEventListener("keydown", u), () => document.removeEventListener("keydown", u);
|
|
264
|
+
}, [t, o]), C(() => {
|
|
265
|
+
t ? (l.current = document.activeElement, _.current?.focus()) : (l.current?.focus(), l.current = null);
|
|
266
|
+
}, [t]), X(_, t), !t) return null;
|
|
267
|
+
const p = !y || f.length > 0;
|
|
268
|
+
return /* @__PURE__ */ c("div", { className: "bca-multi-select-dialog__overlay", children: /* @__PURE__ */ N(
|
|
263
269
|
"div",
|
|
264
270
|
{
|
|
265
|
-
ref:
|
|
271
|
+
ref: _,
|
|
266
272
|
role: "dialog",
|
|
267
273
|
"aria-modal": "true",
|
|
268
274
|
"aria-label": e,
|
|
269
275
|
tabIndex: -1,
|
|
270
|
-
className: `bca-multi-select-dialog ${
|
|
276
|
+
className: `bca-multi-select-dialog ${x}`.trim(),
|
|
271
277
|
children: [
|
|
272
|
-
/* @__PURE__ */
|
|
278
|
+
/* @__PURE__ */ N("div", { className: "bca-multi-select-dialog__header", children: [
|
|
273
279
|
/* @__PURE__ */ c("span", { className: "bca-multi-select-dialog__title", children: e }),
|
|
274
|
-
/* @__PURE__ */ c("button", { type: "button", className: "bca-multi-select-dialog__close", "aria-label": "閉じる", onClick:
|
|
280
|
+
/* @__PURE__ */ c("button", { type: "button", className: "bca-multi-select-dialog__close", "aria-label": "閉じる", onClick: o, children: "×" })
|
|
275
281
|
] }),
|
|
276
|
-
/* @__PURE__ */ c("div", { className: "bca-multi-select-dialog__body", children: /* @__PURE__ */ c(
|
|
277
|
-
/* @__PURE__ */
|
|
282
|
+
/* @__PURE__ */ c("div", { className: "bca-multi-select-dialog__body", children: /* @__PURE__ */ c(G, { options: s, value: f, onChange: r, ...w }) }),
|
|
283
|
+
/* @__PURE__ */ N("div", { className: "bca-multi-select-dialog__footer", children: [
|
|
278
284
|
/* @__PURE__ */ c(
|
|
279
285
|
"button",
|
|
280
286
|
{
|
|
281
287
|
...k,
|
|
282
288
|
type: "button",
|
|
283
289
|
className: `bca-multi-select-dialog__button ${k?.className ?? ""}`.trim(),
|
|
284
|
-
onClick:
|
|
285
|
-
children:
|
|
290
|
+
onClick: o,
|
|
291
|
+
children: d
|
|
286
292
|
}
|
|
287
293
|
),
|
|
288
294
|
/* @__PURE__ */ c(
|
|
289
295
|
"button",
|
|
290
296
|
{
|
|
291
|
-
...
|
|
297
|
+
...m,
|
|
292
298
|
type: "button",
|
|
293
|
-
className: `bca-multi-select-dialog__button bca-multi-select-dialog__button--primary ${
|
|
294
|
-
disabled: !
|
|
295
|
-
onClick: () => n(
|
|
296
|
-
children:
|
|
299
|
+
className: `bca-multi-select-dialog__button bca-multi-select-dialog__button--primary ${m?.className ?? ""}`.trim(),
|
|
300
|
+
disabled: !p,
|
|
301
|
+
onClick: () => n(f),
|
|
302
|
+
children: b
|
|
297
303
|
}
|
|
298
304
|
)
|
|
299
305
|
] })
|
|
300
306
|
]
|
|
301
307
|
}
|
|
302
308
|
) });
|
|
303
|
-
},
|
|
309
|
+
}, ae = ({
|
|
304
310
|
options: t,
|
|
305
311
|
value: e,
|
|
306
312
|
onChange: s,
|
|
307
313
|
name: i,
|
|
308
314
|
addButtonLabel: n = "追加",
|
|
309
|
-
dialogTitle:
|
|
310
|
-
emptyText:
|
|
311
|
-
disabled:
|
|
312
|
-
className:
|
|
313
|
-
maxSelected:
|
|
315
|
+
dialogTitle: o = "選択",
|
|
316
|
+
emptyText: b = "選択されていません",
|
|
317
|
+
disabled: d = !1,
|
|
318
|
+
className: y = "",
|
|
319
|
+
maxSelected: m,
|
|
314
320
|
submitLabel: k,
|
|
315
|
-
cancelLabel:
|
|
316
|
-
requireSelection:
|
|
317
|
-
addButtonProps:
|
|
318
|
-
submitButtonProps:
|
|
319
|
-
cancelButtonProps:
|
|
320
|
-
...
|
|
321
|
+
cancelLabel: x,
|
|
322
|
+
requireSelection: w,
|
|
323
|
+
addButtonProps: f = { className: "bca-btn", "data-bca-btn-type": "add" },
|
|
324
|
+
submitButtonProps: r,
|
|
325
|
+
cancelButtonProps: _,
|
|
326
|
+
...l
|
|
321
327
|
}) => {
|
|
322
|
-
const [
|
|
323
|
-
s(e.filter((
|
|
324
|
-
},
|
|
325
|
-
s([...e, ...
|
|
328
|
+
const [p, u] = D(!1), g = new Set(e.map((h) => h.id)), $ = t.filter((h) => !g.has(h.id)), R = m !== void 0 ? Math.max(0, m - e.length) : void 0, S = m !== void 0 && e.length >= m, M = (h) => {
|
|
329
|
+
s(e.filter((E) => E.id !== h));
|
|
330
|
+
}, I = (h) => {
|
|
331
|
+
s([...e, ...h]), u(!1);
|
|
326
332
|
};
|
|
327
|
-
return /* @__PURE__ */
|
|
328
|
-
i && e.map((
|
|
329
|
-
/* @__PURE__ */ c("div", { className: "bca-multi-select-field__tags", children: /* @__PURE__ */ c(
|
|
333
|
+
return /* @__PURE__ */ N("div", { className: `bca-multi-select-field ${y}`.trim(), children: [
|
|
334
|
+
i && e.map((h) => /* @__PURE__ */ c("input", { type: "hidden", name: i, value: h.id }, h.id)),
|
|
335
|
+
/* @__PURE__ */ c("div", { className: "bca-multi-select-field__tags", children: /* @__PURE__ */ c(T, { items: e, onRemove: M, emptyText: b, disabled: d }) }),
|
|
330
336
|
/* @__PURE__ */ c("div", { className: "bca-multi-select-field__actions", children: /* @__PURE__ */ c(
|
|
331
337
|
"button",
|
|
332
338
|
{
|
|
333
|
-
...
|
|
339
|
+
...f,
|
|
334
340
|
type: "button",
|
|
335
|
-
className: `bca-multi-select-field__add ${
|
|
336
|
-
disabled:
|
|
337
|
-
onClick: () =>
|
|
341
|
+
className: `bca-multi-select-field__add ${f?.className ?? ""}`.trim(),
|
|
342
|
+
disabled: d || S,
|
|
343
|
+
onClick: () => u(!0),
|
|
338
344
|
children: n
|
|
339
345
|
}
|
|
340
346
|
) }),
|
|
341
347
|
/* @__PURE__ */ c(
|
|
342
|
-
|
|
348
|
+
Z,
|
|
343
349
|
{
|
|
344
|
-
open:
|
|
345
|
-
title:
|
|
346
|
-
options:
|
|
347
|
-
onSubmit:
|
|
348
|
-
onCancel: () =>
|
|
349
|
-
maxSelected:
|
|
350
|
+
open: p,
|
|
351
|
+
title: o,
|
|
352
|
+
options: $,
|
|
353
|
+
onSubmit: I,
|
|
354
|
+
onCancel: () => u(!1),
|
|
355
|
+
maxSelected: R,
|
|
350
356
|
submitLabel: k,
|
|
351
|
-
cancelLabel:
|
|
352
|
-
requireSelection:
|
|
353
|
-
submitButtonProps:
|
|
354
|
-
cancelButtonProps:
|
|
355
|
-
...
|
|
357
|
+
cancelLabel: x,
|
|
358
|
+
requireSelection: w,
|
|
359
|
+
submitButtonProps: r,
|
|
360
|
+
cancelButtonProps: _,
|
|
361
|
+
...l
|
|
356
362
|
}
|
|
357
363
|
)
|
|
358
364
|
] });
|
|
359
365
|
};
|
|
360
366
|
export {
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
367
|
+
Z as MultiSelectDialog,
|
|
368
|
+
ae as MultiSelectField,
|
|
369
|
+
G as MultiSelectPicker,
|
|
370
|
+
te as SearchSelect
|
|
365
371
|
};
|
package/dist/style.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
:root{--bca-accent: #D4EDC9;--bca-accent-hover: #E9F7E3;--bca-border: #ccc;--bca-radius: 4px;--bca-font-size: 14px;--bca-z-index: 9999}.bca-selected-tags{display:flex;flex-wrap:wrap;gap:5px;list-style:none;margin:0;padding:0}.bca-selected-tags__empty{color:#999;font-size:var(--bca-font-size)}.bca-selected-tags__item{display:flex;align-items:center;padding:5px 10px;background-color:var(--bca-accent);border-radius:15px;font-size:var(--bca-font-size)}.bca-selected-tags__remove{margin-left:5px}:where(.bca-selected-tags__remove){font:inherit;border:none;background:transparent;font-weight:700;cursor:pointer}.bca-selected-tags__remove:disabled{cursor:not-allowed;opacity:.5}.bca-search-select{position:relative;width:100%;font-size:var(--bca-font-size)}.bca-search-select__trigger{position:relative;
|
|
1
|
+
:root{--bca-accent: #D4EDC9;--bca-accent-hover: #E9F7E3;--bca-border: #ccc;--bca-radius: 4px;--bca-font-size: 14px;--bca-z-index: 9999}.bca-selected-tags{display:flex;flex-wrap:wrap;gap:5px;list-style:none;margin:0;padding:0}.bca-selected-tags__empty{color:#999;font-size:var(--bca-font-size)}.bca-selected-tags__item{display:flex;align-items:center;padding:5px 10px;background-color:var(--bca-accent);border-radius:15px;font-size:var(--bca-font-size)}.bca-selected-tags__remove{margin-left:5px}:where(.bca-selected-tags__remove){font:inherit;border:none;background:transparent;font-weight:700;cursor:pointer}.bca-selected-tags__remove:disabled{cursor:not-allowed;opacity:.5}.bca-search-select{position:relative;width:100%;font-size:var(--bca-font-size)}.bca-search-select__trigger{position:relative;align-items:center;box-sizing:border-box;width:100%;cursor:pointer}.bca-search-select__trigger.bca-search-select__trigger{display:flex;padding-right:32px}:where(.bca-search-select__trigger){padding-top:4px;padding-left:8px;padding-bottom:4px;border:1px solid var(--bca-border);border-radius:var(--bca-radius);background-color:#fff}.bca-search-select__trigger[data-disabled]{background-color:#f4f4f4;color:#999;cursor:not-allowed}.bca-search-select__value,.bca-search-select__placeholder{flex:1 1 auto;min-width:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.bca-search-select__placeholder{color:#999}.bca-search-select__clear,.bca-search-select__arrow{position:absolute;right:8px;top:50%;transform:translateY(-50%);color:#999}.bca-search-select__clear{border:none;background:transparent;font-weight:700;cursor:pointer;padding:0 4px}.bca-search-select__arrow{font-size:12px;pointer-events:none}.bca-search-select__dropdown{position:absolute;left:0;width:100%;box-sizing:border-box;z-index:var(--bca-z-index);background-color:#fff;border:1px solid var(--bca-border);border-radius:var(--bca-radius);box-shadow:0 2px 6px #00000026;max-height:240px;display:flex;flex-direction:column}.bca-search-select__dropdown[data-placement=bottom]{top:100%}.bca-search-select__dropdown[data-placement=top]{bottom:100%}.bca-search-select__search{padding:8px;border-bottom:1px solid #eee}.bca-search-select__search-input{box-sizing:border-box;width:100%}.bca-search-select__search-input.bca-search-select__search-input{font:inherit;margin:0}:where(.bca-search-select__search-input){padding:4px 8px;border:1px solid var(--bca-border);border-radius:var(--bca-radius)}.bca-search-select__list{list-style:none;margin:0;padding:0;overflow-y:auto;flex:1}.bca-search-select__option{padding:6px 12px;cursor:pointer;border-bottom:1px solid #f9f9f9}.bca-search-select__option[data-active]{background-color:var(--bca-accent-hover)}.bca-search-select__option[data-selected]{background-color:var(--bca-accent);font-weight:700}.bca-search-select__option[data-disabled]{color:#999;cursor:not-allowed;background-color:transparent}.bca-search-select__sublabel{margin-left:4px;font-size:.8em;color:#666}.bca-search-select__no-results{padding:8px 12px;color:#999;text-align:center}.bca-multi-select-picker{font-size:var(--bca-font-size)}.bca-multi-select-picker__search{position:relative;margin-bottom:10px}.bca-multi-select-picker__search-input{box-sizing:border-box;width:100%}.bca-multi-select-picker__search-input.bca-multi-select-picker__search-input{font:inherit;margin:0}:where(.bca-multi-select-picker__search-input){padding:4px 30px 4px 8px;border:1px solid var(--bca-border);border-radius:var(--bca-radius)}.bca-multi-select-picker__search-clear{position:absolute;right:8px;top:50%;transform:translateY(-50%);border:none;background:transparent;font-size:16px;color:#999;cursor:pointer}.bca-multi-select-picker__list-wrapper{overflow-y:auto;border:1px solid var(--bca-border);border-radius:var(--bca-radius)}.bca-multi-select-picker__list{list-style:none;margin:0;padding:0}.bca-multi-select-picker__option{padding:8px 10px;border-bottom:1px solid #eee;cursor:pointer}.bca-multi-select-picker__option:hover{background-color:var(--bca-accent-hover)}.bca-multi-select-picker__option:focus-visible{outline:2px solid var(--bca-accent-hover);outline-offset:-2px;background-color:var(--bca-accent-hover)}.bca-multi-select-picker__option[data-disabled]{color:#999;cursor:not-allowed}.bca-multi-select-picker__option[data-disabled]:hover{background-color:transparent}.bca-multi-select-picker__sublabel{margin-left:4px;font-size:.8em;color:#666}.bca-multi-select-picker__no-results{padding:10px;color:#999;text-align:center}.bca-multi-select-picker__selected{margin-top:10px;padding:10px;border:1px solid var(--bca-border);border-radius:var(--bca-radius);min-height:40px;max-height:120px;overflow-y:auto}.bca-multi-select-dialog__overlay{position:fixed;inset:0;display:flex;justify-content:center;align-items:center;background-color:#0000004d;z-index:var(--bca-z-index)}.bca-multi-select-dialog{width:500px;max-width:calc(100vw - 32px);max-height:calc(100vh - 32px);display:flex;flex-direction:column;background-color:#fff;border:1px solid var(--bca-border);border-radius:var(--bca-radius);box-shadow:0 4px 16px #0003;font-size:var(--bca-font-size)}.bca-multi-select-dialog:focus{outline:none}.bca-multi-select-dialog__header{display:flex;justify-content:space-between;align-items:center;padding:8px 12px;border-bottom:1px solid var(--bca-border);background-color:#f5f5f5}.bca-multi-select-dialog__title{font-weight:700}:where(.bca-multi-select-dialog__close){font:inherit;border:none;background:transparent;font-size:18px;cursor:pointer;color:#666}.bca-multi-select-dialog__body{flex:1 1 auto;min-height:0;overflow-y:auto;padding:1em}.bca-multi-select-dialog__footer{display:flex;justify-content:flex-end;gap:8px;padding:8px 12px;border-top:1px solid var(--bca-border)}:where(.bca-multi-select-dialog__button){font:inherit;padding:4px 16px;border:1px solid var(--bca-border);border-radius:var(--bca-radius);background-color:#fff;cursor:pointer}:where(.bca-multi-select-dialog__button--primary){background-color:var(--bca-accent)}.bca-multi-select-dialog__button:disabled{opacity:.5;cursor:not-allowed}.bca-multi-select-field{font-size:var(--bca-font-size)}.bca-multi-select-field__tags{padding:10px;border:1px solid var(--bca-border);border-radius:var(--bca-radius);min-height:40px;box-sizing:border-box}.bca-multi-select-field__actions{margin-top:8px}:where(.bca-multi-select-field__add){font:inherit;padding:4px 16px;border:1px solid var(--bca-border);border-radius:var(--bca-radius);background-color:#fff;cursor:pointer}.bca-multi-select-field__add:disabled{opacity:.5;cursor:not-allowed}
|