@vygruppen/spor-react 12.24.15 → 13.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.
Files changed (88) hide show
  1. package/.turbo/turbo-build.log +10 -10
  2. package/.turbo/turbo-postinstall.log +4 -3
  3. package/CHANGELOG.md +25 -0
  4. package/dist/index.cjs +2888 -2534
  5. package/dist/index.cjs.map +1 -1
  6. package/dist/index.d.cts +921 -572
  7. package/dist/index.d.ts +921 -572
  8. package/dist/index.mjs +2860 -2517
  9. package/dist/index.mjs.map +1 -1
  10. package/package.json +8 -8
  11. package/src/accordion/Accordion.tsx +34 -29
  12. package/src/accordion/Expandable.tsx +20 -21
  13. package/src/alert/Alert.tsx +7 -5
  14. package/src/alert/AlertIcon.tsx +19 -20
  15. package/src/alert/ExpandableAlert.tsx +65 -64
  16. package/src/alert/ServiceAlert.tsx +78 -78
  17. package/src/breadcrumb/Breadcrumb.tsx +37 -34
  18. package/src/button/Button.tsx +64 -57
  19. package/src/button/ButtonGroup.tsx +12 -10
  20. package/src/button/Clipboard.tsx +21 -18
  21. package/src/button/CloseButton.tsx +19 -17
  22. package/src/button/FloatingActionButton.tsx +41 -47
  23. package/src/button/IconButton.tsx +20 -18
  24. package/src/calendar/CalendarContext.tsx +1 -1
  25. package/src/color-mode/color-mode.tsx +7 -5
  26. package/src/datepicker/CalendarTriggerButton.tsx +11 -7
  27. package/src/datepicker/DateField.tsx +53 -51
  28. package/src/datepicker/DatePicker.tsx +127 -134
  29. package/src/datepicker/DateTimeSegment.tsx +44 -40
  30. package/src/datepicker/StyledField.tsx +39 -36
  31. package/src/dialog/Dialog.tsx +14 -11
  32. package/src/dialog/Drawer.tsx +74 -67
  33. package/src/input/AttachedInputs.tsx +35 -41
  34. package/src/input/Autocomplete.tsx +118 -129
  35. package/src/input/CardSelect.tsx +67 -65
  36. package/src/input/Checkbox.tsx +19 -17
  37. package/src/input/CheckboxGroup.tsx +0 -2
  38. package/src/input/ChoiceChip.tsx +42 -38
  39. package/src/input/Combobox.tsx +4 -4
  40. package/src/input/CountryCodeSelect.tsx +8 -8
  41. package/src/input/Field.tsx +78 -75
  42. package/src/input/FloatingLabel.tsx +6 -8
  43. package/src/input/Input.tsx +87 -92
  44. package/src/input/ListBox.tsx +3 -4
  45. package/src/input/Menu.tsx +241 -0
  46. package/src/input/NativeSelect.tsx +7 -5
  47. package/src/input/NumericStepper.tsx +15 -12
  48. package/src/input/PasswordInput.tsx +65 -61
  49. package/src/input/PhoneNumberInput.tsx +7 -7
  50. package/src/input/Popover.tsx +52 -55
  51. package/src/input/Radio.tsx +16 -11
  52. package/src/input/SearchInput.tsx +32 -31
  53. package/src/input/Select.tsx +106 -96
  54. package/src/input/Switch.tsx +43 -41
  55. package/src/input/Textarea.tsx +68 -66
  56. package/src/input/index.ts +1 -0
  57. package/src/layout/PressableCard.tsx +11 -10
  58. package/src/layout/RadioCard.tsx +57 -53
  59. package/src/layout/Separator.tsx +8 -7
  60. package/src/layout/StaticCard.tsx +11 -10
  61. package/src/linjetag/LineIcon.tsx +48 -54
  62. package/src/linjetag/TravelTag.tsx +57 -59
  63. package/src/link/TextLink.tsx +50 -40
  64. package/src/loader/ProgressBar.tsx +41 -46
  65. package/src/loader/ProgressLoader.tsx +83 -86
  66. package/src/loader/Skeleton.tsx +56 -48
  67. package/src/logo/CargonetLogo.tsx +88 -87
  68. package/src/logo/VyLogo.tsx +80 -77
  69. package/src/logo/VyLogoPride.tsx +137 -135
  70. package/src/media-controller/JumpButton.tsx +30 -28
  71. package/src/media-controller/PlayPauseButton.tsx +8 -7
  72. package/src/media-controller/SkipButton.tsx +28 -26
  73. package/src/nudge/Nudge.tsx +66 -70
  74. package/src/pagination/Pagination.tsx +52 -46
  75. package/src/popover/index.tsx +46 -41
  76. package/src/progress-indicator/ProgressIndicator.tsx +10 -7
  77. package/src/stepper/Stepper.tsx +84 -81
  78. package/src/tab/Tabs.tsx +8 -4
  79. package/src/table/Table.tsx +89 -109
  80. package/src/theme/slot-recipes/anatomy.ts +14 -0
  81. package/src/theme/slot-recipes/index.ts +2 -0
  82. package/src/theme/slot-recipes/menu.ts +111 -0
  83. package/src/tooltip.tsx +26 -22
  84. package/src/typography/Badge.tsx +8 -5
  85. package/src/typography/Code.tsx +8 -5
  86. package/src/typography/Heading.tsx +22 -23
  87. package/src/typography/Text.tsx +11 -9
  88. package/tsconfig.json +1 -0
