@vygruppen/spor-react 12.2.1 → 12.3.1
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 +12 -31
- package/.turbo/turbo-typegen.log +1 -19
- package/CHANGELOG.md +18 -0
- package/dist/index.d.mts +77 -123
- package/dist/index.d.ts +77 -123
- package/dist/index.js +547 -446
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +436 -335
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/src/datepicker/DatePicker.tsx +10 -13
- package/src/datepicker/DateRangePicker.tsx +18 -5
- package/src/datepicker/TimePicker.tsx +34 -35
- package/src/input/AttachedInputs.tsx +7 -4
- package/src/input/ChoiceChip.tsx +13 -3
- package/src/input/Combobox.tsx +5 -0
- package/src/input/Field.tsx +56 -20
- package/src/input/FloatingLabel.tsx +38 -0
- package/src/input/Input.tsx +40 -23
- package/src/input/Label.tsx +18 -0
- package/src/input/NativeSelect.tsx +14 -5
- package/src/input/NumericStepper.tsx +102 -99
- package/src/input/PasswordInput.tsx +1 -2
- package/src/input/Select.tsx +29 -15
- package/src/input/Switch.tsx +17 -3
- package/src/input/Textarea.tsx +2 -2
- package/src/theme/slot-recipes/field.ts +1 -28
- package/src/theme/slot-recipes/numeric-stepper.ts +8 -3
- package/src/theme/slot-recipes/select.ts +4 -4
- package/src/theme/slot-recipes/table.ts +1 -0
- package/src/input/InputGroup.tsx +0 -67
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@vygruppen/spor-react",
|
3
|
-
"version": "12.
|
3
|
+
"version": "12.3.1",
|
4
4
|
"main": "./dist/index.js",
|
5
5
|
"module": "./dist/index.mjs",
|
6
6
|
"types": "./dist/index.d.ts",
|
@@ -35,9 +35,9 @@
|
|
35
35
|
"react-stately": "^3.31.1",
|
36
36
|
"react-swipeable": "^7.0.1",
|
37
37
|
"usehooks-ts": "^3.1.0",
|
38
|
-
"@vygruppen/spor-
|
39
|
-
"@vygruppen/spor-
|
40
|
-
"@vygruppen/spor-
|
38
|
+
"@vygruppen/spor-icon-react": "4.0.4",
|
39
|
+
"@vygruppen/spor-design-tokens": "4.0.6",
|
40
|
+
"@vygruppen/spor-loader": "0.6.0"
|
41
41
|
},
|
42
42
|
"devDependencies": {
|
43
43
|
"@chakra-ui/cli": "^3.8.0",
|
@@ -9,13 +9,7 @@ import {
|
|
9
9
|
useFieldContext,
|
10
10
|
useSlotRecipe,
|
11
11
|
} from "@chakra-ui/react";
|
12
|
-
import React, {
|
13
|
-
forwardRef,
|
14
|
-
PropsWithChildren,
|
15
|
-
ReactNode,
|
16
|
-
useId,
|
17
|
-
useRef,
|
18
|
-
} from "react";
|
12
|
+
import React, { forwardRef, PropsWithChildren, useId, useRef } from "react";
|
19
13
|
import {
|
20
14
|
AriaDatePickerProps,
|
21
15
|
DateValue,
|
@@ -24,7 +18,7 @@ import {
|
|
24
18
|
} from "react-aria";
|
25
19
|
import { useDatePickerState } from "react-stately";
|
26
20
|
|
27
|
-
import { Field } from "@/input/Field";
|
21
|
+
import { Field, FieldBaseProps } from "@/input/Field";
|
28
22
|
|
29
23
|
import { datePickerSlotRecipe } from "../theme/slot-recipes/datepicker";
|
30
24
|
import { Calendar } from "./Calendar";
|
@@ -46,8 +40,7 @@ type DatePickerProps = Omit<AriaDatePickerProps<DateValue>, "onChange"> &
|
|
46
40
|
showYearNavigation?: boolean;
|
47
41
|
withPortal?: boolean;
|
48
42
|
onChange?: (value: DateValue | null) => void;
|
49
|
-
|
50
|
-
};
|
43
|
+
} & FieldBaseProps;
|
51
44
|
|
52
45
|
/**
|
53
46
|
* A date picker component.
|
@@ -63,11 +56,13 @@ export const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>(
|
|
63
56
|
(
|
64
57
|
{
|
65
58
|
variant,
|
66
|
-
|
59
|
+
errorText,
|
67
60
|
minHeight,
|
68
61
|
showYearNavigation,
|
69
62
|
withPortal = true,
|
70
63
|
width = "auto",
|
64
|
+
invalid = false,
|
65
|
+
helperText,
|
71
66
|
...props
|
72
67
|
},
|
73
68
|
externalRef,
|
@@ -76,7 +71,7 @@ export const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>(
|
|
76
71
|
const state = useDatePickerState({
|
77
72
|
...props,
|
78
73
|
shouldCloseOnSelect: true,
|
79
|
-
errorMessage,
|
74
|
+
errorMessage: errorText,
|
80
75
|
isRequired: props.isRequired ?? chakraFieldProps?.required,
|
81
76
|
validationState: chakraFieldProps?.invalid ? "invalid" : "valid",
|
82
77
|
});
|
@@ -130,7 +125,9 @@ export const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>(
|
|
130
125
|
display="inline-flex"
|
131
126
|
id={inputGroupId}
|
132
127
|
aria-labelledby={labelId}
|
133
|
-
errorText={
|
128
|
+
errorText={errorText}
|
129
|
+
invalid={invalid}
|
130
|
+
helperText={helperText}
|
134
131
|
>
|
135
132
|
<PopoverAnchor>
|
136
133
|
<StyledField
|
@@ -18,7 +18,7 @@ import {
|
|
18
18
|
} from "react-aria";
|
19
19
|
import { useDateRangePickerState } from "react-stately";
|
20
20
|
|
21
|
-
import { Field } from "../input/Field";
|
21
|
+
import { Field, FieldBaseProps } from "../input/Field";
|
22
22
|
import { datePickerSlotRecipe } from "../theme/slot-recipes/datepicker";
|
23
23
|
import { CalendarTriggerButton } from "./CalendarTriggerButton";
|
24
24
|
import { DateField } from "./DateField";
|
@@ -30,7 +30,7 @@ import { useCurrentLocale } from "./utils";
|
|
30
30
|
|
31
31
|
type DateRangePickerProps = Omit<
|
32
32
|
AriaDateRangePickerProps<DateValue>,
|
33
|
-
"onChange"
|
33
|
+
"onChange" | "errorMessage" | "isInvalid" | "isRequired"
|
34
34
|
> &
|
35
35
|
Pick<BoxProps, "minHeight"> &
|
36
36
|
PropsWithChildren<DatePickerVariantProps> &
|
@@ -46,7 +46,7 @@ type DateRangePickerProps = Omit<
|
|
46
46
|
end: DateValue | null;
|
47
47
|
} | null,
|
48
48
|
) => void;
|
49
|
-
};
|
49
|
+
} & FieldBaseProps;
|
50
50
|
/**
|
51
51
|
* A date range picker component.
|
52
52
|
*
|
@@ -61,13 +61,19 @@ type DateRangePickerProps = Omit<
|
|
61
61
|
startName,
|
62
62
|
endName,
|
63
63
|
withPortal = true,
|
64
|
+
errorText,
|
65
|
+
helperText,
|
66
|
+
invalid,
|
64
67
|
...props
|
65
68
|
}: DateRangePickerProps) {
|
66
69
|
const fieldContextPRops = useFieldContext();
|
67
70
|
const state = useDateRangePickerState({
|
68
71
|
...props,
|
69
72
|
shouldCloseOnSelect: true,
|
70
|
-
|
73
|
+
isInvalid: invalid,
|
74
|
+
errorMessage: errorText,
|
75
|
+
|
76
|
+
isRequired: props.required ?? fieldContextPRops?.required,
|
71
77
|
validationState: fieldContextPRops?.invalid ? "invalid" : "valid",
|
72
78
|
});
|
73
79
|
const ref = useRef(null);
|
@@ -113,7 +119,14 @@ type DateRangePickerProps = Omit<
|
|
113
119
|
</label>
|
114
120
|
)}
|
115
121
|
<ChakraPopover.Root {...dialogProps}>
|
116
|
-
<Field
|
122
|
+
<Field
|
123
|
+
width="auto"
|
124
|
+
display="inline-flex"
|
125
|
+
id={datePickerId}
|
126
|
+
errorText={errorText}
|
127
|
+
helperText={helperText}
|
128
|
+
invalid={invalid}
|
129
|
+
>
|
117
130
|
<PopoverAnchor>
|
118
131
|
<StyledField
|
119
132
|
alignItems="center"
|
@@ -1,5 +1,5 @@
|
|
1
1
|
"use client";
|
2
|
-
import { BoxProps } from "@chakra-ui/react";
|
2
|
+
import { BoxProps, useFieldContext } from "@chakra-ui/react";
|
3
3
|
import { CalendarDateTime } from "@internationalized/date";
|
4
4
|
import { TimeValue } from "@react-types/datepicker";
|
5
5
|
import {
|
@@ -8,43 +8,44 @@ import {
|
|
8
8
|
} from "@vygruppen/spor-icon-react";
|
9
9
|
import { useTimeFieldState } from "react-stately";
|
10
10
|
|
11
|
-
import { Field } from "@/input/Field";
|
11
|
+
import { Field, FieldBaseProps } from "@/input/Field";
|
12
12
|
|
13
13
|
import { createTexts, IconButton, useTranslation } from "..";
|
14
14
|
import { StyledField } from "./StyledField";
|
15
15
|
import { TimeField } from "./TimeField";
|
16
16
|
import { getCurrentTime, useCurrentLocale } from "./utils";
|
17
17
|
|
18
|
-
type TimePickerProps = Omit<BoxProps, "defaultValue" | "onChange"> &
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
18
|
+
type TimePickerProps = Omit<BoxProps, "defaultValue" | "onChange"> &
|
19
|
+
FieldBaseProps & {
|
20
|
+
/** The label. Defaults to a localized version of "Time" */
|
21
|
+
label?: string;
|
22
|
+
/** The name of the form field, if used in a regular form */
|
23
|
+
name?: string;
|
24
|
+
/** The controlled value, if any.
|
25
|
+
*
|
26
|
+
* A `new Time(hours, minutes)` should be passed.
|
27
|
+
* Or `null` if the time should be unset.
|
28
|
+
**/
|
29
|
+
value?: TimeValue | null;
|
30
|
+
/** A default value, if any.
|
31
|
+
*
|
32
|
+
* A `new Time(hours, minutes)` should be passed.
|
33
|
+
* Defaults to the current time if not provided.
|
34
|
+
* Can be set to null if you don't want a time to be selected by default.
|
35
|
+
**/
|
36
|
+
defaultValue?: TimeValue | null;
|
37
|
+
/** Callback for when the time changes */
|
38
|
+
onChange?: (value: TimeValue | null) => void;
|
39
|
+
/** The maxiumum number of minutes to move when the step buttons are used.
|
40
|
+
*
|
41
|
+
* Defaults to 30 minutes.
|
42
|
+
*
|
43
|
+
* An example: If the time is at 13:37 and the minuteInterval is 15, clicking the step forwards button will move the time to 13:45. Next click will move it to 14:00.
|
44
|
+
*/
|
45
|
+
minuteInterval?: number;
|
46
|
+
/** Whether or not the field is disabled */
|
47
|
+
disabled?: boolean;
|
48
|
+
};
|
48
49
|
/** A time picker component.
|
49
50
|
*
|
50
51
|
* This lets the user select a time of day, either through typing it in, using the up and down arrows to select the hour and minute, or by clicking the step buttons to move the time forwards or backwards in pre-defined increments.
|
@@ -126,6 +127,7 @@ export const TimePicker = ({
|
|
126
127
|
const ariaLabel = `${inputLabel} – ${t(
|
127
128
|
texts.selectedTimeIs(`${dateTime?.hour ?? 0} ${dateTime?.minute ?? 0}`),
|
128
129
|
)}`;
|
130
|
+
|
129
131
|
return (
|
130
132
|
<Field as="time" {...boxProps}>
|
131
133
|
<StyledField
|
@@ -201,6 +203,3 @@ const texts = createTexts({
|
|
201
203
|
sv: "minuter",
|
202
204
|
},
|
203
205
|
});
|
204
|
-
function useFieldContext(): { disabled: unknown; invalid: unknown } {
|
205
|
-
throw new Error("Function not implemented.");
|
206
|
-
}
|
@@ -1,12 +1,15 @@
|
|
1
1
|
"use client";
|
2
2
|
|
3
|
-
import {
|
3
|
+
import {
|
4
|
+
chakra,
|
5
|
+
Group,
|
6
|
+
GroupProps,
|
7
|
+
RecipeVariantProps,
|
8
|
+
} from "@chakra-ui/react";
|
4
9
|
import { forwardRef } from "react";
|
5
10
|
|
6
11
|
import { attachedInputsRecipe } from "@/theme/recipes/attached-inputs";
|
7
12
|
|
8
|
-
import { InputGroupProps } from "./InputGroup";
|
9
|
-
|
10
13
|
/**
|
11
14
|
* Attaches several inputs together, so that they look like one input.
|
12
15
|
*
|
@@ -23,7 +26,7 @@ import { InputGroupProps } from "./InputGroup";
|
|
23
26
|
export type AttachedInputsProps = RecipeVariantProps<
|
24
27
|
typeof attachedInputsRecipe
|
25
28
|
> &
|
26
|
-
|
29
|
+
GroupProps;
|
27
30
|
|
28
31
|
const StyledGroup = chakra(Group, attachedInputsRecipe);
|
29
32
|
|
package/src/input/ChoiceChip.tsx
CHANGED
@@ -8,8 +8,13 @@ type CheckBoxIcon = {
|
|
8
8
|
checked: React.ReactNode;
|
9
9
|
};
|
10
10
|
|
11
|
-
export type ChoiceChipProps =
|
11
|
+
export type ChoiceChipProps = Omit<
|
12
|
+
CheckboxCardRootProps,
|
13
|
+
"onCheckedChange" | "checked"
|
14
|
+
> & {
|
12
15
|
icon?: CheckBoxIcon;
|
16
|
+
onCheckedChange?: (checked: boolean) => void;
|
17
|
+
checked: boolean;
|
13
18
|
};
|
14
19
|
|
15
20
|
/**
|
@@ -45,9 +50,14 @@ export type ChoiceChipProps = CheckboxCardRootProps & {
|
|
45
50
|
*/
|
46
51
|
|
47
52
|
export const ChoiceChip = forwardRef<HTMLInputElement, ChoiceChipProps>(
|
48
|
-
({ children, icon, ...rootProps }, ref) => {
|
53
|
+
({ children, icon, onCheckedChange, ...rootProps }, ref) => {
|
49
54
|
return (
|
50
|
-
<CheckboxCard.Root
|
55
|
+
<CheckboxCard.Root
|
56
|
+
{...rootProps}
|
57
|
+
{...(onCheckedChange && {
|
58
|
+
onCheckedChange: (details) => onCheckedChange(!!details.checked),
|
59
|
+
})}
|
60
|
+
>
|
51
61
|
<CheckboxCard.Context>
|
52
62
|
{({ checked }) => (
|
53
63
|
<>
|
package/src/input/Combobox.tsx
CHANGED
@@ -140,6 +140,11 @@ export const Combobox = forwardRef<HTMLDivElement, ComboboxProps<object>>(
|
|
140
140
|
aria-haspopup="listbox"
|
141
141
|
ref={inputRef}
|
142
142
|
role="combobox"
|
143
|
+
errorText={props.errorText}
|
144
|
+
helperText={props.helperText}
|
145
|
+
required={props.required}
|
146
|
+
disabled={props.disabled}
|
147
|
+
invalid={props.invalid}
|
143
148
|
label={label}
|
144
149
|
variant={variant}
|
145
150
|
aria-expanded={state.isOpen}
|
package/src/input/Field.tsx
CHANGED
@@ -3,23 +3,34 @@
|
|
3
3
|
import {
|
4
4
|
Field as ChakraField,
|
5
5
|
RecipeVariantProps,
|
6
|
+
Stack,
|
6
7
|
useSlotRecipe,
|
7
8
|
} from "@chakra-ui/react";
|
8
9
|
import * as React from "react";
|
9
10
|
|
11
|
+
import { Text } from "@/typography";
|
12
|
+
|
10
13
|
import { fieldSlotRecipe } from "../theme/slot-recipes/field";
|
14
|
+
import { FloatingLabel } from "./FloatingLabel";
|
15
|
+
import { Label } from "./Label";
|
11
16
|
|
12
17
|
type FieldVariantProps = RecipeVariantProps<typeof fieldSlotRecipe>;
|
13
18
|
|
19
|
+
export type FieldBaseProps = {
|
20
|
+
direction?: "row" | "column";
|
21
|
+
disabled?: boolean;
|
22
|
+
invalid?: boolean;
|
23
|
+
readOnly?: boolean;
|
24
|
+
required?: boolean;
|
25
|
+
label?: React.ReactNode;
|
26
|
+
helperText?: React.ReactNode;
|
27
|
+
errorText?: React.ReactNode;
|
28
|
+
floatingLabel?: boolean;
|
29
|
+
};
|
30
|
+
|
14
31
|
export type FieldProps = Omit<ChakraField.RootProps, "label"> &
|
15
|
-
React.PropsWithChildren<FieldVariantProps> &
|
16
|
-
|
17
|
-
label?: React.ReactNode;
|
18
|
-
/** Add helpertext underneath the input */
|
19
|
-
helperText?: React.ReactNode;
|
20
|
-
/** Add error text underneath the input */
|
21
|
-
errorText?: React.ReactNode;
|
22
|
-
};
|
32
|
+
React.PropsWithChildren<FieldVariantProps> &
|
33
|
+
FieldBaseProps;
|
23
34
|
|
24
35
|
/**
|
25
36
|
*
|
@@ -40,22 +51,47 @@ export type FieldProps = Omit<ChakraField.RootProps, "label"> &
|
|
40
51
|
|
41
52
|
export const Field = React.forwardRef<HTMLDivElement, FieldProps>(
|
42
53
|
(props, ref) => {
|
43
|
-
const {
|
54
|
+
const {
|
55
|
+
label,
|
56
|
+
children,
|
57
|
+
helperText,
|
58
|
+
errorText,
|
59
|
+
floatingLabel = false,
|
60
|
+
disabled,
|
61
|
+
invalid,
|
62
|
+
readOnly,
|
63
|
+
required,
|
64
|
+
direction,
|
65
|
+
...rest
|
66
|
+
} = props;
|
44
67
|
const recipe = useSlotRecipe({ key: "field" });
|
45
|
-
const styles = recipe(
|
68
|
+
const styles = recipe();
|
69
|
+
|
46
70
|
return (
|
47
|
-
<
|
48
|
-
|
71
|
+
<Stack gap="2" ref={ref} {...rest} width="100%">
|
72
|
+
<ChakraField.Root
|
73
|
+
disabled={disabled}
|
74
|
+
invalid={invalid}
|
75
|
+
readOnly={readOnly}
|
76
|
+
required={required}
|
77
|
+
css={styles.root}
|
78
|
+
direction={direction}
|
79
|
+
>
|
80
|
+
{label && !floatingLabel && <Label>{label}</Label>}
|
81
|
+
|
82
|
+
{children}
|
83
|
+
|
84
|
+
{label && floatingLabel && <FloatingLabel>{label}</FloatingLabel>}
|
85
|
+
{errorText && (
|
86
|
+
<ChakraField.ErrorText>{errorText}</ChakraField.ErrorText>
|
87
|
+
)}
|
88
|
+
</ChakraField.Root>
|
49
89
|
{helperText && (
|
50
|
-
<
|
51
|
-
|
52
|
-
|
53
|
-
<ChakraField.Label css={styles.label}>{label}</ChakraField.Label>
|
54
|
-
)}
|
55
|
-
{errorText && (
|
56
|
-
<ChakraField.ErrorText>{errorText}</ChakraField.ErrorText>
|
90
|
+
<Text fontSize="sm" color="text.tertiary">
|
91
|
+
{helperText}
|
92
|
+
</Text>
|
57
93
|
)}
|
58
|
-
</
|
94
|
+
</Stack>
|
59
95
|
);
|
60
96
|
},
|
61
97
|
);
|
@@ -0,0 +1,38 @@
|
|
1
|
+
import { defineStyle, Field, FieldLabelProps } from "@chakra-ui/react";
|
2
|
+
import { forwardRef } from "react";
|
3
|
+
|
4
|
+
export const FloatingLabel = forwardRef<HTMLLabelElement, FieldLabelProps>(
|
5
|
+
(props, ref) => (
|
6
|
+
<Field.Label ref={ref} {...props} css={floatingLabelStyles} />
|
7
|
+
),
|
8
|
+
);
|
9
|
+
|
10
|
+
FloatingLabel.displayName = "FloatingLabel";
|
11
|
+
|
12
|
+
const floatingLabelStyles = defineStyle({
|
13
|
+
paddingX: 3,
|
14
|
+
fontWeight: "normal",
|
15
|
+
fontSize: ["mobile.xs", "desktop.xs"],
|
16
|
+
color: "text",
|
17
|
+
pointerEvents: "none",
|
18
|
+
zIndex: "docked",
|
19
|
+
_disabled: {
|
20
|
+
opacity: 0.4,
|
21
|
+
},
|
22
|
+
|
23
|
+
pos: "absolute",
|
24
|
+
top: "0.3rem",
|
25
|
+
transition: "position",
|
26
|
+
_peerPlaceholderShown: {
|
27
|
+
/* For when input is not in focus */
|
28
|
+
top: "0.9rem",
|
29
|
+
color: "text",
|
30
|
+
fontSize: ["mobile.sm", "desktop.sm"],
|
31
|
+
},
|
32
|
+
_peerFocusVisible: {
|
33
|
+
/* For when input is in focus */
|
34
|
+
fontSize: ["mobile.xs", "desktop.xs"],
|
35
|
+
color: "text",
|
36
|
+
top: "0.3rem",
|
37
|
+
},
|
38
|
+
});
|
package/src/input/Input.tsx
CHANGED
@@ -1,24 +1,26 @@
|
|
1
1
|
"use client";
|
2
2
|
|
3
3
|
import {
|
4
|
+
Box,
|
4
5
|
chakra,
|
6
|
+
Flex,
|
5
7
|
Input as ChakraInput,
|
8
|
+
InputElement,
|
6
9
|
InputProps as ChakraInputProps,
|
7
10
|
} from "@chakra-ui/react";
|
8
|
-
import React, { forwardRef } from "react";
|
11
|
+
import React, { forwardRef, ReactNode } from "react";
|
9
12
|
|
10
13
|
import { inputRecipe } from "@/theme/recipes/input";
|
11
14
|
|
12
15
|
import { Field, FieldProps } from "./Field";
|
13
|
-
import { InputGroup } from "./InputGroup";
|
14
16
|
|
15
17
|
export type InputProps = Exclude<
|
16
18
|
ChakraInputProps,
|
17
|
-
"size" | "label" | "colorPalette"
|
19
|
+
"size" | "label" | "colorPalette" | "placeholder"
|
18
20
|
> &
|
19
21
|
FieldProps & {
|
20
22
|
/** The input's label */
|
21
|
-
label:
|
23
|
+
label: ReactNode;
|
22
24
|
/** Element that shows up to the left */
|
23
25
|
startElement?: React.ReactNode;
|
24
26
|
/** Element that shows up to the right */
|
@@ -70,25 +72,40 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
|
|
70
72
|
ref,
|
71
73
|
) => {
|
72
74
|
return (
|
73
|
-
<Field
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
75
|
+
<Field
|
76
|
+
invalid={invalid}
|
77
|
+
helperText={helperText}
|
78
|
+
errorText={errorText}
|
79
|
+
label={
|
80
|
+
// Render startElement invisibly to align label text with input content when an icon is present
|
81
|
+
<Flex>
|
82
|
+
<Box visibility="hidden">{startElement}</Box>
|
83
|
+
{label}
|
84
|
+
</Flex>
|
85
|
+
}
|
86
|
+
floatingLabel={true}
|
87
|
+
>
|
88
|
+
{startElement && (
|
89
|
+
<InputElement pointerEvents="none" paddingX={2}>
|
90
|
+
{startElement}
|
91
|
+
</InputElement>
|
92
|
+
)}
|
93
|
+
<StyledInput
|
94
|
+
data-attachable
|
95
|
+
ref={ref}
|
96
|
+
focusVisibleRing="outside"
|
97
|
+
overflow="hidden"
|
98
|
+
paddingLeft={startElement ? "2.6rem" : undefined}
|
99
|
+
paddingRight={endElement ? "2.6rem" : undefined}
|
100
|
+
{...props}
|
101
|
+
className={`peer ${props.className}`}
|
102
|
+
placeholder=""
|
103
|
+
/>
|
104
|
+
{endElement && (
|
105
|
+
<InputElement placement="end" paddingX={2}>
|
106
|
+
{endElement}
|
107
|
+
</InputElement>
|
108
|
+
)}
|
92
109
|
</Field>
|
93
110
|
);
|
94
111
|
},
|
@@ -0,0 +1,18 @@
|
|
1
|
+
import { defineStyle, Field, FieldLabelProps } from "@chakra-ui/react";
|
2
|
+
|
3
|
+
export const Label = (props: FieldLabelProps) => (
|
4
|
+
<Field.Label {...props} css={labelStyles} />
|
5
|
+
);
|
6
|
+
|
7
|
+
const labelStyles = defineStyle({
|
8
|
+
fontWeight: "normal",
|
9
|
+
paddingBottom: 1,
|
10
|
+
paddingX: 1,
|
11
|
+
fontSize: ["mobile.xs", "desktop.xs"],
|
12
|
+
color: "text",
|
13
|
+
pointerEvents: "none",
|
14
|
+
zIndex: "docked",
|
15
|
+
_disabled: {
|
16
|
+
opacity: 0.4,
|
17
|
+
},
|
18
|
+
});
|
@@ -9,7 +9,7 @@ import { DropdownDownFill18Icon } from "@vygruppen/spor-icon-react";
|
|
9
9
|
import * as React from "react";
|
10
10
|
|
11
11
|
import { nativeSelectSlotRecipe } from "../theme/slot-recipes/native-select";
|
12
|
-
import { Field } from "./Field";
|
12
|
+
import { Field, FieldBaseProps } from "./Field";
|
13
13
|
|
14
14
|
type NativeSelectVariantProps = RecipeVariantProps<
|
15
15
|
typeof nativeSelectSlotRecipe
|
@@ -17,11 +17,9 @@ type NativeSelectVariantProps = RecipeVariantProps<
|
|
17
17
|
|
18
18
|
export type NativeSelectdProps =
|
19
19
|
React.PropsWithChildren<NativeSelectVariantProps> &
|
20
|
+
FieldBaseProps &
|
20
21
|
ChakraNativeSelectFieldProps & {
|
21
22
|
icon?: React.ReactNode;
|
22
|
-
label: string;
|
23
|
-
invalid?: boolean;
|
24
|
-
disabled?: boolean;
|
25
23
|
};
|
26
24
|
|
27
25
|
/**
|
@@ -51,6 +49,9 @@ export const NativeSelect = React.forwardRef<
|
|
51
49
|
label,
|
52
50
|
invalid,
|
53
51
|
disabled,
|
52
|
+
required,
|
53
|
+
helperText,
|
54
|
+
errorText,
|
54
55
|
...rest
|
55
56
|
} = props;
|
56
57
|
|
@@ -58,7 +59,15 @@ export const NativeSelect = React.forwardRef<
|
|
58
59
|
const styles = recipe({ variant });
|
59
60
|
|
60
61
|
return (
|
61
|
-
<Field
|
62
|
+
<Field
|
63
|
+
label={label}
|
64
|
+
invalid={invalid}
|
65
|
+
disabled={disabled}
|
66
|
+
required={required}
|
67
|
+
helperText={helperText}
|
68
|
+
errorText={errorText}
|
69
|
+
floatingLabel={true}
|
70
|
+
>
|
62
71
|
<ChakraNativeSelect.Root
|
63
72
|
ref={ref}
|
64
73
|
css={styles.root}
|