@equinor/eds-core-react 2.1.0 → 2.2.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +49 -4
  2. package/dist/eds-core-react.cjs +754 -499
  3. package/dist/esm/components/Autocomplete/AddNewOption.js +31 -14
  4. package/dist/esm/components/Autocomplete/Autocomplete.js +54 -874
  5. package/dist/esm/components/Autocomplete/AutocompleteContext.js +12 -0
  6. package/dist/esm/components/Autocomplete/EmptyOption.js +21 -0
  7. package/dist/esm/components/Autocomplete/MultipleInput.js +85 -0
  8. package/dist/esm/components/Autocomplete/Option.js +42 -23
  9. package/dist/esm/components/Autocomplete/OptionList.js +124 -0
  10. package/dist/esm/components/Autocomplete/RightAdornments.js +48 -0
  11. package/dist/esm/components/Autocomplete/SelectAllOption.js +63 -0
  12. package/dist/esm/components/Autocomplete/SingleInput.js +28 -0
  13. package/dist/esm/components/Autocomplete/useAutocomplete.js +605 -0
  14. package/dist/esm/components/Autocomplete/utils.js +93 -0
  15. package/dist/esm/components/Datepicker/fields/FieldWrapper.js +10 -0
  16. package/dist/esm/components/Dialog/Dialog.js +6 -4
  17. package/dist/esm/components/next/Icon/Icon.js +57 -0
  18. package/dist/esm/components/next/Icon/icon.css.js +6 -0
  19. package/dist/esm/components/next/Placeholder/Placeholder.js +17 -0
  20. package/dist/esm/index.js +1 -1
  21. package/dist/esm/index.next.js +2 -0
  22. package/dist/esm/node_modules/.pnpm/style-inject@0.3.0/node_modules/style-inject/dist/style-inject.es.js +26 -0
  23. package/dist/index.next.cjs +101 -0
  24. package/dist/types/components/Autocomplete/AddNewOption.d.ts +4 -12
  25. package/dist/types/components/Autocomplete/Autocomplete.d.ts +33 -11
  26. package/dist/types/components/Autocomplete/AutocompleteContext.d.ts +228 -0
  27. package/dist/types/components/Autocomplete/EmptyOption.d.ts +1 -0
  28. package/dist/types/components/Autocomplete/MultipleInput.d.ts +1 -0
  29. package/dist/types/components/Autocomplete/Option.d.ts +7 -15
  30. package/dist/types/components/Autocomplete/OptionList.d.ts +2 -0
  31. package/dist/types/components/Autocomplete/RightAdornments.d.ts +1 -0
  32. package/dist/types/components/Autocomplete/SelectAllOption.d.ts +6 -0
  33. package/dist/types/components/Autocomplete/SingleInput.d.ts +1 -0
  34. package/dist/types/components/Autocomplete/useAutocomplete.d.ts +122 -0
  35. package/dist/types/components/Autocomplete/utils.d.ts +13 -0
  36. package/dist/types/components/Datepicker/DateRangePicker.d.ts +1 -1
  37. package/dist/types/components/SideBar/SideBarButton/index.d.ts +1 -1
  38. package/dist/types/components/next/Icon/Icon.d.ts +29 -0
  39. package/dist/types/components/next/Icon/Icon.types.d.ts +19 -0
  40. package/dist/types/components/next/Icon/index.d.ts +2 -0
  41. package/dist/types/components/next/Placeholder/Placeholder.d.ts +9 -0
  42. package/dist/types/components/next/Placeholder/Placeholder.figma.d.ts +16 -0
  43. package/dist/types/components/next/Placeholder/index.d.ts +2 -0
  44. package/dist/types/components/next/index.d.ts +4 -0
  45. package/dist/types/index.next.d.ts +11 -0
  46. package/package.json +11 -5
