@obosbbl/grunnmuren-react 3.0.16 → 3.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +44 -61
- package/dist/index.mjs +289 -246
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { useContextProps, Provider,
|
|
2
|
+
import { useContextProps, DisclosureContext, DisclosureGroupStateContext, Provider, ButtonContext as ButtonContext$1, DEFAULT_SLOT, Button as Button$1, 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, Close, InfoCircle, CheckCircle, Warning, Error, User, ChevronLeft, ChevronRight, LoadingSpinner, Check, Trash, ArrowRight, Download, LinkExternal, PlayerPause, PlayerPlay } from '@obosbbl/grunnmuren-icons-react';
|
|
6
|
-
import { useLayoutEffect, useUpdateEffect, filterDOMProps, mergeRefs, mergeProps, useObjectRef, useFormReset } from '@react-aria/utils';
|
|
7
5
|
import { cva, cx, compose } from 'cva';
|
|
8
|
-
import { createContext,
|
|
9
|
-
import {
|
|
10
|
-
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';
|
|
11
10
|
import { useDisclosureState } from 'react-stately';
|
|
11
|
+
import { useDebouncedCallback } from 'use-debounce';
|
|
12
12
|
import { useFormValidation } from '@react-aria/form';
|
|
13
13
|
import { useFormValidationState } from '@react-stately/form';
|
|
14
14
|
import { useControlledState } from '@react-stately/utils';
|
|
@@ -90,11 +90,176 @@ const Footer = (props)=>/*#__PURE__*/ jsx("div", {
|
|
|
90
90
|
"data-slot": "footer"
|
|
91
91
|
});
|
|
92
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
|
+
|
|
93
257
|
function Accordion(props) {
|
|
94
258
|
const { children, className, ...restProps } = props;
|
|
95
259
|
const childCount = Children.count(children);
|
|
96
260
|
return /*#__PURE__*/ jsx("div", {
|
|
97
261
|
...restProps,
|
|
262
|
+
"data-accordion": true,
|
|
98
263
|
className: cx('rounded-lg bg-white', className),
|
|
99
264
|
children: Children.map(children, (child, index)=>/*#__PURE__*/ jsxs(Fragment, {
|
|
100
265
|
children: [
|
|
@@ -109,40 +274,16 @@ function Accordion(props) {
|
|
|
109
274
|
});
|
|
110
275
|
}
|
|
111
276
|
function AccordionItem(props) {
|
|
112
|
-
const { className, children, defaultOpen
|
|
113
|
-
const
|
|
114
|
-
const
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
// regardless if we are controlled or uncontrolled.
|
|
118
|
-
// If we are controlled, we use a layout effect to sync the controlled state
|
|
119
|
-
// with the internal state.
|
|
120
|
-
//
|
|
121
|
-
const [isOpen, setIsOpen] = useState(// If we are controlled, use that open state, otherwise use the uncontrolled
|
|
122
|
-
isControlled ? controlledIsOpen : defaultOpen);
|
|
123
|
-
useLayoutEffect(()=>{
|
|
124
|
-
if (isControlled) {
|
|
125
|
-
setIsOpen(controlledIsOpen);
|
|
126
|
-
}
|
|
127
|
-
}, [
|
|
128
|
-
controlledIsOpen,
|
|
129
|
-
isControlled
|
|
130
|
-
]);
|
|
131
|
-
const handleOpenChange = ()=>{
|
|
132
|
-
const newOpenState = !isOpen;
|
|
133
|
-
if (!isControlled) {
|
|
134
|
-
setIsOpen(newOpenState);
|
|
135
|
-
}
|
|
136
|
-
// Always call the change handler, even if we're uncontrolled.
|
|
137
|
-
// Easier to add stuff such as tracking etc.
|
|
138
|
-
if (onOpenChange) {
|
|
139
|
-
onOpenChange(newOpenState);
|
|
140
|
-
}
|
|
141
|
-
};
|
|
142
|
-
return /*#__PURE__*/ jsx("div", {
|
|
277
|
+
const { className, children, defaultOpen, isOpen: controlledIsOpen, onOpenChange, defaultExpanded = false, isExpanded, onExpandedChange, ...restProps } = props;
|
|
278
|
+
const _defaultExpanded = defaultOpen ?? defaultExpanded;
|
|
279
|
+
const _isExpanded = controlledIsOpen ?? isExpanded;
|
|
280
|
+
const _onExpandedChange = onOpenChange ?? onExpandedChange;
|
|
281
|
+
return /*#__PURE__*/ jsx(Disclosure, {
|
|
143
282
|
...restProps,
|
|
144
283
|
className: cx('relative px-2', className),
|
|
145
|
-
|
|
284
|
+
defaultExpanded: _defaultExpanded,
|
|
285
|
+
onExpandedChange: _onExpandedChange,
|
|
286
|
+
isExpanded: _isExpanded,
|
|
146
287
|
children: /*#__PURE__*/ jsx(Provider, {
|
|
147
288
|
values: [
|
|
148
289
|
[
|
|
@@ -152,33 +293,22 @@ function AccordionItem(props) {
|
|
|
152
293
|
className: 'font-medium leading-7 -mx-2 text-base',
|
|
153
294
|
// Supply a default level here to make this typecheck ok. Will be overwritten with the consumers set heading level anyways
|
|
154
295
|
level: 3,
|
|
155
|
-
_innerWrapper: (children)=>/*#__PURE__*/
|
|
156
|
-
"aria-controls": contentId,
|
|
157
|
-
"aria-expanded": isOpen,
|
|
296
|
+
_innerWrapper: (children)=>/*#__PURE__*/ jsx(DisclosureButton, {
|
|
158
297
|
// 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
|
|
159
|
-
className: "flex min-h-
|
|
160
|
-
id: buttonId,
|
|
161
|
-
onClick: handleOpenChange,
|
|
298
|
+
className: "flex min-h-11 w-full gap-1.5 rounded-lg px-2 py-3.5 text-left focus-visible:outline-focus-inset!",
|
|
162
299
|
type: "button",
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
/*#__PURE__*/ jsx(ChevronDown, {
|
|
166
|
-
className: cx('flex-none transition-transform duration-300 motion-reduce:transition-none', isOpen && 'rotate-180')
|
|
167
|
-
})
|
|
168
|
-
]
|
|
300
|
+
withChevron: true,
|
|
301
|
+
children: children
|
|
169
302
|
})
|
|
170
303
|
}
|
|
171
304
|
],
|
|
172
305
|
[
|
|
173
306
|
ContentContext,
|
|
174
307
|
{
|
|
175
|
-
className: // Uses pseudo
|
|
176
|
-
'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',
|
|
308
|
+
className: // Uses pseudo elements for vertical padding, since that doesn't affect the height when the accordion is closed
|
|
309
|
+
'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',
|
|
177
310
|
role: 'region',
|
|
178
|
-
|
|
179
|
-
'aria-labelledby': buttonId,
|
|
180
|
-
_outerWrapper: (children)=>/*#__PURE__*/ jsx("div", {
|
|
181
|
-
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]'),
|
|
311
|
+
_outerWrapper: (children)=>/*#__PURE__*/ jsx(DisclosurePanel, {
|
|
182
312
|
children: children
|
|
183
313
|
})
|
|
184
314
|
}
|
|
@@ -808,11 +938,14 @@ const cardLinkVariants = cva({
|
|
|
808
938
|
});
|
|
809
939
|
};
|
|
810
940
|
|
|
811
|
-
const Carousel = ({ className, children, onChange })=>{
|
|
941
|
+
const Carousel = ({ className, children, onChange, ...rest })=>{
|
|
812
942
|
const ref = useRef(null);
|
|
813
943
|
const locale = _useLocale();
|
|
814
944
|
const { previous, next } = translations$1;
|
|
815
945
|
const [scrollTargetIndex, setScrollTargetIndex] = useState(0);
|
|
946
|
+
const isScrollingProgrammatically = useRef(false);
|
|
947
|
+
const scrollTimeoutRef = useRef(null);
|
|
948
|
+
const scrollQueue = useRef([]);
|
|
816
949
|
const [hasReachedScrollStart, setHasReachedScrollStart] = useState(scrollTargetIndex === 0);
|
|
817
950
|
const [hasReachedScrollEnd, setHasReachedScrollEnd] = useState(!ref.current || ref.current.children.length - 1 === scrollTargetIndex);
|
|
818
951
|
useEffect(()=>{
|
|
@@ -824,9 +957,27 @@ const Carousel = ({ className, children, onChange })=>{
|
|
|
824
957
|
// Keep track of the previous index to determine if the user is scrolling forward or backward
|
|
825
958
|
// This is used to determine which callback to call (onPrev or onNext)
|
|
826
959
|
const prevIndex = useRef(0);
|
|
960
|
+
// Processes the next scroll action in the queue, if any
|
|
961
|
+
// All clicks on the prev/next buttons are queued while a programmatic scroll is in progress
|
|
962
|
+
// This is to ensure that rapid clicks on the buttons do not cause janky scrolling behavior
|
|
963
|
+
// while still a snappy response to user clicks
|
|
964
|
+
const processQueue = ()=>{
|
|
965
|
+
if (scrollQueue.current.length > 0 && !isScrollingProgrammatically.current) {
|
|
966
|
+
const nextIndex = scrollQueue.current?.shift();
|
|
967
|
+
if (nextIndex !== undefined) {
|
|
968
|
+
setScrollTargetIndex(nextIndex);
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
};
|
|
827
972
|
// Handle scrolling when user clicks the arrow icons
|
|
828
973
|
useUpdateEffect(()=>{
|
|
829
|
-
if (!ref.current)
|
|
974
|
+
if (!ref.current) {
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
977
|
+
if (scrollTimeoutRef.current) {
|
|
978
|
+
clearTimeout(scrollTimeoutRef.current);
|
|
979
|
+
}
|
|
980
|
+
isScrollingProgrammatically.current = true;
|
|
830
981
|
ref.current.children[scrollTargetIndex]?.scrollIntoView({
|
|
831
982
|
behavior: 'smooth',
|
|
832
983
|
inline: 'start',
|
|
@@ -841,20 +992,76 @@ const Carousel = ({ className, children, onChange })=>{
|
|
|
841
992
|
});
|
|
842
993
|
}
|
|
843
994
|
prevIndex.current = scrollTargetIndex;
|
|
995
|
+
scrollTimeoutRef.current = setTimeout(()=>{
|
|
996
|
+
isScrollingProgrammatically.current = false;
|
|
997
|
+
scrollTimeoutRef.current = null;
|
|
998
|
+
processQueue(); // Process any queued scrolls
|
|
999
|
+
}, 500);
|
|
844
1000
|
}, [
|
|
845
1001
|
scrollTargetIndex
|
|
846
1002
|
]);
|
|
1003
|
+
// Clean up timeout on unmount
|
|
1004
|
+
useEffect(()=>{
|
|
1005
|
+
return ()=>{
|
|
1006
|
+
if (scrollTimeoutRef.current) {
|
|
1007
|
+
clearTimeout(scrollTimeoutRef.current);
|
|
1008
|
+
}
|
|
1009
|
+
};
|
|
1010
|
+
}, []);
|
|
847
1011
|
const onScroll = useDebouncedCallback((event)=>{
|
|
1012
|
+
// Ignore scroll events when we're programmatically scrolling
|
|
1013
|
+
if (isScrollingProgrammatically.current) {
|
|
1014
|
+
return;
|
|
1015
|
+
}
|
|
848
1016
|
const target = event.target;
|
|
1017
|
+
const containerRect = target.getBoundingClientRect();
|
|
849
1018
|
// Calculate the index of the item that is currently in view
|
|
850
1019
|
const newScrollTargetIndex = Array.from(target.children).findIndex((child)=>{
|
|
851
1020
|
const rect = child.getBoundingClientRect();
|
|
852
|
-
|
|
1021
|
+
// Check if the item is more than 50% visible within the container
|
|
1022
|
+
const visibleWidth = Math.min(rect.right, containerRect.right) - Math.max(rect.left, containerRect.left);
|
|
1023
|
+
const itemWidth = rect.width;
|
|
1024
|
+
return visibleWidth / itemWidth > 0.5;
|
|
853
1025
|
});
|
|
854
|
-
if (newScrollTargetIndex !== -1) {
|
|
1026
|
+
if (newScrollTargetIndex !== -1 && newScrollTargetIndex !== scrollTargetIndex) {
|
|
1027
|
+
if (onChange) {
|
|
1028
|
+
onChange({
|
|
1029
|
+
index: newScrollTargetIndex,
|
|
1030
|
+
id: target.children[newScrollTargetIndex]?.id,
|
|
1031
|
+
prevIndex: prevIndex.current,
|
|
1032
|
+
prevId: target.children[prevIndex.current]?.id
|
|
1033
|
+
});
|
|
1034
|
+
}
|
|
1035
|
+
// Update the index and prevIndex
|
|
855
1036
|
setScrollTargetIndex(newScrollTargetIndex);
|
|
1037
|
+
prevIndex.current = newScrollTargetIndex;
|
|
1038
|
+
}
|
|
1039
|
+
}, 150);
|
|
1040
|
+
const handlePrevious = ()=>{
|
|
1041
|
+
const targetIndex = scrollTargetIndex - 1;
|
|
1042
|
+
if (targetIndex < 0) return;
|
|
1043
|
+
if (isScrollingProgrammatically.current) {
|
|
1044
|
+
// If we're already scrolling, queue this action
|
|
1045
|
+
scrollQueue.current = [
|
|
1046
|
+
targetIndex
|
|
1047
|
+
];
|
|
1048
|
+
} else {
|
|
1049
|
+
setScrollTargetIndex(targetIndex);
|
|
1050
|
+
}
|
|
1051
|
+
};
|
|
1052
|
+
const handleNext = ()=>{
|
|
1053
|
+
if (!ref.current) return;
|
|
1054
|
+
const targetIndex = scrollTargetIndex + 1;
|
|
1055
|
+
if (targetIndex >= ref.current.children.length) return;
|
|
1056
|
+
if (isScrollingProgrammatically.current) {
|
|
1057
|
+
// If we're already scrolling, queue this action
|
|
1058
|
+
scrollQueue.current = [
|
|
1059
|
+
targetIndex
|
|
1060
|
+
];
|
|
1061
|
+
} else {
|
|
1062
|
+
setScrollTargetIndex(targetIndex);
|
|
856
1063
|
}
|
|
857
|
-
}
|
|
1064
|
+
};
|
|
858
1065
|
return /*#__PURE__*/ jsx("div", {
|
|
859
1066
|
"data-slot": "carousel",
|
|
860
1067
|
children: /*#__PURE__*/ jsx(Provider, {
|
|
@@ -873,29 +1080,19 @@ const Carousel = ({ className, children, onChange })=>{
|
|
|
873
1080
|
[DEFAULT_SLOT]: {},
|
|
874
1081
|
prev: {
|
|
875
1082
|
'aria-label': previous[locale],
|
|
876
|
-
onPress:
|
|
877
|
-
if (scrollTargetIndex > 0) {
|
|
878
|
-
setScrollTargetIndex((prev)=>prev - 1);
|
|
879
|
-
}
|
|
880
|
-
},
|
|
881
|
-
isDisabled: hasReachedScrollStart
|
|
1083
|
+
onPress: handlePrevious
|
|
882
1084
|
},
|
|
883
1085
|
next: {
|
|
884
1086
|
isIconOnly: true,
|
|
885
1087
|
'aria-label': next[locale],
|
|
886
|
-
onPress:
|
|
887
|
-
if (!ref.current) return;
|
|
888
|
-
if (scrollTargetIndex < ref.current.children.length - 1) {
|
|
889
|
-
setScrollTargetIndex((prev)=>prev + 1);
|
|
890
|
-
}
|
|
891
|
-
},
|
|
892
|
-
isDisabled: hasReachedScrollEnd
|
|
1088
|
+
onPress: handleNext
|
|
893
1089
|
}
|
|
894
1090
|
}
|
|
895
1091
|
}
|
|
896
1092
|
]
|
|
897
1093
|
],
|
|
898
1094
|
children: /*#__PURE__*/ jsxs("div", {
|
|
1095
|
+
...rest,
|
|
899
1096
|
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
|
|
900
1097
|
'[&: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
|
|
901
1098
|
'**:data-[slot="video-loop-button"]:focus-visible:outline-none'),
|
|
@@ -942,8 +1139,17 @@ const Carousel = ({ className, children, onChange })=>{
|
|
|
942
1139
|
const CarouselItemsContext = /*#__PURE__*/ createContext({
|
|
943
1140
|
ref: null
|
|
944
1141
|
});
|
|
945
|
-
const CarouselItems = ({ className, children })
|
|
946
|
-
|
|
1142
|
+
const CarouselItems = ({ className, children })=>{
|
|
1143
|
+
const handleKeyDown = (event)=>{
|
|
1144
|
+
// Prevent default behavior when holding down arrow keys (when repeat is true)
|
|
1145
|
+
// The default behavior in scroll snapping causes a staggering scroll effect that feels janky
|
|
1146
|
+
if (event.repeat && (event.key === 'ArrowLeft' || event.key === 'ArrowRight')) {
|
|
1147
|
+
event.preventDefault();
|
|
1148
|
+
}
|
|
1149
|
+
};
|
|
1150
|
+
return /*#__PURE__*/ jsx(CarouselItemsContext.Consumer, {
|
|
1151
|
+
children: ({ ref, onScroll })=>// biome-ignore lint/a11y/noStaticElementInteractions: The keydown handler is only to prevent undesired scrolling behavior when using the arrow keys
|
|
1152
|
+
/*#__PURE__*/ jsx("div", {
|
|
947
1153
|
"data-slot": "carousel-items",
|
|
948
1154
|
className: cx(className, [
|
|
949
1155
|
'scrollbar-hidden',
|
|
@@ -955,14 +1161,12 @@ const CarouselItems = ({ className, children })=>/*#__PURE__*/ jsx(CarouselItems
|
|
|
955
1161
|
'rounded-[inherit]'
|
|
956
1162
|
]),
|
|
957
1163
|
ref: ref,
|
|
958
|
-
// When the SnapEvent is supported: https://developer.mozilla.org/en-US/docs/Web/API/SnapEvent
|
|
959
|
-
// We can use the scrollsnapchange event to detect when the user has scrolled to a new item.
|
|
960
|
-
// We can then use Array.from(event.target.children).indexOf(event.snapTargetInline) to calculate the index of the item that is currently in view.
|
|
961
|
-
// Another option is to use the scrollEnd event, when Safiri supports it: https://developer.apple.com/documentation/webkitjs/snap_event/scrollend_event
|
|
962
1164
|
onScroll: onScroll,
|
|
1165
|
+
onKeyDown: handleKeyDown,
|
|
963
1166
|
children: children
|
|
964
1167
|
})
|
|
965
1168
|
});
|
|
1169
|
+
};
|
|
966
1170
|
const CarouselItem = ({ className, children, id })=>{
|
|
967
1171
|
return /*#__PURE__*/ jsx("div", {
|
|
968
1172
|
className: cx(className, 'shrink-0 basis-full snap-start'),
|
|
@@ -974,7 +1178,7 @@ const CarouselItem = ({ className, children, id })=>{
|
|
|
974
1178
|
MediaContext,
|
|
975
1179
|
{
|
|
976
1180
|
fit: 'cover',
|
|
977
|
-
className: cx('data-[fit="contain"]:bg-blue-dark', '*:h-full *:w-full', 'aspect-
|
|
1181
|
+
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')
|
|
978
1182
|
}
|
|
979
1183
|
]
|
|
980
1184
|
],
|
|
@@ -1278,167 +1482,6 @@ function Combobox(props) {
|
|
|
1278
1482
|
return render ? render(formatted) : formatted;
|
|
1279
1483
|
};
|
|
1280
1484
|
|
|
1281
|
-
const disclosureButtonVariants = cva({
|
|
1282
|
-
base: [
|
|
1283
|
-
'inline-flex cursor-pointer items-center justify-between rounded-lg focus-visible:outline-current focus-visible:outline-focus',
|
|
1284
|
-
// Ensure a minimum click area of 44x44px, while making it look like it only has the size of the content
|
|
1285
|
-
'-m-2.5 p-2.5 focus-visible:outline-offset-[-0.625rem]'
|
|
1286
|
-
],
|
|
1287
|
-
variants: {
|
|
1288
|
-
withChevron: {
|
|
1289
|
-
true: '[&[aria-expanded="true"]_svg]:rotate-180',
|
|
1290
|
-
false: null
|
|
1291
|
-
},
|
|
1292
|
-
/**
|
|
1293
|
-
* When the button is without text, but with a single icon.
|
|
1294
|
-
* @default false
|
|
1295
|
-
*/ isIconOnly: {
|
|
1296
|
-
true: '[&>svg]:h-7 [&>svg]:w-7',
|
|
1297
|
-
false: 'gap-2.5'
|
|
1298
|
-
}
|
|
1299
|
-
},
|
|
1300
|
-
defaultVariants: {
|
|
1301
|
-
withChevron: false,
|
|
1302
|
-
isIconOnly: false
|
|
1303
|
-
}
|
|
1304
|
-
});
|
|
1305
|
-
const DisclosureButton = ({ className, withChevron, isIconOnly, children, ref: _ref, ...restProps })=>{
|
|
1306
|
-
const [props, ref] = useContextProps(restProps, _ref, ButtonContext$1);
|
|
1307
|
-
return /*#__PURE__*/ jsxs(Button$1, {
|
|
1308
|
-
...props,
|
|
1309
|
-
ref: ref,
|
|
1310
|
-
className: disclosureButtonVariants({
|
|
1311
|
-
className,
|
|
1312
|
-
withChevron,
|
|
1313
|
-
isIconOnly
|
|
1314
|
-
}),
|
|
1315
|
-
slot: "trigger",
|
|
1316
|
-
children: [
|
|
1317
|
-
children,
|
|
1318
|
-
withChevron && /*#__PURE__*/ jsx(ChevronDown, {
|
|
1319
|
-
className: "flex-none transition-transform duration-300 motion-reduce:transition-none"
|
|
1320
|
-
})
|
|
1321
|
-
]
|
|
1322
|
-
});
|
|
1323
|
-
};
|
|
1324
|
-
const DisclosureStateContext = /*#__PURE__*/ createContext(null);
|
|
1325
|
-
const Disclosure = ({ ref: _ref, children, ..._props })=>{
|
|
1326
|
-
const [props, ref] = useContextProps(_props, _ref, DisclosureContext);
|
|
1327
|
-
const groupState = useContext(DisclosureGroupStateContext);
|
|
1328
|
-
let { id, ...otherProps } = props;
|
|
1329
|
-
const defaultId = useId();
|
|
1330
|
-
id ||= defaultId;
|
|
1331
|
-
const isExpanded = groupState ? groupState.expandedKeys.has(id) : props.isExpanded;
|
|
1332
|
-
const state = useDisclosureState({
|
|
1333
|
-
...props,
|
|
1334
|
-
isExpanded,
|
|
1335
|
-
onExpandedChange (isExpanded) {
|
|
1336
|
-
if (groupState) {
|
|
1337
|
-
groupState.toggleKey(id);
|
|
1338
|
-
}
|
|
1339
|
-
props.onExpandedChange?.(isExpanded);
|
|
1340
|
-
}
|
|
1341
|
-
});
|
|
1342
|
-
const isDisabled = props.isDisabled || groupState?.isDisabled || false;
|
|
1343
|
-
const domProps = filterDOMProps(otherProps);
|
|
1344
|
-
const { isFocusVisible: isFocusVisibleWithin } = useFocusRing({
|
|
1345
|
-
within: true
|
|
1346
|
-
});
|
|
1347
|
-
const panelRef = useRef(null);
|
|
1348
|
-
const { buttonProps, panelProps } = useDisclosure({
|
|
1349
|
-
...props,
|
|
1350
|
-
isExpanded,
|
|
1351
|
-
isDisabled
|
|
1352
|
-
}, state, panelRef);
|
|
1353
|
-
const { role: _, ...propsWithoutRole } = panelProps;
|
|
1354
|
-
return /*#__PURE__*/ jsx(Provider, {
|
|
1355
|
-
values: [
|
|
1356
|
-
[
|
|
1357
|
-
DisclosureContext,
|
|
1358
|
-
state
|
|
1359
|
-
],
|
|
1360
|
-
[
|
|
1361
|
-
ButtonContext$1,
|
|
1362
|
-
{
|
|
1363
|
-
slots: {
|
|
1364
|
-
[DEFAULT_SLOT]: {},
|
|
1365
|
-
trigger: buttonProps
|
|
1366
|
-
}
|
|
1367
|
-
}
|
|
1368
|
-
],
|
|
1369
|
-
[
|
|
1370
|
-
DisclosurePanelContext,
|
|
1371
|
-
{
|
|
1372
|
-
...propsWithoutRole,
|
|
1373
|
-
panelRef
|
|
1374
|
-
}
|
|
1375
|
-
],
|
|
1376
|
-
[
|
|
1377
|
-
DisclosureStateContext,
|
|
1378
|
-
state
|
|
1379
|
-
]
|
|
1380
|
-
],
|
|
1381
|
-
children: /*#__PURE__*/ jsx("div", {
|
|
1382
|
-
...domProps,
|
|
1383
|
-
className: otherProps.className,
|
|
1384
|
-
ref: ref,
|
|
1385
|
-
"data-focus-visible-within": isFocusVisibleWithin || undefined,
|
|
1386
|
-
"data-expanded": state.isExpanded || undefined,
|
|
1387
|
-
"data-disabled": isDisabled || undefined,
|
|
1388
|
-
children: typeof children === 'function' ? children({
|
|
1389
|
-
isExpanded: state.isExpanded,
|
|
1390
|
-
isFocusVisibleWithin,
|
|
1391
|
-
isDisabled,
|
|
1392
|
-
state,
|
|
1393
|
-
defaultChildren: null
|
|
1394
|
-
}) : children
|
|
1395
|
-
})
|
|
1396
|
-
});
|
|
1397
|
-
};
|
|
1398
|
-
const DisclosurePanelContext = /*#__PURE__*/ createContext({});
|
|
1399
|
-
const DisclosurePanel = ({ ref, children, ...props })=>{
|
|
1400
|
-
const disclosureContext = useContext(DisclosureContext);
|
|
1401
|
-
const { panelProps, panelRef } = useContext(DisclosurePanelContext);
|
|
1402
|
-
const { role: _role = 'group', className, ...restProps } = props;
|
|
1403
|
-
const ariaLabelledby = props['aria-labelledby'] ?? restProps['aria-labelledby'];
|
|
1404
|
-
const isWithoutRole = _role === 'none';
|
|
1405
|
-
const role = isWithoutRole ? undefined : _role;
|
|
1406
|
-
const { isFocusVisible: isFocusVisibleWithin, focusProps: focusWithinProps } = useFocusRing({
|
|
1407
|
-
within: true
|
|
1408
|
-
});
|
|
1409
|
-
const domProps = filterDOMProps(props);
|
|
1410
|
-
return /*#__PURE__*/ jsx("div", {
|
|
1411
|
-
className: cx('grid transition-all duration-300 motion-reduce:transition-none', disclosureContext?.isExpanded ? 'grid-rows-[1fr]' : 'grid-rows-[0fr]'),
|
|
1412
|
-
children: /*#__PURE__*/ jsx("div", {
|
|
1413
|
-
className: "overflow-hidden",
|
|
1414
|
-
children: /*#__PURE__*/ jsx("div", {
|
|
1415
|
-
ref: mergeRefs(ref, panelRef),
|
|
1416
|
-
...mergeProps(panelProps, focusWithinProps),
|
|
1417
|
-
...restProps,
|
|
1418
|
-
...domProps,
|
|
1419
|
-
"data-focus-visible-within": isFocusVisibleWithin || undefined,
|
|
1420
|
-
className: cx(className, '[content-visibility:visible]'),
|
|
1421
|
-
role: role,
|
|
1422
|
-
"aria-labelledby": isWithoutRole ? undefined : ariaLabelledby,
|
|
1423
|
-
children: /*#__PURE__*/ jsx(Provider, {
|
|
1424
|
-
values: [
|
|
1425
|
-
// Reset the context to avoid passing the same context to children, in case of nested Disclosures
|
|
1426
|
-
[
|
|
1427
|
-
DisclosureContext,
|
|
1428
|
-
null
|
|
1429
|
-
],
|
|
1430
|
-
[
|
|
1431
|
-
ButtonContext$1,
|
|
1432
|
-
null
|
|
1433
|
-
]
|
|
1434
|
-
],
|
|
1435
|
-
children: children
|
|
1436
|
-
})
|
|
1437
|
-
})
|
|
1438
|
-
})
|
|
1439
|
-
});
|
|
1440
|
-
};
|
|
1441
|
-
|
|
1442
1485
|
/**
|
|
1443
1486
|
* A FileTrigger allows a user to access the file system with any pressable React Aria or React Spectrum component, or custom components built with usePress.
|
|
1444
1487
|
*/ const FileTrigger = (props)=>{
|
|
@@ -1792,7 +1835,7 @@ const variants = cva({
|
|
|
1792
1835
|
variant: 'standard'
|
|
1793
1836
|
}
|
|
1794
1837
|
});
|
|
1795
|
-
const Hero = ({ variant, className, children })=>{
|
|
1838
|
+
const Hero = ({ variant, className, children, ...rest })=>{
|
|
1796
1839
|
const variantsClassName = variants({
|
|
1797
1840
|
variant,
|
|
1798
1841
|
className
|
|
@@ -1820,6 +1863,7 @@ const Hero = ({ variant, className, children })=>{
|
|
|
1820
1863
|
],
|
|
1821
1864
|
children: /*#__PURE__*/ jsx("div", {
|
|
1822
1865
|
className: cx(variantsClassName, className),
|
|
1866
|
+
...rest,
|
|
1823
1867
|
children: children
|
|
1824
1868
|
})
|
|
1825
1869
|
});
|
|
@@ -1918,8 +1962,7 @@ const Dialog = ({ className, children, ...restProps })=>{
|
|
|
1918
1962
|
children: [
|
|
1919
1963
|
children,
|
|
1920
1964
|
/*#__PURE__*/ jsx(Button, {
|
|
1921
|
-
slot: "close"
|
|
1922
|
-
,
|
|
1965
|
+
slot: "close",
|
|
1923
1966
|
variant: "tertiary",
|
|
1924
1967
|
className: "!px-2.5 data-focus-visible:outline-focus-inset",
|
|
1925
1968
|
"aria-label": translations$1.close[locale],
|
|
@@ -2707,4 +2750,4 @@ const VideoLoop = ({ src, format, alt, className })=>{
|
|
|
2707
2750
|
});
|
|
2708
2751
|
};
|
|
2709
2752
|
|
|
2710
|
-
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,
|
|
2753
|
+
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 };
|