@vygruppen/spor-react 6.1.0 → 6.2.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.
@@ -1,10 +1,11 @@
1
1
  import {
2
2
  Box,
3
3
  Center,
4
- Button as ChakraButton,
5
4
  ButtonProps as ChakraButtonProps,
5
+ Flex,
6
6
  forwardRef,
7
7
  useButtonGroup,
8
+ useStyleConfig,
8
9
  } from "@chakra-ui/react";
9
10
  import React from "react";
10
11
  import { createTexts, useTranslation } from "../i18n";
@@ -22,7 +23,9 @@ export type ButtonProps = Exclude<
22
23
  size?: "xs" | "sm" | "md" | "lg";
23
24
  /** The different variants of a button
24
25
  *
25
- * Defaults to "primary"
26
+ * Defaults to "primary".
27
+ *
28
+ * "control" is deprecated, and will be removed in a future version
26
29
  */
27
30
  variant?:
28
31
  | "control"
@@ -37,7 +40,6 @@ export type ButtonProps = Exclude<
37
40
  *
38
41
  * There are several button variants. You can specify which one you want with the `variant` prop. The available variants are:
39
42
  *
40
- * - `control`: This button is used for ticket controls only.
41
43
  * - `primary`: This is our main button. It's used for the main actions in a view, like a call to action. There should only be a single primary button in each view.
42
44
  * - `secondary`: Used for secondary actions in a view, and when you need to make several actions available at the same time.
43
45
  * - `tertiary`: Used for additional choices, like a less important secondary action.
@@ -62,61 +64,48 @@ export type ButtonProps = Exclude<
62
64
  */
