@equinor/eds-core-react 0.39.0 → 0.40.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.
@@ -9420,6 +9420,7 @@ const Container$2 = styled__default.default.div.withConfig({
9420
9420
  componentId: "sc-yvif0e-0"
9421
9421
  })(["position:relative;"]);
9422
9422
  const AllSymbol = Symbol('Select all');
9423
+ // MARK: styled components
9423
9424
  const StyledList = styled__default.default(List$1).withConfig({
9424
9425
  displayName: "Autocomplete__StyledList",
9425
9426
  componentId: "sc-yvif0e-1"
@@ -9452,7 +9453,7 @@ const StyledButton$1 = styled__default.default(Button$1).withConfig({
9452
9453
  }
9453
9454
  }
9454
9455
  }) => styled.css(["height:", ";width:", ";"], button.height, button.height));
9455
-
9456
+ // MARK: outside functions
9456
9457
  // Typescript can struggle with parsing generic arrow functions in a .tsx file (see https://github.com/microsoft/TypeScript/issues/15713)
9457
9458
  // Workaround is to add a trailing , after T, which tricks the compiler, but also have to ignore prettier rule.
9458
9459
  // prettier-ignore
@@ -9489,8 +9490,10 @@ function mergeEventsFromRight(props1, props2) {
9489
9490
  const findNextIndex = ({
9490
9491
  index,
9491
9492
  optionDisabled,
9492
- availableItems
9493
+ availableItems,
9494
+ allDisabled
9493
9495
  }) => {
9496
+ if (allDisabled) return 0;
9494
9497
  const options = {
9495
9498
  index,
9496
9499
  optionDisabled,
@@ -9510,8 +9513,10 @@ const findNextIndex = ({
9510
9513
  const findPrevIndex = ({
9511
9514
  index,
9512
9515
  optionDisabled,
9513
- availableItems
9516
+ availableItems,
9517
+ allDisabled
9514
9518
  }) => {
9519
+ if (allDisabled) return 0;
9515
9520
  const options = {
9516
9521
  index,
9517
9522
  optionDisabled,
@@ -9542,9 +9547,9 @@ const handleListFocus = e => {
9542
9547
  });
9543
9548
  };
9544
9549
  const defaultOptionDisabled = () => false;
9550
+ // MARK: types
9545
9551
 
9546
- // prettier-ignore
9547
-
9552
+ // MARK: component
9548
9553
  function AutocompleteInner(props, ref) {
9549
9554
  const {
9550
9555
  options = [],
@@ -9579,6 +9584,8 @@ function AutocompleteInner(props, ref) {
9579
9584
  variant,
9580
9585
  ...other
9581
9586
  } = props;
9587
+
9588
+ // MARK: initializing data/setup
9582
9589
  const selectedOptions = _selectedOptions ? itemCompare ? options.filter(item => _selectedOptions.some(compare => itemCompare(item, compare))) : _selectedOptions : undefined;
9583
9590
  const initialSelectedOptions = _initialSelectedOptions ? itemCompare ? options.filter(item => _initialSelectedOptions.some(compare => itemCompare(item, compare))) : _initialSelectedOptions : undefined;
9584
9591
  if (disablePortal) {
@@ -9588,6 +9595,7 @@ function AutocompleteInner(props, ref) {
9588
9595
  const [inputOptions, setInputOptions] = react.useState(options);
9589
9596
  const [_availableItems, setAvailableItems] = react.useState(inputOptions);
9590
9597
  const [typedInputValue, setTypedInputValue] = react.useState('');
9598
+ const inputRef = react.useRef(null);
9591
9599
  const showSelectAll = react.useMemo(() => {
9592
9600
  if (!multiple && allowSelectAll) {
9593
9601
  throw new Error(`allowSelectAll can only be used with multiple`);
@@ -9650,10 +9658,13 @@ function AutocompleteInner(props, ref) {
9650
9658
  selectedItems,
9651
9659
  setSelectedItems
9652
9660
  } = downshift.useMultipleSelection(multipleSelectionProps);
9661
+
9662
+ // MARK: select all logic
9653
9663
  const enabledItems = react.useMemo(() => {
9654
9664
  const disabledItemsSet = new Set(inputOptions.filter(optionDisabled));
9655
9665
  return inputOptions.filter(x => !disabledItemsSet.has(x));
9656
9666
  }, [inputOptions, optionDisabled]);
9667
+ const allDisabled = enabledItems.length === 0;
9657
9668
  const selectedDisabledItemsSet = react.useMemo(() => new Set(selectedItems.filter(optionDisabled)), [selectedItems, optionDisabled]);
9658
9669
  const selectedEnabledItems = react.useMemo(() => selectedItems.filter(x => !selectedDisabledItemsSet.has(x)), [selectedItems, selectedDisabledItemsSet]);
9659
9670
  const allSelectedState = react.useMemo(() => {
@@ -9669,6 +9680,8 @@ function AutocompleteInner(props, ref) {
9669
9680
  setSelectedItems([...enabledItems, ...selectedDisabledItemsSet]);
9670
9681
  }
9671
9682
  };
9683
+
9684
+ // MARK: getLabel
9672
9685
  const getLabel = react.useCallback(item => {
9673
9686
  //note: non strict check for null or undefined to allow 0
9674
9687
  if (item == null) {
@@ -9691,6 +9704,8 @@ function AutocompleteInner(props, ref) {
9691
9704
  throw new Error('Unable to find label, make sure your are using options as documented');
9692
9705
  }
9693
9706
  }, [optionLabel]);
9707
+
9708
+ // MARK: setup virtualizer
9694
9709
  const scrollContainer = react.useRef(null);
9695
9710
  const rowVirtualizer = reactVirtual.useVirtualizer({
9696
9711
  count: availableItems.length,
@@ -9705,8 +9720,11 @@ function AutocompleteInner(props, ref) {
9705
9720
  edsUtils.useIsomorphicLayoutEffect(() => {
9706
9721
  rowVirtualizer?.measure?.();
9707
9722
  }, [rowVirtualizer, density]);
9723
+
9724
+ // MARK: downshift state
9708
9725
  let comboBoxProps = {
9709
9726
  items: availableItems,
9727
+ //can not pass readonly type to downshift so we cast it to regular T[]
9710
9728
  initialSelectedItem: initialSelectedOptions[0],
9711
9729
  isItemDisabled(item) {
9712
9730
  return optionDisabled(item);
@@ -9771,6 +9789,7 @@ function AutocompleteInner(props, ref) {
9771
9789
  }
9772
9790
  }
9773
9791
  };
9792
+ // MARK: singleselect specific
9774
9793
  if (!multiple) {
9775
9794
  comboBoxProps = {
9776
9795
  ...comboBoxProps,
@@ -9816,7 +9835,8 @@ function AutocompleteInner(props, ref) {
9816
9835
  highlightedIndex: findNextIndex({
9817
9836
  index: changes.highlightedIndex,
9818
9837
  availableItems,
9819
- optionDisabled
9838
+ optionDisabled,
9839
+ allDisabled
9820
9840
  })
9821
9841
  };
9822
9842
  case downshift.useCombobox.stateChangeTypes.InputKeyDownArrowUp:
@@ -9832,7 +9852,8 @@ function AutocompleteInner(props, ref) {
9832
9852
  highlightedIndex: findPrevIndex({
9833
9853
  index: changes.highlightedIndex,
9834
9854
  availableItems,
9835
- optionDisabled
9855
+ optionDisabled,
9856
+ allDisabled
9836
9857
  })
9837
9858
  };
9838
9859
  default:
@@ -9847,6 +9868,7 @@ function AutocompleteInner(props, ref) {
9847
9868
  };
9848
9869
  }
9849
9870
  }
9871
+ // MARK: multiselect specific
9850
9872
  if (multiple) {
9851
9873
  placeholderText = typeof placeholderText !== 'undefined' ? placeholderText : `${selectedItems.length}/${inputOptions.length} selected`;
9852
9874
  comboBoxProps = {
@@ -9876,7 +9898,8 @@ function AutocompleteInner(props, ref) {
9876
9898
  highlightedIndex: findNextIndex({
9877
9899
  index: changes.highlightedIndex,
9878
9900
  availableItems,
9879
- optionDisabled
9901
+ optionDisabled,
9902
+ allDisabled
9880
9903
  })
9881
9904
  };
9882
9905
  case downshift.useCombobox.stateChangeTypes.InputKeyDownArrowUp:
@@ -9892,7 +9915,8 @@ function AutocompleteInner(props, ref) {
9892
9915
  highlightedIndex: findPrevIndex({
9893
9916
  index: changes.highlightedIndex,
9894
9917
  availableItems,
9895
- optionDisabled
9918
+ optionDisabled,
9919
+ allDisabled
9896
9920
  })
9897
9921
  };
9898
9922
  case downshift.useCombobox.stateChangeTypes.InputKeyDownEnter:
@@ -9940,6 +9964,8 @@ function AutocompleteInner(props, ref) {
9940
9964
  inputValue,
9941
9965
  reset: resetCombobox
9942
9966
  } = downshift.useCombobox(comboBoxProps);
9967
+
9968
+ // MARK: floating-ui setup
9943
9969
  const {
9944
9970
  x,
9945
9971
  y,
@@ -9971,6 +9997,8 @@ function AutocompleteInner(props, ref) {
9971
9997
  return react$1.autoUpdate(refs.reference.current, refs.floating.current, update);
9972
9998
  }
9973
9999
  }, [refs.reference, refs.floating, update, isOpen]);
10000
+
10001
+ // MARK: popover toggle
9974
10002
  edsUtils.useIsomorphicLayoutEffect(() => {
9975
10003
  if (isOpen) {
9976
10004
  refs.floating.current.showPopover();
@@ -9983,10 +10011,13 @@ function AutocompleteInner(props, ref) {
9983
10011
  //dont clear items if they are selected and disabled
9984
10012
  setSelectedItems([...selectedDisabledItemsSet]);
9985
10013
  setTypedInputValue('');
10014
+ inputRef.current?.focus();
9986
10015
  };
9987
10016
  const showClearButton = (selectedItems.length > 0 || inputValue) && !readOnly && !hideClearButton;
9988
10017
  const showNoOptions = isOpen && !availableItems.length && noOptionsText.length > 0;
9989
10018
  const selectedItemsLabels = react.useMemo(() => selectedItems.map(getLabel), [selectedItems, getLabel]);
10019
+
10020
+ // MARK: optionsList
9990
10021
  const optionsList = /*#__PURE__*/jsxRuntime.jsx(StyledPopover, {
9991
10022
  popover: "manual",
9992
10023
  ...getFloatingProps({
@@ -10085,9 +10116,12 @@ function AutocompleteInner(props, ref) {
10085
10116
  });
10086
10117
  const inputProps = getInputProps(getDropdownProps({
10087
10118
  preventKeyAction: multiple ? isOpen : undefined,
10088
- disabled
10119
+ disabled,
10120
+ ref: inputRef
10089
10121
  }));
10090
10122
  const consolidatedEvents = mergeEventsFromRight(other, inputProps);
10123
+
10124
+ // MARK: input
10091
10125
  return /*#__PURE__*/jsxRuntime.jsx(styled.ThemeProvider, {
10092
10126
  theme: token,
10093
10127
  children: /*#__PURE__*/jsxRuntime.jsxs(Container$2, {
@@ -10143,6 +10177,7 @@ function AutocompleteInner(props, ref) {
10143
10177
  })
10144
10178
  });
10145
10179
  }
10180
+ // MARK: exported component
10146
10181
  const Autocomplete = /*#__PURE__*/react.forwardRef(AutocompleteInner);
10147
10182
 
10148
10183
  const {
@@ -11601,9 +11636,16 @@ const useDatePickerContext = () => react.useContext(DatePickerContext);
11601
11636
  const Segment = styled__default.default.div.withConfig({
11602
11637
  displayName: "DateSegment__Segment",
11603
11638
  componentId: "sc-19uidpx-0"
11604
- })(["color:", ";font-family:", ";&:focus-visible{outline:2px solid ", ";background-color:", ";}", ""], edsTokens.tokens.typography.input.text.color, edsTokens.tokens.typography.input.text.fontFamily, edsTokens.tokens.colors.interactive.primary__resting.rgba, edsTokens.tokens.colors.ui.background__medium.rgba, ({
11639
+ })(({
11640
+ $placeholder,
11605
11641
  $disabled
11606
- }) => $disabled && `color: ${edsTokens.tokens.colors.interactive.disabled__text.rgba};`);
11642
+ }) => {
11643
+ return styled.css(["font-family:", ";&:focus-visible{outline:2px solid ", ";background-color:", ";}", " ", ""], edsTokens.tokens.typography.input.text.fontFamily, edsTokens.tokens.colors.interactive.primary__resting.rgba, edsTokens.tokens.colors.ui.background__medium.rgba, $placeholder ? styled.css({
11644
+ color: edsTokens.tokens.colors.text.static_icons__tertiary.rgba
11645
+ }) : styled.css({
11646
+ color: edsTokens.tokens.colors.text.static_icons__default.rgba
11647
+ }), $disabled && styled.css(["color:", ";"], edsTokens.tokens.colors.interactive.disabled__text.rgba));
11648
+ });
11607
11649
 
11608
11650
  /**
11609
11651
  * DateSegment is used to represent a single segment of a date in the DateField (i.e. day, month, year)
@@ -11755,10 +11797,11 @@ const Toggle = ({
11755
11797
  disabled,
11756
11798
  buttonProps,
11757
11799
  valueString,
11800
+ showClearButton,
11758
11801
  readonly
11759
11802
  }) => {
11760
11803
  return readonly || disabled ? null : /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
11761
- children: [/*#__PURE__*/jsxRuntime.jsx(StyledButton, {
11804
+ children: [showClearButton && /*#__PURE__*/jsxRuntime.jsx(StyledButton, {
11762
11805
  disabled: disabled,
11763
11806
  variant: 'ghost_icon',
11764
11807
  "aria-label": 'Reset',
@@ -11810,9 +11853,12 @@ const DatePicker = /*#__PURE__*/react.forwardRef(({
11810
11853
  defaultValue,
11811
11854
  showTimeInput,
11812
11855
  granularity,
11856
+ hideClearButton,
11813
11857
  disabled: isDisabled,
11814
11858
  readOnly: isReadOnly,
11815
11859
  formatOptions,
11860
+ helperProps,
11861
+ variant,
11816
11862
  ...props
11817
11863
  }, forwardedRef) => {
11818
11864
  timezone = timezone ?? defaultTimezone;
@@ -11867,7 +11913,8 @@ const DatePicker = /*#__PURE__*/react.forwardRef(({
11867
11913
  locale
11868
11914
  } = reactAria.useLocale();
11869
11915
  const dateCreateProps = {
11870
- ...props,
11916
+ helperProps,
11917
+ variant,
11871
11918
  isDisabled,
11872
11919
  value: _value,
11873
11920
  hideTimeZone: true,
@@ -11888,7 +11935,7 @@ const DatePicker = /*#__PURE__*/react.forwardRef(({
11888
11935
  fieldProps,
11889
11936
  calendarProps
11890
11937
  } = reactAria.useDatePicker(dateCreateProps, pickerState, ref);
11891
- const helperProps = pickerState.displayValidation.isInvalid ? {
11938
+ const helperPropsInvalid = pickerState.displayValidation.isInvalid ? {
11892
11939
  text: pickerState.displayValidation.validationErrors.join('\n'),
11893
11940
  color: edsTokens.tokens.colors.interactive.warning__text.rgba,
11894
11941
  icon: /*#__PURE__*/jsxRuntime.jsx(Icon$1, {
@@ -11896,6 +11943,7 @@ const DatePicker = /*#__PURE__*/react.forwardRef(({
11896
11943
  data: edsIcons.warning_outlined
11897
11944
  })
11898
11945
  } : undefined;
11946
+ const showClearButton = pickerState.dateValue !== null && !hideClearButton;
11899
11947
 
11900
11948
  // innerValue is used as a fallback, especially for uncontrolled inputs, so it needs to be reset when value / defaultValue is reset
11901
11949
  react.useEffect(() => {
@@ -11905,6 +11953,7 @@ const DatePicker = /*#__PURE__*/react.forwardRef(({
11905
11953
  timezone: timezone,
11906
11954
  formatOptions: formatOptions,
11907
11955
  children: /*#__PURE__*/jsxRuntime.jsx(FieldWrapper, {
11956
+ ...props,
11908
11957
  isOpen: isOpen,
11909
11958
  readonly: fieldProps.isReadOnly,
11910
11959
  pickerRef: pickerRef,
@@ -11919,8 +11968,8 @@ const DatePicker = /*#__PURE__*/react.forwardRef(({
11919
11968
  }),
11920
11969
  disabled: isDisabled,
11921
11970
  readOnly: isReadOnly,
11922
- color: pickerState.isInvalid ? 'warning' : props.variant,
11923
- helperProps: helperProps ?? props.helperProps,
11971
+ color: pickerState.isInvalid ? 'warning' : variant,
11972
+ helperProps: helperPropsInvalid ?? helperProps,
11924
11973
  children: /*#__PURE__*/jsxRuntime.jsx(DateField, {
11925
11974
  fieldProps: fieldProps,
11926
11975
  groupProps: groupProps,
@@ -11928,6 +11977,7 @@ const DatePicker = /*#__PURE__*/react.forwardRef(({
11928
11977
  ref: ref,
11929
11978
  onChange: _onChange,
11930
11979
  rightAdornments: /*#__PURE__*/jsxRuntime.jsx(Toggle, {
11980
+ showClearButton: showClearButton,
11931
11981
  setOpen: setIsOpen,
11932
11982
  open: isOpen,
11933
11983
  icon: edsIcons.calendar,
@@ -11941,7 +11991,7 @@ const DatePicker = /*#__PURE__*/react.forwardRef(({
11941
11991
  day: '2-digit'
11942
11992
  })
11943
11993
  }),
11944
- variant: props.variant
11994
+ variant: variant
11945
11995
  })
11946
11996
  })
11947
11997
  });
@@ -12057,6 +12107,7 @@ const DateRangePicker = /*#__PURE__*/react.forwardRef(({
12057
12107
  timezone,
12058
12108
  defaultValue,
12059
12109
  formatOptions,
12110
+ hideClearButton,
12060
12111
  disabled: isDisabled,
12061
12112
  readOnly: isReadOnly,
12062
12113
  ...props
@@ -12134,6 +12185,7 @@ const DateRangePicker = /*#__PURE__*/react.forwardRef(({
12134
12185
  data: edsIcons.warning_outlined
12135
12186
  })
12136
12187
  } : undefined;
12188
+ const showClearButton = state.dateRange !== null && !hideClearButton;
12137
12189
  const formattedValue = state.formatValue(locale, {
12138
12190
  year: 'numeric',
12139
12191
  month: 'short',
@@ -12176,6 +12228,7 @@ const DateRangePicker = /*#__PURE__*/react.forwardRef(({
12176
12228
  variant: props.variant,
12177
12229
  disabled: isDisabled,
12178
12230
  rightAdornments: /*#__PURE__*/jsxRuntime.jsx(Toggle, {
12231
+ showClearButton: showClearButton,
12179
12232
  buttonProps: buttonProps,
12180
12233
  disabled: isDisabled,
12181
12234
  readonly: isReadOnly,
@@ -1,4 +1,4 @@
1
- import { forwardRef, useState, useMemo, useEffect, useCallback, useRef } from 'react';
1
+ import { forwardRef, useState, useRef, useMemo, useEffect, useCallback } from 'react';
2
2
  import { useMultipleSelection, useCombobox } from 'downshift';
3
3
  import { useVirtualizer } from '@tanstack/react-virtual';
4
4
  import styled, { css, ThemeProvider } from 'styled-components';
@@ -24,6 +24,7 @@ const Container = styled.div.withConfig({
24
24
  componentId: "sc-yvif0e-0"
25
25
  })(["position:relative;"]);
26
26
  const AllSymbol = Symbol('Select all');
27
+ // MARK: styled components
27
28
  const StyledList = styled(List).withConfig({
28
29
  displayName: "Autocomplete__StyledList",
29
30
  componentId: "sc-yvif0e-1"
@@ -56,7 +57,7 @@ const StyledButton = styled(Button).withConfig({
56
57
  }
57
58
  }
58
59
  }) => css(["height:", ";width:", ";"], button.height, button.height));
59
-
60
+ // MARK: outside functions
60
61
  // Typescript can struggle with parsing generic arrow functions in a .tsx file (see https://github.com/microsoft/TypeScript/issues/15713)
61
62
  // Workaround is to add a trailing , after T, which tricks the compiler, but also have to ignore prettier rule.
62
63
  // prettier-ignore
@@ -93,8 +94,10 @@ function mergeEventsFromRight(props1, props2) {
93
94
  const findNextIndex = ({
94
95
  index,
95
96
  optionDisabled,
96
- availableItems
97
+ availableItems,
98
+ allDisabled
97
99
  }) => {
100
+ if (allDisabled) return 0;
98
101
  const options = {
99
102
  index,
100
103
  optionDisabled,
@@ -114,8 +117,10 @@ const findNextIndex = ({
114
117
  const findPrevIndex = ({
115
118
  index,
116
119
  optionDisabled,
117
- availableItems
120
+ availableItems,
121
+ allDisabled
118
122
  }) => {
123
+ if (allDisabled) return 0;
119
124
  const options = {
120
125
  index,
121
126
  optionDisabled,
@@ -146,9 +151,9 @@ const handleListFocus = e => {
146
151
  });
147
152
  };
148
153
  const defaultOptionDisabled = () => false;
154
+ // MARK: types
149
155
 
150
- // prettier-ignore
151
-
156
+ // MARK: component
152
157
  function AutocompleteInner(props, ref) {
153
158
  const {
154
159
  options = [],
@@ -183,6 +188,8 @@ function AutocompleteInner(props, ref) {
183
188
  variant,
184
189
  ...other
185
190
  } = props;
191
+
192
+ // MARK: initializing data/setup
186
193
  const selectedOptions = _selectedOptions ? itemCompare ? options.filter(item => _selectedOptions.some(compare => itemCompare(item, compare))) : _selectedOptions : undefined;
187
194
  const initialSelectedOptions = _initialSelectedOptions ? itemCompare ? options.filter(item => _initialSelectedOptions.some(compare => itemCompare(item, compare))) : _initialSelectedOptions : undefined;
188
195
  if (disablePortal) {
@@ -192,6 +199,7 @@ function AutocompleteInner(props, ref) {
192
199
  const [inputOptions, setInputOptions] = useState(options);
193
200
  const [_availableItems, setAvailableItems] = useState(inputOptions);
194
201
  const [typedInputValue, setTypedInputValue] = useState('');
202
+ const inputRef = useRef(null);
195
203
  const showSelectAll = useMemo(() => {
196
204
  if (!multiple && allowSelectAll) {
197
205
  throw new Error(`allowSelectAll can only be used with multiple`);
@@ -254,10 +262,13 @@ function AutocompleteInner(props, ref) {
254
262
  selectedItems,
255
263
  setSelectedItems
256
264
  } = useMultipleSelection(multipleSelectionProps);
265
+
266
+ // MARK: select all logic
257
267
  const enabledItems = useMemo(() => {
258
268
  const disabledItemsSet = new Set(inputOptions.filter(optionDisabled));
259
269
  return inputOptions.filter(x => !disabledItemsSet.has(x));
260
270
  }, [inputOptions, optionDisabled]);
271
+ const allDisabled = enabledItems.length === 0;
261
272
  const selectedDisabledItemsSet = useMemo(() => new Set(selectedItems.filter(optionDisabled)), [selectedItems, optionDisabled]);
262
273
  const selectedEnabledItems = useMemo(() => selectedItems.filter(x => !selectedDisabledItemsSet.has(x)), [selectedItems, selectedDisabledItemsSet]);
263
274
  const allSelectedState = useMemo(() => {
@@ -273,6 +284,8 @@ function AutocompleteInner(props, ref) {
273
284
  setSelectedItems([...enabledItems, ...selectedDisabledItemsSet]);
274
285
  }
275
286
  };
287
+
288
+ // MARK: getLabel
276
289
  const getLabel = useCallback(item => {
277
290
  //note: non strict check for null or undefined to allow 0
278
291
  if (item == null) {
@@ -295,6 +308,8 @@ function AutocompleteInner(props, ref) {
295
308
  throw new Error('Unable to find label, make sure your are using options as documented');
296
309
  }
297
310
  }, [optionLabel]);
311
+
312
+ // MARK: setup virtualizer
298
313
  const scrollContainer = useRef(null);
299
314
  const rowVirtualizer = useVirtualizer({
300
315
  count: availableItems.length,
@@ -309,8 +324,11 @@ function AutocompleteInner(props, ref) {
309
324
  useIsomorphicLayoutEffect(() => {
310
325
  rowVirtualizer?.measure?.();
311
326
  }, [rowVirtualizer, density]);
327
+
328
+ // MARK: downshift state
312
329
  let comboBoxProps = {
313
330
  items: availableItems,
331
+ //can not pass readonly type to downshift so we cast it to regular T[]
314
332
  initialSelectedItem: initialSelectedOptions[0],
315
333
  isItemDisabled(item) {
316
334
  return optionDisabled(item);
@@ -375,6 +393,7 @@ function AutocompleteInner(props, ref) {
375
393
  }
376
394
  }
377
395
  };
396
+ // MARK: singleselect specific
378
397
  if (!multiple) {
379
398
  comboBoxProps = {
380
399
  ...comboBoxProps,
@@ -420,7 +439,8 @@ function AutocompleteInner(props, ref) {
420
439
  highlightedIndex: findNextIndex({
421
440
  index: changes.highlightedIndex,
422
441
  availableItems,
423
- optionDisabled
442
+ optionDisabled,
443
+ allDisabled
424
444
  })
425
445
  };
426
446
  case useCombobox.stateChangeTypes.InputKeyDownArrowUp:
@@ -436,7 +456,8 @@ function AutocompleteInner(props, ref) {
436
456
  highlightedIndex: findPrevIndex({
437
457
  index: changes.highlightedIndex,
438
458
  availableItems,
439
- optionDisabled
459
+ optionDisabled,
460
+ allDisabled
440
461
  })
441
462
  };
442
463
  default:
@@ -451,6 +472,7 @@ function AutocompleteInner(props, ref) {
451
472
  };
452
473
  }
453
474
  }
475
+ // MARK: multiselect specific
454
476
  if (multiple) {
455
477
  placeholderText = typeof placeholderText !== 'undefined' ? placeholderText : `${selectedItems.length}/${inputOptions.length} selected`;
456
478
  comboBoxProps = {
@@ -480,7 +502,8 @@ function AutocompleteInner(props, ref) {
480
502
  highlightedIndex: findNextIndex({
481
503
  index: changes.highlightedIndex,
482
504
  availableItems,
483
- optionDisabled
505
+ optionDisabled,
506
+ allDisabled
484
507
  })
485
508
  };
486
509
  case useCombobox.stateChangeTypes.InputKeyDownArrowUp:
@@ -496,7 +519,8 @@ function AutocompleteInner(props, ref) {
496
519
  highlightedIndex: findPrevIndex({
497
520
  index: changes.highlightedIndex,
498
521
  availableItems,
499
- optionDisabled
522
+ optionDisabled,
523
+ allDisabled
500
524
  })
501
525
  };
502
526
  case useCombobox.stateChangeTypes.InputKeyDownEnter:
@@ -544,6 +568,8 @@ function AutocompleteInner(props, ref) {
544
568
  inputValue,
545
569
  reset: resetCombobox
546
570
  } = useCombobox(comboBoxProps);
571
+
572
+ // MARK: floating-ui setup
547
573
  const {
548
574
  x,
549
575
  y,
@@ -575,6 +601,8 @@ function AutocompleteInner(props, ref) {
575
601
  return autoUpdate(refs.reference.current, refs.floating.current, update);
576
602
  }
577
603
  }, [refs.reference, refs.floating, update, isOpen]);
604
+
605
+ // MARK: popover toggle
578
606
  useIsomorphicLayoutEffect(() => {
579
607
  if (isOpen) {
580
608
  refs.floating.current.showPopover();
@@ -587,10 +615,13 @@ function AutocompleteInner(props, ref) {
587
615
  //dont clear items if they are selected and disabled
588
616
  setSelectedItems([...selectedDisabledItemsSet]);
589
617
  setTypedInputValue('');
618
+ inputRef.current?.focus();
590
619
  };
591
620
  const showClearButton = (selectedItems.length > 0 || inputValue) && !readOnly && !hideClearButton;
592
621
  const showNoOptions = isOpen && !availableItems.length && noOptionsText.length > 0;
593
622
  const selectedItemsLabels = useMemo(() => selectedItems.map(getLabel), [selectedItems, getLabel]);
623
+
624
+ // MARK: optionsList
594
625
  const optionsList = /*#__PURE__*/jsx(StyledPopover, {
595
626
  popover: "manual",
596
627
  ...getFloatingProps({
@@ -689,9 +720,12 @@ function AutocompleteInner(props, ref) {
689
720
  });
690
721
  const inputProps = getInputProps(getDropdownProps({
691
722
  preventKeyAction: multiple ? isOpen : undefined,
692
- disabled
723
+ disabled,
724
+ ref: inputRef
693
725
  }));
694
726
  const consolidatedEvents = mergeEventsFromRight(other, inputProps);
727
+
728
+ // MARK: input
695
729
  return /*#__PURE__*/jsx(ThemeProvider, {
696
730
  theme: token,
697
731
  children: /*#__PURE__*/jsxs(Container, {
@@ -747,6 +781,7 @@ function AutocompleteInner(props, ref) {
747
781
  })
748
782
  });
749
783
  }
784
+ // MARK: exported component
750
785
  const Autocomplete = /*#__PURE__*/forwardRef(AutocompleteInner);
751
786
 
752
787
  export { Autocomplete };
@@ -27,9 +27,12 @@ const DatePicker = /*#__PURE__*/forwardRef(({
27
27
  defaultValue,
28
28
  showTimeInput,
29
29
  granularity,
30
+ hideClearButton,
30
31
  disabled: isDisabled,
31
32
  readOnly: isReadOnly,
32
33
  formatOptions,
34
+ helperProps,
35
+ variant,
33
36
  ...props
34
37
  }, forwardedRef) => {
35
38
  timezone = timezone ?? defaultTimezone;
@@ -84,7 +87,8 @@ const DatePicker = /*#__PURE__*/forwardRef(({
84
87
  locale
85
88
  } = useLocale();
86
89
  const dateCreateProps = {
87
- ...props,
90
+ helperProps,
91
+ variant,
88
92
  isDisabled,
89
93
  value: _value,
90
94
  hideTimeZone: true,
@@ -105,7 +109,7 @@ const DatePicker = /*#__PURE__*/forwardRef(({
105
109
  fieldProps,
106
110
  calendarProps
107
111
  } = useDatePicker(dateCreateProps, pickerState, ref);
108
- const helperProps = pickerState.displayValidation.isInvalid ? {
112
+ const helperPropsInvalid = pickerState.displayValidation.isInvalid ? {
109
113
  text: pickerState.displayValidation.validationErrors.join('\n'),
110
114
  color: tokens.colors.interactive.warning__text.rgba,
111
115
  icon: /*#__PURE__*/jsx(Icon, {
@@ -113,6 +117,7 @@ const DatePicker = /*#__PURE__*/forwardRef(({
113
117
  data: warning_outlined
114
118
  })
115
119
  } : undefined;
120
+ const showClearButton = pickerState.dateValue !== null && !hideClearButton;
116
121
 
117
122
  // innerValue is used as a fallback, especially for uncontrolled inputs, so it needs to be reset when value / defaultValue is reset
118
123
  useEffect(() => {
@@ -122,6 +127,7 @@ const DatePicker = /*#__PURE__*/forwardRef(({
122
127
  timezone: timezone,
123
128
  formatOptions: formatOptions,
124
129
  children: /*#__PURE__*/jsx(FieldWrapper, {
130
+ ...props,
125
131
  isOpen: isOpen,
126
132
  readonly: fieldProps.isReadOnly,
127
133
  pickerRef: pickerRef,
@@ -136,8 +142,8 @@ const DatePicker = /*#__PURE__*/forwardRef(({
136
142
  }),
137
143
  disabled: isDisabled,
138
144
  readOnly: isReadOnly,
139
- color: pickerState.isInvalid ? 'warning' : props.variant,
140
- helperProps: helperProps ?? props.helperProps,
145
+ color: pickerState.isInvalid ? 'warning' : variant,
146
+ helperProps: helperPropsInvalid ?? helperProps,
141
147
  children: /*#__PURE__*/jsx(DateField, {
142
148
  fieldProps: fieldProps,
143
149
  groupProps: groupProps,
@@ -145,6 +151,7 @@ const DatePicker = /*#__PURE__*/forwardRef(({
145
151
  ref: ref,
146
152
  onChange: _onChange,
147
153
  rightAdornments: /*#__PURE__*/jsx(Toggle, {
154
+ showClearButton: showClearButton,
148
155
  setOpen: setIsOpen,
149
156
  open: isOpen,
150
157
  icon: calendar,
@@ -158,7 +165,7 @@ const DatePicker = /*#__PURE__*/forwardRef(({
158
165
  day: '2-digit'
159
166
  })
160
167
  }),
161
- variant: props.variant
168
+ variant: variant
162
169
  })
163
170
  })
164
171
  });
@@ -26,6 +26,7 @@ const DateRangePicker = /*#__PURE__*/forwardRef(({
26
26
  timezone,
27
27
  defaultValue,
28
28
  formatOptions,
29
+ hideClearButton,
29
30
  disabled: isDisabled,
30
31
  readOnly: isReadOnly,
31
32
  ...props
@@ -103,6 +104,7 @@ const DateRangePicker = /*#__PURE__*/forwardRef(({
103
104
  data: warning_outlined
104
105
  })
105
106
  } : undefined;
107
+ const showClearButton = state.dateRange !== null && !hideClearButton;
106
108
  const formattedValue = state.formatValue(locale, {
107
109
  year: 'numeric',
108
110
  month: 'short',
@@ -145,6 +147,7 @@ const DateRangePicker = /*#__PURE__*/forwardRef(({
145
147
  variant: props.variant,
146
148
  disabled: isDisabled,
147
149
  rightAdornments: /*#__PURE__*/jsx(Toggle, {
150
+ showClearButton: showClearButton,
148
151
  buttonProps: buttonProps,
149
152
  disabled: isDisabled,
150
153
  readonly: isReadOnly,
@@ -1,6 +1,6 @@
1
1
  import { useState, useRef } from 'react';
2
2
  import { useDateFormatter, useDateSegment } from 'react-aria';
3
- import styled from 'styled-components';
3
+ import styled, { css } from 'styled-components';
4
4
  import { tokens } from '@equinor/eds-tokens';
5
5
  import { useDatePickerContext } from '../utils/context.js';
6
6
  import { jsx } from 'react/jsx-runtime';
@@ -8,9 +8,16 @@ import { jsx } from 'react/jsx-runtime';
8
8
  const Segment = styled.div.withConfig({
9
9
  displayName: "DateSegment__Segment",
10
10
  componentId: "sc-19uidpx-0"
11
- })(["color:", ";font-family:", ";&:focus-visible{outline:2px solid ", ";background-color:", ";}", ""], tokens.typography.input.text.color, tokens.typography.input.text.fontFamily, tokens.colors.interactive.primary__resting.rgba, tokens.colors.ui.background__medium.rgba, ({
11
+ })(({
12
+ $placeholder,
12
13
  $disabled
13
- }) => $disabled && `color: ${tokens.colors.interactive.disabled__text.rgba};`);
14
+ }) => {
15
+ return css(["font-family:", ";&:focus-visible{outline:2px solid ", ";background-color:", ";}", " ", ""], tokens.typography.input.text.fontFamily, tokens.colors.interactive.primary__resting.rgba, tokens.colors.ui.background__medium.rgba, $placeholder ? css({
16
+ color: tokens.colors.text.static_icons__tertiary.rgba
17
+ }) : css({
18
+ color: tokens.colors.text.static_icons__default.rgba
19
+ }), $disabled && css(["color:", ";"], tokens.colors.interactive.disabled__text.rgba));
20
+ });
14
21
 
15
22
  /**
16
23
  * DateSegment is used to represent a single segment of a date in the DateField (i.e. day, month, year)
@@ -21,10 +21,11 @@ const Toggle = ({
21
21
  disabled,
22
22
  buttonProps,
23
23
  valueString,
24
+ showClearButton,
24
25
  readonly
25
26
  }) => {
26
27
  return readonly || disabled ? null : /*#__PURE__*/jsxs(Fragment, {
27
- children: [/*#__PURE__*/jsx(StyledButton, {
28
+ children: [showClearButton && /*#__PURE__*/jsx(StyledButton, {
28
29
  disabled: disabled,
29
30
  variant: 'ghost_icon',
30
31
  "aria-label": 'Reset',
@@ -1,30 +1,11 @@
1
1
  import { HTMLAttributes, ReactNode } from 'react';
2
2
  import { Variants } from '../types';
3
- type OptionLabelProps<T> = T extends string | number ? {
4
- /** Custom option label */
5
- optionLabel?: (option: T) => string;
6
- /** Disable option
7
- * @default () => false
8
- */
9
- optionDisabled?: (option: T) => boolean;
10
- /** List of options in dropdown */
11
- options: (string | number)[];
12
- } : {
13
- /** Custom option label */
14
- optionLabel: (option: T) => string;
15
- /** Disable option
16
- * @default () => false
17
- */
18
- optionDisabled?: (option: T) => boolean;
19
- /** List of options in dropdown */
20
- options: T[];
21
- };
22
3
  export type AutocompleteChanges<T> = {
23
4
  selectedItems: T[];
24
5
  };
25
6
  export type AutocompleteProps<T> = {
26
7
  /** List of options in dropdown */
27
- options: T[];
8
+ options: readonly T[];
28
9
  /** Label for the select element */
29
10
  label: ReactNode;
30
11
  /** Array of initial selected items
@@ -76,12 +57,18 @@ export type AutocompleteProps<T> = {
76
57
  multiple?: boolean;
77
58
  /** Add select-all option. Throws an error if true while multiple = false */
78
59
  allowSelectAll?: boolean;
60
+ /** Custom option label. NOTE: This is required when option is an object */
61
+ optionLabel?: (option: T) => string;
79
62
  /** Custom option template */
80
63
  optionComponent?: (option: T, isSelected: boolean) => ReactNode;
81
64
  /** Disable use of react portal for dropdown
82
65
  * @deprecated Autocomplete now uses the native popover api to render the dropdown. This prop will be removed in a future version
83
66
  */
84
67
  disablePortal?: boolean;
68
+ /** Disable option
69
+ * @default () => false
70
+ */
71
+ optionDisabled?: (option: T) => boolean;
85
72
  /** Custom filter function for options */
86
73
  optionsFilter?: (option: T, inputValue: string) => boolean;
87
74
  /** If `true` the width of the dropdown will adjust to the width of the input */
@@ -104,11 +91,11 @@ export type AutocompleteProps<T> = {
104
91
  * Method that is used to compare objects by value. If omitted, objects are matched by reference.
105
92
  */
106
93
  itemCompare?: (value: T, compare: T) => boolean;
107
- } & HTMLAttributes<HTMLDivElement> & OptionLabelProps<T>;
94
+ } & HTMLAttributes<HTMLDivElement>;
108
95
  declare function AutocompleteInner<T>(props: AutocompleteProps<T>, ref: React.ForwardedRef<HTMLDivElement>): import("react/jsx-runtime").JSX.Element;
109
96
  export declare const Autocomplete: <T>(props: {
110
97
  /** List of options in dropdown */
111
- options: T[];
98
+ options: readonly T[];
112
99
  /** Label for the select element */
113
100
  label: ReactNode;
114
101
  /** Array of initial selected items
@@ -160,12 +147,18 @@ export declare const Autocomplete: <T>(props: {
160
147
  multiple?: boolean;
161
148
  /** Add select-all option. Throws an error if true while multiple = false */
162
149
  allowSelectAll?: boolean;
150
+ /** Custom option label. NOTE: This is required when option is an object */
151
+ optionLabel?: (option: T) => string;
163
152
  /** Custom option template */
164
153
  optionComponent?: (option: T, isSelected: boolean) => ReactNode;
165
154
  /** Disable use of react portal for dropdown
166
155
  * @deprecated Autocomplete now uses the native popover api to render the dropdown. This prop will be removed in a future version
167
156
  */
168
157
  disablePortal?: boolean;
158
+ /** Disable option
159
+ * @default () => false
160
+ */
161
+ optionDisabled?: (option: T) => boolean;
169
162
  /** Custom filter function for options */
170
163
  optionsFilter?: (option: T, inputValue: string) => boolean;
171
164
  /** If `true` the width of the dropdown will adjust to the width of the input */
@@ -188,7 +181,7 @@ export declare const Autocomplete: <T>(props: {
188
181
  * Method that is used to compare objects by value. If omitted, objects are matched by reference.
189
182
  */
190
183
  itemCompare?: (value: T, compare: T) => boolean;
191
- } & HTMLAttributes<HTMLDivElement> & OptionLabelProps<T> & {
184
+ } & HTMLAttributes<HTMLDivElement> & {
192
185
  ref?: React.ForwardedRef<HTMLDivElement>;
193
186
  /** @ignore */
194
187
  displayName?: string | undefined;
@@ -3,7 +3,7 @@
3
3
  * DatePicker component encapsulates the logic for selecting a single date.
4
4
  * Either by accessible input field or by a calendar.
5
5
  */
6
- export declare const DatePicker: import("react").ForwardRefExoticComponent<Partial<{
6
+ export declare const DatePicker: import("react").ForwardRefExoticComponent<Omit<import("react").HTMLAttributes<HTMLDivElement>, "onChange"> & Partial<{
7
7
  variant: import("../types").Variants;
8
8
  disabled?: boolean;
9
9
  readOnly?: boolean;
@@ -17,6 +17,7 @@ export declare const DatePicker: import("react").ForwardRefExoticComponent<Parti
17
17
  Footer: (props: import("./props").HeaderFooterProps<import("@react-stately/calendar").CalendarState>) => import("react").ReactNode;
18
18
  Header: (props: import("./props").HeaderFooterProps<import("@react-stately/calendar").CalendarState>) => import("react").ReactNode;
19
19
  showTimeInput?: boolean;
20
+ hideClearButton?: boolean;
20
21
  defaultValue?: Date;
21
22
  timezone: string;
22
23
  granularity?: "hour" | "minute" | "second";
@@ -3,25 +3,7 @@
3
3
  * DateRangePicker component encapsulates the logic for selecting a range of dates
4
4
  * Either by accessible input fields or by a calendar.
5
5
  */
6
- export declare const DateRangePicker: import("react").ForwardRefExoticComponent<Omit<Partial<{
7
- variant: import("../types").Variants;
8
- disabled?: boolean;
9
- readOnly?: boolean;
10
- helperProps?: import("../InputWrapper/HelperText").HelperTextProps;
11
- value: Date;
12
- onChange: (date: Date) => void;
13
- label: string;
14
- minValue: Date;
15
- maxValue: Date;
16
- isDateUnavailable?: (v: Date) => boolean;
17
- Footer: (props: import("./props").HeaderFooterProps<import("@react-stately/calendar").CalendarState>) => import("react").ReactNode;
18
- Header: (props: import("./props").HeaderFooterProps<import("@react-stately/calendar").CalendarState>) => import("react").ReactNode;
19
- showTimeInput?: boolean;
20
- defaultValue?: Date;
21
- timezone: string;
22
- granularity?: "hour" | "minute" | "second";
23
- formatOptions?: import("react-aria").DateFormatterOptions;
24
- }>, "defaultValue" | "onChange" | "value" | "multiple" | "showTimeInput" | "stateRef"> & Partial<{
6
+ export declare const DateRangePicker: import("react").ForwardRefExoticComponent<Omit<import("./props").DatePickerProps, "defaultValue" | "onChange" | "value" | "multiple" | "showTimeInput" | "stateRef"> & Partial<{
25
7
  onChange: (range: {
26
8
  from: Date;
27
9
  to: Date;
@@ -3,7 +3,7 @@ import { AriaButtonProps } from 'react-aria';
3
3
  /**
4
4
  * Toggle component encapsulates the reset and open calendar buttons
5
5
  */
6
- export declare const Toggle: ({ reset, setOpen, open, icon, disabled, buttonProps, valueString, readonly, }: {
6
+ export declare const Toggle: ({ reset, setOpen, open, icon, disabled, buttonProps, valueString, showClearButton, readonly, }: {
7
7
  reset: () => void;
8
8
  setOpen: (open: boolean) => void;
9
9
  open: boolean;
@@ -12,4 +12,5 @@ export declare const Toggle: ({ reset, setOpen, open, icon, disabled, buttonProp
12
12
  readonly: boolean;
13
13
  buttonProps: AriaButtonProps;
14
14
  valueString: string;
15
+ showClearButton: boolean;
15
16
  }) => import("react/jsx-runtime").JSX.Element;
@@ -1,2 +1,3 @@
1
1
  export * from './DatePicker';
2
2
  export * from './DateRangePicker';
3
+ export * from './props';
@@ -1,4 +1,4 @@
1
- import { MutableRefObject, ReactNode } from 'react';
1
+ import { MutableRefObject, ReactNode, HTMLAttributes } from 'react';
2
2
  import { CalendarState, RangeCalendarState } from '@react-stately/calendar';
3
3
  import { Variants } from '../types';
4
4
  import { HelperTextProps } from '../InputWrapper/HelperText';
@@ -14,7 +14,7 @@ export type HeaderFooterProps<T = CalendarState | RangeCalendarState> = {
14
14
  month: number;
15
15
  state: T;
16
16
  };
17
- export type DatePickerProps = Partial<{
17
+ export type DatePickerProps = Omit<HTMLAttributes<HTMLDivElement>, 'onChange'> & Partial<{
18
18
  /**
19
19
  * The variant / state of the datepicker field
20
20
  * @default undefined
@@ -71,6 +71,11 @@ export type DatePickerProps = Partial<{
71
71
  * Whether to allow picking the time as well as the date
72
72
  */
73
73
  showTimeInput?: boolean;
74
+ /**
75
+ * hide the clear button even when date is set
76
+ * @default false
77
+ */
78
+ hideClearButton?: boolean;
74
79
  /**
75
80
  * Uncontrolled value
76
81
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@equinor/eds-core-react",
3
- "version": "0.39.0",
3
+ "version": "0.40.0",
4
4
  "description": "The React implementation of the Equinor Design System",
5
5
  "sideEffects": [
6
6
  "**/*.css"
@@ -86,16 +86,12 @@
86
86
  "@react-stately/datepicker": "^3.9.3",
87
87
  "@react-types/shared": "^3.23.0",
88
88
  "@tanstack/react-virtual": "3.5.0",
89
- "downshift": "9.0.4",
89
+ "downshift": "9.0.6",
90
90
  "react-aria": "^3.33.0",
91
91
  "@equinor/eds-icons": "^0.21.0",
92
92
  "@equinor/eds-tokens": "0.9.2",
93
93
  "@equinor/eds-utils": "0.8.5"
94
94
  },
95
- "engines": {
96
- "pnpm": ">=4",
97
- "node": ">=10.0.0"
98
- },
99
95
  "scripts": {
100
96
  "build": "rollup -c --bundleConfigAsCjs && tsc -p tsconfig.build.json",
101
97
  "test": "tsc -p tsconfig.test.json && jest",