@@ -9,7 +9,6 @@ import {
9
9
  } from "@chakra-ui/react";
10
10
  import React, {
11
11
  ComponentProps,
12
- forwardRef,
13
12
  ReactNode,
14
13
  useId,
15
14
  useImperativeHandle,
@@ -64,100 +63,96 @@ export type InputProps = FieldProps &
64
63
  * @see https://spor.vy.no/components/input
65
64
  */
66
65
 
67
- export const Input = forwardRef<HTMLInputElement, InputProps>(
68
- (
69
- {
70
- startElement,
71
- endElement,
72
- label,
73
- invalid,
74
- helperText,
75
- errorText,
76
- required,
77
- hidden,
78
- fontSize,
79
- labelAsChild,
80
- ...props
81
- },
82
- ref,
83
- ) => {
84
- const recipe = useRecipe({ key: "input" });
85
- const [recipeProps, restProps] = recipe.splitVariantProps(props);
86
- const styles = recipe(recipeProps);
66
+ export const Input = ({
67
+ ref,
68
+ startElement,
69
+ endElement,
70
+ label,
71
+ invalid,
72
+ helperText,
73
+ errorText,
74
+ required,
75
+ hidden,
76
+ fontSize,
77
+ labelAsChild,
78
+ ...props
79
+ }: InputProps & {
80
+ ref?: React.RefObject<HTMLInputElement | null>;
81
+ }) => {
82
+ const recipe = useRecipe({ key: "input" });
83
+ const [recipeProps, restProps] = recipe.splitVariantProps(props);
84
+ const styles = recipe(recipeProps);
87
85
 
88
- const labelId = useId();
86
+ const labelId = useId();
89
87
 
90
- const inputRef = useRef<HTMLInputElement>(null);
91
- useImperativeHandle(ref, () => inputRef.current as HTMLInputElement, []);
88
+ const inputRef = useRef<HTMLInputElement>(null);
89
+ useImperativeHandle(ref, () => inputRef.current as HTMLInputElement, []);
92
90
 
93
- const { shouldFloat, handleFocus, handleBlur, handleChange, isControlled } =
94
- useFloatingInputState<HTMLInputElement>({
95
- value: props.value,
96
- defaultValue: props.defaultValue,
97
- onFocus: props.onFocus,
98
- onBlur: props.onBlur,
99
- onChange: props.onChange,
100
- inputRef,
101
- });
91
+ const { shouldFloat, handleFocus, handleBlur, handleChange, isControlled } =
92
+ useFloatingInputState<HTMLInputElement>({
93
+ value: props.value,
94
+ defaultValue: props.defaultValue,
95
+ onFocus: props.onFocus,
96
+ onBlur: props.onBlur,
97
+ onChange: props.onChange,
98
+ inputRef: inputRef as React.RefObject<HTMLInputElement>,
99
+ });
102
100
 
103
- return (
104
- <Field
105
- invalid={invalid}
106
- helperText={helperText}
107
- required={required}
108
- hidden={hidden}
109
- errorText={errorText}
110
- id={props.id}
111
- labelAsChild={labelAsChild}
112
- label={
113
- <Flex id={labelId}>
114
- <Box visibility="hidden">{startElement}</Box>
115
- {label}
116
- </Flex>
117
- }
118
- floatingLabel={true}
119
- shouldFloat={shouldFloat}
120
- >
121
- {startElement && (
122
- <InputElement
123
- pointerEvents="none"
124
- paddingX={2}
125
- aria-hidden="true"
126
- fontSize={fontSize ?? "mobile.md"}
127
- >
128
- {startElement}
129
- </InputElement>
130
- )}
131
- <ChakraInput
132
- data-attachable
133
- ref={inputRef}
134
- focusVisibleRing="outside"
135
- overflow="hidden"
136
- paddingLeft={startElement ? "2.6rem" : undefined}
137
- paddingRight={endElement ? "2.6rem" : undefined}
138
- {...restProps}
139
- className={`peer ${props.className || ""}`}
140
- value={isControlled ? props.value : undefined}
141
- onFocus={handleFocus}
142
- onBlur={handleBlur}
143
- onChange={handleChange}
144
- placeholder=""
145
- css={styles}
101
+ return (
102
+ <Field
103
+ invalid={invalid}
104
+ helperText={helperText}
105
+ required={required}
106
+ hidden={hidden}
107
+ errorText={errorText}
108
+ id={props.id}
109
+ labelAsChild={labelAsChild}
110
+ label={
111
+ <Flex id={labelId}>
112
+ <Box visibility="hidden">{startElement}</Box>
113
+ {label}
114
+ </Flex>
115
+ }
116
+ floatingLabel={true}
117
+ shouldFloat={shouldFloat}
118
+ >
119
+ {startElement && (
120
+ <InputElement
121
+ pointerEvents="none"
122
+ paddingX={2}
123
+ aria-hidden="true"
146
124
  fontSize={fontSize ?? "mobile.md"}
147
125
  aria-labelledby={labelId}
148
- />
149
- {endElement && (
150
- <InputElement
151
- placement="end"
152
- paddingX={2}
153
- fontSize={fontSize ?? "mobile.md"}
154
- >
155
- {endElement}
156
- </InputElement>
157
- )}
158
- </Field>
159
- );
160
- },
161
- );
162
-
163
- Input.displayName = "Input";
126
+ >
127
+ {startElement}
128
+ </InputElement>
129
+ )}
130
+ <ChakraInput
131
+ data-attachable
132
+ ref={inputRef}
133
+ focusVisibleRing="outside"
134
+ overflow="hidden"
135
+ paddingLeft={startElement ? "2.6rem" : undefined}
136
+ paddingRight={endElement ? "2.6rem" : undefined}
137
+ {...restProps}
138
+ className={`peer ${props.className || ""}`}
139
+ value={isControlled ? props.value : undefined}
140
+ onFocus={handleFocus}
141
+ onBlur={handleBlur}
142
+ onChange={handleChange}
143
+ placeholder=""
144
+ css={styles}
145
+ fontSize={fontSize ?? "mobile.md"}
146
+ />
147
+ {endElement && (
148
+ <InputElement
149
+ placement="end"
150
+ paddingX={2}
151
+ fontSize={fontSize ?? "mobile.md"}
152
+ >
153
+ {endElement}
154
+ </InputElement>
155
+ )}
156
+ </Field>
157
+ );
158
+ };
@@ -106,8 +106,6 @@ export const ListBox = (props: ListBoxProps<object>) => {
106
106
  );
107
107
  };
