@vygruppen/spor-react 7.2.2 → 8.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.
@@ -1,7 +1,19 @@
1
1
  import { createMultiStyleConfigHelpers } from "@chakra-ui/react";
2
- import { anatomy, mode } from "@chakra-ui/theme-tools";
3
- import { getBoxShadowString } from "../utils/box-shadow-utils";
2
+ import { anatomy } from "@chakra-ui/theme-tools";
4
3
  import { focusVisibleStyles } from "../utils/focus-util";
4
+ import {
5
+ accentBackground,
6
+ baseBackground,
7
+ brandBackground,
8
+ floatingBackground,
9
+ } from "../utils/background-utils";
10
+ import { baseText, accentText } from "../utils/text-utils";
11
+ import {
12
+ baseBorder,
13
+ accentBorder,
14
+ brandBorder,
15
+ floatingBorder,
16
+ } from "../utils/border-utils";
5
17
 
6
18
  const parts = anatomy("choice-chip").parts("container", "icon", "label");
7
19
 
@@ -10,66 +22,152 @@ const helpers = createMultiStyleConfigHelpers(parts.keys);
10
22
  const config = helpers.defineMultiStyleConfig({
11
23
  baseStyle: (props) => ({
12
24
  container: {
13
- backgroundColor: mode("white", "transparent")(props),
14
- boxShadow: getBoxShadowString({ borderColor: "celadon" }),
15
- color: mode("darkTeal", "white")(props),
16
25
  display: "inline-flex",
17
26
  alignItems: "center",
18
27
  fontSize: "16px",
19
28
  px: 1,
29
+ cursor: "pointer",
20
30
  _checked: {
21
- color: "white",
22
- background: "pine",
23
- boxShadow: getBoxShadowString({ borderColor: "celadon" }),
24
- },
25
- "input:focus-visible + &": focusVisibleStyles(props)._focusVisible,
26
- "@media (hover:hover)": {
31
+ ...accentText("selected", props),
32
+ ...accentBackground("selected", props),
33
+ ...accentBorder("selected", props),
27
34
  _hover: {
28
- color: mode("darkTeal", "white")(props),
29
- boxShadow: getBoxShadowString({
30
- borderColor: "greenHaze",
31
- borderWidth: 2,
32
- }),
33
- background: mode("coralGreen", "whiteAlpha.200")(props),
34
- cursor: "pointer",
35
+ ...brandBackground("hover", props),
36
+ ...baseText("selected", props),
37
+ ...brandBorder("hover", props),
38
+ },
39
+ _active: {
40
+ ...baseText("selected", props),
41
+ ...brandBackground("active", props),
42
+ ...brandBorder("active", props),
35
43
  },
36
44
  },
37
- _active: {
38
- backgroundColor: mode("mint", "whiteAlpha.300")(props),
39
- boxShadow: getBoxShadowString({
40
- borderColor: "pine",
41
- }),
45
+ _disabled: {
46
+ cursor: "not-allowed",
47
+ boxShadow: "none",
48
+ ...baseText("disabled", props),
49
+ ...baseBackground("disabled", props),
50
+ _hover: {
51
+ ...baseBackground("disabled", props),
52
+ boxShadow: "none",
53
+ ...baseText("disabled", props),
54
+ },
55
+ _checked: {
56
+ cursor: "not-allowed",
57
+ boxShadow: "none",
58
+ ...baseText("disabled", props),
59
+ ...baseBackground("disabled", props),
60
+ _hover: {
61
+ ...baseBackground("disabled", props),
62
+ boxShadow: "none",
63
+ ...baseText("disabled", props),
64
+ },
65
+ },
42
66
  },
67
+ "input:focus-visible + &": focusVisibleStyles(props)._focusVisible,
43
68
  },
44
69
  icon: {
45
70
  mr: props.hasLabel ? 1 : 0,
46
71
  },
47
72
  }),
73
+ variants: {
74
+ base: (props) => ({
75
+ container: {
76
+ ...baseBackground("default", props),
77
+ ...baseBorder("default", props),
78
+ ...baseText("default", props),
79
+ "@media (hover:hover)": {
80
+ _hover: {
81
+ ...baseText("default", props),
82
+ ...baseBorder("hover", props),
83
+ ...baseBackground("hover", props),
84
+ },
85
+ },
86
+ _active: {
87
+ ...baseBackground("active", props),
88
+ ...baseBorder("default", props),
89
+ },
90
+ },
91
+ }),
92
+ accent: (props) => ({
93
+ container: {
94
+ ...accentBackground("default", props),
95
+ ...accentText("default", props),
96
+ ...accentBorder("default", props),
97
+ "@media (hover:hover)": {
98
+ _hover: {
99
+ ...accentBackground("hover", props),
100
+ ...accentBorder("hover", props),
101
+ ...accentText("default", props),
102
+ },
103
+ },
104
+ _active: {
105
+ ...accentText("default", props),
106
+ ...accentBorder("active", props),
107
+ ...accentBackground("active", props),
108
+ },
109
+ },
110
+ _active: {
111
+ ...accentText("default", props),
112
+ ...accentBorder("active", props),
113
+ ...accentBackground("active", props),
114
+ },
115
+ }),
116
+ floating: (props) => ({
117
+ container: {
118
+ ...floatingBackground("default", props),
119
+ ...baseText("default", props),
120
+ ...floatingBorder("default", props),
121
+ _hover: {
122
+ ...floatingBackground("hover", props),
123
+ ...floatingBorder("hover", props),
124
+ ...baseText("default", props),
125
+ },
126
+ _active: {
127
+ ...floatingBackground("active", props),
128
+ ...floatingBorder("active", props),
129
+ ...baseText("default", props),
130
+ },
131
+ },
132
+ }),
133
+ },
48
134
  sizes: {
49
135
  sm: {
50
136
  container: {
51
- borderRadius: "15px",
137
+ borderRadius: "30px",
138
+ _checked: {
139
+ borderRadius: "9px",
140
+ },
52
141
  height: "30px",
53
142
  px: 1.5,
54
143
  },
55
144
  },
56
145
  md: {
57
146
  container: {
58
- borderRadius: "18px",
147
+ borderRadius: "30px",
148
+ _checked: {
149
+ borderRadius: "12px",
150
+ },
59
151
  height: "36px",
60
152
  px: 2,
61
153
  },
62
154
  },
63
155
  lg: {
64
156
  container: {
65
- borderRadius: "21px",
157
+ borderRadius: "30px",
158
+ _checked: {
159
+ borderRadius: "12px",
160
+ },
66
161
  height: "42px",
67
162
  px: 2,
68
163
  },
69
164
  },
70
165
  xl: {
71
166
  container: {
72
- borderRadius: "27px",
167
+ borderRadius: "30px",
168
+ _checked: {
169
+ borderRadius: "18px",
170
+ },
73
171
  height: "54px",
74
172
  px: 3,
75
173
  },
@@ -58,10 +58,6 @@ const config = helpers.defineMultiStyleConfig({
58
58
  },
59
59
  calendarTriggerButton: {
60
60
  backgroundColor: mode("white", "night")(props),
61
- boxShadow: getBoxShadowString({
62
- borderColor: mode("darkGrey", "white")(props),
63
- borderWidth: 1,
64
- }),
65
61
  width: 8,
66
62
  display: "flex",
67
63
  alignItems: "center",
@@ -1,6 +1,6 @@
1
1
  import { popoverAnatomy as parts } from "@chakra-ui/anatomy";
2
2
  import { createMultiStyleConfigHelpers } from "@chakra-ui/react";
3
- import { cssVar, mode } from "@chakra-ui/theme-tools";
3
+ import { cssVar } from "@chakra-ui/theme-tools";
4
4
  import { focusVisibleStyles } from "../utils/focus-util";
5
5
 
6
6
  const $popperBg = cssVar("popper-bg");
@@ -15,7 +15,7 @@ const config = helpers.defineMultiStyleConfig({
15
15
  zIndex: "popover",
16
16
  },
17
17
  content: {
18
- [$popperBg.variable]: mode(`colors.darkTeal`, `colors.pine`)(props),
18
+ [$popperBg.variable]: `colors.darkTeal`,
19
19
  backgroundColor: $popperBg.reference,
20
20
  [$arrowBg.variable]: $popperBg.reference,
21
21
  [$arrowShadowColor.variable]: `colors.blackAlpha.300`,
@@ -38,14 +38,13 @@ const config = helpers.defineMultiStyleConfig({
38
38
  closeButton: {
39
39
  position: "absolute",
40
40
  color: "white",
41
- hover: "whiteAlpha.100",
42
41
  ...focusVisibleStyles(props),
43
42
  _active: {
44
43
  backgroundColor: "whiteAlpha.200",
45
44
  },
46
- borderRadius: "xs",
47
- top: 1,
48
- insetEnd: 1,
45
+ borderRadius: "sm",
46
+ top: 2,
47
+ right: 2,
49
48
  width: 2,
50
49
  height: 2,
51
50
  padding: 1,
@@ -4,7 +4,7 @@ import { colors } from "../foundations";
4
4
 
5
5
  type BaseBackgroundState = Subset<
6
6
  State,
7
- "default" | "active" | "selected" | "disabled"
7
+ "default" | "active" | "selected" | "hover" | "disabled"
8
8
  >;
9
9
  export function baseBackground(
10
10
  state: BaseBackgroundState,
@@ -23,6 +23,10 @@ export function baseBackground(
23
23
  return {
24
24
  backgroundColor: mode("silver", "whiteAlpha.100")(props),
25
25
  };
26
+ case "hover":
27
+ return {
28
+ backgroundColor: "transparent",
29
+ };
26
30
  default:
27
31
  return {
28
32
  backgroundColor: "transparent",
@@ -47,7 +51,7 @@ export function ghostBackground(
47
51
  }
48
52
  case "active":
49
53
  return {
50
- backgroundColor: mode("seaMist", "whiteAlpha.200")(props),
54
+ backgroundColor: mode("mint", "whiteAlpha.200")(props),
51
55
  };
52
56
  case "focus":
53
57
  return {
@@ -111,3 +115,65 @@ export function floatingBackground(
111
115
  };
112
116
  }
113
117
  }
118
+
119
+ type AccentBackgroundState = Subset<
120
+ State,
121
+ "default" | "hover" | "focus" | "active" | "selected"
122
+ >;
123
+
124
+ export function accentBackground(
125
+ state: AccentBackgroundState,
126
+ props: StyleFunctionProps,
127
+ ) {
128
+ switch (state) {
129
+ case "selected":
130
+ return {
131
+ backgroundColor: mode("primaryGreen", "coralGreen")(props),
132
+ };
133
+ case "active":
134
+ return {
135
+ backgroundColor: mode("mint", "darkTeal")(props),
136
+ };
137
+ case "hover":
138
+ return {
139
+ backgroundColor: mode("coralGreen", "greenHaze")(props),
140
+ };
141
+ case "focus":
142
+ return {
143
+ backgroundColor: mode("greenHaze", "azure")(props),
144
+ };
145
+ case "default":
146
+ default:
147
+ return {
148
+ backgroundColor: mode("seaMist", "pine")(props),
149
+ };
150
+ }
151
+ }
152
+
153
+ type BrandBackgroundState = Subset<
154
+ State,
155
+ "default" | "hover" | "focus" | "active" | "selected"
156
+ >;
157
+
158
+ export function brandBackground(
159
+ state: BrandBackgroundState,
160
+ props: StyleFunctionProps,
161
+ ) {
162
+ switch (state) {
163
+ case "selected":
164
+ case "active":
165
+ return {
166
+ backgroundColor: mode("greenHaze", "seaMist")(props),
167
+ };
168
+ case "hover":
169
+ return {
170
+ backgroundColor: mode("darkTeal", "blueGreen")(props),
171
+ };
172
+ case "focus":
173
+ case "default":
174
+ default:
175
+ return {
176
+ backgroundColor: mode("pine", "coralGreen")(props),
177
+ };
178
+ }
179
+ }
@@ -84,6 +84,92 @@ export function floatingBorder(
84
84
  return {
85
85
  boxShadow: getBoxShadowString({
86
86
  borderColor: mode("grey.200", "whiteAlpha.400")(props),
87
+ baseShadow: "sm",
88
+ }),
89
+ };
90
+ }
91
+ }
92
+
93
+ type AccentBorderState = Subset<
94
+ State,
95
+ "default" | "hover" | "focus" | "active" | "selected"
96
+ >;
97
+ export function accentBorder(
98
+ state: AccentBorderState,
99
+ props: StyleFunctionProps,
100
+ ) {
101
+ switch (state) {
102
+ case "selected":
103
+ return {
104
+ boxShadow: getBoxShadowString({
105
+ borderColor: mode("primaryGreen", "coralGreen")(props),
106
+ borderWidth: 2,
107
+ }),
108
+ };
109
+ case "active":
110
+ return {
111
+ boxShadow: getBoxShadowString({
112
+ borderColor: mode("mint", "darkTeal")(props),
113
+ borderWidth: 2,
114
+ }),
115
+ };
116
+ case "hover":
117
+ return {
118
+ boxShadow: getBoxShadowString({
119
+ borderColor: mode("coralGreen", "greenHaze")(props),
120
+ borderWidth: 2,
121
+ }),
122
+ };
123
+ case "focus":
124
+ return {
125
+ boxShadow: getBoxShadowString({
126
+ borderColor: mode("greenHaze", "azure")(props),
127
+ borderWidth: 2,
128
+ }),
129
+ };
130
+ case "default":
131
+ default:
132
+ return {
133
+ boxShadow: getBoxShadowString({
134
+ borderColor: mode("seaMist", "pine")(props),
135
+ borderWidth: 2,
136
+ }),
137
+ };
138
+ }
139
+ }
140
+
141
+ type BrandBorderState = Subset<
142
+ State,
143
+ "default" | "hover" | "focus" | "active" | "selected"
144
+ >;
145
+
146
+ export function brandBorder(
147
+ state: BrandBorderState,
148
+ props: StyleFunctionProps,
149
+ ) {
150
+ switch (state) {
151
+ case "selected":
152
+ case "active":
153
+ return {
154
+ boxShadow: getBoxShadowString({
155
+ borderColor: mode("greenHaze", "seaMist")(props),
156
+ borderWidth: 2,
157
+ }),
158
+ };
159
+ case "hover":
160
+ return {
161
+ boxShadow: getBoxShadowString({
162
+ borderColor: mode("darkTeal", "blueGreen")(props),
163
+ borderWidth: 2,
164
+ }),
165
+ };
166
+ case "focus":
167
+ case "default":
168
+ default:
169
+ return {
170
+ boxShadow: getBoxShadowString({
171
+ borderColor: mode("pine", "coralGreen")(props),
172
+ borderWidth: 2,
87
173
  }),
88
174
  };
89
175
  }
@@ -0,0 +1,40 @@
1
+ import { mode, StyleFunctionProps } from "@chakra-ui/theme-tools";
2
+ import { State, Subset } from "./types";
3
+ import { colors } from "../foundations";
4
+
5
+ type BaseTextState = Subset<State, "default" | "selected" | "disabled">;
6
+ export function baseText(state: BaseTextState, props: StyleFunctionProps) {
7
+ switch (state) {
8
+ case "selected":
9
+ return {
10
+ color: mode("white", "darkTeal")(props),
11
+ };
12
+ case "disabled":
13
+ return {
14
+ color: mode(
15
+ "white",
16
+ `color-mix(in srgb, ${props.theme.colors.accent}, ${colors.white} 40%)`,
17
+ )(props),
18
+ };
19
+ default:
20
+ return {
21
+ color: mode("darkGrey", "white")(props),
22
+ };
23
+ }
24
+ }
25
+
26
+ type AccentTextState = Subset<State, "default" | "selected">;
27
+
28
+ export function accentText(state: AccentTextState, props: StyleFunctionProps) {
29
+ switch (state) {
30
+ case "selected":
31
+ return {
32
+ color: mode("white", "darkTeal")(props),
33
+ };
34
+ case "default":
35
+ default:
36
+ return {
37
+ color: mode("darkTeal", "seaMist")(props),
38
+ };
39
+ }
40
+ }
@@ -10,47 +10,45 @@ import {
10
10
  } from "@chakra-ui/react";
11
11
  import React from "react";
12
12
 
13
- type SimplePopoverProps = PopoverProps & {
13
+ export type TooltipProps = PopoverProps & {
14
14
  /**
15
- * Whatever is supposed to trigger the popover.
15
+ * Whatever is supposed to trigger the tooltip.
16
16
  * Must be focusable - like a link or button */
17
- triggerElement?: React.ReactNode;
18
- /** Callback for when the popover is requested to close */
17
+ children: React.ReactNode;
18
+ /** Callback for when the tooltip is requested to close */
19
19
  onClose?: () => void;
20
- /** Should the popover have a close button? */
20
+ /** Should the tooltip have a close button? */
21
21
  withCloseButton?: boolean;
22
- /** The content of the popover */
23
- children: React.ReactNode;
22
+ /** The content of the tooltip */
23
+ content: React.ReactNode;
24
24
  /** Use this prop if you want to control the open state */
25
25
  isOpen?: boolean;
26
- /** Whether or not the popover is open by default */
26
+ /** Whether or not the tooltip is open by default */
27
27
  defaultIsOpen?: boolean;
28
28
  /**
29
- * Where the popover should be placed by default.
29
+ * Where the tooltip should be placed by default.
30
30
  *
31
31
  * Note - this is a suggestion, and may be overridden by space concerns.
32
32
  */
33
33
  placement?: "top" | "bottom" | "left" | "right";
34
34
  /**
35
35
  * The amount of spacing.
36
- * Popovers with lots of content should be `lg`. Defaults to `sm`.
36
+ * Tooltips with lots of content should be `lg`. Defaults to `sm`.
37
37
  **/
38
38
  size?: "sm" | "lg";
39
- borderRadius?: string;
40
39
  };
41
- /** A basic popover component for basic content */
42
- export const SimplePopover = ({
40
+ /** A tooltip component. */
41
+ export const Tooltip = ({
43
42
  children,
44
- triggerElement,
43
+ content,
45
44
  onClose,
46
45
  isOpen,
47
46
  defaultIsOpen,
48
47
  placement = "bottom",
49
48
  size = "sm",
50
49
  withCloseButton = false,
51
- borderRadius,
52
50
  ...props
53
- }: SimplePopoverProps) => {
51
+ }: TooltipProps) => {
54
52
  return (
55
53
  <DarkMode>
56
54
  <Popover
@@ -63,11 +61,11 @@ export const SimplePopover = ({
63
61
  arrowShadowColor="none"
64
62
  {...props}
65
63
  >
66
- {triggerElement && <PopoverTrigger>{triggerElement}</PopoverTrigger>}
67
- <PopoverContent borderRadius={borderRadius}>
64
+ <PopoverTrigger>{children}</PopoverTrigger>
65
+ <PopoverContent>
68
66
  <PopoverArrow />
69
67
  {withCloseButton && <PopoverCloseButton />}
70
- <PopoverBody>{children}</PopoverBody>
68
+ <PopoverBody>{content}</PopoverBody>
71
69
  </PopoverContent>
72
70
  </Popover>
73
71
  </DarkMode>
@@ -0,0 +1 @@
1
+ export * from "./Tooltip";
@@ -1,91 +0,0 @@
1
- import { PopoverBody, usePopoverContext } from "@chakra-ui/react";
2
- import { ArrowRightFill18Icon } from "@vygruppen/spor-icon-react";
3
- import * as React from "react";
4
- import { Box, Button, Flex, Stack, createTexts, useTranslation } from "..";
5
-
6
- export type PopoverWizardProps = {
7
- /** Each child will be their own step */
8
- children: React.ReactNode;
9
- };
10
- /** A popover wizard is great for showing new features one by one */
11
- export const PopoverWizardBody = ({ children }: PopoverWizardProps) => {
12
- const [currentStep, setCurrentStep] = React.useState(1);
13
- const totalSteps = React.Children.count(children);
14
- const { isOpen } = usePopoverContext();
15
- React.useEffect(() => {
16
- if (!isOpen && currentStep > 1) {
17
- const id = setTimeout(() => setCurrentStep(1), 500);
18
- return () => clearTimeout(id);
19
- }
20
- }, [isOpen, currentStep]);
21
- return (
22
- <PopoverBody>
23
- <Stack spacing={1.5}>
24
- <Box>{React.Children.toArray(children)[currentStep - 1]}</Box>
25
- <Flex gap={3}>
26
- <StepIndicator totalSteps={totalSteps} currentStep={currentStep} />
27
- <NextStepButton
28
- isLastStep={totalSteps === currentStep}
29
- onNext={() => setCurrentStep((prev) => prev + 1)}
30
- />
31
- </Flex>
32
- </Stack>
33
- </PopoverBody>
34
- );
35
- };
36
-
37
- type StepIndicatorProps = { totalSteps: number; currentStep: number };
38
- const StepIndicator = ({ totalSteps, currentStep }: StepIndicatorProps) => {
39
- const steps = createRange(1, totalSteps);
40
- return (
41
- <Flex gap={1} alignItems="center">
42
- {steps.map((step) => (
43
- <Box
44
- key={step}
45
- width={1}
46
- height={1}
47
- borderRadius="50%"
48
- transition="medium"
49
- transitionProperty="background-color"
50
- backgroundColor={step === currentStep ? "seaMist" : "greenHaze"}
51
- />
52
- ))}
53
- </Flex>
54
- );
55
- };
56
-
57
- const createRange = (start: number, end: number) => {
58
- return new Array(end - start + 1).fill(null).map((_, i) => i + start);
59
- };
60
-
61
- type NextStepButtonProps = { isLastStep: boolean; onNext: () => void };
62
- const NextStepButton = ({ isLastStep, onNext }: NextStepButtonProps) => {
63
- const { onClose } = usePopoverContext();
64
- const { t } = useTranslation();
65
- return (
66
- <Button
67
- variant="tertiary"
68
- size="sm"
69
- color="white"
70
- leftIcon={isLastStep ? undefined : <ArrowRightFill18Icon />}
71
- onClick={isLastStep ? onClose : onNext}
72
- >
73
- {t(isLastStep ? texts.finish : texts.nextStep)}
74
- </Button>
75
- );
76
- };
77
-
78
- const texts = createTexts({
79
- nextStep: {
80
- nb: "Neste",
81
- nn: "Neste",
82
- sv: "Nästa",
83
- en: "Next",
84
- },
85
- finish: {
86
- nb: "Fullfør",
87
- nn: "Fullfør",
88
- sv: "Fullför",
89
- en: "Finish",
90
- },
91
- });