@vygruppen/spor-react 13.0.2 → 13.0.3

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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vygruppen/spor-react",
3
3
  "type": "module",
4
- "version": "13.0.2",
4
+ "version": "13.0.3",
5
5
  "exports": {
6
6
  ".": {
7
7
  "types": "./dist/index.d.ts",
@@ -47,8 +47,8 @@
47
47
  "react-swipeable": "^7.0.1",
48
48
  "usehooks-ts": "^3.1.0",
49
49
  "@vygruppen/spor-design-tokens": "4.3.3",
50
- "@vygruppen/spor-icon-react": "5.0.0",
51
- "@vygruppen/spor-loader": "0.7.0"
50
+ "@vygruppen/spor-loader": "0.7.0",
51
+ "@vygruppen/spor-icon-react": "5.0.0"
52
52
  },
53
53
  "devDependencies": {
54
54
  "@react-types/datepicker": "^3.10.0",
@@ -0,0 +1,33 @@
1
+ import { Box, Tag as ChakraTag, TagRootProps } from "@chakra-ui/react";
2
+ import { IconComponent } from "@vygruppen/spor-icon-react";
3
+ import React from "react";
4
+
5
+ export type InputChipProps = TagRootProps & {
6
+ startIcon?: IconComponent;
7
+ endIcon?: IconComponent;
8
+ onClick?: VoidFunction;
9
+ };
10
+
11
+ export const InputChip = ({
12
+ startIcon,
13
+ endIcon,
14
+ children,
15
+ ref,
16
+ ...rest
17
+ }: InputChipProps & { ref?: React.Ref<HTMLDivElement> }) => {
18
+ return (
19
+ <ChakraTag.Root ref={ref} {...rest} as="button">
20
+ {startIcon && (
21
+ <ChakraTag.StartElement>
22
+ <Box as={startIcon} />
23
+ </ChakraTag.StartElement>
24
+ )}
25
+ <ChakraTag.Label>{children}</ChakraTag.Label>
26
+ {endIcon && (
27
+ <ChakraTag.EndElement>
28
+ <Box as={endIcon} />
29
+ </ChakraTag.EndElement>
30
+ )}
31
+ </ChakraTag.Root>
32
+ );
33
+ };
@@ -16,7 +16,8 @@ export interface PasswordVisibilityProps {
16
16
  }
17
17
 
18
18
  export interface PasswordInputProps
19
- extends InputProps, PasswordVisibilityProps {
19
+ extends InputProps,
20
+ PasswordVisibilityProps {
20
21
  rootProps?: InputProps;
21
22
  }
22
23
 
@@ -8,6 +8,8 @@ import type {
8
8
  } from "@chakra-ui/react";
9
9
  import {
10
10
  Box,
11
+ Checkbox as ChakraCheckbox,
12
+ Flex,
11
13
  Portal,
12
14
  Select as ChakraSelect,
13
15
  useSelectContext,
@@ -21,6 +23,7 @@ import * as React from "react";
21
23
 
22
24
  import { CloseButton } from "@/button";
23
25
 
26
+ import { Badge } from "..";
24
27
  import { Field, FieldProps } from "./Field";
25
28
 
26
29
  export type SelectProps = ChakraSelectRootProps &
@@ -95,7 +98,7 @@ export const Select = ({
95
98
  position="relative"
96
99
  >
97
100
  <SelectTrigger data-attachable>
98
- <SelectValueText withPlaceholder={label ? true : false} />
101
+ <SelectValueText withPlaceholder={!!label} />
99
102
  </SelectTrigger>
100
103
  {label && <SelectLabel css={styles.label}>{label}</SelectLabel>}
101
104
  <SelectContent css={styles.selectContent} baseStyle={css}>
@@ -112,7 +115,7 @@ export const SelectLabel = (props: SelectLabelProps) => {
112
115
  return (
113
116
  <ChakraSelect.Label
114
117
  {...props}
115
- data-selected={value.length > 0 ? true : undefined}
118
+ data-selected={value.length > 0 || undefined}
116
119
  />
117
120
  );
118
121
  };
@@ -131,8 +134,19 @@ export const SelectItem = ({
131
134
  const { item, children, description, ...rest } = props;
132
135
  const recipe = useSlotRecipe({ key: "select" });
133
136
  const styles = recipe();
137
+ const selectContext = useSelectContext();
138
+ const multiple = selectContext.multiple;
139
+ const isSelected = selectContext.value.includes(item.value);
140
+
134
141
  return (
135
142
  <ChakraSelect.Item item={item} {...rest} ref={ref} css={styles.item}>
143
+ {multiple && (
144
+ <ChakraCheckbox.Root checked={isSelected} pointerEvents="none">
145
+ <ChakraCheckbox.Control>
146
+ <ChakraCheckbox.Indicator />
147
+ </ChakraCheckbox.Control>
148
+ </ChakraCheckbox.Root>
149
+ )}
136
150
  <Box width="100%">
137
151
  <ChakraSelect.ItemText display="flex">{children}</ChakraSelect.ItemText>
138
152
  {description && (
@@ -142,12 +156,15 @@ export const SelectItem = ({
142
156
  )}
143
157
  </Box>
144
158
 
145
- <ChakraSelect.ItemIndicator>
146
- <CheckmarkFill18Icon />
147
- </ChakraSelect.ItemIndicator>
159
+ {!multiple && (
160
+ <ChakraSelect.ItemIndicator>
161
+ <CheckmarkFill18Icon />
162
+ </ChakraSelect.ItemIndicator>
163
+ )}
148
164
  </ChakraSelect.Item>
149
165
  );
150
166
  };
167
+ SelectItem.displayName = "SelectItem";
151
168
 
152
169
  type SelectItemGroupProps = ChakraSelect.ItemGroupProps & {
153
170
  label: React.ReactNode;
@@ -249,6 +266,7 @@ type SelectValueTextProps = Omit<ChakraSelect.ValueTextProps, "children"> & {
249
266
  children?(items: CollectionItem[]): React.ReactNode;
250
267
  placeholder?: string;
251
268
  withPlaceholder?: boolean;
269
+ multiple?: boolean;
252
270
  };
253
271
 
254
272
  export const SelectValueText = function SelectValueText({
@@ -258,6 +276,10 @@ export const SelectValueText = function SelectValueText({
258
276
  ref?: React.Ref<HTMLSpanElement>;
259
277
  }) {
260
278
  const { children, withPlaceholder, placeholder, ...rest } = props;
279
+
280
+ const selectContext = useSelectContext();
281
+ const multiple = selectContext.multiple;
282
+
261
283
  return (
262
284
  <ChakraSelect.ValueText
263
285
  {...rest}
@@ -277,9 +299,24 @@ export const SelectValueText = function SelectValueText({
277
299
  const items = select.selectedItems;
278
300
  if (items.length === 0) return placeholder;
279
301
  if (children) return children(items);
280
- if (items.length === 1)
302
+ if (multiple) {
303
+ return (
304
+ <Flex gap={0.5} marginBottom={1}>
305
+ {items.map((item) => (
306
+ <Badge
307
+ key={select.collection.stringifyItem(item)}
308
+ size="sm"
309
+ colorPalette="green"
310
+ >
311
+ {select.collection.stringifyItem(item)}
312
+ </Badge>
313
+ ))}
314
+ </Flex>
315
+ );
316
+ }
317
+ if (items.length === 1) {
281
318
  return select.collection.stringifyItem(items[0]);
282
- return `${items.length} selected`;
319
+ }
283
320
  }}
284
321
  </ChakraSelect.Context>
285
322
  </ChakraSelect.ValueText>
@@ -8,6 +8,7 @@ export * from "./Combobox";
8
8
  export * from "./Field";
9
9
  export * from "./Fieldset";
10
10
  export * from "./Input";
11
+ export * from "./InputChip";
11
12
  export * from "./ListBox";
12
13
  export * from "./Menu";
13
14
  export * from "./NativeSelect";
@@ -286,6 +286,8 @@ export const comboboxAnatomy = arkComboboxAnatomy.extendWith(
286
286
  "empty",
287
287
  );
288
288
 
289
+ export const tagAnatomy = createAnatomy("tag").parts("root");
290
+
289
291
  export const menuAnatomy = createAnatomy("menu").parts(
290
292
  "trigger",
291
293
  "content",
@@ -13,6 +13,7 @@ import { drawerSlotRecipe } from "./drawer";
13
13
  import { fieldSlotRecipe } from "./field";
14
14
  import { floatingActionButtonSlotRecipe } from "./floating-action-button";
15
15
  import { infoTagSlotRecipe } from "./info-tag";
16
+ import { inputChipSlotRecipe } from "./input-chip";
16
17
  import { lineIconSlotRecipe } from "./line-icon";
17
18
  import { listSlotRecipe } from "./list";
18
19
  import { listBoxSlotRecipe } from "./listbox";
@@ -70,5 +71,6 @@ export const slotRecipes = {
70
71
  checkboxCard: choiceChipSlotRecipe,
71
72
  collapsible: collapsibleSlotRecipe,
72
73
  tooltip: popoverSlotRecipe,
74
+ tag: inputChipSlotRecipe,
73
75
  menu: menuSlotRecipe,
74
76
  };
@@ -0,0 +1,118 @@
1
+ import { defineSlotRecipe } from "@chakra-ui/react";
2
+
3
+ import { tagAnatomy } from "./anatomy";
4
+
5
+ export const inputChipSlotRecipe = defineSlotRecipe({
6
+ slots: tagAnatomy.keys(),
7
+ className: "chakra-tag",
8
+ base: {
9
+ root: {
10
+ display: "flex",
11
+ direction: "row",
12
+ width: "fit-content",
13
+ height: "fit-content",
14
+ alignItems: "center",
15
+ justifyContent: "center",
16
+ gap: "1",
17
+ outline: "none",
18
+ "&:focus": {
19
+ outline: "2px solid",
20
+ outlineColor: "outline.focus",
21
+ },
22
+ },
23
+ },
24
+ variants: {
25
+ variant: {
26
+ core: {
27
+ root: {
28
+ backgroundColor: "surface",
29
+ border: "1px solid",
30
+ borderColor: "outline",
31
+ "&:hover": {
32
+ outline: "2px solid",
33
+ outlineColor: "core.outline.hover",
34
+ },
35
+ "&:active": {
36
+ outline: "none",
37
+ backgroundColor: "core.surface.active",
38
+ },
39
+ },
40
+ },
41
+ accent: {
42
+ root: {
43
+ backgroundColor: "accent.surface",
44
+ color: "text.highlight",
45
+ "& svg": {
46
+ color: "icon.highlight",
47
+ },
48
+ "&:hover": {
49
+ backgroundColor: "accent.surface.hover",
50
+ },
51
+ "&:active": {
52
+ backgroundColor: "accent.surface.active",
53
+ outline: "none",
54
+ },
55
+ },
56
+ },
57
+ brand: {
58
+ root: {
59
+ backgroundColor: "brand.surface",
60
+ color: "text.inverted",
61
+ "& svg": {
62
+ color: "icon.inverted",
63
+ },
64
+ "&:hover": {
65
+ backgroundColor: "brand.surface.hover",
66
+ },
67
+ "&:active": {
68
+ backgroundColor: "brand.surface.active",
69
+ outline: "none",
70
+ },
71
+ },
72
+ },
73
+ },
74
+ size: {
75
+ xs: {
76
+ root: {
77
+ fontSize: "desktop.xs",
78
+ paddingX: "1.5",
79
+ paddingY: "0",
80
+ fontWeight: "normal",
81
+ borderRadius: "xs",
82
+ },
83
+ },
84
+ sm: {
85
+ root: {
86
+ fontSize: "desktop.sm",
87
+ paddingX: "2",
88
+ paddingY: "0.5",
89
+ fontWeight: "bold",
90
+ borderRadius: "9px",
91
+ },
92
+ },
93
+ md: {
94
+ root: {
95
+ padding: 5,
96
+ fontSize: "desktop.md",
97
+ paddingX: "2",
98
+ paddingY: "1",
99
+ fontWeight: "bold",
100
+ borderRadius: "sm",
101
+ },
102
+ },
103
+ lg: {
104
+ root: {
105
+ fontSize: "desktop.md",
106
+ paddingX: "2",
107
+ paddingY: "3",
108
+ fontWeight: "bold",
109
+ borderRadius: "md",
110
+ },
111
+ },
112
+ },
113
+ },
114
+ defaultVariants: {
115
+ variant: "core",
116
+ size: "sm",
117
+ },
118
+ });
@@ -117,7 +117,7 @@ export const selectSlotRecipe = defineSlotRecipe({
117
117
  outlineOffset: "2px",
118
118
  outline: "2px solid",
119
119
  outlineColor: "outline.focus",
120
- backgroundColor: "ghost.surface.hover",
120
+ backgroundColor: "accent.surface.hover",
121
121
  },
122
122
  "&[data-highlighted]": {
123
123
  outlineOffset: "2px",
@@ -125,7 +125,7 @@ export const selectSlotRecipe = defineSlotRecipe({
125
125
  outlineColor: "outline.focus",
126
126
  },
127
127
  _active: {
128
- backgroundColor: "ghost.surface.active",
128
+ backgroundColor: "accent.surface.active",
129
129
  },
130
130
  _highlighted: {
131
131
  _active: {
@@ -133,12 +133,12 @@ export const selectSlotRecipe = defineSlotRecipe({
133
133
  },
134
134
  },
135
135
  _hover: {
136
- backgroundColor: "ghost.surface.hover",
136
+ backgroundColor: "accent.surface.hover",
137
137
  outline: "2px solid core.outline",
138
138
  outlineOffset: "2px",
139
139
  },
140
140
  _selected: {
141
- backgroundColor: "ghost.surface.active",
141
+ backgroundColor: "accent.surface",
142
142
  },
143
143
  _icon: {
144
144
  width: 3,