@vygruppen/spor-react 9.8.2 → 9.9.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,6 +1,8 @@
1
1
  import {
2
2
  Box,
3
3
  chakra,
4
+ Flex,
5
+ FormLabel,
4
6
  ResponsiveValue,
5
7
  useFormControlProps,
6
8
  useMultiStyleConfig,
@@ -11,7 +13,7 @@ import {
11
13
  } from "@vygruppen/spor-icon-react";
12
14
  import React, { useRef } from "react";
13
15
  import { HiddenSelect, useButton, useSelect } from "react-aria";
14
- import { useSelectState } from "react-stately";
16
+ import { Column, useSelectState } from "react-stately";
15
17
  import { createTexts, useTranslation } from "../";
16
18
  import { ListBox } from "./ListBox";
17
19
  import { Popover } from "./Popover";
@@ -152,13 +154,11 @@ type InfoSelectProps<T extends object> = {
152
154
  */
153
155
  export function InfoSelect<T extends object>({
154
156
  placeholder,
155
- width = "100%",
156
- height = "auto",
157
157
  onChange,
158
158
  value,
159
159
  isLabelSrOnly,
160
160
  defaultValue,
161
- variant = "base",
161
+ variant,
162
162
  ...props
163
163
  }: InfoSelectProps<T>) {
164
164
  const renamedProps = {
@@ -188,11 +188,10 @@ export function InfoSelect<T extends object>({
188
188
  const { t } = useTranslation();
189
189
  const formControl = useFormControlProps(props);
190
190
 
191
+ const hasChosenValue = state.selectedItem !== null;
192
+
191
193
  return (
192
194
  <Box sx={styles.container}>
193
- <chakra.div {...labelProps} sx={styles.label}>
194
- {props.label}
195
- </chakra.div>
196
195
  <HiddenSelect
197
196
  state={state}
198
197
  triggerRef={triggerRef}
@@ -205,17 +204,37 @@ export function InfoSelect<T extends object>({
205
204
  ref={triggerRef}
206
205
  sx={styles.button}
207
206
  {...buttonProps}
208
- width={width}
209
- height={height}
210
207
  data-attachable
211
208
  aria-invalid={formControl.isInvalid}
212
209
  aria-describedby={formControl["aria-describedby"]}
213
210
  >
214
- <Box {...valueProps}>
215
- {state.selectedItem
216
- ? state.selectedItem.textValue ?? state.selectedItem.rendered
217
- : placeholder ?? t(texts.selectAnOption)}
218
- </Box>
211
+ <chakra.div sx={styles.innerButton}>
212
+ <chakra.div
213
+ {...labelProps}
214
+ sx={{
215
+ ...styles.label,
216
+ ...(hasChosenValue && {
217
+ transform: "scale(0.825) translateY(-10px) translateX(-10%)",
218
+ transitionProperty: "var(--spor-transition-property-common)",
219
+ transitionDuration: "var(--spor-transition-duration-normal)",
220
+ }),
221
+ }}
222
+ >
223
+ {props.label}
224
+ </chakra.div>
225
+ <Box
226
+ {...valueProps}
227
+ h={!hasChosenValue ? "0px" : "18px"}
228
+ hidden={!hasChosenValue}
229
+ transform={"scale(1) translateY(-10px)"}
230
+ transitionProperty={"var(--spor-transition-property-common)"}
231
+ transitionDuration={"var(--spor-transition-duration-normal)"}
232
+ >
233
+ {state.selectedItem
234
+ ? state.selectedItem.textValue ?? state.selectedItem.rendered
235
+ : placeholder ?? t(texts.selectAnOption)}
236
+ </Box>
237
+ </chakra.div>
219
238
  <Box sx={styles.arrowIcon}>
220
239
  {state.isOpen ? <DropdownUpFill24Icon /> : <DropdownDownFill24Icon />}
221
240
  </Box>
@@ -185,6 +185,7 @@ const OptionContext = React.createContext<OptionContextValue>({
185
185
  labelProps: {},
186
186
  descriptionProps: {},
187
187
  });
188
+
188
189
  const useOptionContext = () => {
189
190
  return useContext(OptionContext);
190
191
  };
@@ -9,7 +9,7 @@ import {
9
9
 
10
10
  type PressableCardProps = BoxProps & {
11
11
  /** Defaults to "base" */
12
- variant: "floating" | "accent" | "base";
12
+ variant?: "floating" | "accent" | "base";
13
13
  };
14
14
 
15
15
  /**
@@ -43,7 +43,7 @@ type PressableCardProps = BoxProps & {
43
43
  */
44
44
 
45
45
  export const PressableCard = forwardRef<PressableCardProps, As>(
46
- ({ children, variant = "base", ...props }, ref) => {
46
+ ({ children, variant = "floating", ...props }, ref) => {
47
47
  const styles = useStyleConfig("PressableCard", {
48
48
  variant,
49
49
  });
@@ -9,7 +9,7 @@ import {
9
9
  import { dataAttr } from "@chakra-ui/utils";
10
10
  import React, { useId } from "react";
11
11
 
12
- type RadioCardProps = UseRadioProps &
12
+ export type RadioCardProps = UseRadioProps &
13
13
  BoxProps & {
14
14
  children: React.ReactNode;
15
15
  /** Defaults to "base" */
@@ -5,6 +5,7 @@ import {
5
5
  Stack,
6
6
  } from "@chakra-ui/react";
7
7
  import React, { Children } from "react";
8
+ import { RadioCard, RadioCardProps } from "./RadioCard";
8
9
 
9
10
  type RadioCardGroupProps = RadioGroupProps & {
10
11
  children: React.ReactNode;
@@ -78,13 +79,38 @@ export const RadioCardGroup = ({
78
79
 
79
80
  return (
80
81
  <Stack direction={direction} {...group}>
81
- {Children.map(
82
- children as React.ReactElement[],
83
- (child: React.ReactElement) => {
82
+ {recursiveMap(children, (child: React.ReactElement) => {
83
+ if (child.type === RadioCard) {
84
84
  const radio = getRadioProps({ value: child.props.value });
85
- return React.cloneElement(child, { ...radio, variant, ...props });
86
- },
87
- )}
85
+ const variantValue = variant as "base" | "floating" | undefined;
86
+ return React.cloneElement(
87
+ child as React.ReactElement<RadioCardProps>,
88
+ { ...radio, variant: variantValue, ...props },
89
+ );
90
+ }
91
+ return child;
92
+ })}
88
93
  </Stack>
89
94
  );
90
95
  };
96
+
97
+ function recursiveMap(
98
+ children: React.ReactNode,
99
+ fn: (child: React.ReactElement) => React.ReactElement,
100
+ ): React.ReactNode {
101
+ return React.Children.map(children, (child) => {
102
+ // If this child is a React element and has children, recurse
103
+ if (React.isValidElement(child) && child.props.children) {
104
+ child = React.cloneElement(child as React.ReactElement<any>, {
105
+ children: recursiveMap(child.props.children, fn),
106
+ });
107
+ }
108
+
109
+ // Apply the function to the child (if it's a React element)
110
+ if (React.isValidElement(child)) {
111
+ return fn(child);
112
+ }
113
+
114
+ return child;
115
+ });
116
+ }
@@ -70,7 +70,6 @@ export const StaticCard = forwardRef<StaticCardProps, As>(
70
70
  return (
71
71
  <Box __css={styles} {...props} ref={ref}>
72
72
  {children}
73
- <Card />
74
73
  </Box>
75
74
  );
76
75
  },
@@ -22,6 +22,7 @@ const config = helpers.defineMultiStyleConfig({
22
22
  position: "absolute",
23
23
  top: 1,
24
24
  right: 1,
25
+ color: "darkGrey",
25
26
  },
26
27
  },
27
28
  variants: {
@@ -19,18 +19,28 @@ const config = helpers.defineMultiStyleConfig({
19
19
  container: {},
20
20
  label: {
21
21
  position: "relative",
22
+ fontSize: ["mobile.xs", "desktop.sm"],
22
23
  ...(props.isLabelSrOnly ? srOnly : {}),
23
24
  },
25
+ innerButton: {
26
+ display: "flex",
27
+ flexDir: "column",
28
+ alignItems: "start",
29
+ justifyContent: "start",
30
+ },
24
31
  button: {
32
+ display: "flex",
25
33
  appearance: "none",
34
+ width: "100%",
35
+ height: "54px",
26
36
  borderTopRadius: "sm",
27
37
  borderBottomRadius: props.isOpen ? 0 : "sm",
28
38
  paddingY: 1.5,
29
39
  paddingX: 3,
30
- display: "flex",
31
40
  justifyContent: "space-between",
32
41
  alignItems: "center",
33
42
  fontSize: "mobile.md",
43
+ h: 8,
34
44
  ...baseBorder("default", props),
35
45
  _hover: {
36
46
  ...baseBorder("hover", props),
@@ -48,6 +58,7 @@ const config = helpers.defineMultiStyleConfig({
48
58
  ...baseBorder("invalid", props),
49
59
  },
50
60
  },
61
+ placeholder: {},
51
62
  arrowIcon: {},
52
63
  }),
53
64
  variants: {
@@ -2,7 +2,7 @@ import { anatomy } from "@chakra-ui/anatomy";
2
2
  import { createMultiStyleConfigHelpers } from "@chakra-ui/react";
3
3
  import { mode } from "@chakra-ui/theme-tools";
4
4
  import { baseBorder } from "../utils/base-utils";
5
- import { ghostBackground } from "../utils/ghost-utils";
5
+ import { ghostBackground, ghostText } from "../utils/ghost-utils";
6
6
  import { surface } from "../utils/surface-utils";
7
7
 
8
8
  const parts = anatomy("ListBox").parts(
@@ -32,7 +32,7 @@ const config = helpers.defineMultiStyleConfig({
32
32
  marginY: 1,
33
33
  marginX: 1,
34
34
  borderRadius: "sm",
35
- color: mode("darkGrey", "white")(props),
35
+ ...ghostText("default", props),
36
36
  cursor: "pointer",
37
37
  outline: "none",
38
38
  _active: {
@@ -45,15 +45,15 @@ const config = helpers.defineMultiStyleConfig({
45
45
  ...ghostBackground("selected", props),
46
46
  },
47
47
  _selected: {
48
- ...ghostBackground("selected", props),
48
+ ...ghostBackground("active", props),
49
49
  },
50
50
  },
51
51
  label: {},
52
52
  description: {
53
53
  fontSize: ["mobile.xs", "desktop.xs"],
54
- color: mode("dimGrey", "silver")(props),
54
+ ...ghostText("default", props),
55
55
  "[aria-selected='true'] &": {
56
- color: mode("lightGrey", "platinum")(props),
56
+ ...ghostText("selected", props),
57
57
  },
58
58
  },
59
59
  }),
@@ -13,9 +13,14 @@ const config = defineStyleConfig({
13
13
  display: "block",
14
14
  borderRadius: "md",
15
15
  cursor: "pointer",
16
- ...focusVisibleStyles(props),
16
+ transitionProperty: "common",
17
+ transitionDuration: "fast",
18
+ "button&, a&, label&, &.is-clickable": {
19
+ ...focusVisibleStyles(props),
20
+ },
17
21
  _disabled: {
18
22
  ...baseBackground("disabled", props),
23
+ ...baseBorder("disabled", props),
19
24
  ...baseText("disabled", props),
20
25
  outline: "none",
21
26
  pointerEvents: "none",
@@ -17,12 +17,10 @@ const config = helpers.defineMultiStyleConfig({
17
17
  borderCollapse: "collapse",
18
18
  ...baseText("default", props),
19
19
  width: "100%",
20
- minWidth: "600px",
21
20
  },
22
21
  th: {
23
22
  fontWeight: "bold",
24
23
  textAlign: "start",
25
- minWidth: "68px",
26
24
  },
27
25
  td: {
28
26
  textAlign: "start",
@@ -25,14 +25,14 @@ export function floatingBackground(
25
25
  return {
26
26
  backgroundColor: mode(
27
27
  "floating.surface.hover.light",
28
- "floating.surface.hover.dark",
28
+ `color-mix(in srgb, white 10%, var(--spor-colors-bg-default-dark))`,
29
29
  )(props),
30
30
  };
31
31
  case "default":
32
32
  return {
33
33
  backgroundColor: mode(
34
- "floating.surface.default.light",
35
- "floating.surface.default.dark",
34
+ "white",
35
+ `color-mix(in srgb, white 10%, var(--spor-colors-bg-default-dark))`,
36
36
  )(props),
37
37
  };
38
38
  }
@@ -29,7 +29,10 @@ export function ghostBackground(
29
29
  };
30
30
  case "selected": {
31
31
  return {
32
- backgroundColor: mode("mint", "whiteAlpha.200")(props),
32
+ backgroundColor: mode(
33
+ "ghost.surface.selected.light",
34
+ "ghost.surface.selected.dark",
35
+ )(props),
33
36
  };
34
37
  }
35
38
  case "default":
@@ -38,3 +41,18 @@ export function ghostBackground(
38
41
  };
39
42
  }
40
43
  }
44
+
45
+ type GhostTextState = Subset<State, "default" | "selected">;
46
+
47
+ export function ghostText(state: GhostTextState, props: StyleFunctionProps) {
48
+ switch (state) {
49
+ case "selected":
50
+ return {
51
+ color: mode("ghost.text.light", "ghost.text.dark")(props),
52
+ };
53
+ default:
54
+ return {
55
+ color: mode("ghost.text.light", "ghost.text.dark")(props),
56
+ };
57
+ }
58
+ }