@vuu-ui/vuu-ui-controls 2.2.1 → 3.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/index.js +2 -0
- package/cjs/index.js.map +1 -1
- package/cjs/item-picker/ItemPicker.css.js +6 -0
- package/cjs/item-picker/ItemPicker.css.js.map +1 -0
- package/cjs/item-picker/ItemPicker.js +261 -0
- package/cjs/item-picker/ItemPicker.js.map +1 -0
- package/cjs/item-picker/useItemPicker.js +118 -0
- package/cjs/item-picker/useItemPicker.js.map +1 -0
- package/cjs/prompt/Prompt.js +2 -0
- package/cjs/prompt/Prompt.js.map +1 -1
- package/esm/index.js +1 -0
- package/esm/index.js.map +1 -1
- package/esm/item-picker/ItemPicker.css.js +4 -0
- package/esm/item-picker/ItemPicker.css.js.map +1 -0
- package/esm/item-picker/ItemPicker.js +258 -0
- package/esm/item-picker/ItemPicker.js.map +1 -0
- package/esm/item-picker/useItemPicker.js +115 -0
- package/esm/item-picker/useItemPicker.js.map +1 -0
- package/esm/prompt/Prompt.js +2 -0
- package/esm/prompt/Prompt.js.map +1 -1
- package/package.json +10 -10
- package/types/index.d.ts +2 -0
- package/types/item-picker/ItemPicker.d.ts +14 -0
- package/types/item-picker/useItemPicker.d.ts +35 -0
- package/types/prompt/Prompt.d.ts +2 -2
package/cjs/index.js
CHANGED
|
@@ -27,6 +27,7 @@ var Icon = require('./icon-button/Icon.js');
|
|
|
27
27
|
var IconButton = require('./icon-button/IconButton.js');
|
|
28
28
|
var ToggleIconButton = require('./icon-button/ToggleIconButton.js');
|
|
29
29
|
var TablePicker = require('./instrument-picker/TablePicker.js');
|
|
30
|
+
var ItemPicker = require('./item-picker/ItemPicker.js');
|
|
30
31
|
var MeasuredContainer = require('./measured-container/MeasuredContainer.js');
|
|
31
32
|
var useMeasuredContainer = require('./measured-container/useMeasuredContainer.js');
|
|
32
33
|
var ModalProvider = require('./modal-provider/ModalProvider.js');
|
|
@@ -120,6 +121,7 @@ exports.Icon = Icon.Icon;
|
|
|
120
121
|
exports.IconButton = IconButton.IconButton;
|
|
121
122
|
exports.ToggleIconButton = ToggleIconButton.ToggleIconButton;
|
|
122
123
|
exports.TablePicker = TablePicker.TablePicker;
|
|
124
|
+
exports.ItemPicker = ItemPicker.ItemPicker;
|
|
123
125
|
exports.MeasuredContainer = MeasuredContainer.MeasuredContainer;
|
|
124
126
|
exports.reduceSizeHeight = useMeasuredContainer.reduceSizeHeight;
|
|
125
127
|
exports.useMeasuredContainer = useMeasuredContainer.useMeasuredContainer;
|
package/cjs/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var itemPickerCss = ".vuuItemPicker {\n --label-margin-left: 0px;\n\n --itempicker-background: var(\n --vuuItemPicker-background,\n var(--salt-container-primary-background)\n );\n background: var(--itempicker-background);\n container-type: inline-size;\n display: flex;\n flex-direction: column;\n gap: var(--salt-spacing-300);\n height: var(--vuuItemPicker-height, 100%);\n overflow: hidden;\n padding: var(--salt-spacing-300) 0;\n\n &.vuuItemPicker-withCalculated {\n --label-margin-left: 8px;\n }\n\n .saltListBox {\n background-color: inherit;\n }\n\n .saltOption {\n align-items: center;\n background-color: inherit;\n border-bottom: var(--vuuItemListItem-border, solid) 1px\n var(--salt-separable-tertiary-borderColor);\n gap: var(--salt-spacing-200);\n height: calc(var(--salt-size-base) + var(--salt-spacing-200));\n .vuuIcon {\n --vuu-icon-color: var(--salt-content-secondary-foreground);\n }\n\n .vuuCalculatedItemIcon {\n --vuu-icon-color: var(--salt-content-secondary-foreground);\n left: 32px;\n position: absolute;\n top: 8px;\n }\n\n .vuuHighlight {\n color: var(--vuu-color-blue-40);\n text-decoration: underline;\n }\n\n .vuuItemPicker-text {\n margin-left: var(--label-margin-left);\n }\n }\n\n .vuuItemPickerListItem-action {\n margin-left: auto;\n visibility: hidden;\n }\n\n .saltOption:hover {\n .vuuItemPickerListItem-action {\n visibility: visible;\n }\n }\n\n .vuuItemPicker-search {\n --saltInput-paddingLeft: var(--salt-spacing-300);\n padding: var(--salt-spacing-200) var(--salt-spacing-300) 0\n var(--salt-spacing-300);\n }\n\n .vuuItemPicker-scrollContainer {\n flex: 1 1 0px;\n overflow: auto;\n\n .vuuItemPicker-sectionHeader {\n align-items: center;\n background: var(--itempicker-background);\n display: flex;\n height: 32px;\n padding: 0 var(--salt-spacing-300);\n position: sticky;\n top: 0;\n z-index: 1;\n }\n\n .vuuItemPicker-availableHeader {\n top: 32px;\n }\n\n .vuuItemPicker-availableList {\n .saltOption {\n padding-left: var(--salt-spacing-300);\n }\n }\n }\n}\n";
|
|
4
|
+
|
|
5
|
+
module.exports = itemPickerCss;
|
|
6
|
+
//# sourceMappingURL=ItemPicker.css.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ItemPicker.css.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;"}
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
var core = require('@salt-ds/core');
|
|
5
|
+
var styles = require('@salt-ds/styles');
|
|
6
|
+
var window = require('@salt-ds/window');
|
|
7
|
+
var vuuTable = require('@vuu-ui/vuu-table');
|
|
8
|
+
var vuuUtils = require('@vuu-ui/vuu-utils');
|
|
9
|
+
var cx = require('clsx');
|
|
10
|
+
var React = require('react');
|
|
11
|
+
var Icon = require('../icon-button/Icon.js');
|
|
12
|
+
var IconButton = require('../icon-button/IconButton.js');
|
|
13
|
+
var useItemPicker = require('./useItemPicker.js');
|
|
14
|
+
var ItemPicker$1 = require('./ItemPicker.css.js');
|
|
15
|
+
|
|
16
|
+
const classBase = "vuuItemPicker";
|
|
17
|
+
const classBaseListItem = "vuuItemPickerListItem";
|
|
18
|
+
const searchIcon = /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { name: "search" });
|
|
19
|
+
const NO_SELECTION = [];
|
|
20
|
+
const useSorting = (id, index) => {
|
|
21
|
+
const { handleRef: sortableHandleRef, ref: sortableRef } = vuuUtils.useSortable({
|
|
22
|
+
id,
|
|
23
|
+
index
|
|
24
|
+
});
|
|
25
|
+
const [handleRef, ref] = React.useMemo(() => {
|
|
26
|
+
return [sortableHandleRef, sortableRef];
|
|
27
|
+
}, [sortableHandleRef, sortableRef]);
|
|
28
|
+
return {
|
|
29
|
+
handleRef,
|
|
30
|
+
ref
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
const SelectedListItem = ({
|
|
34
|
+
className: classNameProp,
|
|
35
|
+
index,
|
|
36
|
+
item,
|
|
37
|
+
onRemove,
|
|
38
|
+
searchPattern = "",
|
|
39
|
+
...optionProps
|
|
40
|
+
}) => {
|
|
41
|
+
const { handleRef, ref } = useSorting(item.name, index);
|
|
42
|
+
const value = useItemPicker.getItemLabel(item);
|
|
43
|
+
const valueWithHighlighting = vuuTable.applyHighlighting(value, searchPattern);
|
|
44
|
+
const handleRemoveButtonClick = React.useCallback(
|
|
45
|
+
(e) => {
|
|
46
|
+
e.stopPropagation();
|
|
47
|
+
onRemove?.(e);
|
|
48
|
+
},
|
|
49
|
+
[onRemove]
|
|
50
|
+
);
|
|
51
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
52
|
+
core.Option,
|
|
53
|
+
{
|
|
54
|
+
...optionProps,
|
|
55
|
+
className: cx(classNameProp, classBaseListItem),
|
|
56
|
+
"data-name": item.name,
|
|
57
|
+
ref,
|
|
58
|
+
children: [
|
|
59
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
60
|
+
IconButton.IconButton,
|
|
61
|
+
{
|
|
62
|
+
"data-embedded": true,
|
|
63
|
+
appearance: "transparent",
|
|
64
|
+
icon: "draggable",
|
|
65
|
+
ref: handleRef,
|
|
66
|
+
size: 16
|
|
67
|
+
}
|
|
68
|
+
),
|
|
69
|
+
item.icon ? /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { name: item.icon }, "icon") : null,
|
|
70
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: `${classBase}-text`, children: valueWithHighlighting }),
|
|
71
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
72
|
+
IconButton.IconButton,
|
|
73
|
+
{
|
|
74
|
+
className: `${classBaseListItem}-action`,
|
|
75
|
+
"data-embedded": true,
|
|
76
|
+
appearance: "transparent",
|
|
77
|
+
icon: "cross",
|
|
78
|
+
onClick: handleRemoveButtonClick,
|
|
79
|
+
size: 16
|
|
80
|
+
}
|
|
81
|
+
)
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
);
|
|
85
|
+
};
|
|
86
|
+
const AvailableListItem = ({
|
|
87
|
+
className: classNameProp,
|
|
88
|
+
index,
|
|
89
|
+
item,
|
|
90
|
+
onAdd,
|
|
91
|
+
searchPattern = "",
|
|
92
|
+
disabled,
|
|
93
|
+
...optionProps
|
|
94
|
+
}) => {
|
|
95
|
+
const value = useItemPicker.getItemLabel(item);
|
|
96
|
+
const valueWithHighlighting = vuuTable.applyHighlighting(value, searchPattern);
|
|
97
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
98
|
+
core.Option,
|
|
99
|
+
{
|
|
100
|
+
...optionProps,
|
|
101
|
+
className: cx(classNameProp, classBaseListItem),
|
|
102
|
+
"data-name": item.name,
|
|
103
|
+
disabled,
|
|
104
|
+
children: [
|
|
105
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: `${classBase}-text`, children: valueWithHighlighting }),
|
|
106
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
107
|
+
IconButton.IconButton,
|
|
108
|
+
{
|
|
109
|
+
className: `${classBaseListItem}-action`,
|
|
110
|
+
"data-embedded": true,
|
|
111
|
+
appearance: "transparent",
|
|
112
|
+
icon: "plus",
|
|
113
|
+
onClick: onAdd,
|
|
114
|
+
size: 16,
|
|
115
|
+
disabled
|
|
116
|
+
}
|
|
117
|
+
)
|
|
118
|
+
]
|
|
119
|
+
}
|
|
120
|
+
);
|
|
121
|
+
};
|
|
122
|
+
const ItemPicker = React.forwardRef(function ItemPicker2({
|
|
123
|
+
className,
|
|
124
|
+
itemTypeName,
|
|
125
|
+
allItems,
|
|
126
|
+
selectedItems,
|
|
127
|
+
maxSelections,
|
|
128
|
+
onSelectedItemsChange,
|
|
129
|
+
onSelectionChange,
|
|
130
|
+
selected = NO_SELECTION,
|
|
131
|
+
createCustomItemProps,
|
|
132
|
+
...htmlAttributes
|
|
133
|
+
}, forwardedRef) {
|
|
134
|
+
const targetWindow = window.useWindow();
|
|
135
|
+
styles.useComponentCssInjection({
|
|
136
|
+
testId: "vuu-item-picker",
|
|
137
|
+
css: ItemPicker$1,
|
|
138
|
+
window: targetWindow
|
|
139
|
+
});
|
|
140
|
+
const searchCallbackRef = React.useCallback((el) => {
|
|
141
|
+
setTimeout(() => {
|
|
142
|
+
el?.querySelector("input")?.focus();
|
|
143
|
+
}, 100);
|
|
144
|
+
}, []);
|
|
145
|
+
const {
|
|
146
|
+
selectedItemsCount,
|
|
147
|
+
availableItemsCount,
|
|
148
|
+
selectedItemsFiltered,
|
|
149
|
+
availableItemsFiltered,
|
|
150
|
+
searchText,
|
|
151
|
+
onChangeSearchInput,
|
|
152
|
+
onAddItemToSelectedList,
|
|
153
|
+
onRemoveItemFromSelectedList,
|
|
154
|
+
onReorderSelectedItems
|
|
155
|
+
} = useItemPicker.useItemPicker({
|
|
156
|
+
allItems,
|
|
157
|
+
selectedItems,
|
|
158
|
+
onSelectedItemsChange,
|
|
159
|
+
maxSelections
|
|
160
|
+
});
|
|
161
|
+
const listRef = React.useRef(null);
|
|
162
|
+
const getOptionName = (option) => {
|
|
163
|
+
if (option) {
|
|
164
|
+
const { name } = option.dataset;
|
|
165
|
+
if (name) {
|
|
166
|
+
return name;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
throw Error("[ItemPicker] list option has no data-name");
|
|
170
|
+
};
|
|
171
|
+
const handleDragEnd = React.useCallback(() => {
|
|
172
|
+
setTimeout(() => {
|
|
173
|
+
const listItems = listRef.current?.querySelectorAll(".saltOption");
|
|
174
|
+
if (listItems) {
|
|
175
|
+
const orderedItemNames = Array.from(listItems).map(getOptionName);
|
|
176
|
+
onReorderSelectedItems(orderedItemNames);
|
|
177
|
+
}
|
|
178
|
+
}, 300);
|
|
179
|
+
}, [availableItemsFiltered, selectedItemsFiltered]);
|
|
180
|
+
const searchPlaceholderText = `Find ${vuuUtils.singularForm(itemTypeName)}`;
|
|
181
|
+
const maxSelectionsSubHeading = maxSelections ? `(${maxSelections} max)` : "";
|
|
182
|
+
const selectedItemsHeading = `${selectedItemsCount} ${selectedItemsCount === 1 ? vuuUtils.singularForm(itemTypeName) : vuuUtils.pluralForm(itemTypeName)} in view ${maxSelectionsSubHeading}`;
|
|
183
|
+
const availableItemsHeading = `${availableItemsCount} available ${availableItemsCount === 1 ? vuuUtils.singularForm(itemTypeName) : vuuUtils.pluralForm(itemTypeName)}`;
|
|
184
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
185
|
+
"div",
|
|
186
|
+
{
|
|
187
|
+
...htmlAttributes,
|
|
188
|
+
className: cx(classBase, className),
|
|
189
|
+
ref: forwardedRef,
|
|
190
|
+
children: [
|
|
191
|
+
/* @__PURE__ */ jsxRuntime.jsx("form", { className: `${classBase}-search`, role: "search", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
192
|
+
core.Input,
|
|
193
|
+
{
|
|
194
|
+
startAdornment: searchIcon,
|
|
195
|
+
placeholder: searchPlaceholderText,
|
|
196
|
+
ref: searchCallbackRef,
|
|
197
|
+
value: searchText,
|
|
198
|
+
onChange: onChangeSearchInput
|
|
199
|
+
}
|
|
200
|
+
) }),
|
|
201
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: `${classBase}-scrollContainer vuuScrollable`, children: [
|
|
202
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: `${classBase}-sectionHeader`, children: selectedItemsHeading }),
|
|
203
|
+
/* @__PURE__ */ jsxRuntime.jsx(vuuUtils.DragDropProvider, { onDragEnd: handleDragEnd, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
204
|
+
core.ListBox,
|
|
205
|
+
{
|
|
206
|
+
className: `${classBase}-selectedList`,
|
|
207
|
+
onSelectionChange,
|
|
208
|
+
ref: listRef,
|
|
209
|
+
selected,
|
|
210
|
+
children: selectedItemsFiltered.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
211
|
+
SelectedListItem,
|
|
212
|
+
{
|
|
213
|
+
item,
|
|
214
|
+
index,
|
|
215
|
+
onRemove: onRemoveItemFromSelectedList,
|
|
216
|
+
searchPattern: searchText.toLowerCase(),
|
|
217
|
+
value: item
|
|
218
|
+
},
|
|
219
|
+
item.name
|
|
220
|
+
))
|
|
221
|
+
}
|
|
222
|
+
) }),
|
|
223
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
224
|
+
"div",
|
|
225
|
+
{
|
|
226
|
+
className: cx(
|
|
227
|
+
`${classBase}-sectionHeader`,
|
|
228
|
+
`${classBase}-availableHeader`
|
|
229
|
+
),
|
|
230
|
+
children: availableItemsHeading
|
|
231
|
+
}
|
|
232
|
+
),
|
|
233
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
234
|
+
core.ListBox,
|
|
235
|
+
{
|
|
236
|
+
className: `${classBase}-availableList`,
|
|
237
|
+
selected: NO_SELECTION,
|
|
238
|
+
children: availableItemsFiltered.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
239
|
+
AvailableListItem,
|
|
240
|
+
{
|
|
241
|
+
item,
|
|
242
|
+
index,
|
|
243
|
+
onAdd: onAddItemToSelectedList,
|
|
244
|
+
searchPattern: searchText.toLowerCase(),
|
|
245
|
+
value: item,
|
|
246
|
+
disabled: selectedItemsCount === maxSelections
|
|
247
|
+
},
|
|
248
|
+
item.name
|
|
249
|
+
))
|
|
250
|
+
}
|
|
251
|
+
)
|
|
252
|
+
] }),
|
|
253
|
+
createCustomItemProps ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: `${classBase}-item-buttons`, children: /* @__PURE__ */ jsxRuntime.jsx(core.Button, { onClick: createCustomItemProps.onClickCreateCustomItem, children: createCustomItemProps.buttonLabel }) }) : null
|
|
254
|
+
]
|
|
255
|
+
}
|
|
256
|
+
);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
exports.ItemPicker = ItemPicker;
|
|
260
|
+
exports.classBaseListItem = classBaseListItem;
|
|
261
|
+
//# sourceMappingURL=ItemPicker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ItemPicker.js","sources":["../../../../packages/vuu-ui-controls/src/item-picker/ItemPicker.tsx"],"sourcesContent":["import {\n Button,\n Input,\n ListBox,\n ListBoxProps,\n Option,\n OptionProps,\n} from \"@salt-ds/core\";\nimport { useComponentCssInjection } from \"@salt-ds/styles\";\nimport { useWindow } from \"@salt-ds/window\";\nimport { applyHighlighting } from \"@vuu-ui/vuu-table\";\nimport {\n DragDropProvider,\n ItemTypeName,\n pluralForm,\n singularForm,\n useSortable,\n} from \"@vuu-ui/vuu-utils\";\nimport cx from \"clsx\";\nimport {\n ForwardedRef,\n forwardRef,\n HTMLAttributes,\n MouseEventHandler,\n RefCallback,\n useCallback,\n useMemo,\n useRef,\n} from \"react\";\nimport { Icon, IconButton } from \"../icon-button\";\nimport {\n CreateCustomItemProps,\n getItemLabel,\n ItemDescriptor,\n ItemPickerHookProps,\n useItemPicker,\n} from \"./useItemPicker\";\n\nimport itemPickerCss from \"./ItemPicker.css\";\n\nconst classBase = \"vuuItemPicker\";\n\nexport const classBaseListItem = \"vuuItemPickerListItem\";\n\nexport interface ItemPickerProps\n extends ItemPickerHookProps,\n HTMLAttributes<HTMLDivElement>,\n Pick<ListBoxProps<ItemDescriptor>, \"selected\" | \"onSelectionChange\"> {\n itemTypeName: ItemTypeName;\n createCustomItemProps?: CreateCustomItemProps;\n}\n\nconst searchIcon = <Icon name=\"search\" />;\nconst NO_SELECTION: ItemDescriptor[] = [] as const;\n\nconst useSorting = (id: string, index: number) => {\n const { handleRef: sortableHandleRef, ref: sortableRef } = useSortable({\n id,\n index,\n });\n\n const [handleRef, ref] = useMemo(() => {\n return [sortableHandleRef, sortableRef];\n }, [sortableHandleRef, sortableRef]);\n\n return {\n handleRef,\n ref,\n };\n};\n\nconst SelectedListItem = ({\n className: classNameProp,\n index,\n item,\n onRemove,\n searchPattern = \"\",\n ...optionProps\n}: OptionProps & {\n index: number;\n item: ItemDescriptor;\n onRemove: MouseEventHandler<HTMLButtonElement>;\n searchPattern?: Lowercase<string>;\n}) => {\n const { handleRef, ref } = useSorting(item.name, index);\n const value = getItemLabel(item as ItemDescriptor);\n const valueWithHighlighting = applyHighlighting(value, searchPattern);\n\n const handleRemoveButtonClick = useCallback<\n MouseEventHandler<HTMLButtonElement>\n >(\n (e) => {\n e.stopPropagation();\n onRemove?.(e);\n },\n [onRemove],\n );\n\n return (\n <Option\n {...optionProps}\n className={cx(classNameProp, classBaseListItem)}\n data-name={item.name}\n ref={ref}\n >\n <IconButton\n data-embedded\n appearance=\"transparent\"\n icon=\"draggable\"\n ref={handleRef}\n size={16}\n />\n {item.icon ? <Icon name={item.icon} key=\"icon\" /> : null}\n <span className={`${classBase}-text`}>{valueWithHighlighting}</span>\n <IconButton\n className={`${classBaseListItem}-action`}\n data-embedded\n appearance=\"transparent\"\n icon=\"cross\"\n onClick={handleRemoveButtonClick}\n size={16}\n />\n </Option>\n );\n};\n\nconst AvailableListItem = ({\n className: classNameProp,\n index,\n item,\n onAdd,\n searchPattern = \"\",\n disabled,\n ...optionProps\n}: OptionProps & {\n index: number;\n item: ItemDescriptor;\n onAdd: MouseEventHandler<HTMLButtonElement>;\n searchPattern?: Lowercase<string>;\n}) => {\n const value = getItemLabel(item as ItemDescriptor);\n const valueWithHighlighting = applyHighlighting(value, searchPattern);\n\n return (\n <Option\n {...optionProps}\n className={cx(classNameProp, classBaseListItem)}\n data-name={item.name}\n disabled={disabled}\n >\n <span className={`${classBase}-text`}>{valueWithHighlighting}</span>\n <IconButton\n className={`${classBaseListItem}-action`}\n data-embedded\n appearance=\"transparent\"\n icon=\"plus\"\n onClick={onAdd}\n size={16}\n disabled={disabled}\n />\n </Option>\n );\n};\n\n/** Generic controlled component that displays items in a 'picker' that are available to search, select, reorder or deselect.\n * As input props, all available items, currently selected items, search text and callback functions for changes in selected items and search text\n * must be supplied by the client.\n */\nexport const ItemPicker = forwardRef(function ItemPicker(\n {\n className,\n itemTypeName,\n allItems,\n selectedItems,\n maxSelections,\n onSelectedItemsChange,\n onSelectionChange,\n selected = NO_SELECTION,\n createCustomItemProps,\n ...htmlAttributes\n }: ItemPickerProps,\n forwardedRef: ForwardedRef<HTMLDivElement>,\n) {\n const targetWindow = useWindow();\n useComponentCssInjection({\n testId: \"vuu-item-picker\",\n css: itemPickerCss,\n window: targetWindow,\n });\n\n const searchCallbackRef = useCallback<RefCallback<HTMLElement>>((el) => {\n setTimeout(() => {\n el?.querySelector(\"input\")?.focus();\n }, 100);\n }, []);\n\n const {\n selectedItemsCount,\n availableItemsCount,\n selectedItemsFiltered,\n availableItemsFiltered,\n searchText,\n onChangeSearchInput,\n onAddItemToSelectedList,\n onRemoveItemFromSelectedList,\n onReorderSelectedItems,\n } = useItemPicker({\n allItems,\n selectedItems,\n onSelectedItemsChange,\n maxSelections,\n });\n\n const listRef = useRef<HTMLDivElement>(null);\n\n const getOptionName = (option?: HTMLElement) => {\n if (option) {\n const { name } = option.dataset;\n if (name) {\n return name;\n }\n }\n throw Error(\"[ItemPicker] list option has no data-name\");\n };\n\n const handleDragEnd = useCallback(() => {\n setTimeout(() => {\n const listItems =\n listRef.current?.querySelectorAll<HTMLDivElement>(\".saltOption\");\n if (listItems) {\n const orderedItemNames = Array.from(listItems).map(getOptionName);\n onReorderSelectedItems(orderedItemNames);\n }\n }, 300);\n }, [availableItemsFiltered, selectedItemsFiltered]);\n\n const searchPlaceholderText = `Find ${singularForm(itemTypeName)}`;\n const maxSelectionsSubHeading = maxSelections ? `(${maxSelections} max)` : \"\";\n const selectedItemsHeading = `${selectedItemsCount} ${selectedItemsCount === 1 ? singularForm(itemTypeName) : pluralForm(itemTypeName)} in view ${maxSelectionsSubHeading}`;\n const availableItemsHeading = `${availableItemsCount} available ${availableItemsCount === 1 ? singularForm(itemTypeName) : pluralForm(itemTypeName)}`;\n\n return (\n <div\n {...htmlAttributes}\n className={cx(classBase, className)}\n ref={forwardedRef}\n >\n <form className={`${classBase}-search`} role=\"search\">\n <Input\n startAdornment={searchIcon}\n placeholder={searchPlaceholderText}\n ref={searchCallbackRef}\n value={searchText}\n onChange={onChangeSearchInput}\n />\n </form>\n\n <div className={`${classBase}-scrollContainer vuuScrollable`}>\n <div className={`${classBase}-sectionHeader`}>\n {selectedItemsHeading}\n </div>\n <DragDropProvider onDragEnd={handleDragEnd}>\n <ListBox\n className={`${classBase}-selectedList`}\n onSelectionChange={onSelectionChange}\n ref={listRef}\n selected={selected}\n >\n {selectedItemsFiltered.map((item, index) => (\n <SelectedListItem\n item={item}\n index={index}\n key={item.name}\n onRemove={onRemoveItemFromSelectedList}\n searchPattern={searchText.toLowerCase() as Lowercase<string>}\n value={item}\n />\n ))}\n </ListBox>\n </DragDropProvider>\n\n <div\n className={cx(\n `${classBase}-sectionHeader`,\n `${classBase}-availableHeader`,\n )}\n >\n {availableItemsHeading}\n </div>\n <ListBox\n className={`${classBase}-availableList`}\n selected={NO_SELECTION}\n >\n {availableItemsFiltered.map((item, index) => (\n <AvailableListItem\n item={item}\n index={index}\n key={item.name}\n onAdd={onAddItemToSelectedList}\n searchPattern={searchText.toLowerCase() as Lowercase<string>}\n value={item}\n disabled={selectedItemsCount === maxSelections}\n />\n ))}\n </ListBox>\n </div>\n {createCustomItemProps ? (\n <div className={`${classBase}-item-buttons`}>\n <Button onClick={createCustomItemProps.onClickCreateCustomItem}>\n {createCustomItemProps.buttonLabel}\n </Button>\n </div>\n ) : null}\n </div>\n );\n});\n"],"names":["jsx","Icon","useSortable","useMemo","getItemLabel","applyHighlighting","useCallback","jsxs","Option","IconButton","forwardRef","ItemPicker","useWindow","useComponentCssInjection","itemPickerCss","useItemPicker","useRef","singularForm","pluralForm","Input","DragDropProvider","ListBox","Button"],"mappings":"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"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var vuuUtils = require('@vuu-ui/vuu-utils');
|
|
4
|
+
var React = require('react');
|
|
5
|
+
|
|
6
|
+
const filterItems = (items, pattern) => {
|
|
7
|
+
if (pattern) {
|
|
8
|
+
const lowerCasePattern = pattern.toLowerCase();
|
|
9
|
+
return items.filter(
|
|
10
|
+
(item) => getItemLabel(item).toLowerCase().indexOf(lowerCasePattern) !== -1
|
|
11
|
+
);
|
|
12
|
+
} else {
|
|
13
|
+
return items;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
const getItemLabel = (item) => {
|
|
17
|
+
if (item.label) return item.label;
|
|
18
|
+
else return item.name;
|
|
19
|
+
};
|
|
20
|
+
const itemName = (target) => {
|
|
21
|
+
const listItem = vuuUtils.queryClosest(target, ".saltOption", true);
|
|
22
|
+
const { name } = listItem.dataset;
|
|
23
|
+
if (name) {
|
|
24
|
+
return name;
|
|
25
|
+
} else {
|
|
26
|
+
throw Error(
|
|
27
|
+
"[useItemPicker] item name could not be identified, data-name attribute not found"
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const byItemName = ({ name: n1 }, { name: n2 }) => n1 > n2 ? 1 : n2 > n1 ? -1 : 0;
|
|
32
|
+
const useItemPicker = ({
|
|
33
|
+
allItems,
|
|
34
|
+
selectedItems,
|
|
35
|
+
maxSelections,
|
|
36
|
+
onSelectedItemsChange
|
|
37
|
+
}) => {
|
|
38
|
+
if (maxSelections && selectedItems.length > maxSelections) {
|
|
39
|
+
throw Error(
|
|
40
|
+
`[useItemPicker] max selections ${maxSelections} exceeded by selected items (that has count ${selectedItems.length})`
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
const [searchPattern, setSearchPattern] = React.useState("");
|
|
44
|
+
const handleChangeSearchInput = React.useCallback((evt) => {
|
|
45
|
+
const { value } = evt.target;
|
|
46
|
+
setSearchPattern(value);
|
|
47
|
+
}, []);
|
|
48
|
+
const handleAddItemToSelectedList = React.useCallback(
|
|
49
|
+
(e) => {
|
|
50
|
+
const name = itemName(e.target);
|
|
51
|
+
const itemToAdd = allItems.find((item) => item.name === name);
|
|
52
|
+
if (itemToAdd) {
|
|
53
|
+
const newSelectedItems = selectedItems.concat(itemToAdd);
|
|
54
|
+
onSelectedItemsChange(newSelectedItems);
|
|
55
|
+
} else {
|
|
56
|
+
throw Error(
|
|
57
|
+
`[useItemPicker] handleAddItemToSelectedList, item '${name}' not found`
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
[allItems, selectedItems, onSelectedItemsChange]
|
|
62
|
+
);
|
|
63
|
+
const handleRemoveItemFromSelectedList = React.useCallback(
|
|
64
|
+
(e) => {
|
|
65
|
+
const name = itemName(e.target);
|
|
66
|
+
const itemToRemove = selectedItems.find((item) => item.name === name);
|
|
67
|
+
if (itemToRemove) {
|
|
68
|
+
const newSelectedItems = selectedItems.filter(
|
|
69
|
+
(item) => item.name !== name
|
|
70
|
+
);
|
|
71
|
+
onSelectedItemsChange(newSelectedItems);
|
|
72
|
+
} else {
|
|
73
|
+
throw Error(
|
|
74
|
+
`[useItemPicker] handleRemoveItemFromSelectedList, item '${name}' not found`
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
[selectedItems, onSelectedItemsChange]
|
|
79
|
+
);
|
|
80
|
+
const handleReorderSelectedItems = React.useCallback(
|
|
81
|
+
(orderedItemNames) => {
|
|
82
|
+
const reorderedSelectedItems = vuuUtils.reorderItems(
|
|
83
|
+
selectedItems,
|
|
84
|
+
orderedItemNames
|
|
85
|
+
);
|
|
86
|
+
onSelectedItemsChange(reorderedSelectedItems);
|
|
87
|
+
},
|
|
88
|
+
[selectedItems, onSelectedItemsChange]
|
|
89
|
+
);
|
|
90
|
+
const getSelectedItemsFiltered = React.useMemo(() => {
|
|
91
|
+
return filterItems(selectedItems, searchPattern);
|
|
92
|
+
}, [selectedItems, searchPattern]);
|
|
93
|
+
const getAvailableItemsFiltered = React.useMemo(() => {
|
|
94
|
+
return filterItems(allItems, searchPattern).filter(
|
|
95
|
+
({ name }) => selectedItems.findIndex((item) => item.name === name) === -1
|
|
96
|
+
).toSorted(byItemName);
|
|
97
|
+
}, [allItems, selectedItems, searchPattern]);
|
|
98
|
+
const getAvailableItemsCount = React.useMemo(() => {
|
|
99
|
+
return allItems.filter(
|
|
100
|
+
({ name }) => selectedItems.findIndex((item) => item.name === name) === -1
|
|
101
|
+
).length;
|
|
102
|
+
}, [allItems, selectedItems]);
|
|
103
|
+
return {
|
|
104
|
+
selectedItemsCount: selectedItems.length,
|
|
105
|
+
availableItemsCount: getAvailableItemsCount,
|
|
106
|
+
selectedItemsFiltered: getSelectedItemsFiltered,
|
|
107
|
+
availableItemsFiltered: getAvailableItemsFiltered,
|
|
108
|
+
searchText: searchPattern,
|
|
109
|
+
onChangeSearchInput: handleChangeSearchInput,
|
|
110
|
+
onAddItemToSelectedList: handleAddItemToSelectedList,
|
|
111
|
+
onRemoveItemFromSelectedList: handleRemoveItemFromSelectedList,
|
|
112
|
+
onReorderSelectedItems: handleReorderSelectedItems
|
|
113
|
+
};
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
exports.getItemLabel = getItemLabel;
|
|
117
|
+
exports.useItemPicker = useItemPicker;
|
|
118
|
+
//# sourceMappingURL=useItemPicker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useItemPicker.js","sources":["../../../../packages/vuu-ui-controls/src/item-picker/useItemPicker.tsx"],"sourcesContent":["import { queryClosest, reorderItems } from \"@vuu-ui/vuu-utils\";\nimport {\n FormEventHandler,\n MouseEventHandler,\n useCallback,\n useMemo,\n useState,\n} from \"react\";\n\n/** This is a public description of an Item that can be displayed in the ItemPicker component, defining all the\n * mandatory and option attributes that can be defined by the client. */\nexport interface ItemDescriptor {\n /** Unique name for this data value */\n name: string;\n /** Optional label for display instead of the name */\n label?: string;\n /** Optional data-icon name for display of an icon to the left of the item label/name in the selected list */\n icon?: string;\n /** Optional string to define groups of items for client reference, not currently used for rendering */\n group?: string;\n}\n\nexport interface CreateCustomItemProps {\n buttonLabel: string;\n onClickCreateCustomItem: MouseEventHandler<HTMLButtonElement>;\n}\n\nexport interface ItemPickerHookProps {\n allItems: ItemDescriptor[];\n selectedItems: ItemDescriptor[];\n maxSelections?: number;\n onSelectedItemsChange: (newSelectedItems: ItemDescriptor[]) => void;\n}\n\nconst filterItems = (\n items: readonly ItemDescriptor[],\n pattern: string,\n): readonly ItemDescriptor[] => {\n if (pattern) {\n const lowerCasePattern = pattern.toLowerCase();\n return items.filter(\n (item) =>\n getItemLabel(item).toLowerCase().indexOf(lowerCasePattern) !== -1,\n );\n } else {\n return items;\n }\n};\n\nexport const getItemLabel = (item: ItemDescriptor) => {\n if (item.label) return item.label;\n else return item.name;\n};\n\nconst itemName = (target: EventTarget): string => {\n const listItem = queryClosest(target, \".saltOption\", true);\n const { name } = listItem.dataset;\n if (name) {\n return name;\n } else {\n throw Error(\n \"[useItemPicker] item name could not be identified, data-name attribute not found\",\n );\n }\n};\n\nconst byItemName = (\n { name: n1 }: ItemDescriptor,\n { name: n2 }: ItemDescriptor,\n) => (n1 > n2 ? 1 : n2 > n1 ? -1 : 0);\n\nexport const useItemPicker = ({\n allItems,\n selectedItems,\n maxSelections,\n onSelectedItemsChange,\n}: ItemPickerHookProps) => {\n if (maxSelections && selectedItems.length > maxSelections) {\n throw Error(\n `[useItemPicker] max selections ${maxSelections} exceeded by selected items (that has count ${selectedItems.length})`,\n );\n }\n\n const [searchPattern, setSearchPattern] = useState(\"\");\n\n const handleChangeSearchInput = useCallback<FormEventHandler>((evt) => {\n const { value } = evt.target as HTMLInputElement;\n setSearchPattern(value);\n }, []);\n\n const handleAddItemToSelectedList = useCallback<\n MouseEventHandler<HTMLButtonElement>\n >(\n (e) => {\n const name = itemName(e.target);\n const itemToAdd = allItems.find((item) => item.name === name);\n if (itemToAdd) {\n const newSelectedItems = selectedItems.concat(itemToAdd);\n onSelectedItemsChange(newSelectedItems);\n } else {\n throw Error(\n `[useItemPicker] handleAddItemToSelectedList, item '${name}' not found`,\n );\n }\n },\n [allItems, selectedItems, onSelectedItemsChange],\n );\n\n const handleRemoveItemFromSelectedList = useCallback<\n MouseEventHandler<HTMLButtonElement>\n >(\n (e) => {\n const name = itemName(e.target);\n const itemToRemove = selectedItems.find((item) => item.name === name);\n if (itemToRemove) {\n const newSelectedItems = selectedItems.filter(\n (item) => item.name !== name,\n );\n onSelectedItemsChange(newSelectedItems);\n } else {\n throw Error(\n `[useItemPicker] handleRemoveItemFromSelectedList, item '${name}' not found`,\n );\n }\n },\n [selectedItems, onSelectedItemsChange],\n );\n\n const handleReorderSelectedItems = useCallback(\n (orderedItemNames: string[]) => {\n const reorderedSelectedItems: ItemDescriptor[] = reorderItems(\n selectedItems,\n orderedItemNames,\n );\n onSelectedItemsChange(reorderedSelectedItems);\n },\n [selectedItems, onSelectedItemsChange],\n );\n\n const getSelectedItemsFiltered = useMemo(() => {\n return filterItems(selectedItems, searchPattern);\n }, [selectedItems, searchPattern]);\n\n const getAvailableItemsFiltered = useMemo(() => {\n return filterItems(allItems, searchPattern)\n .filter(\n ({ name }) =>\n selectedItems.findIndex((item) => item.name === name) === -1,\n )\n .toSorted(byItemName);\n }, [allItems, selectedItems, searchPattern]);\n\n const getAvailableItemsCount = useMemo(() => {\n return allItems.filter(\n ({ name }) =>\n selectedItems.findIndex((item) => item.name === name) === -1,\n ).length;\n }, [allItems, selectedItems]);\n\n return {\n selectedItemsCount: selectedItems.length,\n availableItemsCount: getAvailableItemsCount,\n selectedItemsFiltered: getSelectedItemsFiltered,\n availableItemsFiltered: getAvailableItemsFiltered,\n searchText: searchPattern,\n onChangeSearchInput: handleChangeSearchInput,\n onAddItemToSelectedList: handleAddItemToSelectedList,\n onRemoveItemFromSelectedList: handleRemoveItemFromSelectedList,\n onReorderSelectedItems: handleReorderSelectedItems,\n };\n};\n"],"names":["queryClosest","useState","useCallback","reorderItems","useMemo"],"mappings":"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"}
|
package/cjs/prompt/Prompt.js
CHANGED
|
@@ -39,6 +39,7 @@ const Prompt = ({
|
|
|
39
39
|
confirmButtonLabel = "Confirm",
|
|
40
40
|
confirmButtonProps,
|
|
41
41
|
customAction = null,
|
|
42
|
+
disableDismiss,
|
|
42
43
|
disableAccent,
|
|
43
44
|
headerTag,
|
|
44
45
|
icon,
|
|
@@ -110,6 +111,7 @@ const Prompt = ({
|
|
|
110
111
|
{
|
|
111
112
|
...htmlAttributes,
|
|
112
113
|
className: cx(classBase, className),
|
|
114
|
+
disableDismiss,
|
|
113
115
|
onOpenChange,
|
|
114
116
|
open,
|
|
115
117
|
ref: callbackRef,
|
package/cjs/prompt/Prompt.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Prompt.js","sources":["../../../../packages/vuu-ui-controls/src/prompt/Prompt.tsx"],"sourcesContent":["import {\n Button,\n Dialog,\n DialogActions,\n DialogContent,\n DialogHeader,\n DialogHeaderProps,\n DialogProps,\n} from \"@salt-ds/core\";\nimport cx from \"clsx\";\nimport {\n HTMLAttributes,\n MouseEventHandler,\n ReactNode,\n RefCallback,\n RefObject,\n useCallback,\n} from \"react\";\nimport { Icon, IconButton } from \"../icon-button\";\nimport { useComponentCssInjection } from \"@salt-ds/styles\";\nimport { useWindow } from \"@salt-ds/window\";\n\nimport promptCss from \"./Prompt.css\";\n\nconst classBase = \"vuuPrompt\";\n\n/**\n * Allow fine grained configuration of Prompt buttons\n * The ref is provided to facilitate programmatic focus.\n */\nexport interface PromptButtonProps {\n disabled?: boolean;\n label?: string;\n ref?: RefObject<HTMLButtonElement | null>;\n}\n\nconst getHeader = (\n title: string,\n icon?: string,\n headerTag?: ReactNode,\n): ReactNode => {\n if (icon && headerTag) {\n return (\n <>\n <Icon name={icon} />\n <span>{title}</span>\n {headerTag}\n </>\n );\n } else if (icon) {\n return (\n <>\n <Icon name={icon} />\n <span>{title}</span>\n </>\n );\n } else if (headerTag) {\n return (\n <>\n <span>{title}</span>\n {headerTag}\n </>\n );\n }\n\n return title;\n};\n\nexport interface PromptProps\n extends Pick<DialogProps, \"onOpenChange\" | \"open\" | \"status\">,\n Pick<DialogHeaderProps, \"disableAccent\">,\n Omit<HTMLAttributes<HTMLDivElement>, \"content\" | \"title\"> {\n /**\n * For simple configuration, where just a cancel button label is required.\n */\n cancelButtonLabel?: string;\n /**\n * Allow fine grained configuration of cancel button\n */\n cancelButtonProps?: PromptButtonProps;\n /**\n * For simple configuration, where just a confirm button label is required.\n */\n confirmButtonLabel?: string;\n /**\n * Allow fine grained configuration of confirm button\n */\n confirmButtonProps?: PromptButtonProps;\n\n /**\n * A custom action will be displayed in Prompt button bar, before cancel/confirm buttons.\n */\n customAction?: ReactNode;\n\n /**\n * Intended to allow for the display of a status tag or badge in the header. It will be displayed inline\n * after the title. A small component is expected.\n */\n headerTag?: ReactNode;\n\n icon?: string;\n /**\n * Set this prop if one of the three built-in buttons should receive initial focus.\n * Allows user to quickly dismiss/confirm prompt from keyboard. If the prompt content\n * contains focusable item(s), focus should be controlled by caller.\n */\n initialFocusedItem?: \"confirm\" | \"cancel\" | \"close\";\n onCancel?: () => void;\n onConfirm?: () => void | false;\n onClose?: () => void;\n showCancelButton?: boolean;\n showCloseButton?: boolean;\n showConfirmButton?: boolean;\n title: string;\n}\n\nexport const Prompt = ({\n children,\n className,\n cancelButtonLabel = \"Cancel\",\n cancelButtonProps,\n confirmButtonLabel = \"Confirm\",\n confirmButtonProps,\n customAction = null,\n disableAccent,\n headerTag,\n icon,\n initialFocusedItem,\n onCancel,\n onClose,\n onConfirm,\n onOpenChange,\n open,\n showCancelButton = true,\n showCloseButton = true,\n showConfirmButton = true,\n status,\n title,\n\n ...htmlAttributes\n}: PromptProps) => {\n const targetWindow = useWindow();\n useComponentCssInjection({\n testId: \"vuu-prompt-next\",\n css: promptCss,\n window: targetWindow,\n });\n\n const close = useCallback(() => {\n onClose?.();\n onOpenChange?.(false);\n }, [onClose, onOpenChange]);\n\n const callbackRef = useCallback<RefCallback<HTMLDivElement>>(\n (el) => {\n if (el) {\n let target: HTMLButtonElement | null = null;\n if (initialFocusedItem === \"confirm\" && showConfirmButton) {\n target = el.querySelector(\".vuuPromptConfirmButton\");\n } else if (initialFocusedItem === \"cancel\" && showCancelButton) {\n target = el.querySelector(\".vuuPromptCancelButton\");\n } else if (initialFocusedItem === \"close\" && showCloseButton) {\n target = el.querySelector(\".vuuPromptCloseButton\");\n }\n\n if (target) {\n setTimeout(() => {\n target.focus();\n }, 200);\n }\n }\n },\n [initialFocusedItem, showCancelButton, showCloseButton, showConfirmButton],\n );\n\n const handleCancel = useCallback<MouseEventHandler<HTMLButtonElement>>(() => {\n onCancel?.();\n close();\n }, [close, onCancel]);\n\n const handleConfirm = useCallback<\n MouseEventHandler<HTMLButtonElement>\n >(() => {\n if (onConfirm?.() !== false) {\n close();\n }\n }, [close, onConfirm]);\n\n const actions = showCloseButton ? (\n <IconButton\n appearance=\"transparent\"\n className=\"vuuPromptCloseButton\"\n data-embedded\n icon=\"close\"\n onClick={close}\n />\n ) : null;\n\n const header = getHeader(title, icon, headerTag);\n\n return (\n <Dialog\n {...htmlAttributes}\n className={cx(classBase, className)}\n onOpenChange={onOpenChange}\n open={open}\n ref={callbackRef}\n status={status}\n >\n <DialogHeader\n disableAccent={disableAccent}\n header={header}\n actions={actions}\n />\n <DialogContent>{children}</DialogContent>\n <DialogActions>\n {customAction}\n {showCancelButton ? (\n <Button\n appearance=\"solid\"\n className=\"vuuPromptCancelButton\"\n disabled={cancelButtonProps?.disabled}\n onClick={handleCancel}\n ref={cancelButtonProps?.ref}\n sentiment=\"neutral\"\n >\n {cancelButtonProps?.label ?? cancelButtonLabel}\n </Button>\n ) : null}\n {showConfirmButton ? (\n <Button\n appearance=\"solid\"\n className=\"vuuPromptConfirmButton\"\n disabled={confirmButtonProps?.disabled}\n sentiment=\"accented\"\n onClick={handleConfirm}\n ref={confirmButtonProps?.ref}\n >\n {confirmButtonProps?.label ?? confirmButtonLabel}\n </Button>\n ) : null}\n </DialogActions>\n </Dialog>\n );\n};\n"],"names":["jsxs","Fragment","jsx","Icon","useWindow","useComponentCssInjection","promptCss","useCallback","IconButton","Dialog","DialogHeader","DialogContent","DialogActions","Button"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Prompt.js","sources":["../../../../packages/vuu-ui-controls/src/prompt/Prompt.tsx"],"sourcesContent":["import {\n Button,\n Dialog,\n DialogActions,\n DialogContent,\n DialogHeader,\n DialogHeaderProps,\n DialogProps,\n} from \"@salt-ds/core\";\nimport cx from \"clsx\";\nimport {\n HTMLAttributes,\n MouseEventHandler,\n ReactNode,\n RefCallback,\n RefObject,\n useCallback,\n} from \"react\";\nimport { Icon, IconButton } from \"../icon-button\";\nimport { useComponentCssInjection } from \"@salt-ds/styles\";\nimport { useWindow } from \"@salt-ds/window\";\n\nimport promptCss from \"./Prompt.css\";\n\nconst classBase = \"vuuPrompt\";\n\n/**\n * Allow fine grained configuration of Prompt buttons\n * The ref is provided to facilitate programmatic focus.\n */\nexport interface PromptButtonProps {\n disabled?: boolean;\n label?: string;\n ref?: RefObject<HTMLButtonElement | null>;\n}\n\nconst getHeader = (\n title: string,\n icon?: string,\n headerTag?: ReactNode,\n): ReactNode => {\n if (icon && headerTag) {\n return (\n <>\n <Icon name={icon} />\n <span>{title}</span>\n {headerTag}\n </>\n );\n } else if (icon) {\n return (\n <>\n <Icon name={icon} />\n <span>{title}</span>\n </>\n );\n } else if (headerTag) {\n return (\n <>\n <span>{title}</span>\n {headerTag}\n </>\n );\n }\n\n return title;\n};\n\nexport interface PromptProps\n extends Pick<DialogProps, \"disableDismiss\" | \"onOpenChange\" | \"open\" | \"status\">,\n Pick<DialogHeaderProps, \"disableAccent\">,\n Omit<HTMLAttributes<HTMLDivElement>, \"content\" | \"title\"> {\n /**\n * For simple configuration, where just a cancel button label is required.\n */\n cancelButtonLabel?: string;\n /**\n * Allow fine grained configuration of cancel button\n */\n cancelButtonProps?: PromptButtonProps;\n /**\n * For simple configuration, where just a confirm button label is required.\n */\n confirmButtonLabel?: string;\n /**\n * Allow fine grained configuration of confirm button\n */\n confirmButtonProps?: PromptButtonProps;\n\n /**\n * A custom action will be displayed in Prompt button bar, before cancel/confirm buttons.\n */\n customAction?: ReactNode;\n\n /**\n * Intended to allow for the display of a status tag or badge in the header. It will be displayed inline\n * after the title. A small component is expected.\n */\n headerTag?: ReactNode;\n\n icon?: string;\n /**\n * Set this prop if one of the three built-in buttons should receive initial focus.\n * Allows user to quickly dismiss/confirm prompt from keyboard. If the prompt content\n * contains focusable item(s), focus should be controlled by caller.\n */\n initialFocusedItem?: \"confirm\" | \"cancel\" | \"close\";\n onCancel?: () => void;\n onConfirm?: () => void | false;\n onClose?: () => void;\n showCancelButton?: boolean;\n showCloseButton?: boolean;\n showConfirmButton?: boolean;\n title: string;\n}\n\nexport const Prompt = ({\n children,\n className,\n cancelButtonLabel = \"Cancel\",\n cancelButtonProps,\n confirmButtonLabel = \"Confirm\",\n confirmButtonProps,\n customAction = null,\n disableDismiss,\n disableAccent,\n headerTag,\n icon,\n initialFocusedItem,\n onCancel,\n onClose,\n onConfirm,\n onOpenChange,\n open,\n showCancelButton = true,\n showCloseButton = true,\n showConfirmButton = true,\n status,\n title,\n\n ...htmlAttributes\n}: PromptProps) => {\n const targetWindow = useWindow();\n useComponentCssInjection({\n testId: \"vuu-prompt-next\",\n css: promptCss,\n window: targetWindow,\n });\n\n const close = useCallback(() => {\n onClose?.();\n onOpenChange?.(false);\n }, [onClose, onOpenChange]);\n\n const callbackRef = useCallback<RefCallback<HTMLDivElement>>(\n (el) => {\n if (el) {\n let target: HTMLButtonElement | null = null;\n if (initialFocusedItem === \"confirm\" && showConfirmButton) {\n target = el.querySelector(\".vuuPromptConfirmButton\");\n } else if (initialFocusedItem === \"cancel\" && showCancelButton) {\n target = el.querySelector(\".vuuPromptCancelButton\");\n } else if (initialFocusedItem === \"close\" && showCloseButton) {\n target = el.querySelector(\".vuuPromptCloseButton\");\n }\n\n if (target) {\n setTimeout(() => {\n target.focus();\n }, 200);\n }\n }\n },\n [initialFocusedItem, showCancelButton, showCloseButton, showConfirmButton],\n );\n\n const handleCancel = useCallback<MouseEventHandler<HTMLButtonElement>>(() => {\n onCancel?.();\n close();\n }, [close, onCancel]);\n\n const handleConfirm = useCallback<\n MouseEventHandler<HTMLButtonElement>\n >(() => {\n if (onConfirm?.() !== false) {\n close();\n }\n }, [close, onConfirm]);\n\n const actions = showCloseButton ? (\n <IconButton\n appearance=\"transparent\"\n className=\"vuuPromptCloseButton\"\n data-embedded\n icon=\"close\"\n onClick={close}\n />\n ) : null;\n\n const header = getHeader(title, icon, headerTag);\n\n return (\n <Dialog\n {...htmlAttributes}\n className={cx(classBase, className)}\n disableDismiss={disableDismiss}\n onOpenChange={onOpenChange}\n open={open}\n ref={callbackRef}\n status={status}\n >\n <DialogHeader\n disableAccent={disableAccent}\n header={header}\n actions={actions}\n />\n <DialogContent>{children}</DialogContent>\n <DialogActions>\n {customAction}\n {showCancelButton ? (\n <Button\n appearance=\"solid\"\n className=\"vuuPromptCancelButton\"\n disabled={cancelButtonProps?.disabled}\n onClick={handleCancel}\n ref={cancelButtonProps?.ref}\n sentiment=\"neutral\"\n >\n {cancelButtonProps?.label ?? cancelButtonLabel}\n </Button>\n ) : null}\n {showConfirmButton ? (\n <Button\n appearance=\"solid\"\n className=\"vuuPromptConfirmButton\"\n disabled={confirmButtonProps?.disabled}\n sentiment=\"accented\"\n onClick={handleConfirm}\n ref={confirmButtonProps?.ref}\n >\n {confirmButtonProps?.label ?? confirmButtonLabel}\n </Button>\n ) : null}\n </DialogActions>\n </Dialog>\n );\n};\n"],"names":["jsxs","Fragment","jsx","Icon","useWindow","useComponentCssInjection","promptCss","useCallback","IconButton","Dialog","DialogHeader","DialogContent","DialogActions","Button"],"mappings":"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"}
|
package/esm/index.js
CHANGED
|
@@ -25,6 +25,7 @@ export { Icon } from './icon-button/Icon.js';
|
|
|
25
25
|
export { IconButton } from './icon-button/IconButton.js';
|
|
26
26
|
export { ToggleIconButton } from './icon-button/ToggleIconButton.js';
|
|
27
27
|
export { TablePicker } from './instrument-picker/TablePicker.js';
|
|
28
|
+
export { ItemPicker } from './item-picker/ItemPicker.js';
|
|
28
29
|
export { MeasuredContainer } from './measured-container/MeasuredContainer.js';
|
|
29
30
|
export { reduceSizeHeight, useMeasuredContainer } from './measured-container/useMeasuredContainer.js';
|
|
30
31
|
export { ModalProvider, useModal } from './modal-provider/ModalProvider.js';
|
package/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
var itemPickerCss = ".vuuItemPicker {\n --label-margin-left: 0px;\n\n --itempicker-background: var(\n --vuuItemPicker-background,\n var(--salt-container-primary-background)\n );\n background: var(--itempicker-background);\n container-type: inline-size;\n display: flex;\n flex-direction: column;\n gap: var(--salt-spacing-300);\n height: var(--vuuItemPicker-height, 100%);\n overflow: hidden;\n padding: var(--salt-spacing-300) 0;\n\n &.vuuItemPicker-withCalculated {\n --label-margin-left: 8px;\n }\n\n .saltListBox {\n background-color: inherit;\n }\n\n .saltOption {\n align-items: center;\n background-color: inherit;\n border-bottom: var(--vuuItemListItem-border, solid) 1px\n var(--salt-separable-tertiary-borderColor);\n gap: var(--salt-spacing-200);\n height: calc(var(--salt-size-base) + var(--salt-spacing-200));\n .vuuIcon {\n --vuu-icon-color: var(--salt-content-secondary-foreground);\n }\n\n .vuuCalculatedItemIcon {\n --vuu-icon-color: var(--salt-content-secondary-foreground);\n left: 32px;\n position: absolute;\n top: 8px;\n }\n\n .vuuHighlight {\n color: var(--vuu-color-blue-40);\n text-decoration: underline;\n }\n\n .vuuItemPicker-text {\n margin-left: var(--label-margin-left);\n }\n }\n\n .vuuItemPickerListItem-action {\n margin-left: auto;\n visibility: hidden;\n }\n\n .saltOption:hover {\n .vuuItemPickerListItem-action {\n visibility: visible;\n }\n }\n\n .vuuItemPicker-search {\n --saltInput-paddingLeft: var(--salt-spacing-300);\n padding: var(--salt-spacing-200) var(--salt-spacing-300) 0\n var(--salt-spacing-300);\n }\n\n .vuuItemPicker-scrollContainer {\n flex: 1 1 0px;\n overflow: auto;\n\n .vuuItemPicker-sectionHeader {\n align-items: center;\n background: var(--itempicker-background);\n display: flex;\n height: 32px;\n padding: 0 var(--salt-spacing-300);\n position: sticky;\n top: 0;\n z-index: 1;\n }\n\n .vuuItemPicker-availableHeader {\n top: 32px;\n }\n\n .vuuItemPicker-availableList {\n .saltOption {\n padding-left: var(--salt-spacing-300);\n }\n }\n }\n}\n";
|
|
2
|
+
|
|
3
|
+
export { itemPickerCss as default };
|
|
4
|
+
//# sourceMappingURL=ItemPicker.css.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ItemPicker.css.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { Input, ListBox, Button, Option } from '@salt-ds/core';
|
|
3
|
+
import { useComponentCssInjection } from '@salt-ds/styles';
|
|
4
|
+
import { useWindow } from '@salt-ds/window';
|
|
5
|
+
import { applyHighlighting } from '@vuu-ui/vuu-table';
|
|
6
|
+
import { singularForm, pluralForm, DragDropProvider, useSortable } from '@vuu-ui/vuu-utils';
|
|
7
|
+
import cx from 'clsx';
|
|
8
|
+
import { forwardRef, useCallback, useRef, useMemo } from 'react';
|
|
9
|
+
import { Icon } from '../icon-button/Icon.js';
|
|
10
|
+
import { IconButton } from '../icon-button/IconButton.js';
|
|
11
|
+
import { useItemPicker, getItemLabel } from './useItemPicker.js';
|
|
12
|
+
import itemPickerCss from './ItemPicker.css.js';
|
|
13
|
+
|
|
14
|
+
const classBase = "vuuItemPicker";
|
|
15
|
+
const classBaseListItem = "vuuItemPickerListItem";
|
|
16
|
+
const searchIcon = /* @__PURE__ */ jsx(Icon, { name: "search" });
|
|
17
|
+
const NO_SELECTION = [];
|
|
18
|
+
const useSorting = (id, index) => {
|
|
19
|
+
const { handleRef: sortableHandleRef, ref: sortableRef } = useSortable({
|
|
20
|
+
id,
|
|
21
|
+
index
|
|
22
|
+
});
|
|
23
|
+
const [handleRef, ref] = useMemo(() => {
|
|
24
|
+
return [sortableHandleRef, sortableRef];
|
|
25
|
+
}, [sortableHandleRef, sortableRef]);
|
|
26
|
+
return {
|
|
27
|
+
handleRef,
|
|
28
|
+
ref
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
const SelectedListItem = ({
|
|
32
|
+
className: classNameProp,
|
|
33
|
+
index,
|
|
34
|
+
item,
|
|
35
|
+
onRemove,
|
|
36
|
+
searchPattern = "",
|
|
37
|
+
...optionProps
|
|
38
|
+
}) => {
|
|
39
|
+
const { handleRef, ref } = useSorting(item.name, index);
|
|
40
|
+
const value = getItemLabel(item);
|
|
41
|
+
const valueWithHighlighting = applyHighlighting(value, searchPattern);
|
|
42
|
+
const handleRemoveButtonClick = useCallback(
|
|
43
|
+
(e) => {
|
|
44
|
+
e.stopPropagation();
|
|
45
|
+
onRemove?.(e);
|
|
46
|
+
},
|
|
47
|
+
[onRemove]
|
|
48
|
+
);
|
|
49
|
+
return /* @__PURE__ */ jsxs(
|
|
50
|
+
Option,
|
|
51
|
+
{
|
|
52
|
+
...optionProps,
|
|
53
|
+
className: cx(classNameProp, classBaseListItem),
|
|
54
|
+
"data-name": item.name,
|
|
55
|
+
ref,
|
|
56
|
+
children: [
|
|
57
|
+
/* @__PURE__ */ jsx(
|
|
58
|
+
IconButton,
|
|
59
|
+
{
|
|
60
|
+
"data-embedded": true,
|
|
61
|
+
appearance: "transparent",
|
|
62
|
+
icon: "draggable",
|
|
63
|
+
ref: handleRef,
|
|
64
|
+
size: 16
|
|
65
|
+
}
|
|
66
|
+
),
|
|
67
|
+
item.icon ? /* @__PURE__ */ jsx(Icon, { name: item.icon }, "icon") : null,
|
|
68
|
+
/* @__PURE__ */ jsx("span", { className: `${classBase}-text`, children: valueWithHighlighting }),
|
|
69
|
+
/* @__PURE__ */ jsx(
|
|
70
|
+
IconButton,
|
|
71
|
+
{
|
|
72
|
+
className: `${classBaseListItem}-action`,
|
|
73
|
+
"data-embedded": true,
|
|
74
|
+
appearance: "transparent",
|
|
75
|
+
icon: "cross",
|
|
76
|
+
onClick: handleRemoveButtonClick,
|
|
77
|
+
size: 16
|
|
78
|
+
}
|
|
79
|
+
)
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
);
|
|
83
|
+
};
|
|
84
|
+
const AvailableListItem = ({
|
|
85
|
+
className: classNameProp,
|
|
86
|
+
index,
|
|
87
|
+
item,
|
|
88
|
+
onAdd,
|
|
89
|
+
searchPattern = "",
|
|
90
|
+
disabled,
|
|
91
|
+
...optionProps
|
|
92
|
+
}) => {
|
|
93
|
+
const value = getItemLabel(item);
|
|
94
|
+
const valueWithHighlighting = applyHighlighting(value, searchPattern);
|
|
95
|
+
return /* @__PURE__ */ jsxs(
|
|
96
|
+
Option,
|
|
97
|
+
{
|
|
98
|
+
...optionProps,
|
|
99
|
+
className: cx(classNameProp, classBaseListItem),
|
|
100
|
+
"data-name": item.name,
|
|
101
|
+
disabled,
|
|
102
|
+
children: [
|
|
103
|
+
/* @__PURE__ */ jsx("span", { className: `${classBase}-text`, children: valueWithHighlighting }),
|
|
104
|
+
/* @__PURE__ */ jsx(
|
|
105
|
+
IconButton,
|
|
106
|
+
{
|
|
107
|
+
className: `${classBaseListItem}-action`,
|
|
108
|
+
"data-embedded": true,
|
|
109
|
+
appearance: "transparent",
|
|
110
|
+
icon: "plus",
|
|
111
|
+
onClick: onAdd,
|
|
112
|
+
size: 16,
|
|
113
|
+
disabled
|
|
114
|
+
}
|
|
115
|
+
)
|
|
116
|
+
]
|
|
117
|
+
}
|
|
118
|
+
);
|
|
119
|
+
};
|
|
120
|
+
const ItemPicker = forwardRef(function ItemPicker2({
|
|
121
|
+
className,
|
|
122
|
+
itemTypeName,
|
|
123
|
+
allItems,
|
|
124
|
+
selectedItems,
|
|
125
|
+
maxSelections,
|
|
126
|
+
onSelectedItemsChange,
|
|
127
|
+
onSelectionChange,
|
|
128
|
+
selected = NO_SELECTION,
|
|
129
|
+
createCustomItemProps,
|
|
130
|
+
...htmlAttributes
|
|
131
|
+
}, forwardedRef) {
|
|
132
|
+
const targetWindow = useWindow();
|
|
133
|
+
useComponentCssInjection({
|
|
134
|
+
testId: "vuu-item-picker",
|
|
135
|
+
css: itemPickerCss,
|
|
136
|
+
window: targetWindow
|
|
137
|
+
});
|
|
138
|
+
const searchCallbackRef = useCallback((el) => {
|
|
139
|
+
setTimeout(() => {
|
|
140
|
+
el?.querySelector("input")?.focus();
|
|
141
|
+
}, 100);
|
|
142
|
+
}, []);
|
|
143
|
+
const {
|
|
144
|
+
selectedItemsCount,
|
|
145
|
+
availableItemsCount,
|
|
146
|
+
selectedItemsFiltered,
|
|
147
|
+
availableItemsFiltered,
|
|
148
|
+
searchText,
|
|
149
|
+
onChangeSearchInput,
|
|
150
|
+
onAddItemToSelectedList,
|
|
151
|
+
onRemoveItemFromSelectedList,
|
|
152
|
+
onReorderSelectedItems
|
|
153
|
+
} = useItemPicker({
|
|
154
|
+
allItems,
|
|
155
|
+
selectedItems,
|
|
156
|
+
onSelectedItemsChange,
|
|
157
|
+
maxSelections
|
|
158
|
+
});
|
|
159
|
+
const listRef = useRef(null);
|
|
160
|
+
const getOptionName = (option) => {
|
|
161
|
+
if (option) {
|
|
162
|
+
const { name } = option.dataset;
|
|
163
|
+
if (name) {
|
|
164
|
+
return name;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
throw Error("[ItemPicker] list option has no data-name");
|
|
168
|
+
};
|
|
169
|
+
const handleDragEnd = useCallback(() => {
|
|
170
|
+
setTimeout(() => {
|
|
171
|
+
const listItems = listRef.current?.querySelectorAll(".saltOption");
|
|
172
|
+
if (listItems) {
|
|
173
|
+
const orderedItemNames = Array.from(listItems).map(getOptionName);
|
|
174
|
+
onReorderSelectedItems(orderedItemNames);
|
|
175
|
+
}
|
|
176
|
+
}, 300);
|
|
177
|
+
}, [availableItemsFiltered, selectedItemsFiltered]);
|
|
178
|
+
const searchPlaceholderText = `Find ${singularForm(itemTypeName)}`;
|
|
179
|
+
const maxSelectionsSubHeading = maxSelections ? `(${maxSelections} max)` : "";
|
|
180
|
+
const selectedItemsHeading = `${selectedItemsCount} ${selectedItemsCount === 1 ? singularForm(itemTypeName) : pluralForm(itemTypeName)} in view ${maxSelectionsSubHeading}`;
|
|
181
|
+
const availableItemsHeading = `${availableItemsCount} available ${availableItemsCount === 1 ? singularForm(itemTypeName) : pluralForm(itemTypeName)}`;
|
|
182
|
+
return /* @__PURE__ */ jsxs(
|
|
183
|
+
"div",
|
|
184
|
+
{
|
|
185
|
+
...htmlAttributes,
|
|
186
|
+
className: cx(classBase, className),
|
|
187
|
+
ref: forwardedRef,
|
|
188
|
+
children: [
|
|
189
|
+
/* @__PURE__ */ jsx("form", { className: `${classBase}-search`, role: "search", children: /* @__PURE__ */ jsx(
|
|
190
|
+
Input,
|
|
191
|
+
{
|
|
192
|
+
startAdornment: searchIcon,
|
|
193
|
+
placeholder: searchPlaceholderText,
|
|
194
|
+
ref: searchCallbackRef,
|
|
195
|
+
value: searchText,
|
|
196
|
+
onChange: onChangeSearchInput
|
|
197
|
+
}
|
|
198
|
+
) }),
|
|
199
|
+
/* @__PURE__ */ jsxs("div", { className: `${classBase}-scrollContainer vuuScrollable`, children: [
|
|
200
|
+
/* @__PURE__ */ jsx("div", { className: `${classBase}-sectionHeader`, children: selectedItemsHeading }),
|
|
201
|
+
/* @__PURE__ */ jsx(DragDropProvider, { onDragEnd: handleDragEnd, children: /* @__PURE__ */ jsx(
|
|
202
|
+
ListBox,
|
|
203
|
+
{
|
|
204
|
+
className: `${classBase}-selectedList`,
|
|
205
|
+
onSelectionChange,
|
|
206
|
+
ref: listRef,
|
|
207
|
+
selected,
|
|
208
|
+
children: selectedItemsFiltered.map((item, index) => /* @__PURE__ */ jsx(
|
|
209
|
+
SelectedListItem,
|
|
210
|
+
{
|
|
211
|
+
item,
|
|
212
|
+
index,
|
|
213
|
+
onRemove: onRemoveItemFromSelectedList,
|
|
214
|
+
searchPattern: searchText.toLowerCase(),
|
|
215
|
+
value: item
|
|
216
|
+
},
|
|
217
|
+
item.name
|
|
218
|
+
))
|
|
219
|
+
}
|
|
220
|
+
) }),
|
|
221
|
+
/* @__PURE__ */ jsx(
|
|
222
|
+
"div",
|
|
223
|
+
{
|
|
224
|
+
className: cx(
|
|
225
|
+
`${classBase}-sectionHeader`,
|
|
226
|
+
`${classBase}-availableHeader`
|
|
227
|
+
),
|
|
228
|
+
children: availableItemsHeading
|
|
229
|
+
}
|
|
230
|
+
),
|
|
231
|
+
/* @__PURE__ */ jsx(
|
|
232
|
+
ListBox,
|
|
233
|
+
{
|
|
234
|
+
className: `${classBase}-availableList`,
|
|
235
|
+
selected: NO_SELECTION,
|
|
236
|
+
children: availableItemsFiltered.map((item, index) => /* @__PURE__ */ jsx(
|
|
237
|
+
AvailableListItem,
|
|
238
|
+
{
|
|
239
|
+
item,
|
|
240
|
+
index,
|
|
241
|
+
onAdd: onAddItemToSelectedList,
|
|
242
|
+
searchPattern: searchText.toLowerCase(),
|
|
243
|
+
value: item,
|
|
244
|
+
disabled: selectedItemsCount === maxSelections
|
|
245
|
+
},
|
|
246
|
+
item.name
|
|
247
|
+
))
|
|
248
|
+
}
|
|
249
|
+
)
|
|
250
|
+
] }),
|
|
251
|
+
createCustomItemProps ? /* @__PURE__ */ jsx("div", { className: `${classBase}-item-buttons`, children: /* @__PURE__ */ jsx(Button, { onClick: createCustomItemProps.onClickCreateCustomItem, children: createCustomItemProps.buttonLabel }) }) : null
|
|
252
|
+
]
|
|
253
|
+
}
|
|
254
|
+
);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
export { ItemPicker, classBaseListItem };
|
|
258
|
+
//# sourceMappingURL=ItemPicker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ItemPicker.js","sources":["../../../../packages/vuu-ui-controls/src/item-picker/ItemPicker.tsx"],"sourcesContent":["import {\n Button,\n Input,\n ListBox,\n ListBoxProps,\n Option,\n OptionProps,\n} from \"@salt-ds/core\";\nimport { useComponentCssInjection } from \"@salt-ds/styles\";\nimport { useWindow } from \"@salt-ds/window\";\nimport { applyHighlighting } from \"@vuu-ui/vuu-table\";\nimport {\n DragDropProvider,\n ItemTypeName,\n pluralForm,\n singularForm,\n useSortable,\n} from \"@vuu-ui/vuu-utils\";\nimport cx from \"clsx\";\nimport {\n ForwardedRef,\n forwardRef,\n HTMLAttributes,\n MouseEventHandler,\n RefCallback,\n useCallback,\n useMemo,\n useRef,\n} from \"react\";\nimport { Icon, IconButton } from \"../icon-button\";\nimport {\n CreateCustomItemProps,\n getItemLabel,\n ItemDescriptor,\n ItemPickerHookProps,\n useItemPicker,\n} from \"./useItemPicker\";\n\nimport itemPickerCss from \"./ItemPicker.css\";\n\nconst classBase = \"vuuItemPicker\";\n\nexport const classBaseListItem = \"vuuItemPickerListItem\";\n\nexport interface ItemPickerProps\n extends ItemPickerHookProps,\n HTMLAttributes<HTMLDivElement>,\n Pick<ListBoxProps<ItemDescriptor>, \"selected\" | \"onSelectionChange\"> {\n itemTypeName: ItemTypeName;\n createCustomItemProps?: CreateCustomItemProps;\n}\n\nconst searchIcon = <Icon name=\"search\" />;\nconst NO_SELECTION: ItemDescriptor[] = [] as const;\n\nconst useSorting = (id: string, index: number) => {\n const { handleRef: sortableHandleRef, ref: sortableRef } = useSortable({\n id,\n index,\n });\n\n const [handleRef, ref] = useMemo(() => {\n return [sortableHandleRef, sortableRef];\n }, [sortableHandleRef, sortableRef]);\n\n return {\n handleRef,\n ref,\n };\n};\n\nconst SelectedListItem = ({\n className: classNameProp,\n index,\n item,\n onRemove,\n searchPattern = \"\",\n ...optionProps\n}: OptionProps & {\n index: number;\n item: ItemDescriptor;\n onRemove: MouseEventHandler<HTMLButtonElement>;\n searchPattern?: Lowercase<string>;\n}) => {\n const { handleRef, ref } = useSorting(item.name, index);\n const value = getItemLabel(item as ItemDescriptor);\n const valueWithHighlighting = applyHighlighting(value, searchPattern);\n\n const handleRemoveButtonClick = useCallback<\n MouseEventHandler<HTMLButtonElement>\n >(\n (e) => {\n e.stopPropagation();\n onRemove?.(e);\n },\n [onRemove],\n );\n\n return (\n <Option\n {...optionProps}\n className={cx(classNameProp, classBaseListItem)}\n data-name={item.name}\n ref={ref}\n >\n <IconButton\n data-embedded\n appearance=\"transparent\"\n icon=\"draggable\"\n ref={handleRef}\n size={16}\n />\n {item.icon ? <Icon name={item.icon} key=\"icon\" /> : null}\n <span className={`${classBase}-text`}>{valueWithHighlighting}</span>\n <IconButton\n className={`${classBaseListItem}-action`}\n data-embedded\n appearance=\"transparent\"\n icon=\"cross\"\n onClick={handleRemoveButtonClick}\n size={16}\n />\n </Option>\n );\n};\n\nconst AvailableListItem = ({\n className: classNameProp,\n index,\n item,\n onAdd,\n searchPattern = \"\",\n disabled,\n ...optionProps\n}: OptionProps & {\n index: number;\n item: ItemDescriptor;\n onAdd: MouseEventHandler<HTMLButtonElement>;\n searchPattern?: Lowercase<string>;\n}) => {\n const value = getItemLabel(item as ItemDescriptor);\n const valueWithHighlighting = applyHighlighting(value, searchPattern);\n\n return (\n <Option\n {...optionProps}\n className={cx(classNameProp, classBaseListItem)}\n data-name={item.name}\n disabled={disabled}\n >\n <span className={`${classBase}-text`}>{valueWithHighlighting}</span>\n <IconButton\n className={`${classBaseListItem}-action`}\n data-embedded\n appearance=\"transparent\"\n icon=\"plus\"\n onClick={onAdd}\n size={16}\n disabled={disabled}\n />\n </Option>\n );\n};\n\n/** Generic controlled component that displays items in a 'picker' that are available to search, select, reorder or deselect.\n * As input props, all available items, currently selected items, search text and callback functions for changes in selected items and search text\n * must be supplied by the client.\n */\nexport const ItemPicker = forwardRef(function ItemPicker(\n {\n className,\n itemTypeName,\n allItems,\n selectedItems,\n maxSelections,\n onSelectedItemsChange,\n onSelectionChange,\n selected = NO_SELECTION,\n createCustomItemProps,\n ...htmlAttributes\n }: ItemPickerProps,\n forwardedRef: ForwardedRef<HTMLDivElement>,\n) {\n const targetWindow = useWindow();\n useComponentCssInjection({\n testId: \"vuu-item-picker\",\n css: itemPickerCss,\n window: targetWindow,\n });\n\n const searchCallbackRef = useCallback<RefCallback<HTMLElement>>((el) => {\n setTimeout(() => {\n el?.querySelector(\"input\")?.focus();\n }, 100);\n }, []);\n\n const {\n selectedItemsCount,\n availableItemsCount,\n selectedItemsFiltered,\n availableItemsFiltered,\n searchText,\n onChangeSearchInput,\n onAddItemToSelectedList,\n onRemoveItemFromSelectedList,\n onReorderSelectedItems,\n } = useItemPicker({\n allItems,\n selectedItems,\n onSelectedItemsChange,\n maxSelections,\n });\n\n const listRef = useRef<HTMLDivElement>(null);\n\n const getOptionName = (option?: HTMLElement) => {\n if (option) {\n const { name } = option.dataset;\n if (name) {\n return name;\n }\n }\n throw Error(\"[ItemPicker] list option has no data-name\");\n };\n\n const handleDragEnd = useCallback(() => {\n setTimeout(() => {\n const listItems =\n listRef.current?.querySelectorAll<HTMLDivElement>(\".saltOption\");\n if (listItems) {\n const orderedItemNames = Array.from(listItems).map(getOptionName);\n onReorderSelectedItems(orderedItemNames);\n }\n }, 300);\n }, [availableItemsFiltered, selectedItemsFiltered]);\n\n const searchPlaceholderText = `Find ${singularForm(itemTypeName)}`;\n const maxSelectionsSubHeading = maxSelections ? `(${maxSelections} max)` : \"\";\n const selectedItemsHeading = `${selectedItemsCount} ${selectedItemsCount === 1 ? singularForm(itemTypeName) : pluralForm(itemTypeName)} in view ${maxSelectionsSubHeading}`;\n const availableItemsHeading = `${availableItemsCount} available ${availableItemsCount === 1 ? singularForm(itemTypeName) : pluralForm(itemTypeName)}`;\n\n return (\n <div\n {...htmlAttributes}\n className={cx(classBase, className)}\n ref={forwardedRef}\n >\n <form className={`${classBase}-search`} role=\"search\">\n <Input\n startAdornment={searchIcon}\n placeholder={searchPlaceholderText}\n ref={searchCallbackRef}\n value={searchText}\n onChange={onChangeSearchInput}\n />\n </form>\n\n <div className={`${classBase}-scrollContainer vuuScrollable`}>\n <div className={`${classBase}-sectionHeader`}>\n {selectedItemsHeading}\n </div>\n <DragDropProvider onDragEnd={handleDragEnd}>\n <ListBox\n className={`${classBase}-selectedList`}\n onSelectionChange={onSelectionChange}\n ref={listRef}\n selected={selected}\n >\n {selectedItemsFiltered.map((item, index) => (\n <SelectedListItem\n item={item}\n index={index}\n key={item.name}\n onRemove={onRemoveItemFromSelectedList}\n searchPattern={searchText.toLowerCase() as Lowercase<string>}\n value={item}\n />\n ))}\n </ListBox>\n </DragDropProvider>\n\n <div\n className={cx(\n `${classBase}-sectionHeader`,\n `${classBase}-availableHeader`,\n )}\n >\n {availableItemsHeading}\n </div>\n <ListBox\n className={`${classBase}-availableList`}\n selected={NO_SELECTION}\n >\n {availableItemsFiltered.map((item, index) => (\n <AvailableListItem\n item={item}\n index={index}\n key={item.name}\n onAdd={onAddItemToSelectedList}\n searchPattern={searchText.toLowerCase() as Lowercase<string>}\n value={item}\n disabled={selectedItemsCount === maxSelections}\n />\n ))}\n </ListBox>\n </div>\n {createCustomItemProps ? (\n <div className={`${classBase}-item-buttons`}>\n <Button onClick={createCustomItemProps.onClickCreateCustomItem}>\n {createCustomItemProps.buttonLabel}\n </Button>\n </div>\n ) : null}\n </div>\n );\n});\n"],"names":["ItemPicker"],"mappings":"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"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { reorderItems, queryClosest } from '@vuu-ui/vuu-utils';
|
|
2
|
+
import { useState, useCallback, useMemo } from 'react';
|
|
3
|
+
|
|
4
|
+
const filterItems = (items, pattern) => {
|
|
5
|
+
if (pattern) {
|
|
6
|
+
const lowerCasePattern = pattern.toLowerCase();
|
|
7
|
+
return items.filter(
|
|
8
|
+
(item) => getItemLabel(item).toLowerCase().indexOf(lowerCasePattern) !== -1
|
|
9
|
+
);
|
|
10
|
+
} else {
|
|
11
|
+
return items;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
const getItemLabel = (item) => {
|
|
15
|
+
if (item.label) return item.label;
|
|
16
|
+
else return item.name;
|
|
17
|
+
};
|
|
18
|
+
const itemName = (target) => {
|
|
19
|
+
const listItem = queryClosest(target, ".saltOption", true);
|
|
20
|
+
const { name } = listItem.dataset;
|
|
21
|
+
if (name) {
|
|
22
|
+
return name;
|
|
23
|
+
} else {
|
|
24
|
+
throw Error(
|
|
25
|
+
"[useItemPicker] item name could not be identified, data-name attribute not found"
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
const byItemName = ({ name: n1 }, { name: n2 }) => n1 > n2 ? 1 : n2 > n1 ? -1 : 0;
|
|
30
|
+
const useItemPicker = ({
|
|
31
|
+
allItems,
|
|
32
|
+
selectedItems,
|
|
33
|
+
maxSelections,
|
|
34
|
+
onSelectedItemsChange
|
|
35
|
+
}) => {
|
|
36
|
+
if (maxSelections && selectedItems.length > maxSelections) {
|
|
37
|
+
throw Error(
|
|
38
|
+
`[useItemPicker] max selections ${maxSelections} exceeded by selected items (that has count ${selectedItems.length})`
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
const [searchPattern, setSearchPattern] = useState("");
|
|
42
|
+
const handleChangeSearchInput = useCallback((evt) => {
|
|
43
|
+
const { value } = evt.target;
|
|
44
|
+
setSearchPattern(value);
|
|
45
|
+
}, []);
|
|
46
|
+
const handleAddItemToSelectedList = useCallback(
|
|
47
|
+
(e) => {
|
|
48
|
+
const name = itemName(e.target);
|
|
49
|
+
const itemToAdd = allItems.find((item) => item.name === name);
|
|
50
|
+
if (itemToAdd) {
|
|
51
|
+
const newSelectedItems = selectedItems.concat(itemToAdd);
|
|
52
|
+
onSelectedItemsChange(newSelectedItems);
|
|
53
|
+
} else {
|
|
54
|
+
throw Error(
|
|
55
|
+
`[useItemPicker] handleAddItemToSelectedList, item '${name}' not found`
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
[allItems, selectedItems, onSelectedItemsChange]
|
|
60
|
+
);
|
|
61
|
+
const handleRemoveItemFromSelectedList = useCallback(
|
|
62
|
+
(e) => {
|
|
63
|
+
const name = itemName(e.target);
|
|
64
|
+
const itemToRemove = selectedItems.find((item) => item.name === name);
|
|
65
|
+
if (itemToRemove) {
|
|
66
|
+
const newSelectedItems = selectedItems.filter(
|
|
67
|
+
(item) => item.name !== name
|
|
68
|
+
);
|
|
69
|
+
onSelectedItemsChange(newSelectedItems);
|
|
70
|
+
} else {
|
|
71
|
+
throw Error(
|
|
72
|
+
`[useItemPicker] handleRemoveItemFromSelectedList, item '${name}' not found`
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
[selectedItems, onSelectedItemsChange]
|
|
77
|
+
);
|
|
78
|
+
const handleReorderSelectedItems = useCallback(
|
|
79
|
+
(orderedItemNames) => {
|
|
80
|
+
const reorderedSelectedItems = reorderItems(
|
|
81
|
+
selectedItems,
|
|
82
|
+
orderedItemNames
|
|
83
|
+
);
|
|
84
|
+
onSelectedItemsChange(reorderedSelectedItems);
|
|
85
|
+
},
|
|
86
|
+
[selectedItems, onSelectedItemsChange]
|
|
87
|
+
);
|
|
88
|
+
const getSelectedItemsFiltered = useMemo(() => {
|
|
89
|
+
return filterItems(selectedItems, searchPattern);
|
|
90
|
+
}, [selectedItems, searchPattern]);
|
|
91
|
+
const getAvailableItemsFiltered = useMemo(() => {
|
|
92
|
+
return filterItems(allItems, searchPattern).filter(
|
|
93
|
+
({ name }) => selectedItems.findIndex((item) => item.name === name) === -1
|
|
94
|
+
).toSorted(byItemName);
|
|
95
|
+
}, [allItems, selectedItems, searchPattern]);
|
|
96
|
+
const getAvailableItemsCount = useMemo(() => {
|
|
97
|
+
return allItems.filter(
|
|
98
|
+
({ name }) => selectedItems.findIndex((item) => item.name === name) === -1
|
|
99
|
+
).length;
|
|
100
|
+
}, [allItems, selectedItems]);
|
|
101
|
+
return {
|
|
102
|
+
selectedItemsCount: selectedItems.length,
|
|
103
|
+
availableItemsCount: getAvailableItemsCount,
|
|
104
|
+
selectedItemsFiltered: getSelectedItemsFiltered,
|
|
105
|
+
availableItemsFiltered: getAvailableItemsFiltered,
|
|
106
|
+
searchText: searchPattern,
|
|
107
|
+
onChangeSearchInput: handleChangeSearchInput,
|
|
108
|
+
onAddItemToSelectedList: handleAddItemToSelectedList,
|
|
109
|
+
onRemoveItemFromSelectedList: handleRemoveItemFromSelectedList,
|
|
110
|
+
onReorderSelectedItems: handleReorderSelectedItems
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export { getItemLabel, useItemPicker };
|
|
115
|
+
//# sourceMappingURL=useItemPicker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useItemPicker.js","sources":["../../../../packages/vuu-ui-controls/src/item-picker/useItemPicker.tsx"],"sourcesContent":["import { queryClosest, reorderItems } from \"@vuu-ui/vuu-utils\";\nimport {\n FormEventHandler,\n MouseEventHandler,\n useCallback,\n useMemo,\n useState,\n} from \"react\";\n\n/** This is a public description of an Item that can be displayed in the ItemPicker component, defining all the\n * mandatory and option attributes that can be defined by the client. */\nexport interface ItemDescriptor {\n /** Unique name for this data value */\n name: string;\n /** Optional label for display instead of the name */\n label?: string;\n /** Optional data-icon name for display of an icon to the left of the item label/name in the selected list */\n icon?: string;\n /** Optional string to define groups of items for client reference, not currently used for rendering */\n group?: string;\n}\n\nexport interface CreateCustomItemProps {\n buttonLabel: string;\n onClickCreateCustomItem: MouseEventHandler<HTMLButtonElement>;\n}\n\nexport interface ItemPickerHookProps {\n allItems: ItemDescriptor[];\n selectedItems: ItemDescriptor[];\n maxSelections?: number;\n onSelectedItemsChange: (newSelectedItems: ItemDescriptor[]) => void;\n}\n\nconst filterItems = (\n items: readonly ItemDescriptor[],\n pattern: string,\n): readonly ItemDescriptor[] => {\n if (pattern) {\n const lowerCasePattern = pattern.toLowerCase();\n return items.filter(\n (item) =>\n getItemLabel(item).toLowerCase().indexOf(lowerCasePattern) !== -1,\n );\n } else {\n return items;\n }\n};\n\nexport const getItemLabel = (item: ItemDescriptor) => {\n if (item.label) return item.label;\n else return item.name;\n};\n\nconst itemName = (target: EventTarget): string => {\n const listItem = queryClosest(target, \".saltOption\", true);\n const { name } = listItem.dataset;\n if (name) {\n return name;\n } else {\n throw Error(\n \"[useItemPicker] item name could not be identified, data-name attribute not found\",\n );\n }\n};\n\nconst byItemName = (\n { name: n1 }: ItemDescriptor,\n { name: n2 }: ItemDescriptor,\n) => (n1 > n2 ? 1 : n2 > n1 ? -1 : 0);\n\nexport const useItemPicker = ({\n allItems,\n selectedItems,\n maxSelections,\n onSelectedItemsChange,\n}: ItemPickerHookProps) => {\n if (maxSelections && selectedItems.length > maxSelections) {\n throw Error(\n `[useItemPicker] max selections ${maxSelections} exceeded by selected items (that has count ${selectedItems.length})`,\n );\n }\n\n const [searchPattern, setSearchPattern] = useState(\"\");\n\n const handleChangeSearchInput = useCallback<FormEventHandler>((evt) => {\n const { value } = evt.target as HTMLInputElement;\n setSearchPattern(value);\n }, []);\n\n const handleAddItemToSelectedList = useCallback<\n MouseEventHandler<HTMLButtonElement>\n >(\n (e) => {\n const name = itemName(e.target);\n const itemToAdd = allItems.find((item) => item.name === name);\n if (itemToAdd) {\n const newSelectedItems = selectedItems.concat(itemToAdd);\n onSelectedItemsChange(newSelectedItems);\n } else {\n throw Error(\n `[useItemPicker] handleAddItemToSelectedList, item '${name}' not found`,\n );\n }\n },\n [allItems, selectedItems, onSelectedItemsChange],\n );\n\n const handleRemoveItemFromSelectedList = useCallback<\n MouseEventHandler<HTMLButtonElement>\n >(\n (e) => {\n const name = itemName(e.target);\n const itemToRemove = selectedItems.find((item) => item.name === name);\n if (itemToRemove) {\n const newSelectedItems = selectedItems.filter(\n (item) => item.name !== name,\n );\n onSelectedItemsChange(newSelectedItems);\n } else {\n throw Error(\n `[useItemPicker] handleRemoveItemFromSelectedList, item '${name}' not found`,\n );\n }\n },\n [selectedItems, onSelectedItemsChange],\n );\n\n const handleReorderSelectedItems = useCallback(\n (orderedItemNames: string[]) => {\n const reorderedSelectedItems: ItemDescriptor[] = reorderItems(\n selectedItems,\n orderedItemNames,\n );\n onSelectedItemsChange(reorderedSelectedItems);\n },\n [selectedItems, onSelectedItemsChange],\n );\n\n const getSelectedItemsFiltered = useMemo(() => {\n return filterItems(selectedItems, searchPattern);\n }, [selectedItems, searchPattern]);\n\n const getAvailableItemsFiltered = useMemo(() => {\n return filterItems(allItems, searchPattern)\n .filter(\n ({ name }) =>\n selectedItems.findIndex((item) => item.name === name) === -1,\n )\n .toSorted(byItemName);\n }, [allItems, selectedItems, searchPattern]);\n\n const getAvailableItemsCount = useMemo(() => {\n return allItems.filter(\n ({ name }) =>\n selectedItems.findIndex((item) => item.name === name) === -1,\n ).length;\n }, [allItems, selectedItems]);\n\n return {\n selectedItemsCount: selectedItems.length,\n availableItemsCount: getAvailableItemsCount,\n selectedItemsFiltered: getSelectedItemsFiltered,\n availableItemsFiltered: getAvailableItemsFiltered,\n searchText: searchPattern,\n onChangeSearchInput: handleChangeSearchInput,\n onAddItemToSelectedList: handleAddItemToSelectedList,\n onRemoveItemFromSelectedList: handleRemoveItemFromSelectedList,\n onReorderSelectedItems: handleReorderSelectedItems,\n };\n};\n"],"names":[],"mappings":"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"}
|
package/esm/prompt/Prompt.js
CHANGED
|
@@ -37,6 +37,7 @@ const Prompt = ({
|
|
|
37
37
|
confirmButtonLabel = "Confirm",
|
|
38
38
|
confirmButtonProps,
|
|
39
39
|
customAction = null,
|
|
40
|
+
disableDismiss,
|
|
40
41
|
disableAccent,
|
|
41
42
|
headerTag,
|
|
42
43
|
icon,
|
|
@@ -108,6 +109,7 @@ const Prompt = ({
|
|
|
108
109
|
{
|
|
109
110
|
...htmlAttributes,
|
|
110
111
|
className: cx(classBase, className),
|
|
112
|
+
disableDismiss,
|
|
111
113
|
onOpenChange,
|
|
112
114
|
open,
|
|
113
115
|
ref: callbackRef,
|
package/esm/prompt/Prompt.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Prompt.js","sources":["../../../../packages/vuu-ui-controls/src/prompt/Prompt.tsx"],"sourcesContent":["import {\n Button,\n Dialog,\n DialogActions,\n DialogContent,\n DialogHeader,\n DialogHeaderProps,\n DialogProps,\n} from \"@salt-ds/core\";\nimport cx from \"clsx\";\nimport {\n HTMLAttributes,\n MouseEventHandler,\n ReactNode,\n RefCallback,\n RefObject,\n useCallback,\n} from \"react\";\nimport { Icon, IconButton } from \"../icon-button\";\nimport { useComponentCssInjection } from \"@salt-ds/styles\";\nimport { useWindow } from \"@salt-ds/window\";\n\nimport promptCss from \"./Prompt.css\";\n\nconst classBase = \"vuuPrompt\";\n\n/**\n * Allow fine grained configuration of Prompt buttons\n * The ref is provided to facilitate programmatic focus.\n */\nexport interface PromptButtonProps {\n disabled?: boolean;\n label?: string;\n ref?: RefObject<HTMLButtonElement | null>;\n}\n\nconst getHeader = (\n title: string,\n icon?: string,\n headerTag?: ReactNode,\n): ReactNode => {\n if (icon && headerTag) {\n return (\n <>\n <Icon name={icon} />\n <span>{title}</span>\n {headerTag}\n </>\n );\n } else if (icon) {\n return (\n <>\n <Icon name={icon} />\n <span>{title}</span>\n </>\n );\n } else if (headerTag) {\n return (\n <>\n <span>{title}</span>\n {headerTag}\n </>\n );\n }\n\n return title;\n};\n\nexport interface PromptProps\n extends Pick<DialogProps, \"onOpenChange\" | \"open\" | \"status\">,\n Pick<DialogHeaderProps, \"disableAccent\">,\n Omit<HTMLAttributes<HTMLDivElement>, \"content\" | \"title\"> {\n /**\n * For simple configuration, where just a cancel button label is required.\n */\n cancelButtonLabel?: string;\n /**\n * Allow fine grained configuration of cancel button\n */\n cancelButtonProps?: PromptButtonProps;\n /**\n * For simple configuration, where just a confirm button label is required.\n */\n confirmButtonLabel?: string;\n /**\n * Allow fine grained configuration of confirm button\n */\n confirmButtonProps?: PromptButtonProps;\n\n /**\n * A custom action will be displayed in Prompt button bar, before cancel/confirm buttons.\n */\n customAction?: ReactNode;\n\n /**\n * Intended to allow for the display of a status tag or badge in the header. It will be displayed inline\n * after the title. A small component is expected.\n */\n headerTag?: ReactNode;\n\n icon?: string;\n /**\n * Set this prop if one of the three built-in buttons should receive initial focus.\n * Allows user to quickly dismiss/confirm prompt from keyboard. If the prompt content\n * contains focusable item(s), focus should be controlled by caller.\n */\n initialFocusedItem?: \"confirm\" | \"cancel\" | \"close\";\n onCancel?: () => void;\n onConfirm?: () => void | false;\n onClose?: () => void;\n showCancelButton?: boolean;\n showCloseButton?: boolean;\n showConfirmButton?: boolean;\n title: string;\n}\n\nexport const Prompt = ({\n children,\n className,\n cancelButtonLabel = \"Cancel\",\n cancelButtonProps,\n confirmButtonLabel = \"Confirm\",\n confirmButtonProps,\n customAction = null,\n disableAccent,\n headerTag,\n icon,\n initialFocusedItem,\n onCancel,\n onClose,\n onConfirm,\n onOpenChange,\n open,\n showCancelButton = true,\n showCloseButton = true,\n showConfirmButton = true,\n status,\n title,\n\n ...htmlAttributes\n}: PromptProps) => {\n const targetWindow = useWindow();\n useComponentCssInjection({\n testId: \"vuu-prompt-next\",\n css: promptCss,\n window: targetWindow,\n });\n\n const close = useCallback(() => {\n onClose?.();\n onOpenChange?.(false);\n }, [onClose, onOpenChange]);\n\n const callbackRef = useCallback<RefCallback<HTMLDivElement>>(\n (el) => {\n if (el) {\n let target: HTMLButtonElement | null = null;\n if (initialFocusedItem === \"confirm\" && showConfirmButton) {\n target = el.querySelector(\".vuuPromptConfirmButton\");\n } else if (initialFocusedItem === \"cancel\" && showCancelButton) {\n target = el.querySelector(\".vuuPromptCancelButton\");\n } else if (initialFocusedItem === \"close\" && showCloseButton) {\n target = el.querySelector(\".vuuPromptCloseButton\");\n }\n\n if (target) {\n setTimeout(() => {\n target.focus();\n }, 200);\n }\n }\n },\n [initialFocusedItem, showCancelButton, showCloseButton, showConfirmButton],\n );\n\n const handleCancel = useCallback<MouseEventHandler<HTMLButtonElement>>(() => {\n onCancel?.();\n close();\n }, [close, onCancel]);\n\n const handleConfirm = useCallback<\n MouseEventHandler<HTMLButtonElement>\n >(() => {\n if (onConfirm?.() !== false) {\n close();\n }\n }, [close, onConfirm]);\n\n const actions = showCloseButton ? (\n <IconButton\n appearance=\"transparent\"\n className=\"vuuPromptCloseButton\"\n data-embedded\n icon=\"close\"\n onClick={close}\n />\n ) : null;\n\n const header = getHeader(title, icon, headerTag);\n\n return (\n <Dialog\n {...htmlAttributes}\n className={cx(classBase, className)}\n onOpenChange={onOpenChange}\n open={open}\n ref={callbackRef}\n status={status}\n >\n <DialogHeader\n disableAccent={disableAccent}\n header={header}\n actions={actions}\n />\n <DialogContent>{children}</DialogContent>\n <DialogActions>\n {customAction}\n {showCancelButton ? (\n <Button\n appearance=\"solid\"\n className=\"vuuPromptCancelButton\"\n disabled={cancelButtonProps?.disabled}\n onClick={handleCancel}\n ref={cancelButtonProps?.ref}\n sentiment=\"neutral\"\n >\n {cancelButtonProps?.label ?? cancelButtonLabel}\n </Button>\n ) : null}\n {showConfirmButton ? (\n <Button\n appearance=\"solid\"\n className=\"vuuPromptConfirmButton\"\n disabled={confirmButtonProps?.disabled}\n sentiment=\"accented\"\n onClick={handleConfirm}\n ref={confirmButtonProps?.ref}\n >\n {confirmButtonProps?.label ?? confirmButtonLabel}\n </Button>\n ) : null}\n </DialogActions>\n </Dialog>\n );\n};\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Prompt.js","sources":["../../../../packages/vuu-ui-controls/src/prompt/Prompt.tsx"],"sourcesContent":["import {\n Button,\n Dialog,\n DialogActions,\n DialogContent,\n DialogHeader,\n DialogHeaderProps,\n DialogProps,\n} from \"@salt-ds/core\";\nimport cx from \"clsx\";\nimport {\n HTMLAttributes,\n MouseEventHandler,\n ReactNode,\n RefCallback,\n RefObject,\n useCallback,\n} from \"react\";\nimport { Icon, IconButton } from \"../icon-button\";\nimport { useComponentCssInjection } from \"@salt-ds/styles\";\nimport { useWindow } from \"@salt-ds/window\";\n\nimport promptCss from \"./Prompt.css\";\n\nconst classBase = \"vuuPrompt\";\n\n/**\n * Allow fine grained configuration of Prompt buttons\n * The ref is provided to facilitate programmatic focus.\n */\nexport interface PromptButtonProps {\n disabled?: boolean;\n label?: string;\n ref?: RefObject<HTMLButtonElement | null>;\n}\n\nconst getHeader = (\n title: string,\n icon?: string,\n headerTag?: ReactNode,\n): ReactNode => {\n if (icon && headerTag) {\n return (\n <>\n <Icon name={icon} />\n <span>{title}</span>\n {headerTag}\n </>\n );\n } else if (icon) {\n return (\n <>\n <Icon name={icon} />\n <span>{title}</span>\n </>\n );\n } else if (headerTag) {\n return (\n <>\n <span>{title}</span>\n {headerTag}\n </>\n );\n }\n\n return title;\n};\n\nexport interface PromptProps\n extends Pick<DialogProps, \"disableDismiss\" | \"onOpenChange\" | \"open\" | \"status\">,\n Pick<DialogHeaderProps, \"disableAccent\">,\n Omit<HTMLAttributes<HTMLDivElement>, \"content\" | \"title\"> {\n /**\n * For simple configuration, where just a cancel button label is required.\n */\n cancelButtonLabel?: string;\n /**\n * Allow fine grained configuration of cancel button\n */\n cancelButtonProps?: PromptButtonProps;\n /**\n * For simple configuration, where just a confirm button label is required.\n */\n confirmButtonLabel?: string;\n /**\n * Allow fine grained configuration of confirm button\n */\n confirmButtonProps?: PromptButtonProps;\n\n /**\n * A custom action will be displayed in Prompt button bar, before cancel/confirm buttons.\n */\n customAction?: ReactNode;\n\n /**\n * Intended to allow for the display of a status tag or badge in the header. It will be displayed inline\n * after the title. A small component is expected.\n */\n headerTag?: ReactNode;\n\n icon?: string;\n /**\n * Set this prop if one of the three built-in buttons should receive initial focus.\n * Allows user to quickly dismiss/confirm prompt from keyboard. If the prompt content\n * contains focusable item(s), focus should be controlled by caller.\n */\n initialFocusedItem?: \"confirm\" | \"cancel\" | \"close\";\n onCancel?: () => void;\n onConfirm?: () => void | false;\n onClose?: () => void;\n showCancelButton?: boolean;\n showCloseButton?: boolean;\n showConfirmButton?: boolean;\n title: string;\n}\n\nexport const Prompt = ({\n children,\n className,\n cancelButtonLabel = \"Cancel\",\n cancelButtonProps,\n confirmButtonLabel = \"Confirm\",\n confirmButtonProps,\n customAction = null,\n disableDismiss,\n disableAccent,\n headerTag,\n icon,\n initialFocusedItem,\n onCancel,\n onClose,\n onConfirm,\n onOpenChange,\n open,\n showCancelButton = true,\n showCloseButton = true,\n showConfirmButton = true,\n status,\n title,\n\n ...htmlAttributes\n}: PromptProps) => {\n const targetWindow = useWindow();\n useComponentCssInjection({\n testId: \"vuu-prompt-next\",\n css: promptCss,\n window: targetWindow,\n });\n\n const close = useCallback(() => {\n onClose?.();\n onOpenChange?.(false);\n }, [onClose, onOpenChange]);\n\n const callbackRef = useCallback<RefCallback<HTMLDivElement>>(\n (el) => {\n if (el) {\n let target: HTMLButtonElement | null = null;\n if (initialFocusedItem === \"confirm\" && showConfirmButton) {\n target = el.querySelector(\".vuuPromptConfirmButton\");\n } else if (initialFocusedItem === \"cancel\" && showCancelButton) {\n target = el.querySelector(\".vuuPromptCancelButton\");\n } else if (initialFocusedItem === \"close\" && showCloseButton) {\n target = el.querySelector(\".vuuPromptCloseButton\");\n }\n\n if (target) {\n setTimeout(() => {\n target.focus();\n }, 200);\n }\n }\n },\n [initialFocusedItem, showCancelButton, showCloseButton, showConfirmButton],\n );\n\n const handleCancel = useCallback<MouseEventHandler<HTMLButtonElement>>(() => {\n onCancel?.();\n close();\n }, [close, onCancel]);\n\n const handleConfirm = useCallback<\n MouseEventHandler<HTMLButtonElement>\n >(() => {\n if (onConfirm?.() !== false) {\n close();\n }\n }, [close, onConfirm]);\n\n const actions = showCloseButton ? (\n <IconButton\n appearance=\"transparent\"\n className=\"vuuPromptCloseButton\"\n data-embedded\n icon=\"close\"\n onClick={close}\n />\n ) : null;\n\n const header = getHeader(title, icon, headerTag);\n\n return (\n <Dialog\n {...htmlAttributes}\n className={cx(classBase, className)}\n disableDismiss={disableDismiss}\n onOpenChange={onOpenChange}\n open={open}\n ref={callbackRef}\n status={status}\n >\n <DialogHeader\n disableAccent={disableAccent}\n header={header}\n actions={actions}\n />\n <DialogContent>{children}</DialogContent>\n <DialogActions>\n {customAction}\n {showCancelButton ? (\n <Button\n appearance=\"solid\"\n className=\"vuuPromptCancelButton\"\n disabled={cancelButtonProps?.disabled}\n onClick={handleCancel}\n ref={cancelButtonProps?.ref}\n sentiment=\"neutral\"\n >\n {cancelButtonProps?.label ?? cancelButtonLabel}\n </Button>\n ) : null}\n {showConfirmButton ? (\n <Button\n appearance=\"solid\"\n className=\"vuuPromptConfirmButton\"\n disabled={confirmButtonProps?.disabled}\n sentiment=\"accented\"\n onClick={handleConfirm}\n ref={confirmButtonProps?.ref}\n >\n {confirmButtonProps?.label ?? confirmButtonLabel}\n </Button>\n ) : null}\n </DialogActions>\n </Dialog>\n );\n};\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "
|
|
2
|
+
"version": "3.0.0-beta.1",
|
|
3
3
|
"description": "VUU UI Controls",
|
|
4
4
|
"author": "heswell",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"devDependencies": {
|
|
7
|
-
"@vuu-ui/vuu-data-types": "
|
|
8
|
-
"@vuu-ui/vuu-protocol-types": "
|
|
9
|
-
"@vuu-ui/vuu-table-types": "
|
|
7
|
+
"@vuu-ui/vuu-data-types": "3.0.0-beta.1",
|
|
8
|
+
"@vuu-ui/vuu-protocol-types": "3.0.0-beta.1",
|
|
9
|
+
"@vuu-ui/vuu-table-types": "3.0.0-beta.1"
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@vuu-ui/vuu-context-menu": "
|
|
13
|
-
"@vuu-ui/vuu-data-react": "
|
|
14
|
-
"@vuu-ui/vuu-layout": "
|
|
15
|
-
"@vuu-ui/vuu-popups": "
|
|
16
|
-
"@vuu-ui/vuu-table": "
|
|
17
|
-
"@vuu-ui/vuu-utils": "
|
|
12
|
+
"@vuu-ui/vuu-context-menu": "3.0.0-beta.1",
|
|
13
|
+
"@vuu-ui/vuu-data-react": "3.0.0-beta.1",
|
|
14
|
+
"@vuu-ui/vuu-layout": "3.0.0-beta.1",
|
|
15
|
+
"@vuu-ui/vuu-popups": "3.0.0-beta.1",
|
|
16
|
+
"@vuu-ui/vuu-table": "3.0.0-beta.1",
|
|
17
|
+
"@vuu-ui/vuu-utils": "3.0.0-beta.1",
|
|
18
18
|
"@salt-ds/core": "1.54.1",
|
|
19
19
|
"@salt-ds/icons": "1.16.0",
|
|
20
20
|
"@salt-ds/styles": "0.2.1",
|
package/types/index.d.ts
CHANGED
|
@@ -12,6 +12,8 @@ export * from "./expando-input";
|
|
|
12
12
|
export { HoverOverlay } from "./hover-overlay/HoverOverlay";
|
|
13
13
|
export * from "./icon-button";
|
|
14
14
|
export * from "./instrument-picker";
|
|
15
|
+
export { ItemPicker, type ItemPickerProps } from "./item-picker/ItemPicker";
|
|
16
|
+
export { type ItemDescriptor, type CreateCustomItemProps, } from "./item-picker/useItemPicker";
|
|
15
17
|
export * from "./measured-container";
|
|
16
18
|
export { ModalProvider, useModal } from "./modal-provider/ModalProvider";
|
|
17
19
|
export * from "./overflow-container";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ListBoxProps } from "@salt-ds/core";
|
|
2
|
+
import { ItemTypeName } from "@vuu-ui/vuu-utils";
|
|
3
|
+
import { HTMLAttributes } from "react";
|
|
4
|
+
import { CreateCustomItemProps, ItemDescriptor, ItemPickerHookProps } from "./useItemPicker";
|
|
5
|
+
export declare const classBaseListItem = "vuuItemPickerListItem";
|
|
6
|
+
export interface ItemPickerProps extends ItemPickerHookProps, HTMLAttributes<HTMLDivElement>, Pick<ListBoxProps<ItemDescriptor>, "selected" | "onSelectionChange"> {
|
|
7
|
+
itemTypeName: ItemTypeName;
|
|
8
|
+
createCustomItemProps?: CreateCustomItemProps;
|
|
9
|
+
}
|
|
10
|
+
/** Generic controlled component that displays items in a 'picker' that are available to search, select, reorder or deselect.
|
|
11
|
+
* As input props, all available items, currently selected items, search text and callback functions for changes in selected items and search text
|
|
12
|
+
* must be supplied by the client.
|
|
13
|
+
*/
|
|
14
|
+
export declare const ItemPicker: import("react").ForwardRefExoticComponent<ItemPickerProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { FormEventHandler, MouseEventHandler } from "react";
|
|
2
|
+
/** This is a public description of an Item that can be displayed in the ItemPicker component, defining all the
|
|
3
|
+
* mandatory and option attributes that can be defined by the client. */
|
|
4
|
+
export interface ItemDescriptor {
|
|
5
|
+
/** Unique name for this data value */
|
|
6
|
+
name: string;
|
|
7
|
+
/** Optional label for display instead of the name */
|
|
8
|
+
label?: string;
|
|
9
|
+
/** Optional data-icon name for display of an icon to the left of the item label/name in the selected list */
|
|
10
|
+
icon?: string;
|
|
11
|
+
/** Optional string to define groups of items for client reference, not currently used for rendering */
|
|
12
|
+
group?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface CreateCustomItemProps {
|
|
15
|
+
buttonLabel: string;
|
|
16
|
+
onClickCreateCustomItem: MouseEventHandler<HTMLButtonElement>;
|
|
17
|
+
}
|
|
18
|
+
export interface ItemPickerHookProps {
|
|
19
|
+
allItems: ItemDescriptor[];
|
|
20
|
+
selectedItems: ItemDescriptor[];
|
|
21
|
+
maxSelections?: number;
|
|
22
|
+
onSelectedItemsChange: (newSelectedItems: ItemDescriptor[]) => void;
|
|
23
|
+
}
|
|
24
|
+
export declare const getItemLabel: (item: ItemDescriptor) => string;
|
|
25
|
+
export declare const useItemPicker: ({ allItems, selectedItems, maxSelections, onSelectedItemsChange, }: ItemPickerHookProps) => {
|
|
26
|
+
selectedItemsCount: number;
|
|
27
|
+
availableItemsCount: number;
|
|
28
|
+
selectedItemsFiltered: readonly ItemDescriptor[];
|
|
29
|
+
availableItemsFiltered: ItemDescriptor[];
|
|
30
|
+
searchText: string;
|
|
31
|
+
onChangeSearchInput: FormEventHandler;
|
|
32
|
+
onAddItemToSelectedList: MouseEventHandler<HTMLButtonElement>;
|
|
33
|
+
onRemoveItemFromSelectedList: MouseEventHandler<HTMLButtonElement>;
|
|
34
|
+
onReorderSelectedItems: (orderedItemNames: string[]) => void;
|
|
35
|
+
};
|
package/types/prompt/Prompt.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export interface PromptButtonProps {
|
|
|
9
9
|
label?: string;
|
|
10
10
|
ref?: RefObject<HTMLButtonElement | null>;
|
|
11
11
|
}
|
|
12
|
-
export interface PromptProps extends Pick<DialogProps, "onOpenChange" | "open" | "status">, Pick<DialogHeaderProps, "disableAccent">, Omit<HTMLAttributes<HTMLDivElement>, "content" | "title"> {
|
|
12
|
+
export interface PromptProps extends Pick<DialogProps, "disableDismiss" | "onOpenChange" | "open" | "status">, Pick<DialogHeaderProps, "disableAccent">, Omit<HTMLAttributes<HTMLDivElement>, "content" | "title"> {
|
|
13
13
|
/**
|
|
14
14
|
* For simple configuration, where just a cancel button label is required.
|
|
15
15
|
*/
|
|
@@ -50,4 +50,4 @@ export interface PromptProps extends Pick<DialogProps, "onOpenChange" | "open" |
|
|
|
50
50
|
showConfirmButton?: boolean;
|
|
51
51
|
title: string;
|
|
52
52
|
}
|
|
53
|
-
export declare const Prompt: ({ children, className, cancelButtonLabel, cancelButtonProps, confirmButtonLabel, confirmButtonProps, customAction, disableAccent, headerTag, icon, initialFocusedItem, onCancel, onClose, onConfirm, onOpenChange, open, showCancelButton, showCloseButton, showConfirmButton, status, title, ...htmlAttributes }: PromptProps) => import("react/jsx-runtime").JSX.Element;
|
|
53
|
+
export declare const Prompt: ({ children, className, cancelButtonLabel, cancelButtonProps, confirmButtonLabel, confirmButtonProps, customAction, disableDismiss, disableAccent, headerTag, icon, initialFocusedItem, onCancel, onClose, onConfirm, onOpenChange, open, showCancelButton, showCloseButton, showConfirmButton, status, title, ...htmlAttributes }: PromptProps) => import("react/jsx-runtime").JSX.Element;
|