@equinor/eds-core-react 2.4.0 → 2.4.1-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 (48) hide show
  1. package/build/index.css +370 -29
  2. package/build/index.min.css +1 -1
  3. package/dist/eds-core-react.cjs +24 -4
  4. package/dist/esm/components/Autocomplete/MultipleInput.js +19 -5
  5. package/dist/esm/components/Autocomplete/useAutocomplete.js +6 -0
  6. package/dist/esm/components/InputWrapper/InputWrapper.js +1 -1
  7. package/dist/esm/components/Textarea/Textarea.js +1 -1
  8. package/dist/esm-next/components/next/Banner/Banner.js +88 -0
  9. package/dist/esm-next/components/next/Button/Button.js +1 -2
  10. package/dist/esm-next/components/next/Input/Input.js +1 -1
  11. package/dist/esm-next/components/next/Link/Link.js +36 -0
  12. package/dist/esm-next/components/next/Search/Search.js +118 -0
  13. package/dist/esm-next/components/next/Slot/Slot.js +47 -0
  14. package/dist/esm-next/components/next/TextArea/TextArea.js +131 -0
  15. package/dist/esm-next/components/next/TextField/TextField.js +0 -1
  16. package/dist/esm-next/components/next/Tooltip/Tooltip.js +84 -0
  17. package/dist/esm-next/index.next.js +6 -0
  18. package/dist/index.next.cjs +474 -8
  19. package/dist/types/components/Autocomplete/Autocomplete.d.ts +5 -0
  20. package/dist/types/components/Autocomplete/AutocompleteContext.d.ts +6 -2
  21. package/dist/types/components/Autocomplete/useAutocomplete.d.ts +4 -2
  22. package/dist/types/components/next/Banner/Banner.d.ts +23 -0
  23. package/dist/types/components/next/Banner/Banner.figma.d.ts +1 -0
  24. package/dist/types/components/next/Banner/Banner.types.d.ts +33 -0
  25. package/dist/types/components/next/Banner/index.d.ts +2 -0
  26. package/dist/types/components/next/Button/Button.types.d.ts +1 -2
  27. package/dist/types/components/next/Input/Input.types.d.ts +4 -1
  28. package/dist/types/components/next/Link/Link.d.ts +4 -0
  29. package/dist/types/components/next/Link/Link.figma.d.ts +1 -0
  30. package/dist/types/components/next/Link/Link.types.d.ts +13 -0
  31. package/dist/types/components/next/Link/index.d.ts +2 -0
  32. package/dist/types/components/next/Search/Search.d.ts +9 -0
  33. package/dist/types/components/next/Search/Search.figma.d.ts +1 -0
  34. package/dist/types/components/next/Search/Search.types.d.ts +16 -0
  35. package/dist/types/components/next/Search/index.d.ts +2 -0
  36. package/dist/types/components/next/Slot/Slot.d.ts +3 -0
  37. package/dist/types/components/next/Slot/Slot.types.d.ts +4 -0
  38. package/dist/types/components/next/Slot/index.d.ts +2 -0
  39. package/dist/types/components/next/TextArea/TextArea.d.ts +11 -0
  40. package/dist/types/components/next/TextArea/TextArea.figma.d.ts +1 -0
  41. package/dist/types/components/next/TextArea/TextArea.types.d.ts +21 -0
  42. package/dist/types/components/next/TextArea/index.d.ts +2 -0
  43. package/dist/types/components/next/Tooltip/Tooltip.d.ts +7 -0
  44. package/dist/types/components/next/Tooltip/Tooltip.figma.d.ts +1 -0
  45. package/dist/types/components/next/Tooltip/Tooltip.types.d.ts +17 -0
  46. package/dist/types/components/next/Tooltip/index.d.ts +2 -0
  47. package/dist/types/components/next/index.d.ts +12 -0
  48. package/package.json +39 -32
