@entur/modal 1.8.15 → 2.0.0-next.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/Drawer.d.ts +1 -1
- package/dist/Modal.d.ts +12 -7
- package/dist/ModalContent.d.ts +2 -3
- package/dist/ModalOverlay.d.ts +6 -3
- package/dist/modal.cjs.js +124 -42
- package/dist/modal.cjs.js.map +1 -1
- package/dist/modal.esm.js +125 -43
- package/dist/modal.esm.js.map +1 -1
- package/dist/styles.css +107 -54
- package/package.json +13 -15
package/dist/Drawer.d.ts
CHANGED
|
@@ -25,4 +25,4 @@ export type DrawerProps = {
|
|
|
25
25
|
/** Legger på et overlay over resten av siden */
|
|
26
26
|
overlay?: boolean;
|
|
27
27
|
};
|
|
28
|
-
export declare const Drawer:
|
|
28
|
+
export declare const Drawer: ({ children, className, closeLabel, contrast, open, onDismiss, title, style, overlay, }: DrawerProps) => import("react/jsx-runtime").JSX.Element | null;
|
package/dist/Modal.d.ts
CHANGED
|
@@ -4,12 +4,16 @@ export type ModalProps = {
|
|
|
4
4
|
children: React.ReactNode;
|
|
5
5
|
/** Skjermleser-label til lukk-knappen */
|
|
6
6
|
closeLabel?: string;
|
|
7
|
-
/** En ref til elementet som skal være fokusert når modalen åpnes. Defaulter til
|
|
7
|
+
/** En ref til elementet som skal være fokusert når modalen åpnes. Defaulter til tittelen, ellers første interaktive element */
|
|
8
8
|
initialFocusRef?: React.RefObject<HTMLElement>;
|
|
9
|
-
/**
|
|
9
|
+
/** Styrer om modalen er synlig */
|
|
10
10
|
open?: boolean;
|
|
11
|
-
/** Callback som kalles når brukeren ber om å lukke modalen */
|
|
12
|
-
onDismiss
|
|
11
|
+
/** Callback som kalles når brukeren ber om å lukke modalen. Påkrevd: Esc og klikk utenfor lukker alltid (nettleserens innebygde atferd for native dialog) */
|
|
12
|
+
onDismiss: () => void;
|
|
13
|
+
/** Om lukkeknappen skal vises i øvre høyre hjørne
|
|
14
|
+
* @default true
|
|
15
|
+
*/
|
|
16
|
+
showCloseButton?: boolean;
|
|
13
17
|
/** Størrelsen på modalen */
|
|
14
18
|
size: 'extraSmall' | 'small' | 'medium' | 'large' | 'extraLarge';
|
|
15
19
|
/** Hvordan innholdet skal plasseres i modalen
|
|
@@ -18,10 +22,11 @@ export type ModalProps = {
|
|
|
18
22
|
align?: 'start' | 'center' | 'end';
|
|
19
23
|
/** Tittelen som vises i modalen */
|
|
20
24
|
title?: React.ReactNode;
|
|
25
|
+
/** Tilgjengelig navn for modalen når title ikke er satt */
|
|
26
|
+
'aria-label'?: string;
|
|
21
27
|
/** Om modalen skal lukkes når man klikker på utsiden av den
|
|
22
28
|
* @default true
|
|
23
29
|
*/
|
|
24
30
|
closeOnClickOutside?: boolean;
|
|
25
|
-
|
|
26
|
-
};
|
|
27
|
-
export declare const Modal: React.FC<ModalProps>;
|
|
31
|
+
} & Omit<React.HTMLAttributes<HTMLDivElement>, 'title' | 'aria-label'>;
|
|
32
|
+
export declare const Modal: ({ children, closeLabel, initialFocusRef, open, onDismiss, size, align, title, closeOnClickOutside, showCloseButton, "aria-label": ariaLabel, ...rest }: ModalProps) => import("react/jsx-runtime").JSX.Element;
|
package/dist/ModalContent.d.ts
CHANGED
|
@@ -12,8 +12,7 @@ export type ModalContentProps = {
|
|
|
12
12
|
* @default 'start'
|
|
13
13
|
*/
|
|
14
14
|
align?: 'start' | 'center' | 'end';
|
|
15
|
-
|
|
16
|
-
};
|
|
15
|
+
} & Omit<React.HTMLAttributes<HTMLDivElement>, 'title'>;
|
|
17
16
|
export declare const headingsMap: {
|
|
18
17
|
extraSmall: <E extends React.ElementType = "h4">({ margin, children, as, ...rest }: import('../../typography').Heading4Props<E>) => JSX.Element;
|
|
19
18
|
small: <E extends React.ElementType = "h3">({ margin, children, as, ...rest }: import('../../typography').Heading3Props<E>) => JSX.Element;
|
|
@@ -21,4 +20,4 @@ export declare const headingsMap: {
|
|
|
21
20
|
large: <E extends React.ElementType = "h2">({ margin, children, as, ...rest }: import('../../typography').Heading2Props<E>) => JSX.Element;
|
|
22
21
|
extraLarge: <E extends React.ElementType = "h2">({ margin, children, as, ...rest }: import('../../typography').Heading2Props<E>) => JSX.Element;
|
|
23
22
|
};
|
|
24
|
-
export declare const ModalContent:
|
|
23
|
+
export declare const ModalContent: ({ children, className, size, title, align, ...rest }: ModalContentProps) => import("react/jsx-runtime").JSX.Element;
|
package/dist/ModalOverlay.d.ts
CHANGED
|
@@ -10,6 +10,9 @@ export type ModalOverlayProps = {
|
|
|
10
10
|
className?: string;
|
|
11
11
|
/** En ref til elementet som skal være fokusert når modalen åpnes. Defaulter til lukkeknappen */
|
|
12
12
|
initialFocusRef?: React.RefObject<HTMLElement>;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
/** Om modalen skal lukkes når man klikker utenfor den
|
|
14
|
+
* @default true
|
|
15
|
+
*/
|
|
16
|
+
closeOnClickOutside?: boolean;
|
|
17
|
+
} & Omit<React.DialogHTMLAttributes<HTMLDialogElement>, 'open' | 'onClick'>;
|
|
18
|
+
export declare const ModalOverlay: ({ className, open, onDismiss, initialFocusRef, closeOnClickOutside, children, ...rest }: ModalOverlayProps) => React.ReactPortal | null;
|
package/dist/modal.cjs.js
CHANGED
|
@@ -2,26 +2,69 @@
|
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const utils = require("@entur/utils");
|
|
4
4
|
const jsxRuntime = require("react/jsx-runtime");
|
|
5
|
+
const react = require("react");
|
|
5
6
|
const icons = require("@entur/icons");
|
|
6
7
|
const button = require("@entur/button");
|
|
7
8
|
const typography = require("@entur/typography");
|
|
9
|
+
const reactDom = require("react-dom");
|
|
8
10
|
const classNames = require("classnames");
|
|
9
|
-
const dialog = require("@reach/dialog");
|
|
10
|
-
const React = require("react");
|
|
11
|
-
const reactFocusLock = require("react-focus-lock");
|
|
12
11
|
const layout = require("@entur/layout");
|
|
13
12
|
const ModalOverlay = ({
|
|
14
13
|
className,
|
|
15
14
|
open,
|
|
15
|
+
onDismiss,
|
|
16
|
+
initialFocusRef,
|
|
17
|
+
closeOnClickOutside = true,
|
|
18
|
+
children,
|
|
16
19
|
...rest
|
|
17
|
-
}) =>
|
|
18
|
-
|
|
19
|
-
{
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
);
|
|
20
|
+
}) => {
|
|
21
|
+
const dialogRef = react.useRef(null);
|
|
22
|
+
react.useEffect(() => {
|
|
23
|
+
const dialog = dialogRef.current;
|
|
24
|
+
if (!dialog) return;
|
|
25
|
+
if (open && !dialog.open) {
|
|
26
|
+
dialog.showModal();
|
|
27
|
+
const target = initialFocusRef?.current ?? dialog.querySelector("[data-autofocus]") ?? dialog;
|
|
28
|
+
target.focus();
|
|
29
|
+
} else if (!open && dialog.open) {
|
|
30
|
+
dialog.close();
|
|
31
|
+
}
|
|
32
|
+
}, [open, initialFocusRef]);
|
|
33
|
+
react.useEffect(
|
|
34
|
+
function syncCancelEvent() {
|
|
35
|
+
const dialog = dialogRef.current;
|
|
36
|
+
if (!dialog || !onDismiss) return;
|
|
37
|
+
const handleCancel = (e) => {
|
|
38
|
+
e.preventDefault();
|
|
39
|
+
onDismiss();
|
|
40
|
+
};
|
|
41
|
+
dialog.addEventListener("cancel", handleCancel);
|
|
42
|
+
return () => dialog.removeEventListener("cancel", handleCancel);
|
|
43
|
+
},
|
|
44
|
+
[onDismiss]
|
|
45
|
+
);
|
|
46
|
+
const handleClick = react.useCallback(
|
|
47
|
+
(e) => {
|
|
48
|
+
if (!closeOnClickOutside) return;
|
|
49
|
+
if (e.target === dialogRef.current) onDismiss?.();
|
|
50
|
+
},
|
|
51
|
+
[onDismiss, closeOnClickOutside]
|
|
52
|
+
);
|
|
53
|
+
if (typeof document === "undefined") return null;
|
|
54
|
+
return reactDom.createPortal(
|
|
55
|
+
/* @__PURE__ */ jsxRuntime.jsx(layout.ContrastContext.Provider, { value: false, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
56
|
+
"dialog",
|
|
57
|
+
{
|
|
58
|
+
ref: dialogRef,
|
|
59
|
+
className: classNames("eds-modal__overlay", className),
|
|
60
|
+
...rest,
|
|
61
|
+
onClick: handleClick,
|
|
62
|
+
children: open && children
|
|
63
|
+
}
|
|
64
|
+
) }),
|
|
65
|
+
document.body
|
|
66
|
+
);
|
|
67
|
+
};
|
|
25
68
|
const headingsMap = {
|
|
26
69
|
extraSmall: typography.Heading4,
|
|
27
70
|
small: typography.Heading3,
|
|
@@ -38,9 +81,9 @@ const ModalContent = ({
|
|
|
38
81
|
...rest
|
|
39
82
|
}) => {
|
|
40
83
|
const Heading = headingsMap[size] || typography.Heading2;
|
|
41
|
-
const randomId =
|
|
84
|
+
const randomId = react.useId();
|
|
42
85
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
43
|
-
|
|
86
|
+
"div",
|
|
44
87
|
{
|
|
45
88
|
className: classNames(
|
|
46
89
|
"eds-modal__content",
|
|
@@ -48,7 +91,7 @@ const ModalContent = ({
|
|
|
48
91
|
`eds-modal__content--align-${align}`,
|
|
49
92
|
className
|
|
50
93
|
),
|
|
51
|
-
"aria-labelledby": randomId,
|
|
94
|
+
"aria-labelledby": title ? randomId : void 0,
|
|
52
95
|
...rest,
|
|
53
96
|
children: [
|
|
54
97
|
title && /* @__PURE__ */ jsxRuntime.jsx(Heading, { margin: "bottom", as: "h2", id: randomId, children: title }),
|
|
@@ -67,22 +110,23 @@ const Modal = ({
|
|
|
67
110
|
align = "start",
|
|
68
111
|
title,
|
|
69
112
|
closeOnClickOutside = true,
|
|
113
|
+
showCloseButton = true,
|
|
114
|
+
"aria-label": ariaLabel,
|
|
70
115
|
...rest
|
|
71
116
|
}) => {
|
|
72
|
-
const randomId =
|
|
117
|
+
const randomId = react.useId();
|
|
73
118
|
const Heading = headingsMap[size] || typography.Heading2;
|
|
74
|
-
|
|
75
|
-
let handleOnDismiss;
|
|
76
|
-
if (onDismiss && closeOnClickOutside) {
|
|
77
|
-
handleOnDismiss = onDismiss;
|
|
78
|
-
}
|
|
79
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
119
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
80
120
|
ModalOverlay,
|
|
81
121
|
{
|
|
82
122
|
open,
|
|
83
|
-
onDismiss
|
|
123
|
+
onDismiss,
|
|
124
|
+
closeOnClickOutside,
|
|
84
125
|
initialFocusRef,
|
|
85
|
-
|
|
126
|
+
className: `eds-modal__overlay--size-${size}`,
|
|
127
|
+
"aria-labelledby": title ? randomId : void 0,
|
|
128
|
+
"aria-label": !title ? ariaLabel : void 0,
|
|
129
|
+
children: [
|
|
86
130
|
showCloseButton && /* @__PURE__ */ jsxRuntime.jsx(
|
|
87
131
|
button.IconButton,
|
|
88
132
|
{
|
|
@@ -93,9 +137,21 @@ const Modal = ({
|
|
|
93
137
|
children: /* @__PURE__ */ jsxRuntime.jsx(icons.CloseIcon, {})
|
|
94
138
|
}
|
|
95
139
|
),
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
140
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ModalContent, { size, align, ...rest, children: [
|
|
141
|
+
title && /* @__PURE__ */ jsxRuntime.jsx(
|
|
142
|
+
Heading,
|
|
143
|
+
{
|
|
144
|
+
margin: "bottom",
|
|
145
|
+
as: "h2",
|
|
146
|
+
id: randomId,
|
|
147
|
+
tabIndex: -1,
|
|
148
|
+
"data-autofocus": initialFocusRef ? void 0 : "",
|
|
149
|
+
children: title
|
|
150
|
+
}
|
|
151
|
+
),
|
|
152
|
+
children
|
|
153
|
+
] })
|
|
154
|
+
]
|
|
99
155
|
}
|
|
100
156
|
);
|
|
101
157
|
};
|
|
@@ -110,31 +166,58 @@ const Drawer = ({
|
|
|
110
166
|
style,
|
|
111
167
|
overlay = false
|
|
112
168
|
}) => {
|
|
113
|
-
const titleId =
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
169
|
+
const titleId = react.useId();
|
|
170
|
+
const drawerRef = react.useRef(null);
|
|
171
|
+
const previouslyFocusedRef = react.useRef(null);
|
|
172
|
+
react.useEffect(
|
|
173
|
+
function handleFocus() {
|
|
174
|
+
if (!open || overlay) return;
|
|
175
|
+
previouslyFocusedRef.current = document.activeElement;
|
|
176
|
+
drawerRef.current?.querySelector("[data-autofocus]")?.focus();
|
|
177
|
+
return () => {
|
|
178
|
+
if (previouslyFocusedRef.current instanceof HTMLElement) {
|
|
179
|
+
previouslyFocusedRef.current.focus();
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
},
|
|
183
|
+
[open, overlay]
|
|
184
|
+
);
|
|
185
|
+
const shouldRemoveFromDOM = !open && !overlay;
|
|
186
|
+
if (shouldRemoveFromDOM) return null;
|
|
187
|
+
function handleKeyDown(e) {
|
|
118
188
|
if (e.key === "Escape") {
|
|
119
189
|
e.stopPropagation();
|
|
120
190
|
onDismiss();
|
|
121
191
|
}
|
|
192
|
+
}
|
|
193
|
+
const Wrapper = contrast ? layout.Contrast : "div";
|
|
194
|
+
const nonModalDialogProps = {
|
|
195
|
+
role: "dialog",
|
|
196
|
+
"aria-modal": false,
|
|
197
|
+
"aria-labelledby": titleId
|
|
122
198
|
};
|
|
123
|
-
const Wrapper = contrast ? layout.Contrast : React.Fragment;
|
|
124
|
-
const ContentContainer = overlay ? dialog.DialogContent : "div";
|
|
125
199
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
126
|
-
ConditionalWrapper,
|
|
200
|
+
utils.ConditionalWrapper,
|
|
127
201
|
{
|
|
128
202
|
condition: overlay,
|
|
129
|
-
wrapper: (children2) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
130
|
-
|
|
131
|
-
ContentContainer,
|
|
203
|
+
wrapper: (children2) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
204
|
+
ModalOverlay,
|
|
132
205
|
{
|
|
206
|
+
open,
|
|
207
|
+
onDismiss,
|
|
133
208
|
"aria-labelledby": titleId,
|
|
209
|
+
children: children2
|
|
210
|
+
}
|
|
211
|
+
),
|
|
212
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
213
|
+
Wrapper,
|
|
214
|
+
{
|
|
215
|
+
ref: drawerRef,
|
|
216
|
+
...!overlay && nonModalDialogProps,
|
|
134
217
|
className: classNames("eds-drawer", className),
|
|
135
218
|
onKeyDown: handleKeyDown,
|
|
136
219
|
style,
|
|
137
|
-
children:
|
|
220
|
+
children: [
|
|
138
221
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
139
222
|
button.IconButton,
|
|
140
223
|
{
|
|
@@ -146,16 +229,15 @@ const Drawer = ({
|
|
|
146
229
|
}
|
|
147
230
|
),
|
|
148
231
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "eds-drawer__content", children: [
|
|
149
|
-
/* @__PURE__ */ jsxRuntime.jsx(typography.Heading3, { as: "h2", id: titleId, children: title }),
|
|
232
|
+
/* @__PURE__ */ jsxRuntime.jsx(typography.Heading3, { as: "h2", id: titleId, tabIndex: -1, "data-autofocus": "", children: title }),
|
|
150
233
|
children
|
|
151
234
|
] })
|
|
152
|
-
]
|
|
235
|
+
]
|
|
153
236
|
}
|
|
154
|
-
)
|
|
237
|
+
)
|
|
155
238
|
}
|
|
156
239
|
);
|
|
157
240
|
};
|
|
158
|
-
const ConditionalWrapper = ({ condition, wrapper, children }) => condition ? wrapper(children) : children;
|
|
159
241
|
utils.warnAboutMissingStyles("modal", "icons", "typography", "a11y", "button");
|
|
160
242
|
exports.Drawer = Drawer;
|
|
161
243
|
exports.Modal = Modal;
|
package/dist/modal.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"modal.cjs.js","sources":["../src/ModalOverlay.tsx","../src/ModalContent.tsx","../src/Modal.tsx","../src/Drawer.tsx","../src/index.tsx"],"sourcesContent":["import React from 'react';\nimport classNames from 'classnames';\nimport { DialogOverlay } from '@reach/dialog';\n\nexport type ModalOverlayProps = {\n /** Flagg som sier om modalen er åpen */\n open?: boolean;\n /** Callback som kalles når brukeren ber om å lukke modalen */\n onDismiss?: () => void;\n /** Innholdet i modalen */\n children: React.ReactNode;\n /** Ekstra klassenavn */\n className?: string;\n /** En ref til elementet som skal være fokusert når modalen åpnes. Defaulter til lukkeknappen */\n initialFocusRef?: React.RefObject<HTMLElement>;\n [key: string]: any;\n};\n\nexport const ModalOverlay: React.FC<ModalOverlayProps> = ({\n className,\n open,\n ...rest\n}) => (\n <DialogOverlay\n className={classNames('eds-modal__overlay', className)}\n isOpen={open}\n {...rest}\n />\n);\n","import React from 'react';\nimport classNames from 'classnames';\nimport { DialogContent } from '@reach/dialog';\nimport { Heading2, Heading3, Heading4 } from '@entur/typography';\nimport { useRandomId } from '@entur/utils';\n\nexport type ModalContentProps = {\n /** Innholdet i modalen */\n children: React.ReactNode;\n /** Ekstra klassenavn */\n className?: string;\n /** Størrelsen på modalen */\n size: 'extraSmall' | 'small' | 'medium' | 'large' | 'extraLarge';\n /** Tittelen som vises i modalen */\n title?: React.ReactNode;\n /** Hvordan innholdet skal plasseres i modalen\n * @default 'start'\n */\n align?: 'start' | 'center' | 'end';\n [key: string]: any;\n};\n\nexport const headingsMap = {\n extraSmall: Heading4,\n small: Heading3,\n medium: Heading2,\n large: Heading2,\n extraLarge: Heading2,\n};\n\nexport const ModalContent: React.FC<ModalContentProps> = ({\n children,\n className,\n size,\n title,\n align = 'start',\n ...rest\n}) => {\n const Heading: React.ElementType = headingsMap[size] || Heading2;\n const randomId = useRandomId('eds-modal');\n return (\n <DialogContent\n className={classNames(\n 'eds-modal__content',\n `eds-modal__content--size-${size}`,\n `eds-modal__content--align-${align}`,\n className,\n )}\n aria-labelledby={randomId}\n {...rest}\n >\n {title && (\n <Heading margin=\"bottom\" as=\"h2\" id={randomId}>\n {title}\n </Heading>\n )}\n {children}\n </DialogContent>\n );\n};\n","import React from 'react';\n\nimport { CloseIcon } from '@entur/icons';\nimport { IconButton } from '@entur/button';\nimport { Heading2 } from '@entur/typography';\nimport { useRandomId } from '@entur/utils';\n\nimport { ModalOverlay } from './ModalOverlay';\nimport { ModalContent, headingsMap } from './ModalContent';\n\nimport './Modal.scss';\n\nexport type ModalProps = {\n /** Innholdet i modalen */\n children: React.ReactNode;\n /** Skjermleser-label til lukk-knappen */\n closeLabel?: string;\n /** En ref til elementet som skal være fokusert når modalen åpnes. Defaulter til lukkeknappen */\n initialFocusRef?: React.RefObject<HTMLElement>;\n /** Flagg som sier om modalen er åpen */\n open?: boolean;\n /** Callback som kalles når brukeren ber om å lukke modalen */\n onDismiss?: () => void;\n /** Størrelsen på modalen */\n size: 'extraSmall' | 'small' | 'medium' | 'large' | 'extraLarge';\n /** Hvordan innholdet skal plasseres i modalen\n * @default 'start'\n */\n align?: 'start' | 'center' | 'end';\n /** Tittelen som vises i modalen */\n title?: React.ReactNode;\n /** Om modalen skal lukkes når man klikker på utsiden av den\n * @default true\n */\n closeOnClickOutside?: boolean;\n [key: string]: any;\n};\n\nexport const Modal: React.FC<ModalProps> = ({\n children,\n closeLabel = 'Lukk',\n initialFocusRef,\n open,\n onDismiss,\n size,\n align = 'start',\n title,\n closeOnClickOutside = true,\n ...rest\n}) => {\n const randomId = useRandomId('eds-modal');\n const Heading: React.ElementType = headingsMap[size] || Heading2;\n const showCloseButton = ['medium', 'large', 'extraLarge'].includes(size);\n\n let handleOnDismiss;\n if (onDismiss && closeOnClickOutside) {\n handleOnDismiss = onDismiss;\n }\n return (\n <ModalOverlay\n open={open}\n onDismiss={handleOnDismiss}\n initialFocusRef={initialFocusRef}\n >\n <ModalContent size={size} align={align} {...rest}>\n {showCloseButton && (\n <IconButton\n className=\"eds-modal__close\"\n aria-label={closeLabel}\n onClick={onDismiss}\n type=\"button\"\n >\n <CloseIcon />\n </IconButton>\n )}\n {title && (\n <Heading margin=\"bottom\" as=\"h2\" id={randomId}>\n {title}\n </Heading>\n )}\n {children}\n </ModalContent>\n </ModalOverlay>\n );\n};\n","import React from 'react';\nimport classNames from 'classnames';\nimport { MoveFocusInside } from 'react-focus-lock';\nimport { Contrast } from '@entur/layout';\nimport { CloseIcon } from '@entur/icons';\nimport { Heading3 } from '@entur/typography';\nimport { useRandomId } from '@entur/utils';\nimport { IconButton } from '@entur/button';\n\nimport './Drawer.scss';\nimport { ModalOverlay } from './ModalOverlay';\nimport { DialogContent } from '@reach/dialog';\n\nexport type DrawerProps = {\n /** Innholdet. Typisk tekst, lenker eller knapper */\n children: React.ReactNode;\n /** Ekstra klassenavn */\n className?: string;\n /** Tekst som beskriver lukkeknappen for skjermlesere\n * @default 'Lukk skuff'\n */\n closeLabel?: string;\n /** Om draweren skal vises i mørk variant\n * @default false\n */\n contrast?: boolean;\n /** Callback som kalles når brukeren ønsker å lukke draweren */\n onDismiss: () => void;\n /** Om draweren er åpen eller ikke\n * @default true\n */\n open?: boolean;\n /** Tittel på toppen av draweren */\n title: string;\n /** Styling som sendes til Drawer */\n style?: React.CSSProperties;\n /** Legger på et overlay over resten av siden */\n overlay?: boolean;\n};\n\nexport const Drawer: React.FC<DrawerProps> = ({\n children,\n className,\n closeLabel = 'Lukk skuff',\n contrast = false,\n open = true,\n onDismiss,\n title,\n style,\n overlay = false,\n}) => {\n const titleId = useRandomId('eds-drawer');\n\n if (!open) {\n return null;\n }\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === 'Escape') {\n e.stopPropagation();\n onDismiss();\n }\n };\n\n const Wrapper = contrast ? Contrast : React.Fragment;\n const ContentContainer = overlay ? DialogContent : 'div';\n return (\n <ConditionalWrapper\n condition={overlay}\n wrapper={(children: React.ReactNode) => (\n <ModalOverlay open={open} onDismiss={onDismiss}>\n {children}\n </ModalOverlay>\n )}\n >\n <Wrapper>\n <ContentContainer\n aria-labelledby={titleId}\n className={classNames('eds-drawer', className)}\n onKeyDown={handleKeyDown}\n style={style}\n >\n <MoveFocusInside>\n <IconButton\n className=\"eds-drawer__close-button\"\n onClick={onDismiss}\n type=\"button\"\n aria-label={closeLabel}\n >\n <CloseIcon aria-hidden />\n </IconButton>\n <div className=\"eds-drawer__content\">\n <Heading3 as=\"h2\" id={titleId}>\n {title}\n </Heading3>\n {children}\n </div>\n </MoveFocusInside>\n </ContentContainer>\n </Wrapper>\n </ConditionalWrapper>\n );\n};\n\nconst ConditionalWrapper: React.FC<{\n condition: boolean;\n wrapper: (child: JSX.Element) => JSX.Element;\n children: React.ReactElement;\n}> = ({ condition, wrapper, children }) =>\n condition ? wrapper(children) : children;\n","import { warnAboutMissingStyles } from '@entur/utils';\nimport './index.scss';\n\nwarnAboutMissingStyles('modal', 'icons', 'typography', 'a11y', 'button');\n\nexport * from './Modal';\nexport * from './ModalOverlay';\nexport * from './ModalContent';\nexport * from './Drawer';\n"],"names":["jsx","DialogOverlay","Heading4","Heading3","Heading2","useRandomId","jsxs","DialogContent","IconButton","CloseIcon","Contrast","children","MoveFocusInside","warnAboutMissingStyles"],"mappings":";;;;;;;;;;;;AAkBO,MAAM,eAA4C,CAAC;AAAA,EACxD;AAAA,EACA;AAAA,EACA,GAAG;AACL,MACEA,2BAAAA;AAAAA,EAACC,OAAAA;AAAAA,EAAA;AAAA,IACC,WAAW,WAAW,sBAAsB,SAAS;AAAA,IACrD,QAAQ;AAAA,IACP,GAAG;AAAA,EAAA;AACN;ACLK,MAAM,cAAc;AAAA,EACzB,YAAYC,WAAAA;AAAAA,EACZ,OAAOC,WAAAA;AAAAA,EACP,QAAQC,WAAAA;AAAAA,EACR,OAAOA,WAAAA;AAAAA,EACP,YAAYA,WAAAA;AACd;AAEO,MAAM,eAA4C,CAAC;AAAA,EACxD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR,GAAG;AACL,MAAM;AACJ,QAAM,UAA6B,YAAY,IAAI,KAAKA,WAAAA;AACxD,QAAM,WAAWC,MAAAA,YAAY,WAAW;AACxC,SACEC,2BAAAA;AAAAA,IAACC,OAAAA;AAAAA,IAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA,4BAA4B,IAAI;AAAA,QAChC,6BAA6B,KAAK;AAAA,QAClC;AAAA,MAAA;AAAA,MAEF,mBAAiB;AAAA,MAChB,GAAG;AAAA,MAEH,UAAA;AAAA,QAAA,SACCP,2BAAAA,IAAC,WAAQ,QAAO,UAAS,IAAG,MAAK,IAAI,UAClC,UAAA,MAAA,CACH;AAAA,QAED;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGP;ACrBO,MAAM,QAA8B,CAAC;AAAA,EAC1C;AAAA,EACA,aAAa;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR;AAAA,EACA,sBAAsB;AAAA,EACtB,GAAG;AACL,MAAM;AACJ,QAAM,WAAWK,MAAAA,YAAY,WAAW;AACxC,QAAM,UAA6B,YAAY,IAAI,KAAKD,WAAAA;AACxD,QAAM,kBAAkB,CAAC,UAAU,SAAS,YAAY,EAAE,SAAS,IAAI;AAEvE,MAAI;AACJ,MAAI,aAAa,qBAAqB;AACpC,sBAAkB;AAAA,EACpB;AACA,SACEJ,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC;AAAA,MACA,WAAW;AAAA,MACX;AAAA,MAEA,UAAAM,2BAAAA,KAAC,cAAA,EAAa,MAAY,OAAe,GAAG,MACzC,UAAA;AAAA,QAAA,mBACCN,2BAAAA;AAAAA,UAACQ,OAAAA;AAAAA,UAAA;AAAA,YACC,WAAU;AAAA,YACV,cAAY;AAAA,YACZ,SAAS;AAAA,YACT,MAAK;AAAA,YAEL,yCAACC,MAAAA,WAAA,CAAA,CAAU;AAAA,UAAA;AAAA,QAAA;AAAA,QAGd,wCACE,SAAA,EAAQ,QAAO,UAAS,IAAG,MAAK,IAAI,UAClC,UAAA,MAAA,CACH;AAAA,QAED;AAAA,MAAA,EAAA,CACH;AAAA,IAAA;AAAA,EAAA;AAGN;AC5CO,MAAM,SAAgC,CAAC;AAAA,EAC5C;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb,WAAW;AAAA,EACX,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AACZ,MAAM;AACJ,QAAM,UAAUJ,MAAAA,YAAY,YAAY;AAExC,MAAI,CAAC,MAAM;AACT,WAAO;AAAA,EACT;AAEA,QAAM,gBAAgB,CAAC,MAA2B;AAChD,QAAI,EAAE,QAAQ,UAAU;AACtB,QAAE,gBAAA;AACF,gBAAA;AAAA,IACF;AAAA,EACF;AAEA,QAAM,UAAU,WAAWK,OAAAA,WAAW,MAAM;AAC5C,QAAM,mBAAmB,UAAUH,OAAAA,gBAAgB;AACnD,SACEP,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAW;AAAA,MACX,SAAS,CAACW,cACRX,2BAAAA,IAAC,gBAAa,MAAY,WACvB,UAAAW,WACH;AAAA,MAGF,yCAAC,SAAA,EACC,UAAAX,2BAAAA;AAAAA,QAAC;AAAA,QAAA;AAAA,UACC,mBAAiB;AAAA,UACjB,WAAW,WAAW,cAAc,SAAS;AAAA,UAC7C,WAAW;AAAA,UACX;AAAA,UAEA,0CAACY,gCAAA,EACC,UAAA;AAAA,YAAAZ,2BAAAA;AAAAA,cAACQ,OAAAA;AAAAA,cAAA;AAAA,gBACC,WAAU;AAAA,gBACV,SAAS;AAAA,gBACT,MAAK;AAAA,gBACL,cAAY;AAAA,gBAEZ,UAAAR,2BAAAA,IAACS,MAAAA,WAAA,EAAU,eAAW,KAAA,CAAC;AAAA,cAAA;AAAA,YAAA;AAAA,YAEzBH,2BAAAA,KAAC,OAAA,EAAI,WAAU,uBACb,UAAA;AAAA,cAAAN,+BAACG,WAAAA,UAAA,EAAS,IAAG,MAAK,IAAI,SACnB,UAAA,OACH;AAAA,cACC;AAAA,YAAA,EAAA,CACH;AAAA,UAAA,EAAA,CACF;AAAA,QAAA;AAAA,MAAA,EACF,CACF;AAAA,IAAA;AAAA,EAAA;AAGN;AAEA,MAAM,qBAID,CAAC,EAAE,WAAW,SAAS,eAC1B,YAAY,QAAQ,QAAQ,IAAI;AC1GlCU,MAAAA,uBAAuB,SAAS,SAAS,cAAc,QAAQ,QAAQ;;;;;;"}
|
|
1
|
+
{"version":3,"file":"modal.cjs.js","sources":["../src/ModalOverlay.tsx","../src/ModalContent.tsx","../src/Modal.tsx","../src/Drawer.tsx","../src/index.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useRef } from 'react';\nimport { createPortal } from 'react-dom';\nimport classNames from 'classnames';\nimport { ContrastContext } from '@entur/layout';\n\nexport type ModalOverlayProps = {\n /** Flagg som sier om modalen er åpen */\n open?: boolean;\n /** Callback som kalles når brukeren ber om å lukke modalen */\n onDismiss?: () => void;\n /** Innholdet i modalen */\n children: React.ReactNode;\n /** Ekstra klassenavn */\n className?: string;\n /** En ref til elementet som skal være fokusert når modalen åpnes. Defaulter til lukkeknappen */\n initialFocusRef?: React.RefObject<HTMLElement>;\n /** Om modalen skal lukkes når man klikker utenfor den\n * @default true\n */\n closeOnClickOutside?: boolean;\n} & Omit<React.DialogHTMLAttributes<HTMLDialogElement>, 'open' | 'onClick'>;\n\nexport const ModalOverlay = ({\n className,\n open,\n onDismiss,\n initialFocusRef,\n closeOnClickOutside = true,\n children,\n ...rest\n}: ModalOverlayProps) => {\n const dialogRef = useRef<HTMLDialogElement>(null);\n\n // Toggle native dialog open state. Always stays in DOM so the browser can\n // manage initial focus storage + restore on close.\n useEffect(() => {\n const dialog = dialogRef.current;\n if (!dialog) return;\n if (open && !dialog.open) {\n dialog.showModal();\n const target =\n initialFocusRef?.current ??\n dialog.querySelector<HTMLElement>('[data-autofocus]') ??\n dialog;\n target.focus();\n } else if (!open && dialog.open) {\n dialog.close();\n }\n }, [open, initialFocusRef]);\n\n // Esc fires cancel — preventDefault so we route through onDismiss + state,\n // which triggers the effect above to call close() (native restore runs).\n useEffect(\n function syncCancelEvent() {\n const dialog = dialogRef.current;\n if (!dialog || !onDismiss) return;\n const handleCancel = (e: Event) => {\n e.preventDefault();\n onDismiss();\n };\n dialog.addEventListener('cancel', handleCancel);\n return () => dialog.removeEventListener('cancel', handleCancel);\n },\n [onDismiss],\n );\n\n const handleClick = useCallback(\n (e: React.MouseEvent<HTMLDialogElement>) => {\n if (!closeOnClickOutside) return;\n if (e.target === dialogRef.current) onDismiss?.();\n },\n [onDismiss, closeOnClickOutside],\n );\n\n if (typeof document === 'undefined') return null;\n\n return createPortal(\n <ContrastContext.Provider value={false}>\n <dialog\n ref={dialogRef}\n className={classNames('eds-modal__overlay', className)}\n {...rest}\n onClick={handleClick}\n >\n {open && children}\n </dialog>\n </ContrastContext.Provider>,\n document.body,\n );\n};\n","import React, { useId } from 'react';\nimport classNames from 'classnames';\nimport { Heading4, Heading3, Heading2 } from '@entur/typography';\n\nexport type ModalContentProps = {\n /** Innholdet i modalen */\n children: React.ReactNode;\n /** Ekstra klassenavn */\n className?: string;\n /** Størrelsen på modalen */\n size: 'extraSmall' | 'small' | 'medium' | 'large' | 'extraLarge';\n /** Tittelen som vises i modalen */\n title?: React.ReactNode;\n /** Hvordan innholdet skal plasseres i modalen\n * @default 'start'\n */\n align?: 'start' | 'center' | 'end';\n} & Omit<React.HTMLAttributes<HTMLDivElement>, 'title'>;\n\nexport const headingsMap = {\n extraSmall: Heading4,\n small: Heading3,\n medium: Heading2,\n large: Heading2,\n extraLarge: Heading2,\n};\n\nexport const ModalContent = ({\n children,\n className,\n size,\n title,\n align = 'start',\n ...rest\n}: ModalContentProps) => {\n const Heading: React.ElementType = headingsMap[size] || Heading2;\n const randomId = useId();\n return (\n <div\n className={classNames(\n 'eds-modal__content',\n `eds-modal__content--size-${size}`,\n `eds-modal__content--align-${align}`,\n className,\n )}\n aria-labelledby={title ? randomId : undefined}\n {...rest}\n >\n {title && (\n <Heading margin=\"bottom\" as=\"h2\" id={randomId}>\n {title}\n </Heading>\n )}\n {children}\n </div>\n );\n};\n","import React, { useId } from 'react';\n\nimport { CloseIcon } from '@entur/icons';\nimport { IconButton } from '@entur/button';\nimport { Heading2 } from '@entur/typography';\n\nimport { ModalOverlay } from './ModalOverlay';\nimport { ModalContent, headingsMap } from './ModalContent';\n\nimport './Modal.scss';\n\nexport type ModalProps = {\n /** Innholdet i modalen */\n children: React.ReactNode;\n /** Skjermleser-label til lukk-knappen */\n closeLabel?: string;\n /** En ref til elementet som skal være fokusert når modalen åpnes. Defaulter til tittelen, ellers første interaktive element */\n initialFocusRef?: React.RefObject<HTMLElement>;\n /** Styrer om modalen er synlig */\n open?: boolean;\n /** Callback som kalles når brukeren ber om å lukke modalen. Påkrevd: Esc og klikk utenfor lukker alltid (nettleserens innebygde atferd for native dialog) */\n onDismiss: () => void;\n /** Om lukkeknappen skal vises i øvre høyre hjørne\n * @default true\n */\n showCloseButton?: boolean;\n /** Størrelsen på modalen */\n size: 'extraSmall' | 'small' | 'medium' | 'large' | 'extraLarge';\n /** Hvordan innholdet skal plasseres i modalen\n * @default 'start'\n */\n align?: 'start' | 'center' | 'end';\n /** Tittelen som vises i modalen */\n title?: React.ReactNode;\n /** Tilgjengelig navn for modalen når title ikke er satt */\n 'aria-label'?: string;\n /** Om modalen skal lukkes når man klikker på utsiden av den\n * @default true\n */\n closeOnClickOutside?: boolean;\n} & Omit<React.HTMLAttributes<HTMLDivElement>, 'title' | 'aria-label'>;\n\nexport const Modal = ({\n children,\n closeLabel = 'Lukk',\n initialFocusRef,\n open,\n onDismiss,\n size,\n align = 'start',\n title,\n closeOnClickOutside = true,\n showCloseButton = true,\n 'aria-label': ariaLabel,\n ...rest\n}: ModalProps) => {\n const randomId = useId();\n const Heading: React.ElementType = headingsMap[size] || Heading2;\n\n return (\n <ModalOverlay\n open={open}\n onDismiss={onDismiss}\n closeOnClickOutside={closeOnClickOutside}\n initialFocusRef={initialFocusRef}\n className={`eds-modal__overlay--size-${size}`}\n aria-labelledby={title ? randomId : undefined}\n aria-label={!title ? ariaLabel : undefined}\n >\n {showCloseButton && (\n <IconButton\n className=\"eds-modal__close\"\n aria-label={closeLabel}\n onClick={onDismiss}\n type=\"button\"\n >\n <CloseIcon />\n </IconButton>\n )}\n <ModalContent size={size} align={align} {...rest}>\n {title && (\n <Heading\n margin=\"bottom\"\n as=\"h2\"\n id={randomId}\n tabIndex={-1}\n data-autofocus={initialFocusRef ? undefined : ''}\n >\n {title}\n </Heading>\n )}\n {children}\n </ModalContent>\n </ModalOverlay>\n );\n};\n","import React, { useEffect, useId, useRef } from 'react';\nimport classNames from 'classnames';\nimport { Contrast } from '@entur/layout';\nimport { CloseIcon } from '@entur/icons';\nimport { Heading3 } from '@entur/typography';\nimport { IconButton } from '@entur/button';\nimport { ConditionalWrapper } from '@entur/utils';\nimport { ModalOverlay } from './ModalOverlay';\n\nimport './Drawer.scss';\n\nexport type DrawerProps = {\n /** Innholdet. Typisk tekst, lenker eller knapper */\n children: React.ReactNode;\n /** Ekstra klassenavn */\n className?: string;\n /** Tekst som beskriver lukkeknappen for skjermlesere\n * @default 'Lukk skuff'\n */\n closeLabel?: string;\n /** Om draweren skal vises i mørk variant\n * @default false\n */\n contrast?: boolean;\n /** Callback som kalles når brukeren ønsker å lukke draweren */\n onDismiss: () => void;\n /** Om draweren er åpen eller ikke\n * @default true\n */\n open?: boolean;\n /** Tittel på toppen av draweren */\n title: string;\n /** Styling som sendes til Drawer */\n style?: React.CSSProperties;\n /** Legger på et overlay over resten av siden */\n overlay?: boolean;\n};\n\nexport const Drawer = ({\n children,\n className,\n closeLabel = 'Lukk skuff',\n contrast = false,\n open = true,\n onDismiss,\n title,\n style,\n overlay = false,\n}: DrawerProps) => {\n const titleId = useId();\n const drawerRef = useRef<HTMLDivElement>(null);\n const previouslyFocusedRef = useRef<Element | null>(null);\n\n useEffect(\n function handleFocus() {\n if (!open || overlay) return;\n previouslyFocusedRef.current = document.activeElement;\n drawerRef.current\n ?.querySelector<HTMLElement>('[data-autofocus]')\n ?.focus();\n\n return () => {\n if (previouslyFocusedRef.current instanceof HTMLElement) {\n previouslyFocusedRef.current.focus();\n }\n };\n },\n [open, overlay],\n );\n\n const shouldRemoveFromDOM = !open && !overlay;\n if (shouldRemoveFromDOM) return null;\n\n function handleKeyDown(e: React.KeyboardEvent) {\n if (e.key === 'Escape') {\n e.stopPropagation();\n onDismiss();\n }\n }\n\n const Wrapper = contrast ? Contrast : 'div';\n const nonModalDialogProps = {\n role: 'dialog' as const,\n 'aria-modal': false,\n 'aria-labelledby': titleId,\n };\n\n return (\n <ConditionalWrapper\n condition={overlay}\n wrapper={(children: React.ReactNode) => (\n <ModalOverlay\n open={open}\n onDismiss={onDismiss}\n aria-labelledby={titleId}\n >\n {children}\n </ModalOverlay>\n )}\n >\n <Wrapper\n ref={drawerRef}\n {...(!overlay && nonModalDialogProps)}\n className={classNames('eds-drawer', className)}\n onKeyDown={handleKeyDown}\n style={style}\n >\n <IconButton\n className=\"eds-drawer__close-button\"\n onClick={onDismiss}\n type=\"button\"\n aria-label={closeLabel}\n >\n <CloseIcon aria-hidden />\n </IconButton>\n <div className=\"eds-drawer__content\">\n <Heading3 as=\"h2\" id={titleId} tabIndex={-1} data-autofocus=\"\">\n {title}\n </Heading3>\n {children}\n </div>\n </Wrapper>\n </ConditionalWrapper>\n );\n};\n","import { warnAboutMissingStyles } from '@entur/utils';\nimport './index.scss';\n\nwarnAboutMissingStyles('modal', 'icons', 'typography', 'a11y', 'button');\n\nexport * from './Modal';\nexport * from './ModalOverlay';\nexport * from './ModalContent';\nexport * from './Drawer';\n"],"names":["useRef","useEffect","useCallback","createPortal","jsx","ContrastContext","Heading4","Heading3","Heading2","useId","jsxs","IconButton","CloseIcon","Contrast","ConditionalWrapper","children","warnAboutMissingStyles"],"mappings":";;;;;;;;;;;AAsBO,MAAM,eAAe,CAAC;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,sBAAsB;AAAA,EACtB;AAAA,EACA,GAAG;AACL,MAAyB;AACvB,QAAM,YAAYA,MAAAA,OAA0B,IAAI;AAIhDC,QAAAA,UAAU,MAAM;AACd,UAAM,SAAS,UAAU;AACzB,QAAI,CAAC,OAAQ;AACb,QAAI,QAAQ,CAAC,OAAO,MAAM;AACxB,aAAO,UAAA;AACP,YAAM,SACJ,iBAAiB,WACjB,OAAO,cAA2B,kBAAkB,KACpD;AACF,aAAO,MAAA;AAAA,IACT,WAAW,CAAC,QAAQ,OAAO,MAAM;AAC/B,aAAO,MAAA;AAAA,IACT;AAAA,EACF,GAAG,CAAC,MAAM,eAAe,CAAC;AAI1BA,QAAAA;AAAAA,IACE,SAAS,kBAAkB;AACzB,YAAM,SAAS,UAAU;AACzB,UAAI,CAAC,UAAU,CAAC,UAAW;AAC3B,YAAM,eAAe,CAAC,MAAa;AACjC,UAAE,eAAA;AACF,kBAAA;AAAA,MACF;AACA,aAAO,iBAAiB,UAAU,YAAY;AAC9C,aAAO,MAAM,OAAO,oBAAoB,UAAU,YAAY;AAAA,IAChE;AAAA,IACA,CAAC,SAAS;AAAA,EAAA;AAGZ,QAAM,cAAcC,MAAAA;AAAAA,IAClB,CAAC,MAA2C;AAC1C,UAAI,CAAC,oBAAqB;AAC1B,UAAI,EAAE,WAAW,UAAU,QAAS,aAAA;AAAA,IACtC;AAAA,IACA,CAAC,WAAW,mBAAmB;AAAA,EAAA;AAGjC,MAAI,OAAO,aAAa,YAAa,QAAO;AAE5C,SAAOC,SAAAA;AAAAA,IACLC,2BAAAA,IAACC,OAAAA,gBAAgB,UAAhB,EAAyB,OAAO,OAC/B,UAAAD,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC,KAAK;AAAA,QACL,WAAW,WAAW,sBAAsB,SAAS;AAAA,QACpD,GAAG;AAAA,QACJ,SAAS;AAAA,QAER,UAAA,QAAQ;AAAA,MAAA;AAAA,IAAA,GAEb;AAAA,IACA,SAAS;AAAA,EAAA;AAEb;ACtEO,MAAM,cAAc;AAAA,EACzB,YAAYE,WAAAA;AAAAA,EACZ,OAAOC,WAAAA;AAAAA,EACP,QAAQC,WAAAA;AAAAA,EACR,OAAOA,WAAAA;AAAAA,EACP,YAAYA,WAAAA;AACd;AAEO,MAAM,eAAe,CAAC;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR,GAAG;AACL,MAAyB;AACvB,QAAM,UAA6B,YAAY,IAAI,KAAKA,WAAAA;AACxD,QAAM,WAAWC,MAAAA,MAAA;AACjB,SACEC,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA,4BAA4B,IAAI;AAAA,QAChC,6BAA6B,KAAK;AAAA,QAClC;AAAA,MAAA;AAAA,MAEF,mBAAiB,QAAQ,WAAW;AAAA,MACnC,GAAG;AAAA,MAEH,UAAA;AAAA,QAAA,SACCN,2BAAAA,IAAC,WAAQ,QAAO,UAAS,IAAG,MAAK,IAAI,UAClC,UAAA,MAAA,CACH;AAAA,QAED;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGP;ACdO,MAAM,QAAQ,CAAC;AAAA,EACpB;AAAA,EACA,aAAa;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR;AAAA,EACA,sBAAsB;AAAA,EACtB,kBAAkB;AAAA,EAClB,cAAc;AAAA,EACd,GAAG;AACL,MAAkB;AAChB,QAAM,WAAWK,MAAAA,MAAA;AACjB,QAAM,UAA6B,YAAY,IAAI,KAAKD,WAAAA;AAExD,SACEE,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW,4BAA4B,IAAI;AAAA,MAC3C,mBAAiB,QAAQ,WAAW;AAAA,MACpC,cAAY,CAAC,QAAQ,YAAY;AAAA,MAEhC,UAAA;AAAA,QAAA,mBACCN,2BAAAA;AAAAA,UAACO,OAAAA;AAAAA,UAAA;AAAA,YACC,WAAU;AAAA,YACV,cAAY;AAAA,YACZ,SAAS;AAAA,YACT,MAAK;AAAA,YAEL,yCAACC,MAAAA,WAAA,CAAA,CAAU;AAAA,UAAA;AAAA,QAAA;AAAA,QAGfF,2BAAAA,KAAC,cAAA,EAAa,MAAY,OAAe,GAAG,MACzC,UAAA;AAAA,UAAA,SACCN,2BAAAA;AAAAA,YAAC;AAAA,YAAA;AAAA,cACC,QAAO;AAAA,cACP,IAAG;AAAA,cACH,IAAI;AAAA,cACJ,UAAU;AAAA,cACV,kBAAgB,kBAAkB,SAAY;AAAA,cAE7C,UAAA;AAAA,YAAA;AAAA,UAAA;AAAA,UAGJ;AAAA,QAAA,EAAA,CACH;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGN;ACzDO,MAAM,SAAS,CAAC;AAAA,EACrB;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb,WAAW;AAAA,EACX,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AACZ,MAAmB;AACjB,QAAM,UAAUK,MAAAA,MAAA;AAChB,QAAM,YAAYT,MAAAA,OAAuB,IAAI;AAC7C,QAAM,uBAAuBA,MAAAA,OAAuB,IAAI;AAExDC,QAAAA;AAAAA,IACE,SAAS,cAAc;AACrB,UAAI,CAAC,QAAQ,QAAS;AACtB,2BAAqB,UAAU,SAAS;AACxC,gBAAU,SACN,cAA2B,kBAAkB,GAC7C,MAAA;AAEJ,aAAO,MAAM;AACX,YAAI,qBAAqB,mBAAmB,aAAa;AACvD,+BAAqB,QAAQ,MAAA;AAAA,QAC/B;AAAA,MACF;AAAA,IACF;AAAA,IACA,CAAC,MAAM,OAAO;AAAA,EAAA;AAGhB,QAAM,sBAAsB,CAAC,QAAQ,CAAC;AACtC,MAAI,oBAAqB,QAAO;AAEhC,WAAS,cAAc,GAAwB;AAC7C,QAAI,EAAE,QAAQ,UAAU;AACtB,QAAE,gBAAA;AACF,gBAAA;AAAA,IACF;AAAA,EACF;AAEA,QAAM,UAAU,WAAWY,OAAAA,WAAW;AACtC,QAAM,sBAAsB;AAAA,IAC1B,MAAM;AAAA,IACN,cAAc;AAAA,IACd,mBAAmB;AAAA,EAAA;AAGrB,SACET,2BAAAA;AAAAA,IAACU,MAAAA;AAAAA,IAAA;AAAA,MACC,WAAW;AAAA,MACX,SAAS,CAACC,cACRX,2BAAAA;AAAAA,QAAC;AAAA,QAAA;AAAA,UACC;AAAA,UACA;AAAA,UACA,mBAAiB;AAAA,UAEhB,UAAAW;AAAAA,QAAA;AAAA,MAAA;AAAA,MAIL,UAAAL,2BAAAA;AAAAA,QAAC;AAAA,QAAA;AAAA,UACC,KAAK;AAAA,UACJ,GAAI,CAAC,WAAW;AAAA,UACjB,WAAW,WAAW,cAAc,SAAS;AAAA,UAC7C,WAAW;AAAA,UACX;AAAA,UAEA,UAAA;AAAA,YAAAN,2BAAAA;AAAAA,cAACO,OAAAA;AAAAA,cAAA;AAAA,gBACC,WAAU;AAAA,gBACV,SAAS;AAAA,gBACT,MAAK;AAAA,gBACL,cAAY;AAAA,gBAEZ,UAAAP,2BAAAA,IAACQ,MAAAA,WAAA,EAAU,eAAW,KAAA,CAAC;AAAA,cAAA;AAAA,YAAA;AAAA,YAEzBF,2BAAAA,KAAC,OAAA,EAAI,WAAU,uBACb,UAAA;AAAA,cAAAN,2BAAAA,IAACG,WAAAA,UAAA,EAAS,IAAG,MAAK,IAAI,SAAS,UAAU,IAAI,kBAAe,IACzD,UAAA,MAAA,CACH;AAAA,cACC;AAAA,YAAA,EAAA,CACH;AAAA,UAAA;AAAA,QAAA;AAAA,MAAA;AAAA,IACF;AAAA,EAAA;AAGN;ACzHAS,MAAAA,uBAAuB,SAAS,SAAS,cAAc,QAAQ,QAAQ;;;;;;"}
|
package/dist/modal.esm.js
CHANGED
|
@@ -1,25 +1,68 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ConditionalWrapper, warnAboutMissingStyles } from "@entur/utils";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useRef, useEffect, useCallback, useId } from "react";
|
|
3
4
|
import { CloseIcon } from "@entur/icons";
|
|
4
5
|
import { IconButton } from "@entur/button";
|
|
5
6
|
import { Heading2, Heading3, Heading4 } from "@entur/typography";
|
|
7
|
+
import { createPortal } from "react-dom";
|
|
6
8
|
import classNames from "classnames";
|
|
7
|
-
import {
|
|
8
|
-
import React from "react";
|
|
9
|
-
import { MoveFocusInside } from "react-focus-lock";
|
|
10
|
-
import { Contrast } from "@entur/layout";
|
|
9
|
+
import { ContrastContext, Contrast } from "@entur/layout";
|
|
11
10
|
const ModalOverlay = ({
|
|
12
11
|
className,
|
|
13
12
|
open,
|
|
13
|
+
onDismiss,
|
|
14
|
+
initialFocusRef,
|
|
15
|
+
closeOnClickOutside = true,
|
|
16
|
+
children,
|
|
14
17
|
...rest
|
|
15
|
-
}) =>
|
|
16
|
-
|
|
17
|
-
{
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
);
|
|
18
|
+
}) => {
|
|
19
|
+
const dialogRef = useRef(null);
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
const dialog = dialogRef.current;
|
|
22
|
+
if (!dialog) return;
|
|
23
|
+
if (open && !dialog.open) {
|
|
24
|
+
dialog.showModal();
|
|
25
|
+
const target = initialFocusRef?.current ?? dialog.querySelector("[data-autofocus]") ?? dialog;
|
|
26
|
+
target.focus();
|
|
27
|
+
} else if (!open && dialog.open) {
|
|
28
|
+
dialog.close();
|
|
29
|
+
}
|
|
30
|
+
}, [open, initialFocusRef]);
|
|
31
|
+
useEffect(
|
|
32
|
+
function syncCancelEvent() {
|
|
33
|
+
const dialog = dialogRef.current;
|
|
34
|
+
if (!dialog || !onDismiss) return;
|
|
35
|
+
const handleCancel = (e) => {
|
|
36
|
+
e.preventDefault();
|
|
37
|
+
onDismiss();
|
|
38
|
+
};
|
|
39
|
+
dialog.addEventListener("cancel", handleCancel);
|
|
40
|
+
return () => dialog.removeEventListener("cancel", handleCancel);
|
|
41
|
+
},
|
|
42
|
+
[onDismiss]
|
|
43
|
+
);
|
|
44
|
+
const handleClick = useCallback(
|
|
45
|
+
(e) => {
|
|
46
|
+
if (!closeOnClickOutside) return;
|
|
47
|
+
if (e.target === dialogRef.current) onDismiss?.();
|
|
48
|
+
},
|
|
49
|
+
[onDismiss, closeOnClickOutside]
|
|
50
|
+
);
|
|
51
|
+
if (typeof document === "undefined") return null;
|
|
52
|
+
return createPortal(
|
|
53
|
+
/* @__PURE__ */ jsx(ContrastContext.Provider, { value: false, children: /* @__PURE__ */ jsx(
|
|
54
|
+
"dialog",
|
|
55
|
+
{
|
|
56
|
+
ref: dialogRef,
|
|
57
|
+
className: classNames("eds-modal__overlay", className),
|
|
58
|
+
...rest,
|
|
59
|
+
onClick: handleClick,
|
|
60
|
+
children: open && children
|
|
61
|
+
}
|
|
62
|
+
) }),
|
|
63
|
+
document.body
|
|
64
|
+
);
|
|
65
|
+
};
|
|
23
66
|
const headingsMap = {
|
|
24
67
|
extraSmall: Heading4,
|
|
25
68
|
small: Heading3,
|
|
@@ -36,9 +79,9 @@ const ModalContent = ({
|
|
|
36
79
|
...rest
|
|
37
80
|
}) => {
|
|
38
81
|
const Heading = headingsMap[size] || Heading2;
|
|
39
|
-
const randomId =
|
|
82
|
+
const randomId = useId();
|
|
40
83
|
return /* @__PURE__ */ jsxs(
|
|
41
|
-
|
|
84
|
+
"div",
|
|
42
85
|
{
|
|
43
86
|
className: classNames(
|
|
44
87
|
"eds-modal__content",
|
|
@@ -46,7 +89,7 @@ const ModalContent = ({
|
|
|
46
89
|
`eds-modal__content--align-${align}`,
|
|
47
90
|
className
|
|
48
91
|
),
|
|
49
|
-
"aria-labelledby": randomId,
|
|
92
|
+
"aria-labelledby": title ? randomId : void 0,
|
|
50
93
|
...rest,
|
|
51
94
|
children: [
|
|
52
95
|
title && /* @__PURE__ */ jsx(Heading, { margin: "bottom", as: "h2", id: randomId, children: title }),
|
|
@@ -65,22 +108,23 @@ const Modal = ({
|
|
|
65
108
|
align = "start",
|
|
66
109
|
title,
|
|
67
110
|
closeOnClickOutside = true,
|
|
111
|
+
showCloseButton = true,
|
|
112
|
+
"aria-label": ariaLabel,
|
|
68
113
|
...rest
|
|
69
114
|
}) => {
|
|
70
|
-
const randomId =
|
|
115
|
+
const randomId = useId();
|
|
71
116
|
const Heading = headingsMap[size] || Heading2;
|
|
72
|
-
|
|
73
|
-
let handleOnDismiss;
|
|
74
|
-
if (onDismiss && closeOnClickOutside) {
|
|
75
|
-
handleOnDismiss = onDismiss;
|
|
76
|
-
}
|
|
77
|
-
return /* @__PURE__ */ jsx(
|
|
117
|
+
return /* @__PURE__ */ jsxs(
|
|
78
118
|
ModalOverlay,
|
|
79
119
|
{
|
|
80
120
|
open,
|
|
81
|
-
onDismiss
|
|
121
|
+
onDismiss,
|
|
122
|
+
closeOnClickOutside,
|
|
82
123
|
initialFocusRef,
|
|
83
|
-
|
|
124
|
+
className: `eds-modal__overlay--size-${size}`,
|
|
125
|
+
"aria-labelledby": title ? randomId : void 0,
|
|
126
|
+
"aria-label": !title ? ariaLabel : void 0,
|
|
127
|
+
children: [
|
|
84
128
|
showCloseButton && /* @__PURE__ */ jsx(
|
|
85
129
|
IconButton,
|
|
86
130
|
{
|
|
@@ -91,9 +135,21 @@ const Modal = ({
|
|
|
91
135
|
children: /* @__PURE__ */ jsx(CloseIcon, {})
|
|
92
136
|
}
|
|
93
137
|
),
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
138
|
+
/* @__PURE__ */ jsxs(ModalContent, { size, align, ...rest, children: [
|
|
139
|
+
title && /* @__PURE__ */ jsx(
|
|
140
|
+
Heading,
|
|
141
|
+
{
|
|
142
|
+
margin: "bottom",
|
|
143
|
+
as: "h2",
|
|
144
|
+
id: randomId,
|
|
145
|
+
tabIndex: -1,
|
|
146
|
+
"data-autofocus": initialFocusRef ? void 0 : "",
|
|
147
|
+
children: title
|
|
148
|
+
}
|
|
149
|
+
),
|
|
150
|
+
children
|
|
151
|
+
] })
|
|
152
|
+
]
|
|
97
153
|
}
|
|
98
154
|
);
|
|
99
155
|
};
|
|
@@ -108,31 +164,58 @@ const Drawer = ({
|
|
|
108
164
|
style,
|
|
109
165
|
overlay = false
|
|
110
166
|
}) => {
|
|
111
|
-
const titleId =
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
167
|
+
const titleId = useId();
|
|
168
|
+
const drawerRef = useRef(null);
|
|
169
|
+
const previouslyFocusedRef = useRef(null);
|
|
170
|
+
useEffect(
|
|
171
|
+
function handleFocus() {
|
|
172
|
+
if (!open || overlay) return;
|
|
173
|
+
previouslyFocusedRef.current = document.activeElement;
|
|
174
|
+
drawerRef.current?.querySelector("[data-autofocus]")?.focus();
|
|
175
|
+
return () => {
|
|
176
|
+
if (previouslyFocusedRef.current instanceof HTMLElement) {
|
|
177
|
+
previouslyFocusedRef.current.focus();
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
},
|
|
181
|
+
[open, overlay]
|
|
182
|
+
);
|
|
183
|
+
const shouldRemoveFromDOM = !open && !overlay;
|
|
184
|
+
if (shouldRemoveFromDOM) return null;
|
|
185
|
+
function handleKeyDown(e) {
|
|
116
186
|
if (e.key === "Escape") {
|
|
117
187
|
e.stopPropagation();
|
|
118
188
|
onDismiss();
|
|
119
189
|
}
|
|
190
|
+
}
|
|
191
|
+
const Wrapper = contrast ? Contrast : "div";
|
|
192
|
+
const nonModalDialogProps = {
|
|
193
|
+
role: "dialog",
|
|
194
|
+
"aria-modal": false,
|
|
195
|
+
"aria-labelledby": titleId
|
|
120
196
|
};
|
|
121
|
-
const Wrapper = contrast ? Contrast : React.Fragment;
|
|
122
|
-
const ContentContainer = overlay ? DialogContent : "div";
|
|
123
197
|
return /* @__PURE__ */ jsx(
|
|
124
198
|
ConditionalWrapper,
|
|
125
199
|
{
|
|
126
200
|
condition: overlay,
|
|
127
|
-
wrapper: (children2) => /* @__PURE__ */ jsx(
|
|
128
|
-
|
|
129
|
-
ContentContainer,
|
|
201
|
+
wrapper: (children2) => /* @__PURE__ */ jsx(
|
|
202
|
+
ModalOverlay,
|
|
130
203
|
{
|
|
204
|
+
open,
|
|
205
|
+
onDismiss,
|
|
131
206
|
"aria-labelledby": titleId,
|
|
207
|
+
children: children2
|
|
208
|
+
}
|
|
209
|
+
),
|
|
210
|
+
children: /* @__PURE__ */ jsxs(
|
|
211
|
+
Wrapper,
|
|
212
|
+
{
|
|
213
|
+
ref: drawerRef,
|
|
214
|
+
...!overlay && nonModalDialogProps,
|
|
132
215
|
className: classNames("eds-drawer", className),
|
|
133
216
|
onKeyDown: handleKeyDown,
|
|
134
217
|
style,
|
|
135
|
-
children:
|
|
218
|
+
children: [
|
|
136
219
|
/* @__PURE__ */ jsx(
|
|
137
220
|
IconButton,
|
|
138
221
|
{
|
|
@@ -144,16 +227,15 @@ const Drawer = ({
|
|
|
144
227
|
}
|
|
145
228
|
),
|
|
146
229
|
/* @__PURE__ */ jsxs("div", { className: "eds-drawer__content", children: [
|
|
147
|
-
/* @__PURE__ */ jsx(Heading3, { as: "h2", id: titleId, children: title }),
|
|
230
|
+
/* @__PURE__ */ jsx(Heading3, { as: "h2", id: titleId, tabIndex: -1, "data-autofocus": "", children: title }),
|
|
148
231
|
children
|
|
149
232
|
] })
|
|
150
|
-
]
|
|
233
|
+
]
|
|
151
234
|
}
|
|
152
|
-
)
|
|
235
|
+
)
|
|
153
236
|
}
|
|
154
237
|
);
|
|
155
238
|
};
|
|
156
|
-
const ConditionalWrapper = ({ condition, wrapper, children }) => condition ? wrapper(children) : children;
|
|
157
239
|
warnAboutMissingStyles("modal", "icons", "typography", "a11y", "button");
|
|
158
240
|
export {
|
|
159
241
|
Drawer,
|
package/dist/modal.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"modal.esm.js","sources":["../src/ModalOverlay.tsx","../src/ModalContent.tsx","../src/Modal.tsx","../src/Drawer.tsx","../src/index.tsx"],"sourcesContent":["import React from 'react';\nimport classNames from 'classnames';\nimport { DialogOverlay } from '@reach/dialog';\n\nexport type ModalOverlayProps = {\n /** Flagg som sier om modalen er åpen */\n open?: boolean;\n /** Callback som kalles når brukeren ber om å lukke modalen */\n onDismiss?: () => void;\n /** Innholdet i modalen */\n children: React.ReactNode;\n /** Ekstra klassenavn */\n className?: string;\n /** En ref til elementet som skal være fokusert når modalen åpnes. Defaulter til lukkeknappen */\n initialFocusRef?: React.RefObject<HTMLElement>;\n [key: string]: any;\n};\n\nexport const ModalOverlay: React.FC<ModalOverlayProps> = ({\n className,\n open,\n ...rest\n}) => (\n <DialogOverlay\n className={classNames('eds-modal__overlay', className)}\n isOpen={open}\n {...rest}\n />\n);\n","import React from 'react';\nimport classNames from 'classnames';\nimport { DialogContent } from '@reach/dialog';\nimport { Heading2, Heading3, Heading4 } from '@entur/typography';\nimport { useRandomId } from '@entur/utils';\n\nexport type ModalContentProps = {\n /** Innholdet i modalen */\n children: React.ReactNode;\n /** Ekstra klassenavn */\n className?: string;\n /** Størrelsen på modalen */\n size: 'extraSmall' | 'small' | 'medium' | 'large' | 'extraLarge';\n /** Tittelen som vises i modalen */\n title?: React.ReactNode;\n /** Hvordan innholdet skal plasseres i modalen\n * @default 'start'\n */\n align?: 'start' | 'center' | 'end';\n [key: string]: any;\n};\n\nexport const headingsMap = {\n extraSmall: Heading4,\n small: Heading3,\n medium: Heading2,\n large: Heading2,\n extraLarge: Heading2,\n};\n\nexport const ModalContent: React.FC<ModalContentProps> = ({\n children,\n className,\n size,\n title,\n align = 'start',\n ...rest\n}) => {\n const Heading: React.ElementType = headingsMap[size] || Heading2;\n const randomId = useRandomId('eds-modal');\n return (\n <DialogContent\n className={classNames(\n 'eds-modal__content',\n `eds-modal__content--size-${size}`,\n `eds-modal__content--align-${align}`,\n className,\n )}\n aria-labelledby={randomId}\n {...rest}\n >\n {title && (\n <Heading margin=\"bottom\" as=\"h2\" id={randomId}>\n {title}\n </Heading>\n )}\n {children}\n </DialogContent>\n );\n};\n","import React from 'react';\n\nimport { CloseIcon } from '@entur/icons';\nimport { IconButton } from '@entur/button';\nimport { Heading2 } from '@entur/typography';\nimport { useRandomId } from '@entur/utils';\n\nimport { ModalOverlay } from './ModalOverlay';\nimport { ModalContent, headingsMap } from './ModalContent';\n\nimport './Modal.scss';\n\nexport type ModalProps = {\n /** Innholdet i modalen */\n children: React.ReactNode;\n /** Skjermleser-label til lukk-knappen */\n closeLabel?: string;\n /** En ref til elementet som skal være fokusert når modalen åpnes. Defaulter til lukkeknappen */\n initialFocusRef?: React.RefObject<HTMLElement>;\n /** Flagg som sier om modalen er åpen */\n open?: boolean;\n /** Callback som kalles når brukeren ber om å lukke modalen */\n onDismiss?: () => void;\n /** Størrelsen på modalen */\n size: 'extraSmall' | 'small' | 'medium' | 'large' | 'extraLarge';\n /** Hvordan innholdet skal plasseres i modalen\n * @default 'start'\n */\n align?: 'start' | 'center' | 'end';\n /** Tittelen som vises i modalen */\n title?: React.ReactNode;\n /** Om modalen skal lukkes når man klikker på utsiden av den\n * @default true\n */\n closeOnClickOutside?: boolean;\n [key: string]: any;\n};\n\nexport const Modal: React.FC<ModalProps> = ({\n children,\n closeLabel = 'Lukk',\n initialFocusRef,\n open,\n onDismiss,\n size,\n align = 'start',\n title,\n closeOnClickOutside = true,\n ...rest\n}) => {\n const randomId = useRandomId('eds-modal');\n const Heading: React.ElementType = headingsMap[size] || Heading2;\n const showCloseButton = ['medium', 'large', 'extraLarge'].includes(size);\n\n let handleOnDismiss;\n if (onDismiss && closeOnClickOutside) {\n handleOnDismiss = onDismiss;\n }\n return (\n <ModalOverlay\n open={open}\n onDismiss={handleOnDismiss}\n initialFocusRef={initialFocusRef}\n >\n <ModalContent size={size} align={align} {...rest}>\n {showCloseButton && (\n <IconButton\n className=\"eds-modal__close\"\n aria-label={closeLabel}\n onClick={onDismiss}\n type=\"button\"\n >\n <CloseIcon />\n </IconButton>\n )}\n {title && (\n <Heading margin=\"bottom\" as=\"h2\" id={randomId}>\n {title}\n </Heading>\n )}\n {children}\n </ModalContent>\n </ModalOverlay>\n );\n};\n","import React from 'react';\nimport classNames from 'classnames';\nimport { MoveFocusInside } from 'react-focus-lock';\nimport { Contrast } from '@entur/layout';\nimport { CloseIcon } from '@entur/icons';\nimport { Heading3 } from '@entur/typography';\nimport { useRandomId } from '@entur/utils';\nimport { IconButton } from '@entur/button';\n\nimport './Drawer.scss';\nimport { ModalOverlay } from './ModalOverlay';\nimport { DialogContent } from '@reach/dialog';\n\nexport type DrawerProps = {\n /** Innholdet. Typisk tekst, lenker eller knapper */\n children: React.ReactNode;\n /** Ekstra klassenavn */\n className?: string;\n /** Tekst som beskriver lukkeknappen for skjermlesere\n * @default 'Lukk skuff'\n */\n closeLabel?: string;\n /** Om draweren skal vises i mørk variant\n * @default false\n */\n contrast?: boolean;\n /** Callback som kalles når brukeren ønsker å lukke draweren */\n onDismiss: () => void;\n /** Om draweren er åpen eller ikke\n * @default true\n */\n open?: boolean;\n /** Tittel på toppen av draweren */\n title: string;\n /** Styling som sendes til Drawer */\n style?: React.CSSProperties;\n /** Legger på et overlay over resten av siden */\n overlay?: boolean;\n};\n\nexport const Drawer: React.FC<DrawerProps> = ({\n children,\n className,\n closeLabel = 'Lukk skuff',\n contrast = false,\n open = true,\n onDismiss,\n title,\n style,\n overlay = false,\n}) => {\n const titleId = useRandomId('eds-drawer');\n\n if (!open) {\n return null;\n }\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === 'Escape') {\n e.stopPropagation();\n onDismiss();\n }\n };\n\n const Wrapper = contrast ? Contrast : React.Fragment;\n const ContentContainer = overlay ? DialogContent : 'div';\n return (\n <ConditionalWrapper\n condition={overlay}\n wrapper={(children: React.ReactNode) => (\n <ModalOverlay open={open} onDismiss={onDismiss}>\n {children}\n </ModalOverlay>\n )}\n >\n <Wrapper>\n <ContentContainer\n aria-labelledby={titleId}\n className={classNames('eds-drawer', className)}\n onKeyDown={handleKeyDown}\n style={style}\n >\n <MoveFocusInside>\n <IconButton\n className=\"eds-drawer__close-button\"\n onClick={onDismiss}\n type=\"button\"\n aria-label={closeLabel}\n >\n <CloseIcon aria-hidden />\n </IconButton>\n <div className=\"eds-drawer__content\">\n <Heading3 as=\"h2\" id={titleId}>\n {title}\n </Heading3>\n {children}\n </div>\n </MoveFocusInside>\n </ContentContainer>\n </Wrapper>\n </ConditionalWrapper>\n );\n};\n\nconst ConditionalWrapper: React.FC<{\n condition: boolean;\n wrapper: (child: JSX.Element) => JSX.Element;\n children: React.ReactElement;\n}> = ({ condition, wrapper, children }) =>\n condition ? wrapper(children) : children;\n","import { warnAboutMissingStyles } from '@entur/utils';\nimport './index.scss';\n\nwarnAboutMissingStyles('modal', 'icons', 'typography', 'a11y', 'button');\n\nexport * from './Modal';\nexport * from './ModalOverlay';\nexport * from './ModalContent';\nexport * from './Drawer';\n"],"names":["children"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"modal.esm.js","sources":["../src/ModalOverlay.tsx","../src/ModalContent.tsx","../src/Modal.tsx","../src/Drawer.tsx","../src/index.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useRef } from 'react';\nimport { createPortal } from 'react-dom';\nimport classNames from 'classnames';\nimport { ContrastContext } from '@entur/layout';\n\nexport type ModalOverlayProps = {\n /** Flagg som sier om modalen er åpen */\n open?: boolean;\n /** Callback som kalles når brukeren ber om å lukke modalen */\n onDismiss?: () => void;\n /** Innholdet i modalen */\n children: React.ReactNode;\n /** Ekstra klassenavn */\n className?: string;\n /** En ref til elementet som skal være fokusert når modalen åpnes. Defaulter til lukkeknappen */\n initialFocusRef?: React.RefObject<HTMLElement>;\n /** Om modalen skal lukkes når man klikker utenfor den\n * @default true\n */\n closeOnClickOutside?: boolean;\n} & Omit<React.DialogHTMLAttributes<HTMLDialogElement>, 'open' | 'onClick'>;\n\nexport const ModalOverlay = ({\n className,\n open,\n onDismiss,\n initialFocusRef,\n closeOnClickOutside = true,\n children,\n ...rest\n}: ModalOverlayProps) => {\n const dialogRef = useRef<HTMLDialogElement>(null);\n\n // Toggle native dialog open state. Always stays in DOM so the browser can\n // manage initial focus storage + restore on close.\n useEffect(() => {\n const dialog = dialogRef.current;\n if (!dialog) return;\n if (open && !dialog.open) {\n dialog.showModal();\n const target =\n initialFocusRef?.current ??\n dialog.querySelector<HTMLElement>('[data-autofocus]') ??\n dialog;\n target.focus();\n } else if (!open && dialog.open) {\n dialog.close();\n }\n }, [open, initialFocusRef]);\n\n // Esc fires cancel — preventDefault so we route through onDismiss + state,\n // which triggers the effect above to call close() (native restore runs).\n useEffect(\n function syncCancelEvent() {\n const dialog = dialogRef.current;\n if (!dialog || !onDismiss) return;\n const handleCancel = (e: Event) => {\n e.preventDefault();\n onDismiss();\n };\n dialog.addEventListener('cancel', handleCancel);\n return () => dialog.removeEventListener('cancel', handleCancel);\n },\n [onDismiss],\n );\n\n const handleClick = useCallback(\n (e: React.MouseEvent<HTMLDialogElement>) => {\n if (!closeOnClickOutside) return;\n if (e.target === dialogRef.current) onDismiss?.();\n },\n [onDismiss, closeOnClickOutside],\n );\n\n if (typeof document === 'undefined') return null;\n\n return createPortal(\n <ContrastContext.Provider value={false}>\n <dialog\n ref={dialogRef}\n className={classNames('eds-modal__overlay', className)}\n {...rest}\n onClick={handleClick}\n >\n {open && children}\n </dialog>\n </ContrastContext.Provider>,\n document.body,\n );\n};\n","import React, { useId } from 'react';\nimport classNames from 'classnames';\nimport { Heading4, Heading3, Heading2 } from '@entur/typography';\n\nexport type ModalContentProps = {\n /** Innholdet i modalen */\n children: React.ReactNode;\n /** Ekstra klassenavn */\n className?: string;\n /** Størrelsen på modalen */\n size: 'extraSmall' | 'small' | 'medium' | 'large' | 'extraLarge';\n /** Tittelen som vises i modalen */\n title?: React.ReactNode;\n /** Hvordan innholdet skal plasseres i modalen\n * @default 'start'\n */\n align?: 'start' | 'center' | 'end';\n} & Omit<React.HTMLAttributes<HTMLDivElement>, 'title'>;\n\nexport const headingsMap = {\n extraSmall: Heading4,\n small: Heading3,\n medium: Heading2,\n large: Heading2,\n extraLarge: Heading2,\n};\n\nexport const ModalContent = ({\n children,\n className,\n size,\n title,\n align = 'start',\n ...rest\n}: ModalContentProps) => {\n const Heading: React.ElementType = headingsMap[size] || Heading2;\n const randomId = useId();\n return (\n <div\n className={classNames(\n 'eds-modal__content',\n `eds-modal__content--size-${size}`,\n `eds-modal__content--align-${align}`,\n className,\n )}\n aria-labelledby={title ? randomId : undefined}\n {...rest}\n >\n {title && (\n <Heading margin=\"bottom\" as=\"h2\" id={randomId}>\n {title}\n </Heading>\n )}\n {children}\n </div>\n );\n};\n","import React, { useId } from 'react';\n\nimport { CloseIcon } from '@entur/icons';\nimport { IconButton } from '@entur/button';\nimport { Heading2 } from '@entur/typography';\n\nimport { ModalOverlay } from './ModalOverlay';\nimport { ModalContent, headingsMap } from './ModalContent';\n\nimport './Modal.scss';\n\nexport type ModalProps = {\n /** Innholdet i modalen */\n children: React.ReactNode;\n /** Skjermleser-label til lukk-knappen */\n closeLabel?: string;\n /** En ref til elementet som skal være fokusert når modalen åpnes. Defaulter til tittelen, ellers første interaktive element */\n initialFocusRef?: React.RefObject<HTMLElement>;\n /** Styrer om modalen er synlig */\n open?: boolean;\n /** Callback som kalles når brukeren ber om å lukke modalen. Påkrevd: Esc og klikk utenfor lukker alltid (nettleserens innebygde atferd for native dialog) */\n onDismiss: () => void;\n /** Om lukkeknappen skal vises i øvre høyre hjørne\n * @default true\n */\n showCloseButton?: boolean;\n /** Størrelsen på modalen */\n size: 'extraSmall' | 'small' | 'medium' | 'large' | 'extraLarge';\n /** Hvordan innholdet skal plasseres i modalen\n * @default 'start'\n */\n align?: 'start' | 'center' | 'end';\n /** Tittelen som vises i modalen */\n title?: React.ReactNode;\n /** Tilgjengelig navn for modalen når title ikke er satt */\n 'aria-label'?: string;\n /** Om modalen skal lukkes når man klikker på utsiden av den\n * @default true\n */\n closeOnClickOutside?: boolean;\n} & Omit<React.HTMLAttributes<HTMLDivElement>, 'title' | 'aria-label'>;\n\nexport const Modal = ({\n children,\n closeLabel = 'Lukk',\n initialFocusRef,\n open,\n onDismiss,\n size,\n align = 'start',\n title,\n closeOnClickOutside = true,\n showCloseButton = true,\n 'aria-label': ariaLabel,\n ...rest\n}: ModalProps) => {\n const randomId = useId();\n const Heading: React.ElementType = headingsMap[size] || Heading2;\n\n return (\n <ModalOverlay\n open={open}\n onDismiss={onDismiss}\n closeOnClickOutside={closeOnClickOutside}\n initialFocusRef={initialFocusRef}\n className={`eds-modal__overlay--size-${size}`}\n aria-labelledby={title ? randomId : undefined}\n aria-label={!title ? ariaLabel : undefined}\n >\n {showCloseButton && (\n <IconButton\n className=\"eds-modal__close\"\n aria-label={closeLabel}\n onClick={onDismiss}\n type=\"button\"\n >\n <CloseIcon />\n </IconButton>\n )}\n <ModalContent size={size} align={align} {...rest}>\n {title && (\n <Heading\n margin=\"bottom\"\n as=\"h2\"\n id={randomId}\n tabIndex={-1}\n data-autofocus={initialFocusRef ? undefined : ''}\n >\n {title}\n </Heading>\n )}\n {children}\n </ModalContent>\n </ModalOverlay>\n );\n};\n","import React, { useEffect, useId, useRef } from 'react';\nimport classNames from 'classnames';\nimport { Contrast } from '@entur/layout';\nimport { CloseIcon } from '@entur/icons';\nimport { Heading3 } from '@entur/typography';\nimport { IconButton } from '@entur/button';\nimport { ConditionalWrapper } from '@entur/utils';\nimport { ModalOverlay } from './ModalOverlay';\n\nimport './Drawer.scss';\n\nexport type DrawerProps = {\n /** Innholdet. Typisk tekst, lenker eller knapper */\n children: React.ReactNode;\n /** Ekstra klassenavn */\n className?: string;\n /** Tekst som beskriver lukkeknappen for skjermlesere\n * @default 'Lukk skuff'\n */\n closeLabel?: string;\n /** Om draweren skal vises i mørk variant\n * @default false\n */\n contrast?: boolean;\n /** Callback som kalles når brukeren ønsker å lukke draweren */\n onDismiss: () => void;\n /** Om draweren er åpen eller ikke\n * @default true\n */\n open?: boolean;\n /** Tittel på toppen av draweren */\n title: string;\n /** Styling som sendes til Drawer */\n style?: React.CSSProperties;\n /** Legger på et overlay over resten av siden */\n overlay?: boolean;\n};\n\nexport const Drawer = ({\n children,\n className,\n closeLabel = 'Lukk skuff',\n contrast = false,\n open = true,\n onDismiss,\n title,\n style,\n overlay = false,\n}: DrawerProps) => {\n const titleId = useId();\n const drawerRef = useRef<HTMLDivElement>(null);\n const previouslyFocusedRef = useRef<Element | null>(null);\n\n useEffect(\n function handleFocus() {\n if (!open || overlay) return;\n previouslyFocusedRef.current = document.activeElement;\n drawerRef.current\n ?.querySelector<HTMLElement>('[data-autofocus]')\n ?.focus();\n\n return () => {\n if (previouslyFocusedRef.current instanceof HTMLElement) {\n previouslyFocusedRef.current.focus();\n }\n };\n },\n [open, overlay],\n );\n\n const shouldRemoveFromDOM = !open && !overlay;\n if (shouldRemoveFromDOM) return null;\n\n function handleKeyDown(e: React.KeyboardEvent) {\n if (e.key === 'Escape') {\n e.stopPropagation();\n onDismiss();\n }\n }\n\n const Wrapper = contrast ? Contrast : 'div';\n const nonModalDialogProps = {\n role: 'dialog' as const,\n 'aria-modal': false,\n 'aria-labelledby': titleId,\n };\n\n return (\n <ConditionalWrapper\n condition={overlay}\n wrapper={(children: React.ReactNode) => (\n <ModalOverlay\n open={open}\n onDismiss={onDismiss}\n aria-labelledby={titleId}\n >\n {children}\n </ModalOverlay>\n )}\n >\n <Wrapper\n ref={drawerRef}\n {...(!overlay && nonModalDialogProps)}\n className={classNames('eds-drawer', className)}\n onKeyDown={handleKeyDown}\n style={style}\n >\n <IconButton\n className=\"eds-drawer__close-button\"\n onClick={onDismiss}\n type=\"button\"\n aria-label={closeLabel}\n >\n <CloseIcon aria-hidden />\n </IconButton>\n <div className=\"eds-drawer__content\">\n <Heading3 as=\"h2\" id={titleId} tabIndex={-1} data-autofocus=\"\">\n {title}\n </Heading3>\n {children}\n </div>\n </Wrapper>\n </ConditionalWrapper>\n );\n};\n","import { warnAboutMissingStyles } from '@entur/utils';\nimport './index.scss';\n\nwarnAboutMissingStyles('modal', 'icons', 'typography', 'a11y', 'button');\n\nexport * from './Modal';\nexport * from './ModalOverlay';\nexport * from './ModalContent';\nexport * from './Drawer';\n"],"names":["children"],"mappings":"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"}
|
package/dist/styles.css
CHANGED
|
@@ -199,35 +199,71 @@
|
|
|
199
199
|
|
|
200
200
|
:root{
|
|
201
201
|
--eds-modal:1;
|
|
202
|
-
}:
|
|
203
|
-
|
|
202
|
+
}html:has(dialog.eds-modal__overlay[open]),
|
|
203
|
+
html:has(dialog.eds-modal__overlay[open]) body{
|
|
204
|
+
overflow:hidden;
|
|
204
205
|
}
|
|
205
206
|
|
|
206
207
|
.eds-modal__overlay{
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
left:0;
|
|
214
|
-
overflow:hidden;
|
|
215
|
-
position:fixed;
|
|
216
|
-
right:0;
|
|
217
|
-
top:0;
|
|
208
|
+
border:none;
|
|
209
|
+
padding:0;
|
|
210
|
+
background:transparent;
|
|
211
|
+
color:inherit;
|
|
212
|
+
}
|
|
213
|
+
.eds-modal__overlay[open]{
|
|
218
214
|
z-index:30;
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
-webkit-animation
|
|
224
|
-
-moz-animation
|
|
225
|
-
-o-animation
|
|
226
|
-
animation
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
215
|
+
width:100%;
|
|
216
|
+
overflow:hidden;
|
|
217
|
+
-ms-scroll-chaining:none;
|
|
218
|
+
overscroll-behavior:contain;
|
|
219
|
+
-webkit-animation:fadeInOverlay 0.2s ease-in-out;
|
|
220
|
+
-moz-animation:fadeInOverlay 0.2s ease-in-out;
|
|
221
|
+
-o-animation:fadeInOverlay 0.2s ease-in-out;
|
|
222
|
+
animation:fadeInOverlay 0.2s ease-in-out;
|
|
223
|
+
}
|
|
224
|
+
.eds-modal__overlay[open]::-webkit-backdrop{
|
|
225
|
+
background:rgba(0, 0, 0, 0.5);
|
|
226
|
+
-webkit-animation:fadeInOverlay 0.2s ease-in-out;
|
|
227
|
+
animation:fadeInOverlay 0.2s ease-in-out;
|
|
228
|
+
}
|
|
229
|
+
.eds-modal__overlay[open]::-ms-backdrop{
|
|
230
|
+
background:rgba(0, 0, 0, 0.5);
|
|
231
|
+
animation:fadeInOverlay 0.2s ease-in-out;
|
|
232
|
+
}
|
|
233
|
+
.eds-modal__overlay[open]::backdrop{
|
|
234
|
+
background:rgba(0, 0, 0, 0.5);
|
|
235
|
+
-webkit-animation:fadeInOverlay 0.2s ease-in-out;
|
|
236
|
+
-moz-animation:fadeInOverlay 0.2s ease-in-out;
|
|
237
|
+
-o-animation:fadeInOverlay 0.2s ease-in-out;
|
|
238
|
+
animation:fadeInOverlay 0.2s ease-in-out;
|
|
239
|
+
}
|
|
240
|
+
.eds-modal__overlay--size-extraSmall{
|
|
241
|
+
max-width:21rem;
|
|
242
|
+
}
|
|
243
|
+
.eds-modal__overlay--size-small{
|
|
244
|
+
max-width:28.125rem;
|
|
245
|
+
}
|
|
246
|
+
.eds-modal__overlay--size-medium{
|
|
247
|
+
max-width:45rem;
|
|
248
|
+
}
|
|
249
|
+
.eds-modal__overlay--size-large{
|
|
250
|
+
max-width:56.25rem;
|
|
251
|
+
}
|
|
252
|
+
.eds-modal__overlay--size-extraLarge{
|
|
253
|
+
max-width:78.75rem;
|
|
254
|
+
}
|
|
255
|
+
.eds-modal__close{
|
|
256
|
+
position:absolute;
|
|
257
|
+
top:1rem;
|
|
258
|
+
right:1rem;
|
|
259
|
+
color:var(--components-modal-modal-standard-icon);
|
|
260
|
+
z-index:-webkit-calc(40 + 1);
|
|
261
|
+
z-index:-moz-calc(40 + 1);
|
|
262
|
+
z-index:calc(40 + 1);
|
|
263
|
+
-webkit-animation:slideInContent 0.2s ease-in-out;
|
|
264
|
+
-moz-animation:slideInContent 0.2s ease-in-out;
|
|
265
|
+
-o-animation:slideInContent 0.2s ease-in-out;
|
|
266
|
+
animation:slideInContent 0.2s ease-in-out;
|
|
231
267
|
}
|
|
232
268
|
.eds-modal__content{
|
|
233
269
|
background:var(--components-modal-modal-standard-fill);
|
|
@@ -236,25 +272,14 @@
|
|
|
236
272
|
-moz-border-radius:0.75rem;
|
|
237
273
|
border-radius:0.75rem;
|
|
238
274
|
color:var(--components-modal-modal-standard-text);
|
|
239
|
-
margin:auto;
|
|
240
275
|
padding:1.5rem;
|
|
241
|
-
position:relative;
|
|
242
276
|
width:100%;
|
|
243
277
|
max-height:90vh;
|
|
244
278
|
overflow:auto;
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
animation:slideInContent;
|
|
250
|
-
-webkit-animation-duration:0.2s;
|
|
251
|
-
-moz-animation-duration:0.2s;
|
|
252
|
-
-o-animation-duration:0.2s;
|
|
253
|
-
animation-duration:0.2s;
|
|
254
|
-
-webkit-animation-timing-function:ease-in-out;
|
|
255
|
-
-moz-animation-timing-function:ease-in-out;
|
|
256
|
-
-o-animation-timing-function:ease-in-out;
|
|
257
|
-
animation-timing-function:ease-in-out;
|
|
279
|
+
-webkit-animation:slideInContent 0.2s ease-in-out;
|
|
280
|
+
-moz-animation:slideInContent 0.2s ease-in-out;
|
|
281
|
+
-o-animation:slideInContent 0.2s ease-in-out;
|
|
282
|
+
animation:slideInContent 0.2s ease-in-out;
|
|
258
283
|
}
|
|
259
284
|
.eds-modal__content--align-center{
|
|
260
285
|
display:-webkit-box;
|
|
@@ -296,18 +321,22 @@
|
|
|
296
321
|
.eds-modal__content--size-small{
|
|
297
322
|
max-width:28.125rem;
|
|
298
323
|
padding:2.5rem;
|
|
324
|
+
padding-top:2rem;
|
|
299
325
|
}
|
|
300
326
|
.eds-modal__content--size-medium{
|
|
301
327
|
max-width:45rem;
|
|
302
328
|
padding:3rem;
|
|
329
|
+
padding-top:2rem;
|
|
303
330
|
}
|
|
304
331
|
.eds-modal__content--size-large{
|
|
305
332
|
max-width:56.25rem;
|
|
306
333
|
padding:3rem;
|
|
334
|
+
padding-top:2rem;
|
|
307
335
|
}
|
|
308
336
|
.eds-modal__content--size-extraLarge{
|
|
309
337
|
max-width:78.75rem;
|
|
310
338
|
padding:3rem;
|
|
339
|
+
padding-top:2rem;
|
|
311
340
|
}
|
|
312
341
|
.eds-modal__content::-webkit-scrollbar{
|
|
313
342
|
width:16px;
|
|
@@ -325,12 +354,6 @@
|
|
|
325
354
|
.eds-modal__content::-webkit-scrollbar-thumb:hover{
|
|
326
355
|
background-color:grey;
|
|
327
356
|
}
|
|
328
|
-
.eds-modal__close{
|
|
329
|
-
position:absolute;
|
|
330
|
-
top:1rem;
|
|
331
|
-
right:1rem;
|
|
332
|
-
color:var(--components-modal-modal-standard-icon);
|
|
333
|
-
}
|
|
334
357
|
|
|
335
358
|
@-webkit-keyframes fadeInOverlay{
|
|
336
359
|
from{
|
|
@@ -369,43 +392,73 @@
|
|
|
369
392
|
}
|
|
370
393
|
@-webkit-keyframes slideInContent{
|
|
371
394
|
from{
|
|
372
|
-
|
|
395
|
+
-webkit-transform:translateY(0.5rem);
|
|
396
|
+
transform:translateY(0.5rem);
|
|
373
397
|
opacity:0;
|
|
374
398
|
}
|
|
375
399
|
to{
|
|
376
|
-
|
|
400
|
+
-webkit-transform:translateY(0);
|
|
401
|
+
transform:translateY(0);
|
|
377
402
|
opacity:1;
|
|
378
403
|
}
|
|
379
404
|
}
|
|
380
405
|
@-moz-keyframes slideInContent{
|
|
381
406
|
from{
|
|
382
|
-
|
|
407
|
+
-moz-transform:translateY(0.5rem);
|
|
408
|
+
transform:translateY(0.5rem);
|
|
383
409
|
opacity:0;
|
|
384
410
|
}
|
|
385
411
|
to{
|
|
386
|
-
|
|
412
|
+
-moz-transform:translateY(0);
|
|
413
|
+
transform:translateY(0);
|
|
387
414
|
opacity:1;
|
|
388
415
|
}
|
|
389
416
|
}
|
|
390
417
|
@-o-keyframes slideInContent{
|
|
391
418
|
from{
|
|
392
|
-
|
|
419
|
+
-o-transform:translateY(0.5rem);
|
|
420
|
+
transform:translateY(0.5rem);
|
|
393
421
|
opacity:0;
|
|
394
422
|
}
|
|
395
423
|
to{
|
|
396
|
-
|
|
424
|
+
-o-transform:translateY(0);
|
|
425
|
+
transform:translateY(0);
|
|
397
426
|
opacity:1;
|
|
398
427
|
}
|
|
399
428
|
}
|
|
400
429
|
@keyframes slideInContent{
|
|
401
430
|
from{
|
|
402
|
-
|
|
431
|
+
-webkit-transform:translateY(0.5rem);
|
|
432
|
+
-moz-transform:translateY(0.5rem);
|
|
433
|
+
-o-transform:translateY(0.5rem);
|
|
434
|
+
transform:translateY(0.5rem);
|
|
403
435
|
opacity:0;
|
|
404
436
|
}
|
|
405
437
|
to{
|
|
406
|
-
|
|
438
|
+
-webkit-transform:translateY(0);
|
|
439
|
+
-moz-transform:translateY(0);
|
|
440
|
+
-o-transform:translateY(0);
|
|
441
|
+
transform:translateY(0);
|
|
407
442
|
opacity:1;
|
|
408
443
|
}
|
|
444
|
+
}
|
|
445
|
+
@media (prefers-reduced-motion: reduce){
|
|
446
|
+
.eds-modal__overlay::-webkit-backdrop{
|
|
447
|
+
-webkit-animation:none;
|
|
448
|
+
animation:none;
|
|
449
|
+
}
|
|
450
|
+
.eds-modal__overlay::-ms-backdrop{
|
|
451
|
+
animation:none;
|
|
452
|
+
}
|
|
453
|
+
.eds-modal__overlay,
|
|
454
|
+
.eds-modal__overlay::backdrop,
|
|
455
|
+
.eds-modal__content,
|
|
456
|
+
.eds-modal__close{
|
|
457
|
+
-webkit-animation:none;
|
|
458
|
+
-moz-animation:none;
|
|
459
|
+
-o-animation:none;
|
|
460
|
+
animation:none;
|
|
461
|
+
}
|
|
409
462
|
}.eds-drawer{
|
|
410
463
|
-webkit-animation:slideFromRight forwards ease-out 0.1s;
|
|
411
464
|
-moz-animation:slideFromRight forwards ease-out 0.1s;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@entur/modal",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0-next.0",
|
|
4
4
|
"license": "EUPL-1.2",
|
|
5
5
|
"main": "dist/modal.cjs.js",
|
|
6
6
|
"module": "dist/modal.esm.js",
|
|
@@ -22,24 +22,22 @@
|
|
|
22
22
|
"test": "jest"
|
|
23
23
|
},
|
|
24
24
|
"peerDependencies": {
|
|
25
|
-
"react": ">=
|
|
26
|
-
"react-dom": ">=
|
|
25
|
+
"react": ">=18.0.0",
|
|
26
|
+
"react-dom": ">=18.0.0"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@entur/a11y": "^0.
|
|
30
|
-
"@entur/button": "^
|
|
31
|
-
"@entur/icons": "^
|
|
32
|
-
"@entur/layout": "^
|
|
33
|
-
"@entur/tokens": "^3.24.0",
|
|
34
|
-
"@entur/typography": "^
|
|
35
|
-
"@entur/utils": "^0.
|
|
36
|
-
"
|
|
37
|
-
"classnames": "^2.5.1",
|
|
38
|
-
"react-focus-lock": "^2.13.6"
|
|
29
|
+
"@entur/a11y": "^1.0.0-next.0",
|
|
30
|
+
"@entur/button": "^5.0.0-next.0",
|
|
31
|
+
"@entur/icons": "^10.0.0-next.0",
|
|
32
|
+
"@entur/layout": "^4.0.0-next.0",
|
|
33
|
+
"@entur/tokens": "^3.24.1-next.0",
|
|
34
|
+
"@entur/typography": "^3.0.0-next.0",
|
|
35
|
+
"@entur/utils": "^1.0.0-next.0",
|
|
36
|
+
"classnames": "^2.5.1"
|
|
39
37
|
},
|
|
40
38
|
"devDependencies": {
|
|
41
39
|
"@testing-library/jest-dom": "^5.17.0",
|
|
42
|
-
"@testing-library/react": "^
|
|
40
|
+
"@testing-library/react": "^16.3.0",
|
|
43
41
|
"@testing-library/user-event": "14.6.1",
|
|
44
42
|
"@vitejs/plugin-react": "^5.0.1",
|
|
45
43
|
"jest": "^29.0.0",
|
|
@@ -49,5 +47,5 @@
|
|
|
49
47
|
"vite": "^7.3.2",
|
|
50
48
|
"vite-plugin-dts": "^4.5.4"
|
|
51
49
|
},
|
|
52
|
-
"gitHead": "
|
|
50
|
+
"gitHead": "0f435bdeec98cc3cea1db7434b13b1f771a39a18"
|
|
53
51
|
}
|