@digdir/designsystemet-react 1.11.1 → 1.12.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.
- package/dist/cjs/components/Combobox/Combobox.js +1 -0
- package/dist/cjs/components/Combobox/Option/useComboboxOption.js +1 -0
- package/dist/cjs/components/Combobox/useComboboxKeyboard.js +1 -0
- package/dist/cjs/components/avatar/avatar.js +2 -2
- package/dist/cjs/components/breadcrumbs/breadcrumbs-link.js +2 -1
- package/dist/cjs/components/breadcrumbs/breadcrumbs-list.js +1 -11
- package/dist/cjs/components/breadcrumbs/breadcrumbs.js +2 -1
- package/dist/cjs/components/button/button.js +8 -3
- package/dist/cjs/components/card/card.js +17 -9
- package/dist/cjs/components/details/details-summary.js +3 -3
- package/dist/cjs/components/details/details.js +2 -2
- package/dist/cjs/components/dialog/dialog-trigger-context.js +5 -6
- package/dist/cjs/components/dialog/dialog-trigger.js +3 -8
- package/dist/cjs/components/dialog/dialog.js +25 -55
- package/dist/cjs/components/error-summary/error-summary-heading.js +2 -8
- package/dist/cjs/components/error-summary/error-summary.js +4 -9
- package/dist/cjs/components/field/field-counter.js +6 -41
- package/dist/cjs/components/field/field-description.js +2 -1
- package/dist/cjs/components/field/field.js +6 -4
- package/dist/cjs/components/label/label.js +2 -1
- package/dist/cjs/components/pagination/pagination-button.js +5 -3
- package/dist/cjs/components/pagination/pagination.js +7 -3
- package/dist/cjs/components/popover/popover-trigger.js +6 -10
- package/dist/cjs/components/popover/popover.js +15 -62
- package/dist/cjs/components/select/select.js +2 -12
- package/dist/cjs/components/skeleton/skeleton.js +1 -0
- package/dist/cjs/components/spinner/spinner.js +1 -0
- package/dist/cjs/components/suggestion/suggestion-clear.js +4 -2
- package/dist/cjs/components/suggestion/suggestion-empty.js +3 -1
- package/dist/cjs/components/suggestion/suggestion-input.js +4 -3
- package/dist/cjs/components/suggestion/suggestion-list.js +5 -41
- package/dist/cjs/components/suggestion/suggestion-option.js +3 -1
- package/dist/cjs/components/suggestion/suggestion.js +9 -9
- package/dist/cjs/components/tabs/tabs-list.js +4 -7
- package/dist/cjs/components/tabs/tabs-panel.js +5 -28
- package/dist/cjs/components/tabs/tabs-tab.js +11 -9
- package/dist/cjs/components/tabs/tabs.js +16 -6
- package/dist/cjs/components/toggle-group/index.js +1 -1
- package/dist/cjs/components/toggle-group/toggle-group-item.js +8 -6
- package/dist/cjs/components/toggle-group/toggle-group.js +6 -6
- package/dist/cjs/components/tooltip/tooltip.js +6 -147
- package/dist/cjs/components/validation-message/validation-message.js +2 -1
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/utilities/hooks/use-pagination/use-pagination.js +13 -25
- package/dist/cjs/utilities/index.js +17 -0
- package/dist/cjs/utilities/roving-focus/roving-focus-item.js +2 -0
- package/dist/cjs/utilities/roving-focus/roving-focus-root.js +4 -0
- package/dist/cjs/utilities/roving-focus/use-roving-focus.js +3 -1
- package/dist/esm/components/Combobox/Combobox.js +1 -0
- package/dist/esm/components/Combobox/Option/useComboboxOption.js +1 -0
- package/dist/esm/components/Combobox/useComboboxKeyboard.js +1 -0
- package/dist/esm/components/avatar/avatar.js +2 -2
- package/dist/esm/components/breadcrumbs/breadcrumbs-link.js +2 -1
- package/dist/esm/components/breadcrumbs/breadcrumbs-list.js +2 -12
- package/dist/esm/components/breadcrumbs/breadcrumbs.js +2 -1
- package/dist/esm/components/button/button.js +9 -4
- package/dist/esm/components/card/card.js +18 -10
- package/dist/esm/components/details/details-summary.js +3 -3
- package/dist/esm/components/details/details.js +2 -2
- package/dist/esm/components/dialog/dialog-trigger-context.js +6 -7
- package/dist/esm/components/dialog/dialog-trigger.js +3 -8
- package/dist/esm/components/dialog/dialog.js +26 -56
- package/dist/esm/components/error-summary/error-summary-heading.js +3 -9
- package/dist/esm/components/error-summary/error-summary.js +6 -10
- package/dist/esm/components/field/field-counter.js +8 -43
- package/dist/esm/components/field/field-description.js +2 -1
- package/dist/esm/components/field/field.js +7 -5
- package/dist/esm/components/label/label.js +2 -1
- package/dist/esm/components/pagination/pagination-button.js +5 -3
- package/dist/esm/components/pagination/pagination.js +7 -3
- package/dist/esm/components/popover/popover-trigger.js +6 -10
- package/dist/esm/components/popover/popover.js +15 -62
- package/dist/esm/components/select/select.js +2 -12
- package/dist/esm/components/skeleton/skeleton.js +1 -0
- package/dist/esm/components/spinner/spinner.js +1 -0
- package/dist/esm/components/suggestion/suggestion-clear.js +4 -2
- package/dist/esm/components/suggestion/suggestion-empty.js +3 -1
- package/dist/esm/components/suggestion/suggestion-input.js +4 -3
- package/dist/esm/components/suggestion/suggestion-list.js +5 -41
- package/dist/esm/components/suggestion/suggestion-option.js +3 -1
- package/dist/esm/components/suggestion/suggestion.js +9 -9
- package/dist/esm/components/tabs/tabs-list.js +5 -8
- package/dist/esm/components/tabs/tabs-panel.js +6 -29
- package/dist/esm/components/tabs/tabs-tab.js +12 -10
- package/dist/esm/components/tabs/tabs.js +17 -7
- package/dist/esm/components/toggle-group/index.js +1 -1
- package/dist/esm/components/toggle-group/toggle-group-item.js +10 -8
- package/dist/esm/components/toggle-group/toggle-group.js +7 -7
- package/dist/esm/components/tooltip/tooltip.js +8 -149
- package/dist/esm/components/validation-message/validation-message.js +2 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/utilities/hooks/use-pagination/use-pagination.js +13 -25
- package/dist/esm/utilities/index.js +11 -0
- package/dist/esm/utilities/roving-focus/roving-focus-item.js +2 -0
- package/dist/esm/utilities/roving-focus/roving-focus-root.js +4 -0
- package/dist/esm/utilities/roving-focus/use-roving-focus.js +3 -1
- package/dist/react-types.d.ts +8 -0
- package/dist/types/components/avatar/avatar.d.ts +12 -7
- package/dist/types/components/avatar/avatar.d.ts.map +1 -1
- package/dist/types/components/breadcrumbs/breadcrumbs-link.d.ts.map +1 -1
- package/dist/types/components/breadcrumbs/breadcrumbs-list.d.ts.map +1 -1
- package/dist/types/components/breadcrumbs/breadcrumbs.d.ts +5 -3
- package/dist/types/components/breadcrumbs/breadcrumbs.d.ts.map +1 -1
- package/dist/types/components/button/button.d.ts +1 -1
- package/dist/types/components/button/button.d.ts.map +1 -1
- package/dist/types/components/card/card.d.ts.map +1 -1
- package/dist/types/components/details/details-summary.d.ts.map +1 -1
- package/dist/types/components/details/details.d.ts +1 -1
- package/dist/types/components/details/details.d.ts.map +1 -1
- package/dist/types/components/dialog/dialog-trigger-context.d.ts +10 -3
- package/dist/types/components/dialog/dialog-trigger-context.d.ts.map +1 -1
- package/dist/types/components/dialog/dialog-trigger.d.ts +1 -1
- package/dist/types/components/dialog/dialog-trigger.d.ts.map +1 -1
- package/dist/types/components/dialog/dialog.d.ts +3 -3
- package/dist/types/components/dialog/dialog.d.ts.map +1 -1
- package/dist/types/components/dropdown/dropdown.d.ts +1 -2
- package/dist/types/components/dropdown/dropdown.d.ts.map +1 -1
- package/dist/types/components/error-summary/error-summary-heading.d.ts.map +1 -1
- package/dist/types/components/error-summary/error-summary.d.ts +6 -6
- package/dist/types/components/error-summary/error-summary.d.ts.map +1 -1
- package/dist/types/components/field/field-counter.d.ts +2 -8
- package/dist/types/components/field/field-counter.d.ts.map +1 -1
- package/dist/types/components/field/field-description.d.ts.map +1 -1
- package/dist/types/components/field/field.d.ts +6 -2
- package/dist/types/components/field/field.d.ts.map +1 -1
- package/dist/types/components/index.d.ts +1 -0
- package/dist/types/components/index.d.ts.map +1 -1
- package/dist/types/components/input/input.d.ts +13 -1
- package/dist/types/components/input/input.d.ts.map +1 -1
- package/dist/types/components/label/label.d.ts.map +1 -1
- package/dist/types/components/pagination/pagination-button.d.ts +13 -4
- package/dist/types/components/pagination/pagination-button.d.ts.map +1 -1
- package/dist/types/components/pagination/pagination.d.ts +27 -5
- package/dist/types/components/pagination/pagination.d.ts.map +1 -1
- package/dist/types/components/popover/popover-trigger.d.ts.map +1 -1
- package/dist/types/components/popover/popover.d.ts +2 -14
- package/dist/types/components/popover/popover.d.ts.map +1 -1
- package/dist/types/components/search/search-button.d.ts +1 -1
- package/dist/types/components/select/select.d.ts +2 -0
- package/dist/types/components/select/select.d.ts.map +1 -1
- package/dist/types/components/suggestion/suggestion-clear.d.ts +7 -5
- package/dist/types/components/suggestion/suggestion-clear.d.ts.map +1 -1
- package/dist/types/components/suggestion/suggestion-empty.d.ts +1 -0
- package/dist/types/components/suggestion/suggestion-empty.d.ts.map +1 -1
- package/dist/types/components/suggestion/suggestion-input.d.ts +2 -1
- package/dist/types/components/suggestion/suggestion-input.d.ts.map +1 -1
- package/dist/types/components/suggestion/suggestion-list.d.ts +1 -1
- package/dist/types/components/suggestion/suggestion-list.d.ts.map +1 -1
- package/dist/types/components/suggestion/suggestion-option.d.ts +1 -0
- package/dist/types/components/suggestion/suggestion-option.d.ts.map +1 -1
- package/dist/types/components/suggestion/suggestion.d.ts +5 -6
- package/dist/types/components/suggestion/suggestion.d.ts.map +1 -1
- package/dist/types/components/tabs/tabs-list.d.ts +4 -2
- package/dist/types/components/tabs/tabs-list.d.ts.map +1 -1
- package/dist/types/components/tabs/tabs-panel.d.ts +4 -2
- package/dist/types/components/tabs/tabs-panel.d.ts.map +1 -1
- package/dist/types/components/tabs/tabs-tab.d.ts +4 -2
- package/dist/types/components/tabs/tabs-tab.d.ts.map +1 -1
- package/dist/types/components/tabs/tabs.d.ts +6 -6
- package/dist/types/components/tabs/tabs.d.ts.map +1 -1
- package/dist/types/components/textfield/textfield.d.ts.map +1 -1
- package/dist/types/components/toggle-group/index.d.ts +1 -1
- package/dist/types/components/toggle-group/toggle-group-item.d.ts +12 -3
- package/dist/types/components/toggle-group/toggle-group-item.d.ts.map +1 -1
- package/dist/types/components/toggle-group/toggle-group.d.ts +12 -4
- package/dist/types/components/toggle-group/toggle-group.d.ts.map +1 -1
- package/dist/types/components/tooltip/tooltip.d.ts +10 -3
- package/dist/types/components/tooltip/tooltip.d.ts.map +1 -1
- package/dist/types/components/validation-message/validation-message.d.ts.map +1 -1
- package/dist/types/types.d.ts +2 -0
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/utilities/hooks/use-pagination/use-pagination.d.ts +1 -1
- package/dist/types/utilities/hooks/use-pagination/use-pagination.d.ts.map +1 -1
- package/dist/types/utilities/index.d.ts +6 -0
- package/dist/types/utilities/index.d.ts.map +1 -1
- package/dist/types/utilities/roving-focus/roving-focus-item.d.ts +1 -0
- package/dist/types/utilities/roving-focus/roving-focus-item.d.ts.map +1 -1
- package/dist/types/utilities/roving-focus/roving-focus-root.d.ts +1 -0
- package/dist/types/utilities/roving-focus/roving-focus-root.d.ts.map +1 -1
- package/dist/types/utilities/roving-focus/use-roving-focus.d.ts +3 -1
- package/dist/types/utilities/roving-focus/use-roving-focus.d.ts.map +1 -1
- package/package.json +11 -14
- package/dist/cjs/components/field/field-observer.js +0 -112
- package/dist/cjs/components/toggle-group/use-toggle-groupitem.js +0 -34
- package/dist/esm/components/field/field-observer.js +0 -107
- package/dist/esm/components/toggle-group/use-toggle-groupitem.js +0 -32
- package/dist/types/components/field/field-observer.d.ts +0 -5
- package/dist/types/components/field/field-observer.d.ts.map +0 -1
- package/dist/types/components/toggle-group/use-toggle-groupitem.d.ts +0 -12
- package/dist/types/components/toggle-group/use-toggle-groupitem.d.ts.map +0 -1
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
3
|
import { Slot } from '@radix-ui/react-slot';
|
|
4
4
|
import cl from 'clsx/lite';
|
|
5
|
-
import { forwardRef, useContext, useRef, useEffect } from 'react';
|
|
5
|
+
import { forwardRef, useContext, useRef, useId, useEffect } from 'react';
|
|
6
6
|
import { useMergeRefs } from '../../utilities/hooks/use-merge-refs/use-merge-refs.js';
|
|
7
|
+
import '@digdir/designsystemet-web';
|
|
7
8
|
import { Button } from '../button/button.js';
|
|
8
9
|
import { Context } from './dialog-trigger-context.js';
|
|
9
10
|
|
|
@@ -25,69 +26,38 @@ import { Context } from './dialog-trigger-context.js';
|
|
|
25
26
|
*
|
|
26
27
|
* ...
|
|
27
28
|
*
|
|
28
|
-
* <Button
|
|
29
|
-
* <Dialog
|
|
29
|
+
* <Button command="show-modal" commandfor="my-dialog">Open Dialog</Button>
|
|
30
|
+
* <Dialog id="my-dialog">
|
|
30
31
|
* Content
|
|
31
32
|
* </Dialog>
|
|
32
33
|
*/
|
|
33
|
-
const Dialog = forwardRef(function Dialog({ asChild, children, className,
|
|
34
|
-
const
|
|
34
|
+
const Dialog = forwardRef(function Dialog({ asChild, children, className, closeButton = 'Lukk dialogvindu', id, modal = true, onAnimationEnd, onClick, onClose, open, placement = 'center', ...rest }, ref) {
|
|
35
|
+
const { setContext } = useContext(Context);
|
|
35
36
|
const dialogRef = useRef(null); // This local ref is used to make sure the dialog works without a DialogTriggerContext
|
|
36
37
|
const Component = asChild ? Slot : 'dialog';
|
|
37
|
-
const mergedRefs = useMergeRefs([
|
|
38
|
+
const mergedRefs = useMergeRefs([ref, dialogRef]);
|
|
38
39
|
const showProp = modal ? 'showModal' : 'show';
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
40
|
+
const autoId = useId();
|
|
41
|
+
const usedId = id ?? autoId;
|
|
42
|
+
// Toggle open based on prop
|
|
43
|
+
useEffect(() => dialogRef.current?.[open ? showProp : 'close'](), [open]);
|
|
44
|
+
// Store context for DialogTrigger to consume, so it can open the dialog when the trigger is clicked
|
|
45
|
+
useEffect(() => setContext?.({ id: usedId, modal }), [usedId, modal]);
|
|
46
|
+
return (jsxs(Component, { className: cl('ds-dialog', className), "data-placement": placement, id: usedId, onClose: (event) => onClose?.(event.nativeEvent), onClick: (event) => {
|
|
47
|
+
onClick?.(event);
|
|
48
|
+
const { currentTarget: dialog, target: el, defaultPrevented } = event;
|
|
49
|
+
const isClose = el?.closest?.('[data-command="close"]');
|
|
50
|
+
if (!defaultPrevented && isClose) {
|
|
51
|
+
dialog.close();
|
|
52
|
+
console.warn('Designsystemet: data-command="close" is deprecated. Use command="close" and commandfor="DIALOG-ID" instead.');
|
|
51
53
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
return;
|
|
56
|
-
if (event instanceof KeyboardEvent)
|
|
57
|
-
return (closedby === 'none' &&
|
|
58
|
-
event.key === 'Escape' &&
|
|
59
|
-
event.preventDefault()); // Skip ESC-key if closedby="none"
|
|
60
|
-
if (window.getSelection()?.toString())
|
|
61
|
-
return; // Fix bug where if you select text spanning two divs it thinks you clicked outside
|
|
62
|
-
if (dialog && target === dialog && closedby === 'any') {
|
|
63
|
-
const { top, left, right, bottom } = dialog.getBoundingClientRect();
|
|
64
|
-
const isInDialog = top <= y && y <= bottom && left <= x && x <= right;
|
|
65
|
-
if (!isInDialog)
|
|
66
|
-
dialog?.close(); // Both <dialog> and ::backdrop is considered same event.target
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
|
-
const handleAutoFocus = () => {
|
|
70
|
-
const autofocus = dialog?.querySelector('[autofocus]');
|
|
54
|
+
}, onAnimationEnd: (event) => {
|
|
55
|
+
const { currentTarget: dialog } = event;
|
|
56
|
+
const autofocus = dialog.querySelector('[autofocus]');
|
|
71
57
|
if (document.activeElement !== autofocus)
|
|
72
|
-
autofocus?.focus();
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
dialog?.addEventListener('click', handleClosedby);
|
|
76
|
-
dialog?.addEventListener('keydown', handleClosedby);
|
|
77
|
-
return () => {
|
|
78
|
-
dialog?.removeEventListener('animationend', handleAutoFocus);
|
|
79
|
-
dialog?.removeEventListener('click', handleClosedby);
|
|
80
|
-
dialog?.removeEventListener('keydown', handleClosedby);
|
|
81
|
-
};
|
|
82
|
-
}, [closedby]);
|
|
83
|
-
/* handle closing */
|
|
84
|
-
useEffect(() => {
|
|
85
|
-
const handleClose = (event) => onClose?.(event);
|
|
86
|
-
const currentRef = dialogRef.current;
|
|
87
|
-
currentRef?.addEventListener('close', handleClose);
|
|
88
|
-
return () => currentRef?.removeEventListener('close', handleClose);
|
|
89
|
-
}, [onClose]);
|
|
90
|
-
return (jsxs(Component, { className: cl('ds-dialog', className), ref: mergedRefs, "data-placement": placement, "data-modal": modal, closedby: closedby, ...rest, children: [closeButton !== false && (jsx(Button, { "aria-label": closeButton, "data-color": 'neutral', icon: true, variant: 'tertiary', "data-command": 'close' })), children] }));
|
|
58
|
+
autofocus?.focus(); // Handle autofocus on open
|
|
59
|
+
onAnimationEnd?.(event);
|
|
60
|
+
}, ref: mergedRefs, ...rest, children: [closeButton !== false && (jsx(Button, { "aria-label": closeButton, "data-color": 'neutral', icon: true, variant: 'tertiary', command: 'close', commandfor: id ?? autoId })), children] }));
|
|
91
61
|
});
|
|
92
62
|
|
|
93
63
|
export { Dialog };
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx } from 'react/jsx-runtime';
|
|
3
|
-
import { forwardRef
|
|
3
|
+
import { forwardRef } from 'react';
|
|
4
4
|
import { Heading } from '../heading/heading.js';
|
|
5
|
-
import { ErrorSummaryContext } from './error-summary.js';
|
|
6
5
|
|
|
7
6
|
/**
|
|
8
7
|
* ErrorSummary heading component, used to display a heading for the error summary.
|
|
@@ -12,13 +11,8 @@ import { ErrorSummaryContext } from './error-summary.js';
|
|
|
12
11
|
* <ErrorSummaryHeading>Heading</ErrorSummaryHeading>
|
|
13
12
|
* </ErrorSummary>
|
|
14
13
|
*/
|
|
15
|
-
const ErrorSummaryHeading = forwardRef(function ErrorSummaryHeading(
|
|
16
|
-
|
|
17
|
-
useEffect(() => {
|
|
18
|
-
if (id && headingId !== id)
|
|
19
|
-
setHeadingId(id);
|
|
20
|
-
}, [headingId, id, setHeadingId]);
|
|
21
|
-
return jsx(Heading, { id: headingId, ref: ref, ...rest });
|
|
14
|
+
const ErrorSummaryHeading = forwardRef(function ErrorSummaryHeading(rest, ref) {
|
|
15
|
+
return jsx(Heading, { ref: ref, suppressHydrationWarning: true, ...rest }); // Suppress hydration warning since we will get an ID from <ds-error-summary>
|
|
22
16
|
});
|
|
23
17
|
|
|
24
18
|
export { ErrorSummaryHeading };
|
|
@@ -2,12 +2,8 @@
|
|
|
2
2
|
import { jsx } from 'react/jsx-runtime';
|
|
3
3
|
import { Slot } from '@radix-ui/react-slot';
|
|
4
4
|
import cl from 'clsx/lite';
|
|
5
|
-
import {
|
|
5
|
+
import { forwardRef } from 'react';
|
|
6
6
|
|
|
7
|
-
const ErrorSummaryContext = createContext({
|
|
8
|
-
headingId: 'heading',
|
|
9
|
-
setHeadingId: () => { },
|
|
10
|
-
});
|
|
11
7
|
/**
|
|
12
8
|
* ErrorSummary component, used to display a list of errors.
|
|
13
9
|
*
|
|
@@ -25,10 +21,10 @@ const ErrorSummaryContext = createContext({
|
|
|
25
21
|
* </ErrorSummary>
|
|
26
22
|
*/
|
|
27
23
|
const ErrorSummary = forwardRef(function ErrorSummary({ asChild, className, ...rest }, ref) {
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
24
|
+
const Component = asChild ? Slot : 'ds-error-summary';
|
|
25
|
+
return (jsx(Component, { ...(asChild
|
|
26
|
+
? { className: cl('ds-error-summary', className) }
|
|
27
|
+
: { class: cl('ds-error-summary', className) }), ref: ref, suppressHydrationWarning: true, ...rest }));
|
|
32
28
|
});
|
|
33
29
|
|
|
34
|
-
export { ErrorSummary
|
|
30
|
+
export { ErrorSummary };
|
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import {
|
|
3
|
-
import { forwardRef
|
|
4
|
-
import {
|
|
5
|
-
import '../../utilities/roving-focus/roving-focus-item.js';
|
|
6
|
-
import '../../utilities/roving-focus/roving-focus-root.js';
|
|
7
|
-
import { Paragraph } from '../paragraph/paragraph.js';
|
|
2
|
+
import { jsx } from 'react/jsx-runtime';
|
|
3
|
+
import { forwardRef } from 'react';
|
|
4
|
+
import { warn } from '../../utilities/index.js';
|
|
8
5
|
import { ValidationMessage } from '../validation-message/validation-message.js';
|
|
9
|
-
import { isInputLike } from './field-observer.js';
|
|
10
6
|
|
|
11
|
-
const label = (text, count) => text.replace('%d', Math.abs(count).toString());
|
|
12
7
|
/**
|
|
13
8
|
* FieldCounter component, used to display a counter for a form field.
|
|
14
9
|
*
|
|
@@ -18,41 +13,11 @@ const label = (text, count) => text.replace('%d', Math.abs(count).toString());
|
|
|
18
13
|
* <Field.Counter limit={100} under='%d tegn igjen' over='%d tegn for mye' />
|
|
19
14
|
* </Field>
|
|
20
15
|
*/
|
|
21
|
-
const FieldCounter = forwardRef(function FieldCounter({ limit, under
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const hasExceededLimit = count > limit;
|
|
27
|
-
const remainder = limit - count;
|
|
28
|
-
const debouncedSetLiveRegionText = useDebounceCallback((text) => setLiveRegionText(text), 1200);
|
|
29
|
-
// Listen to native input events (user typing) to update the counter in real time
|
|
30
|
-
useEffect(() => {
|
|
31
|
-
const field = counterRef.current?.closest('.ds-field');
|
|
32
|
-
const input = Array.from(field?.getElementsByTagName('*') || []).find(isInputLike);
|
|
33
|
-
const onInput = ({ target }) => {
|
|
34
|
-
if (isInputLike(target))
|
|
35
|
-
setCount(target.value.length);
|
|
36
|
-
};
|
|
37
|
-
if (input)
|
|
38
|
-
onInput({ target: input }); // Initial setup
|
|
39
|
-
fieldInputRef.current = input;
|
|
40
|
-
field?.addEventListener('input', onInput);
|
|
41
|
-
return () => field?.removeEventListener('input', onInput);
|
|
42
|
-
}, []);
|
|
43
|
-
/* React does not dispatch a native input event when the value prop changes externally.
|
|
44
|
-
Since the parent re-renders this component when value changes, we can sync on render. */
|
|
45
|
-
useEffect(() => {
|
|
46
|
-
if (fieldInputRef.current) {
|
|
47
|
-
const valueLength = fieldInputRef.current.value.length;
|
|
48
|
-
setCount((prev) => (prev === valueLength ? prev : valueLength));
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
// Update live region text when count or limit changes
|
|
52
|
-
useEffect(() => {
|
|
53
|
-
debouncedSetLiveRegionText(label(hasExceededLimit ? over : under, remainder));
|
|
54
|
-
}, [count, limit, over, under, hasExceededLimit, remainder]);
|
|
55
|
-
return (jsxs(Fragment, { children: [jsx("div", { className: 'ds-sr-only', "aria-live": 'polite', ref: counterRef, children: liveRegionText }), hasExceededLimit ? (jsx(ValidationMessage, { ref: ref, ...rest, "aria-hidden": 'true', "data-field": null, children: label(over, remainder) })) : (jsx(Paragraph, { ref: ref, ...rest, "aria-hidden": 'true', children: label(under, remainder) })), jsx("div", { className: 'ds-sr-only', "aria-hidden": 'true', "data-field": 'description', children: label(hint, limit) })] }));
|
|
16
|
+
const FieldCounter = forwardRef(function FieldCounter({ limit, under, over, hint, ...rest }, _ref) {
|
|
17
|
+
if (hint)
|
|
18
|
+
warn('hint attribute is deprecated on Field.Counter');
|
|
19
|
+
return (jsx(ValidationMessage, { suppressHydrationWarning // Since <ds-field> adds attributes
|
|
20
|
+
: true, "data-field": 'counter', "data-limit": limit, "data-under": under, "data-over": over, ...rest }));
|
|
56
21
|
});
|
|
57
22
|
|
|
58
23
|
export { FieldCounter };
|
|
@@ -9,7 +9,8 @@ import { forwardRef } from 'react';
|
|
|
9
9
|
* <FieldDescription>Additional information</FieldDescription>
|
|
10
10
|
*/
|
|
11
11
|
const FieldDescription = forwardRef(function FieldDescription(rest, ref) {
|
|
12
|
-
return jsx("div", {
|
|
12
|
+
return (jsx("div", { suppressHydrationWarning // Since <ds-field> adds attributes
|
|
13
|
+
: true, "data-field": 'description', ref: ref, ...rest }));
|
|
13
14
|
});
|
|
14
15
|
|
|
15
16
|
export { FieldDescription };
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx } from 'react/jsx-runtime';
|
|
3
|
+
import '@digdir/designsystemet-web';
|
|
3
4
|
import { Slot } from '@radix-ui/react-slot';
|
|
4
5
|
import cl from 'clsx/lite';
|
|
5
|
-
import { forwardRef, useRef
|
|
6
|
+
import { forwardRef, useRef } from 'react';
|
|
6
7
|
import { useMergeRefs } from '../../utilities/hooks/use-merge-refs/use-merge-refs.js';
|
|
7
|
-
import { fieldObserver } from './field-observer.js';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Field component, used to wrap a form field.
|
|
@@ -18,11 +18,13 @@ import { fieldObserver } from './field-observer.js';
|
|
|
18
18
|
* </Field>
|
|
19
19
|
*/
|
|
20
20
|
const Field = forwardRef(function Field({ className, position, asChild, ...rest }, ref) {
|
|
21
|
-
const Component = asChild ? Slot : '
|
|
21
|
+
const Component = asChild ? Slot : 'ds-field';
|
|
22
22
|
const fieldRef = useRef(null);
|
|
23
23
|
const mergedRefs = useMergeRefs([fieldRef, ref]);
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
return (jsx(Component, { ...(asChild
|
|
25
|
+
? { className: cl('ds-field', className) }
|
|
26
|
+
: { class: cl('ds-field', className) }), suppressHydrationWarning // Since <ds-field> adds attributes
|
|
27
|
+
: true, "data-position": position, ref: mergedRefs, ...rest }));
|
|
26
28
|
});
|
|
27
29
|
|
|
28
30
|
export { Field };
|
|
@@ -12,7 +12,8 @@ import { forwardRef } from 'react';
|
|
|
12
12
|
*/
|
|
13
13
|
const Label = forwardRef(function Label({ className, weight = 'medium', asChild, ...rest }, ref) {
|
|
14
14
|
const Component = asChild ? Slot : 'label';
|
|
15
|
-
return (jsx(Component, { ref: ref, className: cl('ds-label', className),
|
|
15
|
+
return (jsx(Component, { ref: ref, className: cl('ds-label', className), suppressHydrationWarning // Since <ds-field> will add for attribute dynamically
|
|
16
|
+
: true, "data-weight": weight, ...rest }));
|
|
16
17
|
});
|
|
17
18
|
|
|
18
19
|
export { Label };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx } from 'react/jsx-runtime';
|
|
3
|
+
import { Slot } from '@radix-ui/react-slot';
|
|
3
4
|
import { forwardRef } from 'react';
|
|
4
|
-
import { Button } from '../button/button.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* PaginationButton component, use within a Pagination.Item.
|
|
@@ -11,8 +11,10 @@ import { Button } from '../button/button.js';
|
|
|
11
11
|
* <PaginationButton aria-label='Forrige side'>Forrige</PaginationButton>
|
|
12
12
|
* </PaginationItem>
|
|
13
13
|
*/
|
|
14
|
-
const PaginationButton = forwardRef(function PaginationButton(rest, ref) {
|
|
15
|
-
|
|
14
|
+
const PaginationButton = forwardRef(function PaginationButton({ asChild, ...rest }, ref) {
|
|
15
|
+
const Component = asChild ? Slot : 'button';
|
|
16
|
+
return (jsx(Component, { className: 'ds-button', "data-variant": 'tertiary', suppressHydrationWarning // Since <ds-pagination> adds attributes
|
|
17
|
+
: true, ref: ref, ...rest }));
|
|
16
18
|
});
|
|
17
19
|
|
|
18
20
|
export { PaginationButton };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx } from 'react/jsx-runtime';
|
|
3
|
+
import '@digdir/designsystemet-web';
|
|
3
4
|
import { Slot } from '@radix-ui/react-slot';
|
|
4
5
|
import cl from 'clsx/lite';
|
|
5
6
|
import { forwardRef } from 'react';
|
|
@@ -22,9 +23,12 @@ import { forwardRef } from 'react';
|
|
|
22
23
|
* </Pagination.List>
|
|
23
24
|
* </Pagination>
|
|
24
25
|
*/
|
|
25
|
-
const Pagination = forwardRef(function Pagination({
|
|
26
|
-
const Component = asChild ? Slot : '
|
|
27
|
-
return (jsx(Component, {
|
|
26
|
+
const Pagination = forwardRef(function Pagination({ asChild, className, ...rest }, ref) {
|
|
27
|
+
const Component = asChild ? Slot : 'ds-pagination';
|
|
28
|
+
return (jsx(Component, { ...(asChild
|
|
29
|
+
? { className: cl('ds-pagination', className) }
|
|
30
|
+
: { class: cl('ds-pagination', className) }), suppressHydrationWarning // Since ds-pagination will change aria-label and role after hydration
|
|
31
|
+
: true, ref: ref, ...rest }));
|
|
28
32
|
});
|
|
29
33
|
|
|
30
34
|
export { Pagination };
|
|
@@ -26,18 +26,14 @@ import { Context } from './popover-trigger-context.js';
|
|
|
26
26
|
* </Popover>
|
|
27
27
|
* </PopoverTriggerContext>
|
|
28
28
|
*/
|
|
29
|
-
const PopoverTrigger = forwardRef(function PopoverTrigger({ id, inline, asChild, ...rest }, ref) {
|
|
29
|
+
const PopoverTrigger = forwardRef(function PopoverTrigger({ id, inline, asChild, popovertarget, popoverTarget, ...rest }, ref) {
|
|
30
30
|
const { popoverId } = useContext(Context);
|
|
31
31
|
const Component = asChild ? Slot : inline ? 'button' : Button;
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
: {}),
|
|
39
|
-
}, rest);
|
|
40
|
-
return jsx(Component, { ref: ref, ...popoverProps });
|
|
32
|
+
const popoverVal = popoverTarget ?? popovertarget ?? popoverId;
|
|
33
|
+
const popoverKey = version.startsWith('19')
|
|
34
|
+
? 'popoverTarget'
|
|
35
|
+
: 'popovertarget';
|
|
36
|
+
return (jsx(Component, { ref: ref, "data-popover": inline ? 'inline' : undefined, [popoverKey]: popoverVal, ...rest }));
|
|
41
37
|
});
|
|
42
38
|
|
|
43
39
|
export { PopoverTrigger };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx } from 'react/jsx-runtime';
|
|
3
|
-
import { autoUpdate, computePosition, offset, flip, shift } from '@floating-ui/dom';
|
|
4
3
|
import { Slot } from '@radix-ui/react-slot';
|
|
5
4
|
import cl from 'clsx/lite';
|
|
6
5
|
import { forwardRef, useRef, useContext, useState, useEffect } from 'react';
|
|
6
|
+
import '@digdir/designsystemet-web';
|
|
7
7
|
import { useMergeRefs } from '../../utilities/hooks/use-merge-refs/use-merge-refs.js';
|
|
8
8
|
import { Context } from './popover-trigger-context.js';
|
|
9
9
|
|
|
@@ -34,13 +34,13 @@ const Popover = forwardRef(function Popover({ id, className, onClose, onOpen, op
|
|
|
34
34
|
// NOTE: This code is purely to add React controlled component ability to Popover API
|
|
35
35
|
useEffect(() => {
|
|
36
36
|
const popover = popoverRef.current;
|
|
37
|
+
const trigger = `[popovertarget="${popover?.id}"],[commandfor="${popover?.id}"]`;
|
|
37
38
|
const handleClick = (event) => {
|
|
38
39
|
const el = event.target;
|
|
39
|
-
const isTrigger = el?.closest?.(
|
|
40
|
+
const isTrigger = el?.closest?.(trigger);
|
|
40
41
|
const isOutside = !isTrigger && !popover?.contains(el);
|
|
41
|
-
if (isTrigger)
|
|
42
|
+
if (isTrigger)
|
|
42
43
|
event.preventDefault(); // Prevent native Popover API
|
|
43
|
-
}
|
|
44
44
|
if (controlledOpen && (isTrigger || isOutside)) {
|
|
45
45
|
setInternalOpen(false);
|
|
46
46
|
onClose?.();
|
|
@@ -53,11 +53,20 @@ const Popover = forwardRef(function Popover({ id, className, onClose, onOpen, op
|
|
|
53
53
|
const handleKeydown = (event) => {
|
|
54
54
|
if (event.key !== 'Escape' || !controlledOpen)
|
|
55
55
|
return;
|
|
56
|
+
const isOpen = popoverRef.current?.matches(':popover-open') ||
|
|
57
|
+
popoverRef.current?.classList.contains(':popover-open'); // Polyfill support
|
|
58
|
+
if (!isOpen)
|
|
59
|
+
return;
|
|
56
60
|
event.preventDefault(); // Prevent closing fullscreen in Safari
|
|
61
|
+
document.querySelector(trigger)?.focus?.(); // Move focus back to trigger since `popoover="manual"` doesn't do this
|
|
57
62
|
setInternalOpen(false);
|
|
58
63
|
onClose?.();
|
|
59
64
|
};
|
|
60
65
|
popover?.togglePopover?.(controlledOpen);
|
|
66
|
+
if (controlledOpen) {
|
|
67
|
+
const options = { detail: document.querySelector(trigger) };
|
|
68
|
+
popover?.dispatchEvent(new CustomEvent('ds-toggle-source', options)); // Since togglePopover({ source }) is not supported in all browsers yet
|
|
69
|
+
}
|
|
61
70
|
document.addEventListener('click', handleClick, true); // Use capture to execute before React event API
|
|
62
71
|
document.addEventListener('keydown', handleKeydown);
|
|
63
72
|
return () => {
|
|
@@ -65,69 +74,13 @@ const Popover = forwardRef(function Popover({ id, className, onClose, onOpen, op
|
|
|
65
74
|
document.removeEventListener('keydown', handleKeydown);
|
|
66
75
|
};
|
|
67
76
|
}, [controlledOpen]);
|
|
68
|
-
// Position with floating-ui
|
|
69
|
-
useEffect(() => {
|
|
70
|
-
const popover = popoverRef.current;
|
|
71
|
-
const trigger = document.querySelector(`[popovertarget="${popover?.id}"]`);
|
|
72
|
-
if (popover && trigger && controlledOpen)
|
|
73
|
-
return autoUpdate(trigger, popover, () => {
|
|
74
|
-
computePosition(trigger, popover, {
|
|
75
|
-
placement,
|
|
76
|
-
strategy: 'fixed',
|
|
77
|
-
middleware: [
|
|
78
|
-
offset((data) => {
|
|
79
|
-
// get pseudo element arrow size
|
|
80
|
-
const styles = getComputedStyle(data.elements.floating, '::before');
|
|
81
|
-
return parseFloat(styles.height);
|
|
82
|
-
}),
|
|
83
|
-
...(autoPlacement
|
|
84
|
-
? [flip({ fallbackAxisSideDirection: 'start' }), shift()]
|
|
85
|
-
: []),
|
|
86
|
-
arrowPseudoElement,
|
|
87
|
-
],
|
|
88
|
-
}).then(({ x, y }) => {
|
|
89
|
-
popover.style.translate = `${Math.round(x)}px ${Math.round(y)}px`;
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
}, [controlledOpen, placement, id, autoPlacement]);
|
|
93
77
|
// Update context with id
|
|
94
78
|
useEffect(() => {
|
|
95
79
|
if (id)
|
|
96
80
|
setPopoverId?.(id);
|
|
97
81
|
}, [id]);
|
|
98
|
-
return (jsx(Component, { className: cl('ds-popover', className), id: id || popoverId, popover: 'manual', "data-variant": variant, ref: mergedRefs,
|
|
82
|
+
return (jsx(Component, { className: cl('ds-popover', className), id: id || popoverId, popover: 'manual', "data-placement": placement, "data-variant": variant, ref: mergedRefs, suppressHydrationWarning // Since _ds-floating adds attributes
|
|
83
|
+
: true, ...rest }));
|
|
99
84
|
});
|
|
100
|
-
const arrowPseudoElement = {
|
|
101
|
-
name: 'ArrowPseudoElement',
|
|
102
|
-
fn(data) {
|
|
103
|
-
const { elements, rects, placement } = data;
|
|
104
|
-
let arrowX = `${Math.round(rects.reference.width / 2 + rects.reference.x - data.x)}px`;
|
|
105
|
-
let arrowY = `${Math.round(rects.reference.height / 2 + rects.reference.y - data.y)}px`;
|
|
106
|
-
if (rects.reference.width > rects.floating.width) {
|
|
107
|
-
arrowX = `${Math.round(rects.floating.width / 2)}px`;
|
|
108
|
-
}
|
|
109
|
-
if (rects.reference.height > rects.floating.height) {
|
|
110
|
-
arrowY = `${Math.round(rects.floating.height / 2)}px`;
|
|
111
|
-
}
|
|
112
|
-
switch (placement.split('-')[0]) {
|
|
113
|
-
case 'top':
|
|
114
|
-
arrowY = '100%';
|
|
115
|
-
break;
|
|
116
|
-
case 'right':
|
|
117
|
-
arrowX = '0';
|
|
118
|
-
break;
|
|
119
|
-
case 'bottom':
|
|
120
|
-
arrowY = '0';
|
|
121
|
-
break;
|
|
122
|
-
case 'left':
|
|
123
|
-
arrowX = '100%';
|
|
124
|
-
break;
|
|
125
|
-
}
|
|
126
|
-
elements.floating.setAttribute('data-placement', placement.split('-')[0]); // We only need top/left/right/bottom
|
|
127
|
-
elements.floating.style.setProperty('--ds-popover-arrow-x', arrowX);
|
|
128
|
-
elements.floating.style.setProperty('--ds-popover-arrow-y', arrowY);
|
|
129
|
-
return data;
|
|
130
|
-
},
|
|
131
|
-
};
|
|
132
85
|
|
|
133
86
|
export { Popover };
|
|
@@ -12,18 +12,8 @@ import { forwardRef } from 'react';
|
|
|
12
12
|
* <Select.Option value='2'>Option 2</Select.Option>
|
|
13
13
|
* </Select>
|
|
14
14
|
*/
|
|
15
|
-
const Select = forwardRef(function Select({ className, onKeyDown, onMouseDown, width, ...rest }, ref) {
|
|
16
|
-
return (jsx("select", { className: cl('ds-input', className), "data-width": width, ref: ref,
|
|
17
|
-
if (event.key === 'Tab')
|
|
18
|
-
return;
|
|
19
|
-
if (rest.readOnly)
|
|
20
|
-
event.preventDefault(); // Make readonly work for select
|
|
21
|
-
onKeyDown?.(event);
|
|
22
|
-
}, onMouseDown: (event) => {
|
|
23
|
-
if (rest.readOnly)
|
|
24
|
-
event.preventDefault(); // Make readonly work for select
|
|
25
|
-
onMouseDown?.(event);
|
|
26
|
-
}, ...rest }));
|
|
15
|
+
const Select = forwardRef(function Select({ className, onKeyDown, onMouseDown, width, readOnly, ...rest }, ref) {
|
|
16
|
+
return (jsx("select", { className: cl('ds-input', className), "aria-readonly": rest['aria-readonly'] ?? readOnly, "data-width": width, ref: ref, ...rest }));
|
|
27
17
|
});
|
|
28
18
|
|
|
29
19
|
export { Select };
|
|
@@ -4,6 +4,7 @@ import { Slot } from '@radix-ui/react-slot';
|
|
|
4
4
|
import cl from 'clsx/lite';
|
|
5
5
|
import { forwardRef } from 'react';
|
|
6
6
|
import { useMergeRefs } from '../../utilities/hooks/use-merge-refs/use-merge-refs.js';
|
|
7
|
+
import '@digdir/designsystemet-web';
|
|
7
8
|
import { useSynchronizedAnimation } from '../../utilities/hooks/use-synchronized-animation/use-synchronized-animation.js';
|
|
8
9
|
import '../../utilities/roving-focus/roving-focus-item.js';
|
|
9
10
|
import '../../utilities/roving-focus/roving-focus-root.js';
|
|
@@ -3,6 +3,7 @@ import { jsxs, jsx } from 'react/jsx-runtime';
|
|
|
3
3
|
import cl from 'clsx/lite';
|
|
4
4
|
import { forwardRef } from 'react';
|
|
5
5
|
import { useMergeRefs } from '../../utilities/hooks/use-merge-refs/use-merge-refs.js';
|
|
6
|
+
import '@digdir/designsystemet-web';
|
|
6
7
|
import { useSynchronizedAnimation } from '../../utilities/hooks/use-synchronized-animation/use-synchronized-animation.js';
|
|
7
8
|
import '../../utilities/roving-focus/roving-focus-item.js';
|
|
8
9
|
import '../../utilities/roving-focus/roving-focus-root.js';
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx } from 'react/jsx-runtime';
|
|
3
3
|
import { forwardRef } from 'react';
|
|
4
|
-
import { Button } from '../button/button.js';
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
6
|
* Component that provides a clear button for the Suggestion input.
|
|
@@ -16,7 +15,10 @@ import { Button } from '../button/button.js';
|
|
|
16
15
|
* </Suggestion>
|
|
17
16
|
*/
|
|
18
17
|
const SuggestionClear = forwardRef(function SuggestionClear({ 'aria-label': label = 'Tøm', ...rest }, ref) {
|
|
19
|
-
return (
|
|
18
|
+
return (
|
|
19
|
+
//biome-ignore lint/a11y/useAriaPropsSupportedByRole: <del> needs aria-label when u-combobox makes it the clear button
|
|
20
|
+
jsx("del", { "aria-label": label, hidden: true, ref: ref, suppressHydrationWarning // Since <ds-suggestion> adds attributes
|
|
21
|
+
: true, ...rest }));
|
|
20
22
|
});
|
|
21
23
|
|
|
22
24
|
export { SuggestionClear };
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { jsx } from 'react/jsx-runtime';
|
|
3
3
|
import { forwardRef, useContext } from 'react';
|
|
4
4
|
import { SuggestionContext } from './suggestion.js';
|
|
5
|
+
import '@digdir/designsystemet-web';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Component that provides an empty Suggestion list.
|
|
@@ -15,7 +16,8 @@ import { SuggestionContext } from './suggestion.js';
|
|
|
15
16
|
*/
|
|
16
17
|
const SuggestionEmpty = forwardRef(function SuggestionEmpty(rest, ref) {
|
|
17
18
|
const { isEmpty } = useContext(SuggestionContext);
|
|
18
|
-
return isEmpty ? jsx("u-option", { "data-empty": true,
|
|
19
|
+
return isEmpty ? (jsx("u-option", { "data-empty": true, ref: ref, suppressHydrationWarning // Since <u-option> adds attributes
|
|
20
|
+
: true, value: '', ...rest })) : null;
|
|
19
21
|
});
|
|
20
22
|
|
|
21
23
|
export { SuggestionEmpty };
|
|
@@ -19,14 +19,15 @@ const SuggestionInput = forwardRef(function SuggestionList({ value, onInput, onC
|
|
|
19
19
|
const { handleFilter } = useContext(SuggestionContext);
|
|
20
20
|
useEffect(handleFilter, [value]); // Filter if controlled value
|
|
21
21
|
if (onChange)
|
|
22
|
-
console.warn('SuggestionInput: Avoid using onChange, as Suggestion controls the Input. Use
|
|
22
|
+
console.warn('SuggestionInput: Avoid using onChange, as Suggestion controls the Input. Use onSelectedChange on Suggestion instead, or onInput if fetching API results');
|
|
23
23
|
if (value)
|
|
24
|
-
console.warn('SuggestionInput: Avoid using value, as Suggestion controls the Input. Use
|
|
24
|
+
console.warn('SuggestionInput: Avoid using value, as Suggestion controls the Input. Use selected on Suggestion instead.');
|
|
25
25
|
return (jsx(Input, { placeholder: '' // We need an empty placeholder for the clear button to be able to show/hide
|
|
26
26
|
, ref: ref, onInput: (event) => {
|
|
27
27
|
onInput?.(event); // Should run first
|
|
28
28
|
handleFilter?.(); // Filter if uncontrolled value
|
|
29
|
-
},
|
|
29
|
+
}, suppressHydrationWarning // Since <ds-suggestion> adds attributes
|
|
30
|
+
: true, ...rest }));
|
|
30
31
|
});
|
|
31
32
|
|
|
32
33
|
export { SuggestionInput };
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx } from 'react/jsx-runtime';
|
|
3
3
|
import { forwardRef, useContext, useEffect } from 'react';
|
|
4
|
-
import '@
|
|
5
|
-
import { autoUpdate, computePosition, flip, shift } from '@floating-ui/dom';
|
|
4
|
+
import '@digdir/designsystemet-web';
|
|
6
5
|
import { SuggestionContext } from './suggestion.js';
|
|
7
6
|
|
|
8
7
|
/**
|
|
@@ -17,46 +16,11 @@ import { SuggestionContext } from './suggestion.js';
|
|
|
17
16
|
* </Suggestion>
|
|
18
17
|
*/
|
|
19
18
|
const SuggestionList = forwardRef(function SuggestionList({ singular = '%d forslag', plural = '%d forslag', className, id, autoPlacement = true, ...rest }, ref) {
|
|
20
|
-
const { handleFilter
|
|
19
|
+
const { handleFilter } = useContext(SuggestionContext);
|
|
21
20
|
useEffect(handleFilter); // Must run on every render
|
|
22
|
-
//
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const list = uComboboxRef?.current?.list;
|
|
26
|
-
if (list && trigger) {
|
|
27
|
-
return autoUpdate(trigger, list, () => {
|
|
28
|
-
computePosition(trigger, list, {
|
|
29
|
-
placement: 'bottom',
|
|
30
|
-
strategy: 'fixed',
|
|
31
|
-
middleware: [
|
|
32
|
-
...(autoPlacement
|
|
33
|
-
? [
|
|
34
|
-
flip({
|
|
35
|
-
fallbackAxisSideDirection: 'start',
|
|
36
|
-
fallbackPlacements: ['top'],
|
|
37
|
-
}),
|
|
38
|
-
shift(),
|
|
39
|
-
]
|
|
40
|
-
: []),
|
|
41
|
-
undefined,
|
|
42
|
-
triggerWidth,
|
|
43
|
-
],
|
|
44
|
-
}).then(({ x, y, placement }) => {
|
|
45
|
-
const varOperator = placement.startsWith('top') ? '-' : '+';
|
|
46
|
-
list.style.translate = `${Math.round(x)}px calc(${Math.round(y)}px ${varOperator} var(--dsc-suggestion-list-gap))`;
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
}, []);
|
|
51
|
-
return (jsx("u-datalist", { "data-nofilter": true, "data-sr-singular": singular, "data-sr-plural": plural, class: className, ref: ref, popover: 'manual', ...rest }));
|
|
21
|
+
return (jsx("u-datalist", { class: className, popover: 'manual' // Is also set by field.ts, but is nice to have in case focus runs before toggle event
|
|
22
|
+
, "data-nofilter": true, "data-sr-plural": plural, "data-sr-singular": singular, ref: ref, suppressHydrationWarning // Since <u-datalist> adds attributes
|
|
23
|
+
: true, ...rest }));
|
|
52
24
|
});
|
|
53
|
-
const triggerWidth = {
|
|
54
|
-
name: 'TriggerWidth',
|
|
55
|
-
fn(data) {
|
|
56
|
-
const { elements, rects } = data;
|
|
57
|
-
elements.floating.style.width = `${rects.reference.width}px`;
|
|
58
|
-
return data;
|
|
59
|
-
},
|
|
60
|
-
};
|
|
61
25
|
|
|
62
26
|
export { SuggestionList };
|