@obosbbl/grunnmuren-react 2.0.0-canary.5 → 2.0.0-canary.51

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/index.mjs CHANGED
@@ -1,29 +1,383 @@
1
- import { Text, CheckboxContext, Checkbox as Checkbox$1, Label as Label$1, CheckboxGroup as CheckboxGroup$1, FieldError, ListBoxItem, ComboBox, Group, Input, Button, Popover, ListBox, RadioGroup as RadioGroup$1, Radio as Radio$1, Select as Select$1, SelectValue, TextField as TextField$1, TextArea as TextArea$1 } from 'react-aria-components';
2
- export { Form, I18nProvider } from 'react-aria-components';
3
- export { _ as Button } from './Button-client-wuoyidfi.js';
1
+ 'use client';
2
+ import { I18nProvider, RouterProvider, useLocale, useContextProps, Provider, Link, Button as Button$1, Text, CheckboxContext, Checkbox as Checkbox$1, FieldError, Label as Label$1, CheckboxGroup as CheckboxGroup$1, ListBoxItem as ListBoxItem$1, ListBoxSection as ListBoxSection$1, Header, ListBox as ListBox$1, ComboBox, Group, Input, Popover, RadioGroup as RadioGroup$1, Radio as Radio$1, Select as Select$1, SelectValue, TextField as TextField$1, TextArea as TextArea$1, NumberField as NumberField$1, Breadcrumbs as Breadcrumbs$1, Breadcrumb as Breadcrumb$1, ButtonContext, DisclosureContext, DisclosureGroupStateContext } from 'react-aria-components';
3
+ export { Form, DisclosureGroup as UNSAFE_DisclosureGroup } from 'react-aria-components';
4
4
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
- import { forwardRef, useId } from 'react';
5
+ import { ChevronDown, LoadingSpinner, Check, Close, InfoCircle, CheckCircle, Warning, Error, ChevronRight, ChevronLeft, PlayerPause, PlayerPlay } from '@obosbbl/grunnmuren-icons-react';
6
+ import { useLayoutEffect, filterDOMProps } from '@react-aria/utils';
6
7
  import { cx, cva, compose } from 'cva';
7
- import { Check, LoadingSpinner, ChevronDown } from '@obosbbl/grunnmuren-icons-react';
8
+ import { createContext, forwardRef, Children, useId, useState, useRef, useEffect, useContext } from 'react';
9
+ import { useProgressBar, useDateFormatter, useDisclosure } from 'react-aria';
10
+ import { useDisclosureState } from 'react-stately';
11
+
12
+ function GrunnmurenProvider({ children, locale = 'nb', navigate, useHref }) {
13
+ return /*#__PURE__*/ jsx(I18nProvider, {
14
+ locale: locale,
15
+ children: navigate ? /*#__PURE__*/ jsx(RouterProvider, {
16
+ navigate: navigate,
17
+ useHref: useHref,
18
+ children: children
19
+ }) : children
20
+ });
21
+ }
22
+
23
+ /**
24
+ * Returns the locale set in `<GrunnmurenProvider />`
25
+ */ function _useLocale() {
26
+ // a small wrapper around react-arias useLocale with a simpler return type with only the locales that we actually support
27
+ const locale = useLocale();
28
+ return locale.locale;
29
+ }
30
+
31
+ const HeadingContext = /*#__PURE__*/ createContext({});
32
+ const Heading = (props, ref)=>{
33
+ // biome-ignore lint/style/noParameterAssign: fix when removing refs for React 19
34
+ [props, ref] = useContextProps(props, ref, HeadingContext);
35
+ const { children, level, className, _innerWrapper: innerWrapper, ...restProps } = props;
36
+ const Element = `h${level}`;
37
+ return /*#__PURE__*/ jsx(Element, {
38
+ ...restProps,
39
+ className: className,
40
+ "data-slot": "heading",
41
+ children: innerWrapper ? innerWrapper(children) : children
42
+ });
43
+ };
44
+ const ContentContext = /*#__PURE__*/ createContext({});
45
+ const Content = (props, ref)=>{
46
+ // biome-ignore lint/style/noParameterAssign: fix when removing refs for React 19
47
+ [props, ref] = useContextProps(props, ref, ContentContext);
48
+ const { _outerWrapper: outerWrapper, ...restProps } = props;
49
+ const content = /*#__PURE__*/ jsx("div", {
50
+ ...restProps,
51
+ "data-slot": "content"
52
+ });
53
+ return outerWrapper ? outerWrapper(content) : content;
54
+ };
55
+ const Media = (props)=>/*#__PURE__*/ jsx("div", {
56
+ ...props,
57
+ "data-slot": "media"
58
+ });
59
+ const Caption = ({ className, ...restProps })=>/*#__PURE__*/ jsx("div", {
60
+ ...restProps,
61
+ className: cx('description', className),
62
+ "data-slot": "caption"
63
+ });
64
+ const Footer = (props)=>/*#__PURE__*/ jsx("div", {
65
+ ...props,
66
+ "data-slot": "footer"
67
+ });
68
+ const _Heading = /*#__PURE__*/ forwardRef(Heading);
69
+ const _Content = /*#__PURE__*/ forwardRef(Content);
70
+
71
+ function Accordion(props, ref) {
72
+ const { children, className, ...restProps } = props;
73
+ const childCount = Children.count(children);
74
+ return /*#__PURE__*/ jsx("div", {
75
+ ...restProps,
76
+ ref: ref,
77
+ className: cx('rounded-lg bg-white', className),
78
+ children: Children.map(children, (child, index)=>/*#__PURE__*/ jsxs(Fragment, {
79
+ children: [
80
+ child,
81
+ index < childCount - 1 && // Margin is added to enable support for containers with a background color
82
+ /*#__PURE__*/ jsx("hr", {
83
+ className: "mx-2 border-gray-light",
84
+ "aria-hidden": true
85
+ })
86
+ ]
87
+ }))
88
+ });
89
+ }
90
+ function AccordionItem(props, ref) {
91
+ const { className, children, defaultOpen = false, isOpen: controlledIsOpen, onOpenChange, ...restProps } = props;
92
+ const contentId = useId();
93
+ const buttonId = useId();
94
+ const isControlled = controlledIsOpen != null;
95
+ // This component has internal state that controls whether it is open or not,
96
+ // regardless if we are controlled or uncontrolled.
97
+ // If we are controlled, we use a layout effect to sync the controlled state
98
+ // with the internal state.
99
+ //
100
+ const [isOpen, setIsOpen] = useState(// If we are controlled, use that open state, otherwise use the uncontrolled
101
+ isControlled ? controlledIsOpen : defaultOpen);
102
+ useLayoutEffect(()=>{
103
+ if (isControlled) {
104
+ setIsOpen(controlledIsOpen);
105
+ }
106
+ }, [
107
+ controlledIsOpen,
108
+ isControlled
109
+ ]);
110
+ const handleOpenChange = ()=>{
111
+ const newOpenState = !isOpen;
112
+ if (!isControlled) {
113
+ setIsOpen(newOpenState);
114
+ }
115
+ // Always call the change handler, even if we're uncontrolled.
116
+ // Easier to add stuff such as tracking etc.
117
+ if (onOpenChange) {
118
+ onOpenChange(newOpenState);
119
+ }
120
+ };
121
+ return /*#__PURE__*/ jsx("div", {
122
+ ...restProps,
123
+ className: cx('relative px-2', className),
124
+ ref: ref,
125
+ "data-open": isOpen,
126
+ children: /*#__PURE__*/ jsx(Provider, {
127
+ values: [
128
+ [
129
+ HeadingContext,
130
+ {
131
+ // Negative margin to strech the button to the entire with of the accordion (to support containers with a background color)
132
+ className: 'font-semibold leading-7 -mx-2 text-base',
133
+ // Supply a default level here to make this typecheck ok. Will be overwritten with the consumers set heading level anyways
134
+ level: 3,
135
+ _innerWrapper: (children)=>/*#__PURE__*/ jsxs("button", {
136
+ "aria-controls": contentId,
137
+ "aria-expanded": isOpen,
138
+ // Use outline with offset as focus indicator, this does not cover the left mint border on the expanded content and works with or without a background color on the accordion container
139
+ className: "flex min-h-[44px] w-full items-center justify-between gap-1.5 rounded-lg px-2 py-3.5 text-left focus-visible:outline-focus focus-visible:outline-focus-inset",
140
+ id: buttonId,
141
+ onClick: handleOpenChange,
142
+ type: "button",
143
+ children: [
144
+ children,
145
+ /*#__PURE__*/ jsx(ChevronDown, {
146
+ className: cx('flex-none transition-transform duration-300 motion-reduce:transition-none', isOpen && 'rotate-180')
147
+ })
148
+ ]
149
+ })
150
+ }
151
+ ],
152
+ [
153
+ ContentContext,
154
+ {
155
+ className: // Uses pseudo element for vertical padding, since that doesn't affect the height when the accordion is closed
156
+ 'text-sm font-light leading-6 px-3.5 relative overflow-hidden border-mint border-l-[3px] before:relative before:block before:h-1.5 after:relative after:block after:h-1.5',
157
+ role: 'region',
158
+ // @ts-expect-error TODO: remove this expect-error when we're on React 19 https://github.com/facebook/react/issues/17157#issuecomment-2003750544
159
+ inert: isOpen ? undefined : 'true',
160
+ 'aria-labelledby': buttonId,
161
+ _outerWrapper: (children)=>/*#__PURE__*/ jsx("div", {
162
+ className: cx('grid transition-all duration-300 after:relative after:block after:h-0 after:transition-all after:duration-300 motion-reduce:transition-none', isOpen ? 'grid-rows-[1fr] after:h-3.5' : 'grid-rows-[0fr]'),
163
+ children: children
164
+ })
165
+ }
166
+ ]
167
+ ],
168
+ children: children
169
+ })
170
+ });
171
+ }
172
+ const _Accordion = /*#__PURE__*/ forwardRef(Accordion);
173
+ const _AccordionItem = /*#__PURE__*/ forwardRef(AccordionItem);
174
+
175
+ const badgeVariants = cva({
176
+ base: [
177
+ 'inline-flex w-fit items-center justify-center gap-1.5 rounded-lg [&_svg]:shrink-0'
178
+ ],
179
+ variants: {
180
+ color: {
181
+ 'gray-dark': 'bg-gray-dark text-white',
182
+ mint: 'bg-mint',
183
+ sky: 'bg-sky',
184
+ white: 'bg-white',
185
+ 'blue-dark': 'bg-blue-dark text-white',
186
+ 'green-dark': 'bg-green-dark text-white'
187
+ },
188
+ size: {
189
+ small: 'description px-2 py-0.5 [&_svg]:h-4 [&_svg]:w-4',
190
+ medium: 'description px-2.5 py-1.5 [&_svg]:h-4 [&_svg]:w-4',
191
+ large: 'paragraph px-3 py-2 [&_svg]:h-5 [&_svg]:w-5'
192
+ }
193
+ },
194
+ defaultVariants: {
195
+ size: 'medium'
196
+ }
197
+ });
198
+ function Badge(props, ref) {
199
+ const { className: _className, color, size, ...restProps } = props;
200
+ const className = badgeVariants({
201
+ className: _className,
202
+ color,
203
+ size
204
+ });
205
+ return /*#__PURE__*/ jsx("span", {
206
+ className: className,
207
+ ...restProps,
208
+ ref: ref,
209
+ "data-slot": "badge"
210
+ });
211
+ }
212
+ const _Badge = /*#__PURE__*/ forwardRef(Badge);
213
+
214
+ /**
215
+ * Figma: https://www.figma.com/file/9OvSg0ZXI5E1eQYi7AWiWn/Grunnmuren-2.0-%E2%94%82-Designsystem?node-id=30%3A2574&mode=dev
216
+ */ const buttonVariants = cva({
217
+ base: [
218
+ 'inline-flex min-h-[44px] cursor-pointer items-center justify-center whitespace-nowrap rounded-lg font-medium transition-colors duration-200 focus-visible:outline-focus-offset [&:not([data-focus-visible])]:outline-none'
219
+ ],
220
+ variants: {
221
+ /**
222
+ * The variant of the button
223
+ * @default primary
224
+ */ variant: {
225
+ primary: 'no-underline',
226
+ // by using an inset box-shadow to emulate a border instead of an actual border, the button size will be equal regardless of the variant
227
+ secondary: 'no-underline shadow-[inset_0_0_0_2px]',
228
+ tertiary: 'underline hover:no-underline'
229
+ },
230
+ /**
231
+ * Adjusts the color of the button for usage on different backgrounds.
232
+ * @default green
233
+ */ color: {
234
+ green: 'data-[focus-visible]:outline-focus',
235
+ mint: 'data-[focus-visible]:outline-focus data-[focus-visible]:outline-mint',
236
+ white: 'data-[focus-visible]:outline-focus data-[focus-visible]:outline-white'
237
+ },
238
+ /**
239
+ * When the button is without text, but with a single icon.
240
+ * @default false
241
+ */ isIconOnly: {
242
+ true: 'p-2 [&>svg]:h-7 [&>svg]:w-7',
243
+ false: 'gap-2.5 px-4 py-2'
244
+ },
245
+ // Make the content of the button transparent to hide it's content, but keep the button width
246
+ isPending: {
247
+ true: '!text-transparent relative',
248
+ false: null
249
+ }
250
+ },
251
+ compoundVariants: [
252
+ {
253
+ color: 'green',
254
+ variant: 'primary',
255
+ // Darken bg by 20% on hover. The color is manually crafted
256
+ className: 'bg-green text-white hover:bg-green-dark active:bg-[#007352] [&_[role="progressbar"]]:text-white'
257
+ },
258
+ {
259
+ color: 'green',
260
+ variant: 'secondary',
261
+ className: 'text-black shadow-green hover:bg-green hover:text-white active:bg-green [&:hover_[role="progressbar"]]:text-white [&_[role="progressbar"]]:text-black'
262
+ },
263
+ {
264
+ color: 'green',
265
+ variant: 'tertiary',
266
+ className: '[&_[role="progressbar"]]:text-black'
267
+ },
268
+ {
269
+ color: 'mint',
270
+ variant: 'primary',
271
+ // Darken bg by 20% on hover. The color is manually crafted
272
+ className: 'bg-mint text-black hover:bg-[#8dd4bd] active:[#9ddac6] [&_[role="progressbar"]]:text-black'
273
+ },
274
+ {
275
+ color: 'mint',
276
+ variant: 'secondary',
277
+ className: 'text-mint shadow-mint hover:bg-mint hover:text-black [&:hover_[role="progressbar"]]:text-black [&_[role="progressbar"]]:text-mint'
278
+ },
279
+ {
280
+ color: 'mint',
281
+ variant: 'tertiary',
282
+ className: 'text-mint [&_[role="progressbar"]]:text-mint'
283
+ },
284
+ {
285
+ color: 'white',
286
+ variant: 'primary',
287
+ className: 'bg-white text-black hover:bg-sky active:bg-sky-light [&_[role="progressbar"]]:text-black'
288
+ },
289
+ {
290
+ color: 'white',
291
+ variant: 'secondary',
292
+ className: 'text-white shadow-white hover:bg-white hover:text-black [&:hover_[role="progressbar"]]:text-black [&_[role="progressbar"]]:text-white'
293
+ },
294
+ {
295
+ color: 'white',
296
+ variant: 'tertiary',
297
+ className: 'text-white [&_[role="progressbar"]]:text-white'
298
+ }
299
+ ],
300
+ defaultVariants: {
301
+ variant: 'primary',
302
+ color: 'green',
303
+ isIconOnly: false,
304
+ isPending: false
305
+ }
306
+ });
307
+ function isLinkProps$1(props) {
308
+ return !!props.href;
309
+ }
310
+ const translations$1 = {
311
+ pending: {
312
+ nb: 'venter',
313
+ sv: 'väntar',
314
+ en: 'pending'
315
+ }
316
+ };
317
+ function Button(props, ref) {
318
+ const { children: _children, color, isIconOnly, isLoading, variant, isPending: _isPending, ...restProps } = props;
319
+ const isPending = _isPending || isLoading;
320
+ const className = buttonVariants({
321
+ className: props.className,
322
+ color,
323
+ isIconOnly,
324
+ variant,
325
+ isPending
326
+ });
327
+ const locale = _useLocale();
328
+ const { progressBarProps } = useProgressBar({
329
+ isIndeterminate: true,
330
+ 'aria-label': translations$1.pending[locale]
331
+ });
332
+ const children = isPending ? /*#__PURE__*/ jsxs(Fragment, {
333
+ children: [
334
+ _children,
335
+ /*#__PURE__*/ jsx(LoadingSpinner, {
336
+ className: "absolute m-auto motion-safe:animate-spin",
337
+ ...progressBarProps
338
+ })
339
+ ]
340
+ }) : _children;
341
+ return isLinkProps$1(restProps) ? /*#__PURE__*/ jsx(Link, {
342
+ ...restProps,
343
+ className: className,
344
+ ref: ref,
345
+ children: children
346
+ }) : /*#__PURE__*/ jsx(Button$1, {
347
+ ...restProps,
348
+ className: className,
349
+ isPending: isPending,
350
+ ref: ref,
351
+ children: children
352
+ });
353
+ }
354
+ const _Button = /*#__PURE__*/ forwardRef(Button);
8
355
 
