@lablup/ui-common 0.2.0-alpha.17 → 0.2.0-alpha.19
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 +3 -2
- package/astryx/components/Board.doc.mjs +122 -0
- package/astryx/components/Board.tsx +3 -0
- package/astryx/components/Form.doc.mjs +1 -1
- package/astryx/docs/ui-common.doc.mjs +1 -1
- package/dist/assets/components/Board/Board.css +2 -0
- package/dist/assets/components/Form/FormItemVisual.css +1 -1
- package/dist/astryx/DateInput/tokens.stylex.d.ts +1 -0
- package/dist/astryx/DateInput/tokens.stylex.js +1 -0
- package/dist/astryx/Icon/IconSize.stylex.d.ts +1 -0
- package/dist/astryx/Icon/IconSize.stylex.js +1 -0
- package/dist/astryx/Indicator/indicator.markers.stylex.d.ts +1 -0
- package/dist/astryx/Indicator/indicator.markers.stylex.js +1 -0
- package/dist/astryx/Layer/layerAnimations.stylex.d.ts +1 -0
- package/dist/astryx/Layer/layerAnimations.stylex.js +1 -0
- package/dist/astryx/Layer/layerTextReset.stylex.d.ts +1 -0
- package/dist/astryx/Layer/layerTextReset.stylex.js +1 -0
- package/dist/astryx/Layer/layerViewportInset.stylex.d.ts +1 -0
- package/dist/astryx/Layer/layerViewportInset.stylex.js +1 -0
- package/dist/astryx/Layout/container.stylex.d.ts +1 -0
- package/dist/astryx/Layout/container.stylex.js +1 -0
- package/dist/astryx/Layout/edgeCompensation.stylex.d.ts +1 -0
- package/dist/astryx/Layout/edgeCompensation.stylex.js +1 -0
- package/dist/astryx/Layout/padding.stylex.d.ts +1 -0
- package/dist/astryx/Layout/padding.stylex.js +1 -0
- package/dist/astryx/Markdown/plugin-renderer.d.ts +1 -0
- package/dist/astryx/Markdown/plugin-renderer.js +1 -0
- package/dist/astryx/NavItem/navItemStyles.stylex.d.ts +1 -0
- package/dist/astryx/NavItem/navItemStyles.stylex.js +1 -0
- package/dist/astryx/Selector/selectorPresentation.stylex.d.ts +1 -0
- package/dist/astryx/Selector/selectorPresentation.stylex.js +1 -0
- package/dist/astryx/Stack/stack.stylex.d.ts +1 -0
- package/dist/astryx/Stack/stack.stylex.js +1 -0
- package/dist/astryx/Stack/stackItem.stylex.d.ts +1 -0
- package/dist/astryx/Stack/stackItem.stylex.js +1 -0
- package/dist/astryx/Text/text.stylex.d.ts +1 -0
- package/dist/astryx/Text/text.stylex.js +1 -0
- package/dist/astryx/theme/dataTokens.stylex.d.ts +1 -0
- package/dist/astryx/theme/dataTokens.stylex.js +1 -0
- package/dist/astryx/theme/neutral/fonts.css +3 -0
- package/dist/astryx/utils/focusOutline.stylex.d.ts +1 -0
- package/dist/astryx/utils/focusOutline.stylex.js +1 -0
- package/dist/astryx/utils/interactionOverlay.stylex.d.ts +1 -0
- package/dist/astryx/utils/interactionOverlay.stylex.js +1 -0
- package/dist/components/Board/Board.d.ts +102 -0
- package/dist/components/Board/Board.messages.d.ts +62 -0
- package/dist/components/Board/Board.messages.js +68 -0
- package/dist/components/Board/Board.messages.js.map +1 -0
- package/dist/components/Board/Board2.js +607 -0
- package/dist/components/Board/Board2.js.map +1 -0
- package/dist/components/Board/engine.d.ts +60 -0
- package/dist/components/Board/engine.js +397 -0
- package/dist/components/Board/engine.js.map +1 -0
- package/dist/components/Board/index.d.ts +4 -0
- package/dist/components/Board/index.js +3 -0
- package/dist/components/Board/layout.d.ts +81 -0
- package/dist/components/Board/layout.js +67 -0
- package/dist/components/Board/layout.js.map +1 -0
- package/dist/components/Board/scroll.d.ts +38 -0
- package/dist/components/Board/scroll.js +42 -0
- package/dist/components/Board/scroll.js.map +1 -0
- package/dist/components/DataGrid/DataGrid2.js +128 -128
- package/dist/components/DataGrid/DataGrid2.js.map +1 -1
- package/dist/components/Form/FormItem.d.ts +6 -1
- package/dist/components/Form/FormItem.js +68 -67
- package/dist/components/Form/FormItem.js.map +1 -1
- package/dist/components/Form/FormItemVisual.d.ts +7 -0
- package/dist/components/Form/FormItemVisual2.js +60 -55
- package/dist/components/Form/FormItemVisual2.js.map +1 -1
- package/dist/components/NotificationStack/NotificationStack2.js +1 -0
- package/dist/components/NotificationStack/NotificationStack2.js.map +1 -1
- package/dist/components/PageHeader/PageHeader2.js +4 -4
- package/dist/components/PagedSelector/PagedSelector.d.ts +1 -1
- package/dist/components/PagedSelector/PagedSelector2.js +37 -37
- package/dist/components/PagedSelector/PagedSelector2.js.map +1 -1
- package/dist/components/SelectionLabel/SelectionLabel.js +4 -4
- package/dist/forks/ComplexSelector/ComplexSelector.d.ts +82 -6
- package/dist/forks/ComplexSelector/ComplexSelector.js +170 -114
- package/dist/forks/ComplexSelector/ComplexSelector.js.map +1 -1
- package/dist/forks/ComplexSelector/ComplexSelector.styles.d.ts +40 -0
- package/dist/forks/ComplexSelector/ComplexSelector.styles.js +46 -6
- package/dist/forks/ComplexSelector/ComplexSelector.styles.js.map +1 -1
- package/dist/forks/ComplexSelector/index.d.ts +1 -1
- package/dist/forks/Drawer/Drawer.d.ts +21 -3
- package/dist/forks/Drawer/Drawer.js +70 -65
- package/dist/forks/Drawer/Drawer.js.map +1 -1
- package/dist/forks/Drawer/Drawer.styles.d.ts +16 -0
- package/dist/forks/Drawer/Drawer.styles.js +16 -0
- package/dist/forks/Drawer/Drawer.styles.js.map +1 -1
- package/dist/forks/Drawer/useDrawerDialogPresence.js.map +1 -1
- package/dist/forks/Tour/Tour.js +38 -40
- package/dist/forks/Tour/Tour.js.map +1 -1
- package/dist/forks/Tour/TourContext.js.map +1 -1
- package/dist/forks/Tour/TourStep.js.map +1 -1
- package/dist/forks/Tour/TourStep.styles.js.map +1 -1
- package/dist/forks/Tour/useTour.js.map +1 -1
- package/dist/i18n/catalog.js +27 -25
- package/dist/i18n/catalog.js.map +1 -1
- package/dist/i18n/locales/de-DE.js +60 -0
- package/dist/i18n/locales/de-DE.js.map +1 -1
- package/dist/i18n/locales/el-GR.js +60 -0
- package/dist/i18n/locales/el-GR.js.map +1 -1
- package/dist/i18n/locales/es-ES.js +60 -0
- package/dist/i18n/locales/es-ES.js.map +1 -1
- package/dist/i18n/locales/fi-FI.js +60 -0
- package/dist/i18n/locales/fi-FI.js.map +1 -1
- package/dist/i18n/locales/fr-FR.js +60 -0
- package/dist/i18n/locales/fr-FR.js.map +1 -1
- package/dist/i18n/locales/id-ID.js +60 -0
- package/dist/i18n/locales/id-ID.js.map +1 -1
- package/dist/i18n/locales/it-IT.js +60 -0
- package/dist/i18n/locales/it-IT.js.map +1 -1
- package/dist/i18n/locales/ja-JP.js +60 -0
- package/dist/i18n/locales/ja-JP.js.map +1 -1
- package/dist/i18n/locales/ko-KR.js +60 -0
- package/dist/i18n/locales/ko-KR.js.map +1 -1
- package/dist/i18n/locales/mn-MN.js +60 -0
- package/dist/i18n/locales/mn-MN.js.map +1 -1
- package/dist/i18n/locales/ms-MY.js +60 -0
- package/dist/i18n/locales/ms-MY.js.map +1 -1
- package/dist/i18n/locales/pl-PL.js +60 -0
- package/dist/i18n/locales/pl-PL.js.map +1 -1
- package/dist/i18n/locales/pt-BR.js +60 -0
- package/dist/i18n/locales/pt-BR.js.map +1 -1
- package/dist/i18n/locales/pt-PT.js +60 -0
- package/dist/i18n/locales/pt-PT.js.map +1 -1
- package/dist/i18n/locales/ru-RU.js +60 -0
- package/dist/i18n/locales/ru-RU.js.map +1 -1
- package/dist/i18n/locales/th-TH.js +60 -0
- package/dist/i18n/locales/th-TH.js.map +1 -1
- package/dist/i18n/locales/tr-TR.js +60 -0
- package/dist/i18n/locales/tr-TR.js.map +1 -1
- package/dist/i18n/locales/vi-VN.js +60 -0
- package/dist/i18n/locales/vi-VN.js.map +1 -1
- package/dist/i18n/locales/zh-CN.js +60 -0
- package/dist/i18n/locales/zh-CN.js.map +1 -1
- package/dist/i18n/locales/zh-TW.js +60 -0
- package/dist/i18n/locales/zh-TW.js.map +1 -1
- package/dist/index.d.ts +7 -5
- package/dist/index.js +61 -59
- package/dist/instance.js +2 -2
- package/dist/locales/en.generated.js +34 -0
- package/dist/locales/en.json +136 -0
- package/dist/locales/pseudo.generated.js +34 -0
- package/dist/locales/pseudo.json +102 -0
- package/dist/theme/lablup/built/lablup.d.ts +3 -3
- package/dist/theme/lablup/built/lablup.js +24 -5
- package/dist/theme/lablup/built/lablup.variants.d.ts +2 -2
- package/dist/theme/lablup/built/theme.css +70 -63
- package/dist/theme/lablup/built/theme.css.d.ts +9 -0
- package/dist/ui-common-locales/de-DE.json +60 -0
- package/dist/ui-common-locales/el-GR.json +60 -0
- package/dist/ui-common-locales/en.json +60 -0
- package/dist/ui-common-locales/es-ES.json +60 -0
- package/dist/ui-common-locales/fi-FI.json +60 -0
- package/dist/ui-common-locales/fr-FR.json +60 -0
- package/dist/ui-common-locales/id-ID.json +60 -0
- package/dist/ui-common-locales/it-IT.json +60 -0
- package/dist/ui-common-locales/ja-JP.json +60 -0
- package/dist/ui-common-locales/ko-KR.json +60 -0
- package/dist/ui-common-locales/mn-MN.json +60 -0
- package/dist/ui-common-locales/ms-MY.json +60 -0
- package/dist/ui-common-locales/pl-PL.json +60 -0
- package/dist/ui-common-locales/pt-BR.json +60 -0
- package/dist/ui-common-locales/pt-PT.json +60 -0
- package/dist/ui-common-locales/ru-RU.json +60 -0
- package/dist/ui-common-locales/th-TH.json +60 -0
- package/dist/ui-common-locales/tr-TR.json +60 -0
- package/dist/ui-common-locales/vi-VN.json +60 -0
- package/dist/ui-common-locales/zh-CN.json +60 -0
- package/dist/ui-common-locales/zh-TW.json +60 -0
- package/exports.customs.json +1 -0
- package/package.json +86 -6
|
@@ -8,28 +8,28 @@ import { HStack as s } from "@astryxdesign/core/Stack";
|
|
|
8
8
|
import { Text as c } from "@astryxdesign/core/Text";
|
|
9
9
|
import { themeProps as te } from "@astryxdesign/core/utils";
|
|
10
10
|
import { Fragment as ne, jsx as l, jsxs as u } from "react/jsx-runtime";
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
11
|
+
import { VisuallyHidden as d } from "@astryxdesign/core/VisuallyHidden";
|
|
12
|
+
import { Token as f } from "@astryxdesign/core/Token";
|
|
13
|
+
import { Spinner as p } from "@astryxdesign/core/Spinner";
|
|
13
14
|
import { Divider as re } from "@astryxdesign/core/Divider";
|
|
14
|
-
import { useIndicator as
|
|
15
|
-
import { SelectorOption as
|
|
16
|
-
import { VisuallyHidden as m } from "@astryxdesign/core/VisuallyHidden";
|
|
15
|
+
import { useIndicator as m } from "@astryxdesign/core/Indicator";
|
|
16
|
+
import { SelectorOption as h } from "@astryxdesign/core/Selector";
|
|
17
17
|
//#region src/components/PagedSelector/PagedSelector.tsx
|
|
18
|
-
var
|
|
19
|
-
kmVPX3:
|
|
18
|
+
var g = { padding: "x1717udv" }, _ = t({ reset: {
|
|
19
|
+
kmVPX3: g.padding,
|
|
20
20
|
$$css: !0
|
|
21
21
|
} });
|
|
22
|
-
function
|
|
23
|
-
let { label:
|
|
24
|
-
for (let e of
|
|
25
|
-
let B = (e) =>
|
|
22
|
+
function v(t) {
|
|
23
|
+
let { label: g, options: v, labels: ie, placeholder: ae, hasSearch: y = !0, searchValue: oe, onSearchChange: se, searchPlaceholder: ce, searchLabel: b, clearSearchLabel: le, onEndReached: x, endReachedThreshold: ue = 30, onAtEndChange: S, isLoadingMore: C, totalCount: w, formatTotalCount: de, emptyText: T, header: fe, footer: E, listMaxHeight: pe = 260, triggerDisplay: me = "labels", maxTriggerItems: he = 3, selectionIndicator: D = "check", hasClear: ge, onClear: O, onOpenChange: k, isLoading: A, size: j, isMultiple: M, value: _e, onChange: ve, ...N } = t, P = e(), F = m("check"), I = m("checkbox"), L = i(), ye = i(), R = t.isMultiple ? [...t.value ?? []] : t.value == null ? [] : [t.value], z = a(/* @__PURE__ */ new Map());
|
|
24
|
+
for (let e of v) z.current.set(e.value, e.label);
|
|
25
|
+
let B = (e) => ie?.[e] ?? v.find((t) => t.value === e)?.label ?? z.current.get(e) ?? e, V = (e) => {
|
|
26
26
|
let n = e.map((e) => ({
|
|
27
27
|
value: e,
|
|
28
28
|
label: B(e)
|
|
29
29
|
}));
|
|
30
30
|
t.isMultiple ? t.onChange?.(e, n) : t.onChange?.(e[0] ?? null, n[0] ?? null);
|
|
31
|
-
}, [H, U] = o(-1), [be, xe] = o(""), Se =
|
|
32
|
-
for (let n = e; n >= 0 && n <
|
|
31
|
+
}, [H, U] = o(-1), [be, xe] = o(""), Se = oe ?? be, W = a(!1), G = (e) => `${ye}-option-${e}`, K = v.length === 0 || H < 0 ? -1 : Math.min(H, v.length - 1), q = K >= 0 && v[K]?.isDisabled ? -1 : K, J = (e, t) => {
|
|
32
|
+
for (let n = e; n >= 0 && n < v.length; n += t) if (!v[n]?.isDisabled) return n;
|
|
33
33
|
return -1;
|
|
34
34
|
};
|
|
35
35
|
ee(() => {
|
|
@@ -46,7 +46,7 @@ function _(t) {
|
|
|
46
46
|
}
|
|
47
47
|
V(Y(e.value) ? R.filter((t) => t !== e.value) : [...R, e.value]);
|
|
48
48
|
}, Z = (e, t, n) => {
|
|
49
|
-
if (
|
|
49
|
+
if (v.length === 0) return;
|
|
50
50
|
let r = (e) => {
|
|
51
51
|
e >= 0 && U(e);
|
|
52
52
|
};
|
|
@@ -61,12 +61,12 @@ function _(t) {
|
|
|
61
61
|
e.preventDefault(), r(J(0, 1));
|
|
62
62
|
break;
|
|
63
63
|
case "End":
|
|
64
|
-
e.preventDefault(), r(J(
|
|
64
|
+
e.preventDefault(), r(J(v.length - 1, -1));
|
|
65
65
|
break;
|
|
66
66
|
case " ":
|
|
67
67
|
case "Enter": {
|
|
68
68
|
if (e.key === " " && !n) break;
|
|
69
|
-
let r =
|
|
69
|
+
let r = v[q];
|
|
70
70
|
if (!r || r.isDisabled) break;
|
|
71
71
|
e.preventDefault(), X(r, t);
|
|
72
72
|
break;
|
|
@@ -80,7 +80,7 @@ function _(t) {
|
|
|
80
80
|
gap: .5,
|
|
81
81
|
vAlign: "center",
|
|
82
82
|
wrap: "wrap",
|
|
83
|
-
children: [e.map((e) => /* @__PURE__ */ l(
|
|
83
|
+
children: [e.map((e) => /* @__PURE__ */ l(f, {
|
|
84
84
|
label: B(e),
|
|
85
85
|
size: "sm"
|
|
86
86
|
}, e)), t > 0 ? /* @__PURE__ */ l(c, {
|
|
@@ -93,11 +93,11 @@ function _(t) {
|
|
|
93
93
|
})(), Q = (e) => de?.(e) ?? P("uic.PagedSelector.totalItems", { total: e }), $ = P("uic.PagedSelector.loading");
|
|
94
94
|
return /* @__PURE__ */ l(n, {
|
|
95
95
|
...N,
|
|
96
|
-
label:
|
|
96
|
+
label: g,
|
|
97
97
|
value: R,
|
|
98
98
|
onChange: V,
|
|
99
99
|
triggerLabel: we,
|
|
100
|
-
placeholder:
|
|
100
|
+
placeholder: ae ?? P("uic.PagedSelector.placeholder", { label: g }),
|
|
101
101
|
isLoading: A,
|
|
102
102
|
size: j,
|
|
103
103
|
hasClear: ge,
|
|
@@ -105,15 +105,15 @@ function _(t) {
|
|
|
105
105
|
onOpenChange: (e) => {
|
|
106
106
|
e || U(-1), k?.(e);
|
|
107
107
|
},
|
|
108
|
-
contentXstyle:
|
|
108
|
+
contentXstyle: _.reset,
|
|
109
109
|
children: (e, t, n) => /* @__PURE__ */ u("div", { children: [
|
|
110
|
-
|
|
111
|
-
label:
|
|
112
|
-
clearLabel:
|
|
113
|
-
placeholder:
|
|
110
|
+
y ? /* @__PURE__ */ u(ne, { children: [/* @__PURE__ */ l(r, {
|
|
111
|
+
label: b ?? P("uic.PagedSelector.searchOptions"),
|
|
112
|
+
clearLabel: le ?? P("uic.PagedSelector.clearSearch"),
|
|
113
|
+
placeholder: ce ?? P("uic.PagedSelector.searchPlaceholder"),
|
|
114
114
|
value: Se,
|
|
115
115
|
onValueChange: (e) => {
|
|
116
|
-
xe(e), U(J(0, 1)),
|
|
116
|
+
xe(e), U(J(0, 1)), se?.(e);
|
|
117
117
|
},
|
|
118
118
|
onKeyDown: (e) => Z(e, n, !1),
|
|
119
119
|
"aria-controls": L,
|
|
@@ -123,21 +123,21 @@ function _(t) {
|
|
|
123
123
|
/* @__PURE__ */ l("div", {
|
|
124
124
|
id: L,
|
|
125
125
|
role: "listbox",
|
|
126
|
-
"aria-label":
|
|
126
|
+
"aria-label": g,
|
|
127
127
|
"aria-multiselectable": M || void 0,
|
|
128
|
-
tabIndex:
|
|
129
|
-
"aria-activedescendant": !
|
|
130
|
-
onKeyDown:
|
|
128
|
+
tabIndex: y ? void 0 : 0,
|
|
129
|
+
"aria-activedescendant": !y && q >= 0 ? G(q) : void 0,
|
|
130
|
+
onKeyDown: y ? void 0 : (e) => Z(e, n, !0),
|
|
131
131
|
onScroll: Ce,
|
|
132
132
|
className: "uic-paged-selector__listbox",
|
|
133
133
|
style: { maxHeight: pe },
|
|
134
|
-
children:
|
|
134
|
+
children: v.length === 0 ? /* @__PURE__ */ l("div", {
|
|
135
135
|
className: "uic-paged-selector__empty",
|
|
136
136
|
children: T === void 0 ? A ? /* @__PURE__ */ u(s, {
|
|
137
137
|
gap: 1,
|
|
138
138
|
vAlign: "center",
|
|
139
139
|
hAlign: "center",
|
|
140
|
-
children: [/* @__PURE__ */ l(
|
|
140
|
+
children: [/* @__PURE__ */ l(p, { size: "sm" }), /* @__PURE__ */ l(c, {
|
|
141
141
|
color: "secondary",
|
|
142
142
|
children: $
|
|
143
143
|
})]
|
|
@@ -145,7 +145,7 @@ function _(t) {
|
|
|
145
145
|
color: "secondary",
|
|
146
146
|
children: P("uic.PagedSelector.noResults")
|
|
147
147
|
}) : T
|
|
148
|
-
}) :
|
|
148
|
+
}) : v.map((e, t) => {
|
|
149
149
|
let r = Y(e.value), i = e.isDisabled ?? !1;
|
|
150
150
|
return /* @__PURE__ */ u("div", {
|
|
151
151
|
id: G(t),
|
|
@@ -174,7 +174,7 @@ function _(t) {
|
|
|
174
174
|
}),
|
|
175
175
|
/* @__PURE__ */ l("span", {
|
|
176
176
|
className: "uic-paged-selector__option-content",
|
|
177
|
-
children: /* @__PURE__ */ l(
|
|
177
|
+
children: /* @__PURE__ */ l(h, {
|
|
178
178
|
icon: e.icon,
|
|
179
179
|
label: e.labelContent ?? e.label,
|
|
180
180
|
description: e.description,
|
|
@@ -199,22 +199,22 @@ function _(t) {
|
|
|
199
199
|
vAlign: "center",
|
|
200
200
|
hAlign: "end",
|
|
201
201
|
className: "uic-paged-selector__foot",
|
|
202
|
-
children: [C ? /* @__PURE__ */ l(
|
|
202
|
+
children: [C ? /* @__PURE__ */ l(p, { size: "sm" }) : null, /* @__PURE__ */ l(c, {
|
|
203
203
|
color: "secondary",
|
|
204
204
|
size: "sm",
|
|
205
205
|
children: Q(w)
|
|
206
206
|
})]
|
|
207
207
|
}) : null),
|
|
208
|
-
/* @__PURE__ */ l(
|
|
208
|
+
/* @__PURE__ */ l(d, {
|
|
209
209
|
as: "div",
|
|
210
210
|
"aria-live": "polite",
|
|
211
|
-
children:
|
|
211
|
+
children: v.length === 0 && A ? $ : Q(v.length)
|
|
212
212
|
})
|
|
213
213
|
] })
|
|
214
214
|
});
|
|
215
215
|
}
|
|
216
216
|
//#endregion
|
|
217
|
-
export {
|
|
217
|
+
export { g as CONTENT_RESET, v as PagedSelector };
|
|
218
218
|
|
|
219
219
|
//# sourceMappingURL=PagedSelector2.js.map
|
|
220
220
|
import "../../assets/components/PagedSelector/PagedSelector.css";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PagedSelector2.js","names":[],"sources":["../../../src/components/PagedSelector/PagedSelector.tsx"],"sourcesContent":["/**\n * PagedSelector\n *\n * A searchable selector, single or multiple, over a list that loads a page at\n * a time: scrolling the panel near its end calls `onEndReached`, which is\n * where a consumer loads the next page (a Relay `loadNext`, a cursor fetch).\n * Search is the consumer's too: `onSearchChange` reports every keystroke.\n *\n * Astryx's `Selector` / `MultiSelector` mount every option and have no\n * scroll callback; `Typeahead` replaces its list on each query. This is\n * built on `ComplexSelector` (ui-common's copy), which owns the field,\n * trigger, popover and focus return and hands the panel body back, so the\n * body can own the scroll container. The body is drawn the way `Selector`'s\n * is: Astryx's panel search row, then option rows with the theme's check.\n *\n * - `value` holds option values: a string (or null) alone, a string array\n * with `isMultiple`. `onChange` also gets each chosen value's label, so a\n * consumer can keep labels for values that are not on the loaded page.\n * - A selected value missing from `options` (on another page, or filtered out\n * by the query) is named from `labels`, then from any page it was seen on,\n * then shown as the value itself.\n * - Keyboard: ArrowUp/Down, Home/End move a highlight that skips disabled\n * rows; Enter commits it, and Space too when there is no search box. The\n * panel is `ComplexSelector`'s dialog holding a listbox, not an ARIA\n * combobox.\n * - Options render one DOM row each; paging is what keeps that bounded.\n *\n * @example\n * <PagedSelector\n * label=\"Owner\"\n * value={ownerId}\n * onChange={(id) => setOwnerId(id)}\n * options={users.map((u) => ({ value: u.id, label: u.email }))}\n * onSearchChange={setQuery}\n * onEndReached={() => hasNext && loadNext(10)}\n * isLoadingMore={isLoadingNext}\n * totalCount={count}\n * />\n */\nimport { useId, useLayoutEffect, useRef, useState, type ReactNode } from \"react\";\nimport {\n ComplexSelector,\n type ComplexSelectorProps,\n} from \"../../forks/ComplexSelector\";\nimport { compiledStyles } from \"../../forks/compiled\";\nimport { Divider } from \"@astryxdesign/core/Divider\";\nimport { useIndicator } from \"@astryxdesign/core/Indicator\";\nimport { SelectorOption } from \"@astryxdesign/core/Selector\";\nimport { Spinner } from \"@astryxdesign/core/Spinner\";\nimport { HStack } from \"@astryxdesign/core/Stack\";\nimport { Text } from \"@astryxdesign/core/Text\";\nimport { Token } from \"@astryxdesign/core/Token\";\nimport { VisuallyHidden } from \"@astryxdesign/core/VisuallyHidden\";\nimport { themeProps } from \"@astryxdesign/core/utils\";\n\nimport { useUicTranslator } from \"../../i18n/useUicTranslator\";\nimport { PanelSearchInput } from \"./PanelSearchInput\";\nimport \"./PagedSelector.css\";\n\nexport interface PagedSelectorOption {\n value: string;\n /** The option's name: the trigger text and the row's accessible name. */\n label: string;\n /** Drawn in the row in place of `label`, for a row richer than a string. */\n labelContent?: ReactNode;\n /** Leading visual (avatar, glyph). */\n icon?: ReactNode;\n /** Secondary line under the label. */\n description?: ReactNode;\n /** Trailing content (tokens, meta). */\n endContent?: ReactNode;\n isDisabled?: boolean;\n}\n\n/** A chosen value with its label, as `onChange` reports it. */\nexport interface PagedSelectorSelectedItem {\n value: string;\n label: string;\n}\n\ntype Passthrough = Pick<\n ComplexSelectorProps<string[]>,\n | \"label\"\n | \"isLabelHidden\"\n | \"description\"\n | \"isOptional\"\n | \"isRequired\"\n | \"isDisabled\"\n | \"isLoading\"\n | \"status\"\n | \"statusVariant\"\n | \"labelTooltip\"\n | \"size\"\n | \"variant\"\n | \"startIcon\"\n | \"width\"\n | \"placement\"\n | \"alignment\"\n | \"onOpenChange\"\n | \"xstyle\"\n | \"className\"\n | \"style\"\n | \"data-testid\"\n>;\n\ninterface PagedSelectorBaseProps extends Passthrough {\n /** The loaded options, every page so far, in order. */\n options: readonly PagedSelectorOption[];\n /** Labels for selected values that may not be among `options`. */\n labels?: Readonly<Record<string, string>>;\n /**\n * Placeholder while nothing is selected.\n * @default the catalog's uic.PagedSelector.placeholder (\"Select {label}\")\n */\n placeholder?: string;\n /** Show the search box at the top of the panel. @default true */\n hasSearch?: boolean;\n /** Controlled search text; uncontrolled without it. */\n searchValue?: string;\n /** Called on every keystroke in the search box; debounce it yourself. */\n onSearchChange?: (searchValue: string) => void;\n /** @default the catalog's uic.PagedSelector.searchPlaceholder (\"Search\") */\n searchPlaceholder?: string;\n /** @default the catalog's uic.PagedSelector.searchOptions (\"Search options\") */\n searchLabel?: string;\n /** @default the catalog's uic.PagedSelector.clearSearch (\"Clear search options\") */\n clearSearchLabel?: string;\n /**\n * Called once each time the list is scrolled to within\n * `endReachedThreshold` px of its end. Load the next page here.\n */\n onEndReached?: () => void;\n /** Distance from the end, in px, that counts as reaching it. @default 30 */\n endReachedThreshold?: number;\n /** Called when the list arrives at, or leaves, its end. */\n onAtEndChange?: (isAtEnd: boolean) => void;\n /** A spinner in the foot while the next page loads. */\n isLoadingMore?: boolean;\n /** Options in all pages; shows the \"Total N items\" foot when positive. */\n totalCount?: number;\n /** @default the catalog's uic.PagedSelector.totalItems (\"Total {total} items\") */\n formatTotalCount?: (total: number) => string;\n /**\n * Replaces the empty list's content, the loading row included.\n * @default the catalog's uic.PagedSelector.loading while `isLoading`, else\n * uic.PagedSelector.noResults (\"No results\")\n */\n emptyText?: ReactNode;\n /** Above the option list, under the search box. */\n header?: ReactNode;\n /** Below the option list, in place of the total count. A function gets `close`. */\n footer?: ReactNode | ((close: () => void) => ReactNode);\n /** Height the option list scrolls within, in px. @default 260 */\n listMaxHeight?: number;\n /**\n * How a multiple selection shows in the trigger: `labels` (\"A, B, C, +2\",\n * as `MultiSelector`) or `badges` (tokens). @default \"labels\"\n */\n triggerDisplay?: \"labels\" | \"badges\";\n /** Selected items the trigger names before \"+N\". @default 3 */\n maxTriggerItems?: number;\n /** How a selected row is marked: the theme's check at its end, or a checkbox at its start. @default \"check\" */\n selectionIndicator?: \"check\" | \"checkbox\";\n /** A clear button in the trigger while something is selected. */\n hasClear?: boolean;\n /** Runs on clear. @default onChange(null) alone, onChange([]) with isMultiple */\n onClear?: () => void;\n}\n\nexport interface PagedSelectorSingleProps extends PagedSelectorBaseProps {\n isMultiple?: false;\n value?: string | null;\n onChange?: (value: string | null, item: PagedSelectorSelectedItem | null) => void;\n}\n\nexport interface PagedSelectorMultipleProps extends PagedSelectorBaseProps {\n isMultiple: true;\n value?: readonly string[];\n onChange?: (value: string[], items: PagedSelectorSelectedItem[]) => void;\n}\n\nexport type PagedSelectorProps = PagedSelectorSingleProps | PagedSelectorMultipleProps;\n\n/**\n * `ComplexSelector` insets its panel body by `--spacing-3`; this body brings\n * Selector's own gutters, so the inset goes. Astryx's compiled `padding: 0`\n * class (kmVPX3 is StyleX's key for `padding`), so no StyleX compiler is\n * needed; the test suite fails if astryx.css stops defining it.\n */\nexport const CONTENT_RESET = { padding: \"x1717udv\" } as const;\nconst contentStyles = compiledStyles({\n reset: { kmVPX3: CONTENT_RESET.padding, $$css: true },\n});\n\nexport function PagedSelector(props: PagedSelectorProps) {\n const {\n label,\n options,\n labels,\n placeholder,\n hasSearch = true,\n searchValue,\n onSearchChange,\n searchPlaceholder,\n searchLabel,\n clearSearchLabel,\n onEndReached,\n endReachedThreshold = 30,\n onAtEndChange,\n isLoadingMore,\n totalCount,\n formatTotalCount,\n emptyText,\n header,\n footer,\n listMaxHeight = 260,\n triggerDisplay = \"labels\",\n maxTriggerItems = 3,\n selectionIndicator = \"check\",\n hasClear,\n onClear,\n onOpenChange,\n isLoading,\n size,\n isMultiple,\n value: _value,\n onChange: _onChange,\n ...rest\n } = props;\n const t = useUicTranslator();\n const CheckMark = useIndicator(\"check\");\n const CheckboxMark = useIndicator(\"checkbox\");\n const listboxId = useId();\n const optionIdPrefix = useId();\n\n const selectedValues: string[] = props.isMultiple\n ? [...(props.value ?? [])]\n : props.value != null\n ? [props.value]\n : [];\n\n // Labels of every option seen, so a value picked from a page the list has\n // since dropped (a new query) keeps its name.\n const seenLabels = useRef(new Map<string, string>());\n for (const option of options) seenLabels.current.set(option.value, option.label);\n const labelOf = (value: string) =>\n labels?.[value] ??\n options.find((option) => option.value === value)?.label ??\n seenLabels.current.get(value) ??\n value;\n\n const emit = (values: string[]) => {\n const items = values.map((value) => ({ value, label: labelOf(value) }));\n if (props.isMultiple) props.onChange?.(values, items);\n else props.onChange?.(values[0] ?? null, items[0] ?? null);\n };\n\n // -1: an opened Astryx Selector highlights nothing until the pointer or an\n // arrow key picks a row.\n const [highlightedIndex, setHighlightedIndex] = useState(-1);\n const [internalSearch, setInternalSearch] = useState(\"\");\n const search = searchValue ?? internalSearch;\n const isAtEnd = useRef(false);\n\n const optionIdOf = (index: number) => `${optionIdPrefix}-option-${index}`;\n const clampedIndex =\n options.length === 0 || highlightedIndex < 0\n ? -1\n : Math.min(highlightedIndex, options.length - 1);\n // A row that went disabled under a page append does not keep the highlight.\n const activeIndex =\n clampedIndex >= 0 && options[clampedIndex]?.isDisabled ? -1 : clampedIndex;\n\n const nextEnabledIndex = (from: number, step: 1 | -1) => {\n for (let i = from; i >= 0 && i < options.length; i += step) {\n if (!options[i]?.isDisabled) return i;\n }\n return -1;\n };\n\n useLayoutEffect(() => {\n if (activeIndex < 0) return;\n document\n .getElementById(optionIdOf(activeIndex))\n ?.scrollIntoView({ block: \"nearest\" });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [activeIndex]);\n\n const handleScroll = (event: React.UIEvent<HTMLDivElement>) => {\n if (!onEndReached && !onAtEndChange) return;\n const el = event.currentTarget;\n const isAtEndNow =\n el.scrollHeight - el.scrollTop - el.clientHeight <= endReachedThreshold;\n if (isAtEndNow === isAtEnd.current) return;\n isAtEnd.current = isAtEndNow;\n onAtEndChange?.(isAtEndNow);\n if (isAtEndNow) onEndReached?.();\n };\n\n const isSelected = (value: string) => selectedValues.includes(value);\n\n const commit = (option: PagedSelectorOption, close: () => void) => {\n if (!isMultiple) {\n emit([option.value]);\n close();\n return;\n }\n emit(\n isSelected(option.value)\n ? selectedValues.filter((value) => value !== option.value)\n : [...selectedValues, option.value],\n );\n };\n\n // Shared by the search box and, with no search box, the listbox itself.\n // Space commits only where it is not typing into the search box.\n const handleNavKeyDown = (\n event: React.KeyboardEvent<HTMLElement>,\n close: () => void,\n hasSpaceCommit: boolean,\n ) => {\n if (options.length === 0) return;\n const moveTo = (index: number) => {\n if (index >= 0) setHighlightedIndex(index);\n };\n switch (event.key) {\n case \"ArrowDown\":\n event.preventDefault();\n moveTo(nextEnabledIndex(activeIndex < 0 ? 0 : activeIndex + 1, 1));\n break;\n case \"ArrowUp\":\n event.preventDefault();\n moveTo(\n activeIndex < 0\n ? nextEnabledIndex(0, 1)\n : nextEnabledIndex(activeIndex - 1, -1),\n );\n break;\n case \"Home\":\n event.preventDefault();\n moveTo(nextEnabledIndex(0, 1));\n break;\n case \"End\":\n event.preventDefault();\n moveTo(nextEnabledIndex(options.length - 1, -1));\n break;\n case \" \":\n case \"Enter\": {\n if (event.key === \" \" && !hasSpaceCommit) break;\n const option = options[activeIndex];\n if (!option || option.isDisabled) break;\n event.preventDefault();\n commit(option, close);\n break;\n }\n default:\n break;\n }\n };\n\n const triggerLabel = (() => {\n if (selectedValues.length === 0) return undefined;\n if (!isMultiple) return labelOf(selectedValues[0]!);\n const shown = selectedValues.slice(0, maxTriggerItems);\n const remaining = selectedValues.length - shown.length;\n if (triggerDisplay === \"badges\") {\n // Display-only: the trigger is ComplexSelector's own <button>, so a\n // removable token would nest a button in a button.\n return (\n <HStack gap={0.5} vAlign=\"center\" wrap=\"wrap\">\n {shown.map((value) => (\n <Token key={value} label={labelOf(value)} size=\"sm\" />\n ))}\n {remaining > 0 ? <Text color=\"secondary\">{`+${remaining}`}</Text> : null}\n </HStack>\n );\n }\n const joined = shown.map(labelOf).join(\", \");\n return remaining > 0 ? `${joined}, +${remaining}` : joined;\n })();\n\n const totalText = (total: number) =>\n formatTotalCount?.(total) ?? t(\"uic.PagedSelector.totalItems\", { total });\n const loadingText = t(\"uic.PagedSelector.loading\");\n\n return (\n <ComplexSelector<string[]>\n {...rest}\n label={label}\n value={selectedValues}\n onChange={emit}\n triggerLabel={triggerLabel}\n placeholder={placeholder ?? t(\"uic.PagedSelector.placeholder\", { label })}\n isLoading={isLoading}\n size={size}\n hasClear={hasClear}\n onClear={onClear ?? (() => emit([]))}\n onOpenChange={(isOpen) => {\n // Astryx drops its highlight when the panel closes, so the next open\n // starts clean.\n if (!isOpen) setHighlightedIndex(-1);\n onOpenChange?.(isOpen);\n }}\n contentXstyle={contentStyles.reset}\n >\n {(_current, _commit, close) => (\n <div>\n {hasSearch ? (\n <>\n <PanelSearchInput\n label={searchLabel ?? t(\"uic.PagedSelector.searchOptions\")}\n clearLabel={clearSearchLabel ?? t(\"uic.PagedSelector.clearSearch\")}\n placeholder={\n searchPlaceholder ?? t(\"uic.PagedSelector.searchPlaceholder\")\n }\n value={search}\n onValueChange={(next) => {\n setInternalSearch(next);\n // Seeds the first selectable row, so type-then-Enter works.\n setHighlightedIndex(nextEnabledIndex(0, 1));\n onSearchChange?.(next);\n }}\n onKeyDown={(event) => handleNavKeyDown(event, close, false)}\n aria-controls={listboxId}\n aria-activedescendant={\n activeIndex >= 0 ? optionIdOf(activeIndex) : undefined\n }\n />\n <Divider />\n </>\n ) : null}\n {header}\n <div\n id={listboxId}\n role=\"listbox\"\n aria-label={label}\n aria-multiselectable={isMultiple || undefined}\n // With no search box nothing else in the panel takes focus, so the\n // popover's autofocus lands here and the arrows work.\n tabIndex={hasSearch ? undefined : 0}\n aria-activedescendant={\n !hasSearch && activeIndex >= 0 ? optionIdOf(activeIndex) : undefined\n }\n onKeyDown={\n hasSearch ? undefined : (event) => handleNavKeyDown(event, close, true)\n }\n onScroll={handleScroll}\n className=\"uic-paged-selector__listbox\"\n style={{ maxHeight: listMaxHeight }}\n >\n {options.length === 0 ? (\n <div className=\"uic-paged-selector__empty\">\n {emptyText !== undefined ? (\n emptyText\n ) : isLoading ? (\n <HStack gap={1} vAlign=\"center\" hAlign=\"center\">\n <Spinner size=\"sm\" />\n <Text color=\"secondary\">{loadingText}</Text>\n </HStack>\n ) : (\n <Text color=\"secondary\">{t(\"uic.PagedSelector.noResults\")}</Text>\n )}\n </div>\n ) : (\n options.map((option, index) => {\n const selected = isSelected(option.value);\n const isDisabled = option.isDisabled ?? false;\n return (\n <div\n key={option.value}\n id={optionIdOf(index)}\n role=\"option\"\n aria-selected={selected}\n aria-disabled={isDisabled || undefined}\n className=\"uic-paged-selector__option\"\n data-size={size === \"sm\" ? \"sm\" : undefined}\n data-selected={selected ? \"true\" : undefined}\n data-highlighted={index === activeIndex ? \"true\" : undefined}\n data-disabled={isDisabled ? \"true\" : undefined}\n onClick={() => {\n if (isDisabled) return;\n setHighlightedIndex(index);\n commit(option, close);\n }}\n onMouseEnter={() => {\n if (!isDisabled) setHighlightedIndex(index);\n }}\n >\n {selectionIndicator === \"checkbox\" && (\n <span className=\"uic-paged-selector__option-mark\">\n <CheckboxMark\n state={selected ? \"checked\" : \"unchecked\"}\n size=\"sm\"\n isDisabled={isDisabled}\n />\n </span>\n )}\n <span className=\"uic-paged-selector__option-content\">\n <SelectorOption\n icon={option.icon}\n label={option.labelContent ?? option.label}\n description={option.description}\n endContent={option.endContent}\n />\n </span>\n {/* Rendered with the state passed down: a theme that swaps\n the check for a radio draws its empty circle too. */}\n {selectionIndicator === \"check\" && (\n <span className=\"uic-paged-selector__option-mark\">\n <CheckMark\n state={selected ? \"checked\" : \"unchecked\"}\n size=\"sm\"\n isDisabled={isDisabled}\n {...themeProps(\"selector-check\")}\n />\n </span>\n )}\n </div>\n );\n })\n )}\n </div>\n {(typeof footer === \"function\" ? footer(close) : footer) ??\n (typeof totalCount === \"number\" && totalCount > 0 ? (\n <HStack\n gap={1}\n vAlign=\"center\"\n hAlign=\"end\"\n className=\"uic-paged-selector__foot\"\n >\n {isLoadingMore ? <Spinner size=\"sm\" /> : null}\n <Text color=\"secondary\" size=\"sm\">\n {totalText(totalCount)}\n </Text>\n </HStack>\n ) : null)}\n <VisuallyHidden as=\"div\" aria-live=\"polite\">\n {options.length === 0 && isLoading\n ? loadingText\n : totalText(options.length)}\n </VisuallyHidden>\n </div>\n )}\n </ComplexSelector>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PagedSelector2.js","names":[],"sources":["../../../src/components/PagedSelector/PagedSelector.tsx"],"sourcesContent":["/**\n * PagedSelector\n *\n * A searchable selector, single or multiple, over a list that loads a page at\n * a time: scrolling the panel near its end calls `onEndReached`, which is\n * where a consumer loads the next page (a Relay `loadNext`, a cursor fetch).\n * Search is the consumer's too: `onSearchChange` reports every keystroke.\n *\n * Astryx's `Selector` / `MultiSelector` mount every option and have no\n * scroll callback; `Typeahead` replaces its list on each query. This is\n * built on `ComplexSelector` (ui-common's copy), which owns the field,\n * trigger, popover and focus return and hands the panel body back, so the\n * body can own the scroll container. The body is drawn the way `Selector`'s\n * is: Astryx's panel search row, then option rows with the theme's check.\n *\n * - `value` holds option values: a string (or null) alone, a string array\n * with `isMultiple`. `onChange` also gets each chosen value's label, so a\n * consumer can keep labels for values that are not on the loaded page.\n * - A selected value missing from `options` (on another page, or filtered out\n * by the query) is named from `labels`, then from any page it was seen on,\n * then shown as the value itself.\n * - Keyboard: ArrowUp/Down, Home/End move a highlight that skips disabled\n * rows; Enter commits it, and Space too when there is no search box. The\n * panel is `ComplexSelector`'s dialog holding a listbox, not an ARIA\n * combobox.\n * - Options render one DOM row each; paging is what keeps that bounded.\n *\n * @example\n * <PagedSelector\n * label=\"Owner\"\n * value={ownerId}\n * onChange={(id) => setOwnerId(id)}\n * options={users.map((u) => ({ value: u.id, label: u.email }))}\n * onSearchChange={setQuery}\n * onEndReached={() => hasNext && loadNext(10)}\n * isLoadingMore={isLoadingNext}\n * totalCount={count}\n * />\n */\nimport { useId, useLayoutEffect, useRef, useState, type ReactNode } from \"react\";\nimport {\n ComplexSelector,\n type ComplexSelectorProps,\n} from \"../../forks/ComplexSelector\";\nimport { compiledStyles } from \"../../forks/compiled\";\nimport { Divider } from \"@astryxdesign/core/Divider\";\nimport { useIndicator } from \"@astryxdesign/core/Indicator\";\nimport { SelectorOption } from \"@astryxdesign/core/Selector\";\nimport { Spinner } from \"@astryxdesign/core/Spinner\";\nimport { HStack } from \"@astryxdesign/core/Stack\";\nimport { Text } from \"@astryxdesign/core/Text\";\nimport { Token } from \"@astryxdesign/core/Token\";\nimport { VisuallyHidden } from \"@astryxdesign/core/VisuallyHidden\";\nimport { themeProps } from \"@astryxdesign/core/utils\";\n\nimport { useUicTranslator } from \"../../i18n/useUicTranslator\";\nimport { PanelSearchInput } from \"./PanelSearchInput\";\nimport \"./PagedSelector.css\";\n\nexport interface PagedSelectorOption {\n value: string;\n /** The option's name: the trigger text and the row's accessible name. */\n label: string;\n /** Drawn in the row in place of `label`, for a row richer than a string. */\n labelContent?: ReactNode;\n /** Leading visual (avatar, glyph). */\n icon?: ReactNode;\n /** Secondary line under the label. */\n description?: ReactNode;\n /** Trailing content (tokens, meta). */\n endContent?: ReactNode;\n isDisabled?: boolean;\n}\n\n/** A chosen value with its label, as `onChange` reports it. */\nexport interface PagedSelectorSelectedItem {\n value: string;\n label: string;\n}\n\ntype Passthrough = Pick<\n ComplexSelectorProps<string[]>,\n | \"label\"\n | \"isLabelHidden\"\n | \"description\"\n | \"isOptional\"\n | \"isRequired\"\n | \"isDisabled\"\n | \"isLoading\"\n | \"status\"\n | \"statusVariant\"\n | \"labelTooltip\"\n | \"size\"\n | \"variant\"\n | \"startIcon\"\n | \"width\"\n | \"placement\"\n | \"alignment\"\n | \"onOpenChange\"\n | \"containerXstyle\"\n | \"containerClassName\"\n | \"containerStyle\"\n | \"controlXstyle\"\n | \"controlClassName\"\n | \"controlStyle\"\n | \"data-testid\"\n>;\n\ninterface PagedSelectorBaseProps extends Passthrough {\n /** The loaded options, every page so far, in order. */\n options: readonly PagedSelectorOption[];\n /** Labels for selected values that may not be among `options`. */\n labels?: Readonly<Record<string, string>>;\n /**\n * Placeholder while nothing is selected.\n * @default the catalog's uic.PagedSelector.placeholder (\"Select {label}\")\n */\n placeholder?: string;\n /** Show the search box at the top of the panel. @default true */\n hasSearch?: boolean;\n /** Controlled search text; uncontrolled without it. */\n searchValue?: string;\n /** Called on every keystroke in the search box; debounce it yourself. */\n onSearchChange?: (searchValue: string) => void;\n /** @default the catalog's uic.PagedSelector.searchPlaceholder (\"Search\") */\n searchPlaceholder?: string;\n /** @default the catalog's uic.PagedSelector.searchOptions (\"Search options\") */\n searchLabel?: string;\n /** @default the catalog's uic.PagedSelector.clearSearch (\"Clear search options\") */\n clearSearchLabel?: string;\n /**\n * Called once each time the list is scrolled to within\n * `endReachedThreshold` px of its end. Load the next page here.\n */\n onEndReached?: () => void;\n /** Distance from the end, in px, that counts as reaching it. @default 30 */\n endReachedThreshold?: number;\n /** Called when the list arrives at, or leaves, its end. */\n onAtEndChange?: (isAtEnd: boolean) => void;\n /** A spinner in the foot while the next page loads. */\n isLoadingMore?: boolean;\n /** Options in all pages; shows the \"Total N items\" foot when positive. */\n totalCount?: number;\n /** @default the catalog's uic.PagedSelector.totalItems (\"Total {total} items\") */\n formatTotalCount?: (total: number) => string;\n /**\n * Replaces the empty list's content, the loading row included.\n * @default the catalog's uic.PagedSelector.loading while `isLoading`, else\n * uic.PagedSelector.noResults (\"No results\")\n */\n emptyText?: ReactNode;\n /** Above the option list, under the search box. */\n header?: ReactNode;\n /** Below the option list, in place of the total count. A function gets `close`. */\n footer?: ReactNode | ((close: () => void) => ReactNode);\n /** Height the option list scrolls within, in px. @default 260 */\n listMaxHeight?: number;\n /**\n * How a multiple selection shows in the trigger: `labels` (\"A, B, C, +2\",\n * as `MultiSelector`) or `badges` (tokens). @default \"labels\"\n */\n triggerDisplay?: \"labels\" | \"badges\";\n /** Selected items the trigger names before \"+N\". @default 3 */\n maxTriggerItems?: number;\n /** How a selected row is marked: the theme's check at its end, or a checkbox at its start. @default \"check\" */\n selectionIndicator?: \"check\" | \"checkbox\";\n /** A clear button in the trigger while something is selected. */\n hasClear?: boolean;\n /** Runs on clear. @default onChange(null) alone, onChange([]) with isMultiple */\n onClear?: () => void;\n}\n\nexport interface PagedSelectorSingleProps extends PagedSelectorBaseProps {\n isMultiple?: false;\n value?: string | null;\n onChange?: (value: string | null, item: PagedSelectorSelectedItem | null) => void;\n}\n\nexport interface PagedSelectorMultipleProps extends PagedSelectorBaseProps {\n isMultiple: true;\n value?: readonly string[];\n onChange?: (value: string[], items: PagedSelectorSelectedItem[]) => void;\n}\n\nexport type PagedSelectorProps = PagedSelectorSingleProps | PagedSelectorMultipleProps;\n\n/**\n * `ComplexSelector` insets its panel body by `--spacing-3`; this body brings\n * Selector's own gutters, so the inset goes. Astryx's compiled `padding: 0`\n * class (kmVPX3 is StyleX's key for `padding`), so no StyleX compiler is\n * needed; the test suite fails if astryx.css stops defining it.\n */\nexport const CONTENT_RESET = { padding: \"x1717udv\" } as const;\nconst contentStyles = compiledStyles({\n reset: { kmVPX3: CONTENT_RESET.padding, $$css: true },\n});\n\nexport function PagedSelector(props: PagedSelectorProps) {\n const {\n label,\n options,\n labels,\n placeholder,\n hasSearch = true,\n searchValue,\n onSearchChange,\n searchPlaceholder,\n searchLabel,\n clearSearchLabel,\n onEndReached,\n endReachedThreshold = 30,\n onAtEndChange,\n isLoadingMore,\n totalCount,\n formatTotalCount,\n emptyText,\n header,\n footer,\n listMaxHeight = 260,\n triggerDisplay = \"labels\",\n maxTriggerItems = 3,\n selectionIndicator = \"check\",\n hasClear,\n onClear,\n onOpenChange,\n isLoading,\n size,\n isMultiple,\n value: _value,\n onChange: _onChange,\n ...rest\n } = props;\n const t = useUicTranslator();\n const CheckMark = useIndicator(\"check\");\n const CheckboxMark = useIndicator(\"checkbox\");\n const listboxId = useId();\n const optionIdPrefix = useId();\n\n const selectedValues: string[] = props.isMultiple\n ? [...(props.value ?? [])]\n : props.value != null\n ? [props.value]\n : [];\n\n // Labels of every option seen, so a value picked from a page the list has\n // since dropped (a new query) keeps its name.\n const seenLabels = useRef(new Map<string, string>());\n for (const option of options) seenLabels.current.set(option.value, option.label);\n const labelOf = (value: string) =>\n labels?.[value] ??\n options.find((option) => option.value === value)?.label ??\n seenLabels.current.get(value) ??\n value;\n\n const emit = (values: string[]) => {\n const items = values.map((value) => ({ value, label: labelOf(value) }));\n if (props.isMultiple) props.onChange?.(values, items);\n else props.onChange?.(values[0] ?? null, items[0] ?? null);\n };\n\n // -1: an opened Astryx Selector highlights nothing until the pointer or an\n // arrow key picks a row.\n const [highlightedIndex, setHighlightedIndex] = useState(-1);\n const [internalSearch, setInternalSearch] = useState(\"\");\n const search = searchValue ?? internalSearch;\n const isAtEnd = useRef(false);\n\n const optionIdOf = (index: number) => `${optionIdPrefix}-option-${index}`;\n const clampedIndex =\n options.length === 0 || highlightedIndex < 0\n ? -1\n : Math.min(highlightedIndex, options.length - 1);\n // A row that went disabled under a page append does not keep the highlight.\n const activeIndex =\n clampedIndex >= 0 && options[clampedIndex]?.isDisabled ? -1 : clampedIndex;\n\n const nextEnabledIndex = (from: number, step: 1 | -1) => {\n for (let i = from; i >= 0 && i < options.length; i += step) {\n if (!options[i]?.isDisabled) return i;\n }\n return -1;\n };\n\n useLayoutEffect(() => {\n if (activeIndex < 0) return;\n document\n .getElementById(optionIdOf(activeIndex))\n ?.scrollIntoView({ block: \"nearest\" });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [activeIndex]);\n\n const handleScroll = (event: React.UIEvent<HTMLDivElement>) => {\n if (!onEndReached && !onAtEndChange) return;\n const el = event.currentTarget;\n const isAtEndNow =\n el.scrollHeight - el.scrollTop - el.clientHeight <= endReachedThreshold;\n if (isAtEndNow === isAtEnd.current) return;\n isAtEnd.current = isAtEndNow;\n onAtEndChange?.(isAtEndNow);\n if (isAtEndNow) onEndReached?.();\n };\n\n const isSelected = (value: string) => selectedValues.includes(value);\n\n const commit = (option: PagedSelectorOption, close: () => void) => {\n if (!isMultiple) {\n emit([option.value]);\n close();\n return;\n }\n emit(\n isSelected(option.value)\n ? selectedValues.filter((value) => value !== option.value)\n : [...selectedValues, option.value],\n );\n };\n\n // Shared by the search box and, with no search box, the listbox itself.\n // Space commits only where it is not typing into the search box.\n const handleNavKeyDown = (\n event: React.KeyboardEvent<HTMLElement>,\n close: () => void,\n hasSpaceCommit: boolean,\n ) => {\n if (options.length === 0) return;\n const moveTo = (index: number) => {\n if (index >= 0) setHighlightedIndex(index);\n };\n switch (event.key) {\n case \"ArrowDown\":\n event.preventDefault();\n moveTo(nextEnabledIndex(activeIndex < 0 ? 0 : activeIndex + 1, 1));\n break;\n case \"ArrowUp\":\n event.preventDefault();\n moveTo(\n activeIndex < 0\n ? nextEnabledIndex(0, 1)\n : nextEnabledIndex(activeIndex - 1, -1),\n );\n break;\n case \"Home\":\n event.preventDefault();\n moveTo(nextEnabledIndex(0, 1));\n break;\n case \"End\":\n event.preventDefault();\n moveTo(nextEnabledIndex(options.length - 1, -1));\n break;\n case \" \":\n case \"Enter\": {\n if (event.key === \" \" && !hasSpaceCommit) break;\n const option = options[activeIndex];\n if (!option || option.isDisabled) break;\n event.preventDefault();\n commit(option, close);\n break;\n }\n default:\n break;\n }\n };\n\n const triggerLabel = (() => {\n if (selectedValues.length === 0) return undefined;\n if (!isMultiple) return labelOf(selectedValues[0]!);\n const shown = selectedValues.slice(0, maxTriggerItems);\n const remaining = selectedValues.length - shown.length;\n if (triggerDisplay === \"badges\") {\n // Display-only: the trigger is ComplexSelector's own <button>, so a\n // removable token would nest a button in a button.\n return (\n <HStack gap={0.5} vAlign=\"center\" wrap=\"wrap\">\n {shown.map((value) => (\n <Token key={value} label={labelOf(value)} size=\"sm\" />\n ))}\n {remaining > 0 ? <Text color=\"secondary\">{`+${remaining}`}</Text> : null}\n </HStack>\n );\n }\n const joined = shown.map(labelOf).join(\", \");\n return remaining > 0 ? `${joined}, +${remaining}` : joined;\n })();\n\n const totalText = (total: number) =>\n formatTotalCount?.(total) ?? t(\"uic.PagedSelector.totalItems\", { total });\n const loadingText = t(\"uic.PagedSelector.loading\");\n\n return (\n <ComplexSelector<string[]>\n {...rest}\n label={label}\n value={selectedValues}\n onChange={emit}\n triggerLabel={triggerLabel}\n placeholder={placeholder ?? t(\"uic.PagedSelector.placeholder\", { label })}\n isLoading={isLoading}\n size={size}\n hasClear={hasClear}\n onClear={onClear ?? (() => emit([]))}\n onOpenChange={(isOpen) => {\n // Astryx drops its highlight when the panel closes, so the next open\n // starts clean.\n if (!isOpen) setHighlightedIndex(-1);\n onOpenChange?.(isOpen);\n }}\n contentXstyle={contentStyles.reset}\n >\n {(_current, _commit, close) => (\n <div>\n {hasSearch ? (\n <>\n <PanelSearchInput\n label={searchLabel ?? t(\"uic.PagedSelector.searchOptions\")}\n clearLabel={clearSearchLabel ?? t(\"uic.PagedSelector.clearSearch\")}\n placeholder={\n searchPlaceholder ?? t(\"uic.PagedSelector.searchPlaceholder\")\n }\n value={search}\n onValueChange={(next) => {\n setInternalSearch(next);\n // Seeds the first selectable row, so type-then-Enter works.\n setHighlightedIndex(nextEnabledIndex(0, 1));\n onSearchChange?.(next);\n }}\n onKeyDown={(event) => handleNavKeyDown(event, close, false)}\n aria-controls={listboxId}\n aria-activedescendant={\n activeIndex >= 0 ? optionIdOf(activeIndex) : undefined\n }\n />\n <Divider />\n </>\n ) : null}\n {header}\n <div\n id={listboxId}\n role=\"listbox\"\n aria-label={label}\n aria-multiselectable={isMultiple || undefined}\n // With no search box nothing else in the panel takes focus, so the\n // popover's autofocus lands here and the arrows work.\n tabIndex={hasSearch ? undefined : 0}\n aria-activedescendant={\n !hasSearch && activeIndex >= 0 ? optionIdOf(activeIndex) : undefined\n }\n onKeyDown={\n hasSearch ? undefined : (event) => handleNavKeyDown(event, close, true)\n }\n onScroll={handleScroll}\n className=\"uic-paged-selector__listbox\"\n style={{ maxHeight: listMaxHeight }}\n >\n {options.length === 0 ? (\n <div className=\"uic-paged-selector__empty\">\n {emptyText !== undefined ? (\n emptyText\n ) : isLoading ? (\n <HStack gap={1} vAlign=\"center\" hAlign=\"center\">\n <Spinner size=\"sm\" />\n <Text color=\"secondary\">{loadingText}</Text>\n </HStack>\n ) : (\n <Text color=\"secondary\">{t(\"uic.PagedSelector.noResults\")}</Text>\n )}\n </div>\n ) : (\n options.map((option, index) => {\n const selected = isSelected(option.value);\n const isDisabled = option.isDisabled ?? false;\n return (\n <div\n key={option.value}\n id={optionIdOf(index)}\n role=\"option\"\n aria-selected={selected}\n aria-disabled={isDisabled || undefined}\n className=\"uic-paged-selector__option\"\n data-size={size === \"sm\" ? \"sm\" : undefined}\n data-selected={selected ? \"true\" : undefined}\n data-highlighted={index === activeIndex ? \"true\" : undefined}\n data-disabled={isDisabled ? \"true\" : undefined}\n onClick={() => {\n if (isDisabled) return;\n setHighlightedIndex(index);\n commit(option, close);\n }}\n onMouseEnter={() => {\n if (!isDisabled) setHighlightedIndex(index);\n }}\n >\n {selectionIndicator === \"checkbox\" && (\n <span className=\"uic-paged-selector__option-mark\">\n <CheckboxMark\n state={selected ? \"checked\" : \"unchecked\"}\n size=\"sm\"\n isDisabled={isDisabled}\n />\n </span>\n )}\n <span className=\"uic-paged-selector__option-content\">\n <SelectorOption\n icon={option.icon}\n label={option.labelContent ?? option.label}\n description={option.description}\n endContent={option.endContent}\n />\n </span>\n {/* Rendered with the state passed down: a theme that swaps\n the check for a radio draws its empty circle too. */}\n {selectionIndicator === \"check\" && (\n <span className=\"uic-paged-selector__option-mark\">\n <CheckMark\n state={selected ? \"checked\" : \"unchecked\"}\n size=\"sm\"\n isDisabled={isDisabled}\n {...themeProps(\"selector-check\")}\n />\n </span>\n )}\n </div>\n );\n })\n )}\n </div>\n {(typeof footer === \"function\" ? footer(close) : footer) ??\n (typeof totalCount === \"number\" && totalCount > 0 ? (\n <HStack\n gap={1}\n vAlign=\"center\"\n hAlign=\"end\"\n className=\"uic-paged-selector__foot\"\n >\n {isLoadingMore ? <Spinner size=\"sm\" /> : null}\n <Text color=\"secondary\" size=\"sm\">\n {totalText(totalCount)}\n </Text>\n </HStack>\n ) : null)}\n <VisuallyHidden as=\"div\" aria-live=\"polite\">\n {options.length === 0 && isLoading\n ? loadingText\n : totalText(options.length)}\n </VisuallyHidden>\n </div>\n )}\n </ComplexSelector>\n );\n}\n"],"mappings":"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"}
|
|
@@ -2,8 +2,8 @@ import { useUicTranslator as e } from "../../i18n/useUicTranslator.js";
|
|
|
2
2
|
import { HStack as t } from "@astryxdesign/core/Stack";
|
|
3
3
|
import { Text as n } from "@astryxdesign/core/Text";
|
|
4
4
|
import { jsx as r, jsxs as i } from "react/jsx-runtime";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { IconButton as a } from "@astryxdesign/core/IconButton";
|
|
6
|
+
import { Icon as o } from "@astryxdesign/core/Icon";
|
|
7
7
|
//#region src/components/SelectionLabel/SelectionLabel.tsx
|
|
8
8
|
function s({ count: s, onClear: c, label: l, clearLabel: u, clearIcon: d }) {
|
|
9
9
|
let f = e();
|
|
@@ -12,10 +12,10 @@ function s({ count: s, onClear: c, label: l, clearLabel: u, clearIcon: d }) {
|
|
|
12
12
|
return /* @__PURE__ */ i(t, {
|
|
13
13
|
gap: 1,
|
|
14
14
|
align: "center",
|
|
15
|
-
children: [/* @__PURE__ */ r(n, { children: l ?? f("uic.SelectionLabel.selectedCount", { count: s }) }), c ? /* @__PURE__ */ r(
|
|
15
|
+
children: [/* @__PURE__ */ r(n, { children: l ?? f("uic.SelectionLabel.selectedCount", { count: s }) }), c ? /* @__PURE__ */ r(a, {
|
|
16
16
|
variant: "ghost",
|
|
17
17
|
size: "sm",
|
|
18
|
-
icon: d ?? /* @__PURE__ */ r(
|
|
18
|
+
icon: d ?? /* @__PURE__ */ r(o, { icon: "close" }),
|
|
19
19
|
label: p,
|
|
20
20
|
tooltip: p,
|
|
21
21
|
onClick: c
|
|
@@ -1,12 +1,33 @@
|
|
|
1
1
|
import { default as React, ReactNode } from 'react';
|
|
2
2
|
import { StyleXStyles } from '@stylexjs/stylex';
|
|
3
|
-
import {
|
|
4
|
-
import { FieldStatusVariant } from '@astryxdesign/core/Field';
|
|
3
|
+
import { FieldInputBaseProps, FieldPartStyleProps, FieldStatusVariant } from '@astryxdesign/core/Field';
|
|
5
4
|
import { IconType } from '@astryxdesign/core/Icon';
|
|
6
5
|
import { LayerAlignment, LayerPlacement } from '@astryxdesign/core/Layer';
|
|
7
6
|
import { SizeValue } from '@astryxdesign/core/utils';
|
|
8
7
|
export type ComplexSelectorVariant = "input" | "ghost";
|
|
9
8
|
export type ComplexSelectorSize = "sm" | "md" | "lg";
|
|
9
|
+
/**
|
|
10
|
+
* Props the `renderTrigger` render prop hands to the control the caller
|
|
11
|
+
* renders.
|
|
12
|
+
* Spread them onto that control: it becomes the popup's anchor, the element
|
|
13
|
+
* focus returns to, and the control that announces the popup's state.
|
|
14
|
+
*/
|
|
15
|
+
export interface ComplexSelectorRenderTriggerProps extends Omit<FieldInputBaseProps, keyof FieldPartStyleProps | "id" | "onClick" | "onKeyDown" | "aria-haspopup" | "aria-expanded" | "aria-controls" | "aria-busy"> {
|
|
16
|
+
/**
|
|
17
|
+
* Attaches the control as the popup's anchor and focus-return target, and
|
|
18
|
+
* forwards it to the selector's own `ref`.
|
|
19
|
+
*/
|
|
20
|
+
ref: (element: HTMLElement | null) => void;
|
|
21
|
+
/** The id the field would have given its own button. */
|
|
22
|
+
id: string;
|
|
23
|
+
onClick: (event: React.MouseEvent<HTMLElement>) => void;
|
|
24
|
+
/** ArrowDown opens the popup, as it does on the built-in button. */
|
|
25
|
+
onKeyDown: (event: React.KeyboardEvent<HTMLElement>) => void;
|
|
26
|
+
"aria-haspopup": "dialog";
|
|
27
|
+
"aria-expanded": boolean;
|
|
28
|
+
"aria-controls": string;
|
|
29
|
+
"aria-busy": boolean | undefined;
|
|
30
|
+
}
|
|
10
31
|
export interface ComplexSelectorRenderState {
|
|
11
32
|
/** Whether the selector surface is open. */
|
|
12
33
|
isOpen: boolean;
|
|
@@ -40,9 +61,29 @@ export interface ComplexSelectorStatus {
|
|
|
40
61
|
type: "warning" | "error" | "success";
|
|
41
62
|
message?: string;
|
|
42
63
|
}
|
|
43
|
-
export interface ComplexSelectorProps<Value> extends Omit<
|
|
64
|
+
export interface ComplexSelectorProps<Value> extends Omit<FieldInputBaseProps, "children" | "onChange"> {
|
|
44
65
|
/** Label text for accessibility and the field label. */
|
|
45
66
|
label: string;
|
|
67
|
+
/**
|
|
68
|
+
* Ref to the trigger: the button that opens the popup and carries the
|
|
69
|
+
* accessible name — or, with `renderTrigger`, the caller's control.
|
|
70
|
+
*/
|
|
71
|
+
ref?: React.Ref<HTMLElement>;
|
|
72
|
+
/**
|
|
73
|
+
* Click handler on the trigger. It runs before the popup opens; call
|
|
74
|
+
* `preventDefault()` to keep the popup closed. A press elsewhere in the
|
|
75
|
+
* control box goes through the trigger, so it reaches this handler too.
|
|
76
|
+
* With `renderTrigger` it lands on the caller's control and runs after the
|
|
77
|
+
* built-in toggle, which it cannot cancel.
|
|
78
|
+
*/
|
|
79
|
+
onClick?: React.MouseEventHandler<HTMLElement>;
|
|
80
|
+
/**
|
|
81
|
+
* Key handler on the trigger. It runs after the built-in handling and
|
|
82
|
+
* receives every key, including the ArrowDown that opens the popup. With
|
|
83
|
+
* `renderTrigger` it lands on the caller's control, runs after the built-in
|
|
84
|
+
* handling, and is skipped for keys that handling cancels.
|
|
85
|
+
*/
|
|
86
|
+
onKeyDown?: React.KeyboardEventHandler<HTMLElement>;
|
|
46
87
|
/** Current controlled value. */
|
|
47
88
|
value: Value;
|
|
48
89
|
/** Called when custom content commits a new value. */
|
|
@@ -77,7 +118,13 @@ export interface ComplexSelectorProps<Value> extends Omit<BaseProps<HTMLDivEleme
|
|
|
77
118
|
onClear?: () => void;
|
|
78
119
|
/** Validation status. */
|
|
79
120
|
status?: ComplexSelectorStatus;
|
|
80
|
-
/**
|
|
121
|
+
/**
|
|
122
|
+
* How the status message is placed relative to the input.
|
|
123
|
+
* - 'attached': message overlaps directly below the input (bordered treatment; detached when variant is ghost)
|
|
124
|
+
* - 'detached': message floats below as a separate element with spacing
|
|
125
|
+
* - 'tooltip': no message box; the status icon becomes a focusable info-tip button that reveals the message on hover, keyboard focus, or tap
|
|
126
|
+
* @default 'attached'
|
|
127
|
+
*/
|
|
81
128
|
statusVariant?: FieldStatusVariant;
|
|
82
129
|
/** Tooltip text displayed next to the label. */
|
|
83
130
|
labelTooltip?: string;
|
|
@@ -99,6 +146,35 @@ export interface ComplexSelectorProps<Value> extends Omit<BaseProps<HTMLDivEleme
|
|
|
99
146
|
* state in the parent — the selector owns its visibility.
|
|
100
147
|
*/
|
|
101
148
|
handleRef?: React.Ref<ComplexSelectorHandle>;
|
|
149
|
+
/**
|
|
150
|
+
* Render the control the popup hangs off — a glyph in a list row, a chip,
|
|
151
|
+
* an icon button — instead of the selector's own field and button. Spread
|
|
152
|
+
* the given props onto it; the popup is then anchored to that control and
|
|
153
|
+
* still labelled by `label`. The field chrome (`Field`, status, spinner,
|
|
154
|
+
* chevron) is not rendered; the caller owns the opener. Pair with
|
|
155
|
+
* `handleRef` to open the popup from a keystroke elsewhere.
|
|
156
|
+
*
|
|
157
|
+
* Hover and pressed paint stay yours. The open state reaches your control
|
|
158
|
+
* as `aria-expanded` on the given props, so style it from the rendered
|
|
159
|
+
* attribute. A pressed look keyed to `:active` is not a substitute:
|
|
160
|
+
* `:active` does not behave the same under a coarse pointer, which is why
|
|
161
|
+
* menu rows drop coarse-pointer `:active` paint entirely.
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* ```
|
|
165
|
+
* <ComplexSelector
|
|
166
|
+
* label="Assignee"
|
|
167
|
+
* renderTrigger={props => <IconButton icon="user" label="Assign" {...props} />}
|
|
168
|
+
* />
|
|
169
|
+
* ```
|
|
170
|
+
*
|
|
171
|
+
* @example
|
|
172
|
+
* ```
|
|
173
|
+
* // Styling the open state from the rendered attribute:
|
|
174
|
+
* // .my-trigger[aria-expanded='true'] { background: var(--color-overlay-pressed); }
|
|
175
|
+
* ```
|
|
176
|
+
*/
|
|
177
|
+
renderTrigger?: (props: ComplexSelectorRenderTriggerProps) => ReactNode;
|
|
102
178
|
/**
|
|
103
179
|
* Called whenever the selector surface opens or closes, however it happened
|
|
104
180
|
* — the trigger, the keyboard, a light dismiss, Escape, content that calls
|
|
@@ -108,7 +184,7 @@ export interface ComplexSelectorProps<Value> extends Omit<BaseProps<HTMLDivEleme
|
|
|
108
184
|
onOpenChange?: (isOpen: boolean) => void;
|
|
109
185
|
/** StyleX styles for the popup content container. */
|
|
110
186
|
contentXstyle?: StyleXStyles;
|
|
111
|
-
/** Test ID for the trigger
|
|
187
|
+
/** Test ID for the trigger button. */
|
|
112
188
|
"data-testid"?: string;
|
|
113
189
|
}
|
|
114
190
|
/**
|
|
@@ -138,7 +214,7 @@ export interface ComplexSelectorProps<Value> extends Omit<BaseProps<HTMLDivEleme
|
|
|
138
214
|
* </ComplexSelector>
|
|
139
215
|
* ```
|
|
140
216
|
*/
|
|
141
|
-
export declare function ComplexSelector<Value>({ label, value, onChange, changeAction, children, triggerLabel, placeholder: placeholderFromProps, isLabelHidden, description, isOptional, isRequired, isDisabled, isLoading, hasClear, onClear, status, statusVariant, labelTooltip, size, variant, startIcon, width, placement, alignment, handleRef, onOpenChange, contentXstyle,
|
|
217
|
+
export declare function ComplexSelector<Value>({ label, value, onChange, changeAction, children, triggerLabel, placeholder: placeholderFromProps, isLabelHidden, description, isOptional, isRequired, isDisabled, isLoading, hasClear, onClear, status, statusVariant, labelTooltip, size, variant, startIcon, width, placement, alignment, handleRef, renderTrigger, onOpenChange, contentXstyle, containerXstyle, containerClassName, containerStyle, controlXstyle, controlClassName, controlStyle, ref, id, onClick: onClickProp, onKeyDown: onKeyDownProp, "aria-describedby": ariaDescribedByProp, ...restProps }: ComplexSelectorProps<Value>): React.JSX.Element;
|
|
142
218
|
export declare namespace ComplexSelector {
|
|
143
219
|
var displayName: string;
|
|
144
220
|
}
|