@chayns-ui/core 0.9.1 → 0.9.4
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/dist/combo-box.css +54 -51
- package/dist/components/button/Button.types.d.ts +1 -1
- package/dist/components/button/Button.types.d.ts.map +1 -1
- package/dist/components/button/Button.types.js.map +1 -1
- package/dist/components/button/button-icon/ButtonIcon.js +2 -2
- package/dist/components/button/button-icon/ButtonIcon.js.map +1 -1
- package/dist/components/combo-box/ComboBox.d.ts +1 -1
- package/dist/components/combo-box/ComboBox.d.ts.map +1 -1
- package/dist/components/combo-box/ComboBox.js +163 -109
- package/dist/components/combo-box/ComboBox.js.map +1 -1
- package/dist/components/combo-box/ComboBox.types.d.ts +4 -8
- package/dist/components/combo-box/ComboBox.types.d.ts.map +1 -1
- package/dist/components/combo-box/ComboBoxContext.d.ts +0 -3
- package/dist/components/combo-box/ComboBoxContext.d.ts.map +1 -1
- package/dist/components/combo-box/ComboBoxContext.js.map +1 -1
- package/package.json +1 -1
package/dist/combo-box.css
CHANGED
|
@@ -4,97 +4,77 @@
|
|
|
4
4
|
min-inline-size: 0;
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
-
.chayns-combo-
|
|
7
|
+
.chayns-combo-box__trigger {
|
|
8
8
|
box-sizing: border-box;
|
|
9
|
+
display: flex;
|
|
10
|
+
align-items: center;
|
|
11
|
+
justify-content: space-between;
|
|
9
12
|
inline-size: 100%;
|
|
10
13
|
min-block-size: calc(1.2em + 2 * var(--input-py) + 3px);
|
|
11
|
-
padding: var(--input-py) var(--
|
|
14
|
+
padding: var(--input-py) var(--input-px);
|
|
12
15
|
border: 1.5px solid var(--input-border);
|
|
13
16
|
border-radius: 10px;
|
|
14
17
|
background: var(--surface);
|
|
15
18
|
color: var(--text);
|
|
19
|
+
cursor: pointer;
|
|
16
20
|
font: inherit;
|
|
17
21
|
line-height: 1.2;
|
|
18
22
|
outline: none;
|
|
23
|
+
text-align: start;
|
|
19
24
|
transition:
|
|
20
25
|
border-color 0.18s ease,
|
|
21
26
|
box-shadow 0.18s ease;
|
|
22
27
|
}
|
|
23
28
|
|
|
24
|
-
.chayns-combo-
|
|
29
|
+
.chayns-combo-box__trigger:focus-visible {
|
|
25
30
|
border-color: var(--accent);
|
|
26
31
|
box-shadow: 0 0 0 var(--focus-ring-size)
|
|
27
32
|
rgb(var(--focus-ring-rgb), var(--focus-ring-alpha-soft));
|
|
28
33
|
}
|
|
29
34
|
|
|
35
|
+
.chayns-combo-box__trigger:disabled {
|
|
36
|
+
cursor: not-allowed;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.chayns-combo-box__value {
|
|
40
|
+
min-inline-size: 0;
|
|
41
|
+
overflow: hidden;
|
|
42
|
+
text-overflow: ellipsis;
|
|
43
|
+
white-space: nowrap;
|
|
44
|
+
}
|
|
45
|
+
|
|
30
46
|
.chayns-combo-box__label {
|
|
31
47
|
position: absolute;
|
|
32
48
|
inset-block-start: 0;
|
|
33
49
|
inset-inline-start: 10px;
|
|
34
50
|
padding-inline: var(--k5);
|
|
35
51
|
background: var(--surface);
|
|
36
|
-
color: var(--
|
|
52
|
+
color: var(--accent);
|
|
37
53
|
font-size: var(--fs-body);
|
|
38
54
|
line-height: 1.2;
|
|
39
55
|
pointer-events: none;
|
|
40
|
-
transform: translateY(
|
|
56
|
+
transform: translateY(-50%) scale(0.8);
|
|
41
57
|
transform-origin: left center;
|
|
42
|
-
transition:
|
|
43
|
-
transform 0.2s cubic-bezier(0.22, 0.61, 0.36, 1),
|
|
44
|
-
color 0.15s ease;
|
|
45
58
|
white-space: nowrap;
|
|
46
59
|
}
|
|
47
60
|
|
|
48
61
|
.chayns-combo-box__chevron {
|
|
49
|
-
position: absolute;
|
|
50
|
-
inset-block-start: 50%;
|
|
51
|
-
inset-inline-end: var(--input-px);
|
|
52
62
|
display: inline-flex;
|
|
63
|
+
flex: none;
|
|
53
64
|
align-items: center;
|
|
54
65
|
justify-content: center;
|
|
55
66
|
color: var(--muted);
|
|
56
|
-
pointer-events: none;
|
|
57
|
-
transform: translateY(-50%);
|
|
58
67
|
transition: transform 0.18s ease;
|
|
59
68
|
}
|
|
60
69
|
|
|
61
70
|
.chayns-combo-box--open .chayns-combo-box__chevron {
|
|
62
|
-
transform:
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
.chayns-combo-box--open .chayns-combo-box__label,
|
|
66
|
-
.chayns-combo-box__input:not(:placeholder-shown) + .chayns-combo-box__label {
|
|
67
|
-
color: var(--accent);
|
|
68
|
-
transform: translateY(-50%) scale(0.8);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
.chayns-combo-box__chips {
|
|
72
|
-
position: absolute;
|
|
73
|
-
inset: 1.5px var(--input-px) auto;
|
|
74
|
-
display: flex;
|
|
75
|
-
flex-wrap: wrap;
|
|
76
|
-
gap: var(--k5);
|
|
77
|
-
pointer-events: none;
|
|
78
|
-
transform: translateY(var(--input-py));
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
.chayns-combo-box__chip {
|
|
82
|
-
display: inline-flex;
|
|
83
|
-
align-items: center;
|
|
84
|
-
gap: var(--k5);
|
|
85
|
-
padding: var(--k3) var(--k9);
|
|
86
|
-
border-radius: 999px;
|
|
87
|
-
background: var(--accent);
|
|
88
|
-
color: var(--on-accent);
|
|
89
|
-
font-size: var(--fs-caption);
|
|
90
|
-
font-weight: 500;
|
|
71
|
+
transform: rotate(180deg);
|
|
91
72
|
}
|
|
92
73
|
|
|
93
74
|
.chayns-combo-box__popup {
|
|
94
|
-
position:
|
|
95
|
-
inset-block-start: calc(100% + var(--k4));
|
|
96
|
-
inset-inline: 0;
|
|
75
|
+
position: fixed;
|
|
97
76
|
z-index: var(--z-popover);
|
|
77
|
+
box-sizing: border-box;
|
|
98
78
|
max-block-size: min(320px, 50vh);
|
|
99
79
|
overflow: auto;
|
|
100
80
|
padding: var(--k6);
|
|
@@ -102,21 +82,34 @@
|
|
|
102
82
|
border-radius: 12px;
|
|
103
83
|
background: var(--surface);
|
|
104
84
|
box-shadow: var(--shadow-pop);
|
|
85
|
+
opacity: 1;
|
|
86
|
+
transform: translateY(0);
|
|
87
|
+
transform-origin: top center;
|
|
88
|
+
transition:
|
|
89
|
+
opacity 0.18s ease,
|
|
90
|
+
transform 0.18s ease;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.chayns-combo-box__popup[data-state='closed'] {
|
|
94
|
+
opacity: 0;
|
|
95
|
+
transform: translateY(calc(-1 * var(--k4)));
|
|
105
96
|
}
|
|
106
97
|
|
|
107
98
|
.chayns-combo-box__option {
|
|
108
99
|
display: flex;
|
|
109
100
|
align-items: center;
|
|
110
|
-
min-block-size: var(--
|
|
111
|
-
padding: var(--
|
|
101
|
+
min-block-size: var(--k32);
|
|
102
|
+
padding: var(--k6) var(--k12);
|
|
112
103
|
border-radius: 8px;
|
|
113
104
|
color: var(--text);
|
|
114
105
|
cursor: pointer;
|
|
115
106
|
font-size: var(--fs-body);
|
|
107
|
+
outline: none;
|
|
116
108
|
}
|
|
117
109
|
|
|
118
110
|
.chayns-combo-box__option:hover,
|
|
119
|
-
.chayns-combo-box__option--active
|
|
111
|
+
.chayns-combo-box__option--active,
|
|
112
|
+
.chayns-combo-box__option:focus-visible {
|
|
120
113
|
background: var(--surface-alt);
|
|
121
114
|
}
|
|
122
115
|
|
|
@@ -125,6 +118,11 @@
|
|
|
125
118
|
font-weight: 600;
|
|
126
119
|
}
|
|
127
120
|
|
|
121
|
+
.chayns-combo-box__option--disabled {
|
|
122
|
+
cursor: not-allowed;
|
|
123
|
+
opacity: 0.5;
|
|
124
|
+
}
|
|
125
|
+
|
|
128
126
|
.chayns-combo-box__checkbox {
|
|
129
127
|
display: inline-flex;
|
|
130
128
|
flex: none;
|
|
@@ -152,10 +150,15 @@
|
|
|
152
150
|
transform: rotate(-45deg) translate(1px, -1px);
|
|
153
151
|
}
|
|
154
152
|
|
|
153
|
+
.chayns-combo-box__checkmark {
|
|
154
|
+
margin-inline-start: auto;
|
|
155
|
+
color: var(--accent);
|
|
156
|
+
}
|
|
157
|
+
|
|
155
158
|
@media (prefers-reduced-motion: reduce) {
|
|
156
|
-
.chayns-combo-
|
|
157
|
-
.chayns-combo-
|
|
158
|
-
.chayns-combo-
|
|
159
|
+
.chayns-combo-box__trigger,
|
|
160
|
+
.chayns-combo-box__chevron,
|
|
161
|
+
.chayns-combo-box__popup {
|
|
159
162
|
transition: none;
|
|
160
163
|
}
|
|
161
164
|
}
|
|
@@ -18,7 +18,7 @@ export declare const BUTTON_VARIANTS: readonly ["primary", "outline", "ghost", "
|
|
|
18
18
|
* Brands icons are rendered as a single icon and must not be combined with
|
|
19
19
|
* `far` or `fas`.
|
|
20
20
|
*/
|
|
21
|
-
export type ButtonIcon = `fa-${string}` | `fab fa-${string}`;
|
|
21
|
+
export type ButtonIcon = `fa-${string}` | `fab fa-${string}` | `ts-${string}`;
|
|
22
22
|
/**
|
|
23
23
|
* @description Visible content that gives a `Button` its accessible name.
|
|
24
24
|
* Excludes `boolean`, `null` and `undefined` so a `Button` can never be
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Button.types.d.ts","sourceRoot":"","sources":["../../../src/components/button/Button.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAE9D;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEvE;;;GAGG;AACH,eAAO,MAAM,eAAe,oDAKiB,CAAC;AAE9C;;;;;;GAMG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,MAAM,EAAE,GAAG,UAAU,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"Button.types.d.ts","sourceRoot":"","sources":["../../../src/components/button/Button.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAE9D;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEvE;;;GAGG;AACH,eAAO,MAAM,eAAe,oDAKiB,CAAC;AAE9C;;;;;;GAMG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,MAAM,EAAE,GAAG,UAAU,MAAM,EAAE,GAAG,MAAM,MAAM,EAAE,CAAC;AAE9E;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG,OAAO,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;AAE3E;;;;GAIG;AACH,MAAM,WAAW,WAAY,SAAQ,IAAI,CAAC,qBAAqB,CAAC,QAAQ,CAAC,EAAE,UAAU,CAAC;IACpF;;;;;OAKG;IACH,OAAO,EAAE,aAAa,CAAC;IAEvB;;;;OAIG;IACH,IAAI,CAAC,EAAE,UAAU,CAAC;IAElB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;OAGG;IACH,QAAQ,EAAE,aAAa,CAAC;CACzB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Button.types.js","names":[],"sources":["../../../src/components/button/Button.types.ts"],"sourcesContent":["import type { ComponentPropsWithRef, ReactNode } from 'react';\n\n/**\n * @description Supported visual emphasis levels for {@link ButtonProps} and\n * `IconButtonProps`. Milestone 1 supports exactly these four values; there is\n * no default variant and no fifth value.\n * @see {@link https://tappqa.tobit.com/Bodywork/DesignSystem/#buttons | DesignSystem — Buttons & Aktionen}\n */\nexport type ButtonVariant = 'primary' | 'outline' | 'ghost' | 'danger';\n\n/**\n * Runtime representation of the finite Button variant contract.\n * Keep this list as the single source for Storybook controls and iteration.\n */\nexport const BUTTON_VARIANTS = [\n 'primary',\n 'outline',\n 'ghost',\n 'danger',\n] as const satisfies readonly ButtonVariant[];\n\n/**\n * @description A FontAwesome Classic icon name (e.g. `fa-plus`) or a\n * FontAwesome Brands icon including its style prefix (e.g. `fab fa-github`).\n * Classic icons resolve the matching Regular and Solid weights internally;\n * Brands icons are rendered as a single icon and must not be combined with\n * `far` or `fas`.\n */\nexport type ButtonIcon = `fa-${string}` | `fab fa-${string}`;\n\n/**\n * @description Visible content that gives a `Button` its accessible name.\n * Excludes `boolean`, `null` and `undefined` so a `Button` can never be\n * rendered without real, visible label content.\n */\nexport type ButtonContent = Exclude<ReactNode, boolean | null | undefined>;\n\n/**\n * @description Props for a visible-label native action button. Renders a\n * native `<button>` and forwards all remaining native button props, events,\n * `className` and `ref`.\n */\nexport interface ButtonProps extends Omit<ComponentPropsWithRef<'button'>, 'children'> {\n /**\n * @description Visual emphasis that communicates the action's role within\n * its action scope. At most one `primary` action should exist per closed\n * action scope (page, card, dialog, drawer, form, wizard step, or closed\n * accordion content).\n */\n variant: ButtonVariant;\n\n /**\n * @description Optional leading FontAwesome Classic or Brands icon name.\n * Always rendered before `children`; there is no trailing-icon or icon-only\n * mode on `Button` itself — use `IconButton` for an icon-only action.\n */\n icon?: ButtonIcon;\n\n /**\n * Shows a spinner while preserving the button label and prevents repeated\n * activation through the native disabled state.\n */\n loading?: boolean;\n\n /**\n * @description Visible, non-empty content used as the button label. Loading\n * preserves this content while adding a spinner.\n */\n children: ButtonContent;\n}\n"],"mappings":";;;;;AAcA,IAAa,kBAAkB;CAC7B;CACA;CACA;CACA;AACF"}
|
|
1
|
+
{"version":3,"file":"Button.types.js","names":[],"sources":["../../../src/components/button/Button.types.ts"],"sourcesContent":["import type { ComponentPropsWithRef, ReactNode } from 'react';\n\n/**\n * @description Supported visual emphasis levels for {@link ButtonProps} and\n * `IconButtonProps`. Milestone 1 supports exactly these four values; there is\n * no default variant and no fifth value.\n * @see {@link https://tappqa.tobit.com/Bodywork/DesignSystem/#buttons | DesignSystem — Buttons & Aktionen}\n */\nexport type ButtonVariant = 'primary' | 'outline' | 'ghost' | 'danger';\n\n/**\n * Runtime representation of the finite Button variant contract.\n * Keep this list as the single source for Storybook controls and iteration.\n */\nexport const BUTTON_VARIANTS = [\n 'primary',\n 'outline',\n 'ghost',\n 'danger',\n] as const satisfies readonly ButtonVariant[];\n\n/**\n * @description A FontAwesome Classic icon name (e.g. `fa-plus`) or a\n * FontAwesome Brands icon including its style prefix (e.g. `fab fa-github`).\n * Classic icons resolve the matching Regular and Solid weights internally;\n * Brands icons are rendered as a single icon and must not be combined with\n * `far` or `fas`.\n */\nexport type ButtonIcon = `fa-${string}` | `fab fa-${string}` | `ts-${string}`;\n\n/**\n * @description Visible content that gives a `Button` its accessible name.\n * Excludes `boolean`, `null` and `undefined` so a `Button` can never be\n * rendered without real, visible label content.\n */\nexport type ButtonContent = Exclude<ReactNode, boolean | null | undefined>;\n\n/**\n * @description Props for a visible-label native action button. Renders a\n * native `<button>` and forwards all remaining native button props, events,\n * `className` and `ref`.\n */\nexport interface ButtonProps extends Omit<ComponentPropsWithRef<'button'>, 'children'> {\n /**\n * @description Visual emphasis that communicates the action's role within\n * its action scope. At most one `primary` action should exist per closed\n * action scope (page, card, dialog, drawer, form, wizard step, or closed\n * accordion content).\n */\n variant: ButtonVariant;\n\n /**\n * @description Optional leading FontAwesome Classic or Brands icon name.\n * Always rendered before `children`; there is no trailing-icon or icon-only\n * mode on `Button` itself — use `IconButton` for an icon-only action.\n */\n icon?: ButtonIcon;\n\n /**\n * Shows a spinner while preserving the button label and prevents repeated\n * activation through the native disabled state.\n */\n loading?: boolean;\n\n /**\n * @description Visible, non-empty content used as the button label. Loading\n * preserves this content while adding a spinner.\n */\n children: ButtonContent;\n}\n"],"mappings":";;;;;AAcA,IAAa,kBAAkB;CAC7B;CACA;CACA;CACA;AACF"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
//#region src/components/button/button-icon/ButtonIcon.tsx
|
|
3
3
|
var ButtonIcon = ({ icon }) => {
|
|
4
|
-
const
|
|
4
|
+
const isPlainIcon = icon.startsWith("fab ") || icon.startsWith("ts-");
|
|
5
5
|
return /* @__PURE__ */ jsx("span", {
|
|
6
6
|
"aria-hidden": "true",
|
|
7
7
|
className: "chayns-button-icon",
|
|
8
|
-
children:
|
|
8
|
+
children: isPlainIcon ? /* @__PURE__ */ jsx("span", {
|
|
9
9
|
className: "chayns-button-icon__weight",
|
|
10
10
|
children: /* @__PURE__ */ jsx("i", { className: icon })
|
|
11
11
|
}) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ButtonIcon.js","names":[],"sources":["../../../../src/components/button/button-icon/ButtonIcon.tsx"],"sourcesContent":["import type { ButtonIcon as ButtonIconName } from '../Button.types.js';\n\ninterface ButtonIconProps {\n icon: ButtonIconName;\n}\n\nconst ButtonIcon = ({ icon }: ButtonIconProps) => {\n const
|
|
1
|
+
{"version":3,"file":"ButtonIcon.js","names":[],"sources":["../../../../src/components/button/button-icon/ButtonIcon.tsx"],"sourcesContent":["import type { ButtonIcon as ButtonIconName } from '../Button.types.js';\n\ninterface ButtonIconProps {\n icon: ButtonIconName;\n}\n\nconst ButtonIcon = ({ icon }: ButtonIconProps) => {\n const isPlainIcon = icon.startsWith('fab ') || icon.startsWith('ts-');\n\n return (\n <span aria-hidden=\"true\" className=\"chayns-button-icon\">\n {isPlainIcon ? (\n <span className=\"chayns-button-icon__weight\">\n <i className={icon} />\n </span>\n ) : (\n <>\n <span className=\"chayns-button-icon__weight\">\n <i className={`far ${icon}`} />\n </span>\n <span className=\"chayns-button-icon__weight chayns-button-icon__weight--active\">\n <i className={`fas ${icon}`} />\n </span>\n </>\n )}\n </span>\n );\n};\n\nexport default ButtonIcon;\n"],"mappings":";;AAMA,IAAM,cAAc,EAAE,WAA4B;CAChD,MAAM,cAAc,KAAK,WAAW,MAAM,KAAK,KAAK,WAAW,KAAK;CAEpE,OACE,oBAAC,QAAD;EAAM,eAAY;EAAO,WAAU;EAChC,UAAA,cACC,oBAAC,QAAD;GAAM,WAAU;GACd,UAAA,oBAAC,KAAD,EAAG,WAAW,KAAO,CAAA;EACjB,CAAA,IAEN,qBAAA,UAAA,EAAA,UAAA,CACE,oBAAC,QAAD;GAAM,WAAU;GACd,UAAA,oBAAC,KAAD,EAAG,WAAW,OAAO,OAAS,CAAA;EAC1B,CAAA,GACN,oBAAC,QAAD;GAAM,WAAU;GACd,UAAA,oBAAC,KAAD,EAAG,WAAW,OAAO,OAAS,CAAA;EAC1B,CAAA,CACN,EAAA,CAAA;CAEA,CAAA;AAEV"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ComboBoxOptionProps } from './ComboBox.types.js';
|
|
2
|
-
declare const ComboBox: import("react").ForwardRefExoticComponent<(Omit<import("./ComboBox.types.js").ComboBoxSingleProps, "ref"> | Omit<import("./ComboBox.types.js").ComboBoxMultipleProps, "ref">) & import("react").RefAttributes<
|
|
2
|
+
declare const ComboBox: import("react").ForwardRefExoticComponent<(Omit<import("./ComboBox.types.js").ComboBoxSingleProps, "ref"> | Omit<import("./ComboBox.types.js").ComboBoxMultipleProps, "ref">) & import("react").RefAttributes<HTMLButtonElement>> & {
|
|
3
3
|
Option: import("react").ForwardRefExoticComponent<Omit<ComboBoxOptionProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
|
|
4
4
|
};
|
|
5
5
|
export default ComboBox;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ComboBox.d.ts","sourceRoot":"","sources":["../../../src/components/combo-box/ComboBox.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ComboBox.d.ts","sourceRoot":"","sources":["../../../src/components/combo-box/ComboBox.tsx"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,mBAAmB,EAAiB,MAAM,qBAAqB,CAAC;AAua9E,QAAA,MAAM,QAAQ;;CAA0C,CAAC;AAEzD,eAAe,QAAQ,CAAC"}
|
|
@@ -2,6 +2,7 @@ import ButtonIcon from "../button/button-icon/ButtonIcon.js";
|
|
|
2
2
|
import { ComboBoxContext, getOptionLabel, useComboBoxContext } from "./ComboBoxContext.js";
|
|
3
3
|
import { Children, Fragment, forwardRef, isValidElement, useEffect, useId, useMemo, useRef, useState } from "react";
|
|
4
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { createPortal } from "react-dom";
|
|
5
6
|
//#region src/components/combo-box/ComboBox.tsx
|
|
6
7
|
function getOptionElements(children) {
|
|
7
8
|
const options = [];
|
|
@@ -13,88 +14,139 @@ function getOptionElements(children) {
|
|
|
13
14
|
});
|
|
14
15
|
return options;
|
|
15
16
|
}
|
|
16
|
-
var Option = forwardRef(function Option({ children, className, value, ...optionProps }, ref) {
|
|
17
|
+
var Option = forwardRef(function Option({ children, className, disabled = false, value, ...optionProps }, ref) {
|
|
17
18
|
const comboBox = useComboBoxContext();
|
|
18
|
-
const isVisible = comboBox.visibleValues.includes(value);
|
|
19
19
|
const isSelected = comboBox.isSelected(value);
|
|
20
|
+
const isActive = comboBox.activeValue === value;
|
|
20
21
|
const resolvedClassName = [
|
|
21
22
|
"chayns-combo-box__option",
|
|
22
23
|
isSelected ? "chayns-combo-box__option--selected" : "",
|
|
23
|
-
|
|
24
|
+
isActive ? "chayns-combo-box__option--active" : "",
|
|
25
|
+
disabled ? "chayns-combo-box__option--disabled" : "",
|
|
24
26
|
className
|
|
25
27
|
].filter(Boolean).join(" ");
|
|
26
|
-
if (!isVisible) return null;
|
|
27
28
|
return /* @__PURE__ */ jsxs("div", {
|
|
28
29
|
...optionProps,
|
|
30
|
+
"aria-disabled": disabled || void 0,
|
|
29
31
|
"aria-selected": isSelected,
|
|
30
32
|
className: resolvedClassName,
|
|
31
33
|
id: comboBox.optionId(value),
|
|
32
|
-
onMouseDown: (event) => event.preventDefault(),
|
|
33
34
|
onClick: (event) => {
|
|
34
35
|
optionProps.onClick?.(event);
|
|
35
|
-
if (!event.defaultPrevented) comboBox.select(value);
|
|
36
|
+
if (!event.defaultPrevented && !disabled) comboBox.select(value);
|
|
36
37
|
},
|
|
37
38
|
onKeyDown: (event) => {
|
|
38
39
|
optionProps.onKeyDown?.(event);
|
|
39
|
-
if (!event.defaultPrevented && (event.key === "Enter" || event.key === " ")) {
|
|
40
|
+
if (!event.defaultPrevented && !disabled && (event.key === "Enter" || event.key === " ")) {
|
|
40
41
|
event.preventDefault();
|
|
41
42
|
comboBox.select(value);
|
|
42
43
|
}
|
|
43
44
|
},
|
|
44
45
|
ref,
|
|
45
46
|
role: "option",
|
|
46
|
-
tabIndex: -1,
|
|
47
|
-
children: [
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
tabIndex: isActive && !disabled ? 0 : -1,
|
|
48
|
+
children: [
|
|
49
|
+
comboBox.multiple ? /* @__PURE__ */ jsx("span", {
|
|
50
|
+
"aria-hidden": "true",
|
|
51
|
+
className: "chayns-combo-box__checkbox",
|
|
52
|
+
"data-checked": isSelected || void 0
|
|
53
|
+
}) : null,
|
|
54
|
+
children,
|
|
55
|
+
!comboBox.multiple && isSelected ? /* @__PURE__ */ jsx("span", {
|
|
56
|
+
"aria-hidden": "true",
|
|
57
|
+
className: "chayns-combo-box__checkmark",
|
|
58
|
+
children: /* @__PURE__ */ jsx("i", { className: "fas fa-check" })
|
|
59
|
+
}) : null
|
|
60
|
+
]
|
|
52
61
|
});
|
|
53
62
|
});
|
|
54
63
|
var ComboBoxRoot = forwardRef(function ComboBoxRoot(props, ref) {
|
|
55
|
-
const { "aria-label": ariaLabel, children, className, defaultValue, disabled, id, multiple = false,
|
|
64
|
+
const { "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, children, className, defaultValue, disabled, id, multiple = false, onClick, onKeyDown, onValueChange, placeholder, value, ...buttonProps } = props;
|
|
56
65
|
const generatedId = useId();
|
|
57
|
-
const
|
|
58
|
-
const listboxId = `${
|
|
66
|
+
const triggerId = id ?? generatedId;
|
|
67
|
+
const listboxId = `${triggerId}-listbox`;
|
|
68
|
+
const labelId = `${triggerId}-label`;
|
|
59
69
|
const rootRef = useRef(null);
|
|
60
|
-
const
|
|
70
|
+
const popupRef = useRef(null);
|
|
71
|
+
const triggerRef = useRef(null);
|
|
72
|
+
const [popupStyle, setPopupStyle] = useState();
|
|
61
73
|
const optionElements = useMemo(() => getOptionElements(children), [children]);
|
|
62
74
|
const optionMap = useMemo(() => new Map(optionElements.map((option) => [option.props.value, option])), [optionElements]);
|
|
75
|
+
const focusableValues = optionElements.filter((option) => !option.props.disabled).map((option) => option.props.value);
|
|
63
76
|
const defaultValues = multiple ? defaultValue?.map((option) => option.props.value) ?? [] : [];
|
|
64
77
|
const controlledValues = multiple ? value?.map((option) => option.props.value) ?? [] : [];
|
|
65
78
|
const [uncontrolledValue, setUncontrolledValue] = useState(multiple ? "" : defaultValue ?? "");
|
|
66
79
|
const [uncontrolledValues, setUncontrolledValues] = useState(defaultValues);
|
|
67
|
-
const [inputText, setInputText] = useState(multiple ? "" : defaultValue ?? "");
|
|
68
80
|
const [open, setOpen] = useState(false);
|
|
81
|
+
const [popupMounted, setPopupMounted] = useState(false);
|
|
82
|
+
const [popupClosing, setPopupClosing] = useState(false);
|
|
69
83
|
const [activeValue, setActiveValue] = useState();
|
|
70
84
|
const isControlled = value !== void 0;
|
|
71
85
|
const selectedValue = multiple ? "" : isControlled ? value : uncontrolledValue;
|
|
72
86
|
const selectedValues = multiple ? isControlled ? controlledValues : uncontrolledValues : [];
|
|
73
|
-
const
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
87
|
+
const selectedOptions = selectedValues.map((selected) => optionMap.get(selected)).filter((option) => option !== void 0);
|
|
88
|
+
const selectedOption = optionMap.get(selectedValue);
|
|
89
|
+
const displayValue = multiple ? selectedOptions.map((option) => getOptionLabel(option.props.children)).join(", ") : selectedOption === void 0 ? "" : getOptionLabel(selectedOption.props.children);
|
|
90
|
+
const resolvedClassName = ["chayns-combo-box", open ? "chayns-combo-box--open" : ""].filter(Boolean).join(" ");
|
|
91
|
+
const triggerClassName = ["chayns-combo-box__trigger", className].filter(Boolean).join(" ");
|
|
92
|
+
const accessibleName = ariaLabelledBy ?? (ariaLabel === void 0 && placeholder !== void 0 ? labelId : void 0);
|
|
93
|
+
if (ariaLabel === void 0 && ariaLabelledBy === void 0 && placeholder === void 0) throw new Error("ComboBox requires placeholder, aria-label or aria-labelledby.");
|
|
94
|
+
useEffect(() => {
|
|
95
|
+
if (open) {
|
|
96
|
+
if (!popupMounted) {
|
|
97
|
+
setPopupMounted(true);
|
|
98
|
+
setPopupClosing(true);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (popupClosing) {
|
|
102
|
+
const frame = requestAnimationFrame(() => setPopupClosing(false));
|
|
103
|
+
return () => cancelAnimationFrame(frame);
|
|
104
|
+
}
|
|
105
|
+
} else if (popupMounted) setPopupClosing(true);
|
|
106
|
+
}, [
|
|
107
|
+
open,
|
|
108
|
+
popupClosing,
|
|
109
|
+
popupMounted
|
|
110
|
+
]);
|
|
78
111
|
useEffect(() => {
|
|
79
|
-
if (open
|
|
112
|
+
if (!open || activeValue === void 0) return;
|
|
113
|
+
document.getElementById(`${listboxId}-${activeValue.replace(/[^a-zA-Z0-9_-]/g, "-")}`)?.focus();
|
|
80
114
|
}, [
|
|
81
115
|
activeValue,
|
|
116
|
+
listboxId,
|
|
82
117
|
open,
|
|
83
|
-
|
|
118
|
+
popupMounted
|
|
84
119
|
]);
|
|
85
120
|
useEffect(() => {
|
|
86
121
|
if (!open) return;
|
|
122
|
+
function updatePopupPosition() {
|
|
123
|
+
const trigger = triggerRef.current;
|
|
124
|
+
if (!trigger) return;
|
|
125
|
+
const { bottom, left, width } = trigger.getBoundingClientRect();
|
|
126
|
+
setPopupStyle({
|
|
127
|
+
insetBlockStart: `calc(${bottom}px + var(--k4))`,
|
|
128
|
+
insetInlineStart: left,
|
|
129
|
+
inlineSize: width
|
|
130
|
+
});
|
|
131
|
+
}
|
|
87
132
|
function closeOnOutsidePress(event) {
|
|
88
|
-
if (event.target instanceof Node && !rootRef.current?.contains(event.target)) {
|
|
89
|
-
commitInput();
|
|
133
|
+
if (event.target instanceof Node && !rootRef.current?.contains(event.target) && !popupRef.current?.contains(event.target)) {
|
|
90
134
|
setOpen(false);
|
|
135
|
+
setActiveValue(void 0);
|
|
91
136
|
}
|
|
92
137
|
}
|
|
138
|
+
updatePopupPosition();
|
|
139
|
+
window.addEventListener("resize", updatePopupPosition);
|
|
140
|
+
window.addEventListener("scroll", updatePopupPosition, true);
|
|
93
141
|
document.addEventListener("pointerdown", closeOnOutsidePress);
|
|
94
|
-
return () =>
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
142
|
+
return () => {
|
|
143
|
+
window.removeEventListener("resize", updatePopupPosition);
|
|
144
|
+
window.removeEventListener("scroll", updatePopupPosition, true);
|
|
145
|
+
document.removeEventListener("pointerdown", closeOnOutsidePress);
|
|
146
|
+
};
|
|
147
|
+
}, [open]);
|
|
148
|
+
function setTriggerElement(element) {
|
|
149
|
+
triggerRef.current = element;
|
|
98
150
|
if (typeof ref === "function") ref(element);
|
|
99
151
|
else if (ref !== null) ref.current = element;
|
|
100
152
|
}
|
|
@@ -107,122 +159,124 @@ var ComboBoxRoot = forwardRef(function ComboBoxRoot(props, ref) {
|
|
|
107
159
|
const nextOptions = nextValues.map((nextValue) => optionMap.get(nextValue)).filter((option) => option !== void 0);
|
|
108
160
|
onValueChange?.(nextOptions);
|
|
109
161
|
}
|
|
110
|
-
function
|
|
111
|
-
|
|
162
|
+
function closeAndFocusTrigger() {
|
|
163
|
+
setOpen(false);
|
|
164
|
+
setActiveValue(void 0);
|
|
165
|
+
triggerRef.current?.focus();
|
|
112
166
|
}
|
|
113
167
|
function select(nextValue) {
|
|
114
|
-
if (multiple)
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
setActiveValue(void 0);
|
|
118
|
-
setOpen(true);
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
const option = optionMap.get(nextValue);
|
|
122
|
-
emitValue(nextValue);
|
|
123
|
-
setInputText(option === void 0 ? nextValue : getOptionLabel(option.props.children));
|
|
124
|
-
setActiveValue(void 0);
|
|
125
|
-
setOpen(false);
|
|
168
|
+
if (multiple) emitValues(selectedValues.includes(nextValue) ? selectedValues.filter((selected) => selected !== nextValue) : [...selectedValues, nextValue]);
|
|
169
|
+
else emitValue(nextValue);
|
|
170
|
+
closeAndFocusTrigger();
|
|
126
171
|
}
|
|
127
|
-
function
|
|
128
|
-
|
|
129
|
-
|
|
172
|
+
function openPopup() {
|
|
173
|
+
if (disabled || focusableValues.length === 0) return;
|
|
174
|
+
const selectedFocusableValue = focusableValues.find((optionValue) => multiple ? selectedValues.includes(optionValue) : selectedValue === optionValue);
|
|
175
|
+
setActiveValue(selectedFocusableValue ?? focusableValues[0]);
|
|
130
176
|
setOpen(true);
|
|
131
|
-
props.onChange?.(event);
|
|
132
177
|
}
|
|
133
|
-
function
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
178
|
+
function moveActive(direction) {
|
|
179
|
+
if (focusableValues.length === 0) return;
|
|
180
|
+
const currentIndex = activeValue === void 0 ? -1 : focusableValues.indexOf(activeValue);
|
|
181
|
+
const nextIndex = currentIndex === -1 ? direction === 1 ? 0 : focusableValues.length - 1 : (currentIndex + direction + focusableValues.length) % focusableValues.length;
|
|
182
|
+
setActiveValue(focusableValues[nextIndex]);
|
|
183
|
+
}
|
|
184
|
+
function handleTriggerClick(event) {
|
|
185
|
+
onClick?.(event);
|
|
186
|
+
if (!event.defaultPrevented) {
|
|
187
|
+
if (open) closeAndFocusTrigger();
|
|
188
|
+
else openPopup();
|
|
138
189
|
}
|
|
139
190
|
}
|
|
140
|
-
function
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
if (event.key === "
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
191
|
+
function handleTriggerKeyDown(event) {
|
|
192
|
+
onKeyDown?.(event);
|
|
193
|
+
if (event.defaultPrevented) return;
|
|
194
|
+
if (event.key === "Enter" || event.key === " " || event.key === "ArrowDown" || event.key === "ArrowUp" || event.key === "ArrowDown" && event.altKey) {
|
|
195
|
+
event.preventDefault();
|
|
196
|
+
openPopup();
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
function handleListboxKeyDown(event) {
|
|
200
|
+
if (event.key === "ArrowDown") {
|
|
201
|
+
event.preventDefault();
|
|
202
|
+
moveActive(1);
|
|
203
|
+
} else if (event.key === "ArrowUp") {
|
|
148
204
|
event.preventDefault();
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
} else if (event.key === "Enter" && activeValue !== void 0) {
|
|
205
|
+
moveActive(-1);
|
|
206
|
+
} else if (event.key === "Home") {
|
|
152
207
|
event.preventDefault();
|
|
153
|
-
|
|
208
|
+
setActiveValue(focusableValues[0]);
|
|
209
|
+
} else if (event.key === "End") {
|
|
210
|
+
event.preventDefault();
|
|
211
|
+
setActiveValue(focusableValues.at(-1));
|
|
154
212
|
} else if (event.key === "Escape") {
|
|
155
213
|
event.preventDefault();
|
|
156
|
-
|
|
157
|
-
|
|
214
|
+
closeAndFocusTrigger();
|
|
215
|
+
} else if (event.key === "Tab") {
|
|
158
216
|
setOpen(false);
|
|
217
|
+
setActiveValue(void 0);
|
|
159
218
|
}
|
|
160
|
-
props.onKeyDown?.(event);
|
|
161
219
|
}
|
|
162
|
-
const resolvedClassName = ["chayns-combo-box", open ? "chayns-combo-box--open" : ""].filter(Boolean).join(" ");
|
|
163
|
-
const describedLabel = placeholder === void 0 ? void 0 : `${inputId}-label`;
|
|
164
|
-
const selectedOptions = selectedValues.map((selected) => optionMap.get(selected)).filter((option) => option !== void 0);
|
|
165
|
-
const inputClassName = ["chayns-combo-box__input", className].filter(Boolean).join(" ");
|
|
166
220
|
return /* @__PURE__ */ jsx(ComboBoxContext.Provider, {
|
|
167
221
|
value: {
|
|
168
222
|
activeValue,
|
|
169
|
-
close: () => setOpen(false),
|
|
170
|
-
inputText,
|
|
171
223
|
isSelected: (optionValue) => multiple ? selectedValues.includes(optionValue) : selectedValue === optionValue,
|
|
172
224
|
multiple,
|
|
173
225
|
optionId: (optionValue) => `${listboxId}-${optionValue.replace(/[^a-zA-Z0-9_-]/g, "-")}`,
|
|
174
|
-
select
|
|
175
|
-
visibleValues
|
|
226
|
+
select
|
|
176
227
|
},
|
|
177
228
|
children: /* @__PURE__ */ jsxs("div", {
|
|
178
229
|
className: resolvedClassName,
|
|
179
230
|
ref: rootRef,
|
|
180
231
|
children: [
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
"aria-hidden": "true",
|
|
184
|
-
children: selectedOptions.map((option) => /* @__PURE__ */ jsx("span", {
|
|
185
|
-
className: "chayns-combo-box__chip",
|
|
186
|
-
children: option.props.children
|
|
187
|
-
}, option.props.value))
|
|
188
|
-
}) : null,
|
|
189
|
-
/* @__PURE__ */ jsx("input", {
|
|
190
|
-
...inputProps,
|
|
191
|
-
"aria-activedescendant": open && activeValue ? `${listboxId}-${activeValue.replace(/[^a-zA-Z0-9_-]/g, "-")}` : void 0,
|
|
192
|
-
"aria-autocomplete": "list",
|
|
232
|
+
/* @__PURE__ */ jsxs("button", {
|
|
233
|
+
...buttonProps,
|
|
193
234
|
"aria-controls": listboxId,
|
|
194
235
|
"aria-expanded": open,
|
|
236
|
+
"aria-haspopup": "listbox",
|
|
195
237
|
"aria-label": ariaLabel,
|
|
196
|
-
|
|
238
|
+
"aria-labelledby": accessibleName,
|
|
239
|
+
className: triggerClassName,
|
|
197
240
|
disabled,
|
|
198
|
-
id:
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
241
|
+
id: triggerId,
|
|
242
|
+
onClick: handleTriggerClick,
|
|
243
|
+
onKeyDown: handleTriggerKeyDown,
|
|
244
|
+
ref: setTriggerElement,
|
|
245
|
+
type: "button",
|
|
246
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
247
|
+
className: "chayns-combo-box__value",
|
|
248
|
+
children: displayValue || placeholder
|
|
249
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
250
|
+
"aria-hidden": "true",
|
|
251
|
+
className: "chayns-combo-box__chevron",
|
|
252
|
+
children: /* @__PURE__ */ jsx(ButtonIcon, { icon: "fa-chevron-down" })
|
|
253
|
+
})]
|
|
207
254
|
}),
|
|
208
|
-
placeholder !== void 0 ? /* @__PURE__ */ jsx("
|
|
255
|
+
placeholder !== void 0 ? /* @__PURE__ */ jsx("span", {
|
|
209
256
|
className: "chayns-combo-box__label",
|
|
210
|
-
|
|
211
|
-
id: describedLabel,
|
|
257
|
+
id: labelId,
|
|
212
258
|
children: placeholder
|
|
213
259
|
}) : null,
|
|
214
|
-
/* @__PURE__ */ jsx("
|
|
215
|
-
"aria-hidden":
|
|
216
|
-
className: "chayns-combo-box__chevron",
|
|
217
|
-
children: /* @__PURE__ */ jsx(ButtonIcon, { icon: "fa-chevron-down" })
|
|
218
|
-
}),
|
|
219
|
-
open ? /* @__PURE__ */ jsx("div", {
|
|
260
|
+
popupMounted && typeof document !== "undefined" ? createPortal(/* @__PURE__ */ jsx("div", {
|
|
261
|
+
"aria-hidden": !open && popupClosing ? true : void 0,
|
|
220
262
|
"aria-multiselectable": multiple || void 0,
|
|
221
263
|
className: "chayns-combo-box__popup",
|
|
264
|
+
"data-state": popupClosing ? "closed" : "open",
|
|
222
265
|
id: listboxId,
|
|
266
|
+
inert: !open && popupClosing,
|
|
267
|
+
onKeyDown: handleListboxKeyDown,
|
|
268
|
+
onTransitionEnd: (event) => {
|
|
269
|
+
if (popupClosing && event.propertyName === "opacity") {
|
|
270
|
+
setPopupMounted(false);
|
|
271
|
+
setPopupClosing(false);
|
|
272
|
+
}
|
|
273
|
+
},
|
|
274
|
+
ref: popupRef,
|
|
223
275
|
role: "listbox",
|
|
276
|
+
style: popupStyle,
|
|
277
|
+
tabIndex: -1,
|
|
224
278
|
children
|
|
225
|
-
}) : null
|
|
279
|
+
}), document.body) : null
|
|
226
280
|
]
|
|
227
281
|
})
|
|
228
282
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ComboBox.js","names":[],"sources":["../../../src/components/combo-box/ComboBox.tsx"],"sourcesContent":["import {\n Children,\n forwardRef,\n Fragment,\n isValidElement,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport type { ChangeEvent, FocusEvent, KeyboardEvent, ReactElement, ReactNode } from 'react';\n\nimport ButtonIcon from '../button/button-icon/ButtonIcon.js';\nimport { ComboBoxContext, getOptionLabel, useComboBoxContext } from './ComboBoxContext.js';\nimport type { ComboBoxOptionProps, ComboBoxProps } from './ComboBox.types.js';\n\nfunction getOptionElements(children: ComboBoxProps['children']) {\n const options: ReactElement<ComboBoxOptionProps>[] = [];\n\n Children.forEach(children, (child) => {\n if (!isValidElement(child)) {\n return;\n }\n\n const element = child as ReactElement<{ children?: ReactNode; value?: string }>;\n\n if (element.type === Fragment) {\n options.push(...getOptionElements(element.props.children));\n } else if (typeof element.props.value === 'string') {\n options.push(element as ReactElement<ComboBoxOptionProps>);\n }\n });\n\n return options;\n}\n\nconst Option = forwardRef<HTMLDivElement, ComboBoxOptionProps>(function Option(\n { children, className, value, ...optionProps },\n ref,\n) {\n const comboBox = useComboBoxContext();\n const isVisible = comboBox.visibleValues.includes(value);\n const isSelected = comboBox.isSelected(value);\n const resolvedClassName = [\n 'chayns-combo-box__option',\n isSelected ? 'chayns-combo-box__option--selected' : '',\n comboBox.activeValue === value ? 'chayns-combo-box__option--active' : '',\n className,\n ]\n .filter(Boolean)\n .join(' ');\n\n if (!isVisible) {\n return null;\n }\n\n return (\n <div\n {...optionProps}\n aria-selected={isSelected}\n className={resolvedClassName}\n id={comboBox.optionId(value)}\n onMouseDown={(event) => event.preventDefault()}\n onClick={(event) => {\n optionProps.onClick?.(event);\n if (!event.defaultPrevented) {\n comboBox.select(value);\n }\n }}\n onKeyDown={(event) => {\n optionProps.onKeyDown?.(event);\n if (!event.defaultPrevented && (event.key === 'Enter' || event.key === ' ')) {\n event.preventDefault();\n comboBox.select(value);\n }\n }}\n ref={ref}\n role=\"option\"\n tabIndex={-1}\n >\n {comboBox.multiple ? (\n <span\n aria-hidden=\"true\"\n className=\"chayns-combo-box__checkbox\"\n data-checked={isSelected || undefined}\n />\n ) : null}\n {children}\n </div>\n );\n});\n\nconst ComboBoxRoot = forwardRef<HTMLInputElement, ComboBoxProps>(function ComboBoxRoot(props, ref) {\n const {\n 'aria-label': ariaLabel,\n children,\n className,\n defaultValue,\n disabled,\n id,\n multiple = false,\n onValueChange,\n openOnFocus = true,\n placeholder,\n value,\n ...inputProps\n } = props;\n const generatedId = useId();\n const inputId = id ?? generatedId;\n const listboxId = `${inputId}-listbox`;\n const rootRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const optionElements = useMemo(() => getOptionElements(children), [children]);\n const optionMap = useMemo(\n () => new Map(optionElements.map((option) => [option.props.value, option])),\n [optionElements],\n );\n const defaultValues = multiple\n ? ((defaultValue as ReactElement<ComboBoxOptionProps>[] | undefined)?.map(\n (option) => option.props.value,\n ) ?? [])\n : [];\n const controlledValues = multiple\n ? ((value as ReactElement<ComboBoxOptionProps>[] | undefined)?.map(\n (option) => option.props.value,\n ) ?? [])\n : [];\n const [uncontrolledValue, setUncontrolledValue] = useState(\n multiple ? '' : ((defaultValue as string | undefined) ?? ''),\n );\n const [uncontrolledValues, setUncontrolledValues] = useState(defaultValues);\n const [inputText, setInputText] = useState(\n multiple ? '' : ((defaultValue as string | undefined) ?? ''),\n );\n const [open, setOpen] = useState(false);\n const [activeValue, setActiveValue] = useState<string>();\n const isControlled = value !== undefined;\n const selectedValue = multiple ? '' : isControlled ? (value as string) : uncontrolledValue;\n const selectedValues = multiple ? (isControlled ? controlledValues : uncontrolledValues) : [];\n const normalizedFilter = inputText.trim().toLocaleLowerCase();\n const visibleValues = optionElements\n .filter((option) => {\n const label = getOptionLabel(option.props.children).toLocaleLowerCase();\n return normalizedFilter === '' || label.includes(normalizedFilter);\n })\n .map((option) => option.props.value);\n\n useEffect(() => {\n if (open && activeValue === undefined) {\n setActiveValue(visibleValues[0]);\n }\n }, [activeValue, open, visibleValues]);\n\n useEffect(() => {\n if (!open) {\n return undefined;\n }\n\n function closeOnOutsidePress(event: PointerEvent) {\n if (event.target instanceof Node && !rootRef.current?.contains(event.target)) {\n commitInput();\n setOpen(false);\n }\n }\n\n document.addEventListener('pointerdown', closeOnOutsidePress);\n return () => document.removeEventListener('pointerdown', closeOnOutsidePress);\n });\n\n function setInputElement(element: HTMLInputElement | null) {\n inputRef.current = element;\n\n if (typeof ref === 'function') {\n ref(element);\n } else if (ref !== null) {\n ref.current = element;\n }\n }\n\n function emitValue(nextValue: string) {\n if (!isControlled) {\n setUncontrolledValue(nextValue);\n }\n\n (onValueChange as ((next: string) => void) | undefined)?.(nextValue);\n }\n\n function emitValues(nextValues: string[]) {\n if (!isControlled) {\n setUncontrolledValues(nextValues);\n }\n\n const nextOptions = nextValues\n .map((nextValue) => optionMap.get(nextValue))\n .filter((option): option is ReactElement<ComboBoxOptionProps> => option !== undefined);\n (onValueChange as ((next: ReactElement<ComboBoxOptionProps>[]) => void) | undefined)?.(\n nextOptions,\n );\n }\n\n function commitInput() {\n if (!multiple && inputText !== selectedValue) {\n emitValue(inputText);\n }\n }\n\n function select(nextValue: string) {\n if (multiple) {\n const nextValues = selectedValues.includes(nextValue)\n ? selectedValues.filter((selected) => selected !== nextValue)\n : [...selectedValues, nextValue];\n emitValues(nextValues);\n setInputText('');\n setActiveValue(undefined);\n setOpen(true);\n return;\n }\n\n const option = optionMap.get(nextValue);\n emitValue(nextValue);\n setInputText(option === undefined ? nextValue : getOptionLabel(option.props.children));\n setActiveValue(undefined);\n setOpen(false);\n }\n\n function handleChange(event: ChangeEvent<HTMLInputElement>) {\n setInputText(event.target.value);\n setActiveValue(undefined);\n setOpen(true);\n props.onChange?.(event);\n }\n\n function handleBlur(event: FocusEvent<HTMLInputElement>) {\n props.onBlur?.(event);\n if (!rootRef.current?.contains(event.relatedTarget)) {\n commitInput();\n setOpen(false);\n }\n }\n\n function handleKeyDown(event: KeyboardEvent<HTMLInputElement>) {\n const currentIndex = activeValue === undefined ? -1 : visibleValues.indexOf(activeValue);\n let nextIndex = currentIndex;\n\n if (event.key === 'ArrowDown') nextIndex = Math.min(currentIndex + 1, visibleValues.length - 1);\n if (event.key === 'ArrowUp') nextIndex = Math.max(currentIndex - 1, 0);\n if (event.key === 'Home') nextIndex = 0;\n if (event.key === 'End') nextIndex = visibleValues.length - 1;\n\n if (nextIndex !== currentIndex && visibleValues[nextIndex] !== undefined) {\n event.preventDefault();\n setActiveValue(visibleValues[nextIndex]);\n setOpen(true);\n } else if (event.key === 'Enter' && activeValue !== undefined) {\n event.preventDefault();\n select(activeValue);\n } else if (event.key === 'Escape') {\n event.preventDefault();\n setInputText(multiple ? '' : selectedValue);\n setActiveValue(undefined);\n setOpen(false);\n }\n\n props.onKeyDown?.(event);\n }\n\n const resolvedClassName = ['chayns-combo-box', open ? 'chayns-combo-box--open' : '']\n .filter(Boolean)\n .join(' ');\n const describedLabel = placeholder === undefined ? undefined : `${inputId}-label`;\n const selectedOptions = selectedValues\n .map((selected) => optionMap.get(selected))\n .filter((option): option is ReactElement<ComboBoxOptionProps> => option !== undefined);\n const inputClassName = ['chayns-combo-box__input', className].filter(Boolean).join(' ');\n\n return (\n <ComboBoxContext.Provider\n value={{\n activeValue,\n close: () => setOpen(false),\n inputText,\n isSelected: (optionValue) =>\n multiple ? selectedValues.includes(optionValue) : selectedValue === optionValue,\n multiple,\n optionId: (optionValue) => `${listboxId}-${optionValue.replace(/[^a-zA-Z0-9_-]/g, '-')}`,\n select,\n visibleValues,\n }}\n >\n <div className={resolvedClassName} ref={rootRef}>\n {multiple ? (\n <div className=\"chayns-combo-box__chips\" aria-hidden=\"true\">\n {selectedOptions.map((option) => (\n <span className=\"chayns-combo-box__chip\" key={option.props.value}>\n {option.props.children}\n </span>\n ))}\n </div>\n ) : null}\n <input\n {...inputProps}\n aria-activedescendant={\n open && activeValue\n ? `${listboxId}-${activeValue.replace(/[^a-zA-Z0-9_-]/g, '-')}`\n : undefined\n }\n aria-autocomplete=\"list\"\n aria-controls={listboxId}\n aria-expanded={open}\n aria-label={ariaLabel}\n className={inputClassName}\n disabled={disabled}\n id={inputId}\n onBlur={handleBlur}\n onChange={handleChange}\n onFocus={() => openOnFocus && setOpen(true)}\n onKeyDown={handleKeyDown}\n placeholder={placeholder === undefined ? undefined : ' '}\n ref={setInputElement}\n role=\"combobox\"\n value={inputText}\n />\n {placeholder !== undefined ? (\n <label className=\"chayns-combo-box__label\" htmlFor={inputId} id={describedLabel}>\n {placeholder}\n </label>\n ) : null}\n <span aria-hidden=\"true\" className=\"chayns-combo-box__chevron\">\n <ButtonIcon icon=\"fa-chevron-down\" />\n </span>\n {open ? (\n <div\n aria-multiselectable={multiple || undefined}\n className=\"chayns-combo-box__popup\"\n id={listboxId}\n role=\"listbox\"\n >\n {children}\n </div>\n ) : null}\n </div>\n </ComboBoxContext.Provider>\n );\n});\n\nconst ComboBox = Object.assign(ComboBoxRoot, { Option });\n\nexport default ComboBox;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ComboBox.js","names":[],"sources":["../../../src/components/combo-box/ComboBox.tsx"],"sourcesContent":["import {\n Children,\n forwardRef,\n Fragment,\n isValidElement,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport type { CSSProperties, KeyboardEvent, MouseEvent, ReactElement, ReactNode } from 'react';\n\nimport ButtonIcon from '../button/button-icon/ButtonIcon.js';\nimport { ComboBoxContext, getOptionLabel, useComboBoxContext } from './ComboBoxContext.js';\nimport type { ComboBoxOptionProps, ComboBoxProps } from './ComboBox.types.js';\n\nfunction getOptionElements(children: ComboBoxProps['children']) {\n const options: ReactElement<ComboBoxOptionProps>[] = [];\n\n Children.forEach(children, (child) => {\n if (!isValidElement(child)) {\n return;\n }\n\n const element = child as ReactElement<{ children?: ReactNode; value?: string }>;\n\n if (element.type === Fragment) {\n options.push(...getOptionElements(element.props.children));\n } else if (typeof element.props.value === 'string') {\n options.push(element as ReactElement<ComboBoxOptionProps>);\n }\n });\n\n return options;\n}\n\nconst Option = forwardRef<HTMLDivElement, ComboBoxOptionProps>(function Option(\n { children, className, disabled = false, value, ...optionProps },\n ref,\n) {\n const comboBox = useComboBoxContext();\n const isSelected = comboBox.isSelected(value);\n const isActive = comboBox.activeValue === value;\n const resolvedClassName = [\n 'chayns-combo-box__option',\n isSelected ? 'chayns-combo-box__option--selected' : '',\n isActive ? 'chayns-combo-box__option--active' : '',\n disabled ? 'chayns-combo-box__option--disabled' : '',\n className,\n ]\n .filter(Boolean)\n .join(' ');\n\n return (\n <div\n {...optionProps}\n aria-disabled={disabled || undefined}\n aria-selected={isSelected}\n className={resolvedClassName}\n id={comboBox.optionId(value)}\n onClick={(event) => {\n optionProps.onClick?.(event);\n if (!event.defaultPrevented && !disabled) {\n comboBox.select(value);\n }\n }}\n onKeyDown={(event) => {\n optionProps.onKeyDown?.(event);\n if (!event.defaultPrevented && !disabled && (event.key === 'Enter' || event.key === ' ')) {\n event.preventDefault();\n comboBox.select(value);\n }\n }}\n ref={ref}\n role=\"option\"\n tabIndex={isActive && !disabled ? 0 : -1}\n >\n {comboBox.multiple ? (\n <span\n aria-hidden=\"true\"\n className=\"chayns-combo-box__checkbox\"\n data-checked={isSelected || undefined}\n />\n ) : null}\n {children}\n {!comboBox.multiple && isSelected ? (\n <span aria-hidden=\"true\" className=\"chayns-combo-box__checkmark\">\n <i className=\"fas fa-check\" />\n </span>\n ) : null}\n </div>\n );\n});\n\nconst ComboBoxRoot = forwardRef<HTMLButtonElement, ComboBoxProps>(\n function ComboBoxRoot(props, ref) {\n const {\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n children,\n className,\n defaultValue,\n disabled,\n id,\n multiple = false,\n onClick,\n onKeyDown,\n onValueChange,\n placeholder,\n value,\n ...buttonProps\n } = props;\n const generatedId = useId();\n const triggerId = id ?? generatedId;\n const listboxId = `${triggerId}-listbox`;\n const labelId = `${triggerId}-label`;\n const rootRef = useRef<HTMLDivElement>(null);\n const popupRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLButtonElement>(null);\n const [popupStyle, setPopupStyle] = useState<CSSProperties>();\n const optionElements = useMemo(() => getOptionElements(children), [children]);\n const optionMap = useMemo(\n () => new Map(optionElements.map((option) => [option.props.value, option])),\n [optionElements],\n );\n const focusableValues = optionElements\n .filter((option) => !option.props.disabled)\n .map((option) => option.props.value);\n const defaultValues = multiple\n ? ((defaultValue as ReactElement<ComboBoxOptionProps>[] | undefined)?.map(\n (option) => option.props.value,\n ) ?? [])\n : [];\n const controlledValues = multiple\n ? ((value as ReactElement<ComboBoxOptionProps>[] | undefined)?.map(\n (option) => option.props.value,\n ) ?? [])\n : [];\n const [uncontrolledValue, setUncontrolledValue] = useState(\n multiple ? '' : ((defaultValue as string | undefined) ?? ''),\n );\n const [uncontrolledValues, setUncontrolledValues] = useState(defaultValues);\n const [open, setOpen] = useState(false);\n const [popupMounted, setPopupMounted] = useState(false);\n const [popupClosing, setPopupClosing] = useState(false);\n const [activeValue, setActiveValue] = useState<string>();\n const isControlled = value !== undefined;\n const selectedValue = multiple ? '' : isControlled ? (value as string) : uncontrolledValue;\n const selectedValues = multiple ? (isControlled ? controlledValues : uncontrolledValues) : [];\n const selectedOptions = selectedValues\n .map((selected) => optionMap.get(selected))\n .filter((option): option is ReactElement<ComboBoxOptionProps> => option !== undefined);\n const selectedOption = optionMap.get(selectedValue);\n const displayValue = multiple\n ? selectedOptions.map((option) => getOptionLabel(option.props.children)).join(', ')\n : selectedOption === undefined\n ? ''\n : getOptionLabel(selectedOption.props.children);\n const resolvedClassName = ['chayns-combo-box', open ? 'chayns-combo-box--open' : '']\n .filter(Boolean)\n .join(' ');\n const triggerClassName = ['chayns-combo-box__trigger', className].filter(Boolean).join(' ');\n const accessibleName =\n ariaLabelledBy ??\n (ariaLabel === undefined && placeholder !== undefined ? labelId : undefined);\n\n if (ariaLabel === undefined && ariaLabelledBy === undefined && placeholder === undefined) {\n throw new Error('ComboBox requires placeholder, aria-label or aria-labelledby.');\n }\n\n useEffect(() => {\n if (open) {\n if (!popupMounted) {\n setPopupMounted(true);\n setPopupClosing(true);\n return undefined;\n }\n\n if (popupClosing) {\n const frame = requestAnimationFrame(() => setPopupClosing(false));\n return () => cancelAnimationFrame(frame);\n }\n } else if (popupMounted) {\n setPopupClosing(true);\n }\n\n return undefined;\n }, [open, popupClosing, popupMounted]);\n\n useEffect(() => {\n if (!open || activeValue === undefined) {\n return;\n }\n\n document\n .getElementById(`${listboxId}-${activeValue.replace(/[^a-zA-Z0-9_-]/g, '-')}`)\n ?.focus();\n }, [activeValue, listboxId, open, popupMounted]);\n\n useEffect(() => {\n if (!open) {\n return undefined;\n }\n\n function updatePopupPosition() {\n const trigger = triggerRef.current;\n if (!trigger) {\n return;\n }\n\n const { bottom, left, width } = trigger.getBoundingClientRect();\n setPopupStyle({\n insetBlockStart: `calc(${bottom}px + var(--k4))`,\n insetInlineStart: left,\n inlineSize: width,\n });\n }\n\n function closeOnOutsidePress(event: PointerEvent) {\n if (\n event.target instanceof Node &&\n !rootRef.current?.contains(event.target) &&\n !popupRef.current?.contains(event.target)\n ) {\n setOpen(false);\n setActiveValue(undefined);\n }\n }\n\n updatePopupPosition();\n window.addEventListener('resize', updatePopupPosition);\n window.addEventListener('scroll', updatePopupPosition, true);\n document.addEventListener('pointerdown', closeOnOutsidePress);\n return () => {\n window.removeEventListener('resize', updatePopupPosition);\n window.removeEventListener('scroll', updatePopupPosition, true);\n document.removeEventListener('pointerdown', closeOnOutsidePress);\n };\n }, [open]);\n\n function setTriggerElement(element: HTMLButtonElement | null) {\n triggerRef.current = element;\n\n if (typeof ref === 'function') {\n ref(element);\n } else if (ref !== null) {\n ref.current = element;\n }\n }\n\n function emitValue(nextValue: string) {\n if (!isControlled) {\n setUncontrolledValue(nextValue);\n }\n\n (onValueChange as ((next: string) => void) | undefined)?.(nextValue);\n }\n\n function emitValues(nextValues: string[]) {\n if (!isControlled) {\n setUncontrolledValues(nextValues);\n }\n\n const nextOptions = nextValues\n .map((nextValue) => optionMap.get(nextValue))\n .filter((option): option is ReactElement<ComboBoxOptionProps> => option !== undefined);\n (onValueChange as ((next: ReactElement<ComboBoxOptionProps>[]) => void) | undefined)?.(\n nextOptions,\n );\n }\n\n function closeAndFocusTrigger() {\n setOpen(false);\n setActiveValue(undefined);\n triggerRef.current?.focus();\n }\n\n function select(nextValue: string) {\n if (multiple) {\n const nextValues = selectedValues.includes(nextValue)\n ? selectedValues.filter((selected) => selected !== nextValue)\n : [...selectedValues, nextValue];\n emitValues(nextValues);\n } else {\n emitValue(nextValue);\n }\n\n closeAndFocusTrigger();\n }\n\n function openPopup() {\n if (disabled || focusableValues.length === 0) {\n return;\n }\n\n const selectedFocusableValue = focusableValues.find((optionValue) =>\n multiple ? selectedValues.includes(optionValue) : selectedValue === optionValue,\n );\n setActiveValue(selectedFocusableValue ?? focusableValues[0]);\n setOpen(true);\n }\n\n function moveActive(direction: 1 | -1) {\n if (focusableValues.length === 0) {\n return;\n }\n\n const currentIndex = activeValue === undefined ? -1 : focusableValues.indexOf(activeValue);\n const nextIndex =\n currentIndex === -1\n ? direction === 1\n ? 0\n : focusableValues.length - 1\n : (currentIndex + direction + focusableValues.length) % focusableValues.length;\n setActiveValue(focusableValues[nextIndex]);\n }\n\n function handleTriggerClick(event: MouseEvent<HTMLButtonElement>) {\n onClick?.(event);\n if (!event.defaultPrevented) {\n if (open) {\n closeAndFocusTrigger();\n } else {\n openPopup();\n }\n }\n }\n\n function handleTriggerKeyDown(event: KeyboardEvent<HTMLButtonElement>) {\n onKeyDown?.(event);\n if (event.defaultPrevented) {\n return;\n }\n\n if (\n event.key === 'Enter' ||\n event.key === ' ' ||\n event.key === 'ArrowDown' ||\n event.key === 'ArrowUp' ||\n (event.key === 'ArrowDown' && event.altKey)\n ) {\n event.preventDefault();\n openPopup();\n }\n }\n\n function handleListboxKeyDown(event: KeyboardEvent<HTMLDivElement>) {\n if (event.key === 'ArrowDown') {\n event.preventDefault();\n moveActive(1);\n } else if (event.key === 'ArrowUp') {\n event.preventDefault();\n moveActive(-1);\n } else if (event.key === 'Home') {\n event.preventDefault();\n setActiveValue(focusableValues[0]);\n } else if (event.key === 'End') {\n event.preventDefault();\n setActiveValue(focusableValues.at(-1));\n } else if (event.key === 'Escape') {\n event.preventDefault();\n closeAndFocusTrigger();\n } else if (event.key === 'Tab') {\n setOpen(false);\n setActiveValue(undefined);\n }\n }\n\n return (\n <ComboBoxContext.Provider\n value={{\n activeValue,\n isSelected: (optionValue) =>\n multiple ? selectedValues.includes(optionValue) : selectedValue === optionValue,\n multiple,\n optionId: (optionValue) => `${listboxId}-${optionValue.replace(/[^a-zA-Z0-9_-]/g, '-')}`,\n select,\n }}\n >\n <div className={resolvedClassName} ref={rootRef}>\n <button\n {...buttonProps}\n aria-controls={listboxId}\n aria-expanded={open}\n aria-haspopup=\"listbox\"\n aria-label={ariaLabel}\n aria-labelledby={accessibleName}\n className={triggerClassName}\n disabled={disabled}\n id={triggerId}\n onClick={handleTriggerClick}\n onKeyDown={handleTriggerKeyDown}\n ref={setTriggerElement}\n type=\"button\"\n >\n <span className=\"chayns-combo-box__value\">{displayValue || placeholder}</span>\n <span aria-hidden=\"true\" className=\"chayns-combo-box__chevron\">\n <ButtonIcon icon=\"fa-chevron-down\" />\n </span>\n </button>\n {placeholder !== undefined ? (\n <span className=\"chayns-combo-box__label\" id={labelId}>\n {placeholder}\n </span>\n ) : null}\n {popupMounted && typeof document !== 'undefined'\n ? createPortal(\n <div\n aria-hidden={!open && popupClosing ? true : undefined}\n aria-multiselectable={multiple || undefined}\n className=\"chayns-combo-box__popup\"\n data-state={popupClosing ? 'closed' : 'open'}\n id={listboxId}\n inert={!open && popupClosing}\n onKeyDown={handleListboxKeyDown}\n onTransitionEnd={(event) => {\n if (popupClosing && event.propertyName === 'opacity') {\n setPopupMounted(false);\n setPopupClosing(false);\n }\n }}\n ref={popupRef}\n role=\"listbox\"\n style={popupStyle}\n tabIndex={-1}\n >\n {children}\n </div>,\n document.body,\n )\n : null}\n </div>\n </ComboBoxContext.Provider>\n );\n },\n);\n\nconst ComboBox = Object.assign(ComboBoxRoot, { Option });\n\nexport default ComboBox;\n"],"mappings":"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"}
|
|
@@ -1,19 +1,15 @@
|
|
|
1
1
|
import type { ComponentPropsWithRef, ReactElement, ReactNode } from 'react';
|
|
2
|
-
export interface ComboBoxOptionProps extends Omit<ComponentPropsWithRef<'div'>, 'children' | 'role' | 'aria-selected'> {
|
|
2
|
+
export interface ComboBoxOptionProps extends Omit<ComponentPropsWithRef<'div'>, 'children' | 'role' | 'aria-selected' | 'tabIndex'> {
|
|
3
3
|
value: string;
|
|
4
4
|
children: Exclude<ReactNode, boolean | null | undefined>;
|
|
5
|
+
disabled?: boolean;
|
|
5
6
|
role?: never;
|
|
6
7
|
'aria-selected'?: never;
|
|
8
|
+
tabIndex?: never;
|
|
7
9
|
}
|
|
8
|
-
type ComboBoxBaseProps = Omit<ComponentPropsWithRef<'
|
|
10
|
+
type ComboBoxBaseProps = Omit<ComponentPropsWithRef<'button'>, 'children' | 'defaultValue' | 'value' | 'multiple' | 'type' | 'aria-controls' | 'aria-expanded' | 'aria-haspopup'> & {
|
|
9
11
|
children: ReactNode;
|
|
10
|
-
/** Bodywork floating label. */
|
|
11
12
|
placeholder?: string;
|
|
12
|
-
/** Opens the popup when the input receives focus. */
|
|
13
|
-
openOnFocus?: boolean;
|
|
14
|
-
onBlur?: ComponentPropsWithRef<'input'>['onBlur'];
|
|
15
|
-
onChange?: ComponentPropsWithRef<'input'>['onChange'];
|
|
16
|
-
onKeyDown?: ComponentPropsWithRef<'input'>['onKeyDown'];
|
|
17
13
|
};
|
|
18
14
|
export interface ComboBoxSingleProps extends ComboBoxBaseProps {
|
|
19
15
|
multiple?: false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ComboBox.types.d.ts","sourceRoot":"","sources":["../../../src/components/combo-box/ComboBox.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAE5E,MAAM,WAAW,mBAAoB,SAAQ,IAAI,CAC/C,qBAAqB,CAAC,KAAK,CAAC,EAC5B,UAAU,GAAG,MAAM,GAAG,eAAe,
|
|
1
|
+
{"version":3,"file":"ComboBox.types.d.ts","sourceRoot":"","sources":["../../../src/components/combo-box/ComboBox.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAE5E,MAAM,WAAW,mBAAoB,SAAQ,IAAI,CAC/C,qBAAqB,CAAC,KAAK,CAAC,EAC5B,UAAU,GAAG,MAAM,GAAG,eAAe,GAAG,UAAU,CACnD;IACC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IACzD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,KAAK,CAAC;IACb,eAAe,CAAC,EAAE,KAAK,CAAC;IACxB,QAAQ,CAAC,EAAE,KAAK,CAAC;CAClB;AAED,KAAK,iBAAiB,GAAG,IAAI,CAC3B,qBAAqB,CAAC,QAAQ,CAAC,EAC7B,UAAU,GACV,cAAc,GACd,OAAO,GACP,UAAU,GACV,MAAM,GACN,eAAe,GACf,eAAe,GACf,eAAe,CAClB,GAAG;IACF,QAAQ,EAAE,SAAS,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,WAAW,mBAAoB,SAAQ,iBAAiB;IAC5D,QAAQ,CAAC,EAAE,KAAK,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AAED,MAAM,WAAW,qBAAsB,SAAQ,iBAAiB;IAC9D,QAAQ,EAAE,IAAI,CAAC;IACf,KAAK,CAAC,EAAE,YAAY,CAAC,mBAAmB,CAAC,EAAE,CAAC;IAC5C,YAAY,CAAC,EAAE,YAAY,CAAC,mBAAmB,CAAC,EAAE,CAAC;IACnD,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,mBAAmB,CAAC,EAAE,KAAK,IAAI,CAAC;CACtE;AAED,MAAM,MAAM,aAAa,GAAG,mBAAmB,GAAG,qBAAqB,CAAC"}
|
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import type { ComboBoxOptionProps } from './ComboBox.types.js';
|
|
2
2
|
export interface ComboBoxContextValue {
|
|
3
3
|
activeValue?: string | undefined;
|
|
4
|
-
close: () => void;
|
|
5
|
-
inputText: string;
|
|
6
4
|
isSelected: (value: string) => boolean;
|
|
7
5
|
multiple: boolean;
|
|
8
6
|
optionId: (value: string) => string;
|
|
9
7
|
select: (value: string) => void;
|
|
10
|
-
visibleValues: string[];
|
|
11
8
|
}
|
|
12
9
|
export declare const ComboBoxContext: import("react").Context<ComboBoxContextValue | null>;
|
|
13
10
|
export declare function useComboBoxContext(): ComboBoxContextValue;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ComboBoxContext.d.ts","sourceRoot":"","sources":["../../../src/components/combo-box/ComboBoxContext.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE/D,MAAM,WAAW,oBAAoB;IACnC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,
|
|
1
|
+
{"version":3,"file":"ComboBoxContext.d.ts","sourceRoot":"","sources":["../../../src/components/combo-box/ComboBoxContext.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE/D,MAAM,WAAW,oBAAoB;IACnC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IACvC,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC;AAED,eAAO,MAAM,eAAe,sDAAmD,CAAC;AAEhF,wBAAgB,kBAAkB,IAAI,oBAAoB,CAQzD;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,mBAAmB,CAAC,UAAU,CAAC,GAAG,MAAM,CAM9E"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ComboBoxContext.js","names":[],"sources":["../../../src/components/combo-box/ComboBoxContext.ts"],"sourcesContent":["import { createContext, useContext } from 'react';\n\nimport type { ComboBoxOptionProps } from './ComboBox.types.js';\n\nexport interface ComboBoxContextValue {\n activeValue?: string | undefined;\n
|
|
1
|
+
{"version":3,"file":"ComboBoxContext.js","names":[],"sources":["../../../src/components/combo-box/ComboBoxContext.ts"],"sourcesContent":["import { createContext, useContext } from 'react';\n\nimport type { ComboBoxOptionProps } from './ComboBox.types.js';\n\nexport interface ComboBoxContextValue {\n activeValue?: string | undefined;\n isSelected: (value: string) => boolean;\n multiple: boolean;\n optionId: (value: string) => string;\n select: (value: string) => void;\n}\n\nexport const ComboBoxContext = createContext<ComboBoxContextValue | null>(null);\n\nexport function useComboBoxContext(): ComboBoxContextValue {\n const context = useContext(ComboBoxContext);\n\n if (context === null) {\n throw new Error('ComboBox.Option must be rendered within ComboBox.');\n }\n\n return context;\n}\n\nexport function getOptionLabel(option: ComboBoxOptionProps['children']): string {\n if (typeof option === 'string' || typeof option === 'number') {\n return String(option);\n }\n\n return '';\n}\n"],"mappings":";;AAYA,IAAa,kBAAkB,cAA2C,IAAI;AAE9E,SAAgB,qBAA2C;CACzD,MAAM,UAAU,WAAW,eAAe;CAE1C,IAAI,YAAY,MACd,MAAM,IAAI,MAAM,mDAAmD;CAGrE,OAAO;AACT;AAEA,SAAgB,eAAe,QAAiD;CAC9E,IAAI,OAAO,WAAW,YAAY,OAAO,WAAW,UAClD,OAAO,OAAO,MAAM;CAGtB,OAAO;AACT"}
|