@obosbbl/grunnmuren-react 3.0.15 → 3.1.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/__stories__/form-validation.stories.cjs +488 -0
- package/dist/__stories__/form-validation.stories.d.cts +11 -0
- package/dist/__stories__/form-validation.stories.d.ts +11 -0
- package/dist/__stories__/form-validation.stories.js +482 -0
- package/dist/__stories__/home.stories.cjs +32 -0
- package/dist/__stories__/home.stories.d.cts +10 -0
- package/dist/__stories__/home.stories.d.ts +10 -0
- package/dist/__stories__/home.stories.js +29 -0
- package/dist/__stories__/icons.stories.cjs +47 -0
- package/dist/__stories__/icons.stories.d.cts +8 -0
- package/dist/__stories__/icons.stories.d.ts +8 -0
- package/dist/__stories__/icons.stories.js +24 -0
- package/dist/__stories__/typography.stories.cjs +224 -0
- package/dist/__stories__/typography.stories.d.cts +14 -0
- package/dist/__stories__/typography.stories.d.ts +14 -0
- package/dist/__stories__/typography.stories.js +214 -0
- package/dist/index.d.mts +466 -481
- package/dist/index.mjs +1608 -1562
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -1,37 +1,18 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import {
|
|
2
|
+
import { useContextProps, DisclosureContext, DisclosureGroupStateContext, Provider, ButtonContext as ButtonContext$1, DEFAULT_SLOT, Button as Button$1, DisclosureGroup, useLocale, Link, Breadcrumb as Breadcrumb$1, Breadcrumbs as Breadcrumbs$1, Text, CheckboxContext, Checkbox as Checkbox$1, FieldError, Label as Label$1, CheckboxGroup as CheckboxGroup$1, Header, ListBoxItem as ListBoxItem$1, ListBoxSection as ListBoxSection$1, ListBox as ListBox$1, ComboBox, Group, Input, Popover, useSlottedContext, FormContext, FieldErrorContext, LabelContext, InputContext, I18nProvider, RouterProvider, GroupContext, Dialog as Dialog$1, DialogTrigger as DialogTrigger$1, Modal as Modal$1, ModalOverlay as ModalOverlay$1, NumberField as NumberField$1, Radio as Radio$1, RadioGroup as RadioGroup$1, Select as Select$1, SelectValue, Table as Table$1, TableBody as TableBody$1, Cell, Column, TableHeader as TableHeader$1, Row, Tab as Tab$1, TabListStateContext, TabList as TabList$1, TabPanel as TabPanel$1, Tabs as Tabs$1, TagGroup as TagGroup$1, TagList as TagList$1, Tag as Tag$1, TextField as TextField$1, TextArea as TextArea$1 } from 'react-aria-components';
|
|
3
3
|
export { Form, Group, DisclosureGroup as UNSAFE_DisclosureGroup } from 'react-aria-components';
|
|
4
4
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
5
|
-
import { ChevronDown, LoadingSpinner, Check, Close, InfoCircle, CheckCircle, Warning, Error, ChevronRight, ChevronLeft, PlayerPause, PlayerPlay, Trash, User, ArrowRight, Download, LinkExternal } from '@obosbbl/grunnmuren-icons-react';
|
|
6
|
-
import { useLayoutEffect, filterDOMProps, mergeRefs, mergeProps, useObjectRef, useFormReset, useUpdateEffect } from '@react-aria/utils';
|
|
7
5
|
import { cva, cx, compose } from 'cva';
|
|
8
|
-
import { createContext,
|
|
9
|
-
import {
|
|
6
|
+
import { createContext, useContext, useId, useRef, Children, useState, useEffect, useCallback } from 'react';
|
|
7
|
+
import { ChevronDown, Error, Warning, CheckCircle, InfoCircle, Close, User, ChevronLeft, ChevronRight, LoadingSpinner, Check, Trash, ArrowRight, Download, LinkExternal, PlayerPause, PlayerPlay } from '@obosbbl/grunnmuren-icons-react';
|
|
8
|
+
import { filterDOMProps, mergeProps, mergeRefs, useUpdateEffect, useObjectRef, useFormReset } from '@react-aria/utils';
|
|
9
|
+
import { useFocusRing, useDisclosure, useProgressBar, useDateFormatter, useField } from 'react-aria';
|
|
10
10
|
import { useDisclosureState } from 'react-stately';
|
|
11
|
+
import { useDebouncedCallback } from 'use-debounce';
|
|
11
12
|
import { useFormValidation } from '@react-aria/form';
|
|
12
13
|
import { useFormValidationState } from '@react-stately/form';
|
|
13
14
|
import { useControlledState } from '@react-stately/utils';
|
|
14
15
|
import { PressResponder } from '@react-aria/interactions';
|
|
15
|
-
import { useDebouncedCallback } from 'use-debounce';
|
|
16
|
-
|
|
17
|
-
function GrunnmurenProvider({ children, locale = 'nb', navigate, useHref }) {
|
|
18
|
-
return /*#__PURE__*/ jsx(I18nProvider, {
|
|
19
|
-
locale: locale,
|
|
20
|
-
children: navigate ? /*#__PURE__*/ jsx(RouterProvider, {
|
|
21
|
-
navigate: navigate,
|
|
22
|
-
useHref: useHref,
|
|
23
|
-
children: children
|
|
24
|
-
}) : children
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Returns the locale set in `<GrunnmurenProvider />`
|
|
30
|
-
*/ function _useLocale() {
|
|
31
|
-
// a small wrapper around react-arias useLocale with a simpler return type with only the locales that we actually support
|
|
32
|
-
const locale = useLocale();
|
|
33
|
-
return locale.locale;
|
|
34
|
-
}
|
|
35
16
|
|
|
36
17
|
const HeadingContext = /*#__PURE__*/ createContext({});
|
|
37
18
|
const headingVariants = cva({
|
|
@@ -109,11 +90,177 @@ const Footer = (props)=>/*#__PURE__*/ jsx("div", {
|
|
|
109
90
|
"data-slot": "footer"
|
|
110
91
|
});
|
|
111
92
|
|
|
93
|
+
const disclosureButtonVariants = cva({
|
|
94
|
+
base: [
|
|
95
|
+
'inline-flex cursor-pointer items-center justify-between rounded-lg focus-visible:outline-current focus-visible:outline-focus',
|
|
96
|
+
// Ensure a minimum click area of 44x44px, while making it look like it only has the size of the content
|
|
97
|
+
'p-2.5 focus-visible:outline-offset-[-0.625rem]',
|
|
98
|
+
'data-accordion:-m-2.5'
|
|
99
|
+
],
|
|
100
|
+
variants: {
|
|
101
|
+
withChevron: {
|
|
102
|
+
true: '[&[aria-expanded="true"]_svg]:rotate-180',
|
|
103
|
+
false: null
|
|
104
|
+
},
|
|
105
|
+
/**
|
|
106
|
+
* When the button is without text, but with a single icon.
|
|
107
|
+
* @default false
|
|
108
|
+
*/ isIconOnly: {
|
|
109
|
+
true: '[&>svg]:h-7 [&>svg]:w-7',
|
|
110
|
+
false: 'gap-2.5'
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
defaultVariants: {
|
|
114
|
+
withChevron: false,
|
|
115
|
+
isIconOnly: false
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
const DisclosureButton = ({ className, withChevron, isIconOnly, children, ref: _ref, ...restProps })=>{
|
|
119
|
+
const [props, ref] = useContextProps(restProps, _ref, ButtonContext$1);
|
|
120
|
+
return /*#__PURE__*/ jsxs(Button$1, {
|
|
121
|
+
...props,
|
|
122
|
+
ref: ref,
|
|
123
|
+
className: disclosureButtonVariants({
|
|
124
|
+
className,
|
|
125
|
+
withChevron,
|
|
126
|
+
isIconOnly
|
|
127
|
+
}),
|
|
128
|
+
slot: "trigger",
|
|
129
|
+
children: [
|
|
130
|
+
children,
|
|
131
|
+
withChevron && /*#__PURE__*/ jsx(ChevronDown, {
|
|
132
|
+
className: "flex-none transition-transform duration-300 motion-reduce:transition-none"
|
|
133
|
+
})
|
|
134
|
+
]
|
|
135
|
+
});
|
|
136
|
+
};
|
|
137
|
+
const DisclosureStateContext = /*#__PURE__*/ createContext(null);
|
|
138
|
+
const Disclosure = ({ ref: _ref, children, ..._props })=>{
|
|
139
|
+
const [props, ref] = useContextProps(_props, _ref, DisclosureContext);
|
|
140
|
+
const groupState = useContext(DisclosureGroupStateContext);
|
|
141
|
+
let { id, ...otherProps } = props;
|
|
142
|
+
const defaultId = useId();
|
|
143
|
+
id ||= defaultId;
|
|
144
|
+
const isExpanded = groupState ? groupState.expandedKeys.has(id) : props.isExpanded;
|
|
145
|
+
const state = useDisclosureState({
|
|
146
|
+
...props,
|
|
147
|
+
isExpanded,
|
|
148
|
+
onExpandedChange (isExpanded) {
|
|
149
|
+
if (groupState) {
|
|
150
|
+
groupState.toggleKey(id);
|
|
151
|
+
}
|
|
152
|
+
props.onExpandedChange?.(isExpanded);
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
const isDisabled = props.isDisabled || groupState?.isDisabled || false;
|
|
156
|
+
const domProps = filterDOMProps(otherProps);
|
|
157
|
+
const { isFocusVisible: isFocusVisibleWithin } = useFocusRing({
|
|
158
|
+
within: true
|
|
159
|
+
});
|
|
160
|
+
const panelRef = useRef(null);
|
|
161
|
+
const { buttonProps, panelProps } = useDisclosure({
|
|
162
|
+
...props,
|
|
163
|
+
isExpanded,
|
|
164
|
+
isDisabled
|
|
165
|
+
}, state, panelRef);
|
|
166
|
+
const { role: _, ...propsWithoutRole } = panelProps;
|
|
167
|
+
return /*#__PURE__*/ jsx(Provider, {
|
|
168
|
+
values: [
|
|
169
|
+
[
|
|
170
|
+
DisclosureContext,
|
|
171
|
+
state
|
|
172
|
+
],
|
|
173
|
+
[
|
|
174
|
+
ButtonContext$1,
|
|
175
|
+
{
|
|
176
|
+
slots: {
|
|
177
|
+
[DEFAULT_SLOT]: {},
|
|
178
|
+
trigger: buttonProps
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
],
|
|
182
|
+
[
|
|
183
|
+
DisclosurePanelContext,
|
|
184
|
+
{
|
|
185
|
+
...propsWithoutRole,
|
|
186
|
+
panelRef
|
|
187
|
+
}
|
|
188
|
+
],
|
|
189
|
+
[
|
|
190
|
+
DisclosureStateContext,
|
|
191
|
+
state
|
|
192
|
+
]
|
|
193
|
+
],
|
|
194
|
+
children: /*#__PURE__*/ jsx("div", {
|
|
195
|
+
...domProps,
|
|
196
|
+
className: otherProps.className,
|
|
197
|
+
ref: ref,
|
|
198
|
+
"data-focus-visible-within": isFocusVisibleWithin || undefined,
|
|
199
|
+
"data-expanded": state.isExpanded || undefined,
|
|
200
|
+
"data-disabled": isDisabled || undefined,
|
|
201
|
+
children: typeof children === 'function' ? children({
|
|
202
|
+
isExpanded: state.isExpanded,
|
|
203
|
+
isFocusVisibleWithin,
|
|
204
|
+
isDisabled,
|
|
205
|
+
state,
|
|
206
|
+
defaultChildren: null
|
|
207
|
+
}) : children
|
|
208
|
+
})
|
|
209
|
+
});
|
|
210
|
+
};
|
|
211
|
+
const DisclosurePanelContext = /*#__PURE__*/ createContext({});
|
|
212
|
+
const DisclosurePanel = ({ ref, children, ...props })=>{
|
|
213
|
+
const disclosureContext = useContext(DisclosureContext);
|
|
214
|
+
const { panelProps, panelRef } = useContext(DisclosurePanelContext);
|
|
215
|
+
const { role: _role = 'group', className, ...restProps } = props;
|
|
216
|
+
const ariaLabelledby = props['aria-labelledby'] ?? restProps['aria-labelledby'];
|
|
217
|
+
const isWithoutRole = _role === 'none';
|
|
218
|
+
const role = isWithoutRole ? undefined : _role;
|
|
219
|
+
const { isFocusVisible: isFocusVisibleWithin, focusProps: focusWithinProps } = useFocusRing({
|
|
220
|
+
within: true
|
|
221
|
+
});
|
|
222
|
+
const domProps = filterDOMProps(props);
|
|
223
|
+
return /*#__PURE__*/ jsx("div", {
|
|
224
|
+
className: cx('grid transition-all duration-300 motion-reduce:transition-none', disclosureContext?.isExpanded ? 'grid-rows-[1fr] after:h-3.5' : 'grid-rows-[0fr]'),
|
|
225
|
+
"data-expanded": disclosureContext?.isExpanded || undefined,
|
|
226
|
+
children: /*#__PURE__*/ jsx("div", {
|
|
227
|
+
className: "overflow-hidden",
|
|
228
|
+
children: /*#__PURE__*/ jsx("div", {
|
|
229
|
+
ref: mergeRefs(ref, panelRef),
|
|
230
|
+
...mergeProps(panelProps, focusWithinProps),
|
|
231
|
+
...restProps,
|
|
232
|
+
...domProps,
|
|
233
|
+
"data-focus-visible-within": isFocusVisibleWithin || undefined,
|
|
234
|
+
className: cx(className, '[content-visibility:visible]'),
|
|
235
|
+
role: role,
|
|
236
|
+
"aria-labelledby": isWithoutRole ? undefined : ariaLabelledby,
|
|
237
|
+
inert: disclosureContext?.isExpanded ? undefined : true,
|
|
238
|
+
children: /*#__PURE__*/ jsx(Provider, {
|
|
239
|
+
values: [
|
|
240
|
+
// Reset the context to avoid passing the same context to children, in case of nested Disclosures
|
|
241
|
+
[
|
|
242
|
+
DisclosureContext,
|
|
243
|
+
null
|
|
244
|
+
],
|
|
245
|
+
[
|
|
246
|
+
ButtonContext$1,
|
|
247
|
+
null
|
|
248
|
+
]
|
|
249
|
+
],
|
|
250
|
+
children: children
|
|
251
|
+
})
|
|
252
|
+
})
|
|
253
|
+
})
|
|
254
|
+
});
|
|
255
|
+
};
|
|
256
|
+
|
|
112
257
|
function Accordion(props) {
|
|
113
|
-
const { children, className, ...restProps } = props;
|
|
258
|
+
const { children, className, allowsMultipleExpanded = true, ...restProps } = props;
|
|
114
259
|
const childCount = Children.count(children);
|
|
115
|
-
return /*#__PURE__*/ jsx(
|
|
260
|
+
return /*#__PURE__*/ jsx(DisclosureGroup, {
|
|
116
261
|
...restProps,
|
|
262
|
+
"data-accordion": true,
|
|
263
|
+
allowsMultipleExpanded: allowsMultipleExpanded,
|
|
117
264
|
className: cx('rounded-lg bg-white', className),
|
|
118
265
|
children: Children.map(children, (child, index)=>/*#__PURE__*/ jsxs(Fragment, {
|
|
119
266
|
children: [
|
|
@@ -128,40 +275,16 @@ function Accordion(props) {
|
|
|
128
275
|
});
|
|
129
276
|
}
|
|
130
277
|
function AccordionItem(props) {
|
|
131
|
-
const { className, children, defaultOpen = false, isOpen: controlledIsOpen, onOpenChange, ...restProps } = props;
|
|
132
|
-
const
|
|
133
|
-
const
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
// regardless if we are controlled or uncontrolled.
|
|
137
|
-
// If we are controlled, we use a layout effect to sync the controlled state
|
|
138
|
-
// with the internal state.
|
|
139
|
-
//
|
|
140
|
-
const [isOpen, setIsOpen] = useState(// If we are controlled, use that open state, otherwise use the uncontrolled
|
|
141
|
-
isControlled ? controlledIsOpen : defaultOpen);
|
|
142
|
-
useLayoutEffect(()=>{
|
|
143
|
-
if (isControlled) {
|
|
144
|
-
setIsOpen(controlledIsOpen);
|
|
145
|
-
}
|
|
146
|
-
}, [
|
|
147
|
-
controlledIsOpen,
|
|
148
|
-
isControlled
|
|
149
|
-
]);
|
|
150
|
-
const handleOpenChange = ()=>{
|
|
151
|
-
const newOpenState = !isOpen;
|
|
152
|
-
if (!isControlled) {
|
|
153
|
-
setIsOpen(newOpenState);
|
|
154
|
-
}
|
|
155
|
-
// Always call the change handler, even if we're uncontrolled.
|
|
156
|
-
// Easier to add stuff such as tracking etc.
|
|
157
|
-
if (onOpenChange) {
|
|
158
|
-
onOpenChange(newOpenState);
|
|
159
|
-
}
|
|
160
|
-
};
|
|
161
|
-
return /*#__PURE__*/ jsx("div", {
|
|
278
|
+
const { className, children, defaultOpen = false, isOpen: controlledIsOpen, onOpenChange, defaultExpanded, isExpanded, onExpandedChange, ...restProps } = props;
|
|
279
|
+
const _defaultExpanded = defaultOpen ?? defaultExpanded;
|
|
280
|
+
const _isExpanded = controlledIsOpen ?? isExpanded;
|
|
281
|
+
const _onExpandedChange = onOpenChange ?? onExpandedChange;
|
|
282
|
+
return /*#__PURE__*/ jsx(Disclosure, {
|
|
162
283
|
...restProps,
|
|
163
284
|
className: cx('relative px-2', className),
|
|
164
|
-
|
|
285
|
+
defaultExpanded: _defaultExpanded,
|
|
286
|
+
onExpandedChange: _onExpandedChange,
|
|
287
|
+
isExpanded: _isExpanded,
|
|
165
288
|
children: /*#__PURE__*/ jsx(Provider, {
|
|
166
289
|
values: [
|
|
167
290
|
[
|
|
@@ -171,33 +294,22 @@ function AccordionItem(props) {
|
|
|
171
294
|
className: 'font-medium leading-7 -mx-2 text-base',
|
|
172
295
|
// Supply a default level here to make this typecheck ok. Will be overwritten with the consumers set heading level anyways
|
|
173
296
|
level: 3,
|
|
174
|
-
_innerWrapper: (children)=>/*#__PURE__*/
|
|
175
|
-
"aria-controls": contentId,
|
|
176
|
-
"aria-expanded": isOpen,
|
|
297
|
+
_innerWrapper: (children)=>/*#__PURE__*/ jsx(DisclosureButton, {
|
|
177
298
|
// Use outline with offset as focus indicator, this does not cover the left sky border on the expanded content and works with or without a background color on the accordion container
|
|
178
|
-
className: "flex min-h-
|
|
179
|
-
id: buttonId,
|
|
180
|
-
onClick: handleOpenChange,
|
|
299
|
+
className: "flex min-h-11 w-full gap-1.5 rounded-lg px-2 py-3.5 text-left focus-visible:outline-focus-inset!",
|
|
181
300
|
type: "button",
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
/*#__PURE__*/ jsx(ChevronDown, {
|
|
185
|
-
className: cx('flex-none transition-transform duration-300 motion-reduce:transition-none', isOpen && 'rotate-180')
|
|
186
|
-
})
|
|
187
|
-
]
|
|
301
|
+
withChevron: true,
|
|
302
|
+
children: children
|
|
188
303
|
})
|
|
189
304
|
}
|
|
190
305
|
],
|
|
191
306
|
[
|
|
192
307
|
ContentContext,
|
|
193
308
|
{
|
|
194
|
-
className: // Uses pseudo
|
|
195
|
-
'text-sm font-light leading-6 px-3.5 relative overflow-hidden border-sky border-l-[3px] before:relative before:block before:h-1.5 after:relative after:block after:h-1.5',
|
|
309
|
+
className: // Uses pseudo elements for vertical padding, since that doesn't affect the height when the accordion is closed
|
|
310
|
+
'text-sm font-light leading-6 px-3.5 data-[expanded]:after:h-3.5 relative overflow-hidden border-sky border-l-[3px] before:relative before:block before:h-1.5 after:relative after:block after:h-1.5',
|
|
196
311
|
role: 'region',
|
|
197
|
-
|
|
198
|
-
'aria-labelledby': buttonId,
|
|
199
|
-
_outerWrapper: (children)=>/*#__PURE__*/ jsx("div", {
|
|
200
|
-
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]'),
|
|
312
|
+
_outerWrapper: (children)=>/*#__PURE__*/ jsx(DisclosurePanel, {
|
|
201
313
|
children: children
|
|
202
314
|
})
|
|
203
315
|
}
|
|
@@ -208,43 +320,6 @@ function AccordionItem(props) {
|
|
|
208
320
|
});
|
|
209
321
|
}
|
|
210
322
|
|
|
211
|
-
const badgeVariants = cva({
|
|
212
|
-
base: [
|
|
213
|
-
'inline-flex w-fit items-center justify-center gap-1.5 rounded-lg [&_svg]:shrink-0'
|
|
214
|
-
],
|
|
215
|
-
variants: {
|
|
216
|
-
color: {
|
|
217
|
-
'gray-dark': 'bg-gray-dark text-white',
|
|
218
|
-
mint: 'bg-mint',
|
|
219
|
-
sky: 'bg-sky',
|
|
220
|
-
white: 'bg-white',
|
|
221
|
-
'blue-dark': 'bg-blue-dark text-white',
|
|
222
|
-
'green-dark': 'bg-green-dark text-white'
|
|
223
|
-
},
|
|
224
|
-
size: {
|
|
225
|
-
small: 'description px-2 py-0.5 [&_svg]:h-4 [&_svg]:w-4',
|
|
226
|
-
medium: 'description px-2.5 py-1.5 [&_svg]:h-4 [&_svg]:w-4',
|
|
227
|
-
large: 'paragraph px-3 py-2 [&_svg]:h-5 [&_svg]:w-5'
|
|
228
|
-
}
|
|
229
|
-
},
|
|
230
|
-
defaultVariants: {
|
|
231
|
-
size: 'medium'
|
|
232
|
-
}
|
|
233
|
-
});
|
|
234
|
-
function Badge(props) {
|
|
235
|
-
const { className: _className, color, size, ...restProps } = props;
|
|
236
|
-
const className = badgeVariants({
|
|
237
|
-
className: _className,
|
|
238
|
-
color,
|
|
239
|
-
size
|
|
240
|
-
});
|
|
241
|
-
return /*#__PURE__*/ jsx("span", {
|
|
242
|
-
className: className,
|
|
243
|
-
...restProps,
|
|
244
|
-
"data-slot": "badge"
|
|
245
|
-
});
|
|
246
|
-
}
|
|
247
|
-
|
|
248
323
|
const translations$1 = {
|
|
249
324
|
close: {
|
|
250
325
|
nb: 'Lukk',
|
|
@@ -279,17 +354,243 @@ const translations$1 = {
|
|
|
279
354
|
};
|
|
280
355
|
|
|
281
356
|
/**
|
|
282
|
-
*
|
|
283
|
-
*/
|
|
357
|
+
* Returns the locale set in `<GrunnmurenProvider />`
|
|
358
|
+
*/ function _useLocale() {
|
|
359
|
+
// a small wrapper around react-arias useLocale with a simpler return type with only the locales that we actually support
|
|
360
|
+
const locale = useLocale();
|
|
361
|
+
return locale.locale;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const iconMap = {
|
|
365
|
+
info: InfoCircle,
|
|
366
|
+
success: CheckCircle,
|
|
367
|
+
warning: Warning,
|
|
368
|
+
danger: Error
|
|
369
|
+
};
|
|
370
|
+
const alertVariants = cva({
|
|
284
371
|
base: [
|
|
285
|
-
'
|
|
372
|
+
'grid grid-cols-[auto_1fr_auto] items-center gap-2 rounded-md border-2 px-3 py-2',
|
|
373
|
+
// Heading styles:
|
|
374
|
+
'[&_[data-slot="heading"]]:font-medium [&_[data-slot="heading"]]:text-base [&_[data-slot="heading"]]:leading-7',
|
|
375
|
+
// Content styles:
|
|
376
|
+
'[&:has([data-slot="heading"])_[data-slot="content"]]:col-span-full [&_[data-slot="content"]]:text-sm [&_[data-slot="content"]]:leading-6',
|
|
377
|
+
// Footer styles:
|
|
378
|
+
'[&_[data-slot="footer"]]:col-span-full [&_[data-slot="footer"]]:font-light [&_[data-slot="footer"]]:text-xs [&_[data-slot="footer"]]:leading-6'
|
|
286
379
|
],
|
|
287
380
|
variants: {
|
|
288
381
|
/**
|
|
289
|
-
* The variant of the
|
|
290
|
-
* @default
|
|
382
|
+
* The variant of the alert
|
|
383
|
+
* @default info
|
|
291
384
|
*/ variant: {
|
|
292
|
-
|
|
385
|
+
info: 'border-[#1A7FA7] bg-sky-light',
|
|
386
|
+
success: 'border-[#0F9B6E] bg-mint-light',
|
|
387
|
+
warning: 'border-[#C57C13] bg-[#FFF2DE]',
|
|
388
|
+
danger: 'border-[#C0385D] bg-red-light'
|
|
389
|
+
}
|
|
390
|
+
},
|
|
391
|
+
defaultVariants: {
|
|
392
|
+
variant: 'info'
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
const Alertbox = ({ children, role, className, icon, variant = 'info', isDismissable = false, isDismissed, onDismiss, isExpandable })=>{
|
|
396
|
+
const Icon = icon ?? iconMap[variant];
|
|
397
|
+
const locale = _useLocale();
|
|
398
|
+
const id = useId();
|
|
399
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
|
400
|
+
const isCollapsed = isExpandable && !isExpanded;
|
|
401
|
+
const [isUncontrolledVisible, setIsUncontrolledVisible] = useState(true);
|
|
402
|
+
const isVisible = isDismissed !== undefined ? !isDismissed : isUncontrolledVisible;
|
|
403
|
+
if (!isVisible) return;
|
|
404
|
+
const close = ()=>{
|
|
405
|
+
setIsUncontrolledVisible(false);
|
|
406
|
+
if (onDismiss) onDismiss();
|
|
407
|
+
};
|
|
408
|
+
const isInDevMode = process.env.NODE_ENV !== 'production';
|
|
409
|
+
if (isInDevMode && onDismiss && !isDismissable) {
|
|
410
|
+
console.warn('Passing an `onDismiss` callback without setting the `isDismissable` prop to `true` will not have any effect.');
|
|
411
|
+
}
|
|
412
|
+
if (isInDevMode && !children) {
|
|
413
|
+
console.error('`No children was passed to the <AlertBox/>` component.');
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
const [firstChild, ...restChildren] = Children.toArray(children);
|
|
417
|
+
return /*#__PURE__*/ jsxs("div", {
|
|
418
|
+
className: alertVariants({
|
|
419
|
+
className,
|
|
420
|
+
variant
|
|
421
|
+
}),
|
|
422
|
+
// The role prop is required to force consumers to consider and choose the appropriate alertbox role.
|
|
423
|
+
// role="none" will not have any effect on a div, so it can be omitted.
|
|
424
|
+
role: role === 'none' ? undefined : role,
|
|
425
|
+
children: [
|
|
426
|
+
/*#__PURE__*/ jsx(Icon, {}),
|
|
427
|
+
firstChild,
|
|
428
|
+
isDismissable && /*#__PURE__*/ jsx("button", {
|
|
429
|
+
className: cx('-m-2 grid h-11 w-11 place-items-center rounded-xl', 'focus-visible:-outline-offset-8 cursor-pointer focus-visible:outline-focus'),
|
|
430
|
+
onClick: close,
|
|
431
|
+
"aria-label": translations$1.close[locale],
|
|
432
|
+
type: "button",
|
|
433
|
+
children: /*#__PURE__*/ jsx(Close, {})
|
|
434
|
+
}),
|
|
435
|
+
isExpandable && /*#__PURE__*/ jsxs("button", {
|
|
436
|
+
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:
|
|
437
|
+
'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'),
|
|
438
|
+
onClick: ()=>setIsExpanded((prevState)=>!prevState),
|
|
439
|
+
"aria-expanded": isExpanded,
|
|
440
|
+
"aria-controls": id,
|
|
441
|
+
type: "button",
|
|
442
|
+
children: [
|
|
443
|
+
isExpanded ? translations$1.showLess[locale] : translations$1.showMore[locale],
|
|
444
|
+
/*#__PURE__*/ jsx(ChevronDown, {
|
|
445
|
+
className: cx('transition-transform duration-150 motion-reduce:transition-none', isExpanded && 'rotate-180')
|
|
446
|
+
})
|
|
447
|
+
]
|
|
448
|
+
}),
|
|
449
|
+
restChildren?.length > 0 && /*#__PURE__*/ jsx("div", {
|
|
450
|
+
className: cx('col-span-full grid gap-y-4', isCollapsed && '[&>*:not([data-slot="footer"])]:hidden'),
|
|
451
|
+
id: id,
|
|
452
|
+
children: restChildren
|
|
453
|
+
})
|
|
454
|
+
]
|
|
455
|
+
});
|
|
456
|
+
};
|
|
457
|
+
|
|
458
|
+
const baseClassName = 'h-20 w-20 shrink-0 rounded-full';
|
|
459
|
+
const Avatar = ({ src, alt = '', className, onError, loading = 'lazy', ...rest })=>{
|
|
460
|
+
const [hasError, setHasError] = useState(false);
|
|
461
|
+
const hasValidImage = !hasError && src;
|
|
462
|
+
return hasValidImage ? /*#__PURE__*/ jsx("img", {
|
|
463
|
+
...rest,
|
|
464
|
+
src: src,
|
|
465
|
+
alt: alt,
|
|
466
|
+
loading: loading,
|
|
467
|
+
className: cx(className, baseClassName, 'object-cover'),
|
|
468
|
+
onError: (event)=>{
|
|
469
|
+
onError?.(event);
|
|
470
|
+
setHasError(true);
|
|
471
|
+
}
|
|
472
|
+
}) : /*#__PURE__*/ jsx("div", {
|
|
473
|
+
className: cx(className, baseClassName, 'grid place-items-center bg-gray-light text-gray-dark'),
|
|
474
|
+
children: /*#__PURE__*/ jsx(User, {
|
|
475
|
+
className: "scale-[2.25]"
|
|
476
|
+
})
|
|
477
|
+
});
|
|
478
|
+
};
|
|
479
|
+
|
|
480
|
+
function isLinkProps$1(props) {
|
|
481
|
+
return !!props.href;
|
|
482
|
+
}
|
|
483
|
+
function Backlink(props) {
|
|
484
|
+
const { className, style, children, withUnderline, ref, ...restProps } = props;
|
|
485
|
+
const _className = cx(className, 'group flex max-w-fit cursor-pointer items-center gap-3 rounded-md p-2.5 no-underline focus-visible:outline-focus');
|
|
486
|
+
const content = /*#__PURE__*/ jsxs(Fragment, {
|
|
487
|
+
children: [
|
|
488
|
+
/*#__PURE__*/ jsx(ChevronLeft, {
|
|
489
|
+
className: cx('-ml-[0.5em] group-hover:-translate-x-1 shrink-0 transition-transform duration-300')
|
|
490
|
+
}),
|
|
491
|
+
/*#__PURE__*/ jsx("span", {
|
|
492
|
+
children: /*#__PURE__*/ jsx("span", {
|
|
493
|
+
className: cx('border-transparent border-t-[1px] border-b-[1px] transition-colors duration-300', withUnderline ? 'border-b-black' : 'group-hover:border-b-black'),
|
|
494
|
+
children: children
|
|
495
|
+
})
|
|
496
|
+
})
|
|
497
|
+
]
|
|
498
|
+
});
|
|
499
|
+
if (isLinkProps$1(props)) {
|
|
500
|
+
return /*#__PURE__*/ jsx(Link, {
|
|
501
|
+
...restProps,
|
|
502
|
+
className: _className,
|
|
503
|
+
style: style,
|
|
504
|
+
ref: ref,
|
|
505
|
+
children: content
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
return /*#__PURE__*/ jsx(Button$1, {
|
|
509
|
+
...restProps,
|
|
510
|
+
className: _className,
|
|
511
|
+
style: style,
|
|
512
|
+
ref: ref,
|
|
513
|
+
children: content
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
const badgeVariants = cva({
|
|
518
|
+
base: [
|
|
519
|
+
'inline-flex w-fit items-center justify-center gap-1.5 rounded-lg [&_svg]:shrink-0'
|
|
520
|
+
],
|
|
521
|
+
variants: {
|
|
522
|
+
color: {
|
|
523
|
+
'gray-dark': 'bg-gray-dark text-white',
|
|
524
|
+
mint: 'bg-mint',
|
|
525
|
+
sky: 'bg-sky',
|
|
526
|
+
white: 'bg-white',
|
|
527
|
+
'blue-dark': 'bg-blue-dark text-white',
|
|
528
|
+
'green-dark': 'bg-green-dark text-white'
|
|
529
|
+
},
|
|
530
|
+
size: {
|
|
531
|
+
small: 'description px-2 py-0.5 [&_svg]:h-4 [&_svg]:w-4',
|
|
532
|
+
medium: 'description px-2.5 py-1.5 [&_svg]:h-4 [&_svg]:w-4',
|
|
533
|
+
large: 'paragraph px-3 py-2 [&_svg]:h-5 [&_svg]:w-5'
|
|
534
|
+
}
|
|
535
|
+
},
|
|
536
|
+
defaultVariants: {
|
|
537
|
+
size: 'medium'
|
|
538
|
+
}
|
|
539
|
+
});
|
|
540
|
+
function Badge(props) {
|
|
541
|
+
const { className: _className, color, size, ...restProps } = props;
|
|
542
|
+
const className = badgeVariants({
|
|
543
|
+
className: _className,
|
|
544
|
+
color,
|
|
545
|
+
size
|
|
546
|
+
});
|
|
547
|
+
return /*#__PURE__*/ jsx("span", {
|
|
548
|
+
className: className,
|
|
549
|
+
...restProps,
|
|
550
|
+
"data-slot": "badge"
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
function Breadcrumb(props) {
|
|
555
|
+
const { className, children, href, ...restProps } = props;
|
|
556
|
+
return /*#__PURE__*/ jsxs(Breadcrumb$1, {
|
|
557
|
+
className: cx(className, 'group flex items-center'),
|
|
558
|
+
...restProps,
|
|
559
|
+
children: [
|
|
560
|
+
href ? /*#__PURE__*/ jsx(Link, {
|
|
561
|
+
href: href,
|
|
562
|
+
// use outline instead of ring-3 for focus marker that can be offset without creating a white background between the focus marker and the element content
|
|
563
|
+
className: "rounded-xs focus-visible:outline-focus group-last:no-underline",
|
|
564
|
+
children: children
|
|
565
|
+
}) : children,
|
|
566
|
+
/*#__PURE__*/ jsx(ChevronRight, {
|
|
567
|
+
className: "px-1 group-last:hidden"
|
|
568
|
+
})
|
|
569
|
+
]
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
function Breadcrumbs(props) {
|
|
574
|
+
const { className, children, ...restProps } = props;
|
|
575
|
+
return /*#__PURE__*/ jsx(Breadcrumbs$1, {
|
|
576
|
+
...restProps,
|
|
577
|
+
className: cx(className, 'flex flex-wrap text-sm leading-6'),
|
|
578
|
+
children: children
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/**
|
|
583
|
+
* Figma: https://www.figma.com/file/9OvSg0ZXI5E1eQYi7AWiWn/Grunnmuren-2.0-%E2%94%82-Designsystem?node-id=30%3A2574&mode=dev
|
|
584
|
+
*/ const buttonVariants = cva({
|
|
585
|
+
base: [
|
|
586
|
+
'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'
|
|
587
|
+
],
|
|
588
|
+
variants: {
|
|
589
|
+
/**
|
|
590
|
+
* The variant of the button
|
|
591
|
+
* @default primary
|
|
592
|
+
*/ variant: {
|
|
593
|
+
primary: 'no-underline',
|
|
293
594
|
// 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
|
|
294
595
|
secondary: 'border-2 border-current no-underline hover:border-transparent',
|
|
295
596
|
tertiary: 'underline hover:no-underline'
|
|
@@ -372,7 +673,7 @@ const translations$1 = {
|
|
|
372
673
|
}
|
|
373
674
|
});
|
|
374
675
|
const ButtonContext = /*#__PURE__*/ createContext({});
|
|
375
|
-
function isLinkProps
|
|
676
|
+
function isLinkProps(props) {
|
|
376
677
|
return !!props.href;
|
|
377
678
|
}
|
|
378
679
|
function Button({ ref = null, ...props }) {
|
|
@@ -399,7 +700,7 @@ function Button({ ref = null, ...props }) {
|
|
|
399
700
|
})
|
|
400
701
|
]
|
|
401
702
|
}) : _children;
|
|
402
|
-
return isLinkProps
|
|
703
|
+
return isLinkProps(restProps) ? /*#__PURE__*/ jsx(Link, {
|
|
403
704
|
...restProps,
|
|
404
705
|
className: className,
|
|
405
706
|
ref: ref,
|
|
@@ -413,708 +714,7 @@ function Button({ ref = null, ...props }) {
|
|
|
413
714
|
});
|
|
414
715
|
}
|
|
415
716
|
|
|
416
|
-
const
|
|
417
|
-
const formFieldError = cx('w-fit bg-red-light px-2 py-1 text-red text-sm leading-6', 'group-data-[slot=file-upload]:rounded-lg');
|
|
418
|
-
const input = cva({
|
|
419
|
-
base: [
|
|
420
|
-
// All inputs should always have a white background (this also ensures that type="search" on Safri doesn't get a gray background)
|
|
421
|
-
'bg-white',
|
|
422
|
-
// Use box-content to enable auto width based on number of characters (size)
|
|
423
|
-
// Setting min-height to prevent the input from collapsing in Safari
|
|
424
|
-
// Combining these with a padding-y as base classes makes it easier to standardize the height (44px) of all inputs
|
|
425
|
-
'box-content min-h-6 py-2.5',
|
|
426
|
-
'rounded-md font-normal text-base leading-6 placeholder-[#727070] outline-hidden ring-1 ring-black',
|
|
427
|
-
// invalid styles
|
|
428
|
-
'group-data-invalid:ring-focus group-data-invalid:ring-red',
|
|
429
|
-
// Fix invisible ring on safari: https://github.com/tailwindlabs/tailwindcss.com/issues/1135
|
|
430
|
-
'appearance-none'
|
|
431
|
-
],
|
|
432
|
-
variants: {
|
|
433
|
-
// Focus rings. Can either be :focus or :focus-visible based on the needs of the particular component.
|
|
434
|
-
focusModifier: {
|
|
435
|
-
focus: 'focus:ring-focus group-data-invalid:focus:ring-3 group-data-invalid:focus:ring-red',
|
|
436
|
-
visible: 'data-focus-visible:ring-focus group-data-invalid:data-focus-visible:ring-3 group-data-invalid:data-focus-visible:ring-red'
|
|
437
|
-
},
|
|
438
|
-
isGrouped: {
|
|
439
|
-
false: 'px-3',
|
|
440
|
-
true: '!ring-0 flex-1'
|
|
441
|
-
}
|
|
442
|
-
},
|
|
443
|
-
defaultVariants: {
|
|
444
|
-
focusModifier: 'focus',
|
|
445
|
-
isGrouped: false
|
|
446
|
-
}
|
|
447
|
-
});
|
|
448
|
-
const inputGroup = cx([
|
|
449
|
-
'inline-flex items-center gap-3 overflow-hidden rounded-md bg-white px-3 text-base ring-1 ring-black focus-within:ring-focus',
|
|
450
|
-
'group-data-invalid:ring-focus group-data-invalid:ring-red group-data-invalid:focus-within:ring-3 group-data-invalid:focus-within:ring-red'
|
|
451
|
-
]);
|
|
452
|
-
const dropdown = {
|
|
453
|
-
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-sm data-entering:animate-in data-exiting:animate-out'),
|
|
454
|
-
// overflow-x-hidden is needed to prevent visible vertical scrollbars from overflowing the border radius of the popover
|
|
455
|
-
listbox: cx('max-h-[25rem] overflow-x-hidden text-sm outline-hidden'),
|
|
456
|
-
chevronIcon: cx('text-base transition-transform duration-150 group-data-open:rotate-180 motion-reduce:transition-none')
|
|
457
|
-
};
|
|
458
|
-
|
|
459
|
-
function ErrorMessage(props) {
|
|
460
|
-
const { children, className, ...restProps } = props;
|
|
461
|
-
return /*#__PURE__*/ jsx(Text, {
|
|
462
|
-
...restProps,
|
|
463
|
-
className: cx(className, formFieldError),
|
|
464
|
-
slot: "errorMessage",
|
|
465
|
-
children: children
|
|
466
|
-
});
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
const defaultClasses$1 = cx([
|
|
470
|
-
'group -mx-2.5 relative left-0 inline-flex max-w-fit cursor-pointer items-start gap-4 p-2.5 leading-7'
|
|
471
|
-
]);
|
|
472
|
-
// Pulling this out into it's own component. Will probably export it in the future
|
|
473
|
-
// so it can be used in other views, outside of an input of type checkbox, like in table rows.
|
|
474
|
-
function CheckmarkBox() {
|
|
475
|
-
return /*#__PURE__*/ jsx("span", {
|
|
476
|
-
className: cx([
|
|
477
|
-
'relative left-0 grid flex-none place-content-center rounded-sm border-2 border-black text-white',
|
|
478
|
-
// 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.
|
|
479
|
-
// For the ::before psuedo element the line height of the label is always 1em.
|
|
480
|
-
// When we know the height of the label we use the height of the radio to push it down to align with the label's first line
|
|
481
|
-
// TODO: 1.75 here is the unit less lineheight, altough we use 1.75rem as the line height, so there is a mismatch here. Revisit this when we've worked on typography in v2. Should this be a CSS custom property instead?
|
|
482
|
-
'mt-[calc((1em_*_1.75_-_24px)_/_2)] h-[24px] w-[24px]',
|
|
483
|
-
// selected
|
|
484
|
-
'group-data-selected:group-not-data-hovered:group-not-data-invalid:border-blue group-data-selected:group-not-data-hovered:group-not-data-invalid:bg-blue',
|
|
485
|
-
'group-data-selected:group-not-data-hovered:group-data-invalid:border-red group-data-selected:group-not-data-hovered:group-data-invalid:bg-red',
|
|
486
|
-
// focus
|
|
487
|
-
'group-data-focus-visible:outline-focus-offset',
|
|
488
|
-
// hovered
|
|
489
|
-
'group-data-hovered:group-data-invalid:bg-red-light',
|
|
490
|
-
'group-data-hovered:border-blue',
|
|
491
|
-
'group-data-hovered:bg-sky',
|
|
492
|
-
'group-data-hovered:group-data-selected:group-not-data-invalid:border-blue-dark',
|
|
493
|
-
'group-data-hovered:group-data-selected:group-not-data-invalid:bg-blue-dark',
|
|
494
|
-
// 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
|
|
495
|
-
// so we use an inner shadow of 1 px instead to pad the actual border
|
|
496
|
-
'group-data-invalid:border-red group-data-invalid:shadow-[inset_0_0_0_1px] group-data-invalid:shadow-red'
|
|
497
|
-
]),
|
|
498
|
-
children: /*#__PURE__*/ jsx(Check, {
|
|
499
|
-
className: "h-full w-full opacity-0 group-data-invalid:group-data-hovered:group-data-selected:text-red group-data-selected:opacity-100"
|
|
500
|
-
})
|
|
501
|
-
});
|
|
502
|
-
}
|
|
503
|
-
function Checkbox(props) {
|
|
504
|
-
const { children, className, description, errorMessage, isInvalid: _isInvalid, ...restProps } = props;
|
|
505
|
-
const id = useId();
|
|
506
|
-
const descriptionId = `desc${id}`;
|
|
507
|
-
const errorMessageId = `error${id}`;
|
|
508
|
-
const isInvalid = !!errorMessage || _isInvalid;
|
|
509
|
-
return /*#__PURE__*/ jsx("div", {
|
|
510
|
-
children: /*#__PURE__*/ jsxs(CheckboxContext.Provider, {
|
|
511
|
-
value: {
|
|
512
|
-
'aria-describedby': description ? descriptionId : undefined,
|
|
513
|
-
'aria-errormessage': errorMessage ? errorMessageId : undefined
|
|
514
|
-
},
|
|
515
|
-
children: [
|
|
516
|
-
/*#__PURE__*/ jsxs(Checkbox$1, {
|
|
517
|
-
...restProps,
|
|
518
|
-
className: cx(className, defaultClasses$1),
|
|
519
|
-
isInvalid: isInvalid,
|
|
520
|
-
children: [
|
|
521
|
-
/*#__PURE__*/ jsx(CheckmarkBox, {}),
|
|
522
|
-
children
|
|
523
|
-
]
|
|
524
|
-
}),
|
|
525
|
-
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 */}
|
|
526
|
-
/*#__PURE__*/ jsx("div", {
|
|
527
|
-
id: descriptionId,
|
|
528
|
-
slot: "description",
|
|
529
|
-
className: "description block",
|
|
530
|
-
children: description
|
|
531
|
-
}),
|
|
532
|
-
errorMessage && /*#__PURE__*/ jsx(ErrorMessage, {
|
|
533
|
-
className: "mt-2 block",
|
|
534
|
-
id: errorMessageId,
|
|
535
|
-
children: errorMessage
|
|
536
|
-
})
|
|
537
|
-
]
|
|
538
|
-
})
|
|
539
|
-
});
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
function Description(props) {
|
|
543
|
-
const { className, ...restProps } = props;
|
|
544
|
-
return /*#__PURE__*/ jsx(Text, {
|
|
545
|
-
...restProps,
|
|
546
|
-
className: cx(className, 'description'),
|
|
547
|
-
slot: "description"
|
|
548
|
-
});
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
/**
|
|
552
|
-
* This component handles renders a custom error message (if provided), otherwise it falls back to the browser's native validation.
|
|
553
|
-
* In other words, this handles controlled and uncontrolled form errors.
|
|
554
|
-
*/ function ErrorMessageOrFieldError({ errorMessage }) {
|
|
555
|
-
return errorMessage ? /*#__PURE__*/ jsx(ErrorMessage, {
|
|
556
|
-
children: errorMessage
|
|
557
|
-
}) : /*#__PURE__*/ jsx(FieldError, {
|
|
558
|
-
className: formFieldError
|
|
559
|
-
});
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
function Label(props) {
|
|
563
|
-
const { children, className, ...restProps } = props;
|
|
564
|
-
return /*#__PURE__*/ jsx(Label$1, {
|
|
565
|
-
className: cx(className, 'font-semibold leading-7'),
|
|
566
|
-
...restProps,
|
|
567
|
-
children: children
|
|
568
|
-
});
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
function CheckboxGroup(props) {
|
|
572
|
-
const { children, className, description, errorMessage, label, isRequired, isInvalid: _isInvalid, ...restProps } = props;
|
|
573
|
-
// the order of the conditions matter here, because providing a value for isInvalid makes the validation state "controlled",
|
|
574
|
-
// which will override any built in validation
|
|
575
|
-
const isInvalid = !!errorMessage || _isInvalid;
|
|
576
|
-
return /*#__PURE__*/ jsxs(CheckboxGroup$1, {
|
|
577
|
-
...restProps,
|
|
578
|
-
className: cx(className, 'flex flex-col gap-2'),
|
|
579
|
-
isInvalid: isInvalid,
|
|
580
|
-
isRequired: isRequired,
|
|
581
|
-
children: [
|
|
582
|
-
label && /*#__PURE__*/ jsx(Label, {
|
|
583
|
-
children: label
|
|
584
|
-
}),
|
|
585
|
-
description && /*#__PURE__*/ jsx(Description, {
|
|
586
|
-
children: description
|
|
587
|
-
}),
|
|
588
|
-
children,
|
|
589
|
-
/*#__PURE__*/ jsx(ErrorMessageOrFieldError, {
|
|
590
|
-
errorMessage: errorMessage
|
|
591
|
-
})
|
|
592
|
-
]
|
|
593
|
-
});
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
const ListBox = ({ className, ...restProps })=>/*#__PURE__*/ jsx(ListBox$1, {
|
|
597
|
-
...restProps,
|
|
598
|
-
className: cx(dropdown.listbox, className)
|
|
599
|
-
});
|
|
600
|
-
const ListBoxItem = (props)=>{
|
|
601
|
-
let textValue = props.textValue;
|
|
602
|
-
// When the ListBoxItem child isn't a string we have to set textValue for keyboard completion to work.
|
|
603
|
-
// Since we use a render function (to handle the selected state) the child is never a string.
|
|
604
|
-
// This condition adds back that behaviour
|
|
605
|
-
if (textValue == null && typeof props.children === 'string') {
|
|
606
|
-
textValue = props.children;
|
|
607
|
-
}
|
|
608
|
-
return /*#__PURE__*/ jsx(ListBoxItem$1, {
|
|
609
|
-
...props,
|
|
610
|
-
className: cx(props.className, 'flex cursor-pointer px-6 py-3 leading-6 outline-none data-focused:bg-sky-lightest'),
|
|
611
|
-
textValue: textValue,
|
|
612
|
-
children: ({ isSelected })=>/*#__PURE__*/ jsxs(Fragment, {
|
|
613
|
-
children: [
|
|
614
|
-
isSelected && /*#__PURE__*/ jsx(Check, {
|
|
615
|
-
className: "-ml-6 text-base"
|
|
616
|
-
}),
|
|
617
|
-
props.children
|
|
618
|
-
]
|
|
619
|
-
})
|
|
620
|
-
});
|
|
621
|
-
};
|
|
622
|
-
/**
|
|
623
|
-
* This component can be used to group items in a listbox
|
|
624
|
-
*/ const ListBoxSection = ({ className, ...restProps })=>/*#__PURE__*/ jsx(ListBoxSection$1, {
|
|
625
|
-
...restProps,
|
|
626
|
-
// The :not(:first-child) selector adds extra spacing to all the options, but not the section (group) headings
|
|
627
|
-
// This way we get the desired extra indent on all options within a group
|
|
628
|
-
className: cx(className, 'pb-1 [&>:not(:first-child)]:pl-10')
|
|
629
|
-
});
|
|
630
|
-
/**
|
|
631
|
-
* This component can be used to label grouped items in a `ListBoxSection` with a heading
|
|
632
|
-
*/ const ListBoxHeader = (props)=>/*#__PURE__*/ jsx(Header, {
|
|
633
|
-
...props,
|
|
634
|
-
className: cx(props.className, 'mx-6 cursor-default py-2 font-medium text-blue-dark leading-6')
|
|
635
|
-
});
|
|
636
|
-
|
|
637
|
-
function InputAddonDivider() {
|
|
638
|
-
return /*#__PURE__*/ jsx("span", {
|
|
639
|
-
className: "block h-6 w-px flex-none bg-black"
|
|
640
|
-
});
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
function Combobox(props) {
|
|
644
|
-
const { className, children, description, errorMessage, isPending, label, isInvalid: _isInvalid, ref, ...restProps } = props;
|
|
645
|
-
// the order of the conditions matter here, because providing a value for isInvalid makes the validation state "controlled",
|
|
646
|
-
// which will override any built in validation
|
|
647
|
-
const isInvalid = !!errorMessage || _isInvalid;
|
|
648
|
-
return /*#__PURE__*/ jsxs(ComboBox, {
|
|
649
|
-
...restProps,
|
|
650
|
-
className: cx(className, formField),
|
|
651
|
-
isInvalid: isInvalid,
|
|
652
|
-
children: [
|
|
653
|
-
label && /*#__PURE__*/ jsx(Label, {
|
|
654
|
-
children: label
|
|
655
|
-
}),
|
|
656
|
-
description && /*#__PURE__*/ jsx(Description, {
|
|
657
|
-
children: description
|
|
658
|
-
}),
|
|
659
|
-
/*#__PURE__*/ jsxs(Group, {
|
|
660
|
-
className: inputGroup,
|
|
661
|
-
children: [
|
|
662
|
-
/*#__PURE__*/ jsx(Input, {
|
|
663
|
-
className: input({
|
|
664
|
-
isGrouped: true
|
|
665
|
-
}),
|
|
666
|
-
ref: ref
|
|
667
|
-
}),
|
|
668
|
-
/*#__PURE__*/ jsx(Button$1, {
|
|
669
|
-
children: isPending ? /*#__PURE__*/ jsx(LoadingSpinner, {
|
|
670
|
-
className: "animate-spin"
|
|
671
|
-
}) : /*#__PURE__*/ jsx(ChevronDown, {
|
|
672
|
-
className: dropdown.chevronIcon
|
|
673
|
-
})
|
|
674
|
-
})
|
|
675
|
-
]
|
|
676
|
-
}),
|
|
677
|
-
/*#__PURE__*/ jsx(ErrorMessageOrFieldError, {
|
|
678
|
-
errorMessage: errorMessage
|
|
679
|
-
}),
|
|
680
|
-
/*#__PURE__*/ jsx(Popover, {
|
|
681
|
-
// FIXME: The trigger width doesn't include the padding of the group, so for now we have to apply this workaround.
|
|
682
|
-
// Also... the combobox border gets a pixel wider when focused, so we account for that as well when calculating the width
|
|
683
|
-
// and the offset.
|
|
684
|
-
// The input gutter should probably be moved to a theme variable instead of using the hardcoded value as here.
|
|
685
|
-
className: cx(dropdown.popover, 'min-w-[calc(var(--trigger-width)+26px)]'),
|
|
686
|
-
crossOffset: -13,
|
|
687
|
-
children: /*#__PURE__*/ jsx(ListBox, {
|
|
688
|
-
className: dropdown.listbox,
|
|
689
|
-
children: children
|
|
690
|
-
})
|
|
691
|
-
})
|
|
692
|
-
]
|
|
693
|
-
});
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
function RadioGroup(props) {
|
|
697
|
-
const { children, className, description, errorMessage, label, isRequired, isInvalid: _isInvalid, value, ...restProps } = props;
|
|
698
|
-
// the order of the conditions matter here, because providing a value for isInvalid makes the validation state "controlled",
|
|
699
|
-
// which will override any built in validation
|
|
700
|
-
const isInvalid = !!errorMessage || _isInvalid;
|
|
701
|
-
return /*#__PURE__*/ jsxs(RadioGroup$1, {
|
|
702
|
-
...restProps,
|
|
703
|
-
// Tabindex is set to -1 when the value is an empty string, which makes the radio input not focusable
|
|
704
|
-
value: value === '' ? undefined : value,
|
|
705
|
-
className: cx(className, 'flex flex-col gap-2'),
|
|
706
|
-
isInvalid: isInvalid,
|
|
707
|
-
isRequired: isRequired,
|
|
708
|
-
children: [
|
|
709
|
-
label && /*#__PURE__*/ jsx(Label, {
|
|
710
|
-
children: label
|
|
711
|
-
}),
|
|
712
|
-
description && /*#__PURE__*/ jsx(Description, {
|
|
713
|
-
children: description
|
|
714
|
-
}),
|
|
715
|
-
children,
|
|
716
|
-
/*#__PURE__*/ jsx(ErrorMessageOrFieldError, {
|
|
717
|
-
errorMessage: errorMessage
|
|
718
|
-
})
|
|
719
|
-
]
|
|
720
|
-
});
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
const defaultClasses = cx([
|
|
724
|
-
'-ml-2.5 relative inline-flex max-w-fit cursor-pointer items-start gap-4 py-2.5 pl-2.5 leading-7',
|
|
725
|
-
// the radio button itself
|
|
726
|
-
'before:flex-none before:rounded-full before:border-2 before:border-black',
|
|
727
|
-
// 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.
|
|
728
|
-
// For the ::before psuedo element the line height of the label is always 1em.
|
|
729
|
-
// When we know the height of the label we use the height of the radio to push it down to align with the label's first line
|
|
730
|
-
// TODO: 1.75 here is the unit less lineheight, altough we use 1.75rem as the line height, so there is a mismatch here. Revisit this when we've worked on typography in v2. Should this be a CSS custom property instead?
|
|
731
|
-
'before:mt-[calc((1em_*_1.75_-_24px)_/_2)] before:h-[24px] before:w-[24px]',
|
|
732
|
-
// selected
|
|
733
|
-
'data-selected:before:border-black data-selected:before:bg-blue data-selected:before:shadow-[inset_0_0_0_4px_rgb(255,255,255)]',
|
|
734
|
-
// hover
|
|
735
|
-
'data-hovered:data-selected:before:border-blue-dark data-hovered:data-invalid:before:bg-red-light data-hovered:data-selected:before:bg-blue-dark data-hovered:before:border-blue data-hovered:before:bg-sky',
|
|
736
|
-
// focus
|
|
737
|
-
'data-focus-visible:before:ring-focus-offset',
|
|
738
|
-
// 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
|
|
739
|
-
// so we use an inner outline to artifically pad the border
|
|
740
|
-
'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]'
|
|
741
|
-
]);
|
|
742
|
-
function Radio(props) {
|
|
743
|
-
const { children, className, description, ...restProps } = props;
|
|
744
|
-
return /*#__PURE__*/ jsx(Radio$1, {
|
|
745
|
-
...restProps,
|
|
746
|
-
className: cx(className, defaultClasses),
|
|
747
|
-
children: /*#__PURE__*/ jsxs("div", {
|
|
748
|
-
children: [
|
|
749
|
-
children,
|
|
750
|
-
description && /*#__PURE__*/ jsx(Description, {
|
|
751
|
-
className: "mt-2 block",
|
|
752
|
-
children: description
|
|
753
|
-
})
|
|
754
|
-
]
|
|
755
|
-
})
|
|
756
|
-
});
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
function Select(props) {
|
|
760
|
-
const { className, children, description, errorMessage, label, isInvalid: _isInvalid, ref, ...restProps } = props;
|
|
761
|
-
// the order of the conditions matter here, because providing a value for isInvalid makes the validation state "controlled",
|
|
762
|
-
// which will override any built in validation
|
|
763
|
-
const isInvalid = !!errorMessage || _isInvalid;
|
|
764
|
-
return /*#__PURE__*/ jsxs(Select$1, {
|
|
765
|
-
...restProps,
|
|
766
|
-
className: cx(className, formField),
|
|
767
|
-
isInvalid: isInvalid,
|
|
768
|
-
children: [
|
|
769
|
-
label && /*#__PURE__*/ jsx(Label, {
|
|
770
|
-
children: label
|
|
771
|
-
}),
|
|
772
|
-
description && /*#__PURE__*/ jsx(Description, {
|
|
773
|
-
children: description
|
|
774
|
-
}),
|
|
775
|
-
/*#__PURE__*/ jsxs(Button$1, {
|
|
776
|
-
className: cx(input({
|
|
777
|
-
focusModifier: 'visible'
|
|
778
|
-
}), // How to reuse placeholder text?
|
|
779
|
-
'inline-flex cursor-default items-center gap-2'),
|
|
780
|
-
// See https://github.com/adobe/react-spectrum/discussions/4792#discussioncomment-6492305
|
|
781
|
-
ref: ref,
|
|
782
|
-
children: [
|
|
783
|
-
/*#__PURE__*/ jsx(SelectValue, {
|
|
784
|
-
className: "flex-1 truncate text-left data-[placeholder]:text-[#727070]"
|
|
785
|
-
}),
|
|
786
|
-
/*#__PURE__*/ jsx(ChevronDown, {
|
|
787
|
-
className: dropdown.chevronIcon
|
|
788
|
-
})
|
|
789
|
-
]
|
|
790
|
-
}),
|
|
791
|
-
/*#__PURE__*/ jsx(ErrorMessageOrFieldError, {
|
|
792
|
-
errorMessage: errorMessage
|
|
793
|
-
}),
|
|
794
|
-
/*#__PURE__*/ jsx(Popover, {
|
|
795
|
-
className: dropdown.popover,
|
|
796
|
-
children: /*#__PURE__*/ jsx(ListBox, {
|
|
797
|
-
className: dropdown.listbox,
|
|
798
|
-
children: children
|
|
799
|
-
})
|
|
800
|
-
})
|
|
801
|
-
]
|
|
802
|
-
});
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
function TextArea(props) {
|
|
806
|
-
const { className, description, errorMessage, label, isInvalid: _isInvalid, rows, ref, ...restProps } = props;
|
|
807
|
-
const isInvalid = !!errorMessage || _isInvalid;
|
|
808
|
-
return /*#__PURE__*/ jsxs(TextField$1, {
|
|
809
|
-
...restProps,
|
|
810
|
-
className: cx(className, formField),
|
|
811
|
-
isInvalid: isInvalid,
|
|
812
|
-
children: [
|
|
813
|
-
label && /*#__PURE__*/ jsx(Label, {
|
|
814
|
-
children: label
|
|
815
|
-
}),
|
|
816
|
-
description && /*#__PURE__*/ jsx(Description, {
|
|
817
|
-
children: description
|
|
818
|
-
}),
|
|
819
|
-
/*#__PURE__*/ jsx(TextArea$1, {
|
|
820
|
-
className: input(),
|
|
821
|
-
rows: rows,
|
|
822
|
-
ref: ref
|
|
823
|
-
}),
|
|
824
|
-
/*#__PURE__*/ jsx(ErrorMessageOrFieldError, {
|
|
825
|
-
errorMessage: errorMessage
|
|
826
|
-
})
|
|
827
|
-
]
|
|
828
|
-
});
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
const inputVariants$1 = compose(input, cva({
|
|
832
|
-
base: '',
|
|
833
|
-
variants: {
|
|
834
|
-
textAlign: {
|
|
835
|
-
right: 'text-right',
|
|
836
|
-
left: ''
|
|
837
|
-
},
|
|
838
|
-
autoWidth: {
|
|
839
|
-
true: 'max-w-fit',
|
|
840
|
-
false: ''
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
}));
|
|
844
|
-
function TextField(props) {
|
|
845
|
-
const { className, description, errorMessage, label, leftAddon, isInvalid: _isInvalid, textAlign, rightAddon, withAddonDivider, size, ref, ...restProps } = props;
|
|
846
|
-
// the order of the conditions matter here, because providing a value for isInvalid makes the validation state "controlled",
|
|
847
|
-
// which will override any built in validation
|
|
848
|
-
const isInvalid = !!errorMessage || _isInvalid;
|
|
849
|
-
return /*#__PURE__*/ jsxs(TextField$1, {
|
|
850
|
-
...restProps,
|
|
851
|
-
className: cx(className, formField),
|
|
852
|
-
isInvalid: isInvalid,
|
|
853
|
-
children: [
|
|
854
|
-
label && /*#__PURE__*/ jsx(Label, {
|
|
855
|
-
children: label
|
|
856
|
-
}),
|
|
857
|
-
description && /*#__PURE__*/ jsx(Description, {
|
|
858
|
-
children: description
|
|
859
|
-
}),
|
|
860
|
-
leftAddon || rightAddon ? /*#__PURE__*/ jsxs(Group, {
|
|
861
|
-
className: cx(inputGroup, {
|
|
862
|
-
'w-fit': !!size
|
|
863
|
-
}),
|
|
864
|
-
children: [
|
|
865
|
-
leftAddon,
|
|
866
|
-
withAddonDivider && leftAddon && /*#__PURE__*/ jsx(InputAddonDivider, {}),
|
|
867
|
-
/*#__PURE__*/ jsx(Input, {
|
|
868
|
-
className: inputVariants$1({
|
|
869
|
-
textAlign,
|
|
870
|
-
isGrouped: true,
|
|
871
|
-
autoWidth: !!size
|
|
872
|
-
}),
|
|
873
|
-
ref: ref,
|
|
874
|
-
size: size
|
|
875
|
-
}),
|
|
876
|
-
withAddonDivider && rightAddon && /*#__PURE__*/ jsx(InputAddonDivider, {}),
|
|
877
|
-
rightAddon
|
|
878
|
-
]
|
|
879
|
-
}) : /*#__PURE__*/ jsx(Input, {
|
|
880
|
-
className: inputVariants$1({
|
|
881
|
-
textAlign,
|
|
882
|
-
autoWidth: !!size
|
|
883
|
-
}),
|
|
884
|
-
ref: ref,
|
|
885
|
-
size: size
|
|
886
|
-
}),
|
|
887
|
-
/*#__PURE__*/ jsx(ErrorMessageOrFieldError, {
|
|
888
|
-
errorMessage: errorMessage
|
|
889
|
-
})
|
|
890
|
-
]
|
|
891
|
-
});
|
|
892
|
-
}
|
|
893
|
-
|
|
894
|
-
// This component is based on a copy of ../textfield/TextField, refactoring is TBD: https://github.com/code-obos/grunnmuren/pull/722#issuecomment-1931478786
|
|
895
|
-
const inputVariants = compose(input, cva({
|
|
896
|
-
base: '',
|
|
897
|
-
variants: {
|
|
898
|
-
textAlign: {
|
|
899
|
-
right: 'text-right',
|
|
900
|
-
left: ''
|
|
901
|
-
},
|
|
902
|
-
autoWidth: {
|
|
903
|
-
true: 'max-w-fit',
|
|
904
|
-
false: ''
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
}));
|
|
908
|
-
function NumberField(props) {
|
|
909
|
-
const { className, description, errorMessage, label, leftAddon, isInvalid: _isInvalid, textAlign, rightAddon, withAddonDivider, size, ref, ...restProps } = props;
|
|
910
|
-
// the order of the conditions matter here, because providing a value for isInvalid makes the validation state "controlled",
|
|
911
|
-
// which will override any built in validation
|
|
912
|
-
const isInvalid = !!errorMessage || _isInvalid;
|
|
913
|
-
return /*#__PURE__*/ jsxs(NumberField$1, {
|
|
914
|
-
...restProps,
|
|
915
|
-
className: cx(className, formField),
|
|
916
|
-
isInvalid: isInvalid,
|
|
917
|
-
children: [
|
|
918
|
-
label && /*#__PURE__*/ jsx(Label, {
|
|
919
|
-
children: label
|
|
920
|
-
}),
|
|
921
|
-
description && /*#__PURE__*/ jsx(Description, {
|
|
922
|
-
children: description
|
|
923
|
-
}),
|
|
924
|
-
leftAddon || rightAddon ? /*#__PURE__*/ jsxs(Group, {
|
|
925
|
-
className: cx(inputGroup, {
|
|
926
|
-
'w-fit': !!size
|
|
927
|
-
}),
|
|
928
|
-
children: [
|
|
929
|
-
leftAddon,
|
|
930
|
-
withAddonDivider && leftAddon && /*#__PURE__*/ jsx(InputAddonDivider, {}),
|
|
931
|
-
/*#__PURE__*/ jsx(Input, {
|
|
932
|
-
className: inputVariants({
|
|
933
|
-
textAlign,
|
|
934
|
-
isGrouped: true,
|
|
935
|
-
autoWidth: !!size
|
|
936
|
-
}),
|
|
937
|
-
ref: ref,
|
|
938
|
-
size: size
|
|
939
|
-
}),
|
|
940
|
-
withAddonDivider && rightAddon && /*#__PURE__*/ jsx(InputAddonDivider, {}),
|
|
941
|
-
rightAddon
|
|
942
|
-
]
|
|
943
|
-
}) : /*#__PURE__*/ jsx(Input, {
|
|
944
|
-
className: inputVariants({
|
|
945
|
-
textAlign,
|
|
946
|
-
autoWidth: !!size
|
|
947
|
-
}),
|
|
948
|
-
ref: ref,
|
|
949
|
-
size: size
|
|
950
|
-
}),
|
|
951
|
-
/*#__PURE__*/ jsx(ErrorMessageOrFieldError, {
|
|
952
|
-
errorMessage: errorMessage
|
|
953
|
-
})
|
|
954
|
-
]
|
|
955
|
-
});
|
|
956
|
-
}
|
|
957
|
-
|
|
958
|
-
const iconMap = {
|
|
959
|
-
info: InfoCircle,
|
|
960
|
-
success: CheckCircle,
|
|
961
|
-
warning: Warning,
|
|
962
|
-
danger: Error
|
|
963
|
-
};
|
|
964
|
-
const alertVariants = cva({
|
|
965
|
-
base: [
|
|
966
|
-
'grid grid-cols-[auto_1fr_auto] items-center gap-2 rounded-md border-2 px-3 py-2',
|
|
967
|
-
// Heading styles:
|
|
968
|
-
'[&_[data-slot="heading"]]:font-medium [&_[data-slot="heading"]]:text-base [&_[data-slot="heading"]]:leading-7',
|
|
969
|
-
// Content styles:
|
|
970
|
-
'[&:has([data-slot="heading"])_[data-slot="content"]]:col-span-full [&_[data-slot="content"]]:text-sm [&_[data-slot="content"]]:leading-6',
|
|
971
|
-
// Footer styles:
|
|
972
|
-
'[&_[data-slot="footer"]]:col-span-full [&_[data-slot="footer"]]:font-light [&_[data-slot="footer"]]:text-xs [&_[data-slot="footer"]]:leading-6'
|
|
973
|
-
],
|
|
974
|
-
variants: {
|
|
975
|
-
/**
|
|
976
|
-
* The variant of the alert
|
|
977
|
-
* @default info
|
|
978
|
-
*/ variant: {
|
|
979
|
-
info: 'border-[#1A7FA7] bg-sky-light',
|
|
980
|
-
success: 'border-[#0F9B6E] bg-mint-light',
|
|
981
|
-
warning: 'border-[#C57C13] bg-[#FFF2DE]',
|
|
982
|
-
danger: 'border-[#C0385D] bg-red-light'
|
|
983
|
-
}
|
|
984
|
-
},
|
|
985
|
-
defaultVariants: {
|
|
986
|
-
variant: 'info'
|
|
987
|
-
}
|
|
988
|
-
});
|
|
989
|
-
const Alertbox = ({ children, role, className, icon, variant = 'info', isDismissable = false, isDismissed, onDismiss, isExpandable })=>{
|
|
990
|
-
const Icon = icon ?? iconMap[variant];
|
|
991
|
-
const locale = _useLocale();
|
|
992
|
-
const id = useId();
|
|
993
|
-
const [isExpanded, setIsExpanded] = useState(false);
|
|
994
|
-
const isCollapsed = isExpandable && !isExpanded;
|
|
995
|
-
const [isUncontrolledVisible, setIsUncontrolledVisible] = useState(true);
|
|
996
|
-
const isVisible = isDismissed !== undefined ? !isDismissed : isUncontrolledVisible;
|
|
997
|
-
if (!isVisible) return;
|
|
998
|
-
const close = ()=>{
|
|
999
|
-
setIsUncontrolledVisible(false);
|
|
1000
|
-
if (onDismiss) onDismiss();
|
|
1001
|
-
};
|
|
1002
|
-
const isInDevMode = process.env.NODE_ENV !== 'production';
|
|
1003
|
-
if (isInDevMode && onDismiss && !isDismissable) {
|
|
1004
|
-
console.warn('Passing an `onDismiss` callback without setting the `isDismissable` prop to `true` will not have any effect.');
|
|
1005
|
-
}
|
|
1006
|
-
if (isInDevMode && !children) {
|
|
1007
|
-
console.error('`No children was passed to the <AlertBox/>` component.');
|
|
1008
|
-
return;
|
|
1009
|
-
}
|
|
1010
|
-
const [firstChild, ...restChildren] = Children.toArray(children);
|
|
1011
|
-
return /*#__PURE__*/ jsxs("div", {
|
|
1012
|
-
className: alertVariants({
|
|
1013
|
-
className,
|
|
1014
|
-
variant
|
|
1015
|
-
}),
|
|
1016
|
-
// The role prop is required to force consumers to consider and choose the appropriate alertbox role.
|
|
1017
|
-
// role="none" will not have any effect on a div, so it can be omitted.
|
|
1018
|
-
role: role === 'none' ? undefined : role,
|
|
1019
|
-
children: [
|
|
1020
|
-
/*#__PURE__*/ jsx(Icon, {}),
|
|
1021
|
-
firstChild,
|
|
1022
|
-
isDismissable && /*#__PURE__*/ jsx("button", {
|
|
1023
|
-
className: cx('-m-2 grid h-11 w-11 place-items-center rounded-xl', 'focus-visible:-outline-offset-8 cursor-pointer focus-visible:outline-focus'),
|
|
1024
|
-
onClick: close,
|
|
1025
|
-
"aria-label": translations$1.close[locale],
|
|
1026
|
-
type: "button",
|
|
1027
|
-
children: /*#__PURE__*/ jsx(Close, {})
|
|
1028
|
-
}),
|
|
1029
|
-
isExpandable && /*#__PURE__*/ jsxs("button", {
|
|
1030
|
-
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:
|
|
1031
|
-
'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'),
|
|
1032
|
-
onClick: ()=>setIsExpanded((prevState)=>!prevState),
|
|
1033
|
-
"aria-expanded": isExpanded,
|
|
1034
|
-
"aria-controls": id,
|
|
1035
|
-
type: "button",
|
|
1036
|
-
children: [
|
|
1037
|
-
isExpanded ? translations$1.showLess[locale] : translations$1.showMore[locale],
|
|
1038
|
-
/*#__PURE__*/ jsx(ChevronDown, {
|
|
1039
|
-
className: cx('transition-transform duration-150 motion-reduce:transition-none', isExpanded && 'rotate-180')
|
|
1040
|
-
})
|
|
1041
|
-
]
|
|
1042
|
-
}),
|
|
1043
|
-
restChildren?.length > 0 && /*#__PURE__*/ jsx("div", {
|
|
1044
|
-
className: cx('col-span-full grid gap-y-4', isCollapsed && '[&>*:not([data-slot="footer"])]:hidden'),
|
|
1045
|
-
id: id,
|
|
1046
|
-
children: restChildren
|
|
1047
|
-
})
|
|
1048
|
-
]
|
|
1049
|
-
});
|
|
1050
|
-
};
|
|
1051
|
-
|
|
1052
|
-
function Breadcrumbs(props) {
|
|
1053
|
-
const { className, children, ...restProps } = props;
|
|
1054
|
-
return /*#__PURE__*/ jsx(Breadcrumbs$1, {
|
|
1055
|
-
...restProps,
|
|
1056
|
-
className: cx(className, 'flex flex-wrap text-sm leading-6'),
|
|
1057
|
-
children: children
|
|
1058
|
-
});
|
|
1059
|
-
}
|
|
1060
|
-
|
|
1061
|
-
function Breadcrumb(props) {
|
|
1062
|
-
const { className, children, href, ...restProps } = props;
|
|
1063
|
-
return /*#__PURE__*/ jsxs(Breadcrumb$1, {
|
|
1064
|
-
className: cx(className, 'group flex items-center'),
|
|
1065
|
-
...restProps,
|
|
1066
|
-
children: [
|
|
1067
|
-
href ? /*#__PURE__*/ jsx(Link, {
|
|
1068
|
-
href: href,
|
|
1069
|
-
// use outline instead of ring-3 for focus marker that can be offset without creating a white background between the focus marker and the element content
|
|
1070
|
-
className: "rounded-xs focus-visible:outline-focus group-last:no-underline",
|
|
1071
|
-
children: children
|
|
1072
|
-
}) : children,
|
|
1073
|
-
/*#__PURE__*/ jsx(ChevronRight, {
|
|
1074
|
-
className: "px-1 group-last:hidden"
|
|
1075
|
-
})
|
|
1076
|
-
]
|
|
1077
|
-
});
|
|
1078
|
-
}
|
|
1079
|
-
|
|
1080
|
-
function isLinkProps(props) {
|
|
1081
|
-
return !!props.href;
|
|
1082
|
-
}
|
|
1083
|
-
function Backlink(props) {
|
|
1084
|
-
const { className, style, children, withUnderline, ref, ...restProps } = props;
|
|
1085
|
-
const _className = cx(className, 'group flex max-w-fit cursor-pointer items-center gap-3 rounded-md p-2.5 no-underline focus-visible:outline-focus');
|
|
1086
|
-
const content = /*#__PURE__*/ jsxs(Fragment, {
|
|
1087
|
-
children: [
|
|
1088
|
-
/*#__PURE__*/ jsx(ChevronLeft, {
|
|
1089
|
-
className: cx('-ml-[0.5em] group-hover:-translate-x-1 shrink-0 transition-transform duration-300')
|
|
1090
|
-
}),
|
|
1091
|
-
/*#__PURE__*/ jsx("span", {
|
|
1092
|
-
children: /*#__PURE__*/ jsx("span", {
|
|
1093
|
-
className: cx('border-transparent border-t-[1px] border-b-[1px] transition-colors duration-300', withUnderline ? 'border-b-black' : 'group-hover:border-b-black'),
|
|
1094
|
-
children: children
|
|
1095
|
-
})
|
|
1096
|
-
})
|
|
1097
|
-
]
|
|
1098
|
-
});
|
|
1099
|
-
if (isLinkProps(props)) {
|
|
1100
|
-
return /*#__PURE__*/ jsx(Link, {
|
|
1101
|
-
...restProps,
|
|
1102
|
-
className: _className,
|
|
1103
|
-
style: style,
|
|
1104
|
-
ref: ref,
|
|
1105
|
-
children: content
|
|
1106
|
-
});
|
|
1107
|
-
}
|
|
1108
|
-
return /*#__PURE__*/ jsx(Button$1, {
|
|
1109
|
-
...restProps,
|
|
1110
|
-
className: _className,
|
|
1111
|
-
style: style,
|
|
1112
|
-
ref: ref,
|
|
1113
|
-
children: content
|
|
1114
|
-
});
|
|
1115
|
-
}
|
|
1116
|
-
|
|
1117
|
-
const cardVariants = cva({
|
|
717
|
+
const cardVariants = cva({
|
|
1118
718
|
base: [
|
|
1119
719
|
'group/card',
|
|
1120
720
|
'rounded-[inherit]',
|
|
@@ -1339,259 +939,548 @@ const cardLinkVariants = cva({
|
|
|
1339
939
|
});
|
|
1340
940
|
};
|
|
1341
941
|
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
const
|
|
1348
|
-
|
|
1349
|
-
|
|
942
|
+
const Carousel = ({ className, children, onChange, ...rest })=>{
|
|
943
|
+
const ref = useRef(null);
|
|
944
|
+
const locale = _useLocale();
|
|
945
|
+
const { previous, next } = translations$1;
|
|
946
|
+
const [scrollTargetIndex, setScrollTargetIndex] = useState(0);
|
|
947
|
+
const isScrollingProgrammatically = useRef(false);
|
|
948
|
+
const scrollTimeoutRef = useRef(null);
|
|
949
|
+
const scrollQueue = useRef([]);
|
|
950
|
+
const [hasReachedScrollStart, setHasReachedScrollStart] = useState(scrollTargetIndex === 0);
|
|
951
|
+
const [hasReachedScrollEnd, setHasReachedScrollEnd] = useState(!ref.current || ref.current.children.length - 1 === scrollTargetIndex);
|
|
952
|
+
useEffect(()=>{
|
|
953
|
+
setHasReachedScrollStart(scrollTargetIndex === 0);
|
|
954
|
+
setHasReachedScrollEnd(!ref.current || ref.current.children.length - 1 === scrollTargetIndex);
|
|
955
|
+
}, [
|
|
956
|
+
scrollTargetIndex
|
|
957
|
+
]);
|
|
958
|
+
// Keep track of the previous index to determine if the user is scrolling forward or backward
|
|
959
|
+
// This is used to determine which callback to call (onPrev or onNext)
|
|
960
|
+
const prevIndex = useRef(0);
|
|
961
|
+
// Processes the next scroll action in the queue, if any
|
|
962
|
+
// All clicks on the prev/next buttons are queued while a programmatic scroll is in progress
|
|
963
|
+
// This is to ensure that rapid clicks on the buttons do not cause janky scrolling behavior
|
|
964
|
+
// while still a snappy response to user clicks
|
|
965
|
+
const processQueue = ()=>{
|
|
966
|
+
if (scrollQueue.current.length > 0 && !isScrollingProgrammatically.current) {
|
|
967
|
+
const nextIndex = scrollQueue.current?.shift();
|
|
968
|
+
if (nextIndex !== undefined) {
|
|
969
|
+
setScrollTargetIndex(nextIndex);
|
|
970
|
+
}
|
|
971
|
+
}
|
|
1350
972
|
};
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
}
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
973
|
+
// Handle scrolling when user clicks the arrow icons
|
|
974
|
+
useUpdateEffect(()=>{
|
|
975
|
+
if (!ref.current) {
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
978
|
+
if (scrollTimeoutRef.current) {
|
|
979
|
+
clearTimeout(scrollTimeoutRef.current);
|
|
980
|
+
}
|
|
981
|
+
isScrollingProgrammatically.current = true;
|
|
982
|
+
ref.current.children[scrollTargetIndex]?.scrollIntoView({
|
|
983
|
+
behavior: 'smooth',
|
|
984
|
+
inline: 'start',
|
|
985
|
+
block: 'nearest'
|
|
986
|
+
});
|
|
987
|
+
if (prevIndex.current !== scrollTargetIndex && onChange) {
|
|
988
|
+
onChange({
|
|
989
|
+
index: scrollTargetIndex,
|
|
990
|
+
id: ref.current.children[scrollTargetIndex]?.id,
|
|
991
|
+
prevIndex: prevIndex.current,
|
|
992
|
+
prevId: ref.current.children[prevIndex.current]?.id
|
|
993
|
+
});
|
|
994
|
+
}
|
|
995
|
+
prevIndex.current = scrollTargetIndex;
|
|
996
|
+
scrollTimeoutRef.current = setTimeout(()=>{
|
|
997
|
+
isScrollingProgrammatically.current = false;
|
|
998
|
+
scrollTimeoutRef.current = null;
|
|
999
|
+
processQueue(); // Process any queued scrolls
|
|
1000
|
+
}, 500);
|
|
1001
|
+
}, [
|
|
1002
|
+
scrollTargetIndex
|
|
1003
|
+
]);
|
|
1004
|
+
// Clean up timeout on unmount
|
|
1365
1005
|
useEffect(()=>{
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1006
|
+
return ()=>{
|
|
1007
|
+
if (scrollTimeoutRef.current) {
|
|
1008
|
+
clearTimeout(scrollTimeoutRef.current);
|
|
1009
|
+
}
|
|
1010
|
+
};
|
|
1370
1011
|
}, []);
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1012
|
+
const onScroll = useDebouncedCallback((event)=>{
|
|
1013
|
+
// Ignore scroll events when we're programmatically scrolling
|
|
1014
|
+
if (isScrollingProgrammatically.current) {
|
|
1015
|
+
return;
|
|
1016
|
+
}
|
|
1017
|
+
const target = event.target;
|
|
1018
|
+
const containerRect = target.getBoundingClientRect();
|
|
1019
|
+
// Calculate the index of the item that is currently in view
|
|
1020
|
+
const newScrollTargetIndex = Array.from(target.children).findIndex((child)=>{
|
|
1021
|
+
const rect = child.getBoundingClientRect();
|
|
1022
|
+
// Check if the item is more than 50% visible within the container
|
|
1023
|
+
const visibleWidth = Math.min(rect.right, containerRect.right) - Math.max(rect.left, containerRect.left);
|
|
1024
|
+
const itemWidth = rect.width;
|
|
1025
|
+
return visibleWidth / itemWidth > 0.5;
|
|
1026
|
+
});
|
|
1027
|
+
if (newScrollTargetIndex !== -1 && newScrollTargetIndex !== scrollTargetIndex) {
|
|
1028
|
+
if (onChange) {
|
|
1029
|
+
onChange({
|
|
1030
|
+
index: newScrollTargetIndex,
|
|
1031
|
+
id: target.children[newScrollTargetIndex]?.id,
|
|
1032
|
+
prevIndex: prevIndex.current,
|
|
1033
|
+
prevId: target.children[prevIndex.current]?.id
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
1036
|
+
// Update the index and prevIndex
|
|
1037
|
+
setScrollTargetIndex(newScrollTargetIndex);
|
|
1038
|
+
prevIndex.current = newScrollTargetIndex;
|
|
1039
|
+
}
|
|
1040
|
+
}, 150);
|
|
1041
|
+
const handlePrevious = ()=>{
|
|
1042
|
+
const targetIndex = scrollTargetIndex - 1;
|
|
1043
|
+
if (targetIndex < 0) return;
|
|
1044
|
+
if (isScrollingProgrammatically.current) {
|
|
1045
|
+
// If we're already scrolling, queue this action
|
|
1046
|
+
scrollQueue.current = [
|
|
1047
|
+
targetIndex
|
|
1048
|
+
];
|
|
1379
1049
|
} else {
|
|
1380
|
-
|
|
1381
|
-
setIsPlaying(false);
|
|
1050
|
+
setScrollTargetIndex(targetIndex);
|
|
1382
1051
|
}
|
|
1383
|
-
}
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1052
|
+
};
|
|
1053
|
+
const handleNext = ()=>{
|
|
1054
|
+
if (!ref.current) return;
|
|
1055
|
+
const targetIndex = scrollTargetIndex + 1;
|
|
1056
|
+
if (targetIndex >= ref.current.children.length) return;
|
|
1057
|
+
if (isScrollingProgrammatically.current) {
|
|
1058
|
+
// If we're already scrolling, queue this action
|
|
1059
|
+
scrollQueue.current = [
|
|
1060
|
+
targetIndex
|
|
1061
|
+
];
|
|
1062
|
+
} else {
|
|
1063
|
+
setScrollTargetIndex(targetIndex);
|
|
1064
|
+
}
|
|
1065
|
+
};
|
|
1066
|
+
return /*#__PURE__*/ jsx("div", {
|
|
1067
|
+
"data-slot": "carousel",
|
|
1068
|
+
children: /*#__PURE__*/ jsx(Provider, {
|
|
1069
|
+
values: [
|
|
1070
|
+
[
|
|
1071
|
+
CarouselItemsContext,
|
|
1072
|
+
{
|
|
1073
|
+
ref,
|
|
1074
|
+
onScroll
|
|
1404
1075
|
}
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1076
|
+
],
|
|
1077
|
+
[
|
|
1078
|
+
ButtonContext,
|
|
1079
|
+
{
|
|
1080
|
+
slots: {
|
|
1081
|
+
[DEFAULT_SLOT]: {},
|
|
1082
|
+
prev: {
|
|
1083
|
+
'aria-label': previous[locale],
|
|
1084
|
+
onPress: handlePrevious
|
|
1085
|
+
},
|
|
1086
|
+
next: {
|
|
1087
|
+
isIconOnly: true,
|
|
1088
|
+
'aria-label': next[locale],
|
|
1089
|
+
onPress: handleNext
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
]
|
|
1094
|
+
],
|
|
1095
|
+
children: /*#__PURE__*/ jsxs("div", {
|
|
1096
|
+
...rest,
|
|
1097
|
+
className: cx(className, 'relative rounded-3xl', // If any <CarouselItems/> (the scroll-snap container) or <VideoLoop/> component is focused, apply custom focus styles around the carousel, this makes ensures that the focus outline is visible around the carousel in all cases
|
|
1098
|
+
'[&:has([data-slot="carousel-items"]:focus-visible,[data-slot="video-loop-button"]:focus-visible)]:outline-focus', '[&:has([data-slot="carousel-items"]:focus-visible,[data-slot="video-loop-button"]:focus-visible)]:outline-focus-offset', // Unset the default focus outline for potential video loop buttons, as it interferes with the custom focus styles for the carousel
|
|
1099
|
+
'**:data-[slot="video-loop-button"]:focus-visible:outline-none'),
|
|
1100
|
+
children: [
|
|
1101
|
+
children,
|
|
1102
|
+
/*#__PURE__*/ jsxs(_CarouselControls, {
|
|
1103
|
+
children: [
|
|
1104
|
+
/*#__PURE__*/ jsx(Button, {
|
|
1105
|
+
isIconOnly: true,
|
|
1106
|
+
slot: "prev",
|
|
1107
|
+
variant: "primary",
|
|
1108
|
+
color: "white",
|
|
1109
|
+
className: cx('group/carousel-previous', hasReachedScrollStart && 'invisible'),
|
|
1110
|
+
children: /*#__PURE__*/ jsx(ChevronLeft, {
|
|
1111
|
+
className: "group-hover/carousel-previous:motion-safe:-translate-x-1 transition-transform"
|
|
1112
|
+
})
|
|
1113
|
+
}),
|
|
1114
|
+
/*#__PURE__*/ jsx(Button, {
|
|
1115
|
+
isIconOnly: true,
|
|
1116
|
+
slot: "next",
|
|
1117
|
+
variant: "primary",
|
|
1118
|
+
color: "white",
|
|
1119
|
+
className: cx('group/carousel-next', hasReachedScrollEnd && 'invisible'),
|
|
1120
|
+
children: /*#__PURE__*/ jsx(ChevronRight, {
|
|
1121
|
+
className: "transition-transform group-hover/carousel-next:motion-safe:translate-x-1"
|
|
1122
|
+
})
|
|
1123
|
+
})
|
|
1124
|
+
]
|
|
1125
|
+
})
|
|
1126
|
+
]
|
|
1127
|
+
})
|
|
1128
|
+
})
|
|
1129
|
+
});
|
|
1130
|
+
};
|
|
1131
|
+
/**
|
|
1132
|
+
* This is internal for now, but we will expose it in the future when we support more flexible positioning of prev/next and other actions.
|
|
1133
|
+
* It is used to render the prev/next buttons in the carousel for now.
|
|
1134
|
+
*/ const _CarouselControls = ({ children, className })=>/*#__PURE__*/ jsx("div", {
|
|
1135
|
+
className: cx(className, 'absolute right-6 bottom-6 flex gap-x-2', // Make it easier to position in full-bleed hero variants (these style have no other side effects)
|
|
1136
|
+
'items-end *:h-fit'),
|
|
1137
|
+
"data-slot": "carousel-controls",
|
|
1138
|
+
children: children
|
|
1139
|
+
});
|
|
1140
|
+
const CarouselItemsContext = /*#__PURE__*/ createContext({
|
|
1141
|
+
ref: null
|
|
1142
|
+
});
|
|
1143
|
+
const CarouselItems = ({ className, children })=>{
|
|
1144
|
+
const handleKeyDown = (event)=>{
|
|
1145
|
+
// Prevent default behavior when holding down arrow keys (when repeat is true)
|
|
1146
|
+
// The default behavior in scroll snapping causes a staggering scroll effect that feels janky
|
|
1147
|
+
if (event.repeat && (event.key === 'ArrowLeft' || event.key === 'ArrowRight')) {
|
|
1148
|
+
event.preventDefault();
|
|
1149
|
+
}
|
|
1150
|
+
};
|
|
1151
|
+
return /*#__PURE__*/ jsx(CarouselItemsContext.Consumer, {
|
|
1152
|
+
children: ({ ref, onScroll })=>// biome-ignore lint/a11y/noStaticElementInteractions: The keydown handler is only to prevent undesired scrolling behavior when using the arrow keys
|
|
1153
|
+
/*#__PURE__*/ jsx("div", {
|
|
1154
|
+
"data-slot": "carousel-items",
|
|
1155
|
+
className: cx(className, [
|
|
1156
|
+
'scrollbar-hidden',
|
|
1157
|
+
'flex',
|
|
1158
|
+
'snap-x',
|
|
1159
|
+
'snap-mandatory',
|
|
1160
|
+
'overflow-x-auto',
|
|
1161
|
+
'outline-none',
|
|
1162
|
+
'rounded-[inherit]'
|
|
1422
1163
|
]),
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
}),
|
|
1428
|
-
alt && /*#__PURE__*/ jsx("p", {
|
|
1429
|
-
className: "sr-only",
|
|
1430
|
-
children: alt
|
|
1164
|
+
ref: ref,
|
|
1165
|
+
onScroll: onScroll,
|
|
1166
|
+
onKeyDown: handleKeyDown,
|
|
1167
|
+
children: children
|
|
1431
1168
|
})
|
|
1432
|
-
|
|
1169
|
+
});
|
|
1170
|
+
};
|
|
1171
|
+
const CarouselItem = ({ className, children, id })=>{
|
|
1172
|
+
return /*#__PURE__*/ jsx("div", {
|
|
1173
|
+
className: cx(className, 'shrink-0 basis-full snap-start'),
|
|
1174
|
+
"data-slot": "carousel-item",
|
|
1175
|
+
id: id,
|
|
1176
|
+
children: /*#__PURE__*/ jsx(Provider, {
|
|
1177
|
+
values: [
|
|
1178
|
+
[
|
|
1179
|
+
MediaContext,
|
|
1180
|
+
{
|
|
1181
|
+
fit: 'cover',
|
|
1182
|
+
className: cx('data-[fit="contain"]:bg-blue-dark', '*:h-full *:w-full', 'aspect-square max-sm:data-[fit="contain"]:*:object-cover sm:aspect-4/3 md:aspect-3/2 lg:aspect-2/1')
|
|
1183
|
+
}
|
|
1184
|
+
]
|
|
1185
|
+
],
|
|
1186
|
+
children: children
|
|
1187
|
+
})
|
|
1433
1188
|
});
|
|
1434
1189
|
};
|
|
1435
1190
|
|
|
1436
|
-
const
|
|
1191
|
+
const formField = cx('group flex flex-col gap-2');
|
|
1192
|
+
const formFieldError = cx('w-fit bg-red-light px-2 py-1 text-red text-sm leading-6', 'group-data-[slot=file-upload]:rounded-lg');
|
|
1193
|
+
const input = cva({
|
|
1437
1194
|
base: [
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1195
|
+
// All inputs should always have a white background (this also ensures that type="search" on Safri doesn't get a gray background)
|
|
1196
|
+
'bg-white',
|
|
1197
|
+
// Use box-content to enable auto width based on number of characters (size)
|
|
1198
|
+
// Setting min-height to prevent the input from collapsing in Safari
|
|
1199
|
+
// Combining these with a padding-y as base classes makes it easier to standardize the height (44px) of all inputs
|
|
1200
|
+
'box-content min-h-6 py-2.5',
|
|
1201
|
+
'rounded-md font-normal text-base leading-6 placeholder-[#727070] outline-hidden ring-1 ring-black',
|
|
1202
|
+
// invalid styles
|
|
1203
|
+
'group-data-invalid:ring-focus group-data-invalid:ring-red',
|
|
1204
|
+
// Fix invisible ring on safari: https://github.com/tailwindlabs/tailwindcss.com/issues/1135
|
|
1205
|
+
'appearance-none'
|
|
1441
1206
|
],
|
|
1442
1207
|
variants: {
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1208
|
+
// Focus rings. Can either be :focus or :focus-visible based on the needs of the particular component.
|
|
1209
|
+
focusModifier: {
|
|
1210
|
+
focus: 'focus:ring-focus group-data-invalid:focus:ring-3 group-data-invalid:focus:ring-red',
|
|
1211
|
+
visible: 'data-focus-visible:ring-focus group-data-invalid:data-focus-visible:ring-3 group-data-invalid:data-focus-visible:ring-red'
|
|
1446
1212
|
},
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
*/ isIconOnly: {
|
|
1451
|
-
true: '[&>svg]:h-7 [&>svg]:w-7',
|
|
1452
|
-
false: 'gap-2.5'
|
|
1213
|
+
isGrouped: {
|
|
1214
|
+
false: 'px-3',
|
|
1215
|
+
true: '!ring-0 flex-1'
|
|
1453
1216
|
}
|
|
1454
1217
|
},
|
|
1455
1218
|
defaultVariants: {
|
|
1456
|
-
|
|
1457
|
-
|
|
1219
|
+
focusModifier: 'focus',
|
|
1220
|
+
isGrouped: false
|
|
1458
1221
|
}
|
|
1459
1222
|
});
|
|
1460
|
-
const
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1223
|
+
const inputGroup = cx([
|
|
1224
|
+
'inline-flex items-center gap-3 overflow-hidden rounded-md bg-white px-3 text-base ring-1 ring-black focus-within:ring-focus',
|
|
1225
|
+
'group-data-invalid:ring-focus group-data-invalid:ring-red group-data-invalid:focus-within:ring-3 group-data-invalid:focus-within:ring-red'
|
|
1226
|
+
]);
|
|
1227
|
+
const dropdown = {
|
|
1228
|
+
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-sm data-entering:animate-in data-exiting:animate-out'),
|
|
1229
|
+
// overflow-x-hidden is needed to prevent visible vertical scrollbars from overflowing the border radius of the popover
|
|
1230
|
+
listbox: cx('max-h-[25rem] overflow-x-hidden text-sm outline-hidden'),
|
|
1231
|
+
chevronIcon: cx('text-base transition-transform duration-150 group-data-open:rotate-180 motion-reduce:transition-none')
|
|
1232
|
+
};
|
|
1233
|
+
|
|
1234
|
+
function ErrorMessage(props) {
|
|
1235
|
+
const { children, className, ...restProps } = props;
|
|
1236
|
+
return /*#__PURE__*/ jsx(Text, {
|
|
1237
|
+
...restProps,
|
|
1238
|
+
className: cx(className, formFieldError),
|
|
1239
|
+
slot: "errorMessage",
|
|
1240
|
+
children: children
|
|
1241
|
+
});
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
const defaultClasses$1 = cx([
|
|
1245
|
+
'group -mx-2.5 relative left-0 inline-flex max-w-fit cursor-pointer items-start gap-4 p-2.5 leading-7'
|
|
1246
|
+
]);
|
|
1247
|
+
// Pulling this out into it's own component. Will probably export it in the future
|
|
1248
|
+
// so it can be used in other views, outside of an input of type checkbox, like in table rows.
|
|
1249
|
+
function CheckmarkBox() {
|
|
1250
|
+
return /*#__PURE__*/ jsx("span", {
|
|
1251
|
+
className: cx([
|
|
1252
|
+
'relative left-0 grid flex-none place-content-center rounded-sm border-2 border-black text-white',
|
|
1253
|
+
// 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.
|
|
1254
|
+
// For the ::before psuedo element the line height of the label is always 1em.
|
|
1255
|
+
// When we know the height of the label we use the height of the radio to push it down to align with the label's first line
|
|
1256
|
+
// TODO: 1.75 here is the unit less lineheight, altough we use 1.75rem as the line height, so there is a mismatch here. Revisit this when we've worked on typography in v2. Should this be a CSS custom property instead?
|
|
1257
|
+
'mt-[calc((1em_*_1.75_-_24px)_/_2)] h-[24px] w-[24px]',
|
|
1258
|
+
// selected
|
|
1259
|
+
'group-data-selected:group-not-data-hovered:group-not-data-invalid:border-blue group-data-selected:group-not-data-hovered:group-not-data-invalid:bg-blue',
|
|
1260
|
+
'group-data-selected:group-not-data-hovered:group-data-invalid:border-red group-data-selected:group-not-data-hovered:group-data-invalid:bg-red',
|
|
1261
|
+
// focus
|
|
1262
|
+
'group-data-focus-visible:outline-focus-offset',
|
|
1263
|
+
// hovered
|
|
1264
|
+
'group-data-hovered:group-data-invalid:bg-red-light',
|
|
1265
|
+
'group-data-hovered:border-blue',
|
|
1266
|
+
'group-data-hovered:bg-sky',
|
|
1267
|
+
'group-data-hovered:group-data-selected:group-not-data-invalid:border-blue-dark',
|
|
1268
|
+
'group-data-hovered:group-data-selected:group-not-data-invalid:bg-blue-dark',
|
|
1269
|
+
// 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
|
|
1270
|
+
// so we use an inner shadow of 1 px instead to pad the actual border
|
|
1271
|
+
'group-data-invalid:border-red group-data-invalid:shadow-[inset_0_0_0_1px] group-data-invalid:shadow-red'
|
|
1272
|
+
]),
|
|
1273
|
+
children: /*#__PURE__*/ jsx(Check, {
|
|
1274
|
+
className: "h-full w-full opacity-0 group-data-invalid:group-data-hovered:group-data-selected:text-red group-data-selected:opacity-100"
|
|
1275
|
+
})
|
|
1276
|
+
});
|
|
1277
|
+
}
|
|
1278
|
+
function Checkbox(props) {
|
|
1279
|
+
const { children, className, description, errorMessage, isInvalid: _isInvalid, ...restProps } = props;
|
|
1280
|
+
const id = useId();
|
|
1281
|
+
const descriptionId = `desc${id}`;
|
|
1282
|
+
const errorMessageId = `error${id}`;
|
|
1283
|
+
const isInvalid = !!errorMessage || _isInvalid;
|
|
1284
|
+
return /*#__PURE__*/ jsx("div", {
|
|
1285
|
+
children: /*#__PURE__*/ jsxs(CheckboxContext.Provider, {
|
|
1286
|
+
value: {
|
|
1287
|
+
'aria-describedby': description ? descriptionId : undefined,
|
|
1288
|
+
'aria-errormessage': errorMessage ? errorMessageId : undefined
|
|
1289
|
+
},
|
|
1290
|
+
children: [
|
|
1291
|
+
/*#__PURE__*/ jsxs(Checkbox$1, {
|
|
1292
|
+
...restProps,
|
|
1293
|
+
className: cx(className, defaultClasses$1),
|
|
1294
|
+
isInvalid: isInvalid,
|
|
1295
|
+
children: [
|
|
1296
|
+
/*#__PURE__*/ jsx(CheckmarkBox, {}),
|
|
1297
|
+
children
|
|
1298
|
+
]
|
|
1299
|
+
}),
|
|
1300
|
+
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 */}
|
|
1301
|
+
/*#__PURE__*/ jsx("div", {
|
|
1302
|
+
id: descriptionId,
|
|
1303
|
+
slot: "description",
|
|
1304
|
+
className: "description block",
|
|
1305
|
+
children: description
|
|
1306
|
+
}),
|
|
1307
|
+
errorMessage && /*#__PURE__*/ jsx(ErrorMessage, {
|
|
1308
|
+
className: "mt-2 block",
|
|
1309
|
+
id: errorMessageId,
|
|
1310
|
+
children: errorMessage
|
|
1311
|
+
})
|
|
1312
|
+
]
|
|
1313
|
+
})
|
|
1314
|
+
});
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
function Description(props) {
|
|
1318
|
+
const { className, ...restProps } = props;
|
|
1319
|
+
return /*#__PURE__*/ jsx(Text, {
|
|
1320
|
+
...restProps,
|
|
1321
|
+
className: cx(className, 'description'),
|
|
1322
|
+
slot: "description"
|
|
1323
|
+
});
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
/**
|
|
1327
|
+
* This component handles renders a custom error message (if provided), otherwise it falls back to the browser's native validation.
|
|
1328
|
+
* In other words, this handles controlled and uncontrolled form errors.
|
|
1329
|
+
*/ function ErrorMessageOrFieldError({ errorMessage }) {
|
|
1330
|
+
return errorMessage ? /*#__PURE__*/ jsx(ErrorMessage, {
|
|
1331
|
+
children: errorMessage
|
|
1332
|
+
}) : /*#__PURE__*/ jsx(FieldError, {
|
|
1333
|
+
className: formFieldError
|
|
1334
|
+
});
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
function Label(props) {
|
|
1338
|
+
const { children, className, ...restProps } = props;
|
|
1339
|
+
return /*#__PURE__*/ jsx(Label$1, {
|
|
1340
|
+
className: cx(className, 'font-semibold leading-7'),
|
|
1341
|
+
...restProps,
|
|
1342
|
+
children: children
|
|
1343
|
+
});
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
function CheckboxGroup(props) {
|
|
1347
|
+
const { children, className, description, errorMessage, label, isRequired, isInvalid: _isInvalid, ...restProps } = props;
|
|
1348
|
+
// the order of the conditions matter here, because providing a value for isInvalid makes the validation state "controlled",
|
|
1349
|
+
// which will override any built in validation
|
|
1350
|
+
const isInvalid = !!errorMessage || _isInvalid;
|
|
1351
|
+
return /*#__PURE__*/ jsxs(CheckboxGroup$1, {
|
|
1352
|
+
...restProps,
|
|
1353
|
+
className: cx(className, 'flex flex-col gap-2'),
|
|
1354
|
+
isInvalid: isInvalid,
|
|
1355
|
+
isRequired: isRequired,
|
|
1471
1356
|
children: [
|
|
1357
|
+
label && /*#__PURE__*/ jsx(Label, {
|
|
1358
|
+
children: label
|
|
1359
|
+
}),
|
|
1360
|
+
description && /*#__PURE__*/ jsx(Description, {
|
|
1361
|
+
children: description
|
|
1362
|
+
}),
|
|
1472
1363
|
children,
|
|
1473
|
-
|
|
1474
|
-
|
|
1364
|
+
/*#__PURE__*/ jsx(ErrorMessageOrFieldError, {
|
|
1365
|
+
errorMessage: errorMessage
|
|
1475
1366
|
})
|
|
1476
1367
|
]
|
|
1477
1368
|
});
|
|
1478
|
-
}
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
let { id, ...otherProps } = props;
|
|
1484
|
-
const defaultId = useId();
|
|
1485
|
-
id ||= defaultId;
|
|
1486
|
-
const isExpanded = groupState ? groupState.expandedKeys.has(id) : props.isExpanded;
|
|
1487
|
-
const state = useDisclosureState({
|
|
1488
|
-
...props,
|
|
1489
|
-
isExpanded,
|
|
1490
|
-
onExpandedChange (isExpanded) {
|
|
1491
|
-
if (groupState) {
|
|
1492
|
-
groupState.toggleKey(id);
|
|
1493
|
-
}
|
|
1494
|
-
props.onExpandedChange?.(isExpanded);
|
|
1495
|
-
}
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
function InputAddonDivider() {
|
|
1372
|
+
return /*#__PURE__*/ jsx("span", {
|
|
1373
|
+
className: "block h-6 w-px flex-none bg-black"
|
|
1496
1374
|
});
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
const ListBox = ({ className, ...restProps })=>/*#__PURE__*/ jsx(ListBox$1, {
|
|
1378
|
+
...restProps,
|
|
1379
|
+
className: cx(dropdown.listbox, className)
|
|
1501
1380
|
});
|
|
1502
|
-
|
|
1503
|
-
|
|
1381
|
+
const ListBoxItem = (props)=>{
|
|
1382
|
+
let textValue = props.textValue;
|
|
1383
|
+
// When the ListBoxItem child isn't a string we have to set textValue for keyboard completion to work.
|
|
1384
|
+
// Since we use a render function (to handle the selected state) the child is never a string.
|
|
1385
|
+
// This condition adds back that behaviour
|
|
1386
|
+
if (textValue == null && typeof props.children === 'string') {
|
|
1387
|
+
textValue = props.children;
|
|
1388
|
+
}
|
|
1389
|
+
return /*#__PURE__*/ jsx(ListBoxItem$1, {
|
|
1504
1390
|
...props,
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
[
|
|
1516
|
-
ButtonContext$1,
|
|
1517
|
-
{
|
|
1518
|
-
slots: {
|
|
1519
|
-
[DEFAULT_SLOT]: {},
|
|
1520
|
-
trigger: buttonProps
|
|
1521
|
-
}
|
|
1522
|
-
}
|
|
1523
|
-
],
|
|
1524
|
-
[
|
|
1525
|
-
DisclosurePanelContext,
|
|
1526
|
-
{
|
|
1527
|
-
...propsWithoutRole,
|
|
1528
|
-
panelRef
|
|
1529
|
-
}
|
|
1530
|
-
],
|
|
1531
|
-
[
|
|
1532
|
-
DisclosureStateContext,
|
|
1533
|
-
state
|
|
1534
|
-
]
|
|
1535
|
-
],
|
|
1536
|
-
children: /*#__PURE__*/ jsx("div", {
|
|
1537
|
-
...domProps,
|
|
1538
|
-
className: otherProps.className,
|
|
1539
|
-
ref: ref,
|
|
1540
|
-
"data-focus-visible-within": isFocusVisibleWithin || undefined,
|
|
1541
|
-
"data-expanded": state.isExpanded || undefined,
|
|
1542
|
-
"data-disabled": isDisabled || undefined,
|
|
1543
|
-
children: typeof children === 'function' ? children({
|
|
1544
|
-
isExpanded: state.isExpanded,
|
|
1545
|
-
isFocusVisibleWithin,
|
|
1546
|
-
isDisabled,
|
|
1547
|
-
state,
|
|
1548
|
-
defaultChildren: null
|
|
1549
|
-
}) : children
|
|
1550
|
-
})
|
|
1391
|
+
className: cx(props.className, 'flex cursor-pointer px-6 py-3 leading-6 outline-none data-focused:bg-sky-lightest'),
|
|
1392
|
+
textValue: textValue,
|
|
1393
|
+
children: ({ isSelected })=>/*#__PURE__*/ jsxs(Fragment, {
|
|
1394
|
+
children: [
|
|
1395
|
+
isSelected && /*#__PURE__*/ jsx(Check, {
|
|
1396
|
+
className: "-ml-6 text-base"
|
|
1397
|
+
}),
|
|
1398
|
+
props.children
|
|
1399
|
+
]
|
|
1400
|
+
})
|
|
1551
1401
|
});
|
|
1552
1402
|
};
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
const role = isWithoutRole ? undefined : _role;
|
|
1561
|
-
const { isFocusVisible: isFocusVisibleWithin, focusProps: focusWithinProps } = useFocusRing({
|
|
1562
|
-
within: true
|
|
1403
|
+
/**
|
|
1404
|
+
* This component can be used to group items in a listbox
|
|
1405
|
+
*/ const ListBoxSection = ({ className, ...restProps })=>/*#__PURE__*/ jsx(ListBoxSection$1, {
|
|
1406
|
+
...restProps,
|
|
1407
|
+
// The :not(:first-child) selector adds extra spacing to all the options, but not the section (group) headings
|
|
1408
|
+
// This way we get the desired extra indent on all options within a group
|
|
1409
|
+
className: cx(className, 'pb-1 [&>:not(:first-child)]:pl-10')
|
|
1563
1410
|
});
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1411
|
+
/**
|
|
1412
|
+
* This component can be used to label grouped items in a `ListBoxSection` with a heading
|
|
1413
|
+
*/ const ListBoxHeader = (props)=>/*#__PURE__*/ jsx(Header, {
|
|
1414
|
+
...props,
|
|
1415
|
+
className: cx(props.className, 'mx-6 cursor-default py-2 font-medium text-blue-dark leading-6')
|
|
1416
|
+
});
|
|
1417
|
+
|
|
1418
|
+
function Combobox(props) {
|
|
1419
|
+
const { className, children, description, errorMessage, isPending, label, isInvalid: _isInvalid, ref, ...restProps } = props;
|
|
1420
|
+
// the order of the conditions matter here, because providing a value for isInvalid makes the validation state "controlled",
|
|
1421
|
+
// which will override any built in validation
|
|
1422
|
+
const isInvalid = !!errorMessage || _isInvalid;
|
|
1423
|
+
return /*#__PURE__*/ jsxs(ComboBox, {
|
|
1424
|
+
...restProps,
|
|
1425
|
+
className: cx(className, formField),
|
|
1426
|
+
isInvalid: isInvalid,
|
|
1427
|
+
children: [
|
|
1428
|
+
label && /*#__PURE__*/ jsx(Label, {
|
|
1429
|
+
children: label
|
|
1430
|
+
}),
|
|
1431
|
+
description && /*#__PURE__*/ jsx(Description, {
|
|
1432
|
+
children: description
|
|
1433
|
+
}),
|
|
1434
|
+
/*#__PURE__*/ jsxs(Group, {
|
|
1435
|
+
className: inputGroup,
|
|
1436
|
+
children: [
|
|
1437
|
+
/*#__PURE__*/ jsx(Input, {
|
|
1438
|
+
className: input({
|
|
1439
|
+
isGrouped: true
|
|
1440
|
+
}),
|
|
1441
|
+
ref: ref
|
|
1442
|
+
}),
|
|
1443
|
+
/*#__PURE__*/ jsx(Button$1, {
|
|
1444
|
+
children: isPending ? /*#__PURE__*/ jsx(LoadingSpinner, {
|
|
1445
|
+
className: "animate-spin"
|
|
1446
|
+
}) : /*#__PURE__*/ jsx(ChevronDown, {
|
|
1447
|
+
className: dropdown.chevronIcon
|
|
1448
|
+
})
|
|
1449
|
+
})
|
|
1450
|
+
]
|
|
1451
|
+
}),
|
|
1452
|
+
/*#__PURE__*/ jsx(ErrorMessageOrFieldError, {
|
|
1453
|
+
errorMessage: errorMessage
|
|
1454
|
+
}),
|
|
1455
|
+
/*#__PURE__*/ jsx(Popover, {
|
|
1456
|
+
// FIXME: The trigger width doesn't include the padding of the group, so for now we have to apply this workaround.
|
|
1457
|
+
// Also... the combobox border gets a pixel wider when focused, so we account for that as well when calculating the width
|
|
1458
|
+
// and the offset.
|
|
1459
|
+
// The input gutter should probably be moved to a theme variable instead of using the hardcoded value as here.
|
|
1460
|
+
className: cx(dropdown.popover, 'min-w-[calc(var(--trigger-width)+26px)]'),
|
|
1461
|
+
crossOffset: -13,
|
|
1462
|
+
children: /*#__PURE__*/ jsx(ListBox, {
|
|
1463
|
+
className: dropdown.listbox,
|
|
1590
1464
|
children: children
|
|
1591
1465
|
})
|
|
1592
1466
|
})
|
|
1593
|
-
|
|
1467
|
+
]
|
|
1594
1468
|
});
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
/**
|
|
1472
|
+
* A React component that wraps https://react-spectrum.adobe.com/react-aria/useDateFormatter.html
|
|
1473
|
+
* By default it sets the timeZone to `Europe/Berlin` to prevent the server's timezone from affecting
|
|
1474
|
+
* the localized format
|
|
1475
|
+
*/ const DateFormatter = ({ options: _options, value, children: render })=>{
|
|
1476
|
+
const options = {
|
|
1477
|
+
timeZone: 'Europe/Berlin',
|
|
1478
|
+
..._options
|
|
1479
|
+
};
|
|
1480
|
+
const formatter = useDateFormatter(options);
|
|
1481
|
+
const date = typeof value === 'string' ? new Date(value) : value;
|
|
1482
|
+
const formatted = formatter.format(date);
|
|
1483
|
+
return render ? render(formatted) : formatted;
|
|
1595
1484
|
};
|
|
1596
1485
|
|
|
1597
1486
|
/**
|
|
@@ -1807,222 +1696,66 @@ const FileUpload = ({ children, files: _files, onChange, validate, isInvalid: _i
|
|
|
1807
1696
|
ref: inputRef,
|
|
1808
1697
|
// Delegate focus to the button when the hidden file input is focused (for RAC auto-focusing behavior)
|
|
1809
1698
|
onFocus: ()=>buttonRef.current?.focus(),
|
|
1810
|
-
children: children
|
|
1811
|
-
})
|
|
1812
|
-
}),
|
|
1813
|
-
controlledOrUncontrolledFiles.length > 0 && /*#__PURE__*/ jsx("ul", {
|
|
1814
|
-
className: "mt-4 grid max-w-fit gap-y-2",
|
|
1815
|
-
children: controlledOrUncontrolledFiles.map((file, fileIndex)=>{
|
|
1816
|
-
let fileName = file.name;
|
|
1817
|
-
if (fileTriggerProps.acceptDirectory && file.webkitRelativePath !== '') {
|
|
1818
|
-
fileName = file.webkitRelativePath;
|
|
1819
|
-
}
|
|
1820
|
-
const validation = validate?.(file) ?? true;
|
|
1821
|
-
const hasError = validation !== true;
|
|
1822
|
-
return /*#__PURE__*/ jsxs("li", {
|
|
1823
|
-
children: [
|
|
1824
|
-
/*#__PURE__*/ jsxs("div", {
|
|
1825
|
-
className: cx('flex items-center justify-between gap-2 rounded-lg border-2 px-4 py-2', hasError ? 'border-red bg-red-light' : 'border-gray bg-gray-lightest'),
|
|
1826
|
-
children: [
|
|
1827
|
-
fileName,
|
|
1828
|
-
/*#__PURE__*/ jsx("button", {
|
|
1829
|
-
className: cx('self-start', '-m-2 grid h-11 w-11 shrink-0 cursor-pointer place-items-center rounded-xl', // Focus styles
|
|
1830
|
-
'focus-visible:-outline-offset-8 focus-visible:outline-focus'),
|
|
1831
|
-
onClick: ()=>{
|
|
1832
|
-
// For controlled component
|
|
1833
|
-
onChange?.((prevFiles)=>prevFiles.filter((_, index)=>index !== fileIndex));
|
|
1834
|
-
// For internal file state
|
|
1835
|
-
setFiles((prevFiles)=>prevFiles.filter((_, index)=>index !== fileIndex));
|
|
1836
|
-
// Make sure screen readers doesn't loose track of focus
|
|
1837
|
-
// (without this, the focus will be set to the top of the page for screen readers)
|
|
1838
|
-
buttonRef.current?.focus();
|
|
1839
|
-
},
|
|
1840
|
-
"aria-label": translations.remove[locale],
|
|
1841
|
-
type: "button",
|
|
1842
|
-
children: /*#__PURE__*/ jsx(Trash, {})
|
|
1843
|
-
})
|
|
1844
|
-
]
|
|
1845
|
-
}),
|
|
1846
|
-
hasError && /*#__PURE__*/ jsx(ErrorMessage, {
|
|
1847
|
-
className: "mt-1 block w-full",
|
|
1848
|
-
children: validation
|
|
1849
|
-
})
|
|
1850
|
-
]
|
|
1851
|
-
}, fileName);
|
|
1852
|
-
})
|
|
1853
|
-
}),
|
|
1854
|
-
(controlledOrUncontrolledFiles.length === 0 || !!errorMessage) && /*#__PURE__*/ jsx(ErrorMessageOrFieldError, {
|
|
1855
|
-
errorMessage: errorMessage
|
|
1856
|
-
})
|
|
1857
|
-
]
|
|
1858
|
-
})
|
|
1859
|
-
});
|
|
1860
|
-
};
|
|
1861
|
-
|
|
1862
|
-
const baseClassName = 'h-20 w-20 shrink-0 rounded-full';
|
|
1863
|
-
const Avatar = ({ src, alt = '', className, onError, loading = 'lazy', ...rest })=>{
|
|
1864
|
-
const [hasError, setHasError] = useState(false);
|
|
1865
|
-
const hasValidImage = !hasError && src;
|
|
1866
|
-
return hasValidImage ? /*#__PURE__*/ jsx("img", {
|
|
1867
|
-
...rest,
|
|
1868
|
-
src: src,
|
|
1869
|
-
alt: alt,
|
|
1870
|
-
loading: loading,
|
|
1871
|
-
className: cx(className, baseClassName, 'object-cover'),
|
|
1872
|
-
onError: (event)=>{
|
|
1873
|
-
onError?.(event);
|
|
1874
|
-
setHasError(true);
|
|
1875
|
-
}
|
|
1876
|
-
}) : /*#__PURE__*/ jsx("div", {
|
|
1877
|
-
className: cx(className, baseClassName, 'grid place-items-center bg-gray-light text-gray-dark'),
|
|
1878
|
-
children: /*#__PURE__*/ jsx(User, {
|
|
1879
|
-
className: "scale-[2.25]"
|
|
1880
|
-
})
|
|
1881
|
-
});
|
|
1882
|
-
};
|
|
1883
|
-
|
|
1884
|
-
const DialogTrigger = (props)=>/*#__PURE__*/ jsx(DialogTrigger$1, {
|
|
1885
|
-
...props
|
|
1886
|
-
});
|
|
1887
|
-
const ModalOverlay = (props)=>/*#__PURE__*/ jsx(ModalOverlay$1, {
|
|
1888
|
-
...props,
|
|
1889
|
-
isDismissable: true,
|
|
1890
|
-
className: ({ isEntering, isExiting })=>cx('fixed inset-0 z-10 flex min-h-full items-center justify-center overflow-y-auto bg-black/25 p-4 text-center backdrop-blur-sm', isEntering && 'fade-in animate-in duration-300 ease-out', isExiting && 'fade-out animate-out duration-200 ease-in', // Using the motion-safe class does not work, so we use motion-reduce to overwrite instead
|
|
1891
|
-
'motion-reduce:animate-none')
|
|
1892
|
-
});
|
|
1893
|
-
const Modal = ({ className, ...restProps })=>/*#__PURE__*/ jsx(ModalOverlay, {
|
|
1894
|
-
children: /*#__PURE__*/ jsx(Modal$1, {
|
|
1895
|
-
...restProps,
|
|
1896
|
-
className: ({ isEntering, isExiting })=>cx(className, 'w-full max-w-md overflow-hidden rounded-2xl bg-white p-4 text-left align-middle shadow-xl', isEntering && 'zoom-in-95 animate-in duration-300 ease-out', isExiting && 'zoom-out-95 animate-out duration-200 ease-in', // Using the motion-safe class does not work, so we use motion-reduce to overwrite instead
|
|
1897
|
-
'motion-reduce:animate-none')
|
|
1898
|
-
})
|
|
1899
|
-
});
|
|
1900
|
-
const Dialog = ({ className, children, ...restProps })=>{
|
|
1901
|
-
const locale = _useLocale();
|
|
1902
|
-
return /*#__PURE__*/ jsx(Dialog$1, {
|
|
1903
|
-
...restProps,
|
|
1904
|
-
className: cx('relative grid gap-y-5 outline-none', // Footer
|
|
1905
|
-
'[&_[data-slot="footer"]]:flex [&_[data-slot="footer"]]:gap-x-2'),
|
|
1906
|
-
children: ({ close })=>/*#__PURE__*/ jsx(Fragment, {
|
|
1907
|
-
children: /*#__PURE__*/ jsx(Provider, {
|
|
1908
|
-
values: [
|
|
1909
|
-
[
|
|
1910
|
-
HeadingContext,
|
|
1911
|
-
{
|
|
1912
|
-
slots: {
|
|
1913
|
-
[DEFAULT_SLOT]: {},
|
|
1914
|
-
title: {
|
|
1915
|
-
className: 'heading-s',
|
|
1916
|
-
_outerWrapper: (children)=>/*#__PURE__*/ jsxs("div", {
|
|
1917
|
-
className: "flex items-center justify-between gap-x-2",
|
|
1918
|
-
children: [
|
|
1919
|
-
children,
|
|
1920
|
-
/*#__PURE__*/ jsx(Button, {
|
|
1921
|
-
slot: "close" // RAC Dialog suppors one close button out of the box, so we utilize that here. For other close buttons we use ButtonContext
|
|
1922
|
-
,
|
|
1923
|
-
variant: "tertiary",
|
|
1924
|
-
className: "!px-2.5 data-focus-visible:outline-focus-inset",
|
|
1925
|
-
"aria-label": translations$1.close[locale],
|
|
1926
|
-
children: /*#__PURE__*/ jsx(Close, {})
|
|
1927
|
-
})
|
|
1928
|
-
]
|
|
1929
|
-
})
|
|
1930
|
-
}
|
|
1931
|
-
}
|
|
1932
|
-
}
|
|
1933
|
-
],
|
|
1934
|
-
[
|
|
1935
|
-
ButtonContext$1,
|
|
1936
|
-
{
|
|
1937
|
-
// This is necessary to support multiple close buttons
|
|
1938
|
-
slots: {
|
|
1939
|
-
// We need to define default slot in order to also support non-slotted buttons (i.e. buttons without slot prop)
|
|
1940
|
-
[DEFAULT_SLOT]: {
|
|
1941
|
-
className: 'w-fit'
|
|
1942
|
-
},
|
|
1943
|
-
close: {
|
|
1944
|
-
onPress: close,
|
|
1945
|
-
className: 'w-fit'
|
|
1946
|
-
}
|
|
1947
|
-
}
|
|
1948
|
-
}
|
|
1949
|
-
]
|
|
1950
|
-
],
|
|
1951
|
-
children: children
|
|
1699
|
+
children: children
|
|
1700
|
+
})
|
|
1701
|
+
}),
|
|
1702
|
+
controlledOrUncontrolledFiles.length > 0 && /*#__PURE__*/ jsx("ul", {
|
|
1703
|
+
className: "mt-4 grid max-w-fit gap-y-2",
|
|
1704
|
+
children: controlledOrUncontrolledFiles.map((file, fileIndex)=>{
|
|
1705
|
+
let fileName = file.name;
|
|
1706
|
+
if (fileTriggerProps.acceptDirectory && file.webkitRelativePath !== '') {
|
|
1707
|
+
fileName = file.webkitRelativePath;
|
|
1708
|
+
}
|
|
1709
|
+
const validation = validate?.(file) ?? true;
|
|
1710
|
+
const hasError = validation !== true;
|
|
1711
|
+
return /*#__PURE__*/ jsxs("li", {
|
|
1712
|
+
children: [
|
|
1713
|
+
/*#__PURE__*/ jsxs("div", {
|
|
1714
|
+
className: cx('flex items-center justify-between gap-2 rounded-lg border-2 px-4 py-2', hasError ? 'border-red bg-red-light' : 'border-gray bg-gray-lightest'),
|
|
1715
|
+
children: [
|
|
1716
|
+
fileName,
|
|
1717
|
+
/*#__PURE__*/ jsx("button", {
|
|
1718
|
+
className: cx('self-start', '-m-2 grid h-11 w-11 shrink-0 cursor-pointer place-items-center rounded-xl', // Focus styles
|
|
1719
|
+
'focus-visible:-outline-offset-8 focus-visible:outline-focus'),
|
|
1720
|
+
onClick: ()=>{
|
|
1721
|
+
// For controlled component
|
|
1722
|
+
onChange?.((prevFiles)=>prevFiles.filter((_, index)=>index !== fileIndex));
|
|
1723
|
+
// For internal file state
|
|
1724
|
+
setFiles((prevFiles)=>prevFiles.filter((_, index)=>index !== fileIndex));
|
|
1725
|
+
// Make sure screen readers doesn't loose track of focus
|
|
1726
|
+
// (without this, the focus will be set to the top of the page for screen readers)
|
|
1727
|
+
buttonRef.current?.focus();
|
|
1728
|
+
},
|
|
1729
|
+
"aria-label": translations.remove[locale],
|
|
1730
|
+
type: "button",
|
|
1731
|
+
children: /*#__PURE__*/ jsx(Trash, {})
|
|
1732
|
+
})
|
|
1733
|
+
]
|
|
1734
|
+
}),
|
|
1735
|
+
hasError && /*#__PURE__*/ jsx(ErrorMessage, {
|
|
1736
|
+
className: "mt-1 block w-full",
|
|
1737
|
+
children: validation
|
|
1738
|
+
})
|
|
1739
|
+
]
|
|
1740
|
+
}, fileName);
|
|
1741
|
+
})
|
|
1742
|
+
}),
|
|
1743
|
+
(controlledOrUncontrolledFiles.length === 0 || !!errorMessage) && /*#__PURE__*/ jsx(ErrorMessageOrFieldError, {
|
|
1744
|
+
errorMessage: errorMessage
|
|
1952
1745
|
})
|
|
1953
|
-
|
|
1746
|
+
]
|
|
1747
|
+
})
|
|
1954
1748
|
});
|
|
1955
1749
|
};
|
|
1956
1750
|
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
' data-hovered:bg-sky',
|
|
1966
|
-
// Selected
|
|
1967
|
-
// Allows removing
|
|
1968
|
-
'data-allows-removing:border-transparent',
|
|
1969
|
-
'data-allows-removing:bg-blue',
|
|
1970
|
-
'data-allows-removing:data-hovered:bg-blue-dark',
|
|
1971
|
-
'data-allows-removing:text-white',
|
|
1972
|
-
// Selected
|
|
1973
|
-
'aria-selected:border-transparent',
|
|
1974
|
-
'aria-selected:bg-blue',
|
|
1975
|
-
'aria-selected:data-hovered:bg-blue-dark',
|
|
1976
|
-
'aria-selected:text-white',
|
|
1977
|
-
//Icons
|
|
1978
|
-
'[&_svg]:h-4 [&_svg]:w-4'
|
|
1979
|
-
]
|
|
1980
|
-
});
|
|
1981
|
-
/**
|
|
1982
|
-
* A group component for Tag components that enables selection and organization of options.
|
|
1983
|
-
*/ function TagGroup(props) {
|
|
1984
|
-
const { onRemove, selectionMode = 'single', className, children, ...restProps } = props;
|
|
1985
|
-
return /*#__PURE__*/ jsx(TagGroup$1, {
|
|
1986
|
-
...restProps,
|
|
1987
|
-
className: className,
|
|
1988
|
-
selectionMode: onRemove ? 'none' : selectionMode,
|
|
1989
|
-
onRemove: onRemove,
|
|
1990
|
-
children: children
|
|
1991
|
-
});
|
|
1992
|
-
}
|
|
1993
|
-
/**
|
|
1994
|
-
* A container component for Tag components within a TagGroup.
|
|
1995
|
-
*/ function TagList(props) {
|
|
1996
|
-
const { className, children, ...restProps } = props;
|
|
1997
|
-
return /*#__PURE__*/ jsx(TagList$1, {
|
|
1998
|
-
...restProps,
|
|
1999
|
-
className: cx('flex flex-wrap gap-2', className),
|
|
2000
|
-
children: children
|
|
2001
|
-
});
|
|
2002
|
-
}
|
|
2003
|
-
/**
|
|
2004
|
-
* Interactive tag component for selections, filtering, and categorization.
|
|
2005
|
-
*/ function Tag(props) {
|
|
2006
|
-
const { className, children, ...restProps } = props;
|
|
2007
|
-
const textValue = typeof children === 'string' ? children : undefined;
|
|
2008
|
-
return /*#__PURE__*/ jsx(Tag$1, {
|
|
2009
|
-
className: tagVariants({
|
|
2010
|
-
className
|
|
2011
|
-
}),
|
|
2012
|
-
textValue: textValue,
|
|
2013
|
-
...restProps,
|
|
2014
|
-
children: ({ allowsRemoving })=>allowsRemoving ? /*#__PURE__*/ jsxs(Fragment, {
|
|
2015
|
-
children: [
|
|
2016
|
-
children,
|
|
2017
|
-
/*#__PURE__*/ jsx(Button$1, {
|
|
2018
|
-
className: "cursor-pointer outline-none after:absolute after:top-0 after:right-0 after:bottom-0 after:left-0",
|
|
2019
|
-
slot: "remove",
|
|
2020
|
-
children: /*#__PURE__*/ jsx(Close, {
|
|
2021
|
-
className: "ml-1"
|
|
2022
|
-
})
|
|
2023
|
-
})
|
|
2024
|
-
]
|
|
2025
|
-
}) : children
|
|
1751
|
+
function GrunnmurenProvider({ children, locale = 'nb', navigate, useHref }) {
|
|
1752
|
+
return /*#__PURE__*/ jsx(I18nProvider, {
|
|
1753
|
+
locale: locale,
|
|
1754
|
+
children: navigate ? /*#__PURE__*/ jsx(RouterProvider, {
|
|
1755
|
+
navigate: navigate,
|
|
1756
|
+
useHref: useHref,
|
|
1757
|
+
children: children
|
|
1758
|
+
}) : children
|
|
2026
1759
|
});
|
|
2027
1760
|
}
|
|
2028
1761
|
|
|
@@ -2103,7 +1836,7 @@ const variants = cva({
|
|
|
2103
1836
|
variant: 'standard'
|
|
2104
1837
|
}
|
|
2105
1838
|
});
|
|
2106
|
-
const Hero = ({ variant, className, children })=>{
|
|
1839
|
+
const Hero = ({ variant, className, children, ...rest })=>{
|
|
2107
1840
|
const variantsClassName = variants({
|
|
2108
1841
|
variant,
|
|
2109
1842
|
className
|
|
@@ -2131,188 +1864,317 @@ const Hero = ({ variant, className, children })=>{
|
|
|
2131
1864
|
],
|
|
2132
1865
|
children: /*#__PURE__*/ jsx("div", {
|
|
2133
1866
|
className: cx(variantsClassName, className),
|
|
1867
|
+
...rest,
|
|
2134
1868
|
children: children
|
|
2135
1869
|
})
|
|
2136
1870
|
});
|
|
2137
1871
|
};
|
|
2138
1872
|
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
// Keep track of the previous index to determine if the user is scrolling forward or backward
|
|
2153
|
-
// This is used to determine which callback to call (onPrev or onNext)
|
|
2154
|
-
const prevIndex = useRef(0);
|
|
2155
|
-
// Handle scrolling when user clicks the arrow icons
|
|
2156
|
-
useUpdateEffect(()=>{
|
|
2157
|
-
if (!ref.current) return;
|
|
2158
|
-
ref.current.children[scrollTargetIndex]?.scrollIntoView({
|
|
2159
|
-
behavior: 'smooth',
|
|
2160
|
-
inline: 'start',
|
|
2161
|
-
block: 'nearest'
|
|
2162
|
-
});
|
|
2163
|
-
if (prevIndex.current !== scrollTargetIndex && onChange) {
|
|
2164
|
-
onChange({
|
|
2165
|
-
index: scrollTargetIndex,
|
|
2166
|
-
id: ref.current.children[scrollTargetIndex]?.id,
|
|
2167
|
-
prevIndex: prevIndex.current,
|
|
2168
|
-
prevId: ref.current.children[prevIndex.current]?.id
|
|
2169
|
-
});
|
|
2170
|
-
}
|
|
2171
|
-
prevIndex.current = scrollTargetIndex;
|
|
2172
|
-
}, [
|
|
2173
|
-
scrollTargetIndex
|
|
2174
|
-
]);
|
|
2175
|
-
const onScroll = useDebouncedCallback((event)=>{
|
|
2176
|
-
const target = event.target;
|
|
2177
|
-
// Calculate the index of the item that is currently in view
|
|
2178
|
-
const newScrollTargetIndex = Array.from(target.children).findIndex((child)=>{
|
|
2179
|
-
const rect = child.getBoundingClientRect();
|
|
2180
|
-
return rect.left >= 0 && rect.right <= window.innerWidth && rect.top >= 0;
|
|
2181
|
-
});
|
|
2182
|
-
if (newScrollTargetIndex !== -1) {
|
|
2183
|
-
setScrollTargetIndex(newScrollTargetIndex);
|
|
2184
|
-
}
|
|
2185
|
-
}, 100);
|
|
1873
|
+
/**
|
|
1874
|
+
* A basic link component that extends react-aria-components Link with consistent styling.
|
|
1875
|
+
* Provides accessible focus styles and maintains design system consistency.
|
|
1876
|
+
*/ const CustomLink = ({ children, className, ...restProps })=>{
|
|
1877
|
+
return /*#__PURE__*/ jsx(Link, {
|
|
1878
|
+
...restProps,
|
|
1879
|
+
className: cx(className, 'inline-flex cursor-pointer items-center gap-1 font-medium hover:no-underline focus-visible:outline-current focus-visible:outline-focus-offset [&>svg]:shrink-0'),
|
|
1880
|
+
children: children
|
|
1881
|
+
});
|
|
1882
|
+
};
|
|
1883
|
+
|
|
1884
|
+
const LinkList = ({ className, children, ...restProps })=>{
|
|
1885
|
+
const numberofLinks = Children.count(children);
|
|
2186
1886
|
return /*#__PURE__*/ jsx("div", {
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
1887
|
+
className: cx(className, '@container'),
|
|
1888
|
+
...restProps,
|
|
1889
|
+
children: /*#__PURE__*/ jsx("ul", {
|
|
1890
|
+
className: cx('min-w-fit', // Hide dividers at the top of the list (overflow-y) and prevents arrow icon from overflowing container when animated to the right (overflow-x)
|
|
1891
|
+
'overflow-hidden', // Add a small gap between items that fits the divider lines (this way the divider line don't take up any space in each item)
|
|
1892
|
+
'grid auto-rows-max gap-y-0.25', // Gaps for when the list is displayed in multiple columns
|
|
1893
|
+
'@lg:gap-x-12 @md:gap-x-9 @sm:gap-x-4 @xl:gap-x-16', numberofLinks > 5 && [
|
|
1894
|
+
'@xl:grid-cols-2',
|
|
1895
|
+
(numberofLinks === 9 || numberofLinks > 10) && '@4xl:grid-cols-3'
|
|
1896
|
+
]),
|
|
1897
|
+
children: children
|
|
1898
|
+
})
|
|
1899
|
+
});
|
|
1900
|
+
};
|
|
1901
|
+
const LinkListItem = ({ children, isExternal, className, ...restProps })=>{
|
|
1902
|
+
let Icon = ArrowRight;
|
|
1903
|
+
let iconTransition = 'group-hover:motion-safe:translate-x-1';
|
|
1904
|
+
if (restProps.download) {
|
|
1905
|
+
Icon = Download;
|
|
1906
|
+
iconTransition = 'group-hover:motion-safe:translate-y-1';
|
|
1907
|
+
} else if (isExternal) {
|
|
1908
|
+
iconTransition = 'group-hover:motion-safe:-translate-y-0.5 group-hover:motion-safe:translate-x-0.5';
|
|
1909
|
+
Icon = LinkExternal;
|
|
1910
|
+
}
|
|
1911
|
+
return /*#__PURE__*/ jsx("li", {
|
|
1912
|
+
// Creates divider lines that works in any grid layout and with the focus ring
|
|
1913
|
+
className: "after:-top-0.25 relative p-0.75 after:absolute after:right-0 after:left-0 after:h-0.25 after:w-full after:bg-gray-light",
|
|
1914
|
+
children: /*#__PURE__*/ jsxs(Link, {
|
|
1915
|
+
...restProps,
|
|
1916
|
+
className: cx(className, 'group paragraph flex cursor-pointer justify-between gap-x-2 py-3.5 font-medium no-underline focus-visible:outline-focus'),
|
|
1917
|
+
children: [
|
|
1918
|
+
/*#__PURE__*/ jsx("span", {
|
|
1919
|
+
children: children
|
|
1920
|
+
}),
|
|
1921
|
+
/*#__PURE__*/ jsx(Icon, {
|
|
1922
|
+
className: cx('shrink-0 motion-safe:transition-transform', iconTransition)
|
|
1923
|
+
})
|
|
1924
|
+
]
|
|
1925
|
+
})
|
|
1926
|
+
});
|
|
1927
|
+
};
|
|
1928
|
+
|
|
1929
|
+
const DialogTrigger = (props)=>/*#__PURE__*/ jsx(DialogTrigger$1, {
|
|
1930
|
+
...props
|
|
1931
|
+
});
|
|
1932
|
+
const ModalOverlay = (props)=>/*#__PURE__*/ jsx(ModalOverlay$1, {
|
|
1933
|
+
...props,
|
|
1934
|
+
isDismissable: true,
|
|
1935
|
+
className: ({ isEntering, isExiting })=>cx('fixed inset-0 z-10 flex min-h-full items-center justify-center overflow-y-auto bg-black/25 p-4 text-center backdrop-blur-sm', isEntering && 'fade-in animate-in duration-300 ease-out', isExiting && 'fade-out animate-out duration-200 ease-in', // Using the motion-safe class does not work, so we use motion-reduce to overwrite instead
|
|
1936
|
+
'motion-reduce:animate-none')
|
|
1937
|
+
});
|
|
1938
|
+
const Modal = ({ className, ...restProps })=>/*#__PURE__*/ jsx(ModalOverlay, {
|
|
1939
|
+
children: /*#__PURE__*/ jsx(Modal$1, {
|
|
1940
|
+
...restProps,
|
|
1941
|
+
className: ({ isEntering, isExiting })=>cx(className, 'w-full max-w-md overflow-hidden rounded-2xl bg-white p-4 text-left align-middle shadow-xl', isEntering && 'zoom-in-95 animate-in duration-300 ease-out', isExiting && 'zoom-out-95 animate-out duration-200 ease-in', // Using the motion-safe class does not work, so we use motion-reduce to overwrite instead
|
|
1942
|
+
'motion-reduce:animate-none')
|
|
1943
|
+
})
|
|
1944
|
+
});
|
|
1945
|
+
const Dialog = ({ className, children, ...restProps })=>{
|
|
1946
|
+
const locale = _useLocale();
|
|
1947
|
+
return /*#__PURE__*/ jsx(Dialog$1, {
|
|
1948
|
+
...restProps,
|
|
1949
|
+
className: cx('relative grid gap-y-5 outline-none', // Footer
|
|
1950
|
+
'[&_[data-slot="footer"]]:flex [&_[data-slot="footer"]]:gap-x-2'),
|
|
1951
|
+
children: ({ close })=>/*#__PURE__*/ jsx(Fragment, {
|
|
1952
|
+
children: /*#__PURE__*/ jsx(Provider, {
|
|
1953
|
+
values: [
|
|
1954
|
+
[
|
|
1955
|
+
HeadingContext,
|
|
1956
|
+
{
|
|
1957
|
+
slots: {
|
|
1958
|
+
[DEFAULT_SLOT]: {},
|
|
1959
|
+
title: {
|
|
1960
|
+
className: 'heading-s',
|
|
1961
|
+
_outerWrapper: (children)=>/*#__PURE__*/ jsxs("div", {
|
|
1962
|
+
className: "flex items-center justify-between gap-x-2",
|
|
1963
|
+
children: [
|
|
1964
|
+
children,
|
|
1965
|
+
/*#__PURE__*/ jsx(Button, {
|
|
1966
|
+
slot: "close",
|
|
1967
|
+
variant: "tertiary",
|
|
1968
|
+
className: "!px-2.5 data-focus-visible:outline-focus-inset",
|
|
1969
|
+
"aria-label": translations$1.close[locale],
|
|
1970
|
+
children: /*#__PURE__*/ jsx(Close, {})
|
|
1971
|
+
})
|
|
1972
|
+
]
|
|
1973
|
+
})
|
|
2206
1974
|
}
|
|
2207
|
-
}
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
1975
|
+
}
|
|
1976
|
+
}
|
|
1977
|
+
],
|
|
1978
|
+
[
|
|
1979
|
+
ButtonContext$1,
|
|
1980
|
+
{
|
|
1981
|
+
// This is necessary to support multiple close buttons
|
|
1982
|
+
slots: {
|
|
1983
|
+
// We need to define default slot in order to also support non-slotted buttons (i.e. buttons without slot prop)
|
|
1984
|
+
[DEFAULT_SLOT]: {
|
|
1985
|
+
className: 'w-fit'
|
|
1986
|
+
},
|
|
1987
|
+
close: {
|
|
1988
|
+
onPress: close,
|
|
1989
|
+
className: 'w-fit'
|
|
2217
1990
|
}
|
|
2218
|
-
}
|
|
2219
|
-
isDisabled: hasReachedScrollEnd
|
|
1991
|
+
}
|
|
2220
1992
|
}
|
|
2221
|
-
}
|
|
2222
|
-
}
|
|
2223
|
-
]
|
|
2224
|
-
],
|
|
2225
|
-
children: /*#__PURE__*/ jsxs("div", {
|
|
2226
|
-
className: cx(className, 'relative rounded-3xl', // If any <CarouselItems/> (the scroll-snap container) or <VideoLoop/> component is focused, apply custom focus styles around the carousel, this makes ensures that the focus outline is visible around the carousel in all cases
|
|
2227
|
-
'[&:has([data-slot="carousel-items"]:focus-visible,[data-slot="video-loop-button"]:focus-visible)]:outline-focus', '[&:has([data-slot="carousel-items"]:focus-visible,[data-slot="video-loop-button"]:focus-visible)]:outline-focus-offset', // Unset the default focus outline for potential video loop buttons, as it interferes with the custom focus styles for the carousel
|
|
2228
|
-
'**:data-[slot="video-loop-button"]:focus-visible:outline-none'),
|
|
2229
|
-
children: [
|
|
2230
|
-
children,
|
|
2231
|
-
/*#__PURE__*/ jsxs(_CarouselControls, {
|
|
2232
|
-
children: [
|
|
2233
|
-
/*#__PURE__*/ jsx(Button, {
|
|
2234
|
-
isIconOnly: true,
|
|
2235
|
-
slot: "prev",
|
|
2236
|
-
variant: "primary",
|
|
2237
|
-
color: "white",
|
|
2238
|
-
className: cx('group/carousel-previous', hasReachedScrollStart && 'invisible'),
|
|
2239
|
-
children: /*#__PURE__*/ jsx(ChevronLeft, {
|
|
2240
|
-
className: "group-hover/carousel-previous:motion-safe:-translate-x-1 transition-transform"
|
|
2241
|
-
})
|
|
2242
|
-
}),
|
|
2243
|
-
/*#__PURE__*/ jsx(Button, {
|
|
2244
|
-
isIconOnly: true,
|
|
2245
|
-
slot: "next",
|
|
2246
|
-
variant: "primary",
|
|
2247
|
-
color: "white",
|
|
2248
|
-
className: cx('group/carousel-next', hasReachedScrollEnd && 'invisible'),
|
|
2249
|
-
children: /*#__PURE__*/ jsx(ChevronRight, {
|
|
2250
|
-
className: "transition-transform group-hover/carousel-next:motion-safe:translate-x-1"
|
|
2251
|
-
})
|
|
2252
|
-
})
|
|
2253
1993
|
]
|
|
2254
|
-
|
|
2255
|
-
|
|
1994
|
+
],
|
|
1995
|
+
children: children
|
|
1996
|
+
})
|
|
2256
1997
|
})
|
|
2257
|
-
})
|
|
2258
1998
|
});
|
|
2259
1999
|
};
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2000
|
+
|
|
2001
|
+
// This component is based on a copy of ../textfield/TextField, refactoring is TBD: https://github.com/code-obos/grunnmuren/pull/722#issuecomment-1931478786
|
|
2002
|
+
const inputVariants$1 = compose(input, cva({
|
|
2003
|
+
base: '',
|
|
2004
|
+
variants: {
|
|
2005
|
+
textAlign: {
|
|
2006
|
+
right: 'text-right',
|
|
2007
|
+
left: ''
|
|
2008
|
+
},
|
|
2009
|
+
autoWidth: {
|
|
2010
|
+
true: 'max-w-fit',
|
|
2011
|
+
false: ''
|
|
2012
|
+
}
|
|
2013
|
+
}
|
|
2014
|
+
}));
|
|
2015
|
+
function NumberField(props) {
|
|
2016
|
+
const { className, description, errorMessage, label, leftAddon, isInvalid: _isInvalid, textAlign, rightAddon, withAddonDivider, size, ref, ...restProps } = props;
|
|
2017
|
+
// the order of the conditions matter here, because providing a value for isInvalid makes the validation state "controlled",
|
|
2018
|
+
// which will override any built in validation
|
|
2019
|
+
const isInvalid = !!errorMessage || _isInvalid;
|
|
2020
|
+
return /*#__PURE__*/ jsxs(NumberField$1, {
|
|
2021
|
+
...restProps,
|
|
2022
|
+
className: cx(className, formField),
|
|
2023
|
+
isInvalid: isInvalid,
|
|
2024
|
+
children: [
|
|
2025
|
+
label && /*#__PURE__*/ jsx(Label, {
|
|
2026
|
+
children: label
|
|
2027
|
+
}),
|
|
2028
|
+
description && /*#__PURE__*/ jsx(Description, {
|
|
2029
|
+
children: description
|
|
2030
|
+
}),
|
|
2031
|
+
leftAddon || rightAddon ? /*#__PURE__*/ jsxs(Group, {
|
|
2032
|
+
className: cx(inputGroup, {
|
|
2033
|
+
'w-fit': !!size
|
|
2034
|
+
}),
|
|
2035
|
+
children: [
|
|
2036
|
+
leftAddon,
|
|
2037
|
+
withAddonDivider && leftAddon && /*#__PURE__*/ jsx(InputAddonDivider, {}),
|
|
2038
|
+
/*#__PURE__*/ jsx(Input, {
|
|
2039
|
+
className: inputVariants$1({
|
|
2040
|
+
textAlign,
|
|
2041
|
+
isGrouped: true,
|
|
2042
|
+
autoWidth: !!size
|
|
2043
|
+
}),
|
|
2044
|
+
ref: ref,
|
|
2045
|
+
size: size
|
|
2046
|
+
}),
|
|
2047
|
+
withAddonDivider && rightAddon && /*#__PURE__*/ jsx(InputAddonDivider, {}),
|
|
2048
|
+
rightAddon
|
|
2049
|
+
]
|
|
2050
|
+
}) : /*#__PURE__*/ jsx(Input, {
|
|
2051
|
+
className: inputVariants$1({
|
|
2052
|
+
textAlign,
|
|
2053
|
+
autoWidth: !!size
|
|
2054
|
+
}),
|
|
2284
2055
|
ref: ref,
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
onScroll: onScroll,
|
|
2290
|
-
children: children
|
|
2056
|
+
size: size
|
|
2057
|
+
}),
|
|
2058
|
+
/*#__PURE__*/ jsx(ErrorMessageOrFieldError, {
|
|
2059
|
+
errorMessage: errorMessage
|
|
2291
2060
|
})
|
|
2061
|
+
]
|
|
2292
2062
|
});
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2063
|
+
}
|
|
2064
|
+
|
|
2065
|
+
const defaultClasses = cx([
|
|
2066
|
+
'-ml-2.5 relative inline-flex max-w-fit cursor-pointer items-start gap-4 py-2.5 pl-2.5 leading-7',
|
|
2067
|
+
// the radio button itself
|
|
2068
|
+
'before:flex-none before:rounded-full before:border-2 before:border-black',
|
|
2069
|
+
// 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.
|
|
2070
|
+
// For the ::before psuedo element the line height of the label is always 1em.
|
|
2071
|
+
// When we know the height of the label we use the height of the radio to push it down to align with the label's first line
|
|
2072
|
+
// TODO: 1.75 here is the unit less lineheight, altough we use 1.75rem as the line height, so there is a mismatch here. Revisit this when we've worked on typography in v2. Should this be a CSS custom property instead?
|
|
2073
|
+
'before:mt-[calc((1em_*_1.75_-_24px)_/_2)] before:h-[24px] before:w-[24px]',
|
|
2074
|
+
// selected
|
|
2075
|
+
'data-selected:before:border-black data-selected:before:bg-blue data-selected:before:shadow-[inset_0_0_0_4px_rgb(255,255,255)]',
|
|
2076
|
+
// hover
|
|
2077
|
+
'data-hovered:data-selected:before:border-blue-dark data-hovered:data-invalid:before:bg-red-light data-hovered:data-selected:before:bg-blue-dark data-hovered:before:border-blue data-hovered:before:bg-sky',
|
|
2078
|
+
// focus
|
|
2079
|
+
'data-focus-visible:before:ring-focus-offset',
|
|
2080
|
+
// 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
|
|
2081
|
+
// so we use an inner outline to artifically pad the border
|
|
2082
|
+
'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]'
|
|
2083
|
+
]);
|
|
2084
|
+
function Radio(props) {
|
|
2085
|
+
const { children, className, description, ...restProps } = props;
|
|
2086
|
+
return /*#__PURE__*/ jsx(Radio$1, {
|
|
2087
|
+
...restProps,
|
|
2088
|
+
className: cx(className, defaultClasses),
|
|
2089
|
+
children: /*#__PURE__*/ jsxs("div", {
|
|
2090
|
+
children: [
|
|
2091
|
+
children,
|
|
2092
|
+
description && /*#__PURE__*/ jsx(Description, {
|
|
2093
|
+
className: "mt-2 block",
|
|
2094
|
+
children: description
|
|
2095
|
+
})
|
|
2096
|
+
]
|
|
2097
|
+
})
|
|
2098
|
+
});
|
|
2099
|
+
}
|
|
2100
|
+
|
|
2101
|
+
function RadioGroup(props) {
|
|
2102
|
+
const { children, className, description, errorMessage, label, isRequired, isInvalid: _isInvalid, value, ...restProps } = props;
|
|
2103
|
+
// the order of the conditions matter here, because providing a value for isInvalid makes the validation state "controlled",
|
|
2104
|
+
// which will override any built in validation
|
|
2105
|
+
const isInvalid = !!errorMessage || _isInvalid;
|
|
2106
|
+
return /*#__PURE__*/ jsxs(RadioGroup$1, {
|
|
2107
|
+
...restProps,
|
|
2108
|
+
// Tabindex is set to -1 when the value is an empty string, which makes the radio input not focusable
|
|
2109
|
+
value: value === '' ? undefined : value,
|
|
2110
|
+
className: cx(className, 'flex flex-col gap-2'),
|
|
2111
|
+
isInvalid: isInvalid,
|
|
2112
|
+
isRequired: isRequired,
|
|
2113
|
+
children: [
|
|
2114
|
+
label && /*#__PURE__*/ jsx(Label, {
|
|
2115
|
+
children: label
|
|
2116
|
+
}),
|
|
2117
|
+
description && /*#__PURE__*/ jsx(Description, {
|
|
2118
|
+
children: description
|
|
2119
|
+
}),
|
|
2120
|
+
children,
|
|
2121
|
+
/*#__PURE__*/ jsx(ErrorMessageOrFieldError, {
|
|
2122
|
+
errorMessage: errorMessage
|
|
2123
|
+
})
|
|
2124
|
+
]
|
|
2125
|
+
});
|
|
2126
|
+
}
|
|
2127
|
+
|
|
2128
|
+
function Select(props) {
|
|
2129
|
+
const { className, children, description, errorMessage, label, isInvalid: _isInvalid, ref, ...restProps } = props;
|
|
2130
|
+
// the order of the conditions matter here, because providing a value for isInvalid makes the validation state "controlled",
|
|
2131
|
+
// which will override any built in validation
|
|
2132
|
+
const isInvalid = !!errorMessage || _isInvalid;
|
|
2133
|
+
return /*#__PURE__*/ jsxs(Select$1, {
|
|
2134
|
+
...restProps,
|
|
2135
|
+
className: cx(className, formField),
|
|
2136
|
+
isInvalid: isInvalid,
|
|
2137
|
+
children: [
|
|
2138
|
+
label && /*#__PURE__*/ jsx(Label, {
|
|
2139
|
+
children: label
|
|
2140
|
+
}),
|
|
2141
|
+
description && /*#__PURE__*/ jsx(Description, {
|
|
2142
|
+
children: description
|
|
2143
|
+
}),
|
|
2144
|
+
/*#__PURE__*/ jsxs(Button$1, {
|
|
2145
|
+
className: cx(input({
|
|
2146
|
+
focusModifier: 'visible'
|
|
2147
|
+
}), // How to reuse placeholder text?
|
|
2148
|
+
'inline-flex cursor-default items-center gap-2'),
|
|
2149
|
+
// See https://github.com/adobe/react-spectrum/discussions/4792#discussioncomment-6492305
|
|
2150
|
+
ref: ref,
|
|
2151
|
+
children: [
|
|
2152
|
+
/*#__PURE__*/ jsx(SelectValue, {
|
|
2153
|
+
className: "flex-1 truncate text-left data-[placeholder]:text-[#727070]"
|
|
2154
|
+
}),
|
|
2155
|
+
/*#__PURE__*/ jsx(ChevronDown, {
|
|
2156
|
+
className: dropdown.chevronIcon
|
|
2157
|
+
})
|
|
2306
2158
|
]
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2159
|
+
}),
|
|
2160
|
+
/*#__PURE__*/ jsx(ErrorMessageOrFieldError, {
|
|
2161
|
+
errorMessage: errorMessage
|
|
2162
|
+
}),
|
|
2163
|
+
/*#__PURE__*/ jsx(Popover, {
|
|
2164
|
+
className: dropdown.popover,
|
|
2165
|
+
children: /*#__PURE__*/ jsx(ListBox, {
|
|
2166
|
+
className: dropdown.listbox,
|
|
2167
|
+
children: children
|
|
2168
|
+
})
|
|
2169
|
+
})
|
|
2170
|
+
]
|
|
2310
2171
|
});
|
|
2311
|
-
}
|
|
2172
|
+
}
|
|
2312
2173
|
|
|
2313
2174
|
function ScrollButton({ direction, onClick, isVisible, hasScrollingOccurred, className, iconClassName }) {
|
|
2314
2175
|
const Icon = direction === 'left' ? ChevronLeft : ChevronRight;
|
|
2315
2176
|
return(// biome-ignore lint/a11y/useKeyWithClickEvents: This button is only for mouse interaction to help users scroll. Keyboard and screen reader users can navigate the content directly without needing these scroll helpers.
|
|
2177
|
+
// biome-ignore lint/a11y/noStaticElementInteractions: This button is only for mouse interaction to help users scroll. Keyboard and screen reader users can navigate the content directly without needing these scroll helpers.
|
|
2316
2178
|
/*#__PURE__*/ jsx("div", {
|
|
2317
2179
|
onClick: onClick,
|
|
2318
2180
|
className: cx(// Base scroll button styling
|
|
@@ -2375,6 +2237,126 @@ function ScrollButton({ direction, onClick, isVisible, hasScrollingOccurred, cla
|
|
|
2375
2237
|
};
|
|
2376
2238
|
}
|
|
2377
2239
|
|
|
2240
|
+
const tableVariants = cva({
|
|
2241
|
+
base: [
|
|
2242
|
+
'relative'
|
|
2243
|
+
],
|
|
2244
|
+
variants: {
|
|
2245
|
+
variant: {
|
|
2246
|
+
default: '',
|
|
2247
|
+
'zebra-striped': ''
|
|
2248
|
+
}
|
|
2249
|
+
}
|
|
2250
|
+
});
|
|
2251
|
+
const tableRowVariants = cva({
|
|
2252
|
+
base: [
|
|
2253
|
+
'data-focus-visible:outline-focus-inset',
|
|
2254
|
+
'group-data-[variant=zebra-striped]:odd:bg-white',
|
|
2255
|
+
'group-data-[variant=zebra-striped]:even:bg-sky-lightest'
|
|
2256
|
+
]
|
|
2257
|
+
});
|
|
2258
|
+
/**
|
|
2259
|
+
* A container component for displaying tabular data with horizontal scrolling support.
|
|
2260
|
+
*/ function Table(props) {
|
|
2261
|
+
const { className, children, variant = 'default', ...restProps } = props;
|
|
2262
|
+
const { scrollContainerRef, canScrollLeft, canScrollRight, hasScrollingOccurred } = useHorizontalScroll();
|
|
2263
|
+
const handleScroll = useCallback((direction)=>{
|
|
2264
|
+
const container = scrollContainerRef.current;
|
|
2265
|
+
if (!container) return;
|
|
2266
|
+
const scrollAmount = container.clientWidth * 0.8;
|
|
2267
|
+
container.scrollBy({
|
|
2268
|
+
left: direction === 'left' ? -scrollAmount : scrollAmount,
|
|
2269
|
+
behavior: 'smooth'
|
|
2270
|
+
});
|
|
2271
|
+
}, [
|
|
2272
|
+
scrollContainerRef
|
|
2273
|
+
]);
|
|
2274
|
+
return /*#__PURE__*/ jsx("div", {
|
|
2275
|
+
className: tableVariants({
|
|
2276
|
+
className,
|
|
2277
|
+
variant
|
|
2278
|
+
}),
|
|
2279
|
+
children: /*#__PURE__*/ jsxs("div", {
|
|
2280
|
+
className: "relative overflow-hidden",
|
|
2281
|
+
children: [
|
|
2282
|
+
/*#__PURE__*/ jsx(ScrollButton, {
|
|
2283
|
+
direction: "left",
|
|
2284
|
+
onClick: ()=>handleScroll('left'),
|
|
2285
|
+
isVisible: canScrollLeft,
|
|
2286
|
+
hasScrollingOccurred: hasScrollingOccurred,
|
|
2287
|
+
className: "-translate-y-1/2 absolute top-5 z-10 h-11 w-11",
|
|
2288
|
+
iconClassName: "h-5 w-5"
|
|
2289
|
+
}),
|
|
2290
|
+
/*#__PURE__*/ jsx(ScrollButton, {
|
|
2291
|
+
direction: "right",
|
|
2292
|
+
onClick: ()=>handleScroll('right'),
|
|
2293
|
+
isVisible: canScrollRight,
|
|
2294
|
+
hasScrollingOccurred: hasScrollingOccurred,
|
|
2295
|
+
className: "-translate-y-1/2 absolute top-5 z-10 h-11 w-11",
|
|
2296
|
+
iconClassName: "h-5 w-5"
|
|
2297
|
+
}),
|
|
2298
|
+
/*#__PURE__*/ jsx("div", {
|
|
2299
|
+
ref: scrollContainerRef,
|
|
2300
|
+
className: "scrollbar-hidden overflow-x-auto",
|
|
2301
|
+
style: {
|
|
2302
|
+
WebkitOverflowScrolling: 'touch'
|
|
2303
|
+
},
|
|
2304
|
+
children: /*#__PURE__*/ jsx(Table$1, {
|
|
2305
|
+
...restProps,
|
|
2306
|
+
className: "group w-full min-w-fit",
|
|
2307
|
+
"data-variant": variant,
|
|
2308
|
+
children: children
|
|
2309
|
+
})
|
|
2310
|
+
})
|
|
2311
|
+
]
|
|
2312
|
+
})
|
|
2313
|
+
});
|
|
2314
|
+
}
|
|
2315
|
+
/**
|
|
2316
|
+
* Container for table column headers.
|
|
2317
|
+
*/ function TableHeader({ className, children, ...restProps }) {
|
|
2318
|
+
return /*#__PURE__*/ jsx(TableHeader$1, {
|
|
2319
|
+
...restProps,
|
|
2320
|
+
className: cx(className, 'border-black border-b'),
|
|
2321
|
+
children: children
|
|
2322
|
+
});
|
|
2323
|
+
}
|
|
2324
|
+
function TableColumn(props) {
|
|
2325
|
+
const { className, children, ...restProps } = props;
|
|
2326
|
+
return /*#__PURE__*/ jsx(Column, {
|
|
2327
|
+
...restProps,
|
|
2328
|
+
className: cx(className, 'px-4 py-3 text-left font-medium text-black text-sm', 'data-focus-visible:outline-focus-inset', 'min-w-fit whitespace-nowrap'),
|
|
2329
|
+
children: children
|
|
2330
|
+
});
|
|
2331
|
+
}
|
|
2332
|
+
/**
|
|
2333
|
+
* Container for table rows.
|
|
2334
|
+
*/ function TableBody({ className, children, ...restProps }) {
|
|
2335
|
+
return /*#__PURE__*/ jsx(TableBody$1, {
|
|
2336
|
+
...restProps,
|
|
2337
|
+
className: className,
|
|
2338
|
+
children: children
|
|
2339
|
+
});
|
|
2340
|
+
}
|
|
2341
|
+
function TableRow(props) {
|
|
2342
|
+
const { className, children, ...restProps } = props;
|
|
2343
|
+
return /*#__PURE__*/ jsx(Row, {
|
|
2344
|
+
...restProps,
|
|
2345
|
+
className: tableRowVariants({
|
|
2346
|
+
className
|
|
2347
|
+
}),
|
|
2348
|
+
children: children
|
|
2349
|
+
});
|
|
2350
|
+
}
|
|
2351
|
+
function TableCell(props) {
|
|
2352
|
+
const { className, children, ...restProps } = props;
|
|
2353
|
+
return /*#__PURE__*/ jsx(Cell, {
|
|
2354
|
+
...restProps,
|
|
2355
|
+
className: cx(className, 'px-4 py-3 text-black text-sm leading-relaxed', 'min-w-fit whitespace-nowrap', 'align-top', 'data-focus-visible:outline-focus-inset'),
|
|
2356
|
+
children: children
|
|
2357
|
+
});
|
|
2358
|
+
}
|
|
2359
|
+
|
|
2378
2360
|
const tabsVariants = cva({
|
|
2379
2361
|
base: [
|
|
2380
2362
|
'grid gap-4'
|
|
@@ -2529,180 +2511,244 @@ const tabsVariants = cva({
|
|
|
2529
2511
|
});
|
|
2530
2512
|
}
|
|
2531
2513
|
|
|
2532
|
-
const
|
|
2533
|
-
base: [
|
|
2534
|
-
'relative'
|
|
2535
|
-
],
|
|
2536
|
-
variants: {
|
|
2537
|
-
variant: {
|
|
2538
|
-
default: '',
|
|
2539
|
-
'zebra-striped': ''
|
|
2540
|
-
}
|
|
2541
|
-
}
|
|
2542
|
-
});
|
|
2543
|
-
const tableRowVariants = cva({
|
|
2514
|
+
const tagVariants = cva({
|
|
2544
2515
|
base: [
|
|
2545
|
-
'
|
|
2546
|
-
|
|
2547
|
-
'
|
|
2516
|
+
'relative flex cursor-pointer items-center gap-1 rounded-lg px-2 py-1 font-medium text-sm transition-colors duration-200',
|
|
2517
|
+
// Resting
|
|
2518
|
+
'border-2 border-black bg-white text-black',
|
|
2519
|
+
//Focus
|
|
2520
|
+
'focus-visible:outline-focus-offset',
|
|
2521
|
+
// Hover
|
|
2522
|
+
'data-hovered:bg-sky',
|
|
2523
|
+
// Selected
|
|
2524
|
+
// Allows removing
|
|
2525
|
+
'data-allows-removing:border-transparent',
|
|
2526
|
+
'data-allows-removing:bg-blue',
|
|
2527
|
+
'data-allows-removing:data-hovered:bg-blue-dark',
|
|
2528
|
+
'data-allows-removing:text-white',
|
|
2529
|
+
// Selected
|
|
2530
|
+
'aria-selected:border-transparent',
|
|
2531
|
+
'aria-selected:bg-blue',
|
|
2532
|
+
'aria-selected:data-hovered:bg-blue-dark',
|
|
2533
|
+
'aria-selected:text-white',
|
|
2534
|
+
//Icons
|
|
2535
|
+
'[&_svg]:h-4 [&_svg]:w-4'
|
|
2548
2536
|
]
|
|
2549
2537
|
});
|
|
2550
2538
|
/**
|
|
2551
|
-
* A
|
|
2552
|
-
*/ function
|
|
2553
|
-
const {
|
|
2554
|
-
|
|
2555
|
-
const handleScroll = useCallback((direction)=>{
|
|
2556
|
-
const container = scrollContainerRef.current;
|
|
2557
|
-
if (!container) return;
|
|
2558
|
-
const scrollAmount = container.clientWidth * 0.8;
|
|
2559
|
-
container.scrollBy({
|
|
2560
|
-
left: direction === 'left' ? -scrollAmount : scrollAmount,
|
|
2561
|
-
behavior: 'smooth'
|
|
2562
|
-
});
|
|
2563
|
-
}, [
|
|
2564
|
-
scrollContainerRef
|
|
2565
|
-
]);
|
|
2566
|
-
return /*#__PURE__*/ jsx("div", {
|
|
2567
|
-
className: tableVariants({
|
|
2568
|
-
className,
|
|
2569
|
-
variant
|
|
2570
|
-
}),
|
|
2571
|
-
children: /*#__PURE__*/ jsxs("div", {
|
|
2572
|
-
className: "relative overflow-hidden",
|
|
2573
|
-
children: [
|
|
2574
|
-
/*#__PURE__*/ jsx(ScrollButton, {
|
|
2575
|
-
direction: "left",
|
|
2576
|
-
onClick: ()=>handleScroll('left'),
|
|
2577
|
-
isVisible: canScrollLeft,
|
|
2578
|
-
hasScrollingOccurred: hasScrollingOccurred,
|
|
2579
|
-
className: "-translate-y-1/2 absolute top-5 z-10 h-11 w-11",
|
|
2580
|
-
iconClassName: "h-5 w-5"
|
|
2581
|
-
}),
|
|
2582
|
-
/*#__PURE__*/ jsx(ScrollButton, {
|
|
2583
|
-
direction: "right",
|
|
2584
|
-
onClick: ()=>handleScroll('right'),
|
|
2585
|
-
isVisible: canScrollRight,
|
|
2586
|
-
hasScrollingOccurred: hasScrollingOccurred,
|
|
2587
|
-
className: "-translate-y-1/2 absolute top-5 z-10 h-11 w-11",
|
|
2588
|
-
iconClassName: "h-5 w-5"
|
|
2589
|
-
}),
|
|
2590
|
-
/*#__PURE__*/ jsx("div", {
|
|
2591
|
-
ref: scrollContainerRef,
|
|
2592
|
-
className: "scrollbar-hidden overflow-x-auto",
|
|
2593
|
-
style: {
|
|
2594
|
-
WebkitOverflowScrolling: 'touch'
|
|
2595
|
-
},
|
|
2596
|
-
children: /*#__PURE__*/ jsx(Table$1, {
|
|
2597
|
-
...restProps,
|
|
2598
|
-
className: "group w-full min-w-fit",
|
|
2599
|
-
"data-variant": variant,
|
|
2600
|
-
children: children
|
|
2601
|
-
})
|
|
2602
|
-
})
|
|
2603
|
-
]
|
|
2604
|
-
})
|
|
2605
|
-
});
|
|
2606
|
-
}
|
|
2607
|
-
/**
|
|
2608
|
-
* Container for table column headers.
|
|
2609
|
-
*/ function TableHeader({ className, children, ...restProps }) {
|
|
2610
|
-
return /*#__PURE__*/ jsx(TableHeader$1, {
|
|
2539
|
+
* A group component for Tag components that enables selection and organization of options.
|
|
2540
|
+
*/ function TagGroup(props) {
|
|
2541
|
+
const { onRemove, selectionMode = 'single', className, children, ...restProps } = props;
|
|
2542
|
+
return /*#__PURE__*/ jsx(TagGroup$1, {
|
|
2611
2543
|
...restProps,
|
|
2612
|
-
className:
|
|
2544
|
+
className: className,
|
|
2545
|
+
selectionMode: onRemove ? 'none' : selectionMode,
|
|
2546
|
+
onRemove: onRemove,
|
|
2613
2547
|
children: children
|
|
2614
2548
|
});
|
|
2615
2549
|
}
|
|
2616
|
-
|
|
2550
|
+
/**
|
|
2551
|
+
* A container component for Tag components within a TagGroup.
|
|
2552
|
+
*/ function TagList(props) {
|
|
2617
2553
|
const { className, children, ...restProps } = props;
|
|
2618
|
-
return /*#__PURE__*/ jsx(
|
|
2554
|
+
return /*#__PURE__*/ jsx(TagList$1, {
|
|
2619
2555
|
...restProps,
|
|
2620
|
-
className: cx(
|
|
2556
|
+
className: cx('flex flex-wrap gap-2', className),
|
|
2621
2557
|
children: children
|
|
2622
2558
|
});
|
|
2623
2559
|
}
|
|
2624
2560
|
/**
|
|
2625
|
-
*
|
|
2626
|
-
*/ function
|
|
2627
|
-
return /*#__PURE__*/ jsx(TableBody$1, {
|
|
2628
|
-
...restProps,
|
|
2629
|
-
className: className,
|
|
2630
|
-
children: children
|
|
2631
|
-
});
|
|
2632
|
-
}
|
|
2633
|
-
function TableRow(props) {
|
|
2561
|
+
* Interactive tag component for selections, filtering, and categorization.
|
|
2562
|
+
*/ function Tag(props) {
|
|
2634
2563
|
const { className, children, ...restProps } = props;
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
className:
|
|
2564
|
+
const textValue = typeof children === 'string' ? children : undefined;
|
|
2565
|
+
return /*#__PURE__*/ jsx(Tag$1, {
|
|
2566
|
+
className: tagVariants({
|
|
2638
2567
|
className
|
|
2639
2568
|
}),
|
|
2640
|
-
|
|
2641
|
-
});
|
|
2642
|
-
}
|
|
2643
|
-
function TableCell(props) {
|
|
2644
|
-
const { className, children, ...restProps } = props;
|
|
2645
|
-
return /*#__PURE__*/ jsx(Cell, {
|
|
2569
|
+
textValue: textValue,
|
|
2646
2570
|
...restProps,
|
|
2647
|
-
|
|
2648
|
-
|
|
2571
|
+
children: ({ allowsRemoving })=>allowsRemoving ? /*#__PURE__*/ jsxs(Fragment, {
|
|
2572
|
+
children: [
|
|
2573
|
+
children,
|
|
2574
|
+
/*#__PURE__*/ jsx(Button$1, {
|
|
2575
|
+
className: "cursor-pointer outline-none after:absolute after:top-0 after:right-0 after:bottom-0 after:left-0",
|
|
2576
|
+
slot: "remove",
|
|
2577
|
+
children: /*#__PURE__*/ jsx(Close, {
|
|
2578
|
+
className: "ml-1"
|
|
2579
|
+
})
|
|
2580
|
+
})
|
|
2581
|
+
]
|
|
2582
|
+
}) : children
|
|
2649
2583
|
});
|
|
2650
2584
|
}
|
|
2651
2585
|
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
*/
|
|
2656
|
-
return /*#__PURE__*/ jsx(Link, {
|
|
2586
|
+
function TextArea(props) {
|
|
2587
|
+
const { className, description, errorMessage, label, isInvalid: _isInvalid, rows, ref, ...restProps } = props;
|
|
2588
|
+
const isInvalid = !!errorMessage || _isInvalid;
|
|
2589
|
+
return /*#__PURE__*/ jsxs(TextField$1, {
|
|
2657
2590
|
...restProps,
|
|
2658
|
-
className: cx(className,
|
|
2659
|
-
|
|
2591
|
+
className: cx(className, formField),
|
|
2592
|
+
isInvalid: isInvalid,
|
|
2593
|
+
children: [
|
|
2594
|
+
label && /*#__PURE__*/ jsx(Label, {
|
|
2595
|
+
children: label
|
|
2596
|
+
}),
|
|
2597
|
+
description && /*#__PURE__*/ jsx(Description, {
|
|
2598
|
+
children: description
|
|
2599
|
+
}),
|
|
2600
|
+
/*#__PURE__*/ jsx(TextArea$1, {
|
|
2601
|
+
className: input(),
|
|
2602
|
+
rows: rows,
|
|
2603
|
+
ref: ref
|
|
2604
|
+
}),
|
|
2605
|
+
/*#__PURE__*/ jsx(ErrorMessageOrFieldError, {
|
|
2606
|
+
errorMessage: errorMessage
|
|
2607
|
+
})
|
|
2608
|
+
]
|
|
2660
2609
|
});
|
|
2661
|
-
}
|
|
2610
|
+
}
|
|
2662
2611
|
|
|
2663
|
-
const
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
'
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
(numberofLinks === 9 || numberofLinks > 10) && '@4xl:grid-cols-3'
|
|
2675
|
-
]),
|
|
2676
|
-
children: children
|
|
2677
|
-
})
|
|
2678
|
-
});
|
|
2679
|
-
};
|
|
2680
|
-
const LinkListItem = ({ children, isExternal, className, ...restProps })=>{
|
|
2681
|
-
let Icon = ArrowRight;
|
|
2682
|
-
let iconTransition = 'group-hover:motion-safe:translate-x-1';
|
|
2683
|
-
if (restProps.download) {
|
|
2684
|
-
Icon = Download;
|
|
2685
|
-
iconTransition = 'group-hover:motion-safe:translate-y-1';
|
|
2686
|
-
} else if (isExternal) {
|
|
2687
|
-
iconTransition = 'group-hover:motion-safe:-translate-y-0.5 group-hover:motion-safe:translate-x-0.5';
|
|
2688
|
-
Icon = LinkExternal;
|
|
2612
|
+
const inputVariants = compose(input, cva({
|
|
2613
|
+
base: '',
|
|
2614
|
+
variants: {
|
|
2615
|
+
textAlign: {
|
|
2616
|
+
right: 'text-right',
|
|
2617
|
+
left: ''
|
|
2618
|
+
},
|
|
2619
|
+
autoWidth: {
|
|
2620
|
+
true: 'max-w-fit',
|
|
2621
|
+
false: ''
|
|
2622
|
+
}
|
|
2689
2623
|
}
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2624
|
+
}));
|
|
2625
|
+
function TextField(props) {
|
|
2626
|
+
const { className, description, errorMessage, label, leftAddon, isInvalid: _isInvalid, textAlign, rightAddon, withAddonDivider, size, ref, ...restProps } = props;
|
|
2627
|
+
// the order of the conditions matter here, because providing a value for isInvalid makes the validation state "controlled",
|
|
2628
|
+
// which will override any built in validation
|
|
2629
|
+
const isInvalid = !!errorMessage || _isInvalid;
|
|
2630
|
+
return /*#__PURE__*/ jsxs(TextField$1, {
|
|
2631
|
+
...restProps,
|
|
2632
|
+
className: cx(className, formField),
|
|
2633
|
+
isInvalid: isInvalid,
|
|
2634
|
+
children: [
|
|
2635
|
+
label && /*#__PURE__*/ jsx(Label, {
|
|
2636
|
+
children: label
|
|
2637
|
+
}),
|
|
2638
|
+
description && /*#__PURE__*/ jsx(Description, {
|
|
2639
|
+
children: description
|
|
2640
|
+
}),
|
|
2641
|
+
leftAddon || rightAddon ? /*#__PURE__*/ jsxs(Group, {
|
|
2642
|
+
className: cx(inputGroup, {
|
|
2643
|
+
'w-fit': !!size
|
|
2699
2644
|
}),
|
|
2700
|
-
|
|
2701
|
-
|
|
2645
|
+
children: [
|
|
2646
|
+
leftAddon,
|
|
2647
|
+
withAddonDivider && leftAddon && /*#__PURE__*/ jsx(InputAddonDivider, {}),
|
|
2648
|
+
/*#__PURE__*/ jsx(Input, {
|
|
2649
|
+
className: inputVariants({
|
|
2650
|
+
textAlign,
|
|
2651
|
+
isGrouped: true,
|
|
2652
|
+
autoWidth: !!size
|
|
2653
|
+
}),
|
|
2654
|
+
ref: ref,
|
|
2655
|
+
size: size
|
|
2656
|
+
}),
|
|
2657
|
+
withAddonDivider && rightAddon && /*#__PURE__*/ jsx(InputAddonDivider, {}),
|
|
2658
|
+
rightAddon
|
|
2659
|
+
]
|
|
2660
|
+
}) : /*#__PURE__*/ jsx(Input, {
|
|
2661
|
+
className: inputVariants({
|
|
2662
|
+
textAlign,
|
|
2663
|
+
autoWidth: !!size
|
|
2664
|
+
}),
|
|
2665
|
+
ref: ref,
|
|
2666
|
+
size: size
|
|
2667
|
+
}),
|
|
2668
|
+
/*#__PURE__*/ jsx(ErrorMessageOrFieldError, {
|
|
2669
|
+
errorMessage: errorMessage
|
|
2670
|
+
})
|
|
2671
|
+
]
|
|
2672
|
+
});
|
|
2673
|
+
}
|
|
2674
|
+
|
|
2675
|
+
const VideoLoop = ({ src, format, alt, className })=>{
|
|
2676
|
+
// Control the video playback state, so that the user can pause and play the video at will, also control the video autoplay
|
|
2677
|
+
const [shouldPlay, setShouldPlay] = useState(false);
|
|
2678
|
+
// 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)
|
|
2679
|
+
const [isPlaying, setIsPlaying] = useState(false);
|
|
2680
|
+
// We need to check if the user prefers reduced motion, so that we can prevent the video from autoplaying if so
|
|
2681
|
+
const [userPrefersReducedMotion, setUserPrefersReducedMotion] = useState(null);
|
|
2682
|
+
const videoRef = useRef(null);
|
|
2683
|
+
useEffect(()=>{
|
|
2684
|
+
const { matches: userPrefersReducedMotion } = matchMedia('(prefers-reduced-motion: reduce)');
|
|
2685
|
+
setUserPrefersReducedMotion(userPrefersReducedMotion);
|
|
2686
|
+
// Autoplay the video if the user does not prefer reduced motion
|
|
2687
|
+
setShouldPlay(!userPrefersReducedMotion);
|
|
2688
|
+
}, []);
|
|
2689
|
+
// Follow google's autoplay policy: https://developers.google.com/web/updates/2017/09/autoplay-policy-changes
|
|
2690
|
+
// "Don't assume a video will play, and don't show a pause button when the video is not actually playing."
|
|
2691
|
+
// "You should always look at the Promise returned by the play function to see if it was rejected:"
|
|
2692
|
+
// 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
|
|
2693
|
+
useEffect(()=>{
|
|
2694
|
+
if (!videoRef.current) return;
|
|
2695
|
+
if (shouldPlay) {
|
|
2696
|
+
videoRef.current.play().then(()=>setIsPlaying(true)).catch(()=>setIsPlaying(false));
|
|
2697
|
+
} else {
|
|
2698
|
+
videoRef.current.pause();
|
|
2699
|
+
setIsPlaying(false);
|
|
2700
|
+
}
|
|
2701
|
+
}, [
|
|
2702
|
+
shouldPlay
|
|
2703
|
+
]);
|
|
2704
|
+
return /*#__PURE__*/ jsxs("div", {
|
|
2705
|
+
className: cx(className, 'relative', userPrefersReducedMotion === null && 'opacity-0'),
|
|
2706
|
+
children: [
|
|
2707
|
+
/*#__PURE__*/ jsx("video", {
|
|
2708
|
+
"aria-hidden": true,
|
|
2709
|
+
ref: videoRef,
|
|
2710
|
+
// cursor-pointer is not working on the button below, so we add it here for the same effect
|
|
2711
|
+
className: "h-full max-h-[inherit] w-full cursor-pointer rounded-[inherit] object-cover",
|
|
2712
|
+
playsInline: true,
|
|
2713
|
+
loop: userPrefersReducedMotion === false,
|
|
2714
|
+
autoPlay: userPrefersReducedMotion === false,
|
|
2715
|
+
muted: true,
|
|
2716
|
+
onEnded: (event)=>{
|
|
2717
|
+
if (userPrefersReducedMotion) {
|
|
2718
|
+
// Reset the video to the beginning if the user prefers reduced motion, since the video will not loop
|
|
2719
|
+
event.currentTarget.currentTime = 0;
|
|
2720
|
+
setShouldPlay(false);
|
|
2721
|
+
setIsPlaying(false);
|
|
2722
|
+
}
|
|
2723
|
+
},
|
|
2724
|
+
children: /*#__PURE__*/ jsx("source", {
|
|
2725
|
+
src: src,
|
|
2726
|
+
type: `video/${format}`
|
|
2702
2727
|
})
|
|
2703
|
-
|
|
2704
|
-
|
|
2728
|
+
}),
|
|
2729
|
+
userPrefersReducedMotion !== null && /*#__PURE__*/ jsx("button", {
|
|
2730
|
+
"data-slot": "video-loop-button",
|
|
2731
|
+
"aria-hidden": true,
|
|
2732
|
+
type: "button",
|
|
2733
|
+
onClick: ()=>setShouldPlay((prevState)=>!prevState),
|
|
2734
|
+
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', 'rounded-[inherit]', // Setting the opacity to 0 before applying the transition below will ensure the button only fades in after the video has started playing
|
|
2735
|
+
shouldPlay && 'opacity-0', isPlaying && [
|
|
2736
|
+
'transition-opacity duration-200',
|
|
2737
|
+
// Only show the pause button when the video is hovered or focused
|
|
2738
|
+
'focus-visible:opacity-100',
|
|
2739
|
+
'hover:opacity-100'
|
|
2740
|
+
]),
|
|
2741
|
+
children: /*#__PURE__*/ jsx("span", {
|
|
2742
|
+
className: "grid h-12 w-12 place-items-center rounded-full bg-white outline-hidden",
|
|
2743
|
+
children: isPlaying ? /*#__PURE__*/ jsx(PlayerPause, {}) : /*#__PURE__*/ jsx(PlayerPlay, {})
|
|
2744
|
+
})
|
|
2745
|
+
}),
|
|
2746
|
+
alt && /*#__PURE__*/ jsx("p", {
|
|
2747
|
+
className: "sr-only",
|
|
2748
|
+
children: alt
|
|
2749
|
+
})
|
|
2750
|
+
]
|
|
2705
2751
|
});
|
|
2706
2752
|
};
|
|
2707
2753
|
|
|
2708
|
-
export { Accordion, AccordionItem, Alertbox, Avatar, Backlink, Badge, Breadcrumb, Breadcrumbs, Button, ButtonContext, Caption, Card, CardLink, Checkbox, CheckboxGroup, Combobox, ListBoxHeader as ComboboxHeader, ListBoxItem as ComboboxItem, ListBoxSection as ComboboxSection, Content, ContentContext, DateFormatter, Description, DisclosureStateContext, ErrorMessage, Footer, GrunnmurenProvider, Heading, HeadingContext, Label, Media, MediaContext, NumberField, Radio, RadioGroup, Select, ListBoxHeader as SelectHeader, ListBoxItem as SelectItem, ListBoxSection as SelectSection, TextArea, TextField, Carousel as UNSAFE_Carousel, CarouselItem as UNSAFE_CarouselItem, CarouselItems as UNSAFE_CarouselItems, Dialog as UNSAFE_Dialog, DialogTrigger as UNSAFE_DialogTrigger, Disclosure as UNSAFE_Disclosure, DisclosureButton as UNSAFE_DisclosureButton, DisclosurePanel as UNSAFE_DisclosurePanel, FileUpload as UNSAFE_FileUpload, Hero as UNSAFE_Hero, CustomLink as UNSAFE_Link, LinkList as UNSAFE_LinkList, LinkListItem as UNSAFE_LinkListItem, Modal as UNSAFE_Modal, Tab as UNSAFE_Tab, TabList as UNSAFE_TabList, TabPanel as UNSAFE_TabPanel, Table as UNSAFE_Table, TableBody as UNSAFE_TableBody, TableCell as UNSAFE_TableCell, TableColumn as UNSAFE_TableColumn, TableHeader as UNSAFE_TableHeader, TableRow as UNSAFE_TableRow, Tabs as UNSAFE_Tabs,
|
|
2754
|
+
export { Accordion, AccordionItem, Alertbox, Avatar, Backlink, Badge, Breadcrumb, Breadcrumbs, Button, ButtonContext, Caption, Card, CardLink, Checkbox, CheckboxGroup, Combobox, ListBoxHeader as ComboboxHeader, ListBoxItem as ComboboxItem, ListBoxSection as ComboboxSection, Content, ContentContext, DateFormatter, Description, DisclosureStateContext, ErrorMessage, Footer, GrunnmurenProvider, Heading, HeadingContext, Label, Media, MediaContext, NumberField, Radio, RadioGroup, Select, ListBoxHeader as SelectHeader, ListBoxItem as SelectItem, ListBoxSection as SelectSection, Tag, TagGroup, TagList, TextArea, TextField, Carousel as UNSAFE_Carousel, CarouselItem as UNSAFE_CarouselItem, CarouselItems as UNSAFE_CarouselItems, Dialog as UNSAFE_Dialog, DialogTrigger as UNSAFE_DialogTrigger, Disclosure as UNSAFE_Disclosure, DisclosureButton as UNSAFE_DisclosureButton, DisclosurePanel as UNSAFE_DisclosurePanel, FileUpload as UNSAFE_FileUpload, Hero as UNSAFE_Hero, CustomLink as UNSAFE_Link, LinkList as UNSAFE_LinkList, LinkListItem as UNSAFE_LinkListItem, Modal as UNSAFE_Modal, Tab as UNSAFE_Tab, TabList as UNSAFE_TabList, TabPanel as UNSAFE_TabPanel, Table as UNSAFE_Table, TableBody as UNSAFE_TableBody, TableCell as UNSAFE_TableCell, TableColumn as UNSAFE_TableColumn, TableHeader as UNSAFE_TableHeader, TableRow as UNSAFE_TableRow, Tabs as UNSAFE_Tabs, VideoLoop, _useLocale as useLocale };
|