@vygruppen/spor-react 13.3.1 → 13.4.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.
- package/.turbo/turbo-build.log +10 -10
- package/.turbo/turbo-postinstall.log +1 -1
- package/CHANGELOG.md +21 -0
- package/__tests__/radio.test.tsx +69 -0
- package/dist/index.cjs +274 -270
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +58 -6
- package/dist/index.d.ts +58 -6
- package/dist/index.mjs +275 -271
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -3
- package/setupTests.ts +84 -0
- package/src/accordion/types.ts +8 -6
- package/src/input/Combobox.tsx +1 -0
- package/src/input/CountryCodeSelect.tsx +1 -0
- package/src/input/Field.tsx +8 -6
- package/src/input/FloatingLabel.tsx +2 -13
- package/src/input/Input.tsx +32 -15
- package/src/input/Label.tsx +2 -3
- package/src/input/PasswordInput.tsx +3 -1
- package/src/input/PhoneNumberInput.tsx +3 -1
- package/src/input/SearchInput.tsx +14 -3
- package/src/input/Select.tsx +23 -33
- package/src/theme/recipes/badge.ts +41 -155
- package/src/theme/recipes/input.ts +15 -4
- package/src/theme/slot-recipes/anatomy.ts +1 -3
- package/src/theme/slot-recipes/breadcrumb.ts +0 -1
- package/src/theme/slot-recipes/field.ts +38 -4
- package/src/theme/slot-recipes/native-select.ts +15 -0
- package/src/theme/slot-recipes/select.ts +88 -35
- package/src/typography/Badge.tsx +2 -6
- package/tsconfig.json +1 -3
- package/vitest.config.ts +12 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vygruppen/spor-react",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "13.
|
|
4
|
+
"version": "13.4.0",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -46,23 +46,29 @@
|
|
|
46
46
|
"react-stately": "^3.31.1",
|
|
47
47
|
"react-swipeable": "^7.0.1",
|
|
48
48
|
"usehooks-ts": "^3.1.0",
|
|
49
|
-
"@vygruppen/spor-design-tokens": "5.0.
|
|
49
|
+
"@vygruppen/spor-design-tokens": "5.0.4",
|
|
50
50
|
"@vygruppen/spor-icon-react": "5.0.0",
|
|
51
51
|
"@vygruppen/spor-loader": "0.7.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@react-types/datepicker": "^3.10.0",
|
|
55
55
|
"@react-types/shared": "^3.27.0",
|
|
56
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
57
|
+
"@testing-library/react": "^16.3.2",
|
|
58
|
+
"@testing-library/user-event": "^14.6.1",
|
|
56
59
|
"@types/node": "^22.13.4",
|
|
57
60
|
"@types/react": "19.2.3",
|
|
58
61
|
"@types/react-dom": "19.2.3",
|
|
62
|
+
"@vitejs/plugin-react": "^6.0.2",
|
|
59
63
|
"clsx": "^2.1.1",
|
|
60
64
|
"concurrently": "^9.1.2",
|
|
61
65
|
"eslint": "^9.39.1",
|
|
66
|
+
"jsdom": "^29.1.1",
|
|
62
67
|
"react": "19.2.3",
|
|
63
68
|
"react-dom": "19.2.3",
|
|
64
69
|
"tsup": "^7.2.0",
|
|
65
70
|
"typescript": "^5.7.3",
|
|
71
|
+
"vitest": "^4.1.8",
|
|
66
72
|
"@vygruppen/eslint-config": "2.2.0",
|
|
67
73
|
"@vygruppen/tsconfig": "0.1.1"
|
|
68
74
|
},
|
|
@@ -77,6 +83,7 @@
|
|
|
77
83
|
"typegen": "npx @chakra-ui/cli typegen src/theme/index.ts",
|
|
78
84
|
"typegen:watch": "npx @chakra-ui/cli typegen src/theme/index.ts --watch",
|
|
79
85
|
"typegen:strict": "npx @chakra-ui/cli typegen /src/theme/index.ts --strict",
|
|
80
|
-
"lint": "eslint ."
|
|
86
|
+
"lint": "eslint .",
|
|
87
|
+
"test": "vitest run"
|
|
81
88
|
}
|
|
82
89
|
}
|
package/setupTests.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import "@testing-library/jest-dom";
|
|
2
|
+
|
|
3
|
+
// jsdom doesn't implement window.matchMedia, which is required by next-themes.
|
|
4
|
+
Object.defineProperty(globalThis, "matchMedia", {
|
|
5
|
+
writable: true,
|
|
6
|
+
value: (query: string) => ({
|
|
7
|
+
matches: false,
|
|
8
|
+
media: query,
|
|
9
|
+
onchange: null,
|
|
10
|
+
addListener: () => {},
|
|
11
|
+
removeListener: () => {},
|
|
12
|
+
addEventListener: () => {},
|
|
13
|
+
removeEventListener: () => {},
|
|
14
|
+
dispatchEvent: () => false,
|
|
15
|
+
}),
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// jsdom doesn't implement HTMLCanvasElement.getContext(), which causes lottie-web to crash.
|
|
19
|
+
// Return a no-op mock context so canvas-based animations are silently skipped in tests.
|
|
20
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
21
|
+
(HTMLCanvasElement.prototype as any).getContext = () =>
|
|
22
|
+
({
|
|
23
|
+
fillStyle: "",
|
|
24
|
+
strokeStyle: "",
|
|
25
|
+
lineWidth: 0,
|
|
26
|
+
lineCap: "",
|
|
27
|
+
lineJoin: "",
|
|
28
|
+
globalAlpha: 1,
|
|
29
|
+
globalCompositeOperation: "",
|
|
30
|
+
shadowBlur: 0,
|
|
31
|
+
shadowColor: "",
|
|
32
|
+
shadowOffsetX: 0,
|
|
33
|
+
shadowOffsetY: 0,
|
|
34
|
+
font: "",
|
|
35
|
+
textAlign: "",
|
|
36
|
+
textBaseline: "",
|
|
37
|
+
canvas: document.createElement("canvas"),
|
|
38
|
+
save: () => {},
|
|
39
|
+
restore: () => {},
|
|
40
|
+
scale: () => {},
|
|
41
|
+
rotate: () => {},
|
|
42
|
+
translate: () => {},
|
|
43
|
+
transform: () => {},
|
|
44
|
+
setTransform: () => {},
|
|
45
|
+
resetTransform: () => {},
|
|
46
|
+
clearRect: () => {},
|
|
47
|
+
fillRect: () => {},
|
|
48
|
+
strokeRect: () => {},
|
|
49
|
+
beginPath: () => {},
|
|
50
|
+
closePath: () => {},
|
|
51
|
+
moveTo: () => {},
|
|
52
|
+
lineTo: () => {},
|
|
53
|
+
bezierCurveTo: () => {},
|
|
54
|
+
quadraticCurveTo: () => {},
|
|
55
|
+
arc: () => {},
|
|
56
|
+
arcTo: () => {},
|
|
57
|
+
ellipse: () => {},
|
|
58
|
+
rect: () => {},
|
|
59
|
+
fill: () => {},
|
|
60
|
+
stroke: () => {},
|
|
61
|
+
clip: () => {},
|
|
62
|
+
drawImage: () => {},
|
|
63
|
+
createLinearGradient: () => ({ addColorStop: () => {} }),
|
|
64
|
+
createRadialGradient: () => ({ addColorStop: () => {} }),
|
|
65
|
+
createPattern: () => null,
|
|
66
|
+
measureText: () => ({ width: 0 }),
|
|
67
|
+
fillText: () => {},
|
|
68
|
+
strokeText: () => {},
|
|
69
|
+
getImageData: () => ({
|
|
70
|
+
data: new Uint8ClampedArray(0),
|
|
71
|
+
width: 0,
|
|
72
|
+
height: 0,
|
|
73
|
+
}),
|
|
74
|
+
putImageData: () => {},
|
|
75
|
+
createImageData: () => ({
|
|
76
|
+
data: new Uint8ClampedArray(0),
|
|
77
|
+
width: 0,
|
|
78
|
+
height: 0,
|
|
79
|
+
}),
|
|
80
|
+
setLineDash: () => {},
|
|
81
|
+
getLineDash: () => [],
|
|
82
|
+
isPointInPath: () => false,
|
|
83
|
+
isPointInStroke: () => false,
|
|
84
|
+
}) as unknown as CanvasRenderingContext2D;
|
package/src/accordion/types.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Accordion as ChakraAccordion,
|
|
3
|
+
AccordionItemProps,
|
|
3
4
|
AccordionRootProps as ChakraAccordionProps,
|
|
4
5
|
RecipeVariantProps,
|
|
5
6
|
} from "@chakra-ui/react";
|
|
@@ -59,9 +60,10 @@ export type ExpandableProps = Omit<
|
|
|
59
60
|
title: ReactNode;
|
|
60
61
|
};
|
|
61
62
|
|
|
62
|
-
export type ExpandableItemProps = HeadingLevel &
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
63
|
+
export type ExpandableItemProps = HeadingLevel &
|
|
64
|
+
Omit<AccordionItemProps, "title"> & {
|
|
65
|
+
value: string;
|
|
66
|
+
title: ReactNode;
|
|
67
|
+
children?: React.ReactNode;
|
|
68
|
+
startElement?: React.ReactNode;
|
|
69
|
+
};
|
package/src/input/Combobox.tsx
CHANGED
package/src/input/Field.tsx
CHANGED
|
@@ -4,12 +4,11 @@ import {
|
|
|
4
4
|
Field as ChakraField,
|
|
5
5
|
RecipeVariantProps,
|
|
6
6
|
Stack,
|
|
7
|
+
Text as ChakraText,
|
|
7
8
|
useSlotRecipe,
|
|
8
9
|
} from "@chakra-ui/react";
|
|
9
10
|
import * as React from "react";
|
|
10
11
|
|
|
11
|
-
import { Text } from "@/typography";
|
|
12
|
-
|
|
13
12
|
import { fieldSlotRecipe } from "../theme/slot-recipes/field";
|
|
14
13
|
import { FloatingLabel } from "./FloatingLabel";
|
|
15
14
|
import { Label } from "./Label";
|
|
@@ -55,6 +54,7 @@ export type FieldBaseProps = {
|
|
|
55
54
|
shouldFloat?: boolean;
|
|
56
55
|
labelAsChild?: boolean;
|
|
57
56
|
gap?: string | number;
|
|
57
|
+
size?: "sm" | "md";
|
|
58
58
|
};
|
|
59
59
|
|
|
60
60
|
export type FieldProps = Omit<
|
|
@@ -102,10 +102,11 @@ export const Field = ({
|
|
|
102
102
|
shouldFloat,
|
|
103
103
|
labelAsChild,
|
|
104
104
|
gap,
|
|
105
|
+
size = "md",
|
|
105
106
|
...rest
|
|
106
107
|
} = props;
|
|
107
108
|
const recipe = useSlotRecipe({ key: "field" });
|
|
108
|
-
const styles = recipe();
|
|
109
|
+
const styles = recipe({ size });
|
|
109
110
|
|
|
110
111
|
return (
|
|
111
112
|
<Stack ref={ref} width="100%" {...rest}>
|
|
@@ -120,7 +121,7 @@ export const Field = ({
|
|
|
120
121
|
gap={gap}
|
|
121
122
|
>
|
|
122
123
|
{label && !floatingLabel && (
|
|
123
|
-
<Label asChild={labelAsChild} aria-hidden>
|
|
124
|
+
<Label asChild={labelAsChild} aria-hidden css={styles.label}>
|
|
124
125
|
{renderLabelWithIndicator(label, labelAsChild)}
|
|
125
126
|
</Label>
|
|
126
127
|
)}
|
|
@@ -132,6 +133,7 @@ export const Field = ({
|
|
|
132
133
|
data-float={shouldFloat ? true : undefined}
|
|
133
134
|
asChild={labelAsChild}
|
|
134
135
|
aria-hidden
|
|
136
|
+
css={styles.label}
|
|
135
137
|
>
|
|
136
138
|
{renderLabelWithIndicator(label, labelAsChild)}
|
|
137
139
|
</FloatingLabel>
|
|
@@ -143,9 +145,9 @@ export const Field = ({
|
|
|
143
145
|
)}
|
|
144
146
|
</ChakraField.Root>
|
|
145
147
|
{helperText && (
|
|
146
|
-
<
|
|
148
|
+
<ChakraText data-part="helperText" css={styles.helperText}>
|
|
147
149
|
{helperText}
|
|
148
|
-
</
|
|
150
|
+
</ChakraText>
|
|
149
151
|
)}
|
|
150
152
|
</Stack>
|
|
151
153
|
);
|
|
@@ -2,30 +2,19 @@ import { defineStyle, Field, FieldLabelProps } from "@chakra-ui/react";
|
|
|
2
2
|
|
|
3
3
|
export const FloatingLabel = ({
|
|
4
4
|
ref,
|
|
5
|
+
css,
|
|
5
6
|
...props
|
|
6
7
|
}: FieldLabelProps & {
|
|
7
8
|
ref?: React.Ref<HTMLLabelElement | null>;
|
|
8
|
-
}) => <Field.Label ref={ref} {...props} css={floatingLabelStyles} />;
|
|
9
|
+
}) => <Field.Label ref={ref} {...props} css={[floatingLabelStyles, css]} />;
|
|
9
10
|
|
|
10
11
|
const floatingLabelStyles = defineStyle({
|
|
11
|
-
paddingX: 3,
|
|
12
12
|
fontWeight: "normal",
|
|
13
13
|
pointerEvents: "none",
|
|
14
14
|
zIndex: "docked",
|
|
15
15
|
_disabled: {
|
|
16
16
|
opacity: 0.4,
|
|
17
17
|
},
|
|
18
|
-
|
|
19
18
|
pos: "absolute",
|
|
20
19
|
transition: "top 160ms ease, font-size 160ms ease",
|
|
21
|
-
|
|
22
|
-
top: "0.9rem",
|
|
23
|
-
color: "text",
|
|
24
|
-
fontSize: ["mobile.sm", "desktop.sm"],
|
|
25
|
-
|
|
26
|
-
"&[data-float]": {
|
|
27
|
-
fontSize: ["mobile.2xs", "desktop.2xs"],
|
|
28
|
-
color: "text",
|
|
29
|
-
top: "0.3rem",
|
|
30
|
-
},
|
|
31
20
|
});
|
package/src/input/Input.tsx
CHANGED
|
@@ -21,17 +21,17 @@ import { Field, FieldProps } from "./Field";
|
|
|
21
21
|
import { useFloatingInputState } from "./useFLoatingInputState";
|
|
22
22
|
|
|
23
23
|
export type InputProps = FieldProps &
|
|
24
|
-
Exclude<
|
|
25
|
-
ChakraInputProps,
|
|
26
|
-
"size" | "label" | "colorPalette" | "placeholder"
|
|
27
|
-
> & {
|
|
24
|
+
Exclude<ChakraInputProps, "label" | "colorPalette" | "placeholder"> & {
|
|
28
25
|
/** The input's label */
|
|
29
26
|
label?: ReactNode;
|
|
30
27
|
/** Element that shows up to the left */
|
|
31
28
|
startElement?: React.ReactNode;
|
|
32
29
|
/** Element that shows up to the right */
|
|
33
30
|
endElement?: React.ReactNode;
|
|
31
|
+
/** Override the font size of the start and end elements */
|
|
34
32
|
fontSize?: string;
|
|
33
|
+
|
|
34
|
+
//size?: "sm" | "md";
|
|
35
35
|
};
|
|
36
36
|
/**
|
|
37
37
|
* Inputs let you enter text or other data.
|
|
@@ -75,12 +75,13 @@ export const Input = ({
|
|
|
75
75
|
hidden,
|
|
76
76
|
fontSize,
|
|
77
77
|
labelAsChild,
|
|
78
|
+
size = "md",
|
|
78
79
|
...props
|
|
79
80
|
}: InputProps & {
|
|
80
81
|
ref?: React.Ref<HTMLInputElement | null>;
|
|
81
82
|
}) => {
|
|
82
83
|
const recipe = useRecipe({ key: "input" });
|
|
83
|
-
const [recipeProps, restProps] = recipe.splitVariantProps(props);
|
|
84
|
+
const [recipeProps, restProps] = recipe.splitVariantProps({ size, ...props });
|
|
84
85
|
const styles = recipe(recipeProps);
|
|
85
86
|
|
|
86
87
|
const labelId = useId();
|
|
@@ -98,6 +99,19 @@ export const Input = ({
|
|
|
98
99
|
inputRef: inputRef as React.RefObject<HTMLInputElement>,
|
|
99
100
|
});
|
|
100
101
|
|
|
102
|
+
const fontSizeBySize: Record<string, string> = {
|
|
103
|
+
sm: "xs",
|
|
104
|
+
md: "mobile.md",
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const elementPaddingBySize: Record<string, string> = {
|
|
108
|
+
sm: "2.3rem",
|
|
109
|
+
md: "2.6rem",
|
|
110
|
+
};
|
|
111
|
+
const elementPadding = elementPaddingBySize[size as string] ?? "2.6rem";
|
|
112
|
+
const paddingLeft = elementPadding;
|
|
113
|
+
const paddingRight = elementPadding;
|
|
114
|
+
|
|
101
115
|
return (
|
|
102
116
|
<Field
|
|
103
117
|
invalid={invalid}
|
|
@@ -108,21 +122,24 @@ export const Input = ({
|
|
|
108
122
|
id={props.id}
|
|
109
123
|
labelAsChild={labelAsChild}
|
|
110
124
|
label={
|
|
111
|
-
<Flex
|
|
125
|
+
<Flex
|
|
126
|
+
id={labelId}
|
|
127
|
+
paddingX={startElement && size === "sm" ? 1 : undefined}
|
|
128
|
+
>
|
|
112
129
|
<Box visibility="hidden">{startElement}</Box>
|
|
113
130
|
{label}
|
|
114
131
|
</Flex>
|
|
115
132
|
}
|
|
116
133
|
floatingLabel={true}
|
|
117
134
|
shouldFloat={shouldFloat}
|
|
135
|
+
size={size}
|
|
118
136
|
>
|
|
119
137
|
{startElement && (
|
|
120
138
|
<InputElement
|
|
121
|
-
pointerEvents="none"
|
|
122
|
-
paddingX={2}
|
|
123
139
|
aria-hidden="true"
|
|
124
|
-
fontSize={fontSize ?? "mobile.md"}
|
|
125
140
|
aria-labelledby={labelId}
|
|
141
|
+
paddingX={2}
|
|
142
|
+
fontSize={fontSize ?? fontSizeBySize[size as string]}
|
|
126
143
|
>
|
|
127
144
|
{startElement}
|
|
128
145
|
</InputElement>
|
|
@@ -132,23 +149,23 @@ export const Input = ({
|
|
|
132
149
|
ref={inputRef}
|
|
133
150
|
focusVisibleRing="outside"
|
|
134
151
|
overflow="hidden"
|
|
135
|
-
paddingLeft={startElement ? "2.6rem" : undefined}
|
|
136
|
-
paddingRight={endElement ? "2.6rem" : undefined}
|
|
137
152
|
{...restProps}
|
|
153
|
+
css={styles}
|
|
154
|
+
paddingLeft={startElement ? paddingLeft : undefined}
|
|
155
|
+
paddingRight={endElement ? paddingRight : undefined}
|
|
138
156
|
className={`peer ${props.className || ""}`}
|
|
139
157
|
value={isControlled ? props.value : undefined}
|
|
140
158
|
onFocus={handleFocus}
|
|
141
159
|
onBlur={handleBlur}
|
|
142
160
|
onChange={handleChange}
|
|
143
161
|
placeholder=""
|
|
144
|
-
|
|
145
|
-
fontSize={fontSize ?? "mobile.md"}
|
|
162
|
+
fontSize={fontSize}
|
|
146
163
|
/>
|
|
147
164
|
{endElement && (
|
|
148
165
|
<InputElement
|
|
149
|
-
placement="end"
|
|
150
166
|
paddingX={2}
|
|
151
|
-
|
|
167
|
+
placement="end"
|
|
168
|
+
fontSize={fontSize ?? fontSizeBySize[size as string]}
|
|
152
169
|
>
|
|
153
170
|
{endElement}
|
|
154
171
|
</InputElement>
|
package/src/input/Label.tsx
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { defineStyle, Field, FieldLabelProps } from "@chakra-ui/react";
|
|
2
2
|
|
|
3
|
-
export const Label = (props: FieldLabelProps) => (
|
|
4
|
-
<Field.Label {...props} css={labelStyles} />
|
|
3
|
+
export const Label = ({ css, ...props }: FieldLabelProps) => (
|
|
4
|
+
<Field.Label {...props} css={[labelStyles, css]} />
|
|
5
5
|
);
|
|
6
6
|
|
|
7
7
|
const labelStyles = defineStyle({
|
|
8
8
|
fontWeight: "normal",
|
|
9
9
|
paddingBottom: 1,
|
|
10
10
|
paddingX: 1,
|
|
11
|
-
fontSize: ["mobile.xs", "desktop.xs"],
|
|
12
11
|
color: "text",
|
|
13
12
|
pointerEvents: "none",
|
|
14
13
|
zIndex: "docked",
|
|
@@ -55,6 +55,7 @@ export const PasswordInput = ({
|
|
|
55
55
|
onVisibleChange,
|
|
56
56
|
label,
|
|
57
57
|
startElement,
|
|
58
|
+
size = "md",
|
|
58
59
|
...rest
|
|
59
60
|
} = props;
|
|
60
61
|
|
|
@@ -82,10 +83,12 @@ export const PasswordInput = ({
|
|
|
82
83
|
event.preventDefault();
|
|
83
84
|
setVisible(!visible);
|
|
84
85
|
}}
|
|
86
|
+
size={size}
|
|
85
87
|
>
|
|
86
88
|
{visible ? t(texts.hidePassword) : t(texts.showPassword)}
|
|
87
89
|
</VisibilityTrigger>
|
|
88
90
|
}
|
|
91
|
+
size={size}
|
|
89
92
|
{...rest}
|
|
90
93
|
/>
|
|
91
94
|
);
|
|
@@ -102,7 +105,6 @@ const VisibilityTrigger = ({
|
|
|
102
105
|
ref={ref}
|
|
103
106
|
type="button"
|
|
104
107
|
fontWeight="normal"
|
|
105
|
-
size="sm"
|
|
106
108
|
borderRadius="sm"
|
|
107
109
|
marginRight={1}
|
|
108
110
|
{...props}
|
|
@@ -56,6 +56,7 @@ export const PhoneNumberInput = ({
|
|
|
56
56
|
allowedCountryCodes,
|
|
57
57
|
invalid,
|
|
58
58
|
errorText,
|
|
59
|
+
size = "md",
|
|
59
60
|
} = props;
|
|
60
61
|
|
|
61
62
|
const { t } = useTranslation();
|
|
@@ -91,11 +92,12 @@ export const PhoneNumberInput = ({
|
|
|
91
92
|
variant={variant}
|
|
92
93
|
allowedCountryCodes={allowedCountryCodes}
|
|
93
94
|
data-state="on"
|
|
94
|
-
|
|
95
|
+
size={size}
|
|
95
96
|
/>
|
|
96
97
|
<Input
|
|
97
98
|
ref={ref}
|
|
98
99
|
type="tel"
|
|
100
|
+
size={size}
|
|
99
101
|
{...props}
|
|
100
102
|
value={value.nationalNumber}
|
|
101
103
|
invalid={invalid}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
+
CloseOutline18Icon,
|
|
4
5
|
CloseOutline24Icon,
|
|
6
|
+
SearchOutline18Icon,
|
|
5
7
|
SearchOutline24Icon,
|
|
6
8
|
} from "@vygruppen/spor-icon-react";
|
|
7
9
|
|
|
@@ -25,7 +27,7 @@ export const SearchInput = ({
|
|
|
25
27
|
ref?: React.Ref<HTMLInputElement>;
|
|
26
28
|
}) => {
|
|
27
29
|
const { t } = useTranslation();
|
|
28
|
-
const { variant = "core", onReset, label, value } = props;
|
|
30
|
+
const { variant = "core", onReset, label, value, size = "md" } = props;
|
|
29
31
|
const clearButton = onReset && value;
|
|
30
32
|
|
|
31
33
|
return (
|
|
@@ -33,8 +35,15 @@ export const SearchInput = ({
|
|
|
33
35
|
ref={ref}
|
|
34
36
|
type="search"
|
|
35
37
|
variant={variant}
|
|
38
|
+
size={size}
|
|
36
39
|
{...props}
|
|
37
|
-
startElement={
|
|
40
|
+
startElement={
|
|
41
|
+
size == "md" ? (
|
|
42
|
+
<SearchOutline24Icon color="icon" />
|
|
43
|
+
) : (
|
|
44
|
+
<SearchOutline18Icon color="icon" />
|
|
45
|
+
)
|
|
46
|
+
}
|
|
38
47
|
endElement={
|
|
39
48
|
clearButton && (
|
|
40
49
|
<IconButton
|
|
@@ -42,7 +51,9 @@ export const SearchInput = ({
|
|
|
42
51
|
type="button"
|
|
43
52
|
size="sm"
|
|
44
53
|
aria-label={t(texts.reset)}
|
|
45
|
-
icon={
|
|
54
|
+
icon={
|
|
55
|
+
size == "md" ? <CloseOutline24Icon /> : <CloseOutline18Icon />
|
|
56
|
+
}
|
|
46
57
|
onClick={onReset}
|
|
47
58
|
/>
|
|
48
59
|
)
|
package/src/input/Select.tsx
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import type {
|
|
4
4
|
CollectionItem,
|
|
5
5
|
SelectLabelProps,
|
|
6
|
-
SelectRootProps
|
|
6
|
+
SelectRootProps,
|
|
7
7
|
SystemStyleObject,
|
|
8
8
|
} from "@chakra-ui/react";
|
|
9
9
|
import {
|
|
@@ -13,10 +13,10 @@ import {
|
|
|
13
13
|
Portal,
|
|
14
14
|
Select as ChakraSelect,
|
|
15
15
|
useSelectContext,
|
|
16
|
-
useSlotRecipe,
|
|
17
16
|
} from "@chakra-ui/react";
|
|
18
17
|
import {
|
|
19
18
|
CheckmarkFill18Icon,
|
|
19
|
+
DropdownDownFill18Icon,
|
|
20
20
|
DropdownDownFill24Icon,
|
|
21
21
|
} from "@vygruppen/spor-icon-react";
|
|
22
22
|
import * as React from "react";
|
|
@@ -26,7 +26,7 @@ import { CloseButton } from "@/button";
|
|
|
26
26
|
import { Badge } from "..";
|
|
27
27
|
import { Field, FieldProps } from "./Field";
|
|
28
28
|
|
|
29
|
-
export type SelectProps =
|
|
29
|
+
export type SelectProps = SelectRootProps &
|
|
30
30
|
FieldProps & {
|
|
31
31
|
label?: string;
|
|
32
32
|
};
|
|
@@ -68,6 +68,7 @@ export const Select = ({
|
|
|
68
68
|
}) => {
|
|
69
69
|
const {
|
|
70
70
|
variant = "core",
|
|
71
|
+
size = "md",
|
|
71
72
|
children,
|
|
72
73
|
positioning,
|
|
73
74
|
label,
|
|
@@ -77,8 +78,6 @@ export const Select = ({
|
|
|
77
78
|
css,
|
|
78
79
|
...rest
|
|
79
80
|
} = props;
|
|
80
|
-
const recipe = useSlotRecipe({ key: "select" });
|
|
81
|
-
const styles = recipe({ variant });
|
|
82
81
|
|
|
83
82
|
return (
|
|
84
83
|
<Field
|
|
@@ -94,16 +93,14 @@ export const Select = ({
|
|
|
94
93
|
ref={ref}
|
|
95
94
|
positioning={{ sameWidth: true, ...positioning }}
|
|
96
95
|
variant={variant}
|
|
97
|
-
|
|
96
|
+
size={size}
|
|
98
97
|
position="relative"
|
|
99
98
|
>
|
|
100
|
-
<SelectTrigger data-attachable>
|
|
99
|
+
<SelectTrigger data-attachable size={size}>
|
|
101
100
|
<SelectValueText withPlaceholder={!!label} />
|
|
102
101
|
</SelectTrigger>
|
|
103
|
-
{label && <SelectLabel
|
|
104
|
-
<SelectContent
|
|
105
|
-
{children}
|
|
106
|
-
</SelectContent>
|
|
102
|
+
{label && <SelectLabel>{label}</SelectLabel>}
|
|
103
|
+
<SelectContent baseStyle={css}>{children}</SelectContent>
|
|
107
104
|
</ChakraSelect.Root>
|
|
108
105
|
</Field>
|
|
109
106
|
);
|
|
@@ -132,14 +129,12 @@ export const SelectItem = ({
|
|
|
132
129
|
ref?: React.Ref<HTMLDivElement>;
|
|
133
130
|
}) => {
|
|
134
131
|
const { item, children, description, ...rest } = props;
|
|
135
|
-
const recipe = useSlotRecipe({ key: "select" });
|
|
136
|
-
const styles = recipe();
|
|
137
132
|
const selectContext = useSelectContext();
|
|
138
133
|
const multiple = selectContext.multiple;
|
|
139
134
|
const isSelected = selectContext.value.includes(item.value);
|
|
140
135
|
|
|
141
136
|
return (
|
|
142
|
-
<ChakraSelect.Item item={item} {...rest} ref={ref}
|
|
137
|
+
<ChakraSelect.Item item={item} {...rest} ref={ref}>
|
|
143
138
|
{multiple && (
|
|
144
139
|
<ChakraCheckbox.Root checked={isSelected} pointerEvents="none">
|
|
145
140
|
<ChakraCheckbox.Control>
|
|
@@ -149,11 +144,7 @@ export const SelectItem = ({
|
|
|
149
144
|
)}
|
|
150
145
|
<Box width="100%">
|
|
151
146
|
<ChakraSelect.ItemText display="flex">{children}</ChakraSelect.ItemText>
|
|
152
|
-
{description &&
|
|
153
|
-
<Box data-part="item-description" css={styles.itemDescription}>
|
|
154
|
-
{description}
|
|
155
|
-
</Box>
|
|
156
|
-
)}
|
|
147
|
+
{description && <Box data-part="item-description">{description}</Box>}
|
|
157
148
|
</Box>
|
|
158
149
|
|
|
159
150
|
{!multiple && (
|
|
@@ -189,29 +180,28 @@ export const SelectItemGroup = function SelectItemGroup({
|
|
|
189
180
|
type SelectTriggerProps = ChakraSelect.ControlProps & {
|
|
190
181
|
clearable?: boolean;
|
|
191
182
|
children?: React.ReactNode;
|
|
183
|
+
size: "sm" | "md";
|
|
192
184
|
};
|
|
193
185
|
|
|
194
186
|
export const SelectTrigger = function SelectTrigger({
|
|
195
187
|
ref,
|
|
188
|
+
size = "md",
|
|
196
189
|
...props
|
|
197
190
|
}: SelectTriggerProps & {
|
|
198
191
|
ref?: React.Ref<HTMLButtonElement>;
|
|
199
192
|
}) {
|
|
200
193
|
const { children, clearable, ...rest } = props;
|
|
201
|
-
const recipe = useSlotRecipe({ key: "select" });
|
|
202
|
-
const styles = recipe();
|
|
203
194
|
return (
|
|
204
|
-
<ChakraSelect.Control {...rest}
|
|
205
|
-
<ChakraSelect.Trigger ref={ref}
|
|
206
|
-
|
|
207
|
-
</ChakraSelect.Trigger>
|
|
208
|
-
<ChakraSelect.IndicatorGroup
|
|
209
|
-
css={styles.indicatorGroup}
|
|
210
|
-
data-part="indicator-group"
|
|
211
|
-
>
|
|
195
|
+
<ChakraSelect.Control {...rest}>
|
|
196
|
+
<ChakraSelect.Trigger ref={ref}>{children}</ChakraSelect.Trigger>
|
|
197
|
+
<ChakraSelect.IndicatorGroup data-part="indicator-group">
|
|
212
198
|
{clearable && <SelectClearTrigger />}
|
|
213
|
-
<Box
|
|
214
|
-
|
|
199
|
+
<Box data-part="indicator">
|
|
200
|
+
{size == "md" ? (
|
|
201
|
+
<DropdownDownFill24Icon />
|
|
202
|
+
) : (
|
|
203
|
+
<DropdownDownFill18Icon />
|
|
204
|
+
)}
|
|
215
205
|
</Box>
|
|
216
206
|
</ChakraSelect.IndicatorGroup>
|
|
217
207
|
</ChakraSelect.Control>
|
|
@@ -285,7 +275,7 @@ export const SelectValueText = function SelectValueText({
|
|
|
285
275
|
{...rest}
|
|
286
276
|
ref={ref}
|
|
287
277
|
placeholder={placeholder}
|
|
288
|
-
|
|
278
|
+
data-with-placeholder={withPlaceholder || undefined}
|
|
289
279
|
>
|
|
290
280
|
<ChakraSelect.Context>
|
|
291
281
|
{(select: {
|
|
@@ -301,7 +291,7 @@ export const SelectValueText = function SelectValueText({
|
|
|
301
291
|
if (children) return children(items);
|
|
302
292
|
if (multiple) {
|
|
303
293
|
return (
|
|
304
|
-
<Flex gap={0.5}
|
|
294
|
+
<Flex gap={0.5}>
|
|
305
295
|
{items.map((item) => (
|
|
306
296
|
<Badge
|
|
307
297
|
key={select.collection.stringifyItem(item)}
|