@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/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, 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,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 = 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", {
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
- "data-open": isOpen,
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__*/ jsxs("button", {
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-[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,
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
- 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
- ]
300
+ withChevron: true,
301
+ children: children
169
302
  })
170
303
  }
171
304
  ],
172
305
  [
173
306
  ContentContext,
174
307
  {
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',
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
- 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]'),
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) return;
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
- return rect.left >= 0 && rect.right <= window.innerWidth && rect.top >= 0;
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
- }, 100);
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 })=>/*#__PURE__*/ jsx(CarouselItemsContext.Consumer, {
946
- children: ({ ref, onScroll })=>/*#__PURE__*/ jsx("div", {
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-1/1 max-sm:data-[fit="contain"]:*:object-cover sm:aspect-4/3 md:aspect-3/2 lg:aspect-2/1')
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" // RAC Dialog suppors one close button out of the box, so we utilize that here. For other close buttons we use ButtonContext
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, Tag as UNSAFE_Tag, TagGroup as UNSAFE_TagGroup, TagList as UNSAFE_TagList, VideoLoop, _useLocale as useLocale };
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 };