@vygruppen/spor-react 1.3.3 → 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 (197) hide show
  1. package/.turbo/turbo-build.log +12 -10
  2. package/CHANGELOG.md +40 -0
  3. package/README.md +1 -1
  4. package/dist/index.d.ts +6718 -27
  5. package/dist/index.js +14139 -140
  6. package/dist/index.mjs +13818 -27
  7. package/package.json +19 -31
  8. package/src/accordion/Accordion.test.tsx +20 -0
  9. package/src/accordion/Accordion.tsx +62 -0
  10. package/src/accordion/AccordionContext.tsx +27 -0
  11. package/src/accordion/Expandable.tsx +157 -0
  12. package/src/accordion/index.tsx +2 -0
  13. package/src/alert/AlertIcon.tsx +75 -0
  14. package/src/alert/BaseAlert.test.tsx +37 -0
  15. package/src/alert/BaseAlert.tsx +21 -0
  16. package/src/alert/ClosableAlert.test.tsx +37 -0
  17. package/src/alert/ClosableAlert.tsx +75 -0
  18. package/src/alert/ExpandableAlert.test.tsx +84 -0
  19. package/src/alert/ExpandableAlert.tsx +84 -0
  20. package/src/alert/StaticAlert.tsx +25 -0
  21. package/src/alert/index.tsx +3 -0
  22. package/src/button/Button.test.tsx +23 -0
  23. package/src/button/Button.tsx +162 -0
  24. package/src/button/ButtonGroup.tsx +43 -0
  25. package/src/button/CloseButton.tsx +63 -0
  26. package/src/button/FloatingActionButton.tsx +113 -0
  27. package/src/button/IconButton.tsx +63 -0
  28. package/src/button/index.tsx +5 -0
  29. package/src/card/Card.tsx +59 -0
  30. package/src/card/index.tsx +1 -0
  31. package/src/datepicker/Calendar.tsx +32 -0
  32. package/src/datepicker/CalendarCell.tsx +74 -0
  33. package/src/datepicker/CalendarGrid.tsx +76 -0
  34. package/src/datepicker/CalendarHeader.tsx +153 -0
  35. package/src/datepicker/CalendarNavigationButton.tsx +26 -0
  36. package/src/datepicker/CalendarTriggerButton.tsx +36 -0
  37. package/src/datepicker/DateField.tsx +51 -0
  38. package/src/datepicker/DatePicker.tsx +153 -0
  39. package/src/datepicker/DateRangePicker.tsx +165 -0
  40. package/src/datepicker/DateTimeSegment.tsx +56 -0
  41. package/src/datepicker/RangeCalendar.tsx +35 -0
  42. package/src/datepicker/StyledField.tsx +31 -0
  43. package/src/datepicker/TimeField.tsx +46 -0
  44. package/src/datepicker/TimePicker.test.tsx +74 -0
  45. package/src/datepicker/TimePicker.tsx +196 -0
  46. package/src/datepicker/index.tsx +4 -0
  47. package/src/datepicker/utils.ts +33 -0
  48. package/src/i18n/index.tsx +38 -0
  49. package/src/image/index.tsx +2 -0
  50. package/src/index.tsx +25 -26
  51. package/src/input/CardSelect.tsx +165 -0
  52. package/src/input/Checkbox.tsx +24 -0
  53. package/src/input/CheckboxGroup.tsx +43 -0
  54. package/src/input/ChoiceChip.tsx +102 -0
  55. package/src/input/Dialog.tsx +29 -0
  56. package/src/input/FormControl.tsx +11 -0
  57. package/src/input/FormErrorMessage.tsx +91 -0
  58. package/src/input/FormLabel.tsx +11 -0
  59. package/src/input/InfoSelect.tsx +209 -0
  60. package/src/input/Input.tsx +59 -0
  61. package/src/input/InputElement.tsx +45 -0
  62. package/src/input/ListBox.tsx +123 -0
  63. package/src/input/NativeSelect.tsx +38 -0
  64. package/src/input/PasswordInput.tsx +70 -0
  65. package/src/input/Popover.tsx +70 -0
  66. package/src/input/Radio.tsx +34 -0
  67. package/src/input/RadioGroup.tsx +47 -0
  68. package/src/input/SearchInput.tsx +89 -0
  69. package/src/input/Switch.tsx +40 -0
  70. package/src/input/Textarea.tsx +98 -0
  71. package/src/input/index.tsx +20 -0
  72. package/src/layout/Divider.tsx +26 -0
  73. package/src/layout/Stack.tsx +42 -0
  74. package/src/layout/index.tsx +28 -0
  75. package/src/linjetag/InfoTag.tsx +54 -0
  76. package/src/linjetag/LineIcon.tsx +44 -0
  77. package/src/linjetag/TravelTag.tsx +121 -0
  78. package/src/linjetag/icons.tsx +80 -0
  79. package/src/linjetag/index.tsx +3 -0
  80. package/src/linjetag/types.d.ts +24 -0
  81. package/src/link/TextLink.tsx +45 -0
  82. package/src/link/index.tsx +1 -0
  83. package/src/loader/ClientOnly.tsx +29 -0
  84. package/src/loader/ColorInlineLoader.tsx +27 -0
  85. package/src/loader/ColorSpinner.tsx +44 -0
  86. package/src/loader/ContentLoader.tsx +27 -0
  87. package/src/loader/DarkFullScreenLoader.tsx +23 -0
  88. package/src/loader/DarkInlineLoader.tsx +25 -0
  89. package/src/loader/DarkSpinner.tsx +43 -0
  90. package/src/loader/LightFullScreenLoader.tsx +23 -0
  91. package/src/loader/LightInlineLoader.tsx +25 -0
  92. package/src/loader/LightSpinner.tsx +41 -0
  93. package/src/loader/Lottie.tsx +10 -0
  94. package/src/loader/ProgressBar.tsx +128 -0
  95. package/src/loader/ProgressLoader.tsx +140 -0
  96. package/src/loader/Skeleton.tsx +16 -0
  97. package/src/loader/SkeletonCircle.tsx +13 -0
  98. package/src/loader/SkeletonText.tsx +10 -0
  99. package/src/loader/index.tsx +14 -0
  100. package/src/loader/useHydrated.tsx +34 -0
  101. package/src/loader/useRotatingLabel.tsx +22 -0
  102. package/src/logo/VyLogo.tsx +101 -0
  103. package/src/logo/index.tsx +1 -0
  104. package/src/media-controller/JumpButton.tsx +69 -0
  105. package/src/media-controller/PlayPauseButton.tsx +67 -0
  106. package/src/media-controller/SkipButton.tsx +66 -0
  107. package/src/media-controller/icons.tsx +80 -0
  108. package/src/media-controller/index.test.tsx +59 -0
  109. package/src/media-controller/index.tsx +3 -0
  110. package/src/modal/Drawer.tsx +122 -0
  111. package/src/modal/Modal.tsx +15 -0
  112. package/src/modal/ModalHeader.tsx +31 -0
  113. package/src/modal/SimpleDrawer.tsx +44 -0
  114. package/src/modal/index.tsx +4 -0
  115. package/src/popover/PopoverWizardBody.tsx +91 -0
  116. package/src/popover/SimplePopover.tsx +75 -0
  117. package/src/popover/WizardPopover.tsx +61 -0
  118. package/src/popover/index.tsx +23 -0
  119. package/src/provider/SporProvider.tsx +67 -0
  120. package/src/provider/index.tsx +1 -0
  121. package/src/stepper/Stepper.tsx +115 -0
  122. package/src/stepper/StepperContext.tsx +55 -0
  123. package/src/stepper/StepperStep.tsx +48 -0
  124. package/src/stepper/index.tsx +2 -0
  125. package/src/tab/Tabs.tsx +20 -0
  126. package/src/tab/index.tsx +9 -0
  127. package/src/table/Table.tsx +58 -0
  128. package/src/table/index.tsx +19 -0
  129. package/src/theme/components/accordion.ts +143 -0
  130. package/src/theme/components/alert.ts +59 -0
  131. package/src/theme/components/badge.ts +109 -0
  132. package/src/theme/components/button.ts +217 -0
  133. package/src/theme/components/card-select.ts +158 -0
  134. package/src/theme/components/card.ts +174 -0
  135. package/src/theme/components/checkbox.ts +90 -0
  136. package/src/theme/components/choice-chip.ts +79 -0
  137. package/src/theme/components/close-button.ts +56 -0
  138. package/src/theme/components/code.ts +17 -0
  139. package/src/theme/components/datepicker.ts +194 -0
  140. package/src/theme/components/drawer.ts +92 -0
  141. package/src/theme/components/fab.ts +111 -0
  142. package/src/theme/components/form-label.ts +17 -0
  143. package/src/theme/components/form.ts +27 -0
  144. package/src/theme/components/index.ts +34 -0
  145. package/src/theme/components/info-select.ts +91 -0
  146. package/src/theme/components/info-tag.ts +49 -0
  147. package/src/theme/components/input.ts +97 -0
  148. package/src/theme/components/line-icon.ts +121 -0
  149. package/src/theme/components/link.ts +155 -0
  150. package/src/theme/components/listbox.ts +52 -0
  151. package/src/theme/components/media-controller-button.ts +134 -0
  152. package/src/theme/components/modal.ts +93 -0
  153. package/src/theme/components/popover.ts +63 -0
  154. package/src/theme/components/radio.ts +64 -0
  155. package/src/theme/components/select.ts +52 -0
  156. package/src/theme/components/skeleton.ts +40 -0
  157. package/src/theme/components/stepper.ts +230 -0
  158. package/src/theme/components/switch.ts +227 -0
  159. package/src/theme/components/table.ts +163 -0
  160. package/src/theme/components/tabs.ts +282 -0
  161. package/src/theme/components/textarea.ts +14 -0
  162. package/src/theme/components/toast.ts +28 -0
  163. package/src/theme/components/travel-tag.ts +267 -0
  164. package/src/theme/font-faces.ts +66 -0
  165. package/src/theme/foundations/borders.ts +11 -0
  166. package/src/theme/foundations/breakpoints.ts +9 -0
  167. package/src/theme/foundations/colors.ts +10 -0
  168. package/src/theme/foundations/config.ts +5 -0
  169. package/src/theme/foundations/fontSizes.ts +29 -0
  170. package/src/theme/foundations/fontWeights.ts +5 -0
  171. package/src/theme/foundations/fonts.ts +7 -0
  172. package/src/theme/foundations/index.ts +14 -0
  173. package/src/theme/foundations/lineHeights.ts +5 -0
  174. package/src/theme/foundations/radii.ts +12 -0
  175. package/src/theme/foundations/shadows.ts +8 -0
  176. package/src/theme/foundations/sizes.ts +34 -0
  177. package/src/theme/foundations/spacing.ts +30 -0
  178. package/src/theme/foundations/textStyles.ts +60 -0
  179. package/src/theme/foundations/zIndices.ts +17 -0
  180. package/src/theme/index.ts +14 -0
  181. package/src/theme/utils/box-shadow-utils.ts +44 -0
  182. package/src/theme/utils/focus-utils.ts +16 -0
  183. package/src/toast/ActionToast.test.tsx +22 -0
  184. package/src/toast/ActionToast.tsx +28 -0
  185. package/src/toast/BaseToast.test.tsx +27 -0
  186. package/src/toast/BaseToast.tsx +75 -0
  187. package/src/toast/ClosableToast.test.tsx +17 -0
  188. package/src/toast/ClosableToast.tsx +40 -0
  189. package/src/toast/index.tsx +1 -0
  190. package/src/toast/useToast.tsx +99 -0
  191. package/src/typography/Badge.tsx +68 -0
  192. package/src/typography/Code.tsx +32 -0
  193. package/src/typography/Heading.tsx +32 -0
  194. package/src/typography/Text.tsx +26 -0
  195. package/src/typography/index.tsx +4 -0
  196. package/src/util/externals.tsx +23 -0
  197. package/src/util/index.tsx +1 -0
