@equinor/eds-core-react 2.4.0 → 2.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/index.css +336 -2
- package/build/index.min.css +1 -1
- package/dist/eds-core-react.cjs +5 -1
- package/dist/esm/components/Autocomplete/MultipleInput.js +2 -2
- package/dist/esm/components/Autocomplete/useAutocomplete.js +4 -0
- package/dist/esm/components/InputWrapper/InputWrapper.js +1 -1
- package/dist/esm/components/Textarea/Textarea.js +1 -1
- package/dist/esm-next/components/next/Banner/Banner.js +88 -0
- package/dist/esm-next/components/next/Input/Input.js +1 -1
- package/dist/esm-next/components/next/Link/Link.js +24 -0
- package/dist/esm-next/components/next/Search/Search.js +118 -0
- package/dist/esm-next/components/next/TextArea/TextArea.js +131 -0
- package/dist/esm-next/components/next/TextField/TextField.js +0 -1
- package/dist/esm-next/components/next/Tooltip/Tooltip.js +84 -0
- package/dist/esm-next/index.next.js +5 -0
- package/dist/index.next.cjs +417 -6
- package/dist/types/components/Autocomplete/AutocompleteContext.d.ts +2 -2
- package/dist/types/components/Autocomplete/useAutocomplete.d.ts +2 -2
- package/dist/types/components/next/Banner/Banner.d.ts +23 -0
- package/dist/types/components/next/Banner/Banner.figma.d.ts +1 -0
- package/dist/types/components/next/Banner/Banner.types.d.ts +33 -0
- package/dist/types/components/next/Banner/index.d.ts +2 -0
- package/dist/types/components/next/Input/Input.types.d.ts +4 -1
- package/dist/types/components/next/Link/Link.d.ts +4 -0
- package/dist/types/components/next/Link/Link.figma.d.ts +1 -0
- package/dist/types/components/next/Link/Link.types.d.ts +11 -0
- package/dist/types/components/next/Link/index.d.ts +2 -0
- package/dist/types/components/next/Search/Search.d.ts +9 -0
- package/dist/types/components/next/Search/Search.figma.d.ts +1 -0
- package/dist/types/components/next/Search/Search.types.d.ts +16 -0
- package/dist/types/components/next/Search/index.d.ts +2 -0
- package/dist/types/components/next/TextArea/TextArea.d.ts +11 -0
- package/dist/types/components/next/TextArea/TextArea.figma.d.ts +1 -0
- package/dist/types/components/next/TextArea/TextArea.types.d.ts +21 -0
- package/dist/types/components/next/TextArea/index.d.ts +2 -0
- package/dist/types/components/next/Tooltip/Tooltip.d.ts +7 -0
- package/dist/types/components/next/Tooltip/Tooltip.figma.d.ts +1 -0
- package/dist/types/components/next/Tooltip/Tooltip.types.d.ts +17 -0
- package/dist/types/components/next/Tooltip/index.d.ts +2 -0
- package/dist/types/components/next/index.d.ts +10 -0
- package/package.json +31 -31
|
@@ -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 };
|
|
@@ -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,24 @@
|
|
|
1
|
+
import { forwardRef } from 'react';
|
|
2
|
+
import { jsx } from 'react/jsx-runtime';
|
|
3
|
+
|
|
4
|
+
const Link = /*#__PURE__*/forwardRef(function Link({
|
|
5
|
+
variant = 'inline',
|
|
6
|
+
className,
|
|
7
|
+
children,
|
|
8
|
+
...rest
|
|
9
|
+
}, ref) {
|
|
10
|
+
const classes = ['eds-link', className].filter(Boolean).join(' ');
|
|
11
|
+
return /*#__PURE__*/jsx("a", {
|
|
12
|
+
ref: ref,
|
|
13
|
+
className: classes,
|
|
14
|
+
"data-variant": variant,
|
|
15
|
+
"data-font-family": variant === 'standalone' ? 'ui' : undefined,
|
|
16
|
+
"data-font-size": variant === 'standalone' ? 'md' : undefined,
|
|
17
|
+
"data-line-height": variant === 'standalone' ? 'squished' : undefined,
|
|
18
|
+
...rest,
|
|
19
|
+
children: children
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
Link.displayName = 'Link';
|
|
23
|
+
|
|
24
|
+
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,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,14 @@
|
|
|
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';
|
|
7
10
|
export { Switch } from './components/next/Switch/Switch.js';
|
|
11
|
+
export { TextArea } from './components/next/TextArea/TextArea.js';
|
|
8
12
|
export { TextField } from './components/next/TextField/TextField.js';
|
|
13
|
+
export { Tooltip } from './components/next/Tooltip/Tooltip.js';
|
|
9
14
|
export { useFieldIds } from './components/next/Field/useFieldIds.js';
|