@vygruppen/spor-react 11.3.9 → 12.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 (322) hide show
  1. package/.turbo/turbo-build.log +32 -11
  2. package/.turbo/turbo-typegen.log +23 -0
  3. package/CHANGELOG.md +245 -0
  4. package/dist/index.d.mts +2552 -8319
  5. package/dist/index.d.ts +2552 -8319
  6. package/dist/index.js +9609 -8607
  7. package/dist/index.js.map +1 -1
  8. package/dist/index.mjs +9487 -8454
  9. package/dist/index.mjs.map +1 -1
  10. package/package.json +21 -13
  11. package/src/accordion/Accordion.tsx +96 -45
  12. package/src/accordion/Expandable.tsx +54 -127
  13. package/src/accordion/helpers.ts +31 -0
  14. package/src/accordion/types.ts +60 -0
  15. package/src/alert/Alert.tsx +101 -0
  16. package/src/alert/AlertIcon.tsx +63 -45
  17. package/src/alert/ExpandableAlert.tsx +96 -64
  18. package/src/alert/ServiceAlert.tsx +127 -125
  19. package/src/alert/{index.tsx → index.ts} +1 -2
  20. package/src/breadcrumb/Breadcrumb.tsx +39 -24
  21. package/src/button/Button.tsx +86 -105
  22. package/src/button/ButtonGroup.tsx +45 -20
  23. package/src/button/Clipboard.tsx +82 -0
  24. package/src/button/CloseButton.tsx +4 -3
  25. package/src/button/FloatingActionButton.tsx +35 -41
  26. package/src/button/IconButton.tsx +34 -30
  27. package/src/button/index.tsx +1 -0
  28. package/src/color-mode/color-mode.tsx +75 -0
  29. package/src/color-mode/index.ts +1 -0
  30. package/src/datepicker/Calendar.tsx +17 -8
  31. package/src/datepicker/CalendarCell.tsx +20 -13
  32. package/src/datepicker/CalendarGrid.tsx +18 -10
  33. package/src/datepicker/CalendarHeader.tsx +2 -0
  34. package/src/datepicker/CalendarNavigationButton.tsx +1 -0
  35. package/src/datepicker/CalendarTriggerButton.tsx +43 -45
  36. package/src/datepicker/DateField.tsx +21 -12
  37. package/src/datepicker/DatePicker.tsx +61 -58
  38. package/src/datepicker/DateRangePicker.tsx +52 -58
  39. package/src/datepicker/DateTimeSegment.tsx +13 -5
  40. package/src/datepicker/RangeCalendar.tsx +13 -7
  41. package/src/datepicker/StyledField.tsx +25 -17
  42. package/src/datepicker/TimeField.tsx +10 -8
  43. package/src/datepicker/TimePicker.tsx +48 -45
  44. package/src/datepicker/types.ts +5 -0
  45. package/src/dialog/Dialog.tsx +56 -0
  46. package/src/dialog/Drawer.tsx +187 -0
  47. package/src/dialog/index.ts +2 -0
  48. package/src/dialog/types.ts +26 -0
  49. package/src/image/index.tsx +2 -2
  50. package/src/index.tsx +5 -3
  51. package/src/input/AttachedInputs.tsx +17 -42
  52. package/src/input/CardSelect.tsx +75 -162
  53. package/src/input/Checkbox.tsx +30 -6
  54. package/src/input/CheckboxGroup.tsx +25 -16
  55. package/src/input/ChoiceChip.tsx +58 -77
  56. package/src/input/Combobox.tsx +172 -172
  57. package/src/input/CountryCodeSelect.tsx +42 -28
  58. package/src/input/Dialog.tsx +1 -0
  59. package/src/input/Field.tsx +71 -0
  60. package/src/input/Fieldset.tsx +7 -0
  61. package/src/input/Input.tsx +68 -73
  62. package/src/input/InputGroup.tsx +66 -0
  63. package/src/input/ListBox.tsx +83 -70
  64. package/src/input/NativeSelect.tsx +68 -33
  65. package/src/input/NumericStepper.tsx +173 -171
  66. package/src/input/PasswordInput.tsx +99 -52
  67. package/src/input/PhoneNumberInput.tsx +69 -72
  68. package/src/input/Popover.tsx +1 -0
  69. package/src/input/Radio.tsx +37 -17
  70. package/src/input/SearchInput.tsx +24 -86
  71. package/src/input/Select.tsx +237 -0
  72. package/src/input/Switch.tsx +60 -18
  73. package/src/input/Textarea.tsx +53 -101
  74. package/src/input/{index.tsx → index.ts} +2 -8
  75. package/src/layout/PressableCard.tsx +12 -21
  76. package/src/layout/RadioCard.tsx +68 -100
  77. package/src/layout/Separator.tsx +32 -0
  78. package/src/layout/StaticCard.tsx +13 -33
  79. package/src/layout/index.tsx +3 -7
  80. package/src/linjetag/InfoTag.tsx +16 -9
  81. package/src/linjetag/LineIcon.tsx +74 -28
  82. package/src/linjetag/TravelTag.tsx +38 -27
  83. package/src/link/TextLink.tsx +25 -16
  84. package/src/list/index.tsx +24 -2
  85. package/src/loader/ClientOnly.tsx +8 -7
  86. package/src/loader/ColorInlineLoader.tsx +4 -3
  87. package/src/loader/ColorSpinner.tsx +5 -4
  88. package/src/loader/ContentLoader.tsx +6 -4
  89. package/src/loader/DarkFullScreenLoader.tsx +11 -3
  90. package/src/loader/DarkInlineLoader.tsx +5 -3
  91. package/src/loader/DarkSpinner.tsx +7 -3
  92. package/src/loader/LightFullScreenLoader.tsx +11 -3
  93. package/src/loader/LightInlineLoader.tsx +11 -3
  94. package/src/loader/LightSpinner.tsx +5 -3
  95. package/src/loader/Lottie.tsx +3 -3
  96. package/src/loader/ProgressBar.tsx +83 -84
  97. package/src/loader/ProgressLoader.tsx +120 -75
  98. package/src/loader/Skeleton.tsx +94 -19
  99. package/src/loader/index.tsx +0 -2
  100. package/src/loader/useHydrated.tsx +1 -0
  101. package/src/loader/useRotatingLabel.tsx +2 -1
  102. package/src/logo/CargonetLogo.tsx +89 -89
  103. package/src/logo/VyLogo.tsx +61 -42
  104. package/src/logo/VyLogoPride.tsx +137 -139
  105. package/src/media-controller/JumpButton.tsx +48 -38
  106. package/src/media-controller/PlayPauseButton.tsx +31 -29
  107. package/src/media-controller/SkipButton.tsx +38 -37
  108. package/src/nudge/Nudge.tsx +195 -123
  109. package/src/nudge/index.tsx +0 -1
  110. package/src/pagination/Pagination.tsx +221 -118
  111. package/src/pagination/types.ts +23 -0
  112. package/src/popover/index.tsx +67 -0
  113. package/src/progress-indicator/ProgressDot.tsx +11 -10
  114. package/src/progress-indicator/ProgressIndicator.tsx +28 -15
  115. package/src/provider/SporProvider.tsx +17 -14
  116. package/src/stepper/Stepper.tsx +88 -85
  117. package/src/stepper/StepperContext.tsx +2 -1
  118. package/src/stepper/StepperStep.tsx +28 -21
  119. package/src/tab/Tabs.tsx +62 -12
  120. package/src/tab/index.tsx +1 -9
  121. package/src/table/Table.tsx +35 -30
  122. package/src/table/index.tsx +11 -7
  123. package/src/theme/brand.ts +7 -0
  124. package/src/theme/index.ts +45 -37
  125. package/src/theme/recipes/attached-inputs.ts +43 -0
  126. package/src/theme/recipes/badge.ts +104 -0
  127. package/src/theme/recipes/button.ts +124 -0
  128. package/src/theme/recipes/choice-chip.ts +144 -0
  129. package/src/theme/recipes/close-button.ts +41 -0
  130. package/src/theme/recipes/code.ts +14 -0
  131. package/src/theme/recipes/group.ts +19 -0
  132. package/src/theme/recipes/index.ts +29 -0
  133. package/src/theme/recipes/input.ts +89 -0
  134. package/src/theme/recipes/link.ts +64 -0
  135. package/src/theme/recipes/nudge.ts +12 -0
  136. package/src/theme/recipes/pressable-card.ts +83 -0
  137. package/src/theme/recipes/progress-loader.ts +14 -0
  138. package/src/theme/recipes/separator.ts +85 -0
  139. package/src/theme/recipes/skeleton.ts +57 -0
  140. package/src/theme/recipes/static-card.ts +39 -0
  141. package/src/theme/recipes/textarea.ts +27 -0
  142. package/src/theme/semantic-tokens/colors.ts +22 -0
  143. package/src/theme/semantic-tokens/index.ts +24 -0
  144. package/src/theme/semantic-tokens/radii.ts +14 -0
  145. package/src/theme/semantic-tokens/shadows.ts +17 -0
  146. package/src/theme/slot-recipes/accordion.ts +131 -0
  147. package/src/theme/slot-recipes/alert-expandable.ts +133 -0
  148. package/src/theme/slot-recipes/alert-service.ts +66 -0
  149. package/src/theme/slot-recipes/alert.ts +72 -0
  150. package/src/theme/slot-recipes/anatomy.ts +269 -0
  151. package/src/theme/slot-recipes/breadcrumb.ts +61 -0
  152. package/src/theme/slot-recipes/checkbox.ts +89 -0
  153. package/src/theme/slot-recipes/datepicker.ts +214 -0
  154. package/src/theme/slot-recipes/dialog.ts +221 -0
  155. package/src/theme/slot-recipes/drawer.ts +205 -0
  156. package/src/theme/slot-recipes/field.ts +79 -0
  157. package/src/theme/slot-recipes/floating-action-button.ts +131 -0
  158. package/src/theme/slot-recipes/index.ts +65 -0
  159. package/src/theme/slot-recipes/info-tag.ts +62 -0
  160. package/src/theme/slot-recipes/line-icon.ts +140 -0
  161. package/src/theme/slot-recipes/list.ts +45 -0
  162. package/src/theme/slot-recipes/listbox.ts +72 -0
  163. package/src/theme/slot-recipes/media-controller-button.ts +131 -0
  164. package/src/theme/slot-recipes/native-select.ts +54 -0
  165. package/src/theme/slot-recipes/numeric-stepper.ts +65 -0
  166. package/src/theme/slot-recipes/pagination.ts +41 -0
  167. package/src/theme/slot-recipes/popover.ts +78 -0
  168. package/src/theme/slot-recipes/progress-bar.ts +39 -0
  169. package/src/theme/slot-recipes/progress-indicator.ts +22 -0
  170. package/src/theme/slot-recipes/radio-card.ts +112 -0
  171. package/src/theme/slot-recipes/radio.ts +80 -0
  172. package/src/theme/slot-recipes/select.ts +243 -0
  173. package/src/theme/slot-recipes/stepper.ts +92 -0
  174. package/src/theme/slot-recipes/switch.ts +147 -0
  175. package/src/theme/slot-recipes/table.ts +200 -0
  176. package/src/theme/slot-recipes/tabs.ts +169 -0
  177. package/src/theme/slot-recipes/toast.ts +56 -0
  178. package/src/theme/slot-recipes/travel-tag.ts +192 -0
  179. package/src/theme/tokens/animation-styles.ts +50 -0
  180. package/src/theme/tokens/animations.ts +22 -0
  181. package/src/theme/tokens/aspect-ratios.ts +22 -0
  182. package/src/theme/tokens/blurs.ts +28 -0
  183. package/src/theme/tokens/borders.ts +26 -0
  184. package/src/theme/{foundations → tokens}/breakpoints.ts +0 -1
  185. package/src/theme/tokens/colors.ts +10 -0
  186. package/src/theme/tokens/config.ts +10 -0
  187. package/src/theme/tokens/cursor.ts +28 -0
  188. package/src/theme/tokens/durations.ts +25 -0
  189. package/src/theme/tokens/easings.ts +16 -0
  190. package/src/theme/tokens/font-sizes.ts +30 -0
  191. package/src/theme/tokens/font-weights.ts +31 -0
  192. package/src/theme/tokens/fonts.ts +8 -0
  193. package/src/theme/tokens/global-css.ts +18 -0
  194. package/src/theme/tokens/index.ts +37 -0
  195. package/src/theme/tokens/keyframes.ts +255 -0
  196. package/src/theme/tokens/letter-spacings.ts +19 -0
  197. package/src/theme/tokens/line-heights.ts +19 -0
  198. package/src/theme/tokens/radii.ts +13 -0
  199. package/src/theme/tokens/sizes.ts +51 -0
  200. package/src/theme/tokens/spacing.ts +20 -0
  201. package/src/theme/tokens/text-styles.ts +89 -0
  202. package/src/theme/tokens/z-index.ts +17 -0
  203. package/src/theme/utils/accent-utils.ts +8 -21
  204. package/src/theme/utils/bg-utils.ts +4 -6
  205. package/src/theme/utils/brand-utils.ts +6 -19
  206. package/src/theme/utils/core-utils.ts +91 -0
  207. package/src/theme/utils/floating-utils.ts +20 -39
  208. package/src/theme/utils/ghost-utils.ts +7 -21
  209. package/src/theme/utils/input-utils.ts +32 -37
  210. package/src/theme/utils/outline-utils.ts +4 -11
  211. package/src/theme/utils/surface-utils.ts +5 -19
  212. package/src/theme/utils/types.ts +1 -0
  213. package/src/toast/index.tsx +1 -1
  214. package/src/toast/toast.tsx +105 -0
  215. package/src/transition/index.ts +2 -8
  216. package/src/typography/Badge.tsx +15 -61
  217. package/src/typography/Code.tsx +16 -28
  218. package/src/typography/Heading.tsx +34 -19
  219. package/src/typography/Text.tsx +9 -6
  220. package/src/typography/{index.tsx → index.ts} +1 -0
  221. package/src/util/externals.tsx +13 -27
  222. package/tsconfig.json +5 -1
  223. package/src/accordion/Accordion.test.tsx +0 -20
  224. package/src/alert/BaseAlert.test.tsx +0 -37
  225. package/src/alert/BaseAlert.tsx +0 -34
  226. package/src/alert/ClosableAlert.test.tsx +0 -37
  227. package/src/alert/ClosableAlert.tsx +0 -85
  228. package/src/alert/ExpandableAlert.test.tsx +0 -84
  229. package/src/alert/StaticAlert.tsx +0 -33
  230. package/src/button/Button.test.tsx +0 -23
  231. package/src/datepicker/TimePicker.test.tsx +0 -74
  232. package/src/input/FormControl.tsx +0 -2
  233. package/src/input/FormErrorMessage.tsx +0 -95
  234. package/src/input/FormLabel.tsx +0 -11
  235. package/src/input/InfoSelect.tsx +0 -274
  236. package/src/input/InputElement.tsx +0 -44
  237. package/src/input/RadioGroup.tsx +0 -47
  238. package/src/layout/Divider.tsx +0 -27
  239. package/src/layout/RadioCardGroup.tsx +0 -79
  240. package/src/layout/Stack.tsx +0 -42
  241. package/src/loader/SkeletonCircle.tsx +0 -13
  242. package/src/loader/SkeletonText.tsx +0 -14
  243. package/src/media-controller/index.test.tsx +0 -59
  244. package/src/modal/Drawer.tsx +0 -120
  245. package/src/modal/FullScreenDrawer.tsx +0 -239
  246. package/src/modal/Modal.tsx +0 -15
  247. package/src/modal/ModalHeader.tsx +0 -31
  248. package/src/modal/SimpleDrawer.tsx +0 -51
  249. package/src/modal/index.tsx +0 -5
  250. package/src/nudge/WizardNudge.tsx +0 -107
  251. package/src/theme/components/accordion.ts +0 -102
  252. package/src/theme/components/alert-expandable.ts +0 -125
  253. package/src/theme/components/alert-service.ts +0 -98
  254. package/src/theme/components/alert.ts +0 -71
  255. package/src/theme/components/badge.ts +0 -109
  256. package/src/theme/components/breadcrumb.ts +0 -60
  257. package/src/theme/components/button.ts +0 -125
  258. package/src/theme/components/card-select.ts +0 -117
  259. package/src/theme/components/checkbox.ts +0 -88
  260. package/src/theme/components/choice-chip.ts +0 -161
  261. package/src/theme/components/close-button.ts +0 -48
  262. package/src/theme/components/code.ts +0 -17
  263. package/src/theme/components/datepicker.ts +0 -198
  264. package/src/theme/components/divider.ts +0 -50
  265. package/src/theme/components/drawer.ts +0 -95
  266. package/src/theme/components/fab.ts +0 -109
  267. package/src/theme/components/form-label.ts +0 -17
  268. package/src/theme/components/form.ts +0 -27
  269. package/src/theme/components/index.ts +0 -45
  270. package/src/theme/components/info-select.ts +0 -85
  271. package/src/theme/components/info-tag.ts +0 -63
  272. package/src/theme/components/input.ts +0 -28
  273. package/src/theme/components/line-icon.ts +0 -129
  274. package/src/theme/components/link.ts +0 -78
  275. package/src/theme/components/list.ts +0 -23
  276. package/src/theme/components/listbox.ts +0 -77
  277. package/src/theme/components/media-controller-button.ts +0 -97
  278. package/src/theme/components/modal.ts +0 -96
  279. package/src/theme/components/numeric-stepper.ts +0 -65
  280. package/src/theme/components/pagination.ts +0 -74
  281. package/src/theme/components/popover.ts +0 -68
  282. package/src/theme/components/pressable-card.ts +0 -72
  283. package/src/theme/components/progress-bar.ts +0 -47
  284. package/src/theme/components/progress-indicator.ts +0 -44
  285. package/src/theme/components/radio-card.ts +0 -134
  286. package/src/theme/components/radio.ts +0 -68
  287. package/src/theme/components/select.ts +0 -74
  288. package/src/theme/components/skeleton.ts +0 -40
  289. package/src/theme/components/static-card.ts +0 -82
  290. package/src/theme/components/stepper.ts +0 -100
  291. package/src/theme/components/switch.ts +0 -112
  292. package/src/theme/components/table.ts +0 -161
  293. package/src/theme/components/tabs.ts +0 -135
  294. package/src/theme/components/textarea.ts +0 -33
  295. package/src/theme/components/toast.ts +0 -28
  296. package/src/theme/components/travel-tag.ts +0 -256
  297. package/src/theme/foundations/borders.ts +0 -11
  298. package/src/theme/foundations/colors.ts +0 -12
  299. package/src/theme/foundations/config.ts +0 -5
  300. package/src/theme/foundations/fontSizes.ts +0 -29
  301. package/src/theme/foundations/fontWeights.ts +0 -5
  302. package/src/theme/foundations/fonts.ts +0 -7
  303. package/src/theme/foundations/index.ts +0 -15
  304. package/src/theme/foundations/lineHeights.ts +0 -6
  305. package/src/theme/foundations/radii.ts +0 -12
  306. package/src/theme/foundations/shadows.ts +0 -8
  307. package/src/theme/foundations/sizes.ts +0 -36
  308. package/src/theme/foundations/spacing.ts +0 -31
  309. package/src/theme/foundations/styles.ts +0 -12
  310. package/src/theme/foundations/textStyles.ts +0 -74
  311. package/src/theme/foundations/zIndices.ts +0 -17
  312. package/src/theme/utils/base-utils.ts +0 -104
  313. package/src/theme/utils/focus-utils.ts +0 -10
  314. package/src/toast/ActionToast.test.tsx +0 -22
  315. package/src/toast/ActionToast.tsx +0 -28
  316. package/src/toast/BaseToast.test.tsx +0 -27
  317. package/src/toast/BaseToast.tsx +0 -75
  318. package/src/toast/ClosableToast.test.tsx +0 -17
  319. package/src/toast/ClosableToast.tsx +0 -40
  320. package/src/toast/useToast.tsx +0 -121
  321. package/src/tooltip/Tooltip.tsx +0 -70
  322. package/src/tooltip/index.tsx +0 -1