@@ -0,0 +1,89 @@
1
+ import {
2
+ Input as ChakraInput,
3
+ InputProps as ChakraInputProps,
4
+ IconButton,
5
+ forwardRef,
6
+ useFormControlContext,
7
+ } from "@chakra-ui/react";
8
+ import {
9
+ CloseOutline24Icon,
10
+ SearchOutline24Icon,
11
+ } from "@vygruppen/spor-icon-react";
12
+ import React, { useId } from "react";
13
+ import { FormLabel, InputGroup, InputLeftElement, InputRightElement } from ".";
14
+ import { createTexts, useTranslation } from "..";
15
+
16
+ export type SearchInputProps = Exclude<
17
+ ChakraInputProps,
18
+ "variant" | "size" | "leftIcon" | "rightIcon"
19
+ > & {
20
+ /** Optional label. Defaults to the localized version of "search" */
21
+ label?: string;
22
+ /** Callback for when the clear button is clicked */
23
+ onReset?: () => void;
24
+ };
25
+ /** Simple search input component.
26
+ *
27
+ * Includes a search icon, a localized label and a reset button.
28
+ */
29
+ export const SearchInput = forwardRef<SearchInputProps, "input">(
30
+ ({ label, onReset, ...props }, ref) => {
31
+ const { t } = useTranslation();
32
+ const showClearButton = onReset && Boolean(props.value);
33
+ const formControlProps = useFormControlContext();
34
+ const autoGeneratedId = useId();
35
+ const inputId = props.id ?? formControlProps?.id ?? autoGeneratedId;
36
+ return (
37
+ <InputGroup position="relative">
38
+ <InputLeftElement>
39
+ <SearchOutline24Icon />
40
+ </InputLeftElement>
41
+ <ChakraInput
42
+ pl={7}
43
+ pr={7}
44
+ {...props}
45
+ id={inputId}
46
+ type="search"
47
+ css={{
48
+ "&::-webkit-search-cancel-button": {
49
+ WebkitAppearance: "none",
50
+ },
51
+ }}
52
+ ref={ref}
53
+ placeholder=" " // This is needed to make the label work as expected
54
+ />
55
+ <FormLabel htmlFor={inputId} pointerEvents="none">
56
+ {label ?? t(texts.label)}
57
+ </FormLabel>
58
+ {showClearButton && (
59
+ <InputRightElement width="fit-content">
60
+ <IconButton
61
+ variant="ghost"
62
+ type="button"
63
+ size="sm"
64
+ mr={1}
65
+ aria-label={t(texts.reset)}
66
+ icon={<CloseOutline24Icon />}
67
+ onClick={onReset}
68
+ />
69
+ </InputRightElement>
70
+ )}
71
+ </InputGroup>
72
+ );
73
+ }
74
+ );
75
+
76
+ const texts = createTexts({
77
+ label: {
78
+ nb: "Søk",
79
+ nn: "Søk",
80
+ sv: "Sök",
81
+ en: "Search",
82
+ },
83
+ reset: {
84
+ nb: "Tøm søkefeltet",
85
+ nn: "Tøm søkefelt",
86
+ sv: "Rensa sökrutan",
87
+ en: "Reset search field",
88
+ },
89
+ });
@@ -0,0 +1,40 @@
1
+ import {
2
+ Switch as ChakraSwitch,
3
+ SwitchProps as ChakraSwitchProps,
4
+ forwardRef,
5
+ } from "@chakra-ui/react";
6
+ import React from "react";
7
+
8
+ export type SwitchProps = Exclude<
9
+ ChakraSwitchProps,
10
+ "colorScheme" | "variant"
11
+ > & {
12
+ size?: "sm" | "md" | "lg";
13
+ };
14
+
15
+ /**
16
+ * A switch lets you toggle between on and off, yes and no. It's an alternative to a checkbox.
17
+ *
18
+ * You can use a Switch component inside of a `FormControl` with an associated `FormLabel`:
19
+ *
20
+ * ```tsx
21
+ * <FormControl>
22
+ * <FormLabel>Enable alerts?</FormLabel>
23
+ * <Switch />
24
+ * </FormControl>
25
+ * ```
26
+ *
27
+ * Switches are available in three different sizes - `sm`, `md` and `lg`. There are also two variants - `solid` and `outline`.
28
+ *
29
+ * ```tsx
30
+ * <FormControl>
31
+ * <FormLabel>Enable alerts?</FormLabel>
32
+ * <Switch variant="outline" size="lg" />
33
+ * </FormControl>
34
+ * ```
35
+ */
36
+ export const Switch = forwardRef<SwitchProps, "input">(
37
+ ({ size = "md", ...props }: SwitchProps, ref) => {
38
+ return <ChakraSwitch variant="solid" size={size} {...props} ref={ref} />;
39
+ }
40
+ );
@@ -0,0 +1,98 @@
1
+ import {
2
+ Box,
3
+ FormLabel,
4
+ forwardRef,
5
+ Textarea as ChakraTextarea,
6
+ TextareaProps as ChakraTextareaProps,
7
+ useFormControlContext,
8
+ } from "@chakra-ui/react";
9
+ import React, { useId } from "react";
10
+
11
+ export type TextareaProps = Exclude<ChakraTextareaProps, "variant" | "size"> & {
12
+ label: string;
13
+ };
14
+ /**
15
+ * Text area that works with the `FormControl` component.
16
+ *
17
+ * Note that it requires you to pass a label.
18
+ *
19
+ * ```tsx
20
+ * <FormControl>
21
+ * <Textarea label="E-mail" />
22
+ * </FormControl>
23
+ * ```
24
+ */
25
+ export const Textarea = forwardRef<TextareaProps, "textarea">((props, ref) => {
26
+ const {
27
+ spacingProps,
28
+ remainingProps: { label, ...rest },
29
+ } = getSpacingProps(props);
30
+ const formControlProps = useFormControlContext();
31
+ const fallbackId = `textarea-${useId()}`;
32
+ const inputId = props.id ?? formControlProps?.id ?? fallbackId;
33
+ return (
34
+ <Box {...spacingProps}>
35
+ <FormLabel htmlFor={inputId}>{label}</FormLabel>
36
+ <ChakraTextarea {...rest} id={inputId} ref={ref} />
37
+ </Box>
38
+ );
39
+ });
40
+
41
+ function getSpacingProps<T extends TextareaProps>(props: T) {
42
+ const {
43
+ mt,
44
+ mr,
45
+ mb,
46
+ ml,
47
+ mx,
48
+ my,
49
+ marginTop,
50
+ marginRight,
51
+ marginBottom,
52
+ marginLeft,
53
+ marginX,
54
+ marginY,
55
+ pt,
56
+ pr,
57
+ pb,
58
+ pl,
59
+ px,
60
+ py,
61
+ paddingTop,
62
+ paddingRight,
63
+ paddingBottom,
64
+ paddingLeft,
65
+ paddingX,
66
+ paddingY,
67
+ ...remainingProps
68
+ } = props;
69
+ return {
70
+ spacingProps: {
71
+ mt,
72
+ mr,
73
+ mb,
74
+ ml,
75
+ mx,
76
+ my,
77
+ marginTop,
78
+ marginRight,
79
+ marginBottom,
80
+ marginLeft,
81
+ marginX,
82
+ marginY,
83
+ pt,
84
+ pr,
85
+ pb,
86
+ pl,
87
+ px,
88
+ py,
89
+ paddingTop,
90
+ paddingRight,
91
+ paddingBottom,
92
+ paddingLeft,
93
+ paddingX,
94
+ paddingY,
95
+ },
96
+ remainingProps,
97
+ };
98
+ }
@@ -0,0 +1,20 @@
1
+ export { FormHelperText, InputGroup } from "@chakra-ui/react";
2
+ export type { InputGroupProps } from "@chakra-ui/react";
3
+ export * from "./CardSelect";
4
+ export * from "./Checkbox";
5
+ export * from "./CheckboxGroup";
6
+ export * from "./ChoiceChip";
7
+ export * from "./FormControl";
8
+ export * from "./FormErrorMessage";
9
+ export * from "./FormLabel";
10
+ export * from "./InfoSelect";
11
+ export * from "./Input";
12
+ export * from "./InputElement";
13
+ export * from "./ListBox";
14
+ export * from "./NativeSelect";
15
+ export * from "./PasswordInput";
16
+ export * from "./Radio";
17
+ export * from "./RadioGroup";
18
+ export * from "./SearchInput";
19
+ export * from "./Switch";
20
+ export * from "./Textarea";
@@ -0,0 +1,26 @@
1
+ import { As, Box, BoxProps, forwardRef } from "@chakra-ui/react";
2
+ import React from "react";
3
+
4
+ export type DividerProps = BoxProps;
5
+ /** A dividing line, used to divide content.
6
+ *
7
+ * You can specify margins if you need to give the content some space, or use a `Stack` component to do it for you
8
+ *
9
+ * ```tsx
10
+ * <Divider mt={4} mb={6} />
11
+ * ```
12
+ */
13
+ export const Divider = forwardRef<BoxProps, As>((props, ref) => {
14
+ return (
15
+ <Box
16
+ as="hr"
17
+ height="2px"
18
+ border="0"
19
+ borderRadius="1px"
20
+ backgroundColor="blackAlpha.200"
21
+ width="100%"
22
+ {...props}
23
+ ref={ref}
24
+ />
25
+ );
26
+ });
@@ -0,0 +1,42 @@
1
+ import {
2
+ Stack as ChakraStack,
3
+ StackProps as ChakraStackProps,
4
+ forwardRef,
5
+ } from "@chakra-ui/react";
6
+ import React from "react";
7
+
8
+ export type StackProps = Exclude<ChakraStackProps, "direction"> & {
9
+ flexDirection?: ChakraStackProps["direction"];
10
+ };
11
+ /**
12
+ * Adds consistent spacing between elements
13
+ *
14
+ * ```tsx
15
+ * <Stack>
16
+ * <Text>Here's a paragraph</Text>
17
+ * <Text>Here's another perfectly spaced paragraph</Text>
18
+ * </Stack>
19
+ * ```
20
+ *
21
+ * By default, the stack will be a column. You can change this by setting the `flexDirection` prop to any valid flex direction:
22
+ * ```tsx
23
+ * <Stack flexDirection="row">
24
+ * <Checkbox>Here's a checkbox</Checkbox>
25
+ * <Checkbox>Here's another checkbox, almost right next to the first one</Checkbox>
26
+ * </Stack>
27
+ * ```
28
+ *
29
+ * You can specify the spacing between elements with the `spacing` prop:
30
+ *
31
+ * ```tsx
32
+ * <Stack spacing={4}>
33
+ * <Card>Here's one card</Card>
34
+ * <Card>Here's another card, with a lot of space between it</Card>
35
+ * </Stack>
36
+ * ```
37
+ */
38
+ export const Stack = forwardRef<StackProps, "div">(
39
+ ({ flexDirection, ...props }, ref) => {
40
+ return <ChakraStack {...props} direction={flexDirection} ref={ref} />;
41
+ }
42
+ );
@@ -0,0 +1,28 @@
1
+ export {
2
+ Box,
3
+ Center,
4
+ Container,
5
+ Flex,
6
+ Grid,
7
+ GridItem,
8
+ HStack,
9
+ SimpleGrid,
10
+ Spacer,
11
+ VStack,
12
+ Wrap,
13
+ WrapItem,
14
+ } from "@chakra-ui/react";
15
+ export type {
16
+ BoxProps,
17
+ CenterProps,
18
+ ContainerProps,
19
+ FlexProps,
20
+ GridItemProps,
21
+ GridProps,
22
+ SimpleGridProps,
23
+ SpacerProps,
24
+ WrapItemProps,
25
+ WrapProps,
26
+ } from "@chakra-ui/react";
27
+ export * from "./Divider";
28
+ export * from "./Stack";
@@ -0,0 +1,54 @@
1
+ import { Box, useMultiStyleConfig } from "@chakra-ui/react";
2
+ import React from "react";
3
+ import { LineIcon } from "./LineIcon";
4
+ import type { TagProps } from "./types";
5
+
6
+ export type InfoTagProps = TagProps;
7
+
8
+ /**
9
+ * An info tag component.
10
+ *
11
+ * Shows a line icon, a title and an optional description.
12
+ *
13
+ * ```tsx
14
+ * <InfoTag variant="subway" title="3" description="Ringen" />
15
+ * ```
16
+ * They support three different sizes – `sm`, `md` and `lg`.
17
+ *
18
+ * ```tsx
19
+ * <InfoTag
20
+ * variant="subway"
21
+ * size="lg"
22
+ * title="3"
23
+ * description="Ringen"
24
+ * />
25
+ * ```
26
+ *
27
+ * @see https://spor.vy.no/komponenter/linjetags
28
+ */
29
+ export const InfoTag = ({
30
+ variant,
31
+ size = "md",
32
+ title,
33
+ description,
34
+ }: InfoTagProps) => {
35
+ const styles = useMultiStyleConfig("InfoTag", { variant, size });
36
+ return (
37
+ <Box sx={styles.container}>
38
+ <LineIcon variant={variant} size={size} sx={styles.iconContainer} />
39
+ <Box sx={styles.textContainer}>
40
+ {title && (
41
+ <Box as="span" sx={styles.title}>
42
+ {title}
43
+ </Box>
44
+ )}
45
+ {title && description && " "}
46
+ {description && (
47
+ <Box as="span" sx={styles.description}>
48
+ {description}
49
+ </Box>
50
+ )}
51
+ </Box>
52
+ </Box>
53
+ );
54
+ };
@@ -0,0 +1,44 @@
1
+ import { Box, BoxProps, useMultiStyleConfig } from "@chakra-ui/react";
2
+ import React from "react";
3
+ import { getCorrectIcon } from "./icons";
4
+ import { TagProps } from "./types";
5
+
6
+ export type LineIconProps = BoxProps & {
7
+ variant: TagProps["variant"];
8
+ size: TagProps["size"];
9
+ };
10
+
11
+ /**
12
+ * A line icon component.
13
+ *
14
+ * Shows a line icon with the correct color scheme.
15
+ *
16
+ * ```tsx
17
+ * <LineIcon variant="subway" />
18
+ * ```
19
+ *
20
+ * They support three different sizes – `sm`, `md` and `lg`.
21
+ *
22
+ * ```tsx
23
+ * <LineIcon variant="subway" size="lg" />
24
+ * ```
25
+ *
26
+ * @see https://spor.vy.no/komponenter/linjetags
27
+ */
28
+ export const LineIcon = ({
29
+ variant,
30
+ size = "md",
31
+ sx,
32
+ ...rest
33
+ }: LineIconProps) => {
34
+ const styles = useMultiStyleConfig("LineIcon", { variant, size });
35
+ const Icon: any = getCorrectIcon({ variant, size });
36
+ if (!Icon) {
37
+ return null;
38
+ }
39
+ return (
40
+ <Box sx={{ ...styles.iconContainer, ...sx }} {...rest}>
41
+ <Icon sx={styles.icon} />
42
+ </Box>
43
+ );
44
+ };
@@ -0,0 +1,121 @@
1
+ import {
2
+ As,
3
+ Box,
4
+ BoxProps,
5
+ forwardRef,
6
+ useMultiStyleConfig,
7
+ } from "@chakra-ui/react";
8
+ import {
9
+ ErrorFill18Icon,
10
+ ErrorFill24Icon,
11
+ InformationFill18Icon,
12
+ InformationFill24Icon,
13
+ WarningFill18Icon,
14
+ WarningFill24Icon,
15
+ } from "@vygruppen/spor-icon-react";
16
+ import React from "react";
17
+ import { LineIcon } from "./LineIcon";
18
+ import type { TagProps } from "./types";
19
+
20
+ export type TravelTagProps = TagProps &
21
+ BoxProps & {
22
+ deviationLevel?: "critical" | "major" | "minor" | "info" | "none";
23
+ isDisabled?: boolean;
24
+ };
25
+
26
+ /**
27
+ * A travel tag component.
28
+ *
29
+ * Shows a line icon, a title and an optional description.
30
+ *
31
+ * ```tsx
32
+ * <TravelTag variant="subway" title="3" description="Ringen" />
33
+ * ```
34
+ *
35
+ * They support three different sizes – `sm`, `md` and `lg`.
36
+ *
37
+ * You can also render them with a deviation level to indicate an extra focus:
38
+ *
39
+ * ```tsx
40
+ * <TravelTag
41
+ * variant="subway"
42
+ * title="3"
43
+ * description="Ringen"
44
+ * size="lg"
45
+ * deviationLevel="critical"
46
+ * />
47
+ * ```
48
+ *
49
+ * Travel tags can be clickable by passing an `as="button"` prop. They can also be disabled.
50
+ *
51
+ * ```tsx
52
+ * <TravelTag
53
+ * as="button"
54
+ * onClick={handleClick}
55
+ * variant="subway"
56
+ * title="3"
57
+ * description="Ringen"
58
+ * />
59
+ * ```
60
+ *
61
+ * @see https://spor.vy.no/komponenter/linjetags
62
+ */
63
+ export const TravelTag = forwardRef<TravelTagProps, As>(
64
+ (
65
+ {
66
+ variant,
67
+ size = "md",
68
+ deviationLevel = "none",
69
+ title,
70
+ description,
71
+ isDisabled,
72
+ ...rest
73
+ },
74
+ ref
75
+ ) => {
76
+ const styles = useMultiStyleConfig("TravelTag", {
77
+ variant,
78
+ size,
79
+ deviationLevel,
80
+ });
81
+
82
+ const DeviationLevelIcon = getDeviationLevelIcon({ deviationLevel, size });
83
+
84
+ return (
85
+ <Box sx={styles.container} aria-disabled={isDisabled} ref={ref} {...rest}>
86
+ <LineIcon variant={variant} size={size} sx={styles.iconContainer} />
87
+ <Box sx={styles.textContainer}>
88
+ {title && (
89
+ <Box as="span" sx={styles.title}>
90
+ {title}
91
+ </Box>
92
+ )}
93
+ {title && description && " "}
94
+ {description && (
95
+ <Box as="span" sx={styles.description}>
96
+ {description}
97
+ </Box>
98
+ )}
99
+ </Box>
100
+ {DeviationLevelIcon && <DeviationLevelIcon sx={styles.deviationIcon} />}
101
+ </Box>
102
+ );
103
+ }
104
+ );
105
+
106
+ const getDeviationLevelIcon = ({
107
+ deviationLevel,
108
+ size,
109
+ }: Pick<TravelTagProps, "deviationLevel" | "size">) => {
110
+ switch (deviationLevel) {
111
+ case "critical":
112
+ return size === "lg" ? ErrorFill24Icon : ErrorFill18Icon;
113
+ case "major":
114
+ case "minor":
115
+ return size === "lg" ? WarningFill24Icon : WarningFill18Icon;
116
+ case "info":
117
+ return size === "lg" ? InformationFill24Icon : InformationFill18Icon;
118
+ default:
119
+ return null;
120
+ }
121
+ };
@@ -0,0 +1,80 @@
1
+ import { Box } from "@chakra-ui/react";
2
+ import {
3
+ AltTransportFill18Icon,
4
+ AltTransportFill24Icon,
5
+ AltTransportFill30Icon,
6
+ BusFill18Icon,
7
+ BusFill24Icon,
8
+ BusFill30Icon,
9
+ ExpressBusFill18Icon,
10
+ ExpressBusFill24Icon,
11
+ ExpressBusFill30Icon,
12
+ FerryFill18Icon,
13
+ FerryFill24Icon,
14
+ FerryFill30Icon,
15
+ SubwayFill18Icon,
16
+ SubwayFill24Icon,
17
+ SubwayFill30Icon,
18
+ TrainFill18Icon,
19
+ TrainFill24Icon,
20
+ TrainFill30Icon,
21
+ TramFill18Icon,
22
+ TramFill24Icon,
23
+ TramFill30Icon,
24
+ WalkFill18Icon,
25
+ WalkFill24Icon,
26
+ WalkFill30Icon,
27
+ } from "@vygruppen/spor-icon-react";
28
+ import React from "react";
29
+ import type { Size, Variant } from "./types";
30
+
31
+ const icons: Record<Variant, Record<Size, React.ComponentType>> = {
32
+ "local-train": {
33
+ sm: TrainFill18Icon,
34
+ md: TrainFill24Icon,
35
+ lg: TrainFill30Icon,
36
+ },
37
+ "region-train": {
38
+ sm: TrainFill18Icon,
39
+ md: TrainFill24Icon,
40
+ lg: TrainFill30Icon,
41
+ },
42
+ "region-express-train": {
43
+ sm: TrainFill18Icon,
44
+ md: TrainFill24Icon,
45
+ lg: TrainFill30Icon,
46
+ },
47
+ "long-distance-train": {
48
+ sm: TrainFill18Icon,
49
+ md: TrainFill24Icon,
50
+ lg: TrainFill30Icon,
51
+ },
52
+ "airport-express-train": {
53
+ sm: TrainFill18Icon,
54
+ md: TrainFill24Icon,
55
+ lg: TrainFill30Icon,
56
+ },
57
+ "vy-bus": {
58
+ sm: ExpressBusFill18Icon,
59
+ md: ExpressBusFill24Icon,
60
+ lg: ExpressBusFill30Icon,
61
+ },
62
+ "local-bus": { sm: BusFill18Icon, md: BusFill24Icon, lg: BusFill30Icon },
63
+ ferry: { sm: FerryFill18Icon, md: FerryFill24Icon, lg: FerryFill30Icon },
64
+ subway: { sm: SubwayFill18Icon, md: SubwayFill24Icon, lg: SubwayFill30Icon },
65
+ tram: { sm: TramFill18Icon, md: TramFill24Icon, lg: TramFill30Icon },
66
+ "alt-transport": {
67
+ sm: AltTransportFill18Icon,
68
+ md: AltTransportFill24Icon,
69
+ lg: AltTransportFill30Icon,
70
+ },
71
+ walk: { sm: WalkFill18Icon, md: WalkFill24Icon, lg: WalkFill30Icon },
72
+ };
73
+
74
+ type GetCorrectIconArgs = {
75
+ variant: Variant;
76
+ size: Size;
77
+ };
78
+ export const getCorrectIcon = ({ variant, size }: GetCorrectIconArgs) => {
79
+ return icons[variant]?.[size] ?? Box;
80
+ };
@@ -0,0 +1,3 @@
1
+ export * from "./InfoTag";
2
+ export * from "./LineIcon";
3
+ export * from "./TravelTag";
@@ -0,0 +1,24 @@
1
+ export type Variant =
2
+ | "local-train"
3
+ | "region-train"
4
+ | "region-express-train"
5
+ | "long-distance-train"
6
+ | "airport-express-train"
7
+ | "vy-bus"
8
+ | "local-bus"
9
+ | "ferry"
10
+ | "subway"
11
+ | "tram"
12
+ | "alt-transport"
13
+ | "walk";
14
+
15
+ export type Size = "sm" | "md" | "lg";
16
+
17
+ export type TagType = "info" | "travel";
18
+
19
+ export type TagProps = {
20
+ variant: Variant;
21
+ size: Size;
22
+ title: string;
23
+ description?: string;
24
+ };