@vygruppen/spor-react 7.0.0 → 7.1.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.
@@ -1,22 +1,42 @@
1
- import { Flex, HStack, useMultiStyleConfig } from "@chakra-ui/react";
2
- import { DropdownLeftFill24Icon } from "@vygruppen/spor-icon-react";
1
+ import { Flex, useMultiStyleConfig } from "@chakra-ui/react";
2
+ import { ArrowLeftFill24Icon } from "@vygruppen/spor-icon-react";
3
3
  import React from "react";
4
4
  import { StepperStep } from ".";
5
- import {
6
- Box,
7
- IconButton,
8
- SimplePopover,
9
- createTexts,
10
- useTranslation,
11
- } from "..";
5
+ import { Box, IconButton, Text, createTexts, useTranslation } from "..";
12
6
  import { StepperProvider } from "./StepperContext";
13
7
 
14
8
  type StepperProps = {
9
+ /** Callback for when a step is clicked */
15
10
  onClick: (clickedStep: number) => void;
11
+ /** Callback for when the back button is clicked (on smaller screens).
12
+ * A boolean indicating whether or not the user is on the first step is passed as an argument.
13
+ *
14
+ * If this is not provided, the back button will not be shown on smaller screens on the first step.
15
+ */
16
+ onBackButtonClick?: (isFirstStep: boolean) => void;
17
+ /**
18
+ * Heading shown on smaller devices
19
+ * @deprecated Use `heading` instead
20
+ */
16
21
  title?: string;
22
+ /** Heading shown on smaller devices */
23
+ heading?: string;
24
+ /**
25
+ * The heading level rendered for the heading shown on smaller devices.
26
+ *
27
+ * Defaults to h2
28
+ * */
29
+ headingLevel?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p";
30
+ /** The currently active step */
17
31
  activeStep: number;
32
+ /** The labels of each step */
18
33
  steps: string[];
34
+ /** The variant.
35
+ * "base" has a transparent background,
36
+ * while "accent" has a slight accent color */
19
37
  variant: "base" | "accent";
38
+ /** Disables all clicks */
39
+ isDisabled?: boolean;
20
40
  };
21
41
  /**
22
42
  * A stepper is used to show which step of a process a user is currently in.
@@ -25,74 +45,78 @@ type StepperProps = {
25
45
  *
26
46
  * ```tsx
27
47
  * <Stepper
28
- * title="Eksempel"
48
+ * title="Example"
29
49
  * onClick={handleStepClick}
30
50
  * activeStep={2}
31
- * steps={['Velg hvor', 'Velg når', 'Velg hvordan']}
51
+ * steps={['Where', 'When', 'How']}
32
52
  * />
33
53
  * ```
34
54
  **/
