@vuu-ui/vuu-ui-controls 4.0.0-alpha.2 → 4.0.0-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "4.0.0-alpha.2",
2
+ "version": "4.0.0-alpha.4",
3
3
  "description": "VUU UI Controls",
4
4
  "main": "./src/index.js",
5
5
  "author": "heswell",
@@ -11,16 +11,16 @@
11
11
  "type-defs": "node ../../scripts/build-type-defs.mjs"
12
12
  },
13
13
  "devDependencies": {
14
- "@vuu-ui/vuu-data-types": "4.0.0-alpha.2",
15
- "@vuu-ui/vuu-protocol-types": "4.0.0-alpha.2",
16
- "@vuu-ui/vuu-table-types": "4.0.0-alpha.2"
14
+ "@vuu-ui/vuu-data-types": "4.0.0-alpha.4",
15
+ "@vuu-ui/vuu-protocol-types": "4.0.0-alpha.4",
16
+ "@vuu-ui/vuu-table-types": "4.0.0-alpha.4"
17
17
  },
18
18
  "dependencies": {
19
- "@vuu-ui/vuu-context-menu": "4.0.0-alpha.2",
20
- "@vuu-ui/vuu-data-react": "4.0.0-alpha.2",
21
- "@vuu-ui/vuu-popups": "4.0.0-alpha.2",
22
- "@vuu-ui/vuu-table": "4.0.0-alpha.2",
23
- "@vuu-ui/vuu-utils": "4.0.0-alpha.2",
19
+ "@vuu-ui/vuu-context-menu": "4.0.0-alpha.4",
20
+ "@vuu-ui/vuu-data-react": "4.0.0-alpha.4",
21
+ "@vuu-ui/vuu-popups": "4.0.0-alpha.4",
22
+ "@vuu-ui/vuu-table": "4.0.0-alpha.4",
23
+ "@vuu-ui/vuu-utils": "4.0.0-alpha.4",
24
24
  "@salt-ds/core": "1.67.0",
25
25
  "@salt-ds/icons": "1.16.0",
26
26
  "@salt-ds/styles": "0.2.1",
package/src/index.js CHANGED
@@ -25,6 +25,7 @@ export * from "./vuu-typeahead-input/index.js";
25
25
  export { ContextPanelProvider, useContextPanel, useHideContextPanel } from "./context-panel-provider/ContextPanelProvider.js";
26
26
  export { HoverOverlay } from "./hover-overlay/HoverOverlay.js";
27
27
  export { ItemPicker } from "./item-picker/ItemPicker.js";
28
+ export { SelectedItemsChangeHandler as SelectedItemChangeHandler } from "./item-picker/useItemPicker.js";
28
29
  export { ModalProvider, useModal } from "./modal-provider/ModalProvider.js";
29
30
  export { Prompt } from "./prompt/Prompt.js";
30
31
  export { SortableList, SortableOption } from "./sortable-list/SortableList.js";
@@ -11,10 +11,6 @@ const css = `
11
11
  overflow: hidden;
12
12
  container-type: inline-size;
13
13
 
14
- &.vuuItemPicker-withCalculated {
15
- --label-margin-left: 8px;
16
- }
17
-
18
14
  & .saltListBox {
19
15
  background-color: inherit;
20
16
  }
@@ -1,5 +1,5 @@
1
- import { jsx, jsxs } from "react/jsx-runtime";
2
- import { Button, Input, ListBox, Option } from "@salt-ds/core";
1
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ import { Input, ListBox, Option } from "@salt-ds/core";
3
3
  import { useComponentCssInjection } from "@salt-ds/styles";
4
4
  import { useWindow } from "@salt-ds/window";
5
5
  import { applyHighlighting } from "@vuu-ui/vuu-table";
@@ -32,7 +32,7 @@ const useSorting = (id, index)=>{
32
32
  ref
33
33
  };
34
34
  };
35
- const SelectedListItem = ({ className: classNameProp, index, item, onRemove, searchPattern = "", ...optionProps })=>{
35
+ const DefaultSelectedListItem = ({ className: classNameProp, index, item, onRemove, searchPattern = "", ...optionProps })=>{
36
36
  const { handleRef, ref } = useSorting(item.name, index);
37
37
  const value = getItemLabel(item);
38
38
  const valueWithHighlighting = applyHighlighting(value, searchPattern);
@@ -73,7 +73,7 @@ const SelectedListItem = ({ className: classNameProp, index, item, onRemove, sea
73
73
  ]
74
74
  });
75
75
  };
76
- const AvailableListItem = ({ className: classNameProp, index, item, onAdd, searchPattern = "", disabled, ...optionProps })=>{
76
+ const DefaultAvailableListItem = ({ className: classNameProp, index, item, onAdd, searchPattern = "", disabled, ...optionProps })=>{
77
77
  const value = getItemLabel(item);
78
78
  const valueWithHighlighting = applyHighlighting(value, searchPattern);
79
79
  return /*#__PURE__*/ jsxs(Option, {
@@ -98,7 +98,14 @@ const AvailableListItem = ({ className: classNameProp, index, item, onAdd, searc
98
98
  ]
99
99
  });
100
100
  };
101
- const ItemPicker_ItemPicker = /*#__PURE__*/ forwardRef(function({ className, itemTypeName, searchForm = true, allItems, selectedItems, maxSelections, onSelectedItemsChange, onSelectionChange, selected = NO_SELECTION, createCustomItemProps, ...htmlAttributes }, forwardedRef) {
101
+ const getOptionName = (option)=>{
102
+ if (option) {
103
+ const { name } = option.dataset;
104
+ if (name) return name;
105
+ }
106
+ throw Error("[ItemPicker] list option has no data-name");
107
+ };
108
+ const ItemPicker_ItemPicker = /*#__PURE__*/ forwardRef(function({ AvailableListItem = DefaultAvailableListItem, SelectedListItem = DefaultSelectedListItem, className, itemTypeName = 'Item', searchForm = true, allItems, layout = 'v-selected-available', selectedItems, maxSelections, onSelectedItemsChange, onSelectionChange, selected = NO_SELECTION, ...htmlAttributes }, forwardedRef) {
102
109
  const targetWindow = useWindow();
103
110
  useComponentCssInjection({
104
111
  testId: "vuu-item-picker",
@@ -117,13 +124,6 @@ const ItemPicker_ItemPicker = /*#__PURE__*/ forwardRef(function({ className, ite
117
124
  maxSelections
118
125
  });
119
126
  const listRef = useRef(null);
120
- const getOptionName = (option)=>{
121
- if (option) {
122
- const { name } = option.dataset;
123
- if (name) return name;
124
- }
125
- throw Error("[ItemPicker] list option has no data-name");
126
- };
127
127
  const handleDragEnd = useCallback(()=>{
128
128
  setTimeout(()=>{
129
129
  const listItems = listRef.current?.querySelectorAll(".saltOption");
@@ -133,13 +133,56 @@ const ItemPicker_ItemPicker = /*#__PURE__*/ forwardRef(function({ className, ite
133
133
  }
134
134
  }, 300);
135
135
  }, [
136
- availableItemsFiltered,
137
- selectedItemsFiltered
136
+ onReorderSelectedItems
138
137
  ]);
139
138
  const searchPlaceholderText = `Find ${singularForm(itemTypeName)}`;
140
139
  const maxSelectionsSubHeading = maxSelections ? `(${maxSelections} max)` : "";
141
140
  const selectedItemsHeading = `${selectedItemsCount} ${1 === selectedItemsCount ? singularForm(itemTypeName) : pluralForm(itemTypeName)} in view ${maxSelectionsSubHeading}`;
142
141
  const availableItemsHeading = `${availableItemsCount} available ${1 === availableItemsCount ? singularForm(itemTypeName) : pluralForm(itemTypeName)}`;
142
+ const selectedList = /*#__PURE__*/ jsxs(Fragment, {
143
+ children: [
144
+ /*#__PURE__*/ jsx("div", {
145
+ className: `${classBase}-sectionHeader`,
146
+ children: selectedItemsHeading
147
+ }),
148
+ /*#__PURE__*/ jsx(DragDropProvider, {
149
+ onDragEnd: handleDragEnd,
150
+ children: /*#__PURE__*/ jsx(ListBox, {
151
+ className: `${classBase}-selectedList`,
152
+ onSelectionChange: onSelectionChange,
153
+ ref: listRef,
154
+ selected: selected,
155
+ children: selectedItemsFiltered.map((item, index)=>/*#__PURE__*/ jsx(SelectedListItem, {
156
+ item: item,
157
+ index: index,
158
+ onRemove: onRemoveItemFromSelectedList,
159
+ searchPattern: searchText.toLowerCase(),
160
+ value: item
161
+ }, item.name))
162
+ })
163
+ })
164
+ ]
165
+ });
166
+ const availableList = /*#__PURE__*/ jsxs(Fragment, {
167
+ children: [
168
+ /*#__PURE__*/ jsx("div", {
169
+ className: clsx(`${classBase}-sectionHeader`, `${classBase}-availableHeader`),
170
+ children: availableItemsHeading
171
+ }),
172
+ /*#__PURE__*/ jsx(ListBox, {
173
+ className: `${classBase}-availableList`,
174
+ selected: NO_SELECTION,
175
+ children: availableItemsFiltered.map((item, index)=>/*#__PURE__*/ jsx(AvailableListItem, {
176
+ item: item,
177
+ index: index,
178
+ onAdd: onAddItemToSelectedList,
179
+ searchPattern: searchText.toLowerCase(),
180
+ value: item,
181
+ disabled: selectedItemsCount === maxSelections
182
+ }, item.name))
183
+ })
184
+ ]
185
+ });
143
186
  return /*#__PURE__*/ jsxs("div", {
144
187
  ...htmlAttributes,
145
188
  className: clsx(classBase, className),
@@ -166,54 +209,20 @@ const ItemPicker_ItemPicker = /*#__PURE__*/ forwardRef(function({ className, ite
166
209
  onChange: onChangeSearchInput
167
210
  })
168
211
  }),
169
- /*#__PURE__*/ jsxs("div", {
212
+ /*#__PURE__*/ jsx("div", {
170
213
  className: `${classBase}-scrollContainer vuuScrollable`,
171
- children: [
172
- /*#__PURE__*/ jsx("div", {
173
- className: `${classBase}-sectionHeader`,
174
- children: selectedItemsHeading
175
- }),
176
- /*#__PURE__*/ jsx(DragDropProvider, {
177
- onDragEnd: handleDragEnd,
178
- children: /*#__PURE__*/ jsx(ListBox, {
179
- className: `${classBase}-selectedList`,
180
- onSelectionChange: onSelectionChange,
181
- ref: listRef,
182
- selected: selected,
183
- children: selectedItemsFiltered.map((item, index)=>/*#__PURE__*/ jsx(SelectedListItem, {
184
- item: item,
185
- index: index,
186
- onRemove: onRemoveItemFromSelectedList,
187
- searchPattern: searchText.toLowerCase(),
188
- value: item
189
- }, item.name))
190
- })
191
- }),
192
- /*#__PURE__*/ jsx("div", {
193
- className: clsx(`${classBase}-sectionHeader`, `${classBase}-availableHeader`),
194
- children: availableItemsHeading
195
- }),
196
- /*#__PURE__*/ jsx(ListBox, {
197
- className: `${classBase}-availableList`,
198
- selected: NO_SELECTION,
199
- children: availableItemsFiltered.map((item, index)=>/*#__PURE__*/ jsx(AvailableListItem, {
200
- item: item,
201
- index: index,
202
- onAdd: onAddItemToSelectedList,
203
- searchPattern: searchText.toLowerCase(),
204
- value: item,
205
- disabled: selectedItemsCount === maxSelections
206
- }, item.name))
207
- })
208
- ]
209
- }),
210
- createCustomItemProps ? /*#__PURE__*/ jsx("div", {
211
- className: `${classBase}-item-buttons`,
212
- children: /*#__PURE__*/ jsx(Button, {
213
- onClick: createCustomItemProps.onClickCreateCustomItem,
214
- children: createCustomItemProps.buttonLabel
214
+ children: 'v-selected-available' === layout ? /*#__PURE__*/ jsxs(Fragment, {
215
+ children: [
216
+ selectedList,
217
+ availableList
218
+ ]
219
+ }) : /*#__PURE__*/ jsxs(Fragment, {
220
+ children: [
221
+ availableList,
222
+ selectedList
223
+ ]
215
224
  })
216
- }) : null
225
+ })
217
226
  ]
218
227
  });
219
228
  });
@@ -55,10 +55,7 @@ const useItemPicker = ({ allItems, selectedItems, maxSelections, onSelectedItems
55
55
  selectedItems,
56
56
  onSelectedItemsChange
57
57
  ]);
58
- const getSelectedItemsFiltered = useMemo(()=>filterItems(selectedItems, searchPattern), [
59
- selectedItems,
60
- searchPattern
61
- ]);
58
+ const getSelectedItemsFiltered = (latestSelectedItems, latestSearchPattern)=>filterItems(latestSelectedItems, latestSearchPattern);
62
59
  const getAvailableItemsFiltered = useMemo(()=>filterItems(allItems, searchPattern).filter(({ name })=>-1 === selectedItems.findIndex((item)=>item.name === name)).toSorted(byItemName), [
63
60
  allItems,
64
61
  selectedItems,
@@ -71,7 +68,7 @@ const useItemPicker = ({ allItems, selectedItems, maxSelections, onSelectedItems
71
68
  return {
72
69
  selectedItemsCount: selectedItems.length,
73
70
  availableItemsCount: getAvailableItemsCount,
74
- selectedItemsFiltered: getSelectedItemsFiltered,
71
+ selectedItemsFiltered: getSelectedItemsFiltered(selectedItems, searchPattern),
75
72
  availableItemsFiltered: getAvailableItemsFiltered,
76
73
  searchText: searchPattern,
77
74
  onChangeSearchInput: handleChangeSearchInput,
package/types/index.d.ts CHANGED
@@ -13,7 +13,7 @@ export { HoverOverlay } from "./hover-overlay/HoverOverlay";
13
13
  export * from "./icon-button";
14
14
  export * from "./instrument-picker";
15
15
  export { ItemPicker, type ItemPickerProps } from "./item-picker/ItemPicker";
16
- export { type ItemDescriptor, type CreateCustomItemProps, } from "./item-picker/useItemPicker";
16
+ export { type ItemDescriptor, SelectedItemsChangeHandler as SelectedItemChangeHandler } from "./item-picker/useItemPicker";
17
17
  export * from "./measured-container";
18
18
  export { ModalProvider, useModal } from "./modal-provider/ModalProvider";
19
19
  export * from "./overflow-container";
@@ -1,16 +1,34 @@
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";
1
+ import { type ListBoxProps, type OptionProps } from "@salt-ds/core";
2
+ import { type ItemTypeName } from "@vuu-ui/vuu-utils";
3
+ import { type ComponentType, type HTMLAttributes, type MouseEventHandler } from "react";
4
+ import { type ItemDescriptor, type ItemPickerHookProps } from "./useItemPicker";
5
5
  export declare const classBaseListItem = "vuuItemPickerListItem";
6
+ export type ItemPickerLayout = 'v-available-selected' | 'v-selected-available';
6
7
  export interface ItemPickerProps extends ItemPickerHookProps, HTMLAttributes<HTMLDivElement>, Pick<ListBoxProps<ItemDescriptor>, "selected" | "onSelectionChange"> {
7
- itemTypeName: ItemTypeName;
8
- /** Render the search control as a form. Set false when the picker is nested in a form. */
8
+ AvailableListItem?: ComponentType<AvailableListItemProps>;
9
+ SelectedListItem?: ComponentType<SelectedListItemProps>;
10
+ itemTypeName?: ItemTypeName;
11
+ /**
12
+ * Display order of SelectedItems List / AvailableItems List
13
+ */
14
+ layout?: ItemPickerLayout;
9
15
  searchForm?: boolean;
10
- createCustomItemProps?: CreateCustomItemProps;
16
+ }
17
+ interface SelectedListItemProps extends OptionProps {
18
+ index: number;
19
+ item: ItemDescriptor;
20
+ onRemove: MouseEventHandler<HTMLButtonElement>;
21
+ searchPattern?: Lowercase<string>;
22
+ }
23
+ interface AvailableListItemProps extends OptionProps {
24
+ index: number;
25
+ item: ItemDescriptor;
26
+ onAdd: MouseEventHandler<HTMLButtonElement>;
27
+ searchPattern?: Lowercase<string>;
11
28
  }
12
29
  /** Generic controlled component that displays items in a 'picker' that are available to search, select, reorder or deselect.
13
30
  * As input props, all available items, currently selected items, search text and callback functions for changes in selected items and search text
14
31
  * must be supplied by the client.
15
32
  */
16
33
  export declare const ItemPicker: import("react").ForwardRefExoticComponent<ItemPickerProps & import("react").RefAttributes<HTMLDivElement>>;
34
+ export {};
@@ -1,4 +1,5 @@
1
- import { FormEventHandler, MouseEventHandler } from "react";
1
+ import { type ChangeEventHandler, type MouseEventHandler } from "react";
2
+ export type SelectedItemsChangeHandler = (newSelectedItems: readonly ItemDescriptor[]) => void;
2
3
  /** This is a public description of an Item that can be displayed in the ItemPicker component, defining all the
3
4
  * mandatory and option attributes that can be defined by the client. */
4
5
  export interface ItemDescriptor {
@@ -11,15 +12,11 @@ export interface ItemDescriptor {
11
12
  /** Optional string to define groups of items for client reference, not currently used for rendering */
12
13
  group?: string;
13
14
  }
14
- export interface CreateCustomItemProps {
15
- buttonLabel: string;
16
- onClickCreateCustomItem: MouseEventHandler<HTMLButtonElement>;
17
- }
18
15
  export interface ItemPickerHookProps {
19
16
  allItems: ItemDescriptor[];
20
17
  selectedItems: ItemDescriptor[];
21
18
  maxSelections?: number;
22
- onSelectedItemsChange: (newSelectedItems: ItemDescriptor[]) => void;
19
+ onSelectedItemsChange: SelectedItemsChangeHandler;
23
20
  }
24
21
  export declare const getItemLabel: (item: ItemDescriptor) => string;
25
22
  export declare const useItemPicker: ({ allItems, selectedItems, maxSelections, onSelectedItemsChange, }: ItemPickerHookProps) => {
@@ -28,7 +25,7 @@ export declare const useItemPicker: ({ allItems, selectedItems, maxSelections, o
28
25
  selectedItemsFiltered: readonly ItemDescriptor[];
29
26
  availableItemsFiltered: ItemDescriptor[];
30
27
  searchText: string;
31
- onChangeSearchInput: FormEventHandler;
28
+ onChangeSearchInput: ChangeEventHandler;
32
29
  onAddItemToSelectedList: MouseEventHandler<HTMLButtonElement>;
33
30
  onRemoveItemFromSelectedList: MouseEventHandler<HTMLButtonElement>;
34
31
  onReorderSelectedItems: (orderedItemNames: string[]) => void;