@@ -0,0 +1,12 @@
1
+ import { createContext, use } from 'react';
2
+
3
+ const AutocompleteContext = /*#__PURE__*/createContext(null);
4
+ const useAutocompleteContext = () => {
5
+ const context = use(AutocompleteContext);
6
+ if (!context) {
7
+ throw new Error('Autocomplete compound components must be used within an Autocomplete component');
8
+ }
9
+ return context;
10
+ };
11
+
12
+ export { AutocompleteContext, useAutocompleteContext };
@@ -0,0 +1,21 @@
1
+ import { useAutocompleteContext } from './AutocompleteContext.js';
2
+ import { StyledListItem, AutocompleteOptionLabel } from './Option.js';
3
+ import { jsx } from 'react/jsx-runtime';
4
+
5
+ function EmptyOption() {
6
+ const {
7
+ noOptionsText
8
+ } = useAutocompleteContext();
9
+ return /*#__PURE__*/jsx(StyledListItem, {
10
+ $isdisabled: "true",
11
+ $highlighted: "false",
12
+ "aria-hidden": true,
13
+ $active: "false",
14
+ children: /*#__PURE__*/jsx(AutocompleteOptionLabel, {
15
+ $multiline: false,
16
+ children: noOptionsText
17
+ })
18
+ });
19
+ }
20
+
21
+ export { EmptyOption };
@@ -0,0 +1,85 @@
1
+ import { useRef } from 'react';
2
+ import styled from 'styled-components';
3
+ import { useAutocompleteContext } from './AutocompleteContext.js';
4
+ import { RightAdornments } from './RightAdornments.js';
5
+ import { jsx, jsxs } from 'react/jsx-runtime';
6
+ import { Chip } from '../Chip/Chip.js';
7
+ import { Input } from '../Input/Input.js';
8
+
9
+ const UnstyledInput = styled.input.withConfig({
10
+ displayName: "MultipleInput__UnstyledInput",
11
+ componentId: "sc-1evc1aa-0"
12
+ })(["flex:1;min-width:6rem;font-size:1rem;border:none;padding:0;background:inherit;&:focus-visible{outline:none;}"]);
13
+ const ChipContainer = styled.div.withConfig({
14
+ displayName: "MultipleInput__ChipContainer",
15
+ componentId: "sc-1evc1aa-1"
16
+ })(["display:flex;flex-wrap:wrap;gap:0.5rem;height:100%;"]);
17
+ const MultipleInput = () => {
18
+ const {
19
+ selectedItems,
20
+ selectionDisplay,
21
+ getLabel,
22
+ removeSelectedItem,
23
+ readOnly,
24
+ inputProps,
25
+ placeholderText,
26
+ variant,
27
+ hideClearButton,
28
+ restHtmlProps,
29
+ consolidatedEvents,
30
+ inputRef
31
+ } = useAutocompleteContext();
32
+ const chipRefs = useRef(new Map());
33
+ const handleChipRemove = (item, index, isKeyboardEvent) => {
34
+ if (isKeyboardEvent && selectedItems.length > 1) {
35
+ const isLastChip = index === selectedItems.length - 1;
36
+ const nextItem = selectedItems[isLastChip ? index - 1 : index + 1];
37
+ chipRefs.current.get(getLabel(nextItem))?.focus();
38
+ } else if (!isKeyboardEvent) {
39
+ inputRef.current?.focus();
40
+ }
41
+ removeSelectedItem(item);
42
+ };
43
+ const handleChipDelete = (item, index) => e => {
44
+ const isKeyboard = 'key' in e && e.key === 'Enter';
45
+ handleChipRemove(item, index, isKeyboard);
46
+ };
47
+ const handleChipClick = (item, index) => e => {
48
+ e.preventDefault();
49
+ e.stopPropagation();
50
+ handleChipRemove(item, index, false);
51
+ };
52
+ return /*#__PURE__*/jsx(Input, {
53
+ as: 'div',
54
+ variant: variant,
55
+ rightAdornmentsWidth: hideClearButton ? 24 + 8 : 24 * 2 + 8,
56
+ rightAdornments: /*#__PURE__*/jsx(RightAdornments, {}),
57
+ readOnly: readOnly,
58
+ style: {
59
+ height: 'auto',
60
+ minHeight: '36px'
61
+ },
62
+ children: /*#__PURE__*/jsxs(ChipContainer, {
63
+ children: [selectionDisplay === 'chips' && selectedItems.map((item, index) => /*#__PURE__*/jsx(Chip, {
64
+ ref: el => {
65
+ if (el) chipRefs.current.set(getLabel(item), el);else chipRefs.current.delete(getLabel(item));
66
+ },
67
+ style: {
68
+ outline: '1px solid var(--eds-color-accent-12)'
69
+ },
70
+ onDelete: handleChipDelete(item, index),
71
+ onClick: handleChipClick(item, index),
72
+ disabled: readOnly,
73
+ children: getLabel(item)
74
+ }, getLabel(item))), /*#__PURE__*/jsx(UnstyledInput, {
75
+ ...restHtmlProps,
76
+ ...inputProps,
77
+ ...consolidatedEvents,
78
+ placeholder: placeholderText,
79
+ readOnly: readOnly
80
+ })]
81
+ })
82
+ });
83
+ };
84
+
85
+ export { MultipleInput };
@@ -1,6 +1,6 @@
1
- import { forwardRef } from 'react';
2
- import styled, { css } from 'styled-components';
3
1
  import { typographyTemplate, spacingsTemplate } from '@equinor/eds-utils';