9
356
  const formField = cx('group flex flex-col gap-2');
10
- const formFieldError = cx('w-fit rounded-sm bg-red-light px-2 py-1 text-sm leading-6 text-red');
357
+ const formFieldError = cx('w-fit rounded-sm bg-red-light px-2 py-1 text-red text-sm leading-6');
11
358
  const input = cva({
12
359
  base: [
13
- 'rounded-md px-3 py-2.5 text-sm font-normal leading-6 placeholder-[#727070] outline-none ring-1 ring-black',
360
+ // All inputs should always have a white background (this also ensures that type="search" on Safri doesn't get a gray background)
361
+ 'bg-white',
362
+ // Use box-content to enable auto width based on number of characters (size)
363
+ // Setting min-height to prevent the input from collapsing in Safari
364
+ // Combining these with a padding-y as base classes makes it easier to standardize the height (44px) of all inputs
365
+ 'box-content min-h-6 py-2.5',
366
+ 'rounded-md font-normal text-base leading-6 placeholder-[#727070] outline-none ring-1 ring-black',
14
367
  // invalid styles
15
- 'group-data-[invalid]:ring-2 group-data-[invalid]:ring-red'
368
+ 'group-data-[invalid]:ring-focus group-data-[invalid]:ring-red',
369
+ // Fix invisible ring on safari: https://github.com/tailwindlabs/tailwindcss.com/issues/1135
370
+ 'appearance-none'
16
371
  ],
17
372
  variants: {
18
373
  // Focus rings. Can either be :focus or :focus-visible based on the needs of the particular component.
19
374
  focusModifier: {
20
- focus: 'focus:ring-2 group-data-[invalid]:focus:ring',
21
- visible: 'data-[focus-visible]:ring-2 group-data-[invalid]:data-[focus-visible]:ring'
375
+ focus: 'focus:ring-focus group-data-[invalid]:focus:ring',
376
+ visible: 'data-[focus-visible]:ring-focus group-data-[invalid]:data-[focus-visible]:ring'
22
377
  },
23
378
  isGrouped: {
24
- false: '',
25
- //
26
- true: 'flex-1 !ring-0 first:pl-0 last:pr-0'
379
+ false: 'px-3',
380
+ true: '!ring-0 flex-1'
27
381
  }
28
382
  },
29
383
  defaultVariants: {
@@ -31,10 +385,14 @@ const input = cva({
31
385
  isGrouped: false
32
386
  }
33
387
  });
34
- const inputGroup = cx('inline-flex items-center overflow-hidden rounded-md px-3 ring-1 ring-black focus-within:ring-2 group-data-[invalid]:ring-2 group-data-[invalid]:ring-red group-data-[invalid]:focus-within:ring');
388
+ const inputGroup = cx([
389
+ 'inline-flex items-center gap-3 overflow-hidden rounded-md bg-white px-3 text-base ring-1 ring-black focus-within:ring-focus',
390
+ 'group-data-[invalid]:ring-focus group-data-[invalid]:ring-red group-data-[invalid]:focus-within:ring'
391
+ ]);
35
392
  const dropdown = {
36
- popover: cx('min-w-[--trigger-width] overflow-auto rounded-md border border-black bg-white shadow data-[entering]:animate-in data-[exiting]:animate-out data-[entering]:fade-in data-[exiting]:fade-out'),
37
- listbox: cx('text-sm outline-none'),
393
+ popover: cx('data-[entering]:fade-in data-[exiting]:fade-out min-w-[--trigger-width] overflow-y-auto rounded-md border border-black bg-white shadow data-[entering]:animate-in data-[exiting]:animate-out'),
394
+ // overflow-x-hidden is needed to prevent visible vertical scrollbars from overflowing the border radius of the popover
395
+ listbox: cx('max-h-[25rem] overflow-x-hidden text-sm outline-none'),
38
396
  chevronIcon: cx('text-base transition-transform duration-150 group-data-[open]:rotate-180 motion-reduce:transition-none')
39
397
  };
40
398
 
@@ -48,22 +406,13 @@ function ErrorMessage(props) {
48
406
  });
49
407
  }