@@ -1,23 +1,29 @@
1
+ "use client";
2
+
3
+ import { inputRecipe } from "@/theme/recipes/input";
1
4
  import {
2
- Input as ChakraInput,
5
+ chakra,
3
6
  InputProps as ChakraInputProps,
4
- FormLabel,
5
- InputGroup,
6
- InputLeftElement,
7
- InputRightElement,
8
- forwardRef,
9
- useFormControlContext,
7
+ useRecipe,
8
+ type RecipeVariantProps,
9
+ Input as ChakraInput,
10
10
  } from "@chakra-ui/react";
11
- import React, { useId } from "react";
11
+ import React, { forwardRef, PropsWithChildren } from "react";
12
+ import { Field, FieldProps } from "./Field";
13
+ import { InputGroup } from "./InputGroup";
12
14
 
13
- export type InputProps = Omit<ChakraInputProps, "size"> & {
14
- /** The input's label */
15
- label: string;
16
- /** Icon that shows up to the left */
17
- leftIcon?: React.ReactNode;
18
- /** Icon that shows up to the right */
19
- rightIcon?: React.ReactNode;
20
- };
15
+ export type InputProps = Exclude<
16
+ ChakraInputProps,
17
+ "size" | "label" | "colorPalette"
18
+ > &
19
+ FieldProps & {
20
+ /** The input's label */
21
+ label: string;
22
+ /** Element that shows up to the left */
23
+ startElement?: React.ReactNode;
24
+ /** Element that shows up to the right */
25
+ endElement?: React.ReactNode;
26
+ };
21
27
  /**
22
28
  * Inputs let you enter text or other data.
23
29
  *
@@ -30,71 +36,60 @@ export type InputProps = Omit<ChakraInputProps, "size"> & {
30
36
  * You can also add icons to the left and right of the input. Please use the 24 px icons for this.
31
37
  *
32
38
  * ```tsx
33
- * <Input label="E-mail" leftIcon={<EmailOutline24Icon />} />
39
+ * <Input label="E-mail" startElement={<EmailOutline24Icon />} />
34
40
  * ```
35
41
  *
36
- * Input has two variants base, and floating.
42
+ * Input has two variants core, and floating.
37
43
  *
38
44
  * ```tsx
39
- * <Input label="E-mail" leftIcon={<EmailOutline24Icon />} variant="floating" />
45
+ * <Input label="E-mail" startElement={<EmailOutline24Icon />} variant="floating" />
40
46
  * ```
47
+ *
48
+ * Field is added to Input, so you can add helperText, errorText, and optionalText.
49
+ *
50
+ * ```tsx
51
+ * <Input label="E-mail" startElement={<EmailOutline24Icon />} helperText="We will never share your email." />
52
+ * ```
53
+ *
54
+ * @see https://spor.vy.no/components/input
41
55
  */
