@vygruppen/spor-react 1.3.4 → 2.0.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.
Files changed (196) hide show
  1. package/.turbo/turbo-build.log +12 -10
  2. package/CHANGELOG.md +25 -0
  3. package/dist/index.d.ts +6718 -27
  4. package/dist/index.js +14139 -140
  5. package/dist/index.mjs +13818 -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 +165 -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
@@ -0,0 +1,45 @@
1
+ import {
2
+ Link as ChakraLink,
3
+ LinkProps as ChakraLinkProps,
4
+ forwardRef,
5
+ } from "@chakra-ui/react";
6
+ import { LinkOutOutline24Icon } from "@vygruppen/spor-icon-react";
7
+ import React from "react";
8
+ import { createTexts, useTranslation } from "..";
9
+
10
+ type LinkProps = Omit<ChakraLinkProps, "variant"> & {
11
+ variant?: "primary" | "secondary" | "tertiary";
12
+ };
13
+ /** Link to different sites or parts of site
14
+ *
15
+ * You can specify the `variant` prop to get different link designs. `tertiary` should only be used on dark backgrounds.
16
+ */
17
+ export const TextLink = forwardRef<LinkProps, "a">(
18
+ ({ children, ...props }, ref) => {
19
+ const { t } = useTranslation();
20
+ const isExternal =
21
+ props.isExternal !== undefined
22
+ ? props.isExternal
23
+ : Boolean(props.href?.match(/^https?:\/\//));
24
+ return (
25
+ <ChakraLink {...props} ref={ref} isExternal={isExternal}>
26
+ {children}
27
+ {isExternal && (
28
+ <LinkOutOutline24Icon
29
+ marginLeft={0.5}
30
+ aria-label={t(texts.externalLink)}
31
+ />
32
+ )}
33
+ </ChakraLink>
34
+ );
35
+ }
36
+ );
37
+
38
+ const texts = createTexts({
39
+ externalLink: {
40
+ nb: "Ekstern lenke",
41
+ nn: "Ekstern lenke",
42
+ sv: "Extern länk",
43
+ en: "External link",
44
+ },
45
+ });
@@ -0,0 +1 @@
1
+ export * from "./TextLink";
@@ -0,0 +1,29 @@
1
+ import React from "react";
2
+ import { useHydrated } from "./useHydrated";
3
+
4
+ type ClientOnlyProps = {
5
+ /** A function that renders the client-side only component */
6
+ children: () => React.ReactNode;
7
+ /** An optional fallback to render in place on the server */
8
+ fallback?: React.ReactNode;
9
+ };
10
+
11
+ /**
12
+ * Render the children only after the JS has loaded client-side. Use an optional
13
+ * fallback component if the JS is not yet loaded.
14
+ *
15
+ * Example: Render a Chart component if JS loads, renders a simple FakeChart
16
+ * component server-side or if there is no JS. The FakeChart can have only the
17
+ * UI without the behavior or be a loading spinner or skeleton.
18
+ * ```tsx
19
+ * return (
20
+ * <ClientOnly fallback={<FakeChart />}>
21
+ * {() => <Chart />}
22
+ * </ClientOnly>
23
+ * );
24
+ * ```
25
+ */
26
+ export const ClientOnly = ({ children, fallback = null }: ClientOnlyProps) => {
27
+ const isHydrated = useHydrated();
28
+ return <>{isHydrated ? children() : fallback}</>;
29
+ };
@@ -0,0 +1,27 @@
1
+ import { Box, BoxProps, Center } from "@chakra-ui/react";
2
+ import { inlineLoaderColorData } from "@vygruppen/spor-loader";
3
+ import React from "react";
4
+ import { ClientOnly } from "./ClientOnly";
5
+ import Lottie from "./Lottie";
6
+
7
+ export type ColorInlineLoaderProps = Exclude<BoxProps, "children">;
8
+ /**
9
+ * Loading component that works well in bounded contexts, like inside a button.
10
+ *
11
+ * This component should only be used on light backgrounds with low saturation (e.g. white, light grey etc.). For colored backgrounds, please use the LightInlineLoader component.
12
+ */
13
+ export const ColorInlineLoader = ({
14
+ width,
15
+ maxWidth,
16
+ ...props
17
+ }: ColorInlineLoaderProps) => {
18
+ return (
19
+ <Center {...props}>
20
+ <Box width={width} maxWidth={maxWidth}>
21
+ <ClientOnly>
22
+ {() => <Lottie animationData={inlineLoaderColorData} />}
23
+ </ClientOnly>
24
+ </Box>
25
+ </Center>
26
+ );
27
+ };
@@ -0,0 +1,44 @@
1
+ import { Box, BoxProps, Center } from "@chakra-ui/react";
2
+ import { spinnerColorData } from "@vygruppen/spor-loader";
3
+ import React from "react";
4
+ import { ClientOnly } from "./ClientOnly";
5
+ import Lottie from "./Lottie";
6
+
7
+ export type SpinnerProps = BoxProps;
8
+ export type ColorSpinnerProps = SpinnerProps;
9
+ /** A circular spinner
10
+ *
11
+ * Can be used in place of a loading animation, or for reloading app state, for instance.
12
+ *
13
+ * ```tsx
14
+ * <ColorSpinner width="64px" height="64px" />
15
+ * ```
16
+ *
17
+ * You can also pass an explanatory text as `children`:
18
+ *
19
+ * ```tsx
20
+ * <ColorSpinner>
21
+ * Hold your horses
22
+ * </ColorSpinner>
23
+ */
24
+ export const ColorSpinner = ({
25
+ children,
26
+ width,
27
+ maxWidth,
28
+ ...props
29
+ }: SpinnerProps) => {
30
+ return (
31
+ <Center flexDirection="column" {...props}>
32
+ <Box width={width} maxWidth={maxWidth}>
33
+ <ClientOnly>
34
+ {() => <Lottie animationData={spinnerColorData} />}
35
+ </ClientOnly>
36
+ </Box>
37
+ {children && (
38
+ <Box mt={3} fontWeight="bold">
39
+ {children}
40
+ </Box>
41
+ )}
42
+ </Center>
43
+ );
44
+ };
@@ -0,0 +1,27 @@
1
+ import { Box, BoxProps } from "@chakra-ui/react";
2
+ import { contentLoaderData } from "@vygruppen/spor-loader";
3
+ import React from "react";
4
+ import { ClientOnly } from "./ClientOnly";
5
+ import Lottie from "./Lottie";
6
+
7
+ export type ContentLoaderProps = BoxProps;
8
+ /**
9
+ * ContentLoader is a component that renders a loading animation.
10
+ * It should mostly be used for
11
+ */
12
+ export const ContentLoader = ({ children, ...props }: ContentLoaderProps) => {
13
+ return (
14
+ <Box {...props}>
15
+ <Box maxWidth="140px" mx="auto">
16
+ <ClientOnly>
17
+ {() => <Lottie animationData={contentLoaderData} />}
18
+ </ClientOnly>
19
+ </Box>
20
+ {children && (
21
+ <Box textAlign="center" fontWeight="bold">
22
+ {children}
23
+ </Box>
24
+ )}
25
+ </Box>
26
+ );
27
+ };
@@ -0,0 +1,23 @@
1
+ import { Box, BoxProps, Center } from "@chakra-ui/react";
2
+ import { fullScreenLoaderWhiteData } from "@vygruppen/spor-loader";
3
+ import React from "react";
4
+ import { ClientOnly } from "./ClientOnly";
5
+ import Lottie from "./Lottie";
6
+
7
+ type DarkFullScreenLoaderProps = BoxProps;
8
+
9
+ export const DarkFullScreenLoader = ({
10
+ width,
11
+ maxWidth,
12
+ ...props
13
+ }: DarkFullScreenLoaderProps) => {
14
+ return (
15
+ <Center height="100%" background="darkTeal" {...props}>
16
+ <Box width={width} maxWidth={maxWidth}>
17
+ <ClientOnly>
18
+ {() => <Lottie animationData={fullScreenLoaderWhiteData} />}
19
+ </ClientOnly>
20
+ </Box>
21
+ </Center>
22
+ );
23
+ };
@@ -0,0 +1,25 @@
1
+ import { Box, BoxProps, Center } from "@chakra-ui/react";
2
+ import { inlineLoaderDarkData } from "@vygruppen/spor-loader";
3
+ import React from "react";
4
+ import { ClientOnly } from "./ClientOnly";
5
+ import Lottie from "./Lottie";
6
+
7
+ export type DarkInlineLoaderProps = Exclude<BoxProps, "children">;
8
+ /**
9
+ * Loading component that works well in bounded contexts, like inside a button.
10
+ */
11
+ export const DarkInlineLoader = ({
12
+ width,
13
+ maxWidth,
14
+ ...props
15
+ }: DarkInlineLoaderProps) => {
16
+ return (
17
+ <Center {...props}>
18
+ <Box width={width} maxWidth={maxWidth}>
19
+ <ClientOnly>
20
+ {() => <Lottie animationData={inlineLoaderDarkData} />}
21
+ </ClientOnly>
22
+ </Box>
23
+ </Center>
24
+ );
25
+ };
@@ -0,0 +1,43 @@
1
+ import { Box, BoxProps, Center } from "@chakra-ui/react";
2
+ import { spinnerDarkData } from "@vygruppen/spor-loader";
3
+ import React from "react";
4
+ import { ClientOnly } from "./ClientOnly";
5
+ import Lottie from "./Lottie";
6
+
7
+ export type DarkSpinnerProps = BoxProps;
8
+ /** A circular spinner
9
+ *
10
+ * Can be used in place of a loading animation, or for reloading app state, for instance.
11
+ *
12
+ * ```tsx
13
+ * <DarkSpinner width="64px" height="64px" />
14
+ * ```
15
+ *
16
+ * You can also pass an explanatory text as `children`:
17
+ *
18
+ * ```tsx
19
+ * <DarkSpinner>
20
+ * Hold your horses
21
+ * </DarkSpinner>
22
+ */
23
+ export const DarkSpinner = ({
24
+ children,
25
+ width,
26
+ maxWidth,
27
+ ...props
28
+ }: DarkSpinnerProps) => {
29
+ return (
30
+ <Center flexDirection="column" {...props}>
31
+ <Box width={width} maxWidth={maxWidth}>
32
+ <ClientOnly>
33
+ {() => <Lottie animationData={spinnerDarkData} />}
34
+ </ClientOnly>
35
+ </Box>
36
+ {children && (
37
+ <Box mt={3} fontWeight="bold">
38
+ {children}
39
+ </Box>
40
+ )}
41
+ </Center>
42
+ );
43
+ };
@@ -0,0 +1,23 @@
1
+ import { Box, BoxProps, Center } from "@chakra-ui/react";
2
+ import { fullScreenLoaderBlackData } from "@vygruppen/spor-loader";
3
+ import React from "react";
4
+ import { ClientOnly } from "./ClientOnly";
5
+ import Lottie from "./Lottie";
6
+
7
+ type LightFullScreenLoaderProps = BoxProps;
8
+
9
+ export const LightFullScreenLoader = ({
10
+ width,
11
+ maxWidth,
12
+ ...props
13
+ }: LightFullScreenLoaderProps) => {
14
+ return (
15
+ <Center height="100%" background="white" {...props}>
16
+ <Box width={width} maxWidth={maxWidth}>
17
+ <ClientOnly>
18
+ {() => <Lottie animationData={fullScreenLoaderBlackData} />}
19
+ </ClientOnly>
20
+ </Box>
21
+ </Center>
22
+ );
23
+ };
@@ -0,0 +1,25 @@
1
+ import { Box, BoxProps, Center } from "@chakra-ui/react";
2
+ import { inlineLoaderLightData } from "@vygruppen/spor-loader";
3
+ import React from "react";
4
+ import { ClientOnly } from "./ClientOnly";
5
+ import Lottie from "./Lottie";
6
+
7
+ export type LightInlineLoaderProps = Exclude<BoxProps, "children">;
8
+ /**
9
+ * Loading component that works well in bounded contexts, like inside a button.
10
+ */
11
+ export const LightInlineLoader = ({
12
+ width,
13
+ maxWidth,
14
+ ...props
15
+ }: LightInlineLoaderProps) => {
16
+ return (
17
+ <Center {...props}>
18
+ <Box width={width} maxWidth={maxWidth}>
19
+ <ClientOnly>
20
+ {() => <Lottie animationData={inlineLoaderLightData} />}
21
+ </ClientOnly>
22
+ </Box>
23
+ </Center>
24
+ );
25
+ };
@@ -0,0 +1,41 @@
1
+ import { Box, BoxProps, Center } from "@chakra-ui/react";
2
+ import { spinnerLightData } from "@vygruppen/spor-loader";
3
+ import React from "react";
4
+ import { ClientOnly } from "./ClientOnly";
5
+ import Lottie from "./Lottie";
6
+
7
+ export type LightSpinnerProps = BoxProps;
8
+ /** A circular spinner
9
+ *
10
+ * Can be used in place of a loading animation, or for reloading app state, for instance.
11
+ *
12
+ * ```tsx
13
+ * <LightSpinner width="64px" height="64px" />
14
+ * ```
15
+ *
16
+ * You can also pass an explanatory text as `children`:
17
+ *
18
+ * ```tsx
19
+ * <LightSpinner>
20
+ * Hold your horses
21
+ * </LightSpinner>
22
+ */
23
+ export const LightSpinner = ({
24
+ children,
25
+ width,
26
+ maxWidth,
27
+ ...props
28
+ }: LightSpinnerProps) => {
29
+ return (
30
+ <Center flexDirection="column" {...props}>
31
+ <Box width={width} maxWidth={maxWidth}>
32
+ <ClientOnly>{() => <Lottie animationData={spinnerLightData} />}</ClientOnly>
33
+ </Box>
34
+ {children && (
35
+ <Box mt={3} fontWeight="bold">
36
+ {children}
37
+ </Box>
38
+ )}
39
+ </Center>
40
+ );
41
+ };
@@ -0,0 +1,10 @@
1
+ import { useLottie } from "lottie-react";
2
+ import React from "react";
3
+
4
+ /**
5
+ * A wrapper around Lottie to make it tree-shakeable for SSR.
6
+ */
7
+ export default function Lottie({ animationData }: { animationData: any }) {
8
+ const { View } = useLottie({ animationData, loop: true });
9
+ return <>{View}</>;
10
+ }
@@ -0,0 +1,128 @@
1
+ import { Box, BoxProps, Flex, Text } from "@chakra-ui/react";
2
+ import React from "react";
3
+ import { useProgressBar } from "react-aria";
4
+ import { createTexts, useTranslation } from "..";
5
+ import { useRotatingLabel } from "./useRotatingLabel";
6
+
7
+ type ProgressBarProps = BoxProps & {
8
+ /** The percentage of progress made.
9
+ *
10
+ * The value must be between 0 and 100 */
11
+ value: number;
12
+ /** The height of the progress bar.
13
+ * Defaults to .5rem
14
+ **/
15
+ height?: BoxProps["height"];
16
+ /** The width of the progress bar.
17
+ *
18
+ * Defaults to the width of its container
19
+ **/
20
+ width?: BoxProps["width"];
21
+
22
+ /** Pass if no label is passed to the label */
23
+ "aria-label": string;
24
+ /** Optional text shown below the loader.
25
+ *
26
+ * If you pass an array of strings, the text will rotate every 5 seconds. If you want to change the delay, pass the delay in milliseconds to the `labelRotationDelay` prop.
27
+ */
28
+ label: string | string[];
29
+ /** The number of milliseconds a label is shown, if an array of strings is passed to the `label` prop.
30
+ *
31
+ * Defaults to 5000 (5 seconds).
32
+ */
33
+ labelRotationDelay?: number;
34
+ };
35
+
36
+ /**
37
+ * Shows the progress of a loading process.
38
+ *
39
+ * You can pass the amount of progress with the `value` prop:
40
+ *
41
+ * ```tsx
42
+ * <ProgressBar value={50} />
43
+ * ```
44
+ *
45
+ * You can also pass a label to show below the loader:
46
+ *
47
+ * ```tsx
48
+ * <ProgressBar value={50} label="Loading..." />
49
+ * ```
50
+ *
51
+ * If you pass an array of strings, the text will rotate every 5 seconds. If you want to change the delay, pass the delay in milliseconds to the `labelRotationDelay` prop.
52
+ *
53
+ * ```tsx
54
+ * <ProgressBar value={50} label={["Loading...", "Almost there..."]} />
55
+ * ```
56
+ *
57
+ * If you don't pass a label, you should pass an `aria-label` prop:
58
+ *
59
+ * ```tsx
60
+ * <ProgressBar value={50} aria-label="Loading..." />
61
+ * ```
62
+ */
63
+ export const ProgressBar = ({
64
+ value,
65
+ label,
66
+ labelRotationDelay = 5000,
67
+ height = "0.5rem",
68
+ width = "100%",
69
+ "aria-label": ariaLabel,
70
+ ...rest
71
+ }: ProgressBarProps) => {
72
+ const { t } = useTranslation();
73
+ const currentLoadingText = useRotatingLabel({
74
+ label,
75
+ delay: labelRotationDelay,
76
+ });
77
+ const { labelProps, progressBarProps } = useProgressBar({
78
+ isIndeterminate: value === undefined,
79
+ value,
80
+ "aria-label": ariaLabel || t(texts.label(value)),
81
+ });
82
+ return (
83
+ <>
84
+ <Box
85
+ {...progressBarProps}
86
+ title={t(texts.label(value))}
87
+ minWidth="100px"
88
+ {...rest}
89
+ >
90
+ <Flex
91
+ backgroundColor="coralGreen"
92
+ borderRadius="sm"
93
+ width={width}
94
+ justifyContent="flex-start"
95
+ marginX="auto"
96
+ >
97
+ <Box
98
+ backgroundColor="greenHaze"
99
+ borderRadius="sm"
100
+ height={height}
101
+ width={`${value}%`}
102
+ maxWidth="100%"
103
+ transition="width .2s ease-out"
104
+ />
105
+ </Flex>
106
+ {currentLoadingText && (
107
+ <Text
108
+ textAlign="center"
109
+ marginTop={2}
110
+ fontWeight="bold"
111
+ {...labelProps}
112
+ >
113
+ {currentLoadingText}
114
+ </Text>
115
+ )}
116
+ </Box>
117
+ </>
118
+ );
119
+ };
120
+
121
+ const texts = createTexts({
122
+ label: (value) => ({
123
+ nb: `${value}% ferdig`,
124
+ nn: `${value}% ferdig`,
125
+ sv: `${value}% klart`,
126
+ en: `${value}% done`,
127
+ }),
128
+ });
@@ -0,0 +1,140 @@
1
+ import { Box, BoxProps, Text } from "@chakra-ui/react";
2
+ import React, { useId, useRef } from "react";
3
+ import { useProgressBar } from "react-aria";
4
+ import { createTexts, useTranslation } from "..";
5
+ import { useRotatingLabel } from "./useRotatingLabel";
6
+
7
+ type ProgressLoaderProps = BoxProps & {
8
+ /** The percentage of progress made.
9
+ *
10
+ * The value must be between 0 and 100 */
11
+ value: number;
12
+ /** The width of the progress bar.
13
+ *
14
+ * Defaults to the width of its container
15
+ **/
16
+ width?: BoxProps["width"];
17
+
18
+ /** Pass if no label is passed to the label */
19
+ "aria-label": string;
20
+ /** Optional text shown below the loader.
21
+ *
22
+ * If you pass an array of strings, the text will rotate every 5 seconds. If you want to change the delay, pass the delay in milliseconds to the `labelRotationDelay` prop.
23
+ */
24
+ label: string | string[];
25
+ /** The number of milliseconds a label is shown, if an array of strings is passed to the `label` prop.
26
+ *
27
+ * Defaults to 5000 (5 seconds).
28
+ */
29
+ labelRotationDelay?: number;
30
+ };
31
+
32
+ /**
33
+ * Shows the progress of a loading process.
34
+ *
35
+ * You can pass the amount of progress with the `value` prop:
36
+ *
37
+ * ```tsx
38
+ * <ProgressLoader value={50} />
39
+ * ```
40
+ *
41
+ * You can also pass a label to show below the loader:
42
+ *
43
+ * ```tsx
44
+ * <ProgressLoader value={50} label="Loading..." />
45
+ * ```
46
+ *
47
+ * If you pass an array of strings, the text will rotate every 5 seconds. If you want to change the delay, pass the delay in milliseconds to the `labelRotationDelay` prop.
48
+ *
49
+ * ```tsx
50
+ * <ProgressLoader value={50} label={["Loading...", "Almost done..."]} />
51
+ * ```
52
+ *
53
+ * If you don't pass a label, you should pass an `aria-label` prop:
54
+ *
55
+ * ```tsx
56
+ * <ProgressLoader value={50} aria-label="Fetching your trips..." />
57
+ * ```
58
+ */
59
+ export const ProgressLoader = ({
60
+ value,
61
+ label,
62
+ labelRotationDelay = 5000,
63
+ "aria-label": ariaLabel,
64
+ width,
65
+ ...rest
66
+ }: ProgressLoaderProps) => {
67
+ const { t } = useTranslation();
68
+ const currentLoadingText = useRotatingLabel({
69
+ label,
70
+ delay: labelRotationDelay,
71
+ });
72
+ const { labelProps, progressBarProps } = useProgressBar({
73
+ isIndeterminate: value === undefined,
74
+ value,
75
+ "aria-label": ariaLabel ?? t(texts.fallbackLabel(value ?? "?")),
76
+ });
77
+ const pathRef = useRef<SVGPathElement>(null);
78
+ const progressPathLength = pathRef.current?.getTotalLength() ?? 0;
79
+ const progress = ((value - 100) / 100) * progressPathLength;
80
+ const id = useId();
81
+ return (
82
+ <Box {...progressBarProps} minWidth="100px" width={width} {...rest}>
83
+ <Box as="svg" viewBox="0 0 246 78" fill="none">
84
+ <Box
85
+ as="path"
86
+ id={`${id}-start-dot`}
87
+ d="M14.0479 44.8251C19.4332 44.8251 23.7988 40.5242 23.7988 35.2187C23.7988 29.9133 19.4332 25.6124 14.0479 25.6124C8.66254 25.6124 4.29688 29.9133 4.29688 35.2187C4.29688 40.5242 8.66254 44.8251 14.0479 44.8251Z"
88
+ fill="#FFB466"
89
+ />
90
+ <Box
91
+ as="path"
92
+ id={`${id}-track`}
93
+ d="M204.911 39.1156C204.911 39.1156 175.012 46.8319 157.651 30.4354C140.29 14.0388 121 21.7547 110.391 47.6529C103.22 65.157 78.9634 67.0859 67.9533 47.6529C59.8376 33.3287 36.125 37.1866 36.125 37.1866"
94
+ stroke="coralGreen"
95
+ strokeWidth="13.6469"
96
+ strokeLinecap="round"
97
+ strokeLinejoin="round"
98
+ />
99
+ <Box
100
+ as="path"
101
+ id={`${id}-progress`}
102
+ d="M204.911 39.1156C204.911 39.1156 175.012 46.8319 157.651 30.4354C140.29 14.0388 121 21.7547 110.391 47.6529C103.22 65.157 78.9634 67.0859 67.9533 47.6529C59.8376 33.3287 36.125 37.1866 36.125 37.1866"
103
+ stroke="greenHaze"
104
+ strokeWidth="13.6469"
105
+ strokeLinecap="round"
106
+ strokeLinejoin="round"
107
+ strokeDasharray={progressPathLength}
108
+ strokeDashoffset={progress}
109
+ transition="stroke-dashoffset .2s ease-out"
110
+ ref={pathRef as any}
111
+ />
112
+ <Box
113
+ as="path"
114
+ id={`${id}-end-dot`}
115
+ d="M226.025 44.8251C231.411 44.8251 235.776 40.5242 235.776 35.2187C235.776 29.9133 231.411 25.6124 226.025 25.6124C220.64 25.6124 216.274 29.9133 216.274 35.2187C216.274 40.5242 220.64 44.8251 226.025 44.8251Z"
116
+ fill="#688CBA"
117
+ />
118
+ </Box>
119
+ {currentLoadingText && (
120
+ <Text
121
+ textAlign="center"
122
+ marginTop={2}
123
+ fontWeight="bold"
124
+ {...labelProps}
125
+ >
126
+ {currentLoadingText}
127
+ </Text>
128
+ )}
129
+ </Box>
130
+ );
131
+ };
132
+
133
+ const texts = createTexts({
134
+ fallbackLabel: (value) => ({
135
+ nb: `${value}% ferdig`,
136
+ nn: `${value}% ferdig`,
137
+ sv: `${value}% klart`,
138
+ en: `${value}% done`,
139
+ }),
140
+ });
@@ -0,0 +1,16 @@
1
+ import {
2
+ BoxProps,
3
+ forwardRef,
4
+ Skeleton as ChakraSkeleton,
5
+ } from "@chakra-ui/react";
6
+ import React from "react";
7
+
8
+ export type SkeletonProps = BoxProps & {
9
+ isLoaded?: boolean;
10
+ };
11
+ /**
12
+ * Skeleton renders a loading animation for a given box. It works great as a placeholder to avoid layout shifts.
13
+ */
14
+ export const Skeleton = forwardRef<SkeletonProps, "div">((props, ref) => (
15
+ <ChakraSkeleton {...props} ref={ref} />
16
+ ));
@@ -0,0 +1,13 @@
1
+ import {
2
+ BoxProps,
3
+ SkeletonCircle as ChakraSkeletonCircle,
4
+ } from "@chakra-ui/react";
5
+ import React from "react";
6
+
7
+ export type SkeletonCircleProps = BoxProps;
8
+ /**
9
+ * SkeletonCircle renders a loading animation for a given circle. It works great as a placeholder to avoid layout shifts.
10
+ */
11
+ export const SkeletonCircle = (props: SkeletonCircleProps) => (
12
+ <ChakraSkeletonCircle boxSize={6} borderRadius="50%" {...props} />
13
+ );
@@ -0,0 +1,10 @@
1
+ import { BoxProps, SkeletonText as ChakraSkeletonText } from "@chakra-ui/react";
2
+ import React from "react";
3
+
4
+ export type SkeletonTextProps = BoxProps;
5
+ /**
6
+ * SkeletonText renders a loading animation for a given text. It works great as a placeholder to avoid layout shifts.
7
+ */
8
+ export const SkeletonText = (props: SkeletonTextProps) => (
9
+ <ChakraSkeletonText boxSize={6} {...props} />
10
+ );