35
55
  export const Stepper = ({
36
56
  onClick = () => {},
57
+ onBackButtonClick,
37
58
  steps,
38
59
  activeStep: activeStepAsStringOrNumber,
39
60
  title,
61
+ heading,
62
+ headingLevel,
40
63
  variant,
64
+ isDisabled,
41
65
  }: StepperProps) => {
42
66
  const style = useMultiStyleConfig("Stepper", { variant });
43
67
  const numberOfSteps = steps.length;
44
68
  const activeStep = Number(activeStepAsStringOrNumber);
45
69
  const { t } = useTranslation();
70
+ const hideBackButtonOnFirstStep = activeStep === 1 && !onBackButtonClick;
71
+ const shownHeading = heading || title;
46
72
  return (
47
- <Box __css={style.root}>
73
+ <Box sx={style.root}>
48
74
  <StepperProvider
49
75
  onClick={onClick}
50
76
  activeStep={activeStep}
51
77
  variant={variant}
52
78
  numberOfSteps={numberOfSteps}
53
79
  >
54
- <Box __css={style.container}>
55
- <Box __css={style.innerContainer}>
56
- <HStack>
57
- {activeStep > 1 && (
58
- <IconButton
59
- aria-label={t(texts.back)}
60
- icon={<DropdownLeftFill24Icon />}
61
- variant="ghost"
62
- size="sm"
63
- onClick={() => onClick(activeStep - 1)}
64
- __css={style.backButton}
65
- />
80
+ <Box sx={style.container}>
81
+ <Box sx={style.innerContainer}>
82
+ <Flex
83
+ justifyContent="space-between"
84
+ alignItems="center"
85
+ gap={2}
86
+ flex={1}
87
+ >
88
+ <IconButton
89
+ aria-label={t(texts.back)}
90
+ icon={<ArrowLeftFill24Icon />}
91
+ variant="ghost"
92
+ size="sm"
93
+ visibility={hideBackButtonOnFirstStep ? "hidden" : "visible"}
94
+ onClick={() => {
95
+ if (onBackButtonClick) {
96
+ onBackButtonClick(activeStep === 1);
97
+ }
98
+ onClick(activeStep - 1);
99
+ }}
100
+ />
101
+ {shownHeading && (
102
+ <Text flex={1} variant="sm" as={headingLevel} sx={style.title}>
103
+ {shownHeading}
104
+ </Text>
66
105
  )}
67
-
68
- <SimplePopover
69
- triggerElement={
70
- <Box as="button" __css={style.stepCounter}>
71
- {t(texts.stepsOf(activeStep, numberOfSteps))}
72
- </Box>
73
- }
74
- borderRadius="xs"
75
- >
76
- {steps.map((step, index) => (
77
- <StepperStep
78
- key={step}
79
- stepNumber={index + 1}
80
- variant={variant}
81
- >
82
- {step}
83
- </StepperStep>
84
- ))}
85
- </SimplePopover>
86
- </HStack>
87
- {title && (
88
- <Box as="h3" __css={style.title}>
89
- {title}
106
+ <Box sx={style.stepCounter}>
107
+ {t(texts.stepsOf(activeStep, numberOfSteps))}
90
108
  </Box>
91
- )}
109
+ </Flex>
92
110
  </Box>
93
- <Flex justifyContent="center" display={["none", "flex"]}>
111
+ <Flex justifyContent="center" display={["none", null, "flex"]}>
94
112
  {steps.map((step, index) => (
95
- <StepperStep key={index} stepNumber={index + 1} variant={variant}>
113
+ <StepperStep
114
+ key={index}
115
+ stepNumber={index + 1}
116
+ variant={variant}
117
+ aria-current={index + 1 === activeStep ? "step" : undefined}
118
+ isDisabled={isDisabled}
119
+ >
96
120
  {step}
97
121
  </StepperStep>
98
122
  ))}
@@ -105,10 +129,10 @@ export const Stepper = ({
105
129
 
106
130
  const texts = createTexts({
107
131
  stepsOf: (activeStep, numberOfSteps) => ({
108
- nb: `Steg ${activeStep} av ${numberOfSteps}`,
109
- nn: `Steg ${activeStep} av ${numberOfSteps}`,
110
- sv: `Steg ${activeStep} av ${numberOfSteps}`,
111
- en: `Step ${activeStep} of ${numberOfSteps}`,
132
+ nb: `Steg ${activeStep}/${numberOfSteps}`,
133
+ nn: `Steg ${activeStep}/${numberOfSteps}`,
134
+ sv: `Steg ${activeStep}/${numberOfSteps}`,
135
+ en: `Step ${activeStep}/${numberOfSteps}`,
112
136
  }),
113
137
  back: {
114
138
  nb: "Tilbake",
@@ -1,82 +1,73 @@
1
- import { useMultiStyleConfig } from "@chakra-ui/react";
1
+ import { useColorModeValue, useMultiStyleConfig } from "@chakra-ui/react";
2
2
  import { DropdownRightFill18Icon } from "@vygruppen/spor-icon-react";
3
3
  import React from "react";
4
- import { Box, Button } from "..";
4
+ import { Box, Button, Text } from "..";
5
5
  import { useStepper } from "./StepperContext";
6
6
 
7
7
  type StepperStepProps = {
8
8
  children: React.ReactNode;
9
9
  stepNumber: number;
10
10
  variant: "base" | "accent";
11
+ isDisabled?: boolean;
11
12
  };
12
13
  export const StepperStep = ({
13
14
  children,
14
15
  stepNumber,
15
16
  variant,
17
+ isDisabled: isDisabledOverride,
16
18
  }: StepperStepProps) => {
17
19
  const { activeStep, onClick } = useStepper();
18
- const state = getState(stepNumber!, activeStep);
20
+ const state = getState(stepNumber, activeStep);
19
21
  const style = useMultiStyleConfig("Stepper", {
20
22
  state,
21
23
  variant,
22
24
  });
25
+ const disabledTextColor = useColorModeValue(
26
+ "blackAlpha.400",
27
+ "whiteAlpha.400",
28
+ );
29
+ const iconColor = useColorModeValue("blackAlpha.200", "whiteAlpha.200");
23
30
 
24
- const adjustedProps = getButtonStylesForState(state);
31
+ const isDisabled =
32
+ (state !== "active" && isDisabledOverride) || state === "disabled";
25
33
 
26
34
  return (
27
- <Box __css={style.stepContainer}>
35
+ <Box sx={style.stepContainer}>
28
36
  {stepNumber > 1 && (
29
- <DropdownRightFill18Icon marginX={5} display={["none", "block"]} />
37
+ <DropdownRightFill18Icon
38
+ marginX={5}
39
+ display={["none", null, "block"]}
40
+ color={iconColor}
41
+ />
42
+ )}
43
+ {isDisabled ? (
44
+ <Text
45
+ variant="xs"
46
+ fontSize="16px"
47
+ color={disabledTextColor}
48
+ cursor="default"
49
+ paddingX={2}
50
+ >
51
+ {children}
52
+ </Text>
53
+ ) : (
54
+ <Button
55
+ size="xs"
56
+ variant={state === "active" ? "primary" : "ghost"}
57
+ onClick={
58
+ state === "completed" ? () => onClick(stepNumber) : undefined
59
+ }
60
+ pointerEvents={state === "active" ? "none" : "auto"}
61
+ tabIndex={state === "active" ? -1 : undefined}
62
+ sx={style.stepButton}
63
+ >
64
+ {children}
65
+ </Button>
30
66
  )}
31
-
32
- <Button
33
- size={"xs"}
34
- variant={
35
- state === "active"
36
- ? "primary"
37
- : state === "completed"
38
- ? "tertiary"
39
- : "ghost"
40
- }
41
- {...adjustedProps}
42
- onClick={() => onClick(stepNumber)}
43
- >
44
- {children}
45
- </Button>
46
67
  </Box>
47
68
  );
48
69
  };
49
70
 
50
- const getButtonStylesForState = (
51
- state: "completed" | "active" | "disabled",
52
- ): Record<string, any> => {
53
- switch (state) {
54
- case "active":
55
- return {
56
- _hover: {},
57
- boxShadow: "none",
58
- _focus: {},
59
- _active: {},
60
- cursor: "auto",
61
- };
62
- case "completed":
63
- return {
64
- boxShadow: "none",
65
- };
66
- case "disabled":
67
- return {
68
- _disabled: {},
69
- _hover: {},
70
- _focus: {},
71
- _active: {},
72
- color: "dimGrey",
73
- cursor: "auto",
74
- };
75
- default:
76
- return {};
77
- }
78
- };
79
-
80
71
  const getState = (stepNumber: number, activeStep: number) => {
81
72
  if (stepNumber < activeStep) {
82
73
  return "completed";
@@ -24,12 +24,12 @@ const config = defineStyleConfig({
24
24
  _disabled: {
25
25
  cursor: "not-allowed",
26
26
  boxShadow: "none",
27
- backgroundColor: "silver",
28
- color: "dimGrey",
27
+ backgroundColor: mode("blackAlpha.100", "whiteAlpha.100")(props),
28
+ color: mode("blackAlpha.400", "whiteAlpha.400")(props),
29
29
  },
30
30
  _hover: {
31
31
  _disabled: {
32
- background: "silver",
32
+ background: mode("blackAlpha.100", "whiteAlpha.100")(props),
33
33
  },
34
34
  },
35
35
  }),
@@ -23,12 +23,16 @@ const config = helpers.defineMultiStyleConfig({
23
23
  position: "fixed",
24
24
  ...getPositionProps(props),
25
25
  _disabled: {
26
- backgroundColor: "whiteAlpha.400",
27
- color: "white",
26
+ backgroundColor: mode("blackAlpha.100", "whiteAlpha.100")(props),
27
+ color: mode("blackAlpha.400", "whiteAlpha.400")(props),
28
28
  },
29
29
  ...focusVisibleStyles(props),
30
30
  _hover: {
31
31
  backgroundColor: "seaMist",
32
+ _disabled: {
33
+ backgroundColor: mode("blackAlpha.100", "whiteAlpha.100")(props),
34
+ color: mode("blackAlpha.400", "whiteAlpha.400")(props),
35
+ },
32
36
  },
33
37
  zIndex: "sticky",
34
38
  },
@@ -29,7 +29,7 @@ const config = helpers.defineMultiStyleConfig({
29
29
  },
30
30
  },
31
31
  circle: {
32
- fill: mode("blackAlpha.400", "whiteAlpha.400")(props),
32
+ fill: mode("blackAlpha.200", "whiteAlpha.200")(props),
33
33
  },
34
34
  },
35
35
  }),
@@ -5,7 +5,6 @@ const parts = anatomy("stepper").parts(
5
5
  "root",
6
6
  "container",
7
7
  "innerContainer",
8
- "backButton",
9
8
  "title",
10
9
  "stepCounter",
11
10
  "stepContainer",
@@ -18,32 +17,27 @@ const parts = anatomy("stepper").parts(
18
17
  const helpers = createMultiStyleConfigHelpers(parts.keys);
19
18
 
20
19
  const config = helpers.defineMultiStyleConfig({
21
- baseStyle: (props) => ({
20
+ baseStyle: {
22
21
  root: {
23
22
  display: "flex",
24
23
  alignItems: "center",
25
- justifyContent: ["space-between", "center"],
26
- minHeight: ["48px", "60px"],
24
+ justifyContent: ["space-between", null, "center"],
25
+ minHeight: ["48px", null, "60px"],
27
26
  overflowX: "auto",
28
27
  width: "100%",
29
28
  },
30
29
  container: {
31
- paddingX: [2, 2, 0],
30
+ paddingX: [2, null, null, 0],
32
31
  maxWidth: "container.lg",
33
32
  marginX: "auto",
34
33
  width: "100%",
35
34
  },
36
35
  innerContainer: {
37
36
  overflow: "hidden",
38
- display: ["flex", "none"],
37
+ display: ["flex", null, "none"],
39
38
  alignItems: "center",
40
39
  justifyContent: "space-between",
41
- },
42
- backButton: {
43
- borderRadius: "xs",
44
- paddingX: 0,
45
- width: "auto",
46
- minWidth: "auto",
40
+ gap: 3,
47
41
  },
48
42
  title: {
49
43
  overflow: "hidden",
@@ -51,8 +45,8 @@ const config = helpers.defineMultiStyleConfig({
51
45
  WebkitLineClamp: 2,
52
46
  display: "-webkit-box",
53
47
  WebkitBoxOrient: "vertical",
54
- marginLeft: 2,
55
- textAlign: "right",
48
+ textAlign: "center",
49
+ maxWidth: "80%",
56
50
  },
57
51
  stepContainer: {
58
52
  display: "flex",
@@ -62,7 +56,7 @@ const config = helpers.defineMultiStyleConfig({
62
56
  textStyle: "sm",
63
57
  whiteSpace: "nowrap",
64
58
  },
65
- }),
59
+ },
66
60
  variants: {
67
61
  base: () => ({
68
62
  root: {
@@ -72,6 +66,26 @@ const config = helpers.defineMultiStyleConfig({
72
66
  accent: (props) => ({
73
67
  root: {
74
68
  backgroundColor: mode("seaMist", "pine")(props),
69
+ color: mode("darkTeal", "seaMist")(props),
70
+ },
71
+ stepButton: {
72
+ color:
73
+ props.state === "disabled"
74
+ ? mode("blackAlpha.400", "whiteAlpha.400")(props)
75
+ : props.state === "completed"
76
+ ? mode("darkTeal", "white")(props)
77
+ : mode("white", "darkTeal")(props),
78
+ _hover: {
79
+ backgroundColor:
80
+ props.state === "disabled"
81
+ ? "transparent"
82
+ : mode("coralGreen", "greenHaze")(props),
83
+ },
84
+ },
85
+ backButton: {
86
+ _hover: {
87
+ backgroundColor: mode("coralGreen", "greenHaze")(props),
88
+ },
75
89
  },
76
90
  }),
77
91
  },