63
65
  export const Button = forwardRef<ButtonProps, "button">((props, ref) => {
64
66
  const {
67
+ as = "button",
68
+ fontWeight,
65
69
  size,
66
- variant,
67
70
  children,
68
71
  isLoading,
69
72
  isDisabled,
70
73
  leftIcon,
71
74
  rightIcon,
75
+ sx,
72
76
  ...rest
73
77
  } = props;
74
78
  const ariaLabel = useCorrectAriaLabel(props);
75
79
  const buttonGroup = useButtonGroup();
76
- const finalVariant = (variant ??
77
- buttonGroup?.variant ??
78
- "primary") as Required<ButtonProps["variant"]>;
79
80
  const finalSize = (size ?? buttonGroup?.size ?? "md") as Required<
80
81
  ButtonProps["size"]
81
82
  >;
83
+ const styles = useStyleConfig("Button", {
84
+ ...buttonGroup,
85
+ ...rest,
86
+ size: finalSize,
87
+ leftIcon,
88
+ rightIcon,
89
+ });
90
+
91
+ // We want to explicitly allow to override the fontWeight prop
92
+ if (fontWeight) {
93
+ styles.fontWeight = fontWeight;
94
+ }
82
95
 
83
96
  return (
84
- <ChakraButton
85
- size={finalSize}
86
- variant={finalVariant}
97
+ <Box
87
98
  {...rest}
99
+ as={as}
100
+ sx={{ ...styles, ...sx }}
88
101
  ref={ref}
89
102
  aria-label={ariaLabel}
90
103
  aria-busy={isLoading}
91
- isDisabled={isDisabled || isLoading}
92
- leftIcon={
93
- isLoading && leftIcon ? (
94
- <Box visibility={isLoading ? "hidden" : "visible"} aria-hidden="true">
95
- {leftIcon}
96
- </Box>
97
- ) : (
98
- leftIcon
99
- )
100
- }
101
- rightIcon={
102
- isLoading && rightIcon ? (
103
- <Box visibility={isLoading ? "hidden" : "visible"} aria-hidden="true">
104
- {rightIcon}
105
- </Box>
106
- ) : (
107
- rightIcon
108
- )
109
- }
104
+ disabled={isDisabled || isLoading}
110
105
  position="relative"
111
106
  >
112
107
  {isLoading && (
113
- <Center
114
- position="absolute"
115
- right="0"
116
- paddingBottom={1}
117
- left="0"
118
- paddingTop={2}
119
- >
108
+ <Center position="absolute" right={0} left={0} top={1} bottom={0}>
120
109
  <ColorInlineLoader
121
110
  maxWidth={getLoaderWidth(finalSize)}
122
111
  width="80%"
@@ -125,18 +114,30 @@ export const Button = forwardRef<ButtonProps, "button">((props, ref) => {
125
114
  />
126
115
  </Center>
127
116
  )}
128
- <Box
117
+ <Flex
118
+ justifyContent="space-between"
119
+ alignItems="center"
120
+ gap={1}
129
121
  visibility={isLoading ? "hidden" : "visible"}
130
- whiteSpace="normal"
131
- textAlign="left"
122
+ aria-hidden={isLoading}
132
123
  >
133
- {children}
134
- </Box>
135
- </ChakraButton>
124
+ <Flex justifyContent="center" alignItems="center" gap={1}>
125
+ {leftIcon}
126
+ <Box
127
+ visibility={isLoading ? "hidden" : "visible"}
128
+ whiteSpace="normal"
129
+ textAlign="left"
130
+ >
131
+ {children}
132
+ </Box>
133
+ </Flex>
134
+ {rightIcon}
135
+ </Flex>
136
+ </Box>
136
137
  );
137
138
  });
138
139
 
139
- function getLoaderWidth(size: any) {
140
+ function getLoaderWidth(size: Required<ButtonProps["size"]>) {
140
141
  switch (size) {
141
142
  case "xs":
142
143
  return "4rem";
@@ -8,6 +8,10 @@ import React from "react";
8
8
  import { ColorSpinner } from "..";
9
9
 
10
10
  export type IconButtonProps = Omit<ChakraIconButtonProps, "variant"> & {
11
+ /** The button variant.
12
+ *
13
+ * "control" is deprecated
14
+ */
11
15
  variant:
12
16
  | "control"
13
17
  | "primary"
@@ -52,14 +52,9 @@ export const DateField = forwardRef<HTMLDivElement, DateFieldProps>(
52
52
  {props.label}
53
53
  </FormLabel>
54
54
  )}
55
- <Flex {...fieldProps} paddingTop="3" paddingBottom="0.5">
55
+ <Flex {...fieldProps} ref={ref} paddingTop="3" paddingBottom="0.5">
56
56
  {state.segments.map((segment, i) => (
57
- <DateTimeSegment
58
- ref={i === 0 ? ref : undefined}
59
- key={i}
60
- segment={segment}
61
- state={state}
62
- />
57
+ <DateTimeSegment key={i} segment={segment} state={state} />
63
58
  ))}
64
59
  </Flex>
65
60
  <input
@@ -14,7 +14,6 @@ import {
14
14
  useTranslation,
15
15
  } from "..";
16
16
  import { getBoxShadowString } from "../theme/utils/box-shadow-utils";
17
- import { focusVisible } from "../theme/utils/focus-utils";
18
17
 
19
18
  type NumericStepperProps = {
20
19
  /** The name of the input field */
@@ -197,13 +196,10 @@ const VerySmallButton = (props: VerySmallButtonProps) => {
197
196
  size="xs"
198
197
  minWidth="24px"
199
198
  minHeight="24px"
200
- sx={focusVisible({
201
- notFocus: { boxShadow: "none" },
202
- focus: {
203
- boxShadow:
204
- "inset 0 0 0 2px var(--spor-colors-pine), inset 0 0 0 3px white",
205
- },
206
- })}
199
+ _focusVisible={{
200
+ boxShadow:
201
+ "inset 0 0 0 2px var(--spor-colors-pine), inset 0 0 0 3px white",
202
+ }}
207
203
  {...props}
208
204
  />
209
205
  );
@@ -24,6 +24,17 @@ export type InfoTagProps = TagProps;
24
24
  * />
25
25
  * ```
26
26
  *
27
+ * If required, you can also override the colors and icons in these line tags:
28
+ *
29
+ * ```tsx
30
+ * <InfoTag
31
+ * variant="custom"
32
+ * customIconVariant="ferry"
33
+ * foregroundColor="#b4da55"
34
+ * backgroundColor="#c0ffee"
35
+ * />
36
+ * ```
37
+ *
27
38
  * @see https://spor.vy.no/components/line-tags
28
39
  */
29
40
  export const InfoTag = ({
@@ -31,11 +42,21 @@ export const InfoTag = ({
31
42
  size = "md",
32
43
  title,
33
44
  description,
45
+ ...customProps
34
46
  }: InfoTagProps) => {
35
- const styles = useMultiStyleConfig("InfoTag", { variant, size });
47
+ const styles = useMultiStyleConfig("InfoTag", {
48
+ variant,
49
+ size,
50
+ ...customProps,
51
+ });
36
52
  return (
37
53
  <Box sx={styles.container}>
38
- <LineIcon variant={variant} size={size} sx={styles.iconContainer} />
54
+ <LineIcon
55
+ variant={variant}
56
+ size={size}
57
+ sx={styles.iconContainer}
58
+ {...(customProps as any)} // TODO: Fix this
59
+ />
39
60
  <Box sx={styles.textContainer}>
40
61
  {title && (
41
62
  <Box as="span" sx={styles.title}>
@@ -3,10 +3,23 @@ import React from "react";
3
3
  import { getCorrectIcon } from "./icons";
4
4
  import { TagProps } from "./types";
5
5
 
6
- export type LineIconProps = BoxProps & {
7
- variant: TagProps["variant"];
8
- size: TagProps["size"];
6
+ type DefaultVariants = Exclude<TagProps["variant"], "custom">;
7
+
8
+ type DefaultVariantProps = {
9
+ variant: DefaultVariants;
10
+ };
11
+ type CustomVariantProps = {
12
+ variant: "custom";
13
+ customIconVariant: DefaultVariants;
14
+ foregroundColor: string;
15
+ backgroundColor: string;
9
16
  };
17
+ type VariantProps = DefaultVariantProps | CustomVariantProps;
18
+
19
+ export type LineIconProps = Exclude<BoxProps, "variant"> &
20
+ VariantProps & {
21
+ size: TagProps["size"];
22
+ };
10
23
 
11
24
  /**
12
25
  * A line icon component.
@@ -23,6 +36,18 @@ export type LineIconProps = BoxProps & {
23
36
  * <LineIcon variant="subway" size="lg" />
24
37
  * ```
25
38
  *
39
+ * If you require some one-off colors, but still want to use the line tag component,
40
+ * you can do so like this:
41
+ *
42
+ * ```tsx
43
+ * <LineIcon
44
+ * variant="custom"
45
+ * customIconVariant="ferry"
46
+ * foregroundColor="#b4da55"
47
+ * backgroundColor="#c0ffee"
48
+ * />
49
+ * ```
50
+ *
26
51
  * @see https://spor.vy.no/components/line-tags
27
52
  */
28
53
  export const LineIcon = ({
@@ -31,8 +56,16 @@ export const LineIcon = ({
31
56
  sx,
32
57
  ...rest
33
58
  }: LineIconProps) => {
34
- const styles = useMultiStyleConfig("LineIcon", { variant, size });
35
- const Icon: any = getCorrectIcon({ variant, size });
59
+ const styles = useMultiStyleConfig("LineIcon", { variant, size, ...rest });
60
+ const Icon: any = getCorrectIcon({
61
+ variant:
62
+ variant === "custom" && "customIconVariant" in rest
63
+ ? rest.customIconVariant
64
+ : variant === "custom"
65
+ ? "local-train"
66
+ : variant,
67
+ size,
68
+ });
36
69
  if (!Icon) {
37
70
  return null;
38
71
  }
@@ -58,6 +58,19 @@ export type TravelTagProps = TagProps &
58
58
  * />
59
59
  * ```
60
60
  *
61
+ * If required, you can also override the colors and icons in these travel tags:
62
+ *
63
+ * ```tsx
64
+ * <TravelTag
65
+ * variant="custom"
66
+ * customIconVariant="ferry"
67
+ * foregroundColor="#b4da55"
68
+ * backgroundColor="#c0ffee"
69
+ * title="3"
70
+ * description="Ringen"
71
+ * />
72
+ * ```
73
+ *
61
74
  * @see https://spor.vy.no/components/line-tags
62
75
  */
63
76
  export const TravelTag = forwardRef<TravelTagProps, As>(
@@ -77,13 +90,20 @@ export const TravelTag = forwardRef<TravelTagProps, As>(
77
90
  variant,
78
91
  size,
79
92
  deviationLevel,
93
+ foregroundColor: variant === "custom" ? rest.foregroundColor : undefined,
94
+ backgroundColor: variant === "custom" ? rest.backgroundColor : undefined,
80
95
  });
81
96
 
82
97
  const DeviationLevelIcon = getDeviationLevelIcon({ deviationLevel, size });
83
98
 
84
99
  return (
85
100
  <Box sx={styles.container} aria-disabled={isDisabled} ref={ref} {...rest}>
86
- <LineIcon variant={variant} size={size} sx={styles.iconContainer} />
101
+ <LineIcon
102
+ variant={variant}
103
+ size={size}
104
+ sx={styles.iconContainer}
105
+ {...(rest as any)}
106
+ />
87
107
  <Box sx={styles.textContainer}>
88
108
  {title && (
89
109
  <Box as="span" sx={styles.title}>
@@ -16,9 +16,24 @@ export type Size = "sm" | "md" | "lg";
16
16
 
17
17
  export type TagType = "info" | "travel";
18
18
 
19
- export type TagProps = {
20
- variant: Variant;
19
+ export type TagProps = VariantProps & {
21
20
  size: Size;
22
21
  title: string;
23
22
  description?: string;
24
23
  };
24
+
25
+ type DefaultVariantProps = {
26
+ variant: Variant;
27
+ };
28
+ type CustomVariantProps = {
29
+ variant: "custom";
30
+ /** When variant="custom", the foreground color of the tag */
31
+ foregroundColor: string;
32
+ /** When variant="custom", the background color of the tag */
33
+ backgroundColor: string;
34
+ /** When variant="custom", this is the icon you want to display.
35
+ * It should be one of the other variants
36
+ */
37
+ customIconVariant: Variant;
38
+ };
39
+ type VariantProps = DefaultVariantProps | CustomVariantProps;
@@ -2,13 +2,11 @@ import { defineStyleConfig } from "@chakra-ui/react";
2
2
  import { mode } from "@chakra-ui/theme-tools";
3
3
  import { colors } from "../foundations";
4
4
  import { getBoxShadowString } from "../utils/box-shadow-utils";
5
- import { focusVisible } from "../utils/focus-utils";
6
5
 
7
6
  const config = defineStyleConfig({
8
7
  baseStyle: {
9
8
  border: 0,
10
9
  borderRadius: "xl",
11
- fontWeight: "bold",
12
10
  transitionProperty: "common",
13
11
  transitionDuration: "normal",
14
12
  textWrap: "wrap",
@@ -36,18 +34,15 @@ const config = defineStyleConfig({
36
34
  // hardcoded background color as alpha-"hack" below is not feasible for dark mode with solid background color
37
35
  backgroundColor: mode("pine", "coralGreen")(props),
38
36
  color: mode("white", "darkTeal")(props),
39
- ...focusVisible({
40
- focus: {
41
- boxShadow: `inset 0 0 0 2px ${mode(
42
- colors.greenHaze,
43
- colors.azure,
44
- )(props)}, inset 0 0 0 4px ${mode(
45
- colors.white,
46
- colors.darkGrey,
47
- )(props)}`,
48
- },
49
- notFocus: { boxShadow: "none" },
50
- }),
37
+ _focusVisible: {
38
+ boxShadow: `inset 0 0 0 2px ${mode(
39
+ colors.greenHaze,
40
+ colors.azure,
41
+ )(props)}, inset 0 0 0 4px ${mode(
42
+ colors.white,
43
+ colors.darkGrey,
44
+ )(props)}`,
45
+ },
51
46
  _hover: {
52
47
  backgroundColor: mode("darkTeal", "blueGreen")(props),
53
48
  },
@@ -58,34 +53,29 @@ const config = defineStyleConfig({
58
53
  secondary: (props) => ({
59
54
  // FIXME: Update to use global defined background color for darkMode whenever it is available instead of alpha
60
55
  backgroundColor: mode("seaMist", "primaryGreen")(props),
61
- color: mode("darkTeal", "white")(props),
56
+ color: mode("darkTeal", "seaMist")(props),
62
57
  // order is important here for now while we do not have global defined background color for darkMode
63
58
  _hover: {
64
59
  backgroundColor: mode("coralGreen", "greenHaze")(props),
65
60
  },
66
- ...focusVisible({
67
- focus: {
61
+ _focusVisible: {
62
+ boxShadow: `inset 0 0 0 2px ${mode(
63
+ colors.greenHaze,
64
+ colors.primaryGreen,
65
+ )(props)}, inset 0 0 0 4px ${mode(
66
+ colors.white,
67
+ colors.darkTeal,
68
+ )(props)}`,
69
+ _hover: {
68
70
  boxShadow: `inset 0 0 0 2px ${mode(
69
71
  colors.greenHaze,
70
- colors.primaryGreen,
72
+ colors.azure,
71
73
  )(props)}, inset 0 0 0 4px ${mode(
72
74
  colors.white,
73
- colors.darkTeal,
75
+ colors.blackAlpha[500],
74
76
  )(props)}`,
75
- _hover: {
76
- boxShadow: `inset 0 0 0 2px ${mode(
77
- colors.greenHaze,
78
- colors.azure,
79
- )(props)}, inset 0 0 0 4px ${mode(
80
- colors.white,
81
- colors.blackAlpha[500],
82
- )(props)}`,
83
- },
84
77
  },
85
- notFocus: {
86
- boxShadow: "none",
87
- },
88
- }),
78
+ },
89
79
  _active: {
90
80
  backgroundColor: mode("mint", "darkTeal")(props),
91
81
  boxShadow: `inset 0 0 0 2px ${mode(
@@ -109,25 +99,16 @@ const config = defineStyleConfig({
109
99
  tertiary: (props) => ({
110
100
  backgroundColor: "transparent",
111
101
  color: mode("darkGrey", "white")(props),
112
- fontWeight: "normal",
113
102
  boxShadow: `inset 0 0 0 1px ${mode(
114
103
  colors.blackAlpha[400],
115
104
  colors.whiteAlpha[400],
116
105
  )(props)}`,
117
- ...focusVisible({
118
- focus: {
119
- boxShadow: getBoxShadowString({
120
- borderWidth: 2,
121
- borderColor: "azure",
122
- }),
123
- },
124
- notFocus: {
125
- boxShadow: `inset 0 0 0 1px ${mode(
126
- colors.blackAlpha[400],
127
- colors.whiteAlpha[400],
128
- )(props)}`,
129
- },
130
- }),
106
+ _focusVisible: {
107
+ boxShadow: getBoxShadowString({
108
+ borderWidth: 2,
109
+ borderColor: "azure",
110
+ }),
111
+ },
131
112
  _hover: {
132
113
  boxShadow: `inset 0 0 0 2px currentColor`,
133
114
  },
@@ -142,18 +123,12 @@ const config = defineStyleConfig({
142
123
  ghost: (props) => ({
143
124
  backgroundColor: "transparent",
144
125
  color: mode("darkGrey", "white")(props),
145
- fontWeight: "normal",
146
- ...focusVisible({
147
- focus: {
148
- boxShadow: getBoxShadowString({
149
- borderColor: mode("greenHaze", "azure")(props),
150
- borderWidth: 2,
151
- }),
152
- },
153
- notFocus: {
154
- outline: "none",
155
- },
156
- }),
126
+ _focusVisible: {
127
+ boxShadow: getBoxShadowString({
128
+ borderColor: mode("greenHaze", "azure")(props),
129
+ borderWidth: 2,
130
+ }),
131
+ },
157
132
  _hover: {
158
133
  backgroundColor: mode("seaMist", "whiteAlpha.200")(props),
159
134
  _disabled: {
@@ -181,26 +156,20 @@ const config = defineStyleConfig({
181
156
  borderWidth: 2,
182
157
  }),
183
158
  },
184
- ...focusVisible({
185
- focus: {
159
+ _focusVisible: {
160
+ boxShadow: getBoxShadowString({
161
+ borderColor: mode("greenHaze", "azure")(props),
162
+ borderWidth: 2,
163
+ baseShadow: "sm",
164
+ }),
165
+ _hover: {
186
166
  boxShadow: getBoxShadowString({
187
167
  borderColor: mode("greenHaze", "azure")(props),
188
168
  borderWidth: 2,
189
- baseShadow: "sm",
169
+ baseShadow: "md",
190
170
  }),
191
- _hover: {
192
- boxShadow: getBoxShadowString({
193
- borderColor: mode("greenHaze", "azure")(props),
194
- borderWidth: 2,
195
- baseShadow: "md",
196
- }),
197
- },
198
171
  },
199
- notFocus: {
200
- outline: "none",
201
- boxShadow: "sm",
202
- },
203
- }),
172
+ },
204
173
  }),
205
174
  },
206
175
  sizes: {
@@ -208,22 +177,26 @@ const config = defineStyleConfig({
208
177
  minHeight: 8,
209
178
  minWidth: 8,
210
179
  fontSize: "18px",
180
+ fontWeight: "bold",
211
181
  },
212
182
  md: {
213
183
  minHeight: 7,
214
184
  minWidth: 7,
215
185
  fontSize: "18px",
186
+ fontWeight: "bold",
216
187
  },
217
188
  sm: {
218
189
  minHeight: 6,
219
190
  minWidth: 6,
220
191
  fontSize: "16px",
192
+ fontWeight: "normal",
221
193
  },
222
194
  xs: {
223
195
  minHeight: 5,
224
196
  minWidth: 5,
225
197
  fontSize: "16px",
226
198
  paddingX: 2,
199
+ fontWeight: "normal",
227
200
  },
228
201
  },
229
202
  defaultProps: {
@@ -2,7 +2,6 @@ import { defineStyleConfig } from "@chakra-ui/react";
2
2
  import { mode } from "@chakra-ui/theme-tools";
3
3
  import { colors } from "../foundations";
4
4
  import { getBoxShadowString } from "../utils/box-shadow-utils";
5
- import { focusVisible } from "../utils/focus-utils";
6
5
 
7
6
  const config = defineStyleConfig({
8
7
  baseStyle: (props: any) => ({
@@ -20,21 +19,15 @@ const config = defineStyleConfig({
20
19
  ...getColorSchemeClickableProps(props),
21
20
  _hover: getColorSchemeHoverProps(props),
22
21
  _active: getColorSchemeActiveProps(props),
23
- ...focusVisible({
24
- focus: {
25
- boxShadow: getBoxShadowString({
26
- borderColor: mode("greenHaze", "azure")(props),
27
- borderWidth: 2,
28
- isInset: false,
29
- }),
30
- outline: "none",
31
- _active: getColorSchemeActiveProps(props),
32
- },
33
- notFocus: {
34
- ...getColorSchemeClickableProps(props),
35
- _active: getColorSchemeActiveProps(props),
36
- },
37
- }),
22
+ _focusVisible: {
23
+ boxShadow: getBoxShadowString({
24
+ borderColor: mode("greenHaze", "azure")(props),
25
+ borderWidth: 2,
26
+ isInset: false,
27
+ }),
28
+ outline: "none",
29
+ _active: getColorSchemeActiveProps(props),
30
+ },
38
31
  _disabled: {
39
32
  backgroundColor: "platinum",
40
33
  boxShadow: getBoxShadowString({
@@ -92,6 +92,11 @@ const config = helpers.defineMultiStyleConfig({
92
92
  },
93
93
  },
94
94
  }),
95
+ custom: (props) => ({
96
+ iconContainer: {
97
+ backgroundColor: props.backgroundColor,
98
+ },
99
+ }),
95
100
  },
96
101
  sizes: {
97
102
  sm: {
@@ -172,6 +172,11 @@ const config = helpers.defineMultiStyleConfig({
172
172
  display: "none",
173
173
  },
174
174
  }),
175
+ custom: (props) => ({
176
+ container: {
177
+ backgroundColor: props.foregroundColor,
178
+ },
179
+ }),
175
180
  },
176
181
  sizes: {
177
182
  sm: {
package/tsconfig.json CHANGED
@@ -3,6 +3,6 @@
3
3
  "include": ["."],
4
4
  "exclude": ["dist", "build", "node_modules"],
5
5
  "compilerOptions": {
6
- "rootDir": "."
7
- }
6
+ "rootDir": ".",
7
+ },
8
8
  }