108
108
 
109
- ListBox.displayName = "ListBox";
110
-
111
109
  /**
112
110
  * Renders a label for a listbox item.
113
111
  *
@@ -143,7 +141,7 @@ type OptionProps = {
143
141
  state: SelectState<unknown> | ListState<unknown>;
144
142
  };
145
143
  function Option({ item, state }: OptionProps) {
146
- const ref = useRef(null);
144
+ const ref = useRef<HTMLLIElement>(null);
147
145
  const {
148
146
  optionProps,
149
147
  isSelected,
@@ -177,7 +175,7 @@ function Option({ item, state }: OptionProps) {
177
175
  TODO: Follow up with react-spectrum to see if they can solve it on their end
178
176
  */
179
177
  useEffect(() => {
180
- (ref as React.RefObject<HTMLDivElement>)?.current?.addEventListener(
178
+ ref.current?.addEventListener(
181
179
  "touchend",
182
180
  (event: TouchEvent) => {
183
181
  event.preventDefault();
@@ -185,6 +183,7 @@ function Option({ item, state }: OptionProps) {
185
183
  { passive: false, once: true },
186
184
  );
187
185
  }, []);
186
+
188
187
  return (
189
188
  <OptionContext.Provider value={{ labelProps, descriptionProps }}>
190
189
  <ListItem
@@ -0,0 +1,241 @@
1
+ "use client";
2
+ import {
3
+ Flex,
4
+ Menu as ChakraMenu,
5
+ MenuCheckboxItemProps,
6
+ MenuContentProps,
7
+ MenuItemGroupProps as ChakraMenuItemGroupProps,
8
+ MenuItemProps as ChakraMenuItemProps,
9
+ MenuRadioItemGroupProps,
10
+ MenuRadioItemProps,
11
+ MenuRootProps,
12
+ MenuSeparatorProps,
13
+ MenuTriggerItemProps as ChakraMenuTriggerItemProps,
14
+ Portal,
15
+ useMenuContext as useChakraMenuContext,
16
+ } from "@chakra-ui/react";
17
+ import {
18
+ DropdownDownFill18Icon,
19
+ DropdownDownFill24Icon,
20
+ } from "@vygruppen/spor-icon-react";
21
+ import { ReactNode, Ref } from "react";
22
+ import { createContext, useContext } from "react";
23
+
24
+ import { Button, ButtonProps, Checkbox } from "..";
25
+
26
+ type Variant = Pick<MenuRootProps, "variant">;
27
+
28
+ const CustomMenuContext = createContext<Variant>({
29
+ variant: "core",
30
+ });
31
+ export const useMenuContext = () => useContext(CustomMenuContext);
32
+
33
+ /**
34
+ * Menu component.
35
+ *
36
+ * Used to create an accessible dropdown menu.
37
+ *
38
+ * @example
39
+ * ```tsx
40
+ <Menu>
41
+ <MenuTrigger> Menu </MenuTrigger>
42
+ <MenuContent>
43
+ <MenuItem value="1"> Item 1 </MenuItem>
44
+ <MenuItem value="2"> Item 2 </MenuItem>
45
+ <MenuItem value="3"> Item 3 </MenuItem>
46
+ </MenuContent>
47
+ </Menu>
48
+ * ```
49
+ *
50
+ */
51
+ export const Menu = ({ children, ...props }: MenuRootProps) => {
52
+ return (
53
+ <CustomMenuContext.Provider value={{ variant: props.variant }}>
54
+ <ChakraMenu.Root {...props}>{children}</ChakraMenu.Root>
55
+ </CustomMenuContext.Provider>
56
+ );
57
+ };
58
+
59
+ export const MenuContent = ({
60
+ children,
61
+ ref,
62
+ ...props
63
+ }: MenuContentProps & { ref?: Ref<HTMLDivElement> }) => {
64
+ return (
65
+ <Portal>
66
+ <ChakraMenu.Positioner>
67
+ <ChakraMenu.Content ref={ref} {...props}>
68
+ {children}
69
+ </ChakraMenu.Content>
70
+ </ChakraMenu.Positioner>
71
+ </Portal>
72
+ );
73
+ };
74
+
75
+ export type MenuTriggerProps = {
76
+ icon?: ReactNode;
77
+ } & Omit<ButtonProps, "variant" | "rightIcon" | "leftIcon"> & {
78
+ ref?: Ref<HTMLButtonElement>;
79
+ };
80
+
81
+ export const MenuTrigger = ({
82
+ icon,
83
+ size,
84
+ children,
85
+ ref,
86
+ ...props
87
+ }: MenuTriggerProps) => {
88
+ const { variant } = useMenuContext();
89
+ const { open } = useChakraMenuContext();
90
+ const ChevronIcon =
91
+ size === "sm" ? DropdownDownFill18Icon : DropdownDownFill24Icon;
92
+
93
+ const getButtonVariant = (): ButtonProps["variant"] => {
94
+ if (variant === "floating") return "floating";
95
+ if (variant === "accent") return "secondary";
96
+ return "tertiary";
97
+ };
98
+
99
+ return (
100
+ <ChakraMenu.Trigger asChild ref={ref}>
101
+ <Button
102
+ leftIcon={icon}
103
+ variant={getButtonVariant()}
104
+ size={size}
105
+ {...props}
106
+ rightIcon={
107
+ <ChevronIcon
108
+ transform={open ? "rotate(180deg)" : undefined}
109
+ transition="transform 0.3s"
110
+ />
111
+ }
112
+ >
113
+ {children}
114
+ </Button>
115
+ </ChakraMenu.Trigger>
116
+ );
117
+ };
118
+
119
+ export type MenuItemProps = {
120
+ itemCommand?: string;
121
+ leftIcon?: React.ReactNode;
122
+ rightIcon?: React.ReactNode;
123
+ } & ChakraMenuItemProps & { ref?: Ref<HTMLDivElement> };
124
+
125
+ export const MenuItem = ({
126
+ itemCommand,
127
+ children,
128
+ value,
129
+ leftIcon,
130
+ rightIcon,
131
+ ref,
132
+ ...props
133
+ }: MenuItemProps) => {
134
+ return (
135
+ <ChakraMenu.Item value={value} {...props} ref={ref}>
136
+ {leftIcon}
137
+ {children}
138
+ {itemCommand && (
139
+ <ChakraMenu.ItemCommand>{itemCommand}</ChakraMenu.ItemCommand>
140
+ )}
141
+ {rightIcon}
142
+ </ChakraMenu.Item>
143
+ );
144
+ };
145
+
146
+ export type MenuTriggerItemProps = {
147
+ leftIcon?: React.ReactNode;
148
+ rightIcon?: React.ReactNode;
149
+ } & ChakraMenuTriggerItemProps & { ref?: Ref<HTMLDivElement> };
150
+
151
+ export const MenuTriggerItem = ({
152
+ children,
153
+ leftIcon,
154
+ rightIcon,
155
+ ref,
156
+ ...props
157
+ }: MenuTriggerItemProps) => {
158
+ return (
159
+ <ChakraMenu.TriggerItem {...props} ref={ref}>
160
+ {leftIcon}
161
+ {children}
162
+ {rightIcon}
163
+ </ChakraMenu.TriggerItem>
164
+ );
165
+ };
166
+
167
+ export const MenuRadioItemGroup = ({
168
+ children,
169
+ ref,
170
+ ...props
171
+ }: MenuRadioItemGroupProps & { ref?: Ref<HTMLDivElement> }) => {
172
+ return (
173
+ <ChakraMenu.RadioItemGroup {...props} ref={ref}>
174
+ {children}
175
+ </ChakraMenu.RadioItemGroup>
176
+ );
177
+ };
178
+
179
+ export const MenuRadioItem = ({
180
+ children,
181
+ ref,
182
+ ...props
183
+ }: MenuRadioItemProps & { ref?: Ref<HTMLDivElement> }) => {
184
+ return (
185
+ <ChakraMenu.RadioItem {...props} ref={ref}>
186
+ {children}
187
+ <Flex w="1.25rem" justify="center" align="center">
188
+ <ChakraMenu.ItemIndicator />
189
+ </Flex>
190
+ </ChakraMenu.RadioItem>
191
+ );
192
+ };
193
+
194
+ export type MenuItemGroupProps = {
195
+ label?: string;
196
+ } & ChakraMenuItemGroupProps & { ref?: Ref<HTMLDivElement> };
197
+
198
+ export const MenuItemGroup = ({
199
+ children,
200
+ label,
201
+ ref,
202
+ ...props
203
+ }: MenuItemGroupProps) => {
204
+ return (
205
+ <ChakraMenu.ItemGroup {...props} ref={ref}>
206
+ {label && <ChakraMenu.ItemGroupLabel>{label}</ChakraMenu.ItemGroupLabel>}
207
+ {children}
208
+ </ChakraMenu.ItemGroup>
209
+ );
210
+ };
211
+
212
+ export const MenuCheckboxItem = ({
213
+ children,
214
+ closeOnSelect = false,
215
+ ref,
216
+ ...props
217
+ }: MenuCheckboxItemProps & { ref?: Ref<HTMLDivElement> }) => {
218
+ return (
219
+ <ChakraMenu.CheckboxItem
220
+ {...props}
221
+ ref={ref}
222
+ closeOnSelect={closeOnSelect}
223
+ checked={props.checked}
224
+ onCheckedChange={props.onCheckedChange}
225
+ >
226
+ <Checkbox
227
+ checked={props.checked}
228
+ onCheckedChange={() => props.onCheckedChange}
229
+ >
230
+ {children}
231
+ </Checkbox>
232
+ </ChakraMenu.CheckboxItem>
233
+ );
234
+ };
235
+
236
+ export const MenuSeparator = ({
237
+ ref,
238
+ ...props
239
+ }: MenuSeparatorProps & { ref?: Ref<HTMLHRElement> }) => {
240
+ return <ChakraMenu.Separator ref={ref} {...props} />;
241
+ };
@@ -39,10 +39,12 @@ export type NativeSelectdProps =
39
39
  *
40
40
  */
41
41
 
42
- export const NativeSelect = React.forwardRef<
43
- HTMLDivElement,
44
- NativeSelectdProps
45
- >(function NativeSelect(props, ref) {
42
+ export const NativeSelect = function NativeSelect({
43
+ ref,
44
+ ...props
45
+ }: NativeSelectdProps & {
46
+ ref?: React.RefObject<HTMLDivElement>;
47
+ }) {
46
48
  const {
47
49
  children,
48
50
  variant = "core",
@@ -88,4 +90,4 @@ export const NativeSelect = React.forwardRef<
88
90
  </ChakraNativeSelect.Root>
89
91
  </Field>
90
92
  );
91
- });
93
+ };
@@ -63,10 +63,12 @@ export type NumericStepperProps = FieldBaseProps &
63
63
  * @see https://spor.vy.no/components/numeric-stepper
64
64
  */
65
65
 
66
- export const NumericStepper = React.forwardRef<
67
- HTMLDivElement,
68
- NumericStepperProps
69
- >((props: NumericStepperProps, ref) => {
66
+ export const NumericStepper = ({
67
+ ref,
68
+ ...props
69
+ }: NumericStepperProps & {
70
+ ref?: React.RefObject<HTMLDivElement>;
71
+ }) => {
70
72
  const {
71
73
  name: nameProperty,
72
74
  id: idProperty,
@@ -192,8 +194,7 @@ export const NumericStepper = React.forwardRef<
192
194
  />
193
195
  </Field>
194
196
  );
195
- });
196
- NumericStepper.displayName = "NumericStepper";
197
+ };
197
198
 
198
199
  type VerySmallButtonProps = {
199
200
  /** The icon to render */
@@ -213,11 +214,14 @@ type VerySmallButtonProps = {
213
214
  };
214
215
 
215
216
  /** Internal override for extra small icon buttons */
216
- const VerySmallButton = React.forwardRef<
217
- HTMLButtonElement,
218
- VerySmallButtonProps
219
- >((props, ref) => {
217
+ const VerySmallButton = ({
218
+ ref,
219
+ ...props
220
+ }: VerySmallButtonProps & {
221
+ ref?: React.RefObject<HTMLButtonElement | null>;
222
+ }) => {
220
223
  const { withStepLabel = false, ...rest } = props;
224
+
221
225
  const recipe = useSlotRecipe({ key: "numericStepper" });
222
226
  const styles = recipe({ withStepLabel });
223
227
  return (
@@ -229,8 +233,7 @@ const VerySmallButton = React.forwardRef<
229
233
  {...rest}
230
234
  />
231
235
  );
232
- });
233
- VerySmallButton.displayName = "VerySmallButton";
236
+ };
234
237
 
235
238
  type IconPropertyTypes = BoxProps & { stepLabel: number };
236
239
 
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  import { Button, useControllableState } from "@chakra-ui/react";
4
- import React, { forwardRef } from "react";
4
+ import React from "react";
5
5
 
6
6
  import { ButtonProps, Input, InputProps } from "..";
7
7
  import { createTexts, useTranslation } from "..";
@@ -42,70 +42,74 @@ export interface PasswordInputProps
42
42
  * @see https://spor.vy.no/components/password-input
43
43
  */
44
44
 
45
- export const PasswordInput = forwardRef<HTMLInputElement, PasswordInputProps>(
46
- (props, ref) => {
47
- const {
48
- defaultVisible,
49
- visible: visibleProperty,
50
- onVisibleChange,
51
- label,
52
- startElement,
53
- ...rest
54
- } = props;
45
+ export const PasswordInput = ({
46
+ ref,
47
+ ...props
48
+ }: PasswordInputProps & {
49
+ ref?: React.RefObject<HTMLInputElement>;
50
+ }) => {
51
+ const {
52
+ defaultVisible,
53
+ visible: visibleProperty,
54
+ onVisibleChange,
55
+ label,
56
+ startElement,
57
+ ...rest
58
+ } = props;
55
59
 
56
- const [visible, setVisible] = useControllableState({
57
- value: visibleProperty,
58
- defaultValue: defaultVisible || false,
59
- onChange: onVisibleChange,
60
- });
60
+ const [visible, setVisible] = useControllableState({
61
+ value: visibleProperty,
62
+ defaultValue: defaultVisible || false,
63
+ onChange: onVisibleChange,
64
+ });
61
65
 
62
- const { t } = useTranslation();
66
+ const { t } = useTranslation();
63
67
 
64
- return (
65
- <Input
66
- ref={ref}
67
- startElement={startElement && startElement}
68
- label={label}
69
- type={visible ? "text" : "password"}
70
- endElement={
71
- <VisibilityTrigger
72
- variant="ghost"
73
- disabled={rest.disabled}
74
- onPointerDown={(event) => {
75
- if (rest.disabled) return;
76
- if (event.button !== 0) return;
77
- event.preventDefault();
78
- setVisible(!visible);
79
- }}
80
- >
81
- {visible ? t(texts.hidePassword) : t(texts.showPassword)}
82
- </VisibilityTrigger>
83
- }
84
- {...rest}
85
- />
86
- );
87
- },
88
- );
89
- PasswordInput.displayName = "PasswordInput";
68
+ return (
69
+ <Input
70
+ ref={ref}
71
+ startElement={startElement && startElement}
72
+ label={label}
73
+ type={visible ? "text" : "password"}
74
+ endElement={
75
+ <VisibilityTrigger
76
+ variant="ghost"
77
+ disabled={rest.disabled}
78
+ onPointerDown={(event) => {
79
+ if (rest.disabled) return;
80
+ if (event.button !== 0) return;
81
+ event.preventDefault();
82
+ setVisible(!visible);
83
+ }}
84
+ >
85
+ {visible ? t(texts.hidePassword) : t(texts.showPassword)}
86
+ </VisibilityTrigger>
87
+ }
88
+ {...rest}
89
+ />
90
+ );
91
+ };
90
92
 
91
- const VisibilityTrigger = React.forwardRef<HTMLButtonElement, ButtonProps>(
92
- (props, ref) => {
93
- return (
94
- <Button
95
- ref={ref}
96
- type="button"
97
- fontWeight="normal"
98
- size="sm"
99
- borderRadius="sm"
100
- marginRight={1}
101
- {...props}
102
- >
103
- {props.children}
104
- </Button>
105
- );
106
- },
107
- );
108
- VisibilityTrigger.displayName = "VisibilityTrigger";
93
+ const VisibilityTrigger = ({
94
+ ref,
95
+ ...props
96
+ }: ButtonProps & {
97
+ ref?: React.RefObject<HTMLButtonElement>;
98
+ }) => {
99
+ return (
100
+ <Button
101
+ ref={ref}
102
+ type="button"
103
+ fontWeight="normal"
104
+ size="sm"
105
+ borderRadius="sm"
106
+ marginRight={1}
107
+ {...props}
108
+ >
109
+ {props.children}
110
+ </Button>
111
+ );
112
+ };
109
113
 
110
114
  const texts = createTexts({
111
115
  showPassword: {