50
408
 
51
- function Description(props) {
52
- const { className, ...restProps } = props;
53
- return /*#__PURE__*/ jsx(Text, {
54
- ...restProps,
55
- className: cx(className, 'text-sm font-light leading-6'),
56
- slot: "description"
57
- });
58
- }
59
-
60
409
  const defaultClasses$1 = cx([
61
- 'group relative left-0 inline-flex max-w-fit cursor-pointer items-start gap-4 py-2 leading-7'
410
+ 'group -mx-2.5 relative left-0 inline-flex max-w-fit cursor-pointer items-start gap-4 p-2.5 leading-7'
62
411
  ]);
63
412
  // Pulling this out into it's own component. Will probably export it in the future
64
413
  // so it can be used in other views, outside of an input of type checkbox, like in table rows.
65
414
  function CheckmarkBox() {
66
- return /*#__PURE__*/ jsx("div", {
415
+ return /*#__PURE__*/ jsx("span", {
67
416
  className: cx([
68
417
  'relative left-0 grid flex-none place-content-center rounded-sm border-2 border-black text-white',
69
418
  // to vertically align the radio we need to calculate the label's height, which is equal to it's font size multiplied by the line height.
@@ -74,12 +423,12 @@ function CheckmarkBox() {
74
423
  // selected
75
424
  'group-data-[selected]:!border-green group-data-[selected]:!bg-green',
76
425
  // focus
77
- 'group-data-[focus-visible]:ring-2 group-data-[focus-visible]:ring-black group-data-[focus-visible]:ring-offset-[9px]',
426
+ 'group-data-[focus-visible]:outline-focus-offset',
78
427
  // hovered
79
- 'group-data-[hovered]:border-green group-data-[hovered]:group-data-[invalid]:border-red group-data-[hovered]:bg-green-lightest group-data-[hovered]:group-data-[invalid]:bg-red-light',
428
+ 'group-data-[hovered]:group-data-[invalid]:border-red group-data-[hovered]:group-data-[invalid]:bg-red-light group-data-[hovered]:border-green group-data-[hovered]:bg-green-lightest',
80
429
  // invalid - The border is 1 px thicker when invalid. We don't actually want to change the border width, as that causes the element's size to change
81
430
  // so we use an inner shadow of 1 px instead to pad the actual border
82
- 'group-data-[invalid]:border-red group-data-[invalid]:group-data-[selected]:shadow-none group-data-[invalid]:shadow-[inset_0_0_0_1px] group-data-[invalid]:shadow-red'
431
+ 'group-data-[invalid]:group-data-[selected]:shadow-none group-data-[invalid]:border-red group-data-[invalid]:shadow-[inset_0_0_0_1px] group-data-[invalid]:shadow-red'
83
432
  ]),
84
433
  children: /*#__PURE__*/ jsx(Check, {
85
434
  className: "h-full w-full opacity-0 group-data-[selected]:opacity-100"
@@ -89,9 +438,9 @@ function CheckmarkBox() {
89
438
  function Checkbox(props, ref) {
90
439
  const { children, className, description, errorMessage, isInvalid: _isInvalid, ...restProps } = props;
91
440
  const id = useId();
92
- const descriptionId = 'desc' + id;
93
- const errorMessageId = 'error' + id;
94
- const isInvalid = _isInvalid || errorMessage != null;
441
+ const descriptionId = `desc${id}`;
442
+ const errorMessageId = `error${id}`;
443
+ const isInvalid = errorMessage != null || _isInvalid;
95
444
  return /*#__PURE__*/ jsx("div", {
96
445
  children: /*#__PURE__*/ jsxs(CheckboxContext.Provider, {
97
446
  value: {
@@ -105,16 +454,15 @@ function Checkbox(props, ref) {
105
454
  isInvalid: isInvalid,
106
455
  ref: ref,
107
456
  children: [
108
- /*#__PURE__*/ jsx("div", {
109
- className: "absolute -left-2.5 top-0 z-10 h-11 w-11"
110
- }),
111
457
  /*#__PURE__*/ jsx(CheckmarkBox, {}),
112
458
  children
113
459
  ]
114
460
  }),
115
- description && /*#__PURE__*/ jsx(Description, {
116
- className: "block",
461
+ description && // {/* Use a div instead of the Description component to avoid infinite re-render loops in React until this bug in RAC is fixed: https://github.com/adobe/react-spectrum/issues/6229 */}
462
+ /*#__PURE__*/ jsx("div", {
117
463
  id: descriptionId,
464
+ slot: "description",
465
+ className: "description block",
118
466
  children: description
119
467
  }),
120
468
  errorMessage && /*#__PURE__*/ jsx(ErrorMessage, {
@@ -128,6 +476,26 @@ function Checkbox(props, ref) {
128
476
  }
129
477
  const _Checkbox = /*#__PURE__*/ forwardRef(Checkbox);
130
478
 
479
+ function Description(props) {
480
+ const { className, ...restProps } = props;
481
+ return /*#__PURE__*/ jsx(Text, {
482
+ ...restProps,
483
+ className: cx(className, 'description'),
484
+ slot: "description"
485
+ });
486
+ }
487
+
488
+ /**
489
+ * This component handles renders a custom error message (if provided), otherwise it falls back to the browser's native validation.
490
+ * In other words, this handles controlled and uncontrolled form errors.
491
+ */ function ErrorMessageOrFieldError({ errorMessage }) {
492
+ return errorMessage ? /*#__PURE__*/ jsx(ErrorMessage, {
493
+ children: errorMessage
494
+ }) : /*#__PURE__*/ jsx(FieldError, {
495
+ className: formFieldError
496
+ });
497
+ }
498
+
131
499
  function Label(props) {
132
500
  const { children, className, ...restProps } = props;
133
501
  return /*#__PURE__*/ jsx(Label$1, {
@@ -139,7 +507,9 @@ function Label(props) {
139
507
 
140
508
  function CheckboxGroup(props, ref) {
141
509
  const { children, className, description, errorMessage, label, isRequired, isInvalid: _isInvalid, ...restProps } = props;
142
- const isInvalid = _isInvalid || errorMessage != null;
510
+ // the order of the conditions matter here, because providing a value for isInvalid makes the validation state "controlled",
511
+ // which will override any built in validation
512
+ const isInvalid = errorMessage != null || _isInvalid;
143
513
  return /*#__PURE__*/ jsxs(CheckboxGroup$1, {
144
514
  ...restProps,
145
515
  className: cx(className, 'flex flex-col gap-2'),
@@ -154,28 +524,67 @@ function CheckboxGroup(props, ref) {
154
524
  children: description
155
525
  }),
156
526
  children,
157
- errorMessage && /*#__PURE__*/ jsx(ErrorMessage, {
158
- children: errorMessage
527
+ /*#__PURE__*/ jsx(ErrorMessageOrFieldError, {
528
+ errorMessage: errorMessage
159
529
  })
160
530
  ]
161
531
  });
162
532
  }
163
533
  const _CheckboxGroup = /*#__PURE__*/ forwardRef(CheckboxGroup);
164
534
 
535
+ const ListBox = ({ className, ...restProps })=>/*#__PURE__*/ jsx(ListBox$1, {
536
+ ...restProps,
537
+ className: cx(dropdown.listbox, className)
538
+ });
539
+ const ListBoxItem = (props)=>{
540
+ let textValue = props.textValue;
541
+ // When the ListBoxItem child isn't a string we have to set textValue for keyboard completion to work.
542
+ // Since we use a render function (to handle the selected state) the child is never a string.
543
+ // This condition adds back that behaviour
544
+ if (textValue == null && typeof props.children === 'string') {
545
+ textValue = props.children;
546
+ }
547
+ return /*#__PURE__*/ jsx(ListBoxItem$1, {
548
+ ...props,
549
+ className: cx(props.className, 'flex cursor-pointer px-6 py-3 leading-6 outline-none data-[focused]:bg-sky-lightest'),
550
+ textValue: textValue,
551
+ children: ({ isSelected })=>/*#__PURE__*/ jsxs(Fragment, {
552
+ children: [
553
+ isSelected && /*#__PURE__*/ jsx(Check, {
554
+ className: "-ml-6 text-base"
555
+ }),
556
+ props.children
557
+ ]
558
+ })
559
+ });
560
+ };
165
561
  /**
166
- * This component handles renders a custom error message (if provided), otherwise it falls back to the browser's native validation.
167
- * In other words, this handles controlled and uncontrolled form errors.
168
- */ function ErrorMessageOrFieldError({ errorMessage }) {
169
- return errorMessage ? /*#__PURE__*/ jsx(ErrorMessage, {
170
- children: errorMessage
171
- }) : /*#__PURE__*/ jsx(FieldError, {
172
- className: formFieldError
562
+ * This component can be used to group items in a listbox
563
+ */ const ListBoxSection = ({ className, ...restProps })=>/*#__PURE__*/ jsx(ListBoxSection$1, {
564
+ ...restProps,
565
+ // The :not(:first-child) selector adds extra spacing to all the options, but not the section (group) headings
566
+ // This way we get the desired extra indent on all options within a group
567
+ className: cx(className, 'pb-1 [&>:not(:first-child)]:pl-10')
568
+ });
569
+ /**
570
+ * This component can be used to label grouped items in a `ListBoxSection` with a heading
571
+ */ const ListBoxHeader = (props)=>/*#__PURE__*/ jsx(Header, {
572
+ ...props,
573
+ className: cx(props.className, 'mx-6 cursor-default py-2 font-medium text-blue-dark leading-6')
574
+ });
575
+
576
+ function InputAddonDivider() {
577
+ return /*#__PURE__*/ jsx("span", {
578
+ className: "block h-6 w-px flex-none bg-black"
173
579
  });
174
580
  }
175
581
 
176
582
  function Combobox(props, ref) {
177
- const { className, children, description, errorMessage, isLoading, label, isInvalid: _isInvalid, ...restProps } = props;
178
- const isInvalid = _isInvalid || errorMessage != null;
583
+ const { className, children, description, errorMessage, isLoading, isPending: _isPending, label, isInvalid: _isInvalid, ...restProps } = props;
584
+ const isPending = _isPending || isLoading;
585
+ // the order of the conditions matter here, because providing a value for isInvalid makes the validation state "controlled",
586
+ // which will override any built in validation
587
+ const isInvalid = errorMessage != null || _isInvalid;
179
588
  return /*#__PURE__*/ jsxs(ComboBox, {
180
589
  ...restProps,
181
590
  className: cx(className, formField),
@@ -196,8 +605,8 @@ function Combobox(props, ref) {
196
605
  }),
197
606
  ref: ref
198
607
  }),
199
- /*#__PURE__*/ jsx(Button, {
200
- children: isLoading ? /*#__PURE__*/ jsx(LoadingSpinner, {
608
+ /*#__PURE__*/ jsx(Button$1, {
609
+ children: isPending ? /*#__PURE__*/ jsx(LoadingSpinner, {
201
610
  className: "animate-spin"
202
611
  }) : /*#__PURE__*/ jsx(ChevronDown, {
203
612
  className: dropdown.chevronIcon
@@ -223,33 +632,13 @@ function Combobox(props, ref) {
223
632
  ]
224
633
  });
225
634
  }
226
- const ComboboxItem = (props)=>{
227
- let textValue = props.textValue;
228
- // When the ListBoxItem child isn't a string we have to set textValue for keyboard completion to work.
229
- // Since we use a render function (to handle the selected state) the child is never a string.
230
- // This condition adds back that behaviour
231
- if (textValue == null && typeof props.children === 'string') {
232
- textValue = props.children;
233
- }
234
- return /*#__PURE__*/ jsx(ListBoxItem, {
235
- ...props,
236
- className: cx(props.className, 'flex cursor-default px-6 py-2 leading-6 outline-none data-[focused]:bg-sky-lightest'),
237
- textValue: textValue,
238
- children: ({ isSelected })=>/*#__PURE__*/ jsxs(Fragment, {
239
- children: [
240
- isSelected && /*#__PURE__*/ jsx(Check, {
241
- className: "-ml-6 text-base"
242
- }),
243
- props.children
244
- ]
245
- })
246
- });
247
- };
248
635
  const _Combobox = /*#__PURE__*/ forwardRef(Combobox);
249
636
 
250
637
  function RadioGroup(props, ref) {
251
638
  const { children, className, description, errorMessage, label, isRequired, isInvalid: _isInvalid, ...restProps } = props;
252
- const isInvalid = _isInvalid || errorMessage != null;
639
+ // the order of the conditions matter here, because providing a value for isInvalid makes the validation state "controlled",
640
+ // which will override any built in validation
641
+ const isInvalid = errorMessage != null || _isInvalid;
253
642
  return /*#__PURE__*/ jsxs(RadioGroup$1, {
254
643
  ...restProps,
255
644
  className: cx(className, 'flex flex-col gap-2'),
@@ -264,8 +653,8 @@ function RadioGroup(props, ref) {
264
653
  children: description
265
654
  }),
266
655
  children,
267
- errorMessage && /*#__PURE__*/ jsx(ErrorMessage, {
268
- children: errorMessage
656
+ /*#__PURE__*/ jsx(ErrorMessageOrFieldError, {
657
+ errorMessage: errorMessage
269
658
  })
270
659
  ]
271
660
  });
@@ -273,7 +662,7 @@ function RadioGroup(props, ref) {
273
662
  const _RadioGroup = /*#__PURE__*/ forwardRef(RadioGroup);
274
663
 
275
664
  const defaultClasses = cx([
276
- 'relative inline-flex max-w-fit cursor-pointer items-start gap-4 py-2 leading-7',
665
+ '-ml-2.5 relative inline-flex max-w-fit cursor-pointer items-start gap-4 py-2.5 pl-2.5 leading-7',
277
666
  // the radio button itself
278
667
  'before:flex-none before:rounded-full before:border-2 before:border-black',
279
668
  // to vertically align the radio we need to calculate the label's height, which is equal to it's font size multiplied by the line height.
@@ -284,40 +673,37 @@ const defaultClasses = cx([
284
673
  // selected
285
674
  'data-[selected]:before:border-black data-[selected]:before:bg-green data-[selected]:before:shadow-[inset_0_0_0_4px_rgb(255,255,255)]',
286
675
  // hover
287
- 'data-[hovered]:before:border-green data-[hovered]:before:bg-green-lightest data-[hovered]:data-[invalid]:before:bg-red-light',
676
+ 'data-[hovered]:data-[invalid]:before:bg-red-light data-[hovered]:before:border-green data-[hovered]:before:bg-green-lightest',
288
677
  // focus
289
- 'data-[focus-visible]:before:ring data-[focus-visible]:before:ring-black data-[focus-visible]:before:ring-offset-[9px]',
678
+ 'data-[focus-visible]:before:ring-focus-offset',
290
679
  // invalid - The border is 1 px thicker when invalid. We don't actually want to change the border width, as that causes the element's size to change
291
680
  // so we use an inner outline to artifically pad the border
292
- 'data-[invalid]:before:outline-solid data-[invalid]:before:border-red data-[invalid]:data-[selected]:before:!bg-red data-[invalid]:before:outline data-[invalid]:before:outline-[3px] data-[invalid]:before:outline-offset-[-3px] data-[invalid]:before:outline-red'
681
+ 'data-[invalid]:data-[selected]:before:!bg-red data-[invalid]:before:border-red data-[invalid]:before:outline data-[invalid]:before:outline-[3px] data-[invalid]:before:outline-red data-[invalid]:before:outline-solid data-[invalid]:before:outline-offset-[-3px]'
293
682
  ]);
294
683
  function Radio(props, ref) {
295
684
  const { children, className, description, ...restProps } = props;
296
- return /*#__PURE__*/ jsxs(Radio$1, {
685
+ return /*#__PURE__*/ jsx(Radio$1, {
297
686
  ...restProps,
298
687
  className: cx(className, defaultClasses),
299
688
  ref: ref,
300
- children: [
301
- /*#__PURE__*/ jsx("div", {
302
- className: "absolute -left-2.5 top-0 z-10 h-11 w-11 "
303
- }),
304
- /*#__PURE__*/ jsxs("div", {
305
- children: [
306
- children,
307
- description && /*#__PURE__*/ jsx(Description, {
308
- className: "mt-2 block",
309
- children: description
310
- })
311
- ]
312
- })
313
- ]
689
+ children: /*#__PURE__*/ jsxs("div", {
690
+ children: [
691
+ children,
692
+ description && /*#__PURE__*/ jsx(Description, {
693
+ className: "mt-2 block",
694
+ children: description
695
+ })
696
+ ]
697
+ })
314
698
  });
315
699
  }
316
700
  const _Radio = /*#__PURE__*/ forwardRef(Radio);
317
701
 
318
702
  function Select(props, ref) {
319
703
  const { className, children, description, errorMessage, label, isInvalid: _isInvalid, ...restProps } = props;
320
- const isInvalid = _isInvalid || errorMessage != null;
704
+ // the order of the conditions matter here, because providing a value for isInvalid makes the validation state "controlled",
705
+ // which will override any built in validation
706
+ const isInvalid = errorMessage != null || _isInvalid;
321
707
  return /*#__PURE__*/ jsxs(Select$1, {
322
708
  ...restProps,
323
709
  className: cx(className, formField),
@@ -329,7 +715,7 @@ function Select(props, ref) {
329
715
  description && /*#__PURE__*/ jsx(Description, {
330
716
  children: description
331
717
  }),
332
- /*#__PURE__*/ jsxs(Button, {
718
+ /*#__PURE__*/ jsxs(Button$1, {
333
719
  className: cx(input({
334
720
  focusModifier: 'visible'
335
721
  }), // How to reuse placeholder text?
@@ -358,33 +744,11 @@ function Select(props, ref) {
358
744
  ]
359
745
  });
360
746
  }
361
- const SelectItem = (props)=>{
362
- let textValue = props.textValue;
363
- // When the ListBoxItem child isn't a string we have to set textValue for keyboard completion to work.
364
- // Since we use a render function (to handle the selected state) the child is never a string.
365
- // This condition adds back that behaviour
366
- if (textValue == null && typeof props.children === 'string') {
367
- textValue = props.children;
368
- }
369
- return /*#__PURE__*/ jsx(ListBoxItem, {
370
- ...props,
371
- className: cx(props.className, 'flex cursor-default px-6 py-2 leading-6 outline-none data-[focused]:bg-sky-lightest'),
372
- textValue: textValue,
373
- children: ({ isSelected })=>/*#__PURE__*/ jsxs(Fragment, {
374
- children: [
375
- isSelected && /*#__PURE__*/ jsx(Check, {
376
- className: "-ml-6 text-base"
377
- }),
378
- props.children
379
- ]
380
- })
381
- });
382
- };
383
747
  const _Select = /*#__PURE__*/ forwardRef(Select);
384
748
 
385
749
  function TextArea(props, ref) {
386
750
  const { className, description, errorMessage, label, isInvalid: _isInvalid, rows, ...restProps } = props;
387
- const isInvalid = _isInvalid || errorMessage != null;
751
+ const isInvalid = errorMessage != null || _isInvalid;
388
752
  return /*#__PURE__*/ jsxs(TextField$1, {
389
753
  ...restProps,
390
754
  className: cx(className, formField),
@@ -409,18 +773,24 @@ function TextArea(props, ref) {
409
773
  }
410
774
  const _TextArea = /*#__PURE__*/ forwardRef(TextArea);
411
775
 
412
- const inputWithAlignment = compose(input, cva({
776
+ const inputVariants$1 = compose(input, cva({
413
777
  base: '',
414
778
  variants: {
415
779
  textAlign: {
416
780
  right: 'text-right',
417
781
  left: ''
782
+ },
783
+ autoWidth: {
784
+ true: 'max-w-fit',
785
+ false: ''
418
786
  }
419
787
  }
420
788
  }));
421
789
  function TextField(props, ref) {
422
- const { className, description, errorMessage, label, leftAddon, isInvalid: _isInvalid, textAlign, rightAddon, withAddonDivider, ...restProps } = props;
423
- const isInvalid = _isInvalid || errorMessage != null;
790
+ const { className, description, errorMessage, label, leftAddon, isInvalid: _isInvalid, textAlign, rightAddon, withAddonDivider, size, ...restProps } = props;
791
+ // the order of the conditions matter here, because providing a value for isInvalid makes the validation state "controlled",
792
+ // which will override any built in validation
793
+ const isInvalid = errorMessage != null || _isInvalid;
424
794
  return /*#__PURE__*/ jsxs(TextField$1, {
425
795
  ...restProps,
426
796
  className: cx(className, formField),
@@ -433,29 +803,96 @@ function TextField(props, ref) {
433
803
  children: description
434
804
  }),
435
805
  leftAddon || rightAddon ? /*#__PURE__*/ jsxs(Group, {
436
- className: inputGroup,
806
+ className: cx(inputGroup, {
807
+ 'w-fit': !!size
808
+ }),
437
809
  children: [
438
810
  leftAddon,
439
- withAddonDivider && leftAddon && /*#__PURE__*/ jsx(Divider, {
440
- className: "ml-3"
441
- }),
811
+ withAddonDivider && leftAddon && /*#__PURE__*/ jsx(InputAddonDivider, {}),
442
812
  /*#__PURE__*/ jsx(Input, {
443
- className: inputWithAlignment({
813
+ className: inputVariants$1({
444
814
  textAlign,
445
- isGrouped: true
815
+ isGrouped: true,
816
+ autoWidth: !!size
446
817
  }),
447
- ref: ref
818
+ ref: ref,
819
+ size: size
448
820
  }),
449
- withAddonDivider && rightAddon && /*#__PURE__*/ jsx(Divider, {
450
- className: "mr-3"
821
+ withAddonDivider && rightAddon && /*#__PURE__*/ jsx(InputAddonDivider, {}),
822
+ rightAddon
823
+ ]
824
+ }) : /*#__PURE__*/ jsx(Input, {
825
+ className: inputVariants$1({
826
+ textAlign,
827
+ autoWidth: !!size
828
+ }),
829
+ ref: ref,
830
+ size: size
831
+ }),
832
+ /*#__PURE__*/ jsx(ErrorMessageOrFieldError, {
833
+ errorMessage: errorMessage
834
+ })
835
+ ]
836
+ });
837
+ }
838
+ const _TextField = /*#__PURE__*/ forwardRef(TextField);
839
+
840
+ // This component is based on a copy of ../textfield/TextField, refactoring is TBD: https://github.com/code-obos/grunnmuren/pull/722#issuecomment-1931478786
841
+ const inputVariants = compose(input, cva({
842
+ base: '',
843
+ variants: {
844
+ textAlign: {
845
+ right: 'text-right',
846
+ left: ''
847
+ },
848
+ autoWidth: {
849
+ true: 'max-w-fit',
850
+ false: ''
851
+ }
852
+ }
853
+ }));
854
+ function NumberField(props, ref) {
855
+ const { className, description, errorMessage, label, leftAddon, isInvalid: _isInvalid, textAlign, rightAddon, withAddonDivider, size, ...restProps } = props;
856
+ // the order of the conditions matter here, because providing a value for isInvalid makes the validation state "controlled",
857
+ // which will override any built in validation
858
+ const isInvalid = errorMessage != null || _isInvalid;
859
+ return /*#__PURE__*/ jsxs(NumberField$1, {
860
+ ...restProps,
861
+ className: cx(className, formField),
862
+ isInvalid: isInvalid,
863
+ children: [
864
+ label && /*#__PURE__*/ jsx(Label, {
865
+ children: label
866
+ }),
867
+ description && /*#__PURE__*/ jsx(Description, {
868
+ children: description
869
+ }),
870
+ leftAddon || rightAddon ? /*#__PURE__*/ jsxs(Group, {
871
+ className: cx(inputGroup, {
872
+ 'w-fit': !!size
873
+ }),
874
+ children: [
875
+ leftAddon,
876
+ withAddonDivider && leftAddon && /*#__PURE__*/ jsx(InputAddonDivider, {}),
877
+ /*#__PURE__*/ jsx(Input, {
878
+ className: inputVariants({
879
+ textAlign,
880
+ isGrouped: true,
881
+ autoWidth: !!size
882
+ }),
883
+ ref: ref,
884
+ size: size
451
885
  }),
886
+ withAddonDivider && rightAddon && /*#__PURE__*/ jsx(InputAddonDivider, {}),
452
887
  rightAddon
453
888
  ]
454
889
  }) : /*#__PURE__*/ jsx(Input, {
455
- className: inputWithAlignment({
456
- textAlign
890
+ className: inputVariants({
891
+ textAlign,
892
+ autoWidth: !!size
457
893
  }),
458
- ref: ref
894
+ ref: ref,
895
+ size: size
459
896
  }),
460
897
  /*#__PURE__*/ jsx(ErrorMessageOrFieldError, {
461
898
  errorMessage: errorMessage
@@ -463,11 +900,594 @@ function TextField(props, ref) {
463
900
  ]
464
901
  });
465
902
  }
466
- function Divider({ className }) {
467
- return /*#__PURE__*/ jsx("span", {
468
- className: cx(className, 'block h-6 w-px flex-none bg-black')
903
+ const _NumberField = /*#__PURE__*/ forwardRef(NumberField);
904
+
905
+ const iconMap = {
906
+ info: InfoCircle,
907
+ success: CheckCircle,
908
+ warning: Warning,
909
+ danger: Error
910
+ };
911
+ const alertVariants = cva({
912
+ base: [
913
+ 'grid grid-cols-[auto_1fr_auto] items-center gap-2 rounded-md border-2 px-3 py-2',
914
+ // Heading styles:
915
+ '[&_[data-slot="heading"]]:font-medium [&_[data-slot="heading"]]:text-base [&_[data-slot="heading"]]:leading-7',
916
+ // Content styles:
917
+ '[&:has([data-slot="heading"])_[data-slot="content"]]:col-span-full [&_[data-slot="content"]]:text-sm [&_[data-slot="content"]]:leading-6',
918
+ // Footer styles:
919
+ '[&_[data-slot="footer"]]:col-span-full [&_[data-slot="footer"]]:font-light [&_[data-slot="footer"]]:text-xs [&_[data-slot="footer"]]:leading-6'
920
+ ],
921
+ variants: {
922
+ /**
923
+ * The variant of the alert
924
+ * @default info
925
+ */ variant: {
926
+ info: 'border-[#1A7FA7] bg-sky-light',
927
+ success: 'border-[#0F9B6E] bg-mint-light',
928
+ warning: 'border-[#C57C13] bg-[#FFF2DE]',
929
+ danger: 'border-[#C0385D] bg-red-light'
930
+ }
931
+ },
932
+ defaultVariants: {
933
+ variant: 'info'
934
+ }
935
+ });
936
+ const translations = {
937
+ close: {
938
+ nb: 'Lukk',
939
+ sv: 'Stäng',
940
+ en: 'Close'
941
+ },
942
+ showMore: {
943
+ nb: 'Les mer',
944
+ sv: 'Läs mer',
945
+ en: 'Read more'
946
+ },
947
+ showLess: {
948
+ nb: 'Vis mindre',
949
+ sv: 'Dölj',
950
+ en: 'Show less'
951
+ }
952
+ };
953
+ const Alertbox = ({ children, role, className, icon, variant = 'info', isDismissable = false, isDismissed, onDismiss, isExpandable })=>{
954
+ const Icon = icon ?? iconMap[variant];
955
+ const locale = _useLocale();
956
+ const id = useId();
957
+ const [isExpanded, setIsExpanded] = useState(false);
958
+ const isCollapsed = isExpandable && !isExpanded;
959
+ const [isUncontrolledVisible, setIsUncontrolledVisible] = useState(true);
960
+ const isVisible = isDismissed !== undefined ? !isDismissed : isUncontrolledVisible;
961
+ if (!isVisible) return;
962
+ const close = ()=>{
963
+ setIsUncontrolledVisible(false);
964
+ if (onDismiss) onDismiss();
965
+ };
966
+ const isInDevMode = process.env.NODE_ENV !== 'production';
967
+ if (isInDevMode && onDismiss && !isDismissable) {
968
+ console.warn('Passing an `onDismiss` callback without setting the `isDismissable` prop to `true` will not have any effect.');
969
+ }
970
+ if (isInDevMode && !children) {
971
+ console.error('`No children was passed to the <AlertBox/>` component.');
972
+ return;
973
+ }
974
+ const [firstChild, ...restChildren] = Children.toArray(children);
975
+ return /*#__PURE__*/ jsxs("div", {
976
+ className: alertVariants({
977
+ className,
978
+ variant
979
+ }),
980
+ // The role prop is required to force consumers to consider and choose the appropriate alertbox role.
981
+ // role="none" will not have any effect on a div, so it can be omitted.
982
+ role: role === 'none' ? undefined : role,
983
+ children: [
984
+ /*#__PURE__*/ jsx(Icon, {}),
985
+ firstChild,
986
+ isDismissable && /*#__PURE__*/ jsx("button", {
987
+ className: cx('-m-2 grid h-11 w-11 place-items-center rounded-xl', 'focus-visible:-outline-offset-8 focus-visible:outline-focus'),
988
+ onClick: close,
989
+ "aria-label": translations.close[locale],
990
+ type: "button",
991
+ children: /*#__PURE__*/ jsx(Close, {})
992
+ }),
993
+ isExpandable && /*#__PURE__*/ jsxs("button", {
994
+ className: cx('-my-3 relative col-span-full row-start-2 inline-flex max-w-fit cursor-pointer items-center gap-1 py-3 text-sm leading-6', // Focus styles:
995
+ 'outline-none after:absolute after:right-0 after:bottom-3 after:left-0 after:h-0', 'focus-visible:after:h-[2px] focus-visible:after:bg-black'),
996
+ onClick: ()=>setIsExpanded((prevState)=>!prevState),
997
+ "aria-expanded": isExpanded,
998
+ "aria-controls": id,
999
+ type: "button",
1000
+ children: [
1001
+ isExpanded ? translations.showLess[locale] : translations.showMore[locale],
1002
+ /*#__PURE__*/ jsx(ChevronDown, {
1003
+ className: cx('transition-transform duration-150 motion-reduce:transition-none', isExpanded && 'rotate-180')
1004
+ })
1005
+ ]
1006
+ }),
1007
+ restChildren?.length > 0 && /*#__PURE__*/ jsx("div", {
1008
+ className: cx('col-span-full grid gap-y-4', isCollapsed && '[&>*:not([data-slot="footer"])]:hidden'),
1009
+ id: id,
1010
+ children: restChildren
1011
+ })
1012
+ ]
1013
+ });
1014
+ };
1015
+
1016
+ function Breadcrumbs(props, ref) {
1017
+ const { className, children, ...restProps } = props;
1018
+ return /*#__PURE__*/ jsx(Breadcrumbs$1, {
1019
+ ...restProps,
1020
+ className: cx(className, 'flex flex-wrap text-sm leading-6'),
1021
+ ref: ref,
1022
+ children: children
469
1023
  });
470
1024
  }
471
- const _TextField = /*#__PURE__*/ forwardRef(TextField);
1025
+ const _Breadcrumbs = /*#__PURE__*/ forwardRef(Breadcrumbs);
1026
+
1027
+ function Breadcrumb(props, ref) {
1028
+ const { className, children, href, ...restProps } = props;
1029
+ return /*#__PURE__*/ jsxs(Breadcrumb$1, {
1030
+ className: cx(className, 'group flex items-center'),
1031
+ ...restProps,
1032
+ ref: ref,
1033
+ children: [
1034
+ href ? /*#__PURE__*/ jsx(Link, {
1035
+ href: href,
1036
+ // use outline instead of ring for focus marker that can be offset without creating a white background between the focus marker and the element content
1037
+ className: "rounded-sm group-last:no-underline data-[focus-visible]:outline-focus [&:not([data-focus-visible])]:outline-none",
1038
+ children: children
1039
+ }) : children,
1040
+ /*#__PURE__*/ jsx(ChevronRight, {
1041
+ className: "px-1 group-last:hidden"
1042
+ })
1043
+ ]
1044
+ });
1045
+ }
1046
+ const _Breadcrumb = /*#__PURE__*/ forwardRef(Breadcrumb);
1047
+
1048
+ function isLinkProps(props) {
1049
+ return !!props.href;
1050
+ }
1051
+ function Backlink(props, ref) {
1052
+ const { className, children, withUnderline, ...restProps } = props;
1053
+ const Component = isLinkProps(props) ? Link : Button$1;
1054
+ return /*#__PURE__*/ jsxs(Component, {
1055
+ className: cx(className, 'group flex max-w-fit cursor-pointer items-center gap-3 rounded-md p-2.5 no-underline data-[focus-visible]:outline-focus [&:not([data-focus-visible])]:outline-none'),
1056
+ ...restProps,
1057
+ // @ts-expect-error ignore the type of the ref here
1058
+ ref: ref,
1059
+ children: [
1060
+ /*#__PURE__*/ jsx(ChevronLeft, {
1061
+ className: cx('-ml-[0.5em] group-hover:-translate-x-1 flex-shrink-0 transition-transform duration-300')
1062
+ }),
1063
+ /*#__PURE__*/ jsx("span", {
1064
+ children: /*#__PURE__*/ jsx("span", {
1065
+ className: cx('border-transparent border-t-[1px] border-b-[1px] transition-colors duration-300', withUnderline ? 'border-b-black' : 'group-hover:border-b-black'),
1066
+ children: children
1067
+ })
1068
+ })
1069
+ ]
1070
+ });
1071
+ }
1072
+ const _Backlink = /*#__PURE__*/ forwardRef(Backlink);
1073
+
1074
+ const cardVariants = cva({
1075
+ base: [
1076
+ 'group/card',
1077
+ 'rounded-2xl border p-3',
1078
+ 'flex gap-y-4',
1079
+ 'relative',
1080
+ // **** Heading ****
1081
+ '[&_[data-slot="heading"]]:inline',
1082
+ '[&_[data-slot="heading"]]:heading-s',
1083
+ '[&_[data-slot="heading"]]:leading-6',
1084
+ '[&_[data-slot="heading"]]:w-fit',
1085
+ '[&_[data-slot="heading"]]:text-pretty',
1086
+ '[&_[data-slot="heading"]]:hyphens-auto',
1087
+ '[&_[data-slot="heading"]]:[word-break:break-word]',
1088
+ // **** Content ****
1089
+ '[&_[data-slot="content"]]:flex [&_[data-slot="content"]]:flex-col [&_[data-slot="content"]]:gap-y-4',
1090
+ // **** Media ****
1091
+ '[&_[data-slot="media"]]:overflow-hidden',
1092
+ '[&_[data-slot="media"]]:relative',
1093
+ // Position media at the edges of the card (because of these negative margins the media-element must be a wrapper around the actual image or other media content)
1094
+ '[&_[data-slot="media"]]:mx-[calc(theme(space.3)*-1-theme(borderWidth.DEFAULT))] [&_[data-slot="media"]]:mt-[calc(theme(space.3)*-1-theme(borderWidth.DEFAULT))]',
1095
+ // Sets the aspect ratio of the media content (width: 100% is necessary to make aspect ratio work in FF)
1096
+ '[&_[data-slot="media"]>*:not([data-slot="badge"])]:aspect-[3/2] [&_[data-slot="media"]>*:not([data-slot="badge"])]:w-full [&_[data-slot="media"]_img]:object-cover',
1097
+ // Prepare zoom animation for hover effects. The hover effect can also be enabled by classes on the parent component, so it is always prepared here.
1098
+ '[&_[data-slot="media"]>*]:duration-300 [&_[data-slot="media"]>*]:ease-in-out [&_[data-slot="media"]>*]:motion-safe:transition-transform',
1099
+ // **** Card link ****
1100
+ // **** Hover ****
1101
+ // Enables the zoom hover effect on media (note that we can't use group-hover/card here, because there might be other clickable elements in the card aside from the heading)
1102
+ '[&:has([data-slot="card-link"]_a:hover)_[data-slot="media"]>*:not([data-slot="badge"])]:scale-110',
1103
+ // **** Card link in Heading ****
1104
+ '[&:has([data-slot="heading"]_[data-slot="card-link"]:hover)_[data-slot="media"]>*:not([data-slot="badge"])]:scale-110',
1105
+ // Border (bottom/top) is set to transparent to make sure the bottom underline is not visible when the card is hovered
1106
+ // Border top is set to even out the border bottom used for the underline
1107
+ '[&_[data-slot="heading"]_[data-slot="card-link"]]:no-underline',
1108
+ '[&_[data-slot="heading"]_[data-slot="card-link"]]:border-y-2',
1109
+ '[&_[data-slot="heading"]_[data-slot="card-link"]]:border-y-transparent',
1110
+ '[&_[data-slot="heading"]_[data-slot="card-link"]]:transition-colors',
1111
+ '[&_[data-slot="heading"]_[data-slot="card-link"]:hover]:border-b-current',
1112
+ // Mimic heading styles for the card link if placed in the heading slot. This is necessary to make the custom underline align with the link text
1113
+ '[&_[data-slot="heading"]_[data-slot="card-link"]]:heading-s',
1114
+ '[&_[data-slot="heading"]_[data-slot="card-link"]]:leading-6',
1115
+ '[&_[data-slot="heading"]_[data-slot="card-link"]]:text-pretty',
1116
+ '[&_[data-slot="heading"]_[data-slot="card-link"]]:hyphens-auto',
1117
+ '[&_[data-slot="heading"]_[data-slot="card-link"]]:[word-break:break-word]',
1118
+ // **** Fail-safe for interactive elements ****
1119
+ // Make interactive elements clickable by themselves, while the rest of the card is clickable as a whole
1120
+ // The card is made clickable by a pseudo-element on the heading that covers the entire card
1121
+ '[&:not(:has([data-slot="card-link"]_a))_a:not([data-slot="card-link"])]:relative [&_button]:relative [&_input]:relative',
1122
+ // Our Button component has position: relative by default, so we need to override that if it is used in a CardLink (to make the entire card clickable)
1123
+ '[&_[data-slot="card-link"]_a]:static',
1124
+ // Place other interactive on top of the pseudo-element that makes the entire card clickable
1125
+ // by setting a higher z-index than the pseudo-element (which implicitly z-index 0)
1126
+ '[&_a:not([data-slot="card-link"])]:z-[1] [&_button]:z-[1] [&_input]:z-[1]',
1127
+ // **** Badge ****
1128
+ '[&_[data-slot="media"]_[data-slot="badge"]]:absolute [&_[data-slot="media"]_[data-slot="badge"]]:top-0',
1129
+ // Increasing z-index Preserves badge position when media content is hovered (the transform scale effect might otherwise move the badge behind the other media content)
1130
+ '[&_[data-slot="media"]_[data-slot="badge"]]:z-[1]',
1131
+ // Left aligned - override default corner radius of the badge
1132
+ '[&_[data-slot="media"]_[data-slot="badge"]:first-child]:rounded-tl-2xl',
1133
+ '[&_[data-slot="media"]_[data-slot="badge"]:first-child]:rounded-br-2xl',
1134
+ '[&_[data-slot="media"]_[data-slot="badge"]:first-child]:rounded-tr-none',
1135
+ '[&_[data-slot="media"]_[data-slot="badge"]:first-child]:rounded-bl-none',
1136
+ // Right aligned - override default corner radius of the badge
1137
+ '[&_[data-slot="media"]_[data-slot="badge"]:last-child]:rounded-tl-none',
1138
+ '[&_[data-slot="media"]_[data-slot="badge"]:last-child]:rounded-br-none',
1139
+ '[&_[data-slot="media"]_[data-slot="badge"]:last-child]:rounded-tr-2xl',
1140
+ '[&_[data-slot="media"]_[data-slot="badge"]:last-child]:rounded-bl-2xl',
1141
+ // ... and position the badge at the right edge of the media content
1142
+ '[&_[data-slot="media"]_[data-slot="badge"]:last-child]:right-0'
1143
+ ],
1144
+ variants: {
1145
+ /**
1146
+ * The variant of the card
1147
+ * @default subtle
1148
+ */ variant: {
1149
+ subtle: [
1150
+ 'border-transparent',
1151
+ // **** Media styles ****
1152
+ '[&_[data-slot="media"]]:rounded-2xl'
1153
+ ],
1154
+ outlined: 'border border-black'
1155
+ },
1156
+ /**
1157
+ * The layout of the card
1158
+ * @default vertical
1159
+ */ layout: {
1160
+ vertical: [
1161
+ 'flex-col',
1162
+ // **** Media ****
1163
+ '[&_[data-slot="media"]]:rounded-t-2xl'
1164
+ ],
1165
+ horizontal: [
1166
+ 'gap-x-4',
1167
+ // **** With Media ****
1168
+ '[&:has(>[data-slot="media"]:first-child)]:flex-col',
1169
+ '[&:has(>[data-slot="media"]:last-child)]:flex-col-reverse',
1170
+ '[&:has(>[data-slot="media"])]:md:!flex-row',
1171
+ '[&_[data-slot="media"]]:md:h-fit',
1172
+ '[&:has(>[data-slot="media"])>*]:md:basis-1/2',
1173
+ // Position media at the edges of the card
1174
+ '[&_[data-slot="media"]]:md:mb-[calc(theme(space.3)*-1-theme(borderWidth.DEFAULT))]',
1175
+ '[&_[data-slot="media"]:first-child]:md:mr-0',
1176
+ '[&_[data-slot="media"]:last-child]:md:ml-0',
1177
+ // Make sure the card link is clickable when the media is on the right side
1178
+ // This i necessary because the media content is positioned after the card link in the DOM
1179
+ '[&:has(>[data-slot="media"]:last-child)_[data-slot="card-link"]]:z-[1]',
1180
+ // **** Without Media ****
1181
+ '[&:not(:has(>[data-slot="media"]))]:flex-row',
1182
+ // Make the layout responsive: when the Content reaches a minimum width of 18rem, the layout switches to vertical. Also makes sure Content takes up the remaining space available.
1183
+ '[&:not(:has(>[data-slot="media"]))]:flex-wrap [&:not(:has(>[data-slot="media"]))_[data-slot="content"]]:grow [&:not(:has(>[data-slot="media"]))_[data-slot="content"]]:basis-[18rem]',
1184
+ // Make sure svg's etc. are not shrinkable
1185
+ '[&>:not([data-slot="content"],[data-slot="media"])]:shrink-0'
1186
+ ]
1187
+ }
1188
+ },
1189
+ defaultVariants: {
1190
+ variant: 'subtle',
1191
+ layout: 'vertical'
1192
+ },
1193
+ compoundVariants: [
1194
+ {
1195
+ variant: 'outlined',
1196
+ layout: 'horizontal',
1197
+ className: [
1198
+ // **** Media ****
1199
+ // Some rounded corners are removed when the card is outlined
1200
+ '[&_[data-slot="media"]]:rounded-t-2xl',
1201
+ '[&_[data-slot="media"]:first-child]:md:rounded-tr-none [&_[data-slot="media"]:first-child]:md:rounded-bl-2xl',
1202
+ '[&_[data-slot="media"]:last-child]:md:rounded-tl-none [&_[data-slot="media"]:last-child]:md:rounded-br-2xl',
1203
+ // **** Badge ****
1204
+ // Override default corner radius of the badge to match the media border radius
1205
+ '[&_[data-slot="media"]:first-child_[data-slot="badge"]:last-child]:md:rounded-tr-none',
1206
+ '[&_[data-slot="media"]:last-child_[data-slot="badge"]:first-child]:md:rounded-tl-none'
1207
+ ]
1208
+ }
1209
+ ]
1210
+ });
1211
+ const Card = ({ children, className: _className, variant, layout, ...restProps })=>{
1212
+ const className = cardVariants({
1213
+ className: _className,
1214
+ variant,
1215
+ layout
1216
+ });
1217
+ return /*#__PURE__*/ jsx("div", {
1218
+ className: className,
1219
+ ...restProps,
1220
+ children: children
1221
+ });
1222
+ };
1223
+ const cardLinkVariants = cva({
1224
+ base: 'w-fit max-w-full',
1225
+ variants: {
1226
+ withHref: {
1227
+ true: [
1228
+ // **** Clickarea ****
1229
+ 'cursor-pointer',
1230
+ 'after:absolute',
1231
+ 'after:inset-[calc(theme(borderWidth.DEFAULT)*-1)]',
1232
+ 'after:rounded-[calc(theme(borderRadius.2xl)-theme(borderWidth.DEFAULT))]',
1233
+ // **** Focus ****
1234
+ 'focus-visible:outline-none',
1235
+ 'data-[focus-visible]:after:outline-focus',
1236
+ 'data-[focus-visible]:after:outline-offset-2',
1237
+ // **** Hover ****
1238
+ // Links are underlined by default, and the underline is removed on hover.
1239
+ // So we make sure that also happens when the user hovers the clickable area.
1240
+ 'hover:no-underline'
1241
+ ],
1242
+ false: [
1243
+ // **** Clickarea ****
1244
+ '[&_a]:after:cursor-pointer',
1245
+ '[&_a]:after:absolute',
1246
+ '[&_a]:after:inset-[calc(theme(borderWidth.DEFAULT)*-1)]',
1247
+ '[&_a]:after:rounded-[calc(theme(borderRadius.2xl)-theme(borderWidth.DEFAULT))]',
1248
+ // **** Focus ****
1249
+ '[&_a[data-focus-visible]]:outline-none',
1250
+ '[&_a[data-focus-visible]]:after:outline-focus',
1251
+ '[&_a[data-focus-visible]]:after:outline-offset-2',
1252
+ // **** Hover ****
1253
+ // Links are underlined by default, and the underline is removed on hover.
1254
+ // So we make sure that also happens when the user hovers the card.
1255
+ // The group-hover ensures that the hover effect also applies when this component is used as a wrapper around a link.
1256
+ '[&_a]:group-hover/card:no-underline'
1257
+ ]
1258
+ }
1259
+ }
1260
+ });
1261
+ /**
1262
+ * A component that creates a clickable area on a card.
1263
+ * It can be used either as a wrapper around a link or as a standalone link.
1264
+ */ const CardLink = ({ className: _className, href, ...restProps })=>{
1265
+ const className = cardLinkVariants({
1266
+ className: _className,
1267
+ withHref: !!href
1268
+ });
1269
+ return href ? /*#__PURE__*/ jsx(Link, {
1270
+ "data-slot": "card-link",
1271
+ ...restProps,
1272
+ href: href,
1273
+ className: className
1274
+ }) : // We can't utilize that the `Link` component from react-aria-components renders as a span if it doesn't have an href,
1275
+ // because it still renders with role="link" and tabindex="0" which makes it focusable.
1276
+ // So we need to render a div instead.
1277
+ /*#__PURE__*/ jsx("div", {
1278
+ ...restProps,
1279
+ "data-slot": "card-link",
1280
+ className: className
1281
+ });
1282
+ };
1283
+
1284
+ /**
1285
+ * A React component that wraps https://react-spectrum.adobe.com/react-aria/useDateFormatter.html
1286
+ * By default it sets the timeZone to `Europe/Berlin` to prevent the server's timezone from affecting
1287
+ * the localized format
1288
+ */ const DateFormatter = ({ options: _options, value, children: render })=>{
1289
+ const options = {
1290
+ timeZone: 'Europe/Berlin',
1291
+ ..._options
1292
+ };
1293
+ const formatter = useDateFormatter(options);
1294
+ const date = typeof value === 'string' ? new Date(value) : value;
1295
+ const formatted = formatter.format(date);
1296
+ return render ? render(formatted) : formatted;
1297
+ };
1298
+
1299
+ const VideoLoop = ({ src, format, alt, className })=>{
1300
+ // Control the video playback state, so that the user can pause and play the video at will, also control the video autoplay
1301
+ const [shouldPlay, setShouldPlay] = useState(false);
1302
+ // Needed to show the pause button when the video is actually playing (refer to google's autoplay policy: https://developers.google.com/web/updates/2017/09/autoplay-policy-changes)
1303
+ const [isPlaying, setIsPlaying] = useState(false);
1304
+ // We need to check if the user prefers reduced motion, so that we can prevent the video from autoplaying if so
1305
+ const [userPrefersReducedMotion, setUserPrefersReducedMotion] = useState(null);
1306
+ const videoRef = useRef(null);
1307
+ useEffect(()=>{
1308
+ const { matches: userPrefersReducedMotion } = matchMedia('(prefers-reduced-motion: reduce)');
1309
+ setUserPrefersReducedMotion(userPrefersReducedMotion);
1310
+ // Autoplay the video if the user does not prefer reduced motion
1311
+ setShouldPlay(!userPrefersReducedMotion);
1312
+ }, []);
1313
+ // Follow google's autoplay policy: https://developers.google.com/web/updates/2017/09/autoplay-policy-changes
1314
+ // "Don't assume a video will play, and don't show a pause button when the video is not actually playing."
1315
+ // "You should always look at the Promise returned by the play function to see if it was rejected:"
1316
+ // This is why we use the promise returned by the play function, and an extra state variable to determine if the video is actually playing or not
1317
+ useEffect(()=>{
1318
+ if (!videoRef.current) return;
1319
+ if (shouldPlay) {
1320
+ videoRef.current.play().then(()=>setIsPlaying(true)).catch(()=>setIsPlaying(false));
1321
+ } else {
1322
+ videoRef.current.pause();
1323
+ setIsPlaying(false);
1324
+ }
1325
+ }, [
1326
+ shouldPlay
1327
+ ]);
1328
+ return /*#__PURE__*/ jsxs("div", {
1329
+ className: cx(className, 'relative', userPrefersReducedMotion === null && 'opacity-0'),
1330
+ children: [
1331
+ /*#__PURE__*/ jsx("video", {
1332
+ "aria-hidden": true,
1333
+ ref: videoRef,
1334
+ // cursor-pointer is not working on the button below, so we add it here for the same effect
1335
+ className: "h-full w-full cursor-pointer object-cover",
1336
+ playsInline: true,
1337
+ loop: userPrefersReducedMotion === false,
1338
+ autoPlay: userPrefersReducedMotion === false,
1339
+ muted: true,
1340
+ onEnded: (event)=>{
1341
+ if (userPrefersReducedMotion) {
1342
+ // Reset the video to the beginning if the user prefers reduced motion, since the video will not loop
1343
+ event.currentTarget.currentTime = 0;
1344
+ setShouldPlay(false);
1345
+ setIsPlaying(false);
1346
+ }
1347
+ },
1348
+ children: /*#__PURE__*/ jsx("source", {
1349
+ src: src,
1350
+ type: `video/${format}`
1351
+ })
1352
+ }),
1353
+ userPrefersReducedMotion !== null && /*#__PURE__*/ jsx("button", {
1354
+ "aria-hidden": true,
1355
+ type: "button",
1356
+ onClick: ()=>setShouldPlay((prevState)=>!prevState),
1357
+ className: cx('absolute top-0 right-0 bottom-0 left-0 m-auto grid place-items-center', 'focus-visible:outline-focus focus-visible:outline-focus-offset', // Setting the opacity to 0 before applying the transition below will ensure the button only fades in after the video has started playing
1358
+ shouldPlay && 'opacity-0', isPlaying && [
1359
+ 'transition-opacity duration-200',
1360
+ // Only show the pause button when the video is hovered or focused
1361
+ 'focus-visible:opacity-100',
1362
+ 'hover:opacity-100'
1363
+ ]),
1364
+ children: /*#__PURE__*/ jsx("span", {
1365
+ className: "grid h-12 w-12 place-items-center rounded-full bg-white outline-none",
1366
+ children: isPlaying ? /*#__PURE__*/ jsx(PlayerPause, {}) : /*#__PURE__*/ jsx(PlayerPlay, {})
1367
+ })
1368
+ }),
1369
+ alt && /*#__PURE__*/ jsx("p", {
1370
+ className: "sr-only",
1371
+ children: alt
1372
+ })
1373
+ ]
1374
+ });
1375
+ };
1376
+
1377
+ const disclosureButtonVariants = cva({
1378
+ base: [
1379
+ 'inline-flex cursor-pointer items-center justify-between rounded-lg outline-none data-[focus-visible]:outline-focus',
1380
+ // Ensure a minimum click area of 44x44px, while making it look like it only has the size of the content
1381
+ '-m-2.5 p-2.5 data-[focus-visible]:outline-offset-[-0.625rem]'
1382
+ ],
1383
+ variants: {
1384
+ withChevron: {
1385
+ true: '[&[aria-expanded="true"]_svg]:rotate-180',
1386
+ false: null
1387
+ },
1388
+ /**
1389
+ * When the button is without text, but with a single icon.
1390
+ * @default false
1391
+ */ isIconOnly: {
1392
+ true: '[&>svg]:h-7 [&>svg]:w-7',
1393
+ false: 'gap-2.5'
1394
+ }
1395
+ },
1396
+ defaultVariants: {
1397
+ withChevron: false,
1398
+ isIconOnly: false
1399
+ }
1400
+ });
1401
+ const DisclosureButton = ({ className, withChevron, isIconOnly, children, ref: _ref, ...restProps })=>{
1402
+ const [props, ref] = useContextProps(restProps, _ref, ButtonContext);
1403
+ return /*#__PURE__*/ jsxs(Button$1, {
1404
+ ...props,
1405
+ ref: ref,
1406
+ className: disclosureButtonVariants({
1407
+ className,
1408
+ withChevron,
1409
+ isIconOnly
1410
+ }),
1411
+ children: [
1412
+ children,
1413
+ withChevron && /*#__PURE__*/ jsx(ChevronDown, {
1414
+ className: "flex-none transition-transform duration-300 motion-reduce:transition-none"
1415
+ })
1416
+ ]
1417
+ });
1418
+ };
1419
+ const Disclosure = ({ ref: _ref, children, ..._props })=>{
1420
+ const [props, ref] = useContextProps(_props, _ref, DisclosureContext);
1421
+ const groupState = useContext(DisclosureGroupStateContext);
1422
+ let { id, ...otherProps } = props;
1423
+ const defaultId = useId();
1424
+ id ||= defaultId;
1425
+ const isExpanded = groupState ? groupState.expandedKeys.has(id) : props.isExpanded;
1426
+ const state = useDisclosureState({
1427
+ ...props,
1428
+ isExpanded,
1429
+ onExpandedChange (isExpanded) {
1430
+ if (groupState) {
1431
+ groupState.toggleKey(id);
1432
+ }
1433
+ props.onExpandedChange?.(isExpanded);
1434
+ }
1435
+ });
1436
+ const isDisabled = props.isDisabled || groupState?.isDisabled || false;
1437
+ const domProps = filterDOMProps(otherProps);
1438
+ const panelRef = useRef(null);
1439
+ const { buttonProps, panelProps } = useDisclosure({
1440
+ ...props,
1441
+ isExpanded,
1442
+ isDisabled
1443
+ }, state, panelRef);
1444
+ const { role: _, ...propsWithoutRole } = panelProps;
1445
+ return /*#__PURE__*/ jsx(Provider, {
1446
+ values: [
1447
+ [
1448
+ DisclosureContext,
1449
+ state
1450
+ ],
1451
+ [
1452
+ ButtonContext,
1453
+ buttonProps
1454
+ ],
1455
+ [
1456
+ DisclosurePanelContext,
1457
+ {
1458
+ ...propsWithoutRole,
1459
+ ref: panelRef
1460
+ }
1461
+ ]
1462
+ ],
1463
+ children: /*#__PURE__*/ jsx("div", {
1464
+ ...domProps,
1465
+ className: otherProps.className,
1466
+ ref: ref,
1467
+ "data-expanded": state.isExpanded || undefined,
1468
+ "data-disabled": isDisabled || undefined,
1469
+ children: children
1470
+ })
1471
+ });
1472
+ };
1473
+ const DisclosurePanelContext = /*#__PURE__*/ createContext({});
1474
+ const DisclosurePanel = ({ ref: _ref, ..._props })=>{
1475
+ const disclosureContext = useContext(DisclosureContext);
1476
+ const [props, ref] = useContextProps(_props, _ref, DisclosurePanelContext);
1477
+ const { role: _role = 'group', className, ...restProps } = props;
1478
+ const ariaLabelledby = _props['aria-labelledby'] ?? restProps['aria-labelledby'];
1479
+ const isWithoutRole = _role === 'none';
1480
+ const role = isWithoutRole ? undefined : _role;
1481
+ return /*#__PURE__*/ jsx("div", {
1482
+ className: cx('grid transition-all duration-300 after:relative after:block after:h-0 after:transition-all after:duration-300 motion-reduce:transition-none', disclosureContext?.isExpanded ? 'grid-rows-[1fr] after:h-3.5' : 'grid-rows-[0fr]'),
1483
+ children: /*#__PURE__*/ jsx("div", {
1484
+ ...restProps,
1485
+ ref: ref,
1486
+ className: cx(className, 'relative overflow-hidden [content-visibility:visible] before:relative before:block before:h-1.5 after:relative after:block after:h-1.5'),
1487
+ role: role,
1488
+ "aria-labelledby": isWithoutRole ? undefined : ariaLabelledby
1489
+ })
1490
+ });
1491
+ };
472
1492
 
473
- export { _Checkbox as Checkbox, _CheckboxGroup as CheckboxGroup, _Combobox as Combobox, ComboboxItem, _Radio as Radio, _RadioGroup as RadioGroup, _Select as Select, SelectItem, _TextArea as TextArea, _TextField as TextField };
1493
+ export { _Accordion as Accordion, _AccordionItem as AccordionItem, Alertbox, _Backlink as Backlink, _Badge as Badge, _Breadcrumb as Breadcrumb, _Breadcrumbs as Breadcrumbs, _Button as Button, Caption, Card, CardLink, _Checkbox as Checkbox, _CheckboxGroup as CheckboxGroup, _Combobox as Combobox, ListBoxHeader as ComboboxHeader, ListBoxItem as ComboboxItem, ListBoxSection as ComboboxSection, _Content as Content, ContentContext, DateFormatter, Footer, GrunnmurenProvider, _Heading as Heading, HeadingContext, Media, _NumberField as NumberField, _Radio as Radio, _RadioGroup as RadioGroup, _Select as Select, ListBoxHeader as SelectHeader, ListBoxItem as SelectItem, ListBoxSection as SelectSection, _TextArea as TextArea, _TextField as TextField, Disclosure as UNSAFE_Disclosure, DisclosureButton as UNSAFE_DisclosureButton, DisclosurePanel as UNSAFE_DisclosurePanel, VideoLoop, _useLocale as useLocale };