@@ -23,6 +23,7 @@ const useAutocomplete = ({
23
23
  onInputChange,
24
24
  selectedOptions: _selectedOptions,
25
25
  selectionDisplay = 'summary',
26
+ chipCount,
26
27
  multiple,
27
28
  itemToKey: _itemToKey,
28
29
  itemCompare: _itemCompare,
@@ -43,6 +44,7 @@ const useAutocomplete = ({
43
44
  variant,
44
45
  onClear,
45
46
  ref,
47
+ id,
46
48
  ...other
47
49
  }) => {
48
50
  const [lastScrollOffset, setLastScrollOffset] = useState(0);
@@ -204,6 +206,9 @@ const useAutocomplete = ({
204
206
 
205
207
  // MARK: downshift state
206
208
  let comboBoxProps = {
209
+ ...(id !== undefined && {
210
+ inputId: id
211
+ }),
207
212
  items: availableItems,
208
213
  //can not pass readonly type to downshift so we cast it to regular T[]
209
214
  initialSelectedItem: initialSelectedOptions[0],
@@ -591,6 +596,7 @@ const useAutocomplete = ({
591
596
  onInputChange,
592
597
  selectedOptions,
593
598
  selectionDisplay,
599
+ chipCount,
594
600
  itemToKey,
595
601
  itemCompare,
596
602
  allowSelectAll,
@@ -3,9 +3,9 @@ import styled, { ThemeProvider } from 'styled-components';
3
3
  import { useToken } from '@equinor/eds-utils';
4
4
  import { inputToken } from './InputWrapper.tokens.js';
5
5
  import { jsx, jsxs } from 'react/jsx-runtime';
6
+ import { useEds } from '../EdsProvider/eds.context.js';
6
7
  import { Label as Label$1 } from '../Label/Label.js';
7
8
  import { HelperText as TextfieldHelperText } from './HelperText/HelperText.js';
8
- import { useEds } from '../EdsProvider/eds.context.js';
9
9
 
10
10
  const Container = styled.div.withConfig({
11
11
  displayName: "InputWrapper__Container",
@@ -4,8 +4,8 @@ import { input as input$1 } from '../Input/Input.tokens.js';
4
4
  import { useAutoResize, mergeRefs } from '@equinor/eds-utils';
5
5
  import { jsx } from 'react/jsx-runtime';
6
6
  import { useInputField } from '../InputWrapper/useInputField.js';
7
- import { InputWrapper } from '../InputWrapper/InputWrapper.js';
8
7
  import { useEds } from '../EdsProvider/eds.context.js';
8
+ import { InputWrapper } from '../InputWrapper/InputWrapper.js';
9
9
  import { Input } from '../Input/Input.js';
10
10
 
11
11
  const {
@@ -0,0 +1,88 @@
1
+ import { forwardRef } from 'react';
2
+ import { close } from '@equinor/eds-icons';
3
+ import { jsxs, jsx } from 'react/jsx-runtime';
4
+ import { Button } from '../Button/Button.js';
5
+ import { Icon } from '../Icon/Icon.js';
6
+ import { TypographyNext } from '../../Typography/Typography.new.js';
7
+
8
+ const BannerIcon = /*#__PURE__*/forwardRef(function BannerIcon({
9
+ className,
10
+ children,
11
+ ...rest
12
+ }, ref) {
13
+ return /*#__PURE__*/jsx("span", {
14
+ ref: ref,
15
+ className: ['eds-banner__icon', className].filter(Boolean).join(' '),
16
+ ...rest,
17
+ children: children
18
+ });
19
+ });
20
+ const BannerMessage = /*#__PURE__*/forwardRef(function BannerMessage({
21
+ className,
22
+ children,
23
+ ...rest
24
+ }, ref) {
25
+ return /*#__PURE__*/jsx(TypographyNext, {
26
+ ref: ref,
27
+ as: "p",
28
+ family: "ui",
29
+ size: "md",
30
+ baseline: "center",
31
+ lineHeight: "default",
32
+ tracking: "normal",
33
+ className: ['eds-banner__message', className].filter(Boolean).join(' '),
34
+ ...rest,
35
+ children: children
36
+ });
37
+ });
38
+ const BannerActions = /*#__PURE__*/forwardRef(function BannerActions({
39
+ placement = 'left',
40
+ className,
41
+ children,
42
+ ...rest
43
+ }, ref) {
44
+ return /*#__PURE__*/jsx("div", {
45
+ ref: ref,
46
+ className: ['eds-banner__actions', className].filter(Boolean).join(' '),
47
+ "data-placement": placement,
48
+ ...rest,
49
+ children: children
50
+ });
51
+ });
52
+ const BannerComponent = /*#__PURE__*/forwardRef(function Banner({
53
+ tone = 'info',
54
+ role = 'status',
55
+ onDismiss,
56
+ className,
57
+ children,
58
+ ...rest
59
+ }, ref) {
60
+ return /*#__PURE__*/jsxs("div", {
61
+ ref: ref,
62
+ className: ['eds-banner', className].filter(Boolean).join(' '),
63
+ "data-color-appearance": tone,
64
+ role: role,
65
+ ...rest,
66
+ children: [children, onDismiss && /*#__PURE__*/jsx(Button, {
67
+ variant: "ghost",
68
+ icon: true,
69
+ size: "small",
70
+ className: "eds-banner__dismiss",
71
+ "aria-label": "Dismiss",
72
+ onClick: onDismiss,
73
+ children: /*#__PURE__*/jsx(Icon, {
74
+ data: close
75
+ })
76
+ })]
77
+ });
78
+ });
79
+ BannerIcon.displayName = 'Banner.Icon';
80
+ BannerMessage.displayName = 'Banner.Message';
81
+ BannerActions.displayName = 'Banner.Actions';
82
+ BannerComponent.displayName = 'Banner';
83
+ const Banner = BannerComponent;
84
+ Banner.Icon = BannerIcon;
85
+ Banner.Message = BannerMessage;
86
+ Banner.Actions = BannerActions;
87
+
88
+ export { Banner };
@@ -4,8 +4,7 @@ import { TypographyNext } from '../../Typography/Typography.new.js';
4
4
 
5
5
  const SIZE_MAPPING = {
6
6
  small: 'sm',
7
- default: 'md',
8
- large: 'lg'
7
+ default: 'md'
9
8
  };
10
9
  const sizeToTypography = SIZE_MAPPING;
11
10
  const sizeToSelectableSpace = SIZE_MAPPING;
@@ -56,7 +56,7 @@ const Input = /*#__PURE__*/forwardRef(function Input({
56
56
  })]
57
57
  }), /*#__PURE__*/jsx(Component, {
58
58
  ref: ref,
59
- type: type,
59
+ type: Component === 'textarea' ? undefined : type,
60
60
  disabled: disabled,
61
61
  readOnly: readOnly,
62
62
  className: ['eds-input', className].filter(Boolean).join(' '),
@@ -0,0 +1,36 @@
1
+ import { forwardRef } from 'react';
2
+ import { jsx } from 'react/jsx-runtime';
3
+ import { Slot } from '../Slot/Slot.js';
4
+
5
+ const Link = /*#__PURE__*/forwardRef(function Link({
6
+ variant = 'inline',
7
+ asChild,
8
+ className,
9
+ children,
10
+ ...rest
11
+ }, ref) {
12
+ const classes = ['eds-link', className].filter(Boolean).join(' ');
13
+ const sharedProps = {
14
+ ref,
15
+ className: classes,
16
+ 'data-variant': variant,
17
+ 'data-color-appearance': 'info',
18
+ 'data-font-family': variant === 'standalone' ? 'ui' : undefined,
19
+ 'data-font-size': variant === 'standalone' ? 'md' : undefined,
20
+ 'data-line-height': variant === 'standalone' ? 'squished' : undefined,
21
+ ...rest
22
+ };
23
+ if (asChild) {
24
+ return /*#__PURE__*/jsx(Slot, {
25
+ ...sharedProps,
26
+ children: children
27
+ });
28
+ }
29
+ return /*#__PURE__*/jsx("a", {
30
+ ...sharedProps,
31
+ children: children
32
+ });
33
+ });
34
+ Link.displayName = 'Link';
35
+
36
+ export { Link };
@@ -0,0 +1,118 @@
1
+ import { forwardRef, useState, useRef, useCallback } from 'react';
2
+ import { close, search } from '@equinor/eds-icons';
3
+ import { jsx, jsxs } from 'react/jsx-runtime';
4
+ import { useFieldIds } from '../Field/useFieldIds.js';
5
+ import { Field } from '../Field/Field.js';
6
+ import { Input } from '../Input/Input.js';
7
+ import { Button } from '../Button/Button.js';
8
+ import { Icon } from '../Icon/Icon.js';
9
+
10
+ const Search = /*#__PURE__*/forwardRef(function Search({
11
+ label,
12
+ description,
13
+ helperMessage,
14
+ id: providedId,
15
+ invalid,
16
+ disabled,
17
+ readOnly,
18
+ value,
19
+ defaultValue,
20
+ onChange,
21
+ onClear,
22
+ clearLabel = 'Clear search',
23
+ ...inputProps
24
+ }, forwardedRef) {
25
+ const {
26
+ inputId,
27
+ labelId,
28
+ descriptionId,
29
+ helperMessageId,
30
+ getDescribedBy
31
+ } = useFieldIds(providedId);
32
+ const isControlled = value !== undefined;
33
+ const [internalHasValue, setInternalHasValue] = useState(() => Boolean(defaultValue));
34
+ const hasValue = isControlled ? Boolean(value) : internalHasValue;
35
+ const inputRef = useRef(null);
36
+ const mergedRef = useCallback(node => {
37
+ inputRef.current = node;
38
+ if (typeof forwardedRef === 'function') {
39
+ forwardedRef(node);
40
+ } else if (forwardedRef) {
41
+ forwardedRef.current = node;
42
+ }
43
+ }, [forwardedRef]);
44
+ const handleChange = e => {
45
+ if (!isControlled) {
46
+ setInternalHasValue(Boolean(e.target.value));
47
+ }
48
+ onChange?.(e);
49
+ };
50
+ const handleClear = () => {
51
+ if (!isControlled && inputRef.current) {
52
+ // Direct DOM mutation: bypasses React's synthetic onChange, which is
53
+ // intentional — onClear is the designated callback for clear actions.
54
+ inputRef.current.value = '';
55
+ setInternalHasValue(false);
56
+ }
57
+ onClear?.();
58
+ inputRef.current?.focus();
59
+ };
60
+ const showClear = hasValue && !disabled && !readOnly;
61
+ // Accent only in interactive states — grey in error, readonly, disabled
62
+ const iconTone = disabled || readOnly || invalid ? 'neutral' : 'accent';
63
+ return /*#__PURE__*/jsx("search", {
64
+ className: "eds-search",
65
+ "aria-labelledby": label ? labelId : undefined,
66
+ children: /*#__PURE__*/jsxs(Field, {
67
+ disabled: disabled,
68
+ children: [label && /*#__PURE__*/jsx(Field.Label, {
69
+ id: labelId,
70
+ htmlFor: inputId,
71
+ children: label
72
+ }), description && /*#__PURE__*/jsx(Field.Description, {
73
+ id: descriptionId,
74
+ children: description
75
+ }), /*#__PURE__*/jsx(Input, {
76
+ ref: mergedRef,
77
+ id: inputId,
78
+ type: "search",
79
+ disabled: disabled,
80
+ readOnly: readOnly,
81
+ invalid: invalid,
82
+ value: value,
83
+ defaultValue: defaultValue,
84
+ onChange: handleChange,
85
+ "aria-describedby": getDescribedBy({
86
+ hasDescription: !!description,
87
+ hasHelperMessage: !!helperMessage
88
+ }),
89
+ hideErrorIcon: true,
90
+ startAdornment: /*#__PURE__*/jsx(Icon, {
91
+ data: search,
92
+ className: "search-icon",
93
+ "data-color-appearance": iconTone
94
+ }),
95
+ endAdornment: showClear ? /*#__PURE__*/jsx(Button, {
96
+ variant: "ghost",
97
+ icon: true,
98
+ round: true,
99
+ size: "small",
100
+ tone: invalid ? 'neutral' : 'accent',
101
+ onClick: handleClear,
102
+ "aria-label": clearLabel,
103
+ children: /*#__PURE__*/jsx(Icon, {
104
+ data: close
105
+ })
106
+ }) : undefined,
107
+ ...inputProps
108
+ }), helperMessage && /*#__PURE__*/jsx(Field.HelperMessage, {
109
+ id: helperMessageId,
110
+ role: invalid ? 'alert' : undefined,
111
+ children: helperMessage
112
+ })]
113
+ })
114
+ });
115
+ });
116
+ Search.displayName = 'Search';
117
+
118
+ export { Search };
@@ -0,0 +1,47 @@
1
+ import { forwardRef, isValidElement, cloneElement } from 'react';
2
+
3
+ function mergeClassNames(...classNames) {
4
+ return classNames.filter(Boolean).join(' ');
5
+ }
6
+ function mergeProps(slotProps, childProps) {
7
+ const merged = {
8
+ ...childProps
9
+ };
10
+ for (const key of Object.keys(slotProps)) {
11
+ const slotValue = slotProps[key];
12
+ const childValue = childProps[key];
13
+ if (key === 'className') {
14
+ merged[key] = mergeClassNames(slotValue, childValue);
15
+ } else if (key === 'style') {
16
+ merged[key] = {
17
+ ...slotValue,
18
+ ...childValue
19
+ };
20
+ } else if (typeof slotValue === 'function' && typeof childValue === 'function') {
21
+ merged[key] = (...args) => {
22
+ childValue(...args);
23
+ slotValue(...args);
24
+ };
25
+ } else if (slotValue !== undefined) {
26
+ merged[key] = slotValue;
27
+ }
28
+ }
29
+ return merged;
30
+ }
31
+ const Slot = /*#__PURE__*/forwardRef(function Slot({
32
+ children,
33
+ ...slotProps
34
+ }, ref) {
35
+ if (! /*#__PURE__*/isValidElement(children)) {
36
+ return null;
37
+ }
38
+ const child = children;
39
+ const merged = mergeProps(slotProps, child.props);
40
+ return /*#__PURE__*/cloneElement(child, {
41
+ ...merged,
42
+ ref
43
+ });
44
+ });
45
+ Slot.displayName = 'Slot';
46
+
47
+ export { Slot };
@@ -0,0 +1,131 @@
1
+ import { forwardRef, useState, useEffect, useRef, useMemo } from 'react';
2
+ import { useAutoResize, mergeRefs } from '@equinor/eds-utils';
3
+ import { info_circle } from '@equinor/eds-icons';
4
+ import { jsxs, jsx } from 'react/jsx-runtime';
5
+ import { useFieldIds } from '../Field/useFieldIds.js';
6
+ import { Field } from '../Field/Field.js';
7
+ import { Tooltip } from '../../Tooltip/Tooltip.js';
8
+ import { Button } from '../Button/Button.js';
9
+ import { Icon } from '../Icon/Icon.js';
10
+ import { Input } from '../Input/Input.js';
11
+
12
+ const TextArea = /*#__PURE__*/forwardRef(function TextArea({
13
+ label,
14
+ labelInfo,
15
+ indicator,
16
+ description,
17
+ helperMessage,
18
+ id: providedId,
19
+ invalid,
20
+ disabled,
21
+ maxRows,
22
+ showCharacterCount,
23
+ ...textareaProps
24
+ }, ref) {
25
+ const {
26
+ inputId,
27
+ descriptionId,
28
+ helperMessageId,
29
+ getDescribedBy
30
+ } = useFieldIds(providedId);
31
+ const [charCount, setCharCount] = useState(() => String(textareaProps.value ?? textareaProps.defaultValue ?? '').length);
32
+ useEffect(() => {
33
+ if (textareaProps.value !== undefined) {
34
+ setCharCount(String(textareaProps.value).length);
35
+ }
36
+ }, [textareaProps.value]);
37
+ const {
38
+ maxLength,
39
+ onChange: onChangeProp,
40
+ ...restTextareaProps
41
+ } = textareaProps;
42
+ const handleChange = e => {
43
+ setCharCount(e.target.value.length);
44
+ onChangeProp?.(e);
45
+ };
46
+ const internalRef = useRef(null);
47
+ const [maxPixelHeight, setMaxPixelHeight] = useState(undefined);
48
+
49
+ // Auto-grow is always on. When maxRows is set, compute a pixel cap after
50
+ // mount using the element's actual rendered line-height and padding
51
+ // (density-aware). Until the cap is computed (or if maxRows is not set),
52
+ // pass Infinity so the textarea grows without bound.
53
+ const autoResizeHeight = maxRows !== undefined && maxPixelHeight !== undefined ? maxPixelHeight : Infinity;
54
+ const autoResizeRef = useAutoResize(autoResizeHeight);
55
+ useEffect(() => {
56
+ if (!maxRows || !internalRef.current) return;
57
+ const el = internalRef.current;
58
+ const updateMaxHeight = () => {
59
+ const style = window.getComputedStyle(el);
60
+ const lineHeight = parseFloat(style.lineHeight);
61
+ const paddingBlockStart = parseFloat(style.paddingBlockStart);
62
+ const paddingBlockEnd = parseFloat(style.paddingBlockEnd);
63
+ setMaxPixelHeight(lineHeight * maxRows + paddingBlockStart + paddingBlockEnd);
64
+ };
65
+ const observer = new ResizeObserver(updateMaxHeight);
66
+ observer.observe(el);
67
+ updateMaxHeight();
68
+ return () => observer.disconnect();
69
+ }, [maxRows]);
70
+ const combinedRef = useMemo(() => mergeRefs(ref, autoResizeRef, internalRef), [ref, autoResizeRef, internalRef]);
71
+ const showHelperRow = helperMessage || showCharacterCount;
72
+ return /*#__PURE__*/jsxs(Field, {
73
+ className: "eds-text-area",
74
+ disabled: disabled,
75
+ children: [label && /*#__PURE__*/jsxs("div", {
76
+ className: "label-row",
77
+ children: [/*#__PURE__*/jsx(Field.Label, {
78
+ htmlFor: inputId,
79
+ indicator: indicator,
80
+ children: label
81
+ }), labelInfo && /*#__PURE__*/jsx(Tooltip, {
82
+ title: labelInfo,
83
+ placement: "top",
84
+ children: /*#__PURE__*/jsx(Button, {
85
+ variant: "ghost",
86
+ icon: true,
87
+ round: true,
88
+ size: "small",
89
+ tone: "neutral",
90
+ "aria-label": "More information",
91
+ children: /*#__PURE__*/jsx(Icon, {
92
+ data: info_circle,
93
+ size: "xs"
94
+ })
95
+ })
96
+ })]
97
+ }), description && /*#__PURE__*/jsx(Field.Description, {
98
+ id: descriptionId,
99
+ children: description
100
+ }), /*#__PURE__*/jsx(Input, {
101
+ ref: combinedRef,
102
+ as: "textarea",
103
+ id: inputId,
104
+ disabled: disabled,
105
+ invalid: invalid,
106
+ maxLength: maxLength,
107
+ onChange: handleChange,
108
+ "aria-describedby": getDescribedBy({
109
+ hasDescription: !!description,
110
+ hasHelperMessage: !!helperMessage
111
+ }),
112
+ ...restTextareaProps
113
+ }), showHelperRow && /*#__PURE__*/jsxs("div", {
114
+ className: "helper-row",
115
+ children: [helperMessage && /*#__PURE__*/jsx(Field.HelperMessage, {
116
+ id: helperMessageId,
117
+ role: invalid ? 'alert' : undefined,
118
+ children: helperMessage
119
+ }), showCharacterCount && /*#__PURE__*/jsx("span", {
120
+ className: "char-count",
121
+ "data-font-family": "ui",
122
+ "data-font-size": "xs",
123
+ "aria-live": maxLength !== undefined && charCount >= maxLength * 0.8 ? 'polite' : 'off',
124
+ children: maxLength !== undefined ? `${charCount} / ${maxLength}` : charCount
125
+ })]
126
+ })]
127
+ });
128
+ });
129
+ TextArea.displayName = 'TextArea';
130
+
131
+ export { TextArea };
@@ -27,7 +27,6 @@ const TextField = /*#__PURE__*/forwardRef(function TextField({
27
27
  } = useFieldIds(providedId);
28
28
  return /*#__PURE__*/jsxs(Field, {
29
29
  disabled: disabled,
30
- className: "eds-text-field",
31
30
  children: [label && /*#__PURE__*/jsxs("div", {
32
31
  className: "eds-text-field__header",
33
32
  children: [/*#__PURE__*/jsx(Field.Label, {
@@ -0,0 +1,84 @@
1
+ import { forwardRef, useId, useRef, cloneElement } from 'react';
2
+ import { jsxs, jsx } from 'react/jsx-runtime';
3
+
4
+ const Tooltip = /*#__PURE__*/forwardRef(function Tooltip({
5
+ title,
6
+ placement = 'top',
7
+ disabled,
8
+ children,
9
+ className,
10
+ onMouseEnter: onMouseEnterProp,
11
+ onMouseLeave: onMouseLeaveProp,
12
+ ...rest
13
+ }, ref) {
14
+ const uid = useId();
15
+ const tooltipId = `eds-tooltip-${uid.replace(/:/g, '')}`;
16
+ const anchorName = `--${tooltipId}`;
17
+ const tooltipRef = useRef(null);
18
+ const hideTimer = useRef(null);
19
+ const active = Boolean(title) && !disabled;
20
+ const show = () => {
21
+ if (hideTimer.current) clearTimeout(hideTimer.current);
22
+ if (!tooltipRef.current?.matches(':popover-open')) {
23
+ tooltipRef.current?.showPopover();
24
+ }
25
+ };
26
+
27
+ // Short delay so mouse can travel from trigger to tooltip (WCAG 1.4.13 — hoverable)
28
+ const hide = () => {
29
+ hideTimer.current = setTimeout(() => {
30
+ if (tooltipRef.current?.matches(':popover-open')) {
31
+ tooltipRef.current.hidePopover();
32
+ }
33
+ }, 100);
34
+ };
35
+
36
+ // Merge forwarded ref with internal ref
37
+ const setRef = node => {
38
+ tooltipRef.current = node;
39
+ if (typeof ref === 'function') ref(node);else if (ref) ref.current = node;
40
+ };
41
+ if (!active) return children;
42
+ return /*#__PURE__*/jsxs("span", {
43
+ className: "eds-tooltip-anchor",
44
+ style: {
45
+ '--tooltip-anchor-name': anchorName
46
+ },
47
+ onMouseEnter: show,
48
+ onMouseLeave: hide,
49
+ onFocus: show,
50
+ onBlur: hide,
51
+ children: [/*#__PURE__*/cloneElement(children, {
52
+ 'aria-describedby': tooltipId
53
+ }), /*#__PURE__*/jsx("div", {
54
+ ref: setRef,
55
+ id: tooltipId,
56
+ role: "tooltip"
57
+ // hint: top-layer + Escape/light-dismiss. Safari falls back to manual (no Escape).
58
+ ,
59
+ popover: "hint",
60
+ className: ['eds-tooltip', className].filter(Boolean).join(' '),
61
+ "data-placement": placement,
62
+ "data-space-proportions": "squished",
63
+ ...rest,
64
+ onMouseEnter: e => {
65
+ if (hideTimer.current) clearTimeout(hideTimer.current);
66
+ onMouseEnterProp?.(e);
67
+ },
68
+ onMouseLeave: e => {
69
+ hide();
70
+ onMouseLeaveProp?.(e);
71
+ },
72
+ children: /*#__PURE__*/jsx("span", {
73
+ className: "label",
74
+ "data-font-family": "ui",
75
+ "data-font-size": "sm",
76
+ "data-baseline": "center",
77
+ children: title
78
+ })
79
+ })]
80
+ });
81
+ });
82
+ Tooltip.displayName = 'Tooltip';
83
+
84
+ export { Tooltip };
@@ -1,9 +1,15 @@
1
+ export { Banner } from './components/next/Banner/Banner.js';
1
2
  export { Button } from './components/next/Button/Button.js';
2
3
  export { Checkbox } from './components/next/Checkbox/Checkbox.js';
3
4
  export { Field } from './components/next/Field/Field.js';
4
5
  export { Icon } from './components/next/Icon/Icon.js';
5
6
  export { Input } from './components/next/Input/Input.js';
7
+ export { Link } from './components/next/Link/Link.js';
6
8
  export { Radio } from './components/next/Radio/Radio.js';
9
+ export { Search } from './components/next/Search/Search.js';
10
+ export { Slot } from './components/next/Slot/Slot.js';
7
11
  export { Switch } from './components/next/Switch/Switch.js';
12
+ export { TextArea } from './components/next/TextArea/TextArea.js';
8
13
  export { TextField } from './components/next/TextField/TextField.js';
14
+ export { Tooltip } from './components/next/Tooltip/Tooltip.js';
9
15
  export { useFieldIds } from './components/next/Field/useFieldIds.js';