@obosbbl/grunnmuren-react 3.0.16 → 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 +46 -61
- package/dist/index.mjs +292 -248
- 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, 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, 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,177 @@ 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
|
-
const { children, className, ...restProps } = props;
|
|
258
|
+
const { children, className, allowsMultipleExpanded = true, ...restProps } = props;
|
|
95
259
|
const childCount = Children.count(children);
|
|
96
|
-
return /*#__PURE__*/ jsx(
|
|
260
|
+
return /*#__PURE__*/ jsx(DisclosureGroup, {
|
|
97
261
|
...restProps,
|
|
262
|
+
"data-accordion": true,
|
|
263
|
+
allowsMultipleExpanded: allowsMultipleExpanded,
|
|
98
264
|
className: cx('rounded-lg bg-white', className),
|
|
99
265
|
children: Children.map(children, (child, index)=>/*#__PURE__*/ jsxs(Fragment, {
|
|
100
266
|
children: [
|
|
@@ -109,40 +275,16 @@ function Accordion(props) {
|
|
|
109
275
|
});
|
|
110
276
|
}
|
|
111
277
|
function AccordionItem(props) {
|
|
112
|
-
const { className, children, defaultOpen = false, isOpen: controlledIsOpen, onOpenChange, ...restProps } = props;
|
|
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", {
|
|
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, {
|
|
143
283
|
...restProps,
|
|
144
284
|
className: cx('relative px-2', className),
|
|
145
|
-
|
|
285
|
+
defaultExpanded: _defaultExpanded,
|
|
286
|
+
onExpandedChange: _onExpandedChange,
|
|
287
|
+
isExpanded: _isExpanded,
|
|
146
288
|
children: /*#__PURE__*/ jsx(Provider, {
|
|
147
289
|
values: [
|
|
148
290
|
[
|
|
@@ -152,33 +294,22 @@ function AccordionItem(props) {
|
|
|
152
294
|
className: 'font-medium leading-7 -mx-2 text-base',
|
|
153
295
|
// Supply a default level here to make this typecheck ok. Will be overwritten with the consumers set heading level anyways
|
|
154
296
|
level: 3,
|
|
155
|
-
_innerWrapper: (children)=>/*#__PURE__*/
|
|
156
|
-
"aria-controls": contentId,
|
|
157
|
-
"aria-expanded": isOpen,
|
|
297
|
+
_innerWrapper: (children)=>/*#__PURE__*/ jsx(DisclosureButton, {
|
|
158
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
|
|
159
|
-
className: "flex min-h-
|
|
160
|
-
id: buttonId,
|
|
161
|
-
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!",
|
|
162
300
|
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
|
-
]
|
|
301
|
+
withChevron: true,
|
|
302
|
+
children: children
|
|
169
303
|
})
|
|
170
304
|
}
|
|
171
305
|
],
|
|
172
306
|
[
|
|
173
307
|
ContentContext,
|
|
174
308
|
{
|
|
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',
|
|
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',
|
|
177
311
|
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]'),
|
|
312
|
+
_outerWrapper: (children)=>/*#__PURE__*/ jsx(DisclosurePanel, {
|
|
182
313
|
children: children
|
|
183
314
|
})
|
|
184
315
|
}
|
|
@@ -808,11 +939,14 @@ const cardLinkVariants = cva({
|
|
|
808
939
|
});
|
|
809
940
|
};
|
|
810
941
|
|
|
811
|
-
const Carousel = ({ className, children, onChange })=>{
|
|
942
|
+
const Carousel = ({ className, children, onChange, ...rest })=>{
|
|
812
943
|
const ref = useRef(null);
|
|
813
944
|
const locale = _useLocale();
|
|
814
945
|
const { previous, next } = translations$1;
|
|
815
946
|
const [scrollTargetIndex, setScrollTargetIndex] = useState(0);
|
|
947
|
+
const isScrollingProgrammatically = useRef(false);
|
|
948
|
+
const scrollTimeoutRef = useRef(null);
|
|
949
|
+
const scrollQueue = useRef([]);
|
|
816
950
|
const [hasReachedScrollStart, setHasReachedScrollStart] = useState(scrollTargetIndex === 0);
|
|
817
951
|
const [hasReachedScrollEnd, setHasReachedScrollEnd] = useState(!ref.current || ref.current.children.length - 1 === scrollTargetIndex);
|
|
818
952
|
useEffect(()=>{
|
|
@@ -824,9 +958,27 @@ const Carousel = ({ className, children, onChange })=>{
|
|
|
824
958
|
// Keep track of the previous index to determine if the user is scrolling forward or backward
|
|
825
959
|
// This is used to determine which callback to call (onPrev or onNext)
|
|
826
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
|
+
}
|
|
972
|
+
};
|
|
827
973
|
// Handle scrolling when user clicks the arrow icons
|
|
828
974
|
useUpdateEffect(()=>{
|
|
829
|
-
if (!ref.current)
|
|
975
|
+
if (!ref.current) {
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
978
|
+
if (scrollTimeoutRef.current) {
|
|
979
|
+
clearTimeout(scrollTimeoutRef.current);
|
|
980
|
+
}
|
|
981
|
+
isScrollingProgrammatically.current = true;
|
|
830
982
|
ref.current.children[scrollTargetIndex]?.scrollIntoView({
|
|
831
983
|
behavior: 'smooth',
|
|
832
984
|
inline: 'start',
|
|
@@ -841,20 +993,76 @@ const Carousel = ({ className, children, onChange })=>{
|
|
|
841
993
|
});
|
|
842
994
|
}
|
|
843
995
|
prevIndex.current = scrollTargetIndex;
|
|
996
|
+
scrollTimeoutRef.current = setTimeout(()=>{
|
|
997
|
+
isScrollingProgrammatically.current = false;
|
|
998
|
+
scrollTimeoutRef.current = null;
|
|
999
|
+
processQueue(); // Process any queued scrolls
|
|
1000
|
+
}, 500);
|
|
844
1001
|
}, [
|
|
845
1002
|
scrollTargetIndex
|
|
846
1003
|
]);
|
|
1004
|
+
// Clean up timeout on unmount
|
|
1005
|
+
useEffect(()=>{
|
|
1006
|
+
return ()=>{
|
|
1007
|
+
if (scrollTimeoutRef.current) {
|
|
1008
|
+
clearTimeout(scrollTimeoutRef.current);
|
|
1009
|
+
}
|
|
1010
|
+
};
|
|
1011
|
+
}, []);
|
|
847
1012
|
const onScroll = useDebouncedCallback((event)=>{
|
|
1013
|
+
// Ignore scroll events when we're programmatically scrolling
|
|
1014
|
+
if (isScrollingProgrammatically.current) {
|
|
1015
|
+
return;
|
|
1016
|
+
}
|
|
848
1017
|
const target = event.target;
|
|
1018
|
+
const containerRect = target.getBoundingClientRect();
|
|
849
1019
|
// Calculate the index of the item that is currently in view
|
|
850
1020
|
const newScrollTargetIndex = Array.from(target.children).findIndex((child)=>{
|
|
851
1021
|
const rect = child.getBoundingClientRect();
|
|
852
|
-
|
|
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;
|
|
853
1026
|
});
|
|
854
|
-
if (newScrollTargetIndex !== -1) {
|
|
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
|
|
855
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
|
+
];
|
|
1049
|
+
} else {
|
|
1050
|
+
setScrollTargetIndex(targetIndex);
|
|
856
1051
|
}
|
|
857
|
-
}
|
|
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
|
+
};
|
|
858
1066
|
return /*#__PURE__*/ jsx("div", {
|
|
859
1067
|
"data-slot": "carousel",
|
|
860
1068
|
children: /*#__PURE__*/ jsx(Provider, {
|
|
@@ -873,29 +1081,19 @@ const Carousel = ({ className, children, onChange })=>{
|
|
|
873
1081
|
[DEFAULT_SLOT]: {},
|
|
874
1082
|
prev: {
|
|
875
1083
|
'aria-label': previous[locale],
|
|
876
|
-
onPress:
|
|
877
|
-
if (scrollTargetIndex > 0) {
|
|
878
|
-
setScrollTargetIndex((prev)=>prev - 1);
|
|
879
|
-
}
|
|
880
|
-
},
|
|
881
|
-
isDisabled: hasReachedScrollStart
|
|
1084
|
+
onPress: handlePrevious
|
|
882
1085
|
},
|
|
883
1086
|
next: {
|
|
884
1087
|
isIconOnly: true,
|
|
885
1088
|
'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
|
|
1089
|
+
onPress: handleNext
|
|
893
1090
|
}
|
|
894
1091
|
}
|
|
895
1092
|
}
|
|
896
1093
|
]
|
|
897
1094
|
],
|
|
898
1095
|
children: /*#__PURE__*/ jsxs("div", {
|
|
1096
|
+
...rest,
|
|
899
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
|
|
900
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
|
|
901
1099
|
'**:data-[slot="video-loop-button"]:focus-visible:outline-none'),
|
|
@@ -942,8 +1140,17 @@ const Carousel = ({ className, children, onChange })=>{
|
|
|
942
1140
|
const CarouselItemsContext = /*#__PURE__*/ createContext({
|
|
943
1141
|
ref: null
|
|
944
1142
|
});
|
|
945
|
-
const CarouselItems = ({ className, children })
|
|
946
|
-
|
|
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", {
|
|
947
1154
|
"data-slot": "carousel-items",
|
|
948
1155
|
className: cx(className, [
|
|
949
1156
|
'scrollbar-hidden',
|
|
@@ -955,14 +1162,12 @@ const CarouselItems = ({ className, children })=>/*#__PURE__*/ jsx(CarouselItems
|
|
|
955
1162
|
'rounded-[inherit]'
|
|
956
1163
|
]),
|
|
957
1164
|
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
1165
|
onScroll: onScroll,
|
|
1166
|
+
onKeyDown: handleKeyDown,
|
|
963
1167
|
children: children
|
|
964
1168
|
})
|
|
965
1169
|
});
|
|
1170
|
+
};
|
|
966
1171
|
const CarouselItem = ({ className, children, id })=>{
|
|
967
1172
|
return /*#__PURE__*/ jsx("div", {
|
|
968
1173
|
className: cx(className, 'shrink-0 basis-full snap-start'),
|
|
@@ -974,7 +1179,7 @@ const CarouselItem = ({ className, children, id })=>{
|
|
|
974
1179
|
MediaContext,
|
|
975
1180
|
{
|
|
976
1181
|
fit: 'cover',
|
|
977
|
-
className: cx('data-[fit="contain"]:bg-blue-dark', '*:h-full *:w-full', 'aspect-
|
|
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')
|
|
978
1183
|
}
|
|
979
1184
|
]
|
|
980
1185
|
],
|
|
@@ -1278,167 +1483,6 @@ function Combobox(props) {
|
|
|
1278
1483
|
return render ? render(formatted) : formatted;
|
|
1279
1484
|
};
|
|
1280
1485
|
|
|
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
1486
|
/**
|
|
1443
1487
|
* 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
1488
|
*/ const FileTrigger = (props)=>{
|
|
@@ -1792,7 +1836,7 @@ const variants = cva({
|
|
|
1792
1836
|
variant: 'standard'
|
|
1793
1837
|
}
|
|
1794
1838
|
});
|
|
1795
|
-
const Hero = ({ variant, className, children })=>{
|
|
1839
|
+
const Hero = ({ variant, className, children, ...rest })=>{
|
|
1796
1840
|
const variantsClassName = variants({
|
|
1797
1841
|
variant,
|
|
1798
1842
|
className
|
|
@@ -1820,6 +1864,7 @@ const Hero = ({ variant, className, children })=>{
|
|
|
1820
1864
|
],
|
|
1821
1865
|
children: /*#__PURE__*/ jsx("div", {
|
|
1822
1866
|
className: cx(variantsClassName, className),
|
|
1867
|
+
...rest,
|
|
1823
1868
|
children: children
|
|
1824
1869
|
})
|
|
1825
1870
|
});
|
|
@@ -1918,8 +1963,7 @@ const Dialog = ({ className, children, ...restProps })=>{
|
|
|
1918
1963
|
children: [
|
|
1919
1964
|
children,
|
|
1920
1965
|
/*#__PURE__*/ jsx(Button, {
|
|
1921
|
-
slot: "close"
|
|
1922
|
-
,
|
|
1966
|
+
slot: "close",
|
|
1923
1967
|
variant: "tertiary",
|
|
1924
1968
|
className: "!px-2.5 data-focus-visible:outline-focus-inset",
|
|
1925
1969
|
"aria-label": translations$1.close[locale],
|
|
@@ -2707,4 +2751,4 @@ const VideoLoop = ({ src, format, alt, className })=>{
|
|
|
2707
2751
|
});
|
|
2708
2752
|
};
|
|
2709
2753
|
|
|
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,
|
|
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 };
|