@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/index.mjs CHANGED
@@ -1,14 +1,14 @@
1
1
  'use client';
2
- import { useContextProps, Provider, useLocale, Link, Button as Button$1, Breadcrumb as Breadcrumb$1, Breadcrumbs as Breadcrumbs$1, DEFAULT_SLOT, Text, CheckboxContext, Checkbox as Checkbox$1, FieldError, Label as Label$1, CheckboxGroup as CheckboxGroup$1, ListBoxItem as ListBoxItem$1, ListBoxSection as ListBoxSection$1, Header, ListBox as ListBox$1, ComboBox, Group, Input, Popover, ButtonContext as ButtonContext$1, DisclosureContext, DisclosureGroupStateContext, useSlottedContext, FormContext, FieldErrorContext, LabelContext, InputContext, I18nProvider, RouterProvider, GroupContext, DialogTrigger as DialogTrigger$1, Modal as Modal$1, Dialog as Dialog$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, TableHeader as TableHeader$1, Column, TableBody as TableBody$1, Row, Cell, Tabs as Tabs$1, TabListStateContext, TabList as TabList$1, Tab as Tab$1, TabPanel as TabPanel$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';
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, Children, useId, useState, useRef, useEffect, useContext, useCallback } from 'react';
9
- import { useProgressBar, useDateFormatter, useFocusRing, useDisclosure, useField } from 'react-aria';
10
- import { useDebouncedCallback } from 'use-debounce';
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("div", {
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 contentId = useId();
114
- const buttonId = useId();
115
- const isControlled = controlledIsOpen != null;
116
- // This component has internal state that controls whether it is open or not,
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
- "data-open": isOpen,
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__*/ jsxs("button", {
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-[44px] w-full cursor-pointer items-center justify-between gap-1.5 rounded-lg px-2 py-3.5 text-left focus-visible:outline-focus focus-visible:outline-focus-inset",
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
- children: [
164
- children,
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 element for vertical padding, since that doesn't affect the height when the accordion is closed
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
- inert: !isOpen,
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) return;
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
- return rect.left >= 0 && rect.right <= window.innerWidth && rect.top >= 0;
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
- }, 100);
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 })=>/*#__PURE__*/ jsx(CarouselItemsContext.Consumer, {
946
- children: ({ ref, onScroll })=>/*#__PURE__*/ jsx("div", {
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-1/1 max-sm:data-[fit="contain"]:*:object-cover sm:aspect-4/3 md:aspect-3/2 lg:aspect-2/1')
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" // RAC Dialog suppors one close button out of the box, so we utilize that here. For other close buttons we use ButtonContext
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, Tag as UNSAFE_Tag, TagGroup as UNSAFE_TagGroup, TagList as UNSAFE_TagList, VideoLoop, _useLocale as useLocale };
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 };