42
- export const Input = forwardRef<InputProps, "input">(
43
- ({ label, leftIcon, rightIcon, id, size, ...props }, ref) => {
44
- const formControlProps = useFormControlContext();
45
- const fallbackId = `input-${useId()}`;
46
- const inputId = id ?? formControlProps?.id ?? fallbackId;
47
- const labelId = `${useId()}-label`;
48
- return (
49
- <InputGroup position="relative">
50
- {leftIcon && (
51
- <InputLeftElement pointerEvents="none">{leftIcon}</InputLeftElement>
52
- )}
53
- <ChakraInput
54
- data-attachable
55
- paddingLeft={leftIcon ? 7 : undefined}
56
- paddingRight={rightIcon ? 7 : undefined}
57
- {...props}
58
- id={inputId}
59
- aria-labelledby={labelId}
60
- ref={ref}
61
- overflow="hidden"
62
- placeholder=" " // This is needed to make the label work as expected
63
- css={{
64
- "&::-webkit-search-cancel-button": {
65
- WebkitAppearance: "none",
66
- },
67
- }}
68
- />
69
56
 
70
- <FormLabel
71
- htmlFor={inputId}
72
- id={labelId}
73
- pointerEvents="none"
74
- sx={{
75
- position: "absolute",
76
- left: "2.6rem",
77
- top: "26.9%",
78
- fontSize: "1.13rem",
79
- pointerEvents: "none",
80
- margin: 0,
81
- zIndex: 2,
82
- "input:focus + &, input[data-has-value] + &": {
83
- color: "var(--chakra-colors-gray-600)",
84
- },
85
- "input[data-has-value] + &": {
86
- transform: "translateY(-40%) scale(0.9)",
87
- },
88
- }}
57
+ const StyledInput = chakra(ChakraInput, inputRecipe);
58
+
59
+ export const Input = forwardRef<HTMLInputElement, InputProps>(
60
+ (
61
+ {
62
+ startElement,
63
+ endElement,
64
+ label,
65
+ invalid,
66
+ helperText,
67
+ errorText,
68
+ ...props
69
+ },
70
+ ref,
71
+ ) => {
72
+ return (
73
+ <Field invalid={invalid} helperText={helperText} errorText={errorText}>
74
+ <InputGroup
75
+ endElement={endElement && endElement}
76
+ startElement={startElement && startElement}
77
+ width="100%"
78
+ position="relative"
79
+ label={label}
89
80
  >
90
- {label}
91
- </FormLabel>
92
- {rightIcon && (
93
- <InputRightElement pointerEvents="none">
94
- {rightIcon}
95
- </InputRightElement>
96
- )}
97
- </InputGroup>
81
+ <StyledInput
82
+ data-attachable
83
+ ref={ref}
84
+ className="peer"
85
+ overflow="hidden"
86
+ paddingLeft={startElement ? "2.6rem" : undefined}
87
+ paddingRight={endElement ? "2.6rem" : undefined}
88
+ placeholder=""
89
+ {...props}
90
+ />
91
+ </InputGroup>
92
+ </Field>
98
93
  );
99
94
  },
100
95
  );
@@ -0,0 +1,66 @@
1
+ "use client";
2
+
3
+ import type { InputElementProps, GroupProps } from "@chakra-ui/react";
4
+ import { Group, InputElement } from "@chakra-ui/react";
5
+ import * as React from "react";
6
+ import { FieldLabel } from "./Field";
7
+
8
+ export type InputGroupProps = GroupProps & {
9
+ startElementProps?: InputElementProps;
10
+ endElementProps?: InputElementProps;
11
+ startElement?: React.ReactNode;
12
+ endElement?: React.ReactNode;
13
+ children: React.ReactElement;
14
+ label?: string;
15
+ };
16
+
17
+ /**
18
+ *
19
+ * InputGroup is a wrapper for inputs that have a startElement and/or endElement.
20
+ *
21
+ * It is not exported to users, but used internally in the Input component.
22
+ *
23
+ */
24
+
25
+ export const InputGroup = React.forwardRef<HTMLDivElement, InputGroupProps>(
26
+ (props, ref) => {
27
+ const {
28
+ startElement,
29
+ startElementProps,
30
+ endElement,
31
+ endElementProps,
32
+ label,
33
+ children,
34
+ attached,
35
+ ...rest
36
+ } = props;
37
+
38
+ return (
39
+ <Group ref={ref} {...rest}>
40
+ {startElement && (
41
+ <InputElement
42
+ pointerEvents="none"
43
+ paddingX={2}
44
+ {...startElementProps}
45
+ >
46
+ {startElement}
47
+ </InputElement>
48
+ )}
49
+ {React.cloneElement(children, {
50
+ ...children.props,
51
+ })}
52
+ {label && (
53
+ <FieldLabel left={startElement ? 4 : "0"} right={endElement ? 4 : 0}>
54
+ {label}
55
+ </FieldLabel>
56
+ )}
57
+
58
+ {endElement && (
59
+ <InputElement placement="end" paddingX={2} {...endElementProps}>
60
+ {endElement}
61
+ </InputElement>
62
+ )}
63
+ </Group>
64
+ );
65
+ },
66
+ );
@@ -1,13 +1,20 @@
1
+ "use client";
1
2
  import {
2
3
  Box,
3
- List,
4
4
  ListItem,
5
- useColorModeValue,
6
- useMultiStyleConfig,
5
+ ListRootProps,
6
+ RecipeVariantProps,
7
+ useSlotRecipe,
7
8
  type BoxProps,
8
9
  } from "@chakra-ui/react";
9
10
  import type { Node } from "@react-types/shared";
10
- import React, { useContext, useEffect, useRef } from "react";
11
+ import React, {
12
+ forwardRef,
13
+ PropsWithChildren,
14
+ useContext,
15
+ useEffect,
16
+ useRef,
17
+ } from "react";
11
18
  import {
12
19
  AriaListBoxProps,
13
20
  useListBox,
@@ -15,21 +22,30 @@ import {
15
22
  useOption,
16
23
  } from "react-aria";
17
24
  import { type ListState, type SelectState } from "react-stately";
25
+ import { List } from "..";
26
+ import { useColorModeValue } from "../color-mode";
27
+ import { listBoxSlotRecipe } from "../theme/slot-recipes/listbox";
18
28
 
19
29
  export { Item, Section } from "react-stately";
20
30
 
31
+ type ListBoxVariantProps = RecipeVariantProps<typeof listBoxSlotRecipe>;
32
+
21
33
  type ListBoxProps<T> = AriaListBoxProps<T> &
22
- Omit<BoxProps, "filter" | "autoFocus" | "children"> & {
34
+ Omit<BoxProps, "filter" | "autoFocus" | "children"> &
35
+ PropsWithChildren<ListBoxVariantProps> &
36
+ Exclude<ListRootProps, "variant"> & {
23
37
  /** External reference to the ListBox itself */
24
38
  listBoxRef: React.RefObject<HTMLUListElement>;
25
39
  /** Whether or not the listbox is waiting on new data, i.e. through a autosuggest search */
26
- isLoading?: boolean;
40
+ loading?: boolean;
27
41
  /** The state of the listbox, provided externally somehow. */
28
42
  state: ListState<T> | SelectState<T>;
29
43
  /** UI to render if the collection is empty */
30
44
  emptyContent?: React.ReactNode;
31
45
  maxWidth?: BoxProps["maxWidth"];
32
- variant?: "base" | "floating";
46
+ variant?: "core" | "floating";
47
+ children: React.ReactNode;
48
+ autoFocus?: boolean;
33
49
  };
34
50
 
35
51
  /**
@@ -66,37 +82,34 @@ type ListBoxProps<T> = AriaListBoxProps<T> &
66
82
  * );
67
83
  * ```
68
84
  */
69
- export function ListBox<T extends object>({
70
- isLoading,
71
- listBoxRef,
72
- state,
73
- maxWidth,
74
- variant,
75
- ...props
76
- }: ListBoxProps<T>) {
77
- const { listBoxProps } = useListBox(props, state, listBoxRef);
78
- const styles = useMultiStyleConfig("ListBox", { variant });
79
85
 
80
- return (
81
- <List
82
- {...listBoxProps}
83
- ref={listBoxRef}
84
- sx={styles.container}
85
- aria-busy={isLoading}
86
- maxWidth={maxWidth}
87
- variant={variant}
88
- >
89
- {state.collection.size === 0 && props.emptyContent}
90
- {Array.from(state.collection).map((item) =>
91
- item.type === "section" ? (
92
- <ListBoxSection key={item.key} section={item} state={state} />
93
- ) : (
94
- <Option key={item.key} item={item} state={state} />
95
- ),
96
- )}
97
- </List>
98
- );
99
- }
86
+ export const ListBox = forwardRef<HTMLDivElement, ListBoxProps<object>>(
87
+ (props) => {
88
+ const { loading, listBoxRef, state, maxWidth, variant, children } = props;
89
+ const { listBoxProps } = useListBox(props, state, listBoxRef);
90
+ const recipe = useSlotRecipe({ key: "listBox" });
91
+ const styles = recipe({ variant });
92
+ return (
93
+ <List
94
+ {...listBoxProps}
95
+ ref={listBoxRef}
96
+ css={styles.root}
97
+ aria-busy={loading}
98
+ maxWidth={maxWidth}
99
+ >
100
+ {state.collection.size === 0 && props.emptyContent}
101
+ {Array.from(state.collection).map((item) =>
102
+ item.type === "section" ? (
103
+ <ListBoxSection key={item.key} section={item} state={state} />
104
+ ) : (
105
+ <Option key={item.key} item={item} state={state} />
106
+ ),
107
+ )}
108
+ {children}
109
+ </List>
110
+ );
111
+ },
112
+ );
100
113
 
101
114
  /**
102
115
  * Renders a label for a listbox item.
@@ -105,12 +118,7 @@ export function ListBox<T extends object>({
105
118
  */
106
119
  export function ItemLabel({ children }: { children: React.ReactNode }) {
107
120
  let { labelProps } = useOptionContext();
108
- const styles = useMultiStyleConfig("ListBox", {});
109
- return (
110
- <Box {...labelProps} sx={styles.label}>
111
- {children}
112
- </Box>
113
- );
121
+ return <Box {...labelProps}>{children}</Box>;
114
122
  }
115
123
 
116
124
  /**
@@ -120,9 +128,10 @@ export function ItemLabel({ children }: { children: React.ReactNode }) {
120
128
  */
121
129
  export function ItemDescription({ children }: { children: React.ReactNode }) {
122
130
  let { descriptionProps } = useOptionContext();
123
- const styles = useMultiStyleConfig("ListBox", {});
131
+ const recipe = useSlotRecipe({ key: "listbox" });
132
+ const styles = recipe({});
124
133
  return (
125
- <Box {...descriptionProps} sx={styles.description}>
134
+ <Box {...descriptionProps} css={styles} fontSize={"xs"}>
126
135
  {children}
127
136
  </Box>
128
137
  );
@@ -144,7 +153,9 @@ function Option({ item, state }: OptionProps) {
144
153
  descriptionProps,
145
154
  } = useOption({ key: item.key }, state, ref);
146
155
 
147
- const styles = useMultiStyleConfig("ListBox", {});
156
+ const recipe = useSlotRecipe({ key: "listBox" });
157
+ const styles = recipe({});
158
+
148
159
  let dataFields: Record<string, boolean> = {};
149
160
  if (isSelected) {
150
161
  dataFields["data-selected"] = true;
@@ -173,10 +184,9 @@ function Option({ item, state }: OptionProps) {
173
184
  { passive: false, once: true },
174
185
  );
175
186
  }, []);
176
-
177
187
  return (
178
188
  <OptionContext.Provider value={{ labelProps, descriptionProps }}>
179
- <ListItem {...optionProps} {...dataFields} ref={ref} sx={styles.item}>
189
+ <ListItem {...optionProps} {...dataFields} ref={ref} css={styles.item}>
180
190
  {item.rendered}
181
191
  </ListItem>
182
192
  </OptionContext.Provider>
@@ -209,29 +219,32 @@ function ListBoxSection({ section, state }: ListBoxSectionProps) {
209
219
 
210
220
  const isFirstSection = section.key === state.collection.getFirstKey();
211
221
  const titleColor = useColorModeValue("darkGrey", "white");
222
+
212
223
  return (
213
- <ListItem {...itemProps}>
214
- {section.rendered && (
215
- <Box
216
- fontSize="mobile.xs"
217
- color={titleColor}
218
- paddingX={3}
219
- paddingY={1}
220
- marginTop={isFirstSection ? 0 : 3}
221
- textTransform="uppercase"
222
- fontWeight="bold"
223
- {...headingProps}
224
- >
225
- {section.rendered}
226
- </Box>
227
- )}
228
- <List {...groupProps} padding={0} listStyleType="none">
229
- {Array.from(state.collection.getChildren(section.key)).map(
230
- (item: any) => (
231
- <Option key={item.key} item={item} state={state} />
232
- ),
224
+ <List>
225
+ <ListItem {...itemProps} listStyleType={"none"}>
226
+ {section.rendered && (
227
+ <Box
228
+ fontSize="mobile.xs"
229
+ color={titleColor}
230
+ paddingX={3}
231
+ paddingTop={1}
232
+ marginTop={isFirstSection ? 0 : 2}
233
+ textTransform="uppercase"
234
+ fontWeight="bold"
235
+ {...headingProps}
236
+ >
237
+ {section.rendered}
238
+ </Box>
233
239
  )}
234
- </List>
235
- </ListItem>
240
+ <List {...groupProps} padding={0} listStyleType="none">
241
+ {Array.from(state.collection.getChildren(section.key)).map(
242
+ (item: any) => (
243
+ <Option key={item.key} item={item} state={state} />
244
+ ),
245
+ )}
246
+ </List>
247
+ </ListItem>
248
+ </List>
236
249
  );
237
250
  }
@@ -1,43 +1,78 @@
1
+ "use client";
1
2
  import {
2
- Select as ChakraSelect,
3
- SelectProps as ChakraSelectProps,
4
- forwardRef,
5
- useMultiStyleConfig,
3
+ RecipeVariantProps,
4
+ NativeSelect as ChakraNativeSelect,
5
+ useSlotRecipe,
6
6
  } from "@chakra-ui/react";
7
- import React from "react";
8
- import { FormControl, FormLabel } from ".";
7
+ import { DropdownDownFill18Icon } from "@vygruppen/spor-icon-react";
8
+ import * as React from "react";
9
+ import { nativeSelectSlotRecipe } from "../theme/slot-recipes/native-select";
10
+ import { Field } from "./Field";
11
+
12
+ type NativeSelectVariantProps = RecipeVariantProps<
13
+ typeof nativeSelectSlotRecipe
14
+ >;
15
+
16
+ type NativeSelectRootProps =
17
+ React.PropsWithChildren<NativeSelectVariantProps> & {
18
+ icon?: React.ReactNode;
19
+ label: string;
20
+ invalid?: boolean;
21
+ disabled?: boolean;
22
+ };
9
23
 
10
- export type NativeSelectProps = Exclude<
11
- ChakraSelectProps,
12
- "colorScheme" | "variant" | "size"
13
- > & { label?: string };
14
24
  /**
15
25
  * Selects let you choose between several options
16
26
  *
17
27
  * You should consider only using the Select component when you have more than 4 options. Otherwise, you should use the `<Radio>` component.
18
28
  *
19
- * ```tsx
20
- * <NativeSelect label="Select level of luxury">
21
- * <option>No luxury</option>
22
- * <option>Some luxury</option>
23
- * <option>Lots of luxury</option>
24
- * <option>I'm rich</option>
29
+ * <NativeSelect label="Choose language">
30
+ * <option>Norwegian (Bokmål)</option>
31
+ * <option>Norwegian (Nynorsk)</option>
32
+ * <option>Sami</option>
33
+ * <option>Swedish</option>
34
+ * <option>Danish</option>
35
+ * <option>Finnish</option>
36
+ * <option>English</option>
25
37
  * </NativeSelect>
26
- * ```
38
+ *
27
39
  */
28
- export const NativeSelect = forwardRef<NativeSelectProps, "select">(
29
- ({ label, ...props }, ref) => {
30
- const styles = useMultiStyleConfig("Select", props);
31
- return (
32
- <FormControl>
33
- <ChakraSelect
34
- data-attachable
35
- {...props}
36
- rootProps={{ __css: styles.root }}
37
- ref={ref}
38
- />
39
- {label && <FormLabel>{label}</FormLabel>}
40
- </FormControl>
41
- );
42
- },
43
- );
40
+
41
+ export const NativeSelect = React.forwardRef<
42
+ HTMLDivElement,
43
+ NativeSelectRootProps
44
+ >(function NativeSelect(props, ref) {
45
+ const {
46
+ icon,
47
+ children,
48
+ variant = "core",
49
+ label,
50
+ invalid,
51
+ disabled,
52
+ ...rest
53
+ } = props;
54
+
55
+ const recipe = useSlotRecipe({ recipe: nativeSelectSlotRecipe });
56
+ const styles = recipe({ variant });
57
+
58
+ return (
59
+ <Field label={label} invalid={invalid} disabled={disabled}>
60
+ <ChakraNativeSelect.Root
61
+ ref={ref}
62
+ css={styles.root}
63
+ aria-disabled={disabled}
64
+ >
65
+ <ChakraNativeSelect.Field
66
+ css={styles.field}
67
+ aria-invalid={invalid}
68
+ {...rest}
69
+ >
70
+ {children}
71
+ </ChakraNativeSelect.Field>
72
+ <ChakraNativeSelect.Indicator css={styles.icon}>
73
+ <DropdownDownFill18Icon />
74
+ </ChakraNativeSelect.Indicator>
75
+ </ChakraNativeSelect.Root>
76
+ </Field>
77
+ );
78
+ });