@backstage/ui 0.15.1-next.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +167 -0
- package/dist/components/Combobox/Combobox.esm.js +150 -52
- package/dist/components/Combobox/Combobox.esm.js.map +1 -1
- package/dist/components/Combobox/Combobox.module.css.esm.js +2 -2
- package/dist/components/Combobox/ComboboxItem.esm.js +76 -0
- package/dist/components/Combobox/ComboboxItem.esm.js.map +1 -0
- package/dist/components/Combobox/ComboboxListBox.esm.js +215 -17
- package/dist/components/Combobox/ComboboxListBox.esm.js.map +1 -1
- package/dist/components/Combobox/definition.esm.js +62 -3
- package/dist/components/Combobox/definition.esm.js.map +1 -1
- package/dist/components/Combobox/useAsyncComboboxState.esm.js +133 -0
- package/dist/components/Combobox/useAsyncComboboxState.esm.js.map +1 -0
- package/dist/components/Header/HeaderNav.esm.js +0 -1
- package/dist/components/Header/HeaderNav.esm.js.map +1 -1
- package/dist/components/Select/Select.esm.js +87 -19
- package/dist/components/Select/Select.esm.js.map +1 -1
- package/dist/components/Select/Select.module.css.esm.js +2 -2
- package/dist/components/Select/SelectContent.esm.js +70 -18
- package/dist/components/Select/SelectContent.esm.js.map +1 -1
- package/dist/components/Select/SelectItem.esm.js +76 -0
- package/dist/components/Select/SelectItem.esm.js.map +1 -0
- package/dist/components/Select/SelectListBox.esm.js +175 -19
- package/dist/components/Select/SelectListBox.esm.js.map +1 -1
- package/dist/components/Select/SelectTrigger.esm.js +1 -1
- package/dist/components/Select/SelectTrigger.esm.js.map +1 -1
- package/dist/components/Select/definition.esm.js +72 -9
- package/dist/components/Select/definition.esm.js.map +1 -1
- package/dist/components/Skeleton/Skeleton.module.css.esm.js +2 -2
- package/dist/components/Skeleton/definition.esm.js +1 -0
- package/dist/components/Skeleton/definition.esm.js.map +1 -1
- package/dist/components/Table/Table.module.css.esm.js +2 -2
- package/dist/components/Table/components/Table.esm.js +60 -57
- package/dist/components/Table/components/Table.esm.js.map +1 -1
- package/dist/components/Table/definition.esm.js +2 -1
- package/dist/components/Table/definition.esm.js.map +1 -1
- package/dist/components/TablePagination/TablePagination.esm.js +4 -1
- package/dist/components/TablePagination/TablePagination.esm.js.map +1 -1
- package/dist/components/Tabs/TabsIndicators.esm.js +155 -108
- package/dist/components/Tabs/TabsIndicators.esm.js.map +1 -1
- package/dist/css/styles.css +4 -4
- package/dist/hooks/useCollectionAdapter.esm.js +67 -0
- package/dist/hooks/useCollectionAdapter.esm.js.map +1 -0
- package/dist/hooks/useDelayedVisibility.esm.js +17 -0
- package/dist/hooks/useDelayedVisibility.esm.js.map +1 -0
- package/dist/hooks/useTrackedSelectionKeys.esm.js +23 -0
- package/dist/hooks/useTrackedSelectionKeys.esm.js.map +1 -0
- package/dist/index.d.ts +742 -77
- package/dist/index.esm.js +5 -2
- package/dist/index.esm.js.map +1 -1
- package/dist/utils/selectableCollection.esm.js +75 -0
- package/dist/utils/selectableCollection.esm.js.map +1 -0
- package/package.json +4 -4
|
@@ -1,45 +1,243 @@
|
|
|
1
|
-
import { jsx,
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
2
|
+
import { useContext, cloneElement } from 'react';
|
|
3
|
+
import { ComboBoxStateContext, ListBox, SelectableCollectionContext, Collection, ListBoxLoadMoreItem, ListBoxSection, Header, Text } from 'react-aria-components';
|
|
4
|
+
import { useFilter } from 'react-aria';
|
|
5
|
+
import clsx from 'clsx';
|
|
4
6
|
import { useDefinition } from '../../hooks/useDefinition/useDefinition.esm.js';
|
|
5
|
-
import {
|
|
7
|
+
import { normalizeOptions } from '../../utils/selectableCollection.esm.js';
|
|
8
|
+
import { ComboboxItem } from './ComboboxItem.esm.js';
|
|
9
|
+
import { ComboboxListBoxDefinition, ComboboxSectionDefinition, ComboboxItemTextDefinition, ComboboxListBoxItemDefinition } from './definition.esm.js';
|
|
10
|
+
import { Skeleton } from '../Skeleton/Skeleton.esm.js';
|
|
11
|
+
import '../Skeleton/Skeleton.module.css.esm.js';
|
|
12
|
+
import { useDelayedVisibility } from '../../hooks/useDelayedVisibility.esm.js';
|
|
6
13
|
|
|
14
|
+
const loadingRowWidths = ["70%", "55%", "65%"];
|
|
15
|
+
const loadingIndicatorDelayMs = 300;
|
|
16
|
+
function LoadingRows({
|
|
17
|
+
count,
|
|
18
|
+
className,
|
|
19
|
+
rowClassName
|
|
20
|
+
}) {
|
|
21
|
+
return /* @__PURE__ */ jsx("div", { className, "aria-hidden": "true", children: loadingRowWidths.slice(0, count).map((width) => /* @__PURE__ */ jsx("div", { className: rowClassName, children: /* @__PURE__ */ jsx(Skeleton, { width, height: "0.75rem" }) }, width)) });
|
|
22
|
+
}
|
|
7
23
|
const NoResults = () => {
|
|
8
24
|
const { ownProps } = useDefinition(ComboboxListBoxDefinition, {});
|
|
9
25
|
const { classes } = ownProps;
|
|
10
26
|
return /* @__PURE__ */ jsx("div", { className: classes.noResults, children: "No results found." });
|
|
11
27
|
};
|
|
12
|
-
function
|
|
13
|
-
const { ownProps } = useDefinition(
|
|
28
|
+
function ComboboxOptionItem({ option }) {
|
|
29
|
+
const { ownProps } = useDefinition(ComboboxItemTextDefinition, {
|
|
30
|
+
title: option.label,
|
|
31
|
+
description: option.description,
|
|
32
|
+
leadingIcon: option.leadingIcon
|
|
33
|
+
});
|
|
14
34
|
const { classes } = ownProps;
|
|
35
|
+
const {
|
|
36
|
+
ownProps: { classes: legacyOptionClasses }
|
|
37
|
+
} = useDefinition(ComboboxListBoxItemDefinition, {});
|
|
15
38
|
return /* @__PURE__ */ jsxs(
|
|
16
|
-
|
|
39
|
+
ComboboxItem,
|
|
17
40
|
{
|
|
18
|
-
id: option.
|
|
19
|
-
|
|
41
|
+
id: option.id,
|
|
42
|
+
value: option,
|
|
20
43
|
className: classes.root,
|
|
44
|
+
textValue: option.label,
|
|
21
45
|
isDisabled: option.disabled,
|
|
46
|
+
showSelectionIndicator: true,
|
|
22
47
|
children: [
|
|
23
|
-
/* @__PURE__ */ jsx("div", { className: classes.
|
|
24
|
-
/* @__PURE__ */
|
|
48
|
+
option.leadingIcon && /* @__PURE__ */ jsx("div", { className: classes.leadingIcon, children: option.leadingIcon }),
|
|
49
|
+
/* @__PURE__ */ jsxs("div", { className: classes.text, children: [
|
|
50
|
+
/* @__PURE__ */ jsx(
|
|
51
|
+
Text,
|
|
52
|
+
{
|
|
53
|
+
slot: "label",
|
|
54
|
+
className: clsx(classes.title, legacyOptionClasses.label),
|
|
55
|
+
children: option.label
|
|
56
|
+
}
|
|
57
|
+
),
|
|
58
|
+
option.description && /* @__PURE__ */ jsx(Text, { slot: "description", className: classes.description, children: option.description })
|
|
59
|
+
] })
|
|
25
60
|
]
|
|
26
61
|
}
|
|
27
62
|
);
|
|
28
63
|
}
|
|
29
|
-
function ComboboxSectionItems({
|
|
64
|
+
function ComboboxSectionItems({
|
|
65
|
+
section
|
|
66
|
+
}) {
|
|
30
67
|
const { ownProps } = useDefinition(ComboboxSectionDefinition, {});
|
|
31
68
|
const { classes } = ownProps;
|
|
32
69
|
return /* @__PURE__ */ jsxs(ListBoxSection, { className: classes.root, children: [
|
|
33
70
|
/* @__PURE__ */ jsx(Header, { className: classes.header, children: section.title }),
|
|
34
|
-
section.options.map((option) => /* @__PURE__ */ jsx(
|
|
71
|
+
section.options.map((option) => /* @__PURE__ */ jsx(ComboboxOptionItem, { option }, option.id))
|
|
35
72
|
] });
|
|
36
73
|
}
|
|
74
|
+
function renderComboboxOption(item) {
|
|
75
|
+
if ("options" in item) {
|
|
76
|
+
return /* @__PURE__ */ jsx(ComboboxSectionItems, { section: item }, item.title);
|
|
77
|
+
}
|
|
78
|
+
return /* @__PURE__ */ jsx(ComboboxOptionItem, { option: item }, item.id);
|
|
79
|
+
}
|
|
80
|
+
function getCollectionFilter({
|
|
81
|
+
search,
|
|
82
|
+
hasItems,
|
|
83
|
+
inputValue,
|
|
84
|
+
contains
|
|
85
|
+
}) {
|
|
86
|
+
const searchProps = typeof search === "object" ? search : void 0;
|
|
87
|
+
if (searchProps?.mode === "server") {
|
|
88
|
+
return void 0;
|
|
89
|
+
}
|
|
90
|
+
const customFilter = searchProps?.filter;
|
|
91
|
+
if (customFilter) {
|
|
92
|
+
return (_textValue, node) => customFilter(node.value, inputValue);
|
|
93
|
+
}
|
|
94
|
+
if (search || hasItems) {
|
|
95
|
+
return (textValue) => contains(textValue, inputValue);
|
|
96
|
+
}
|
|
97
|
+
return void 0;
|
|
98
|
+
}
|
|
99
|
+
function ComboboxCollection({
|
|
100
|
+
normalizedOptions,
|
|
101
|
+
items,
|
|
102
|
+
children,
|
|
103
|
+
dependencies,
|
|
104
|
+
getItemTextValue
|
|
105
|
+
}) {
|
|
106
|
+
if (normalizedOptions) {
|
|
107
|
+
return /* @__PURE__ */ jsx(Fragment, { children: normalizedOptions.map(renderComboboxOption) });
|
|
108
|
+
}
|
|
109
|
+
if (items) {
|
|
110
|
+
const renderItemWithTextValue = (item) => {
|
|
111
|
+
let renderedItem;
|
|
112
|
+
if (typeof children === "function") {
|
|
113
|
+
renderedItem = children(item);
|
|
114
|
+
} else {
|
|
115
|
+
renderedItem = /* @__PURE__ */ jsx(ComboboxOptionItem, { option: item });
|
|
116
|
+
}
|
|
117
|
+
if (!getItemTextValue) {
|
|
118
|
+
return renderedItem;
|
|
119
|
+
}
|
|
120
|
+
return cloneElement(renderedItem, {
|
|
121
|
+
textValue: getItemTextValue(item)
|
|
122
|
+
});
|
|
123
|
+
};
|
|
124
|
+
return /* @__PURE__ */ jsx(Collection, { items, dependencies, children: renderItemWithTextValue });
|
|
125
|
+
}
|
|
126
|
+
return children;
|
|
127
|
+
}
|
|
128
|
+
function ComboboxEmptyState({
|
|
129
|
+
isLoading,
|
|
130
|
+
className,
|
|
131
|
+
rowClassName
|
|
132
|
+
}) {
|
|
133
|
+
if (!isLoading) {
|
|
134
|
+
return /* @__PURE__ */ jsx(NoResults, {});
|
|
135
|
+
}
|
|
136
|
+
return /* @__PURE__ */ jsx(LoadingRows, { count: 3, className, rowClassName });
|
|
137
|
+
}
|
|
138
|
+
function ComboboxLoadMoreIndicator({
|
|
139
|
+
loading,
|
|
140
|
+
isCollectionLoading,
|
|
141
|
+
isIndicatorVisible,
|
|
142
|
+
className,
|
|
143
|
+
rowClassName
|
|
144
|
+
}) {
|
|
145
|
+
if (!loading?.onLoadMore || isCollectionLoading) {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
const onLoadMore = loading.state === "loadingMore" ? void 0 : loading.onLoadMore;
|
|
149
|
+
return /* @__PURE__ */ jsx(
|
|
150
|
+
ListBoxLoadMoreItem,
|
|
151
|
+
{
|
|
152
|
+
isLoading: isIndicatorVisible,
|
|
153
|
+
onLoadMore,
|
|
154
|
+
scrollOffset: 0,
|
|
155
|
+
children: /* @__PURE__ */ jsx(
|
|
156
|
+
LoadingRows,
|
|
157
|
+
{
|
|
158
|
+
count: 1,
|
|
159
|
+
className,
|
|
160
|
+
rowClassName
|
|
161
|
+
}
|
|
162
|
+
)
|
|
163
|
+
}
|
|
164
|
+
);
|
|
165
|
+
}
|
|
37
166
|
function ComboboxListBox(props) {
|
|
38
167
|
const { ownProps } = useDefinition(ComboboxListBoxDefinition, props);
|
|
39
|
-
const {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
168
|
+
const {
|
|
169
|
+
classes,
|
|
170
|
+
options,
|
|
171
|
+
items,
|
|
172
|
+
children,
|
|
173
|
+
dependencies,
|
|
174
|
+
search,
|
|
175
|
+
loading,
|
|
176
|
+
isStale,
|
|
177
|
+
getItemTextValue
|
|
178
|
+
} = ownProps;
|
|
179
|
+
const normalizedOptions = options && normalizeOptions(options);
|
|
180
|
+
const state = useContext(ComboBoxStateContext);
|
|
181
|
+
const { contains } = useFilter({ sensitivity: "base" });
|
|
182
|
+
const inputValue = state?.inputValue ?? "";
|
|
183
|
+
const filter = getCollectionFilter({
|
|
184
|
+
search,
|
|
185
|
+
hasItems: items !== void 0,
|
|
186
|
+
inputValue,
|
|
187
|
+
contains
|
|
188
|
+
});
|
|
189
|
+
const isCollectionLoading = loading?.state === "loading" || loading?.state === "filtering" || loading?.state === "sorting";
|
|
190
|
+
const isBusy = isCollectionLoading || loading?.state === "loadingMore";
|
|
191
|
+
const showStale = useDelayedVisibility(
|
|
192
|
+
isStale ?? false,
|
|
193
|
+
loadingIndicatorDelayMs
|
|
194
|
+
);
|
|
195
|
+
const showLoadMoreIndicator = useDelayedVisibility(
|
|
196
|
+
loading?.state === "loadingMore",
|
|
197
|
+
loadingIndicatorDelayMs
|
|
198
|
+
);
|
|
199
|
+
const listBox = /* @__PURE__ */ jsxs(
|
|
200
|
+
ListBox,
|
|
201
|
+
{
|
|
202
|
+
className: classes.root,
|
|
203
|
+
"data-stale": showStale || void 0,
|
|
204
|
+
renderEmptyState: () => /* @__PURE__ */ jsx(
|
|
205
|
+
ComboboxEmptyState,
|
|
206
|
+
{
|
|
207
|
+
isLoading: isCollectionLoading,
|
|
208
|
+
className: classes.loading,
|
|
209
|
+
rowClassName: classes.loadingRow
|
|
210
|
+
}
|
|
211
|
+
),
|
|
212
|
+
children: [
|
|
213
|
+
/* @__PURE__ */ jsx(
|
|
214
|
+
ComboboxCollection,
|
|
215
|
+
{
|
|
216
|
+
normalizedOptions,
|
|
217
|
+
items,
|
|
218
|
+
dependencies,
|
|
219
|
+
getItemTextValue,
|
|
220
|
+
children
|
|
221
|
+
}
|
|
222
|
+
),
|
|
223
|
+
/* @__PURE__ */ jsx(
|
|
224
|
+
ComboboxLoadMoreIndicator,
|
|
225
|
+
{
|
|
226
|
+
loading,
|
|
227
|
+
isCollectionLoading,
|
|
228
|
+
isIndicatorVisible: showLoadMoreIndicator,
|
|
229
|
+
className: classes.loading,
|
|
230
|
+
rowClassName: classes.loadingRow
|
|
231
|
+
}
|
|
232
|
+
)
|
|
233
|
+
]
|
|
234
|
+
}
|
|
235
|
+
);
|
|
236
|
+
const busyListBox = /* @__PURE__ */ jsx("div", { "aria-busy": isBusy || void 0, children: listBox });
|
|
237
|
+
if (!filter) {
|
|
238
|
+
return busyListBox;
|
|
239
|
+
}
|
|
240
|
+
return /* @__PURE__ */ jsx(SelectableCollectionContext.Provider, { value: { filter }, children: busyListBox });
|
|
43
241
|
}
|
|
44
242
|
|
|
45
243
|
export { ComboboxListBox };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ComboboxListBox.esm.js","sources":["../../../src/components/Combobox/ComboboxListBox.tsx"],"sourcesContent":["/*\n * Copyright 2026 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n ListBox,\n ListBoxItem,\n ListBoxSection,\n Header,\n Text,\n} from 'react-aria-components';\nimport { RiCheckLine } from '@remixicon/react';\nimport { useDefinition } from '../../hooks/useDefinition';\nimport {\n ComboboxListBoxDefinition,\n ComboboxListBoxItemDefinition,\n ComboboxSectionDefinition,\n} from './definition';\nimport type { Option, OptionSection, ComboboxListBoxOwnProps } from './types';\n\nconst NoResults = () => {\n const { ownProps } = useDefinition(ComboboxListBoxDefinition, {});\n const { classes } = ownProps;\n\n return <div className={classes.noResults}>No results found.</div>;\n};\n\nfunction ComboboxItem({ option }: { option: Option }) {\n const { ownProps } = useDefinition(ComboboxListBoxItemDefinition, {});\n const { classes } = ownProps;\n\n return (\n <ListBoxItem\n id={option.value}\n textValue={option.label}\n className={classes.root}\n isDisabled={option.disabled}\n >\n <div className={classes.indicator}>\n <RiCheckLine aria-hidden=\"true\" />\n </div>\n <Text slot=\"label\" className={classes.label}>\n {option.label}\n </Text>\n </ListBoxItem>\n );\n}\n\nfunction ComboboxSectionItems({ section }: { section: OptionSection }) {\n const { ownProps } = useDefinition(ComboboxSectionDefinition, {});\n const { classes } = ownProps;\n\n return (\n <ListBoxSection className={classes.root}>\n <Header className={classes.header}>{section.title}</Header>\n {section.options.map(option => (\n <ComboboxItem key={option.value} option={option} />\n ))}\n </ListBoxSection>\n );\n}\n\nexport function ComboboxListBox(props: ComboboxListBoxOwnProps) {\n const { ownProps } = useDefinition(ComboboxListBoxDefinition, props);\n const { classes, options } = ownProps;\n\n return (\n <ListBox className={classes.root} renderEmptyState={() => <NoResults />}>\n {options?.map(item =>\n 'options' in item ? (\n <ComboboxSectionItems key={item.title} section={item} />\n ) : (\n <ComboboxItem key={item.value} option={item} />\n ),\n )}\n </ListBox>\n );\n}\n"],"names":[],"mappings":";;;;;;AAgCA,MAAM,YAAY,MAAM;AACtB,EAAA,MAAM,EAAE,QAAA,EAAS,GAAI,aAAA,CAAc,yBAAA,EAA2B,EAAE,CAAA;AAChE,EAAA,MAAM,EAAE,SAAQ,GAAI,QAAA;AAEpB,EAAA,uBAAO,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,OAAA,CAAQ,WAAW,QAAA,EAAA,mBAAA,EAAiB,CAAA;AAC7D,CAAA;AAEA,SAAS,YAAA,CAAa,EAAE,MAAA,EAAO,EAAuB;AACpD,EAAA,MAAM,EAAE,QAAA,EAAS,GAAI,aAAA,CAAc,6BAAA,EAA+B,EAAE,CAAA;AACpE,EAAA,MAAM,EAAE,SAAQ,GAAI,QAAA;AAEpB,EAAA,uBACE,IAAA;AAAA,IAAC,WAAA;AAAA,IAAA;AAAA,MACC,IAAI,MAAA,CAAO,KAAA;AAAA,MACX,WAAW,MAAA,CAAO,KAAA;AAAA,MAClB,WAAW,OAAA,CAAQ,IAAA;AAAA,MACnB,YAAY,MAAA,CAAO,QAAA;AAAA,MAEnB,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,KAAA,EAAA,EAAI,WAAW,OAAA,CAAQ,SAAA,EACtB,8BAAC,WAAA,EAAA,EAAY,aAAA,EAAY,QAAO,CAAA,EAClC,CAAA;AAAA,wBACA,GAAA,CAAC,QAAK,IAAA,EAAK,OAAA,EAAQ,WAAW,OAAA,CAAQ,KAAA,EACnC,iBAAO,KAAA,EACV;AAAA;AAAA;AAAA,GACF;AAEJ;AAEA,SAAS,oBAAA,CAAqB,EAAE,OAAA,EAAQ,EAA+B;AACrE,EAAA,MAAM,EAAE,QAAA,EAAS,GAAI,aAAA,CAAc,yBAAA,EAA2B,EAAE,CAAA;AAChE,EAAA,MAAM,EAAE,SAAQ,GAAI,QAAA;AAEpB,EAAA,uBACE,IAAA,CAAC,cAAA,EAAA,EAAe,SAAA,EAAW,OAAA,CAAQ,IAAA,EACjC,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,MAAA,EAAA,EAAO,SAAA,EAAW,OAAA,CAAQ,MAAA,EAAS,kBAAQ,KAAA,EAAM,CAAA;AAAA,IACjD,OAAA,CAAQ,QAAQ,GAAA,CAAI,CAAA,MAAA,yBAClB,YAAA,EAAA,EAAgC,MAAA,EAAA,EAAd,MAAA,CAAO,KAAuB,CAClD;AAAA,GAAA,EACH,CAAA;AAEJ;AAEO,SAAS,gBAAgB,KAAA,EAAgC;AAC9D,EAAA,MAAM,EAAE,QAAA,EAAS,GAAI,aAAA,CAAc,2BAA2B,KAAK,CAAA;AACnE,EAAA,MAAM,EAAE,OAAA,EAAS,OAAA,EAAQ,GAAI,QAAA;AAE7B,EAAA,uBACE,GAAA,CAAC,OAAA,EAAA,EAAQ,SAAA,EAAW,OAAA,CAAQ,IAAA,EAAM,kBAAkB,sBAAM,GAAA,CAAC,SAAA,EAAA,EAAU,CAAA,EAClE,QAAA,EAAA,OAAA,EAAS,GAAA;AAAA,IAAI,CAAA,IAAA,KACZ,SAAA,IAAa,IAAA,mBACX,GAAA,CAAC,wBAAsC,OAAA,EAAS,IAAA,EAAA,EAArB,IAAA,CAAK,KAAsB,oBAEtD,GAAA,CAAC,YAAA,EAAA,EAA8B,MAAA,EAAQ,IAAA,EAAA,EAApB,KAAK,KAAqB;AAAA,GAEjD,EACF,CAAA;AAEJ;;;;"}
|
|
1
|
+
{"version":3,"file":"ComboboxListBox.esm.js","sources":["../../../src/components/Combobox/ComboboxListBox.tsx"],"sourcesContent":["/*\n * Copyright 2026 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { cloneElement, useContext } from 'react';\nimport {\n Collection,\n ComboBoxStateContext,\n Header,\n ListBox,\n ListBoxLoadMoreItem,\n ListBoxSection,\n SelectableCollectionContext,\n Text,\n} from 'react-aria-components';\nimport { useFilter } from 'react-aria';\nimport clsx from 'clsx';\nimport { useDefinition } from '../../hooks/useDefinition';\nimport { normalizeOptions } from '../../utils/selectableCollection';\nimport { ComboboxItem } from './ComboboxItem';\nimport {\n ComboboxItemTextDefinition,\n ComboboxListBoxDefinition,\n ComboboxListBoxItemDefinition,\n ComboboxSectionDefinition,\n} from './definition';\nimport type {\n CollectionItem,\n LoadingConfig,\n NormalizedOption,\n NormalizedOptionSection,\n} from '../../types/selectableCollection';\nimport type { ComboboxListBoxOwnProps } from './types';\nimport type { Node } from '@react-types/shared';\nimport { Skeleton } from '../Skeleton';\nimport { useDelayedVisibility } from '../../hooks/useDelayedVisibility';\n\nconst loadingRowWidths = ['70%', '55%', '65%'];\nconst loadingIndicatorDelayMs = 300;\n\nfunction LoadingRows({\n count,\n className,\n rowClassName,\n}: {\n count: number;\n className: string;\n rowClassName: string;\n}) {\n return (\n <div className={className} aria-hidden=\"true\">\n {loadingRowWidths.slice(0, count).map(width => (\n <div key={width} className={rowClassName}>\n <Skeleton width={width} height=\"0.75rem\" />\n </div>\n ))}\n </div>\n );\n}\n\nconst NoResults = () => {\n const { ownProps } = useDefinition(ComboboxListBoxDefinition, {});\n const { classes } = ownProps;\n\n return <div className={classes.noResults}>No results found.</div>;\n};\n\nfunction ComboboxOptionItem({ option }: { option: NormalizedOption }) {\n const { ownProps } = useDefinition(ComboboxItemTextDefinition, {\n title: option.label,\n description: option.description,\n leadingIcon: option.leadingIcon,\n });\n const { classes } = ownProps;\n const {\n ownProps: { classes: legacyOptionClasses },\n } = useDefinition(ComboboxListBoxItemDefinition, {});\n\n return (\n <ComboboxItem\n id={option.id}\n value={option}\n className={classes.root}\n textValue={option.label}\n isDisabled={option.disabled}\n showSelectionIndicator\n >\n {option.leadingIcon && (\n <div className={classes.leadingIcon}>{option.leadingIcon}</div>\n )}\n <div className={classes.text}>\n <Text\n slot=\"label\"\n className={clsx(classes.title, legacyOptionClasses.label)}\n >\n {option.label}\n </Text>\n {option.description && (\n <Text slot=\"description\" className={classes.description}>\n {option.description}\n </Text>\n )}\n </div>\n </ComboboxItem>\n );\n}\n\nfunction ComboboxSectionItems({\n section,\n}: {\n section: NormalizedOptionSection;\n}) {\n const { ownProps } = useDefinition(ComboboxSectionDefinition, {});\n const { classes } = ownProps;\n\n return (\n <ListBoxSection className={classes.root}>\n <Header className={classes.header}>{section.title}</Header>\n {section.options.map(option => (\n <ComboboxOptionItem key={option.id} option={option} />\n ))}\n </ListBoxSection>\n );\n}\n\nfunction renderComboboxOption(\n item: NormalizedOption | NormalizedOptionSection,\n) {\n if ('options' in item) {\n return <ComboboxSectionItems key={item.title} section={item} />;\n }\n\n return <ComboboxOptionItem key={item.id} option={item} />;\n}\n\nfunction getCollectionFilter<T extends CollectionItem>({\n search,\n hasItems,\n inputValue,\n contains,\n}: {\n search?: ComboboxListBoxOwnProps<T>['search'];\n hasItems: boolean;\n inputValue: string;\n contains: (textValue: string, inputValue: string) => boolean;\n}) {\n const searchProps = typeof search === 'object' ? search : undefined;\n if (searchProps?.mode === 'server') {\n return undefined;\n }\n\n const customFilter = searchProps?.filter;\n if (customFilter) {\n return (_textValue: string, node: Node<T>) =>\n customFilter(node.value as T, inputValue);\n }\n\n if (search || hasItems) {\n return (textValue: string) => contains(textValue, inputValue);\n }\n\n return undefined;\n}\n\nfunction ComboboxCollection<T extends CollectionItem>({\n normalizedOptions,\n items,\n children,\n dependencies,\n getItemTextValue,\n}: {\n normalizedOptions?: Array<NormalizedOption | NormalizedOptionSection>;\n items?: Iterable<T>;\n children?: React.ReactNode | ((item: T) => React.ReactElement);\n dependencies?: ReadonlyArray<unknown>;\n getItemTextValue?: (item: T) => string;\n}) {\n if (normalizedOptions) {\n return <>{normalizedOptions.map(renderComboboxOption)}</>;\n }\n\n if (items) {\n const renderItemWithTextValue = (item: T) => {\n let renderedItem: React.ReactElement;\n if (typeof children === 'function') {\n renderedItem = children(item);\n } else {\n renderedItem = (\n <ComboboxOptionItem option={item as unknown as NormalizedOption} />\n );\n }\n\n if (!getItemTextValue) {\n return renderedItem;\n }\n\n return cloneElement(renderedItem, {\n textValue: getItemTextValue(item),\n });\n };\n\n return (\n <Collection items={items} dependencies={dependencies}>\n {renderItemWithTextValue}\n </Collection>\n );\n }\n\n return children as React.ReactNode;\n}\n\nfunction ComboboxEmptyState({\n isLoading,\n className,\n rowClassName,\n}: {\n isLoading: boolean;\n className: string;\n rowClassName: string;\n}) {\n if (!isLoading) {\n return <NoResults />;\n }\n\n return (\n <LoadingRows count={3} className={className} rowClassName={rowClassName} />\n );\n}\n\nfunction ComboboxLoadMoreIndicator({\n loading,\n isCollectionLoading,\n isIndicatorVisible,\n className,\n rowClassName,\n}: {\n loading?: LoadingConfig;\n isCollectionLoading: boolean;\n isIndicatorVisible: boolean;\n className: string;\n rowClassName: string;\n}) {\n if (!loading?.onLoadMore || isCollectionLoading) {\n return null;\n }\n\n const onLoadMore =\n loading.state === 'loadingMore' ? undefined : loading.onLoadMore;\n\n return (\n <ListBoxLoadMoreItem\n isLoading={isIndicatorVisible}\n onLoadMore={onLoadMore}\n scrollOffset={0}\n >\n <LoadingRows\n count={1}\n className={className}\n rowClassName={rowClassName}\n />\n </ListBoxLoadMoreItem>\n );\n}\n\nexport function ComboboxListBox<T extends CollectionItem>(\n props: ComboboxListBoxOwnProps<T>,\n) {\n const { ownProps } = useDefinition(ComboboxListBoxDefinition, props);\n const {\n classes,\n options,\n items,\n children,\n dependencies,\n search,\n loading,\n isStale,\n getItemTextValue,\n } = ownProps;\n const normalizedOptions = options && normalizeOptions(options);\n const state = useContext(ComboBoxStateContext);\n const { contains } = useFilter({ sensitivity: 'base' });\n const inputValue = state?.inputValue ?? '';\n const filter = getCollectionFilter({\n search,\n hasItems: items !== undefined,\n inputValue,\n contains,\n });\n const isCollectionLoading =\n loading?.state === 'loading' ||\n loading?.state === 'filtering' ||\n loading?.state === 'sorting';\n const isBusy = isCollectionLoading || loading?.state === 'loadingMore';\n const showStale = useDelayedVisibility(\n isStale ?? false,\n loadingIndicatorDelayMs,\n );\n const showLoadMoreIndicator = useDelayedVisibility(\n loading?.state === 'loadingMore',\n loadingIndicatorDelayMs,\n );\n\n const listBox = (\n <ListBox\n className={classes.root}\n data-stale={showStale || undefined}\n renderEmptyState={() => (\n <ComboboxEmptyState\n isLoading={isCollectionLoading}\n className={classes.loading}\n rowClassName={classes.loadingRow}\n />\n )}\n >\n <ComboboxCollection\n normalizedOptions={normalizedOptions}\n items={items}\n dependencies={dependencies}\n getItemTextValue={getItemTextValue}\n >\n {children}\n </ComboboxCollection>\n <ComboboxLoadMoreIndicator\n loading={loading}\n isCollectionLoading={isCollectionLoading}\n isIndicatorVisible={showLoadMoreIndicator}\n className={classes.loading}\n rowClassName={classes.loadingRow}\n />\n </ListBox>\n );\n\n const busyListBox = <div aria-busy={isBusy || undefined}>{listBox}</div>;\n\n if (!filter) {\n return busyListBox;\n }\n\n return (\n <SelectableCollectionContext.Provider value={{ filter }}>\n {busyListBox}\n </SelectableCollectionContext.Provider>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -18,6 +18,11 @@ const ComboboxDefinition = defineComponent()({
|
|
|
18
18
|
icon: {},
|
|
19
19
|
size: { dataAttribute: true, default: "small" },
|
|
20
20
|
options: {},
|
|
21
|
+
items: {},
|
|
22
|
+
children: {},
|
|
23
|
+
dependencies: {},
|
|
24
|
+
search: {},
|
|
25
|
+
loading: {},
|
|
21
26
|
placeholder: {},
|
|
22
27
|
label: {},
|
|
23
28
|
secondaryLabel: {},
|
|
@@ -46,10 +51,19 @@ const ComboboxListBoxDefinition = defineComponent()({
|
|
|
46
51
|
styles,
|
|
47
52
|
classNames: {
|
|
48
53
|
root: "bui-ComboboxList",
|
|
49
|
-
noResults: "bui-ComboboxNoResults"
|
|
54
|
+
noResults: "bui-ComboboxNoResults",
|
|
55
|
+
loading: "bui-ComboboxLoading",
|
|
56
|
+
loadingRow: "bui-ComboboxLoadingRow"
|
|
50
57
|
},
|
|
51
58
|
propDefs: {
|
|
52
|
-
options: {}
|
|
59
|
+
options: {},
|
|
60
|
+
items: {},
|
|
61
|
+
children: {},
|
|
62
|
+
dependencies: {},
|
|
63
|
+
search: {},
|
|
64
|
+
loading: {},
|
|
65
|
+
isStale: {},
|
|
66
|
+
getItemTextValue: {}
|
|
53
67
|
}
|
|
54
68
|
});
|
|
55
69
|
const ComboboxListBoxItemDefinition = defineComponent()({
|
|
@@ -61,6 +75,51 @@ const ComboboxListBoxItemDefinition = defineComponent()({
|
|
|
61
75
|
},
|
|
62
76
|
propDefs: {}
|
|
63
77
|
});
|
|
78
|
+
const ComboboxItemDefinition = defineComponent()({
|
|
79
|
+
styles,
|
|
80
|
+
classNames: {
|
|
81
|
+
root: "bui-ComboboxItem",
|
|
82
|
+
indicator: "bui-ComboboxItemIndicator",
|
|
83
|
+
content: "bui-ComboboxItemContent"
|
|
84
|
+
},
|
|
85
|
+
propDefs: {
|
|
86
|
+
children: {},
|
|
87
|
+
textValue: {},
|
|
88
|
+
showSelectionIndicator: {},
|
|
89
|
+
className: {}
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
const ComboboxItemTextDefinition = defineComponent()({
|
|
93
|
+
styles,
|
|
94
|
+
classNames: {
|
|
95
|
+
root: "bui-ComboboxItemText",
|
|
96
|
+
leadingIcon: "bui-ComboboxItemTextLeadingIcon",
|
|
97
|
+
text: "bui-ComboboxItemTextContent",
|
|
98
|
+
title: "bui-ComboboxItemTitle",
|
|
99
|
+
description: "bui-ComboboxItemDescription"
|
|
100
|
+
},
|
|
101
|
+
propDefs: {
|
|
102
|
+
title: {},
|
|
103
|
+
description: {},
|
|
104
|
+
leadingIcon: {},
|
|
105
|
+
textValue: {},
|
|
106
|
+
className: {}
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
const ComboboxItemProfileDefinition = defineComponent()({
|
|
110
|
+
styles,
|
|
111
|
+
classNames: {
|
|
112
|
+
root: "bui-ComboboxItemProfile",
|
|
113
|
+
avatar: "bui-ComboboxItemAvatar",
|
|
114
|
+
name: "bui-ComboboxItemTitle"
|
|
115
|
+
},
|
|
116
|
+
propDefs: {
|
|
117
|
+
name: {},
|
|
118
|
+
src: {},
|
|
119
|
+
textValue: {},
|
|
120
|
+
className: {}
|
|
121
|
+
}
|
|
122
|
+
});
|
|
64
123
|
const ComboboxSectionDefinition = defineComponent()({
|
|
65
124
|
styles,
|
|
66
125
|
classNames: {
|
|
@@ -70,5 +129,5 @@ const ComboboxSectionDefinition = defineComponent()({
|
|
|
70
129
|
propDefs: {}
|
|
71
130
|
});
|
|
72
131
|
|
|
73
|
-
export { ComboboxDefinition, ComboboxInputDefinition, ComboboxListBoxDefinition, ComboboxListBoxItemDefinition, ComboboxSectionDefinition };
|
|
132
|
+
export { ComboboxDefinition, ComboboxInputDefinition, ComboboxItemDefinition, ComboboxItemProfileDefinition, ComboboxItemTextDefinition, ComboboxListBoxDefinition, ComboboxListBoxItemDefinition, ComboboxSectionDefinition };
|
|
74
133
|
//# sourceMappingURL=definition.esm.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"definition.esm.js","sources":["../../../src/components/Combobox/definition.ts"],"sourcesContent":["/*\n * Copyright 2026 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { defineComponent } from '../../hooks/useDefinition';\nimport type {\n ComboboxOwnProps,\n ComboboxInputOwnProps,\n ComboboxListBoxOwnProps,\n ComboboxListBoxItemOwnProps,\n ComboboxSectionOwnProps,\n} from './types';\nimport styles from './Combobox.module.css';\n\n
|
|
1
|
+
{"version":3,"file":"definition.esm.js","sources":["../../../src/components/Combobox/definition.ts"],"sourcesContent":["/*\n * Copyright 2026 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { defineComponent } from '../../hooks/useDefinition';\nimport type {\n ComboboxOwnProps,\n ComboboxInputOwnProps,\n ComboboxItemOwnProps,\n ComboboxItemProfileOwnProps,\n ComboboxItemTextOwnProps,\n ComboboxListBoxOwnProps,\n ComboboxListBoxItemOwnProps,\n ComboboxSectionOwnProps,\n} from './types';\nimport styles from './Combobox.module.css';\n\n/** @public */\nexport const ComboboxDefinition = defineComponent<ComboboxOwnProps>()({\n styles,\n classNames: {\n root: 'bui-Combobox',\n popover: 'bui-ComboboxPopover',\n },\n propDefs: {\n icon: {},\n size: { dataAttribute: true, default: 'small' },\n options: {},\n items: {},\n children: {},\n dependencies: {},\n search: {},\n loading: {},\n placeholder: {},\n label: {},\n secondaryLabel: {},\n description: {},\n isRequired: {},\n className: {},\n },\n});\n\n/** @public */\nexport const ComboboxInputDefinition = defineComponent<ComboboxInputOwnProps>()(\n {\n styles,\n classNames: {\n root: 'bui-ComboboxInput',\n icon: 'bui-ComboboxInputIcon',\n input: 'bui-ComboboxInputField',\n chevron: 'bui-ComboboxInputChevron',\n },\n bg: 'consumer',\n propDefs: {\n icon: {},\n placeholder: {},\n },\n },\n);\n\n/** @public */\nexport const ComboboxListBoxDefinition = defineComponent<\n ComboboxListBoxOwnProps<any>\n>()({\n styles,\n classNames: {\n root: 'bui-ComboboxList',\n noResults: 'bui-ComboboxNoResults',\n loading: 'bui-ComboboxLoading',\n loadingRow: 'bui-ComboboxLoadingRow',\n },\n propDefs: {\n options: {},\n items: {},\n children: {},\n dependencies: {},\n search: {},\n loading: {},\n isStale: {},\n getItemTextValue: {},\n },\n});\n\n/** @public */\nexport const ComboboxListBoxItemDefinition =\n defineComponent<ComboboxListBoxItemOwnProps>()({\n styles,\n classNames: {\n root: 'bui-ComboboxItem',\n indicator: 'bui-ComboboxItemIndicator',\n label: 'bui-ComboboxItemLabel',\n },\n propDefs: {},\n });\n\n/** @public */\nexport const ComboboxItemDefinition = defineComponent<ComboboxItemOwnProps>()({\n styles,\n classNames: {\n root: 'bui-ComboboxItem',\n indicator: 'bui-ComboboxItemIndicator',\n content: 'bui-ComboboxItemContent',\n },\n propDefs: {\n children: {},\n textValue: {},\n showSelectionIndicator: {},\n className: {},\n },\n});\n\n/** @public */\nexport const ComboboxItemTextDefinition =\n defineComponent<ComboboxItemTextOwnProps>()({\n styles,\n classNames: {\n root: 'bui-ComboboxItemText',\n leadingIcon: 'bui-ComboboxItemTextLeadingIcon',\n text: 'bui-ComboboxItemTextContent',\n title: 'bui-ComboboxItemTitle',\n description: 'bui-ComboboxItemDescription',\n },\n propDefs: {\n title: {},\n description: {},\n leadingIcon: {},\n textValue: {},\n className: {},\n },\n });\n\n/** @public */\nexport const ComboboxItemProfileDefinition =\n defineComponent<ComboboxItemProfileOwnProps>()({\n styles,\n classNames: {\n root: 'bui-ComboboxItemProfile',\n avatar: 'bui-ComboboxItemAvatar',\n name: 'bui-ComboboxItemTitle',\n },\n propDefs: {\n name: {},\n src: {},\n textValue: {},\n className: {},\n },\n });\n\n/** @public */\nexport const ComboboxSectionDefinition =\n defineComponent<ComboboxSectionOwnProps>()({\n styles,\n classNames: {\n root: 'bui-ComboboxSection',\n header: 'bui-ComboboxSectionHeader',\n },\n propDefs: {},\n });\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { useState, useRef, useCallback, useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
function getItemTextValue(item) {
|
|
4
|
+
return "textValue" in item ? item.textValue : item.label;
|
|
5
|
+
}
|
|
6
|
+
function resolveNextSelectedItem({
|
|
7
|
+
key,
|
|
8
|
+
currentItem,
|
|
9
|
+
sourceItems,
|
|
10
|
+
isControlled
|
|
11
|
+
}) {
|
|
12
|
+
if (key === null) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
if (isControlled && currentItem?.id === key) {
|
|
16
|
+
return currentItem;
|
|
17
|
+
}
|
|
18
|
+
return sourceItems.find((item) => item.id === key) ?? currentItem;
|
|
19
|
+
}
|
|
20
|
+
function useAsyncComboboxState(props) {
|
|
21
|
+
const isEnabled = props !== void 0;
|
|
22
|
+
const { source, value, defaultValue, onChange, allowsCustomValue } = props ?? {};
|
|
23
|
+
const isControlled = value !== void 0;
|
|
24
|
+
const [uncontrolledValue, setUncontrolledValue] = useState(
|
|
25
|
+
defaultValue ?? null
|
|
26
|
+
);
|
|
27
|
+
const selectedItem = isControlled ? value ?? null : uncontrolledValue;
|
|
28
|
+
const selectedItemRef = useRef(selectedItem);
|
|
29
|
+
const sourceItemsRef = useRef([]);
|
|
30
|
+
const inputValueRef = useRef("");
|
|
31
|
+
const sourceFilterText = source?.filterText ?? "";
|
|
32
|
+
const setSourceFilterText = source?.setFilterText;
|
|
33
|
+
const sourceFilterTextRef = useRef(sourceFilterText);
|
|
34
|
+
const preserveInputOnClearRef = useRef(false);
|
|
35
|
+
const [inputValue, setInputValue] = useState(
|
|
36
|
+
() => selectedItem ? getAsyncComboboxItemTextValue(selectedItem) : sourceFilterText
|
|
37
|
+
);
|
|
38
|
+
selectedItemRef.current = selectedItem;
|
|
39
|
+
sourceItemsRef.current = source ? [...source.items] : [];
|
|
40
|
+
inputValueRef.current = inputValue;
|
|
41
|
+
sourceFilterTextRef.current = sourceFilterText;
|
|
42
|
+
const updateInputValue = useCallback(
|
|
43
|
+
(nextInputValue) => {
|
|
44
|
+
setInputValue(nextInputValue);
|
|
45
|
+
if (isEnabled && setSourceFilterText && sourceFilterTextRef.current !== nextInputValue) {
|
|
46
|
+
sourceFilterTextRef.current = nextInputValue;
|
|
47
|
+
setSourceFilterText(nextInputValue);
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
[isEnabled, setSourceFilterText]
|
|
51
|
+
);
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
if (isEnabled) {
|
|
54
|
+
setInputValue(sourceFilterText);
|
|
55
|
+
}
|
|
56
|
+
}, [isEnabled, sourceFilterText]);
|
|
57
|
+
const selectedId = selectedItem?.id ?? null;
|
|
58
|
+
const selectedTextValue = selectedItem ? getAsyncComboboxItemTextValue(selectedItem) : void 0;
|
|
59
|
+
const previousSelectedId = useRef(null);
|
|
60
|
+
const previousSelectedTextValue = useRef();
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
if (!isEnabled) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const selectionChanged = previousSelectedId.current !== selectedId || previousSelectedTextValue.current !== selectedTextValue;
|
|
66
|
+
previousSelectedId.current = selectedId;
|
|
67
|
+
previousSelectedTextValue.current = selectedTextValue;
|
|
68
|
+
if (selectionChanged) {
|
|
69
|
+
if (selectedTextValue !== void 0) {
|
|
70
|
+
updateInputValue(selectedTextValue);
|
|
71
|
+
} else if (preserveInputOnClearRef.current) {
|
|
72
|
+
preserveInputOnClearRef.current = false;
|
|
73
|
+
} else {
|
|
74
|
+
updateInputValue("");
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}, [isEnabled, selectedId, selectedTextValue, updateInputValue]);
|
|
78
|
+
const freshSelectedItem = selectedItem ? sourceItemsRef.current.find((item) => item.id === selectedItem.id) : void 0;
|
|
79
|
+
const isEditing = selectedTextValue !== void 0 && inputValueRef.current !== selectedTextValue;
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
if (isEnabled && !isControlled && !isEditing && freshSelectedItem && freshSelectedItem !== selectedItemRef.current) {
|
|
82
|
+
setUncontrolledValue(freshSelectedItem);
|
|
83
|
+
}
|
|
84
|
+
}, [freshSelectedItem, isControlled, isEditing, isEnabled]);
|
|
85
|
+
const handleChange = useCallback(
|
|
86
|
+
(key) => {
|
|
87
|
+
const currentItem = selectedItemRef.current;
|
|
88
|
+
const currentTextValue = currentItem ? getAsyncComboboxItemTextValue(currentItem) : void 0;
|
|
89
|
+
if (key === null && allowsCustomValue && currentTextValue === inputValueRef.current) {
|
|
90
|
+
updateInputValue(currentTextValue);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (key === null && allowsCustomValue && currentItem) {
|
|
94
|
+
preserveInputOnClearRef.current = true;
|
|
95
|
+
}
|
|
96
|
+
if (key === null && !currentItem && !allowsCustomValue) {
|
|
97
|
+
updateInputValue("");
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const nextItem = resolveNextSelectedItem({
|
|
101
|
+
key,
|
|
102
|
+
currentItem,
|
|
103
|
+
sourceItems: sourceItemsRef.current,
|
|
104
|
+
isControlled
|
|
105
|
+
});
|
|
106
|
+
if (nextItem) {
|
|
107
|
+
updateInputValue(getAsyncComboboxItemTextValue(nextItem));
|
|
108
|
+
}
|
|
109
|
+
if (!isControlled) {
|
|
110
|
+
setUncontrolledValue(nextItem);
|
|
111
|
+
}
|
|
112
|
+
if (nextItem?.id !== currentItem?.id) {
|
|
113
|
+
onChange?.(nextItem);
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
[allowsCustomValue, isControlled, onChange, updateInputValue]
|
|
117
|
+
);
|
|
118
|
+
if (!isEnabled) {
|
|
119
|
+
return void 0;
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
value: selectedItem?.id ?? null,
|
|
123
|
+
inputValue,
|
|
124
|
+
onChange: handleChange,
|
|
125
|
+
onInputChange: updateInputValue
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
function getAsyncComboboxItemTextValue(item) {
|
|
129
|
+
return getItemTextValue(item);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export { getAsyncComboboxItemTextValue, useAsyncComboboxState };
|
|
133
|
+
//# sourceMappingURL=useAsyncComboboxState.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useAsyncComboboxState.esm.js","sources":["../../../src/components/Combobox/useAsyncComboboxState.ts"],"sourcesContent":["/*\n * Copyright 2026 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport type { Key } from 'react-aria-components';\nimport type {\n AsyncListSource,\n CollectionItem,\n} from '../../types/selectableCollection';\n\ntype AsyncComboboxItem = CollectionItem &\n ({ textValue: string } | { label: string });\n\nfunction getItemTextValue(item: AsyncComboboxItem) {\n return 'textValue' in item ? item.textValue : item.label;\n}\n\nfunction resolveNextSelectedItem<T extends CollectionItem>({\n key,\n currentItem,\n sourceItems,\n isControlled,\n}: {\n key: Key | null;\n currentItem: T | null;\n sourceItems: T[];\n isControlled: boolean;\n}) {\n if (key === null) {\n return null;\n }\n\n if (isControlled && currentItem?.id === key) {\n return currentItem;\n }\n\n return sourceItems.find(item => item.id === key) ?? currentItem;\n}\n\ntype UseAsyncComboboxStateProps<T extends CollectionItem> = {\n source: AsyncListSource<T>;\n value?: T | null;\n defaultValue?: T | null;\n onChange?: (value: T | null) => void;\n allowsCustomValue?: boolean;\n};\n\n/** @internal */\nexport function useAsyncComboboxState<T extends CollectionItem>(\n props?: UseAsyncComboboxStateProps<T>,\n) {\n const isEnabled = props !== undefined;\n const { source, value, defaultValue, onChange, allowsCustomValue } =\n props ?? {};\n const isControlled = value !== undefined;\n const [uncontrolledValue, setUncontrolledValue] = useState<T | null>(\n defaultValue ?? null,\n );\n const selectedItem = isControlled ? value ?? null : uncontrolledValue;\n const selectedItemRef = useRef(selectedItem);\n const sourceItemsRef = useRef<T[]>([]);\n const inputValueRef = useRef('');\n const sourceFilterText = source?.filterText ?? '';\n const setSourceFilterText = source?.setFilterText;\n const sourceFilterTextRef = useRef(sourceFilterText);\n const preserveInputOnClearRef = useRef(false);\n const [inputValue, setInputValue] = useState(() =>\n selectedItem\n ? getAsyncComboboxItemTextValue(selectedItem)\n : sourceFilterText,\n );\n\n selectedItemRef.current = selectedItem;\n sourceItemsRef.current = source ? [...source.items] : [];\n inputValueRef.current = inputValue;\n sourceFilterTextRef.current = sourceFilterText;\n\n const updateInputValue = useCallback(\n (nextInputValue: string) => {\n setInputValue(nextInputValue);\n if (\n isEnabled &&\n setSourceFilterText &&\n sourceFilterTextRef.current !== nextInputValue\n ) {\n sourceFilterTextRef.current = nextInputValue;\n setSourceFilterText(nextInputValue);\n }\n },\n [isEnabled, setSourceFilterText],\n );\n\n useEffect(() => {\n if (isEnabled) {\n setInputValue(sourceFilterText);\n }\n }, [isEnabled, sourceFilterText]);\n\n const selectedId = selectedItem?.id ?? null;\n const selectedTextValue = selectedItem\n ? getAsyncComboboxItemTextValue(selectedItem)\n : undefined;\n const previousSelectedId = useRef<Key | null>(null);\n const previousSelectedTextValue = useRef<string>();\n\n useEffect(() => {\n if (!isEnabled) {\n return;\n }\n\n const selectionChanged =\n previousSelectedId.current !== selectedId ||\n previousSelectedTextValue.current !== selectedTextValue;\n\n previousSelectedId.current = selectedId;\n previousSelectedTextValue.current = selectedTextValue;\n\n if (selectionChanged) {\n if (selectedTextValue !== undefined) {\n updateInputValue(selectedTextValue);\n } else if (preserveInputOnClearRef.current) {\n preserveInputOnClearRef.current = false;\n } else {\n updateInputValue('');\n }\n }\n }, [isEnabled, selectedId, selectedTextValue, updateInputValue]);\n\n const freshSelectedItem = selectedItem\n ? sourceItemsRef.current.find(item => item.id === selectedItem.id)\n : undefined;\n const isEditing =\n selectedTextValue !== undefined &&\n inputValueRef.current !== selectedTextValue;\n\n useEffect(() => {\n if (\n isEnabled &&\n !isControlled &&\n !isEditing &&\n freshSelectedItem &&\n freshSelectedItem !== selectedItemRef.current\n ) {\n setUncontrolledValue(freshSelectedItem);\n }\n }, [freshSelectedItem, isControlled, isEditing, isEnabled]);\n\n const handleChange = useCallback(\n (key: Key | null) => {\n const currentItem = selectedItemRef.current;\n const currentTextValue = currentItem\n ? getAsyncComboboxItemTextValue(currentItem)\n : undefined;\n\n if (\n key === null &&\n allowsCustomValue &&\n currentTextValue === inputValueRef.current\n ) {\n updateInputValue(currentTextValue);\n return;\n }\n\n if (key === null && allowsCustomValue && currentItem) {\n preserveInputOnClearRef.current = true;\n }\n\n if (key === null && !currentItem && !allowsCustomValue) {\n updateInputValue('');\n return;\n }\n\n const nextItem = resolveNextSelectedItem({\n key,\n currentItem,\n sourceItems: sourceItemsRef.current,\n isControlled,\n });\n\n if (nextItem) {\n updateInputValue(getAsyncComboboxItemTextValue(nextItem));\n }\n\n if (!isControlled) {\n setUncontrolledValue(nextItem);\n }\n\n if (nextItem?.id !== currentItem?.id) {\n onChange?.(nextItem);\n }\n },\n [allowsCustomValue, isControlled, onChange, updateInputValue],\n );\n\n if (!isEnabled) {\n return undefined;\n }\n\n return {\n value: selectedItem?.id ?? null,\n inputValue,\n onChange: handleChange,\n onInputChange: updateInputValue,\n };\n}\n\n/** @internal */\nexport function getAsyncComboboxItemTextValue(item: CollectionItem) {\n return getItemTextValue(item as AsyncComboboxItem);\n}\n"],"names":[],"mappings":"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"}
|