@vygruppen/spor-react 3.0.2 → 3.0.4

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 +1 @@
1
- export { Accordion, AccordionButton, AccordionIcon, AccordionItem, AccordionPanel, AttachedInputs, Badge, Box, Button, ButtonGroup, Card, CardSelect, Center, Checkbox, CheckboxGroup, ChoiceChip, ClosableAlert, CloseButton, Code, Collapse, ColorInlineLoader, ColorSpinner, Combobox, Container, ContentLoader, DarkFullScreenLoader, DarkInlineLoader, DarkMode, DarkSpinner, DatePicker, DateRangePicker, Divider, Drawer, DrawerBody, DrawerCloseButton, DrawerContent, DrawerFooter, ModalHeader as DrawerHeader, DrawerOverlay, Expandable, ExpandableAlert, ExpandableItem, Fade, Flex, FloatingActionButton, FormControl, FormErrorMessage, FormHelperText, FormLabel, Grid, GridItem, HStack, Heading, IconButton, Image, Img, InfoSelect, InfoTag, Input, InputGroup, InputLeftElement, InputRightElement, Item, ItemDescription, ItemLabel, JumpButton, Language, LanguageProvider, LightFullScreenLoader, LightInlineLoader, LightMode, LightSpinner, LineIcon, ListBox, Modal, ModalBody, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalOverlay, NativeSelect, NumericStepper, PasswordInput, PhoneNumberInput, PlayPauseButton, Popover, PopoverAnchor, PopoverArrow, PopoverBody, PopoverCloseButton, PopoverContent, PopoverFooter, PopoverHeader, PopoverTrigger, PopoverWizardBody, ProgressBar, ProgressLoader, Radio, RadioGroup, ScaleFade, SearchInput, Section, SimpleDrawer, SimpleGrid, SimplePopover, Skeleton, SkeletonCircle, SkeletonText, SkipButton, Slide, SlideFade, Spacer, SporProvider, Stack, StaticAlert, Stepper, StepperStep, Switch, Tab, TabList, TabPanel, TabPanels, Table, TableCaption, Tabs, Tbody, Td, Text, TextLink, Textarea, Tfoot, Th, Thead, Time, TimePicker, Tr, TravelTag, VStack, VyLogo, WizardPopover, Wrap, WrapItem, createTexts, extendTheme, fontFaces, theme, tokens, useBreakpointValue, useClipboard, useColorMode, useColorModePreference, useColorModeValue, useControllableProp, useDisclosure, useMediaQuery, useMergeRefs, useOutsideClick, usePrefersReducedMotion, useTheme, useToast, useToken, useTranslation } from './chunk-CIZ54LAU.mjs';
1
+ export { Accordion, AccordionButton, AccordionIcon, AccordionItem, AccordionPanel, AttachedInputs, Badge, Box, Button, ButtonGroup, Card, CardSelect, Center, Checkbox, CheckboxGroup, ChoiceChip, ClosableAlert, CloseButton, Code, Collapse, ColorInlineLoader, ColorSpinner, Combobox, Container, ContentLoader, DarkFullScreenLoader, DarkInlineLoader, DarkMode, DarkSpinner, DatePicker, DateRangePicker, Divider, Drawer, DrawerBody, DrawerCloseButton, DrawerContent, DrawerFooter, ModalHeader as DrawerHeader, DrawerOverlay, Expandable, ExpandableAlert, ExpandableItem, Fade, Flex, FloatingActionButton, FormControl, FormErrorMessage, FormHelperText, FormLabel, Grid, GridItem, HStack, Heading, IconButton, Image, Img, InfoSelect, InfoTag, Input, InputGroup, InputLeftElement, InputRightElement, Item, ItemDescription, ItemLabel, JumpButton, Language, LanguageProvider, LightFullScreenLoader, LightInlineLoader, LightMode, LightSpinner, LineIcon, ListBox, Modal, ModalBody, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalOverlay, NativeSelect, NumericStepper, PasswordInput, PhoneNumberInput, PlayPauseButton, Popover, PopoverAnchor, PopoverArrow, PopoverBody, PopoverCloseButton, PopoverContent, PopoverFooter, PopoverHeader, PopoverTrigger, PopoverWizardBody, ProgressBar, ProgressLoader, Radio, RadioGroup, ScaleFade, SearchInput, Section, SimpleDrawer, SimpleGrid, SimplePopover, Skeleton, SkeletonCircle, SkeletonText, SkipButton, Slide, SlideFade, Spacer, SporProvider, Stack, StaticAlert, Stepper, StepperStep, Switch, Tab, TabList, TabPanel, TabPanels, Table, TableCaption, Tabs, Tbody, Td, Text, TextLink, Textarea, Tfoot, Th, Thead, Time, TimePicker, Tr, TravelTag, VStack, VyLogo, WizardPopover, Wrap, WrapItem, createTexts, extendTheme, fontFaces, theme, tokens, useBreakpointValue, useClipboard, useColorMode, useColorModePreference, useColorModeValue, useControllableProp, useDisclosure, useMediaQuery, useMergeRefs, useOutsideClick, usePrefersReducedMotion, useTheme, useToast, useToken, useTranslation } from './chunk-ETCK74GR.mjs';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vygruppen/spor-react",
3
- "version": "3.0.2",
3
+ "version": "3.0.4",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.ts",
@@ -2,7 +2,7 @@ import { Box, Flex, FormLabel, useMultiStyleConfig } from "@chakra-ui/react";
2
2
  import { DateValue, GregorianCalendar } from "@internationalized/date";
3
3
  import { useDateFieldState } from "@react-stately/datepicker";
4
4
  import { DOMAttributes, FocusableElement } from "@react-types/shared";
5
- import React, { useRef } from "react";
5
+ import React, { RefObject, forwardRef, useRef } from "react";
6
6
  import { AriaDateFieldProps, useDateField } from "react-aria";
7
7
  import { DateTimeSegment } from "./DateTimeSegment";
8
8
  import { useCurrentLocale } from "./utils";
@@ -21,31 +21,51 @@ type DateFieldProps = AriaDateFieldProps<DateValue> & {
21
21
  labelProps?: DOMAttributes<FocusableElement>;
22
22
  name?: string;
23
23
  };
24
- export function DateField(props: DateFieldProps) {
25
- const locale = useCurrentLocale();
26
- const styles = useMultiStyleConfig("Datepicker", {});
27
- const state = useDateFieldState({
28
- ...props,
29
- locale,
30
- createCalendar,
31
- });
24
+ export const DateField = forwardRef<HTMLDivElement, DateFieldProps>(
25
+ (props, externalRef) => {
26
+ const locale = useCurrentLocale();
27
+ const styles = useMultiStyleConfig("Datepicker", {});
28
+ const state = useDateFieldState({
29
+ ...props,
30
+ locale,
31
+ createCalendar,
32
+ });
32
33
 
33
- const ref = useRef(null);
34
- const { fieldProps, labelProps } = useDateField(props, state, ref);
34
+ const internalRef = useRef(null);
35
+ const ref = externalRef ?? internalRef;
36
+ const { fieldProps, labelProps } = useDateField(
37
+ props,
38
+ state,
39
+ ref as RefObject<HTMLDivElement>
40
+ );
35
41
 
36
- return (
37
- <Box minWidth="6rem" width="100%">
38
- {props.label && (
39
- <FormLabel {...props.labelProps} {...labelProps} sx={styles.inputLabel}>
40
- {props.label}
41
- </FormLabel>
42
- )}
43
- <Flex {...fieldProps} ref={ref}>
44
- {state.segments.map((segment, i) => (
45
- <DateTimeSegment key={i} segment={segment} state={state} />
46
- ))}
47
- </Flex>
48
- <input type="hidden" value={state.value?.toString()} name={props.name} />
49
- </Box>
50
- );
51
- }
42
+ return (
43
+ <Box minWidth="6rem" width="100%">
44
+ {props.label && (
45
+ <FormLabel
46
+ {...props.labelProps}
47
+ {...labelProps}
48
+ sx={styles.inputLabel}
49
+ >
50
+ {props.label}
51
+ </FormLabel>
52
+ )}
53
+ <Flex {...fieldProps}>
54
+ {state.segments.map((segment, i) => (
55
+ <DateTimeSegment
56
+ ref={i === 0 ? ref : undefined}
57
+ key={i}
58
+ segment={segment}
59
+ state={state}
60
+ />
61
+ ))}
62
+ </Flex>
63
+ <input
64
+ type="hidden"
65
+ value={state.value?.toString()}
66
+ name={props.name}
67
+ />
68
+ </Box>
69
+ );
70
+ }
71
+ );
@@ -16,7 +16,7 @@ import {
16
16
  import { DateValue } from "@internationalized/date";
17
17
  import { useDatePickerState } from "@react-stately/datepicker";
18
18
  import { CalendarOutline24Icon } from "@vygruppen/spor-icon-react";
19
- import React, { useRef } from "react";
19
+ import React, { forwardRef, useRef } from "react";
20
20
  import { AriaDatePickerProps, I18nProvider, useDatePicker } from "react-aria";
21
21
  import { FormErrorMessage } from "..";
22
22
  import { Calendar } from "./Calendar";
@@ -40,118 +40,133 @@ type DatePickerProps = AriaDatePickerProps<DateValue> &
40
40
  * <DatePicker label="Dato" variant="simple" />
41
41
  * ```
42
42
  */
43
- export function DatePicker({
44
- variant,
45
- errorMessage,
46
- minHeight,
47
- showYearNavigation,
48
- width = "auto",
49
- ...props
50
- }: DatePickerProps) {
51
- const formControlProps = useFormControlContext();
52
- const state = useDatePickerState({
53
- ...props,
54
- shouldCloseOnSelect: true,
55
- errorMessage,
56
- isRequired: props.isRequired ?? formControlProps?.isRequired,
57
- validationState: formControlProps?.isInvalid ? "invalid" : "valid",
58
- });
59
- const ref = useRef(null);
60
- const {
61
- groupProps,
62
- labelProps,
63
- fieldProps,
64
- buttonProps,
65
- dialogProps,
66
- calendarProps,
67
- errorMessageProps,
68
- } = useDatePicker(props, state, ref);
43
+ export const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>(
44
+ (
45
+ {
46
+ variant,
47
+ errorMessage,
48
+ minHeight,
49
+ showYearNavigation,
50
+ width = "auto",
51
+ ...props
52
+ },
53
+ externalRef
54
+ ) => {
55
+ const formControlProps = useFormControlContext();
56
+ const state = useDatePickerState({
57
+ ...props,
58
+ shouldCloseOnSelect: true,
59
+ errorMessage,
60
+ isRequired: props.isRequired ?? formControlProps?.isRequired,
61
+ validationState: formControlProps?.isInvalid ? "invalid" : "valid",
62
+ });
63
+ const internalRef = useRef<HTMLDivElement>(null);
64
+ const ref = externalRef ?? internalRef;
65
+ const {
66
+ groupProps,
67
+ labelProps,
68
+ fieldProps,
69
+ buttonProps,
70
+ dialogProps,
71
+ calendarProps,
72
+ errorMessageProps,
73
+ } = useDatePicker(
74
+ props,
75
+ state,
76
+ ref as React.MutableRefObject<HTMLDivElement>
77
+ );
69
78
 
70
- const responsiveVariant =
71
- useBreakpointValue(typeof variant === "string" ? [variant] : variant) ??
72
- "simple";
79
+ const responsiveVariant =
80
+ useBreakpointValue(typeof variant === "string" ? [variant] : variant) ??
81
+ "simple";
73
82
 
74
- const locale = useCurrentLocale();
83
+ const locale = useCurrentLocale();
75
84
 
76
- const handleEnterClick = (e: React.KeyboardEvent) => {
77
- if (responsiveVariant === "simple" && e.key === "Enter" && !state.isOpen) {
78
- // Don't submit the form
79
- e.stopPropagation();
80
- state.setOpen(true);
81
- }
82
- };
85
+ const handleEnterClick = (e: React.KeyboardEvent) => {
86
+ if (
87
+ responsiveVariant === "simple" &&
88
+ e.key === "Enter" &&
89
+ !state.isOpen
90
+ ) {
91
+ // Don't submit the form
92
+ e.stopPropagation();
93
+ state.setOpen(true);
94
+ }
95
+ };
83
96
 
84
- const onFieldClick = () => {
85
- if (!hasTrigger) {
86
- state.setOpen(true);
87
- }
88
- };
97
+ const onFieldClick = () => {
98
+ if (!hasTrigger) {
99
+ state.setOpen(true);
100
+ }
101
+ };
89
102
 
90
- const hasTrigger = responsiveVariant === "with-trigger";
103
+ const hasTrigger = responsiveVariant === "with-trigger";
91
104
 
92
- const styles = useMultiStyleConfig("Datepicker", {});
105
+ const styles = useMultiStyleConfig("Datepicker", {});
93
106
 
94
- return (
95
- <I18nProvider locale={locale}>
96
- <Box
97
- position="relative"
98
- display="inline-flex"
99
- flexDirection="column"
100
- width={width}
101
- >
102
- <Popover
103
- {...dialogProps}
104
- isOpen={state.isOpen}
105
- onClose={() => state.setOpen(false)}
106
- onOpen={() => state.setOpen(true)}
107
- closeOnBlur
108
- closeOnEsc
109
- returnFocusOnClose
107
+ return (
108
+ <I18nProvider locale={locale}>
109
+ <Box
110
+ position="relative"
111
+ display="inline-flex"
112
+ flexDirection="column"
113
+ width={width}
110
114
  >
111
- <InputGroup {...groupProps} ref={ref} display="inline-flex">
112
- <PopoverAnchor>
113
- <StyledField
114
- variant={responsiveVariant}
115
- onClick={onFieldClick}
116
- onKeyPress={handleEnterClick}
117
- paddingX={3}
118
- minHeight={minHeight}
119
- >
120
- {!hasTrigger && (
121
- <CalendarOutline24Icon marginRight={2} alignSelf="center" />
122
- )}
123
- <DateField
124
- label={props.label}
125
- labelProps={labelProps}
126
- name={props.name}
127
- {...fieldProps}
128
- />
129
- </StyledField>
130
- </PopoverAnchor>
131
- {hasTrigger && <CalendarTriggerButton {...buttonProps} />}
132
- </InputGroup>
133
- <FormErrorMessage {...errorMessageProps}>
134
- {errorMessage}
135
- </FormErrorMessage>
136
- {state.isOpen && !props.isDisabled && (
137
- <Portal>
138
- <PopoverContent
139
- color="darkGrey"
140
- boxShadow="md"
141
- sx={styles.calendar}
142
- >
143
- <PopoverArrow sx={styles.arrow} />
144
- <PopoverBody>
145
- <Calendar
146
- {...calendarProps}
147
- showYearNavigation={showYearNavigation}
115
+ <Popover
116
+ {...dialogProps}
117
+ isOpen={state.isOpen}
118
+ onClose={state.close}
119
+ onOpen={state.open}
120
+ closeOnBlur
121
+ closeOnEsc
122
+ returnFocusOnClose
123
+ >
124
+ <InputGroup {...groupProps} display="inline-flex">
125
+ <PopoverAnchor>
126
+ <StyledField
127
+ variant={responsiveVariant}
128
+ onClick={onFieldClick}
129
+ onKeyPress={handleEnterClick}
130
+ paddingX={3}
131
+ minHeight={minHeight}
132
+ >
133
+ {!hasTrigger && (
134
+ <CalendarOutline24Icon marginRight={2} alignSelf="center" />
135
+ )}
136
+ <DateField
137
+ label={props.label}
138
+ labelProps={labelProps}
139
+ name={props.name}
140
+ ref={ref}
141
+ {...fieldProps}
148
142
  />
149
- </PopoverBody>
150
- </PopoverContent>
151
- </Portal>
152
- )}
153
- </Popover>
154
- </Box>
155
- </I18nProvider>
156
- );
157
- }
143
+ </StyledField>
144
+ </PopoverAnchor>
145
+ {hasTrigger && <CalendarTriggerButton {...buttonProps} />}
146
+ </InputGroup>
147
+ <FormErrorMessage {...errorMessageProps}>
148
+ {errorMessage}
149
+ </FormErrorMessage>
150
+ {state.isOpen && !props.isDisabled && (
151
+ <Portal>
152
+ <PopoverContent
153
+ color="darkGrey"
154
+ boxShadow="md"
155
+ sx={styles.calendar}
156
+ >
157
+ <PopoverArrow sx={styles.arrow} />
158
+ <PopoverBody>
159
+ <Calendar
160
+ {...calendarProps}
161
+ showYearNavigation={showYearNavigation}
162
+ />
163
+ </PopoverBody>
164
+ </PopoverContent>
165
+ </Portal>
166
+ )}
167
+ </Popover>
168
+ </Box>
169
+ </I18nProvider>
170
+ );
171
+ }
172
+ );
@@ -1,5 +1,5 @@
1
1
  import { Box, useMultiStyleConfig } from "@chakra-ui/react";
2
- import React, { useRef } from "react";
2
+ import React, { RefObject, forwardRef, useRef } from "react";
3
3
  import { useDateSegment } from "react-aria";
4
4
  import { DateFieldState, DateSegment } from "react-stately";
5
5
 
@@ -14,40 +14,49 @@ type DateTimeSegmentProps = {
14
14
  *
15
15
  * This component should be used with the react-aria library, and is not meant to be used directly.
16
16
  * */
17
- export const DateTimeSegment = ({ segment, state }: DateTimeSegmentProps) => {
18
- const ref = useRef(null);
17
+ export const DateTimeSegment = forwardRef<HTMLDivElement, DateTimeSegmentProps>(
18
+ ({ segment, state }, externalRef) => {
19
+ const internalRef = useRef(null);
20
+ const ref = externalRef ?? internalRef;
19
21
 
20
- const { segmentProps } = useDateSegment(segment, state, ref);
22
+ const { segmentProps } = useDateSegment(
23
+ segment,
24
+ state,
25
+ ref as RefObject<HTMLDivElement>
26
+ );
21
27
 
22
- const styles = useMultiStyleConfig("Datepicker", {
23
- isPlaceholder: segment.isPlaceholder,
24
- isEditable: segment.isEditable,
25
- });
28
+ const styles = useMultiStyleConfig("Datepicker", {
29
+ isPlaceholder: segment.isPlaceholder,
30
+ isEditable: segment.isEditable,
31
+ });
26
32
 
27
- return (
28
- <Box
29
- {...segmentProps}
30
- ref={ref}
31
- style={{
32
- ...segmentProps.style,
33
- fontVariantNumeric: "tabular-nums",
34
- boxSizing: "content-box",
35
- }}
36
- paddingX="1px"
37
- textAlign="end"
38
- outline="none"
39
- borderRadius="xs"
40
- fontSize="mobile.md"
41
- sx={styles.dateTimeSegment}
42
- _focus={{
43
- backgroundColor: "darkTeal",
44
- color: "white",
45
- }}
46
- >
47
- {isPaddable(segment.type) ? segment.text.padStart(2, "0") : segment.text}
48
- </Box>
49
- );
50
- };
33
+ return (
34
+ <Box
35
+ {...segmentProps}
36
+ ref={ref}
37
+ style={{
38
+ ...segmentProps.style,
39
+ fontVariantNumeric: "tabular-nums",
40
+ boxSizing: "content-box",
41
+ }}
42
+ paddingX="1px"
43
+ textAlign="end"
44
+ outline="none"
45
+ borderRadius="xs"
46
+ fontSize="mobile.md"
47
+ sx={styles.dateTimeSegment}
48
+ _focus={{
49
+ backgroundColor: "darkTeal",
50
+ color: "white",
51
+ }}
52
+ >
53
+ {isPaddable(segment.type)
54
+ ? segment.text.padStart(2, "0")
55
+ : segment.text}
56
+ </Box>
57
+ );
58
+ }
59
+ );
51
60
 
52
61
  const isPaddable = (segmentType: DateSegment["type"]) =>
53
62
  segmentType === "month" ||
@@ -28,6 +28,7 @@ export const TimeField = ({ state, ...props }: TimeFieldProps) => {
28
28
  htmlFor={fieldProps.id}
29
29
  marginBottom={0}
30
30
  fontSize="mobile.xs"
31
+ cursor="text"
31
32
  >
32
33
  {props.label}
33
34
  </FormLabel>
@@ -1,7 +1,7 @@
1
1
  import React, { useEffect, useRef, useState } from "react";
2
2
  import { AriaComboBoxProps, useComboBox, useFilter } from "react-aria";
3
3
  import { useComboBoxState } from "react-stately";
4
- import { ColorSpinner, Input, InputProps, ListBox } from "..";
4
+ import { ColorSpinner, Input, InputProps, ListBox, useOutsideClick } from "..";
5
5
  import { Popover } from "./Popover";
6
6
 
7
7
  export type ComboboxProps<T> = AriaComboBoxProps<T> & {
@@ -96,6 +96,12 @@ export function Combobox<T extends object>({
96
96
  label,
97
97
  });
98
98
 
99
+ useOutsideClick({
100
+ ref: listBoxRef,
101
+ handler: state.close,
102
+ enabled: true,
103
+ });
104
+
99
105
  const {
100
106
  inputProps: { size, ...inputProps },
101
107
  listBoxProps,
@@ -141,6 +147,7 @@ export function Combobox<T extends object>({
141
147
  <ColorSpinner
142
148
  width="1.5rem"
143
149
  alignSelf="center"
150
+ paddingRight={paddingRight}
144
151
  css={{
145
152
  div: {
146
153
  display: "flex",
@@ -159,6 +166,8 @@ export function Combobox<T extends object>({
159
166
  triggerRef={inputRef as any}
160
167
  ref={popoverRef}
161
168
  placement="bottom start"
169
+ shouldFlip={false}
170
+ hasBackdrop={false}
162
171
  >
163
172
  <ListBox
164
173
  {...listBoxProps}
@@ -27,6 +27,23 @@ type PopoverProps = {
27
27
  * Defaults to "bottom"
28
28
  */
29
29
  placement?: AriaPopoverProps["placement"];
30
+ /**
31
+ * Whether or not the list should flip to the opposite side of the trigger if there is not enough space.
32
+ * Defaults to false.
33
+ */
34
+ shouldFlip?: boolean;
35
+ /**
36
+ * Whether the popover is non-modal, i.e. elements outside the popover may be interacted with by assistive technologies.
37
+ * Most popovers should not use this option as it may negatively impact the screen reader experience. Only use with components such as combobox, which are designed to handle this situation carefully.
38
+ *
39
+ * Defaults to false.
40
+ */
41
+ isNonModal?: boolean;
42
+ /** Whether or not the popover renders a backdrop that stops the user from interacting with background elements
43
+ *
44
+ * Defaults to true
45
+ */
46
+ hasBackdrop?: boolean;
30
47
  };
31
48
  /**
32
49
  * Internal popover component.
@@ -42,6 +59,9 @@ export const Popover = forwardRef<HTMLDivElement, PopoverProps>(
42
59
  offset = 0,
43
60
  crossOffset = 0,
44
61
  placement = "bottom",
62
+ shouldFlip = false,
63
+ isNonModal = false,
64
+ hasBackdrop = true,
45
65
  },
46
66
  ref
47
67
  ) => {
@@ -55,13 +75,15 @@ export const Popover = forwardRef<HTMLDivElement, PopoverProps>(
55
75
  offset,
56
76
  crossOffset,
57
77
  placement,
78
+ shouldFlip,
79
+ isNonModal,
58
80
  },
59
81
  state
60
82
  );
61
83
 
62
84
  return (
63
85
  <Overlay>
64
- <Box {...underlayProps} position="fixed" inset="0" />
86
+ {hasBackdrop && <Box {...underlayProps} position="fixed" inset="0" />}
65
87
  <Box
66
88
  {...popoverProps}
67
89
  ref={popoverRef}
@@ -70,6 +70,7 @@ const config = helpers.defineMultiStyleConfig({
70
70
  fontSize: "mobile.xs",
71
71
  color: mode("darkGrey", "white")(props),
72
72
  margin: 0,
73
+ cursor: "text",
73
74
  },
74
75
  dateTimeSegment: {
75
76
  color: mode(