2
+ import styled, { css } from 'styled-components';
3
+ import { useAutocompleteContext } from './AutocompleteContext.js';
4
4
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
5
5
  import { Checkbox } from '../Checkbox/Checkbox.js';
6
6
 
@@ -25,36 +25,56 @@ const AutocompleteOptionLabel = styled.span.withConfig({
25
25
  }) => {
26
26
  return css(["", " text-overflow:ellipsis;white-space:", ";overflow:", ";overflow-wrap:anywhere;@supports (-moz-appearance:none){overflow:", ";}"], spacingsTemplate(theme.entities.label.spacings), $multiline ? 'normal' : 'nowrap', $multiline ? 'initial' : 'hidden', $multiline ? 'initial' : 'clip');
27
27
  });
28
- function AutocompleteOptionInner(props, ref) {
28
+ function Option({
29
+ indeterminate,
30
+ index,
31
+ item,
32
+ virtualItem
33
+ }) {
29
34
  const {
30
- value,
31
- optionComponent,
32
35
  multiple,
33
- isSelected,
34
- indeterminate,
35
- isDisabled,
36
+ availableItems,
37
+ highlightedIndex,
36
38
  multiline,
37
- highlighted,
38
- onClick,
39
- 'aria-selected': ariaSelected,
40
- ...other
41
- } = props;
39
+ optionDisabled,
40
+ selectedItemsLabels,
41
+ getLabel,
42
+ getItemProps,
43
+ optionComponent: _optionComponent,
44
+ rowVirtualizer
45
+ } = useAutocompleteContext();
46
+ const isDisabled = optionDisabled(item);
47
+ const label = getLabel(item);
48
+ const isSelected = selectedItemsLabels.includes(label);
49
+ const optionComponent = _optionComponent?.(item, isSelected);
50
+ const highlighted = highlightedIndex === index && !isDisabled ? 'true' : 'false';
51
+ const itemProps = getItemProps({
52
+ ...(multiline && {
53
+ ref: rowVirtualizer.measureElement
54
+ }),
55
+ item,
56
+ index,
57
+ style: {
58
+ transform: `translateY(${virtualItem.start}px)`,
59
+ ...(!multiline && {
60
+ height: `${virtualItem.size}px`
61
+ })
62
+ }
63
+ });
42
64
  return /*#__PURE__*/jsxs(StyledListItem, {
43
- ref: ref,
44
65
  $isdisabled: isDisabled ? 'true' : 'false',
45
66
  $highlighted: highlighted,
46
67
  "aria-hidden": isDisabled,
47
68
  $active: !multiple && isSelected ? 'true' : 'false',
48
- onClick: e => {
49
- !isDisabled && onClick(e);
50
- },
51
- "aria-selected": multiple ? isSelected : ariaSelected,
52
- ...other,
69
+ "aria-setsize": availableItems.length,
70
+ "data-index": index,
71
+ "aria-posinset": index + 1,
72
+ ...itemProps,
53
73
  children: [multiple && /*#__PURE__*/jsx(Checkbox, {
54
74
  disabled: isDisabled,
55
75
  checked: isSelected,
56
76
  indeterminate: indeterminate,
57
- value: value,
77
+ value: label,
58
78
  onChange: () => {
59
79
  return null;
60
80
  }
@@ -62,10 +82,9 @@ function AutocompleteOptionInner(props, ref) {
62
82
  children: optionComponent
63
83
  }) : /*#__PURE__*/jsx(AutocompleteOptionLabel, {
64
84
  $multiline: multiline,
65
- children: value
85
+ children: label
66
86
  })]
67
87
  });
68
88
  }
69
- const AutocompleteOption = /*#__PURE__*/forwardRef(AutocompleteOptionInner);
70
89
 
71
- export { AutocompleteOption, AutocompleteOptionLabel, StyledListItem };
90
+ export { AutocompleteOptionLabel, Option, StyledListItem };
@@ -0,0 +1,124 @@
1
+ import { useIsomorphicLayoutEffect, bordersTemplate } from '@equinor/eds-utils';
2
+ import { useInteractions, autoUpdate } from '@floating-ui/react';
3
+ import { useEffect } from 'react';
4
+ import styled, { css } from 'styled-components';
5
+ import { List } from '../List/index.js';
6
+ import { AddNewOption } from './AddNewOption.js';
7
+ import { AllSymbol, AddSymbol } from './Autocomplete.js';
8
+ import { useAutocompleteContext } from './AutocompleteContext.js';
9
+ import { Option } from './Option.js';
10
+ import { SelectAllOption } from './SelectAllOption.js';
11
+ import { handleListFocus } from './utils.js';
12
+ import { EmptyOption } from './EmptyOption.js';
13
+ import { jsx, jsxs } from 'react/jsx-runtime';
14
+
15
+ const OptionList = ({
16
+ refs,
17
+ strategy,
18
+ x,
19
+ y,
20
+ update
21
+ }) => {
22
+ const {
23
+ getFloatingProps
24
+ } = useInteractions([]);
25
+ const {
26
+ isOpen,
27
+ getMenuProps,
28
+ multiple,
29
+ scrollContainer,
30
+ dropdownHeight,
31
+ availableItems,
32
+ noOptionsText,
33
+ rowVirtualizer,
34
+ onAddNewOption
35
+ } = useAutocompleteContext();
36
+ useEffect(() => {
37
+ if (refs.reference.current && refs.floating.current && isOpen) {
38
+ return autoUpdate(refs.reference.current, refs.floating.current, update);
39
+ }
40
+ }, [refs.reference, refs.floating, update, isOpen]);
41
+
42
+ // MARK: popover toggle
43
+ useIsomorphicLayoutEffect(() => {
44
+ if (isOpen) {
45
+ refs.floating.current?.showPopover();
46
+ } else {
47
+ refs.floating.current?.hidePopover();
48
+ }
49
+ }, [isOpen, refs.floating]);
50
+ const showNoOptions = isOpen && !availableItems.length && noOptionsText.length > 0;
51
+ const floatingProps = getFloatingProps({
52
+ ref: refs.setFloating,
53
+ onFocus: handleListFocus,
54
+ style: {
55
+ position: strategy,
56
+ top: y || 0,
57
+ left: x || 0
58
+ }
59
+ });
60
+ const menuProps = getMenuProps({
61
+ 'aria-multiselectable': multiple ? 'true' : null,
62
+ ref: scrollContainer,
63
+ style: {
64
+ maxHeight: `${dropdownHeight}px`
65
+ }
66
+ }, {
67
+ suppressRefError: true
68
+ });
69
+ return /*#__PURE__*/jsx(StyledPopover, {
70
+ popover: "manual",
71
+ ...floatingProps,
72
+ children: /*#__PURE__*/jsxs(StyledList, {
73
+ ...menuProps,
74
+ children: [showNoOptions && /*#__PURE__*/jsx(AutocompleteNoOptions, {}), isOpen && /*#__PURE__*/jsx("li", {
75
+ role: "presentation",
76
+ style: {
77
+ height: `${rowVirtualizer.getTotalSize()}px`,
78
+ margin: '0',
79
+ gridArea: '1 / -1'
80
+ }
81
+ }, "total-size"), !isOpen ? null : rowVirtualizer.getVirtualItems().map(virtualItem => {
82
+ const index = virtualItem.index;
83
+ const item = availableItems[index];
84
+ if (item === AllSymbol) {
85
+ return /*#__PURE__*/jsx(SelectAllOption, {
86
+ item: item,
87
+ index: index
88
+ }, "select-all");
89
+ }
90
+ if (item === AddSymbol && onAddNewOption) {
91
+ return /*#__PURE__*/jsx(AddNewOption, {
92
+ item: item,
93
+ index: index
94
+ }, "add-item");
95
+ }
96
+ return /*#__PURE__*/jsx(Option, {
97
+ item: item,
98
+ virtualItem: virtualItem,
99
+ index: index
100
+ }, virtualItem.key);
101
+ })]
102
+ })
103
+ });
104
+ };
105
+ const StyledPopover = styled('div').withConfig({
106
+ shouldForwardProp: () => true //workaround to avoid warning until popover gets added to react types
107
+ }).withConfig({
108
+ displayName: "OptionList__StyledPopover",
109
+ componentId: "sc-t6tp7k-0"
110
+ })(["inset:unset;border:0;padding:0;margin:0;overflow:visible;&::backdrop{background-color:transparent;}"]);
111
+ const StyledList = styled(List).withConfig({
112
+ displayName: "OptionList__StyledList",
113
+ componentId: "sc-t6tp7k-1"
114
+ })(({
115
+ theme
116
+ }) => css(["background-color:", ";box-shadow:", ";", " overflow-y:auto;overflow-x:hidden;padding:0;display:grid;@supports (-moz-appearance:none){scrollbar-width:thin;}"], theme.background, theme.boxShadow, bordersTemplate(theme.border)));
117
+ const AutocompleteNoOptions = styled(EmptyOption).withConfig({
118
+ displayName: "OptionList__AutocompleteNoOptions",
119
+ componentId: "sc-t6tp7k-2"
120
+ })(({
121
+ theme
122
+ }) => css(["color:", ";"], theme.entities.noOptions.typography.color));
123
+
124
+ export { OptionList };
@@ -0,0 +1,48 @@
1
+ import { close, arrow_drop_up, arrow_drop_down } from '@equinor/eds-icons';
2
+ import { Icon } from '../Icon/index.js';
3
+ import { Progress } from '../Progress/index.js';
4
+ import { useAutocompleteContext } from './AutocompleteContext.js';
5
+ import { StyledButton } from './Autocomplete.js';
6
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
7
+
8
+ const RightAdornments = () => {
9
+ const {
10
+ readOnly,
11
+ selectedItems,
12
+ inputValue,
13
+ loading,
14
+ disabled,
15
+ getToggleButtonProps,
16
+ hideClearButton,
17
+ isOpen,
18
+ clear
19
+ } = useAutocompleteContext();
20
+ const showClearButton = (selectedItems.length > 0 || inputValue) && !readOnly && !hideClearButton;
21
+ return /*#__PURE__*/jsxs(Fragment, {
22
+ children: [loading && /*#__PURE__*/jsx(Progress.Circular, {
23
+ size: 16
24
+ }), showClearButton && /*#__PURE__*/jsx(StyledButton, {
25
+ variant: "ghost_icon",
26
+ disabled: disabled || readOnly,
27
+ "aria-label": 'clear options',
28
+ title: "clear",
29
+ onClick: clear,
30
+ children: /*#__PURE__*/jsx(Icon, {
31
+ data: close,
32
+ size: 16
33
+ })
34
+ }), !readOnly && /*#__PURE__*/jsx(StyledButton, {
35
+ variant: "ghost_icon",
36
+ ...getToggleButtonProps({
37
+ disabled: disabled || readOnly
38
+ }),
39
+ "aria-label": 'toggle options',
40
+ title: "open",
41
+ children: /*#__PURE__*/jsx(Icon, {
42
+ data: isOpen ? arrow_drop_up : arrow_drop_down
43
+ })
44
+ })]
45
+ });
46
+ };
47
+
48
+ export { RightAdornments };
@@ -0,0 +1,63 @@
1
+ import { useAutocompleteContext } from './AutocompleteContext.js';
2
+ import { StyledListItem, AutocompleteOptionLabel } from './Option.js';
3
+ import { jsxs, jsx } from 'react/jsx-runtime';
4
+ import { Checkbox } from '../Checkbox/Checkbox.js';
5
+
6
+ function SelectAllOption({
7
+ item,
8
+ index,
9
+ ref
10
+ }) {
11
+ const {
12
+ availableItems,
13
+ allSelectedState,
14
+ highlightedIndex,
15
+ optionDisabled,
16
+ multiline,
17
+ getItemProps,
18
+ rowVirtualizer
19
+ } = useAutocompleteContext();
20
+ const value = 'Select all';
21
+ const isSelected = allSelectedState === 'ALL';
22
+ const isDisabled = optionDisabled(item);
23
+ const indeterminate = allSelectedState === 'SOME';
24
+ const highlighted = highlightedIndex === index && !isDisabled ? 'true' : 'false';
25
+ const itemProps = getItemProps({
26
+ ...(multiline && {
27
+ ref: rowVirtualizer.measureElement
28
+ }),
29
+ item,
30
+ index: index
31
+ });
32
+ return /*#__PURE__*/jsxs(StyledListItem, {
33
+ ref: ref,
34
+ $isdisabled: isDisabled ? 'true' : 'false',
35
+ $highlighted: highlighted,
36
+ "aria-hidden": isDisabled,
37
+ $active: "false",
38
+ "aria-selected": isSelected,
39
+ "data-index": 0,
40
+ "data-testid": 'select-all',
41
+ "aria-setsize": availableItems.length,
42
+ style: {
43
+ position: 'sticky',
44
+ top: 0,
45
+ zIndex: 99
46
+ },
47
+ ...itemProps,
48
+ children: [/*#__PURE__*/jsx(Checkbox, {
49
+ disabled: isDisabled,
50
+ checked: isSelected,
51
+ indeterminate: indeterminate,
52
+ value: value,
53
+ onChange: () => {
54
+ return null;
55
+ }
56
+ }), /*#__PURE__*/jsx(AutocompleteOptionLabel, {
57
+ $multiline: multiline,
58
+ children: value
59
+ })]
60
+ });
61
+ }
62
+
63
+ export { SelectAllOption };
@@ -0,0 +1,28 @@
1
+ import { useAutocompleteContext } from './AutocompleteContext.js';
2
+ import { RightAdornments } from './RightAdornments.js';
3
+ import { jsx } from 'react/jsx-runtime';
4
+ import { Input } from '../Input/Input.js';
5
+
6
+ const SingleInput = () => {
7
+ const {
8
+ consolidatedEvents,
9
+ inputProps,
10
+ variant,
11
+ hideClearButton,
12
+ restHtmlProps,
13
+ placeholder,
14
+ readOnly
15
+ } = useAutocompleteContext();
16
+ return /*#__PURE__*/jsx(Input, {
17
+ variant: variant,
18
+ placeholder: placeholder,
19
+ readOnly: readOnly,
20
+ rightAdornmentsWidth: hideClearButton ? 24 + 8 : 24 * 2 + 8,
21
+ rightAdornments: /*#__PURE__*/jsx(RightAdornments, {}),
22
+ ...restHtmlProps,
23
+ ...consolidatedEvents,
24
+ ...inputProps
25
+ });
26
+ };
27
+
28
+ export { SingleInput };