@vygruppen/spor-react 1.3.4 → 2.0.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.
Files changed (196) hide show
  1. package/.turbo/turbo-build.log +12 -10
  2. package/CHANGELOG.md +31 -0
  3. package/dist/index.d.ts +6720 -27
  4. package/dist/index.js +14143 -140
  5. package/dist/index.mjs +13822 -27
  6. package/package.json +19 -31
  7. package/src/accordion/Accordion.test.tsx +20 -0
  8. package/src/accordion/Accordion.tsx +62 -0
  9. package/src/accordion/AccordionContext.tsx +27 -0
  10. package/src/accordion/Expandable.tsx +157 -0
  11. package/src/accordion/index.tsx +2 -0
  12. package/src/alert/AlertIcon.tsx +75 -0
  13. package/src/alert/BaseAlert.test.tsx +37 -0
  14. package/src/alert/BaseAlert.tsx +21 -0
  15. package/src/alert/ClosableAlert.test.tsx +37 -0
  16. package/src/alert/ClosableAlert.tsx +75 -0
  17. package/src/alert/ExpandableAlert.test.tsx +84 -0
  18. package/src/alert/ExpandableAlert.tsx +84 -0
  19. package/src/alert/StaticAlert.tsx +25 -0
  20. package/src/alert/index.tsx +3 -0
  21. package/src/button/Button.test.tsx +23 -0
  22. package/src/button/Button.tsx +162 -0
  23. package/src/button/ButtonGroup.tsx +43 -0
  24. package/src/button/CloseButton.tsx +63 -0
  25. package/src/button/FloatingActionButton.tsx +113 -0
  26. package/src/button/IconButton.tsx +63 -0
  27. package/src/button/index.tsx +5 -0
  28. package/src/card/Card.tsx +59 -0
  29. package/src/card/index.tsx +1 -0
  30. package/src/datepicker/Calendar.tsx +32 -0
  31. package/src/datepicker/CalendarCell.tsx +74 -0
  32. package/src/datepicker/CalendarGrid.tsx +76 -0
  33. package/src/datepicker/CalendarHeader.tsx +153 -0
  34. package/src/datepicker/CalendarNavigationButton.tsx +26 -0
  35. package/src/datepicker/CalendarTriggerButton.tsx +36 -0
  36. package/src/datepicker/DateField.tsx +51 -0
  37. package/src/datepicker/DatePicker.tsx +153 -0
  38. package/src/datepicker/DateRangePicker.tsx +171 -0
  39. package/src/datepicker/DateTimeSegment.tsx +56 -0
  40. package/src/datepicker/RangeCalendar.tsx +35 -0
  41. package/src/datepicker/StyledField.tsx +31 -0
  42. package/src/datepicker/TimeField.tsx +46 -0
  43. package/src/datepicker/TimePicker.test.tsx +74 -0
  44. package/src/datepicker/TimePicker.tsx +196 -0
  45. package/src/datepicker/index.tsx +4 -0
  46. package/src/datepicker/utils.ts +33 -0
  47. package/src/i18n/index.tsx +38 -0
  48. package/src/image/index.tsx +2 -0
  49. package/src/index.tsx +25 -26
  50. package/src/input/CardSelect.tsx +165 -0
  51. package/src/input/Checkbox.tsx +24 -0
  52. package/src/input/CheckboxGroup.tsx +43 -0
  53. package/src/input/ChoiceChip.tsx +102 -0
  54. package/src/input/Dialog.tsx +29 -0
  55. package/src/input/FormControl.tsx +11 -0
  56. package/src/input/FormErrorMessage.tsx +91 -0
  57. package/src/input/FormLabel.tsx +11 -0
  58. package/src/input/InfoSelect.tsx +209 -0
  59. package/src/input/Input.tsx +59 -0
  60. package/src/input/InputElement.tsx +45 -0
  61. package/src/input/ListBox.tsx +123 -0
  62. package/src/input/NativeSelect.tsx +38 -0
  63. package/src/input/PasswordInput.tsx +70 -0
  64. package/src/input/Popover.tsx +70 -0
  65. package/src/input/Radio.tsx +34 -0
  66. package/src/input/RadioGroup.tsx +47 -0
  67. package/src/input/SearchInput.tsx +89 -0
  68. package/src/input/Switch.tsx +40 -0
  69. package/src/input/Textarea.tsx +98 -0
  70. package/src/input/index.tsx +20 -0
  71. package/src/layout/Divider.tsx +26 -0
  72. package/src/layout/Stack.tsx +42 -0
  73. package/src/layout/index.tsx +28 -0
  74. package/src/linjetag/InfoTag.tsx +54 -0
  75. package/src/linjetag/LineIcon.tsx +44 -0
  76. package/src/linjetag/TravelTag.tsx +121 -0
  77. package/src/linjetag/icons.tsx +80 -0
  78. package/src/linjetag/index.tsx +3 -0
  79. package/src/linjetag/types.d.ts +24 -0
  80. package/src/link/TextLink.tsx +45 -0
  81. package/src/link/index.tsx +1 -0
  82. package/src/loader/ClientOnly.tsx +29 -0
  83. package/src/loader/ColorInlineLoader.tsx +27 -0
  84. package/src/loader/ColorSpinner.tsx +44 -0
  85. package/src/loader/ContentLoader.tsx +27 -0
  86. package/src/loader/DarkFullScreenLoader.tsx +23 -0
  87. package/src/loader/DarkInlineLoader.tsx +25 -0
  88. package/src/loader/DarkSpinner.tsx +43 -0
  89. package/src/loader/LightFullScreenLoader.tsx +23 -0
  90. package/src/loader/LightInlineLoader.tsx +25 -0
  91. package/src/loader/LightSpinner.tsx +41 -0
  92. package/src/loader/Lottie.tsx +10 -0
  93. package/src/loader/ProgressBar.tsx +128 -0
  94. package/src/loader/ProgressLoader.tsx +140 -0
  95. package/src/loader/Skeleton.tsx +16 -0
  96. package/src/loader/SkeletonCircle.tsx +13 -0
  97. package/src/loader/SkeletonText.tsx +10 -0
  98. package/src/loader/index.tsx +14 -0
  99. package/src/loader/useHydrated.tsx +34 -0
  100. package/src/loader/useRotatingLabel.tsx +22 -0
  101. package/src/logo/VyLogo.tsx +101 -0
  102. package/src/logo/index.tsx +1 -0
  103. package/src/media-controller/JumpButton.tsx +69 -0
  104. package/src/media-controller/PlayPauseButton.tsx +67 -0
  105. package/src/media-controller/SkipButton.tsx +66 -0
  106. package/src/media-controller/icons.tsx +80 -0
  107. package/src/media-controller/index.test.tsx +59 -0
  108. package/src/media-controller/index.tsx +3 -0
  109. package/src/modal/Drawer.tsx +122 -0
  110. package/src/modal/Modal.tsx +15 -0
  111. package/src/modal/ModalHeader.tsx +31 -0
  112. package/src/modal/SimpleDrawer.tsx +44 -0
  113. package/src/modal/index.tsx +4 -0
  114. package/src/popover/PopoverWizardBody.tsx +91 -0
  115. package/src/popover/SimplePopover.tsx +75 -0
  116. package/src/popover/WizardPopover.tsx +61 -0
  117. package/src/popover/index.tsx +23 -0
  118. package/src/provider/SporProvider.tsx +67 -0
  119. package/src/provider/index.tsx +1 -0
  120. package/src/stepper/Stepper.tsx +115 -0
  121. package/src/stepper/StepperContext.tsx +55 -0
  122. package/src/stepper/StepperStep.tsx +48 -0
  123. package/src/stepper/index.tsx +2 -0
  124. package/src/tab/Tabs.tsx +20 -0
  125. package/src/tab/index.tsx +9 -0
  126. package/src/table/Table.tsx +58 -0
  127. package/src/table/index.tsx +19 -0
  128. package/src/theme/components/accordion.ts +143 -0
  129. package/src/theme/components/alert.ts +59 -0
  130. package/src/theme/components/badge.ts +109 -0
  131. package/src/theme/components/button.ts +217 -0
  132. package/src/theme/components/card-select.ts +158 -0
  133. package/src/theme/components/card.ts +174 -0
  134. package/src/theme/components/checkbox.ts +90 -0
  135. package/src/theme/components/choice-chip.ts +79 -0
  136. package/src/theme/components/close-button.ts +56 -0
  137. package/src/theme/components/code.ts +17 -0
  138. package/src/theme/components/datepicker.ts +194 -0
  139. package/src/theme/components/drawer.ts +92 -0
  140. package/src/theme/components/fab.ts +111 -0
  141. package/src/theme/components/form-label.ts +17 -0
  142. package/src/theme/components/form.ts +27 -0
  143. package/src/theme/components/index.ts +34 -0
  144. package/src/theme/components/info-select.ts +91 -0
  145. package/src/theme/components/info-tag.ts +49 -0
  146. package/src/theme/components/input.ts +97 -0
  147. package/src/theme/components/line-icon.ts +121 -0
  148. package/src/theme/components/link.ts +155 -0
  149. package/src/theme/components/listbox.ts +52 -0
  150. package/src/theme/components/media-controller-button.ts +134 -0
  151. package/src/theme/components/modal.ts +93 -0
  152. package/src/theme/components/popover.ts +63 -0
  153. package/src/theme/components/radio.ts +64 -0
  154. package/src/theme/components/select.ts +52 -0
  155. package/src/theme/components/skeleton.ts +40 -0
  156. package/src/theme/components/stepper.ts +230 -0
  157. package/src/theme/components/switch.ts +227 -0
  158. package/src/theme/components/table.ts +163 -0
  159. package/src/theme/components/tabs.ts +282 -0
  160. package/src/theme/components/textarea.ts +14 -0
  161. package/src/theme/components/toast.ts +28 -0
  162. package/src/theme/components/travel-tag.ts +267 -0
  163. package/src/theme/font-faces.ts +66 -0
  164. package/src/theme/foundations/borders.ts +11 -0
  165. package/src/theme/foundations/breakpoints.ts +9 -0
  166. package/src/theme/foundations/colors.ts +10 -0
  167. package/src/theme/foundations/config.ts +5 -0
  168. package/src/theme/foundations/fontSizes.ts +29 -0
  169. package/src/theme/foundations/fontWeights.ts +5 -0
  170. package/src/theme/foundations/fonts.ts +7 -0
  171. package/src/theme/foundations/index.ts +14 -0
  172. package/src/theme/foundations/lineHeights.ts +5 -0
  173. package/src/theme/foundations/radii.ts +12 -0
  174. package/src/theme/foundations/shadows.ts +8 -0
  175. package/src/theme/foundations/sizes.ts +34 -0
  176. package/src/theme/foundations/spacing.ts +30 -0
  177. package/src/theme/foundations/textStyles.ts +60 -0
  178. package/src/theme/foundations/zIndices.ts +17 -0
  179. package/src/theme/index.ts +14 -0
  180. package/src/theme/utils/box-shadow-utils.ts +44 -0
  181. package/src/theme/utils/focus-utils.ts +16 -0
  182. package/src/toast/ActionToast.test.tsx +22 -0
  183. package/src/toast/ActionToast.tsx +28 -0
  184. package/src/toast/BaseToast.test.tsx +27 -0
  185. package/src/toast/BaseToast.tsx +75 -0
  186. package/src/toast/ClosableToast.test.tsx +17 -0
  187. package/src/toast/ClosableToast.tsx +40 -0
  188. package/src/toast/index.tsx +1 -0
  189. package/src/toast/useToast.tsx +99 -0
  190. package/src/typography/Badge.tsx +68 -0
  191. package/src/typography/Code.tsx +32 -0
  192. package/src/typography/Heading.tsx +32 -0
  193. package/src/typography/Text.tsx +26 -0
  194. package/src/typography/index.tsx +4 -0
  195. package/src/util/externals.tsx +23 -0
  196. package/src/util/index.tsx +1 -0
package/src/index.tsx CHANGED
@@ -1,27 +1,26 @@
1
1
  export { extendTheme } from "@chakra-ui/react";
2
- export * from "@vygruppen/spor-accordion-react";
3
- export * from "@vygruppen/spor-alert-react";
4
- export * from "@vygruppen/spor-button-react";
5
- export * from "@vygruppen/spor-card-react";
6
- export * from "@vygruppen/spor-datepicker-react";
7
- export * from "@vygruppen/spor-design-tokens";
8
- export * from "@vygruppen/spor-i18n-react";
9
- export * from "@vygruppen/spor-icon-react";
10
- export * from "@vygruppen/spor-image-react";
11
- export * from "@vygruppen/spor-input-react";
12
- export * from "@vygruppen/spor-layout-react";
13
- export * from "@vygruppen/spor-linjetag-react";
14
- export * from "@vygruppen/spor-link-react";
15
- export * from "@vygruppen/spor-loader-react";
16
- export * from "@vygruppen/spor-logo-react";
17
- export * from "@vygruppen/spor-media-controller-react";
18
- export * from "@vygruppen/spor-modal-react";
19
- export * from "@vygruppen/spor-popover-react";
20
- export * from "@vygruppen/spor-provider-react";
21
- export * from "@vygruppen/spor-stepper-react";
22
- export * from "@vygruppen/spor-tab-react";
23
- export * from "@vygruppen/spor-table-react";
24
- export * from "@vygruppen/spor-theme-react";
25
- export * from "@vygruppen/spor-toast-react";
26
- export * from "@vygruppen/spor-typography-react";
27
- export * from "@vygruppen/spor-util-react";
2
+ export * as tokens from "@vygruppen/spor-design-tokens";
3
+ export * from "./accordion";
4
+ export * from "./alert";
5
+ export * from "./button";
6
+ export * from "./card";
7
+ export * from "./datepicker";
8
+ export * from "./i18n";
9
+ export * from "./image";
10
+ export * from "./input";
11
+ export * from "./layout";
12
+ export * from "./linjetag";
13
+ export * from "./link";
14
+ export * from "./loader";
15
+ export * from "./logo";
16
+ export * from "./media-controller";
17
+ export * from "./modal";
18
+ export * from "./popover";
19
+ export * from "./provider";
20
+ export * from "./stepper";
21
+ export * from "./tab";
22
+ export * from "./table";
23
+ export * from "./theme";
24
+ export * from "./toast";
25
+ export * from "./typography";
26
+ export * from "./util";
@@ -0,0 +1,165 @@
1
+ import {
2
+ Box,
3
+ BoxProps,
4
+ chakra,
5
+ Flex,
6
+ forwardRef,
7
+ useMultiStyleConfig,
8
+ } from "@chakra-ui/react";
9
+ import {
10
+ DropdownDownFill18Icon,
11
+ DropdownDownFill24Icon,
12
+ } from "@vygruppen/spor-icon-react";
13
+ import React, { useEffect, useRef, useState } from "react";
14
+ import { AriaPositionProps, useButton, useOverlayTrigger } from "react-aria";
15
+ import { useOverlayTriggerState } from "react-stately";
16
+ import { Card } from "..";
17
+ import { Dialog } from "./Dialog";
18
+ import { Popover } from "./Popover";
19
+
20
+ type CardSelectProps = BoxProps & {
21
+ /** The design of the trigger button.
22
+ *
23
+ * - `ghost` is a transparent button with text
24
+ * - `outline` is a button with a border and text
25
+ * - `card` is a button with a drop shadow (like a card) and text
26
+ */
27
+ variant: "ghost" | "outline" | "card";
28
+ /** The size of the trigger button */
29
+ size: "sm" | "md" | "lg";
30
+ /** Whether the card select is open / active, if controlled */
31
+ isOpen?: boolean;
32
+ /** The default state of the card select. Defaults to false (closed) */
33
+ defaultOpen?: boolean;
34
+ /** Callback for when the card select opens or closes. */
35
+ onToggle?: (isOpen: boolean) => void;
36
+ /** The text of the trigger button */
37
+ label: string;
38
+ /** An optional trigger button icon, rendered to the left of the label */
39
+ icon?: React.ReactNode;
40
+ /** The content of the card select */
41
+ children: React.ReactNode;
42
+ /** The horizontalOffset of the popover card */
43
+ crossOffset?: number;
44
+ /** The position of the popover card */
45
+ placement?: AriaPositionProps["placement"];
46
+ /** Whether or not to show the chevron. Defaults to true */
47
+ withChevron?: boolean;
48
+ };
49
+
50
+ /**
51
+ * A card select component.
52
+ *
53
+ * This component consists of a trigger button and a card select popover. The trigger button has several different variants and sizes, and can have an optional icon.
54
+ *
55
+ * ```tsx
56
+ * <CardSelect label="Languages" variant="card" size="md">
57
+ * <LanguageSettings />
58
+ * </CardSelect>
59
+ * ```
60
+ *
61
+ * @see https://spor.vy.no/komponenter/card-select
62
+ */
63
+ export const CardSelect = forwardRef<CardSelectProps, "button">(
64
+ (
65
+ {
66
+ variant,
67
+ size,
68
+ isOpen: externalIsOpen,
69
+ defaultOpen = false,
70
+ onToggle,
71
+ label,
72
+ icon,
73
+ children,
74
+ width = "fit-content",
75
+ crossOffset = 0,
76
+ placement = "bottom",
77
+ withChevron = true,
78
+ ...props
79
+ },
80
+ externalRef
81
+ ) => {
82
+ const internalRef = useRef<HTMLButtonElement>(null);
83
+ const triggerRef = (externalRef ??
84
+ internalRef) as React.RefObject<HTMLButtonElement>;
85
+
86
+ const state = useOverlayTriggerState({
87
+ isOpen: externalIsOpen,
88
+ onOpenChange: onToggle,
89
+ defaultOpen,
90
+ });
91
+ const { triggerProps, overlayProps } = useOverlayTrigger(
92
+ { type: "dialog" },
93
+ state,
94
+ triggerRef
95
+ );
96
+
97
+ const { buttonProps } = useButton(triggerProps, triggerRef);
98
+
99
+ const styles = useMultiStyleConfig("CardSelect", { variant, size });
100
+ useForceRerender(state.isOpen);
101
+
102
+ const ChevronIcon =
103
+ size === "sm" ? DropdownDownFill18Icon : DropdownDownFill24Icon;
104
+
105
+ return (
106
+ <Box {...props}>
107
+ <chakra.button
108
+ type="button"
109
+ ref={triggerRef}
110
+ sx={styles.trigger}
111
+ {...buttonProps}
112
+ width={width}
113
+ >
114
+ <Flex gap={1.5} alignItems="center">
115
+ {icon}
116
+ <Box as="span">{label}</Box>
117
+ {withChevron ? (
118
+ <ChevronIcon
119
+ transform={state.isOpen ? "rotate(180deg)" : "none"}
120
+ />
121
+ ) : null}
122
+ </Flex>
123
+ </chakra.button>
124
+ {state.isOpen && (
125
+ <Popover
126
+ state={state}
127
+ triggerRef={triggerRef}
128
+ offset={size === "sm" ? 6 : 12}
129
+ crossOffset={crossOffset}
130
+ placement={placement}
131
+ >
132
+ <Card
133
+ colorScheme="white"
134
+ size="lg"
135
+ sx={styles.card}
136
+ {...overlayProps}
137
+ >
138
+ <Dialog aria-label={label}>{children}</Dialog>
139
+ </Card>
140
+ </Popover>
141
+ )}
142
+ </Box>
143
+ );
144
+ }
145
+ );
146
+
147
+ /**
148
+ * Hold my beer.
149
+ *
150
+ * This is a workaround for a "bug" in react-aria where the overlay doesn't
151
+ * calculate the placement correctly for some reason.
152
+ *
153
+ * This is a hack, which forces React to rerender the component one extra time
154
+ * after the state changes from closed to open.
155
+ *
156
+ * There is probably a better way to do this, but I could not come up with one.
157
+ */
158
+ function useForceRerender(shouldRerender: boolean) {
159
+ const [_, update] = useState(false);
160
+ useEffect(() => {
161
+ if (shouldRerender) {
162
+ update((x) => !x);
163
+ }
164
+ }, [shouldRerender]);
165
+ }
@@ -0,0 +1,24 @@
1
+ import {
2
+ Checkbox as ChakraCheckbox,
3
+ CheckboxProps as ChakraCheckboxProps,
4
+ forwardRef,
5
+ } from "@chakra-ui/react";
6
+ import React from "react";
7
+
8
+ export type CheckboxProps = ChakraCheckboxProps;
9
+ /**
10
+ * Creates a checkbox.
11
+ *
12
+ * The checkbox contains its own label, which is passed as a children prop:
13
+ *
14
+ * ```tsx
15
+ * <Checkbox>Accept the terms</Checkbox>
16
+ * ```
17
+ *
18
+ * Unlike regular inputs, it doesn't require its own `FormControl`.
19
+ *
20
+ * You can group several of these together with a `CheckboxGroup`.
21
+ */
22
+ export const Checkbox = forwardRef<CheckboxProps, "input">((props, ref) => {
23
+ return <ChakraCheckbox {...props} ref={ref} />;
24
+ });
@@ -0,0 +1,43 @@
1
+ import {
2
+ CheckboxGroup as ChakraCheckboxGroup,
3
+ CheckboxGroupProps as ChakraCheckboxGroupProps,
4
+ Stack,
5
+ StackDirection,
6
+ } from "@chakra-ui/react";
7
+ import React from "react";
8
+
9
+ export type CheckboxGroupProps = Exclude<
10
+ ChakraCheckboxGroupProps,
11
+ "colorScheme" | "size" | "variant"
12
+ > & { direction?: StackDirection };
13
+ /**
14
+ * Used to group several checkboxes together. You can pass the default value, as well as whether or not they're all disabled
15
+ *
16
+ * ```tsx
17
+ * <CheckboxGroup isDisabled defaultValue={['red', 'blue']}>
18
+ * <Checkbox value="red">Red</Checkbox>
19
+ * <Checkbox value="blue">Blue</Checkbox>
20
+ * <Checkbox value="green">Green</Checkbox>
21
+ * </CheckboxGroup>
22
+ * ```
23
+ *
24
+ * By default, checkboxes in a CheckboxGroup show up horizontally. If you want them to show up vertically, please specify the `direction="column"` prop.
25
+ *
26
+ * ```tsx
27
+ * <CheckboxGroup direction="column">
28
+ * <Checkbox>Economy</Checkbox>
29
+ * <Checkbox>Business</Checkbox>
30
+ * <Checkbox>First Class</Checkbox>
31
+ * </CheckboxGroup>
32
+ */
33
+ export const CheckboxGroup = ({
34
+ direction = "row",
35
+ children,
36
+ ...props
37
+ }: CheckboxGroupProps) => {
38
+ return (
39
+ <ChakraCheckboxGroup {...props}>
40
+ <Stack direction={direction}>{children}</Stack>
41
+ </ChakraCheckboxGroup>
42
+ );
43
+ };
@@ -0,0 +1,102 @@
1
+ import {
2
+ chakra,
3
+ forwardRef,
4
+ useCheckbox,
5
+ useMultiStyleConfig,
6
+ } from "@chakra-ui/react";
7
+ import { dataAttr } from "@chakra-ui/utils";
8
+ import { CloseOutline24Icon } from "@vygruppen/spor-icon-react";
9
+ import React, { ChangeEvent, useId } from "react";
10
+
11
+ export type ChoiceChipProps = {
12
+ onChange?: (value: ChangeEvent<HTMLInputElement>) => void;
13
+ isChecked?: boolean;
14
+ defaultChecked?: boolean;
15
+ /** The button text */
16
+ children: React.ReactNode;
17
+ icon?: {
18
+ default: React.ReactNode;
19
+ checked: React.ReactNode;
20
+ };
21
+ size?: "sm" | "md" | "lg" | "xl";
22
+ variant?: "icon" | "choice" | "filter";
23
+ };
24
+ /**
25
+ * Choice chips are checkboxes that look like selectable buttons.
26
+ *
27
+ * Choice chips are available in four different sizes - `sm`, `md`, `lg` and `xl`.
28
+ *
29
+ * ```tsx
30
+ * <Stack flexDirection="row">
31
+ * <ChoiceChip size="lg">Bus</ChoiceChip>
32
+ * <ChoiceChip size="lg">Train</ChoiceChip>
33
+ * </Stack>
34
+ * ```
35
+ *
36
+ * There are also three different variants - `icon`, `choice` and `filter`.
37
+ *
38
+ * ```tsx
39
+ * <Stack flexDirection="row">
40
+ * <ChoiceChip variant="icon" icon={<Bus24Icon />}>Bus</ChoiceChip>
41
+ * <ChoiceChip variant="choice" icon={<Bus24Icon />}>Bus</ChoiceChip>
42
+ * <ChoiceChip variant="filter" icon={<Bus24Icon />}>Bus</ChoiceChip>
43
+ * </Stack>
44
+ *
45
+ * You can add an icon as well, if you want to!
46
+ *
47
+ * ```tsx
48
+ * <Stack flexDirection="row">
49
+ * <ChoiceChip size="md" icon={<BusIcon />}>Bus</ChoiceChip>
50
+ * <ChoiceChip size="lg" icon={<TrainIcon />}>Train</ChoiceChip>
51
+ * </Stack>
52
+ * ```
53
+ */
54
+ export const ChoiceChip = forwardRef((props: ChoiceChipProps, ref) => {
55
+ const { children, icon, size = "md", variant = "choice" } = props;
56
+
57
+ const {
58
+ state,
59
+ getInputProps,
60
+ getCheckboxProps,
61
+ getRootProps,
62
+ getLabelProps,
63
+ } = useCheckbox(props);
64
+ const styles = useMultiStyleConfig("ChoiceChip", {
65
+ size,
66
+ variant,
67
+ icon,
68
+ hasLabel: Boolean(children),
69
+ });
70
+
71
+ const id = `choice-chip-${useId()}`;
72
+
73
+ return (
74
+ <chakra.label
75
+ htmlFor={id}
76
+ {...getRootProps()}
77
+ aria-label={String(children)}
78
+ >
79
+ <chakra.input {...getInputProps({}, ref)} id={id} />
80
+ <chakra.div
81
+ {...getLabelProps()}
82
+ __css={styles.container}
83
+ data-checked={dataAttr(state.isChecked)}
84
+ data-hover={dataAttr(state.isHovered)}
85
+ data-focus={dataAttr(state.isFocused)}
86
+ >
87
+ {icon && (
88
+ <chakra.span __css={styles.icon}>
89
+ {state.isChecked ? icon.checked : icon.default}
90
+ </chakra.span>
91
+ )}
92
+
93
+ <chakra.span __css={styles.label} {...getCheckboxProps()}>
94
+ {variant !== "icon" && children}
95
+ </chakra.span>
96
+ {variant === "filter" && state.isChecked && (
97
+ <CloseOutline24Icon ml={1.5} />
98
+ )}
99
+ </chakra.div>
100
+ </chakra.label>
101
+ );
102
+ });
@@ -0,0 +1,29 @@
1
+ import { Box, Heading } from "@chakra-ui/react";
2
+ import React, { useRef } from "react";
3
+ import { AriaDialogProps, useDialog } from "react-aria";
4
+
5
+ type DialogProps = AriaDialogProps & {
6
+ /** Optional title tag
7
+ *
8
+ * Important! If you don't pass a title, you must pass an `aria-label` prop to the dialog.
9
+ */
10
+ title?: string;
11
+ /** The content of the dialog */
12
+ children: React.ReactNode;
13
+ };
14
+ /** Internal component for creating card select popovers */
15
+ export const Dialog = ({ title, children, ...props }: DialogProps) => {
16
+ const ref = useRef<HTMLDivElement>(null);
17
+ const { dialogProps, titleProps } = useDialog(props, ref);
18
+
19
+ return (
20
+ <Box {...dialogProps} ref={ref} outline="none">
21
+ {title && (
22
+ <Heading as="h3" {...titleProps}>
23
+ {title}
24
+ </Heading>
25
+ )}
26
+ {children}
27
+ </Box>
28
+ );
29
+ };
@@ -0,0 +1,11 @@
1
+ import {
2
+ FormControl as ChakraFormControl,
3
+ FormControlProps as ChakraFormControlProps,
4
+ forwardRef,
5
+ } from "@chakra-ui/react";
6
+ import React from "react";
7
+
8
+ export type FormControlProps = ChakraFormControlProps;
9
+ export const FormControl = forwardRef<FormControlProps, "div">((props, ref) => {
10
+ return <ChakraFormControl {...props} ref={ref} />;
11
+ });
@@ -0,0 +1,91 @@
1
+ import { Box, BoxProps, useFormControlContext } from "@chakra-ui/react";
2
+ import React from "react";
3
+
4
+ export type FormErrorMessageProps = {
5
+ /**
6
+ * The error message itself.
7
+ */
8
+ children: React.ReactNode;
9
+ };
10
+ /** An error message
11
+ *
12
+ * This component is used to show error messages related to a form input.
13
+ * It should _always_ be used inside of a `FormControl` component.
14
+ *
15
+ * It will only render if the `isInvalid` prop of its enclosing `FormControl` component is set to true.
16
+ *
17
+ * ```tsx
18
+ * <FormControl isInvalid={email === ""}>
19
+ * <Input label="E-mail" value={email} onChange={updateEmail} />
20
+ * <FormErrorMessage>This field is required</FormErrorMessage>
21
+ * </FormControl>
22
+ * ```
23
+ *
24
+ * If you're using a `FormHelperText` (or any other components inside of the `FormControl`), make sure to render the `FormErrorMessage` before those.
25
+ * ```tsx
26
+ * <FormControl isInvalid={email === ""}>
27
+ * <Input label="E-mail" value={email} onChange={updateEmail} />
28
+ * <FormErrorMessage>This field is required</FormErrorMessage>
29
+ * <FormHelperText>We don't share your email</FormHelperText>
30
+ * </FormControl>
31
+ * ```
32
+ *
33
+ * @see https://spor.vy.no/komponenter/skjemaelementer
34
+ */
35
+ export const FormErrorMessage = ({ children }: FormErrorMessageProps) => {
36
+ const formControlContext = useFormControlContext();
37
+ if (!formControlContext) {
38
+ throw new Error(
39
+ "FormErrorMessage must be used within a FormControl component"
40
+ );
41
+ }
42
+ if (!formControlContext.isInvalid) {
43
+ return null;
44
+ }
45
+ const { ref, ...errorMessageProps } =
46
+ formControlContext.getErrorMessageProps();
47
+ return (
48
+ <Box position="relative" ref={ref}>
49
+ <Box
50
+ borderRadius="xs"
51
+ backgroundColor="lightRed"
52
+ color="darkGrey"
53
+ paddingX={1.5}
54
+ paddingY={1}
55
+ textStyle="xs"
56
+ width="fit-content"
57
+ position="absolute"
58
+ top={-1.5}
59
+ left={3}
60
+ zIndex="popover"
61
+ maxWidth="50ch"
62
+ {...errorMessageProps}
63
+ >
64
+ <Arrow position="absolute" top="-0.25em" left="1em" />
65
+ {children}
66
+ </Box>
67
+ </Box>
68
+ );
69
+ };
70
+
71
+ /** The arrow of the error message box */
72
+ const Arrow = (props: BoxProps) => {
73
+ return (
74
+ <Box
75
+ {...(props as any)}
76
+ as="svg"
77
+ width="16"
78
+ height="16"
79
+ viewBox="0 0 16 16"
80
+ fill="none"
81
+ transform="rotate(45deg)"
82
+ >
83
+ <Box
84
+ as="path"
85
+ fill="lightRed"
86
+ d="M 0
87
+ 0 Q 2.4 6 0 12 Q 6 9.6 12 12 Q 9.6 6 12 0 Q 6 2.4 0 0 z"
88
+ />
89
+ </Box>
90
+ );
91
+ };
@@ -0,0 +1,11 @@
1
+ import {
2
+ FormLabel as ChakraFormLabel,
3
+ FormLabelProps as ChakraFormLabelProps,
4
+ forwardRef,
5
+ } from "@chakra-ui/react";
6
+ import React from "react";
7
+
8
+ export type FormLabelProps = ChakraFormLabelProps;
9
+ export const FormLabel = forwardRef<FormLabelProps, "label">((props, ref) => {
10
+ return <ChakraFormLabel {...props} ref={ref} />;
11
+ });