@vygruppen/spor-react 11.3.10 → 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.
- package/.turbo/turbo-build.log +32 -11
- package/.turbo/turbo-typegen.log +23 -0
- package/CHANGELOG.md +239 -0
- package/dist/index.d.mts +2552 -8319
- package/dist/index.d.ts +2552 -8319
- package/dist/index.js +9609 -8608
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +9487 -8455
- package/dist/index.mjs.map +1 -1
- package/package.json +21 -13
- package/src/accordion/Accordion.tsx +96 -45
- package/src/accordion/Expandable.tsx +54 -127
- package/src/accordion/helpers.ts +31 -0
- package/src/accordion/types.ts +60 -0
- package/src/alert/Alert.tsx +101 -0
- package/src/alert/AlertIcon.tsx +63 -45
- package/src/alert/ExpandableAlert.tsx +96 -64
- package/src/alert/ServiceAlert.tsx +127 -125
- package/src/alert/{index.tsx → index.ts} +1 -2
- package/src/breadcrumb/Breadcrumb.tsx +39 -24
- package/src/button/Button.tsx +86 -105
- package/src/button/ButtonGroup.tsx +45 -20
- package/src/button/Clipboard.tsx +82 -0
- package/src/button/CloseButton.tsx +4 -3
- package/src/button/FloatingActionButton.tsx +35 -41
- package/src/button/IconButton.tsx +34 -30
- package/src/button/index.tsx +1 -0
- package/src/color-mode/color-mode.tsx +75 -0
- package/src/color-mode/index.ts +1 -0
- package/src/datepicker/Calendar.tsx +17 -8
- package/src/datepicker/CalendarCell.tsx +20 -13
- package/src/datepicker/CalendarGrid.tsx +18 -10
- package/src/datepicker/CalendarHeader.tsx +2 -0
- package/src/datepicker/CalendarNavigationButton.tsx +1 -0
- package/src/datepicker/CalendarTriggerButton.tsx +43 -45
- package/src/datepicker/DateField.tsx +21 -12
- package/src/datepicker/DatePicker.tsx +61 -58
- package/src/datepicker/DateRangePicker.tsx +52 -58
- package/src/datepicker/DateTimeSegment.tsx +13 -5
- package/src/datepicker/RangeCalendar.tsx +13 -7
- package/src/datepicker/StyledField.tsx +25 -17
- package/src/datepicker/TimeField.tsx +10 -8
- package/src/datepicker/TimePicker.tsx +48 -45
- package/src/datepicker/types.ts +5 -0
- package/src/dialog/Dialog.tsx +56 -0
- package/src/dialog/Drawer.tsx +187 -0
- package/src/dialog/index.ts +2 -0
- package/src/dialog/types.ts +26 -0
- package/src/image/index.tsx +2 -2
- package/src/index.tsx +5 -3
- package/src/input/AttachedInputs.tsx +17 -42
- package/src/input/CardSelect.tsx +75 -162
- package/src/input/Checkbox.tsx +30 -6
- package/src/input/CheckboxGroup.tsx +25 -16
- package/src/input/ChoiceChip.tsx +58 -77
- package/src/input/Combobox.tsx +172 -172
- package/src/input/CountryCodeSelect.tsx +42 -28
- package/src/input/Dialog.tsx +1 -0
- package/src/input/Field.tsx +71 -0
- package/src/input/Fieldset.tsx +7 -0
- package/src/input/Input.tsx +68 -73
- package/src/input/InputGroup.tsx +66 -0
- package/src/input/ListBox.tsx +83 -70
- package/src/input/NativeSelect.tsx +68 -33
- package/src/input/NumericStepper.tsx +173 -171
- package/src/input/PasswordInput.tsx +99 -52
- package/src/input/PhoneNumberInput.tsx +69 -72
- package/src/input/Popover.tsx +1 -0
- package/src/input/Radio.tsx +37 -17
- package/src/input/SearchInput.tsx +24 -86
- package/src/input/Select.tsx +237 -0
- package/src/input/Switch.tsx +60 -18
- package/src/input/Textarea.tsx +53 -101
- package/src/input/{index.tsx → index.ts} +2 -8
- package/src/layout/PressableCard.tsx +12 -21
- package/src/layout/RadioCard.tsx +68 -101
- package/src/layout/Separator.tsx +32 -0
- package/src/layout/StaticCard.tsx +13 -33
- package/src/layout/index.tsx +3 -7
- package/src/linjetag/InfoTag.tsx +16 -9
- package/src/linjetag/LineIcon.tsx +74 -28
- package/src/linjetag/TravelTag.tsx +38 -27
- package/src/link/TextLink.tsx +25 -16
- package/src/list/index.tsx +24 -2
- package/src/loader/ClientOnly.tsx +8 -7
- package/src/loader/ColorInlineLoader.tsx +4 -3
- package/src/loader/ColorSpinner.tsx +5 -4
- package/src/loader/ContentLoader.tsx +6 -4
- package/src/loader/DarkFullScreenLoader.tsx +11 -3
- package/src/loader/DarkInlineLoader.tsx +5 -3
- package/src/loader/DarkSpinner.tsx +7 -3
- package/src/loader/LightFullScreenLoader.tsx +11 -3
- package/src/loader/LightInlineLoader.tsx +11 -3
- package/src/loader/LightSpinner.tsx +5 -3
- package/src/loader/Lottie.tsx +3 -3
- package/src/loader/ProgressBar.tsx +83 -84
- package/src/loader/ProgressLoader.tsx +120 -75
- package/src/loader/Skeleton.tsx +94 -19
- package/src/loader/index.tsx +0 -2
- package/src/loader/useHydrated.tsx +1 -0
- package/src/loader/useRotatingLabel.tsx +2 -1
- package/src/logo/CargonetLogo.tsx +89 -89
- package/src/logo/VyLogo.tsx +61 -42
- package/src/logo/VyLogoPride.tsx +137 -139
- package/src/media-controller/JumpButton.tsx +48 -38
- package/src/media-controller/PlayPauseButton.tsx +31 -29
- package/src/media-controller/SkipButton.tsx +38 -37
- package/src/nudge/Nudge.tsx +195 -123
- package/src/nudge/index.tsx +0 -1
- package/src/pagination/Pagination.tsx +221 -118
- package/src/pagination/types.ts +23 -0
- package/src/popover/index.tsx +67 -0
- package/src/progress-indicator/ProgressDot.tsx +11 -10
- package/src/progress-indicator/ProgressIndicator.tsx +28 -15
- package/src/provider/SporProvider.tsx +17 -14
- package/src/stepper/Stepper.tsx +88 -85
- package/src/stepper/StepperContext.tsx +2 -1
- package/src/stepper/StepperStep.tsx +28 -21
- package/src/tab/Tabs.tsx +62 -12
- package/src/tab/index.tsx +1 -9
- package/src/table/Table.tsx +35 -30
- package/src/table/index.tsx +11 -7
- package/src/theme/brand.ts +7 -0
- package/src/theme/index.ts +45 -37
- package/src/theme/recipes/attached-inputs.ts +43 -0
- package/src/theme/recipes/badge.ts +104 -0
- package/src/theme/recipes/button.ts +124 -0
- package/src/theme/recipes/choice-chip.ts +144 -0
- package/src/theme/recipes/close-button.ts +41 -0
- package/src/theme/recipes/code.ts +14 -0
- package/src/theme/recipes/group.ts +19 -0
- package/src/theme/recipes/index.ts +29 -0
- package/src/theme/recipes/input.ts +89 -0
- package/src/theme/recipes/link.ts +64 -0
- package/src/theme/recipes/nudge.ts +12 -0
- package/src/theme/recipes/pressable-card.ts +83 -0
- package/src/theme/recipes/progress-loader.ts +14 -0
- package/src/theme/recipes/separator.ts +85 -0
- package/src/theme/recipes/skeleton.ts +57 -0
- package/src/theme/recipes/static-card.ts +39 -0
- package/src/theme/recipes/textarea.ts +27 -0
- package/src/theme/semantic-tokens/colors.ts +22 -0
- package/src/theme/semantic-tokens/index.ts +24 -0
- package/src/theme/semantic-tokens/radii.ts +14 -0
- package/src/theme/semantic-tokens/shadows.ts +17 -0
- package/src/theme/slot-recipes/accordion.ts +131 -0
- package/src/theme/slot-recipes/alert-expandable.ts +133 -0
- package/src/theme/slot-recipes/alert-service.ts +66 -0
- package/src/theme/slot-recipes/alert.ts +72 -0
- package/src/theme/slot-recipes/anatomy.ts +269 -0
- package/src/theme/slot-recipes/breadcrumb.ts +61 -0
- package/src/theme/slot-recipes/checkbox.ts +89 -0
- package/src/theme/slot-recipes/datepicker.ts +214 -0
- package/src/theme/slot-recipes/dialog.ts +221 -0
- package/src/theme/slot-recipes/drawer.ts +205 -0
- package/src/theme/slot-recipes/field.ts +79 -0
- package/src/theme/slot-recipes/floating-action-button.ts +131 -0
- package/src/theme/slot-recipes/index.ts +65 -0
- package/src/theme/slot-recipes/info-tag.ts +62 -0
- package/src/theme/slot-recipes/line-icon.ts +140 -0
- package/src/theme/slot-recipes/list.ts +45 -0
- package/src/theme/slot-recipes/listbox.ts +72 -0
- package/src/theme/slot-recipes/media-controller-button.ts +131 -0
- package/src/theme/slot-recipes/native-select.ts +54 -0
- package/src/theme/slot-recipes/numeric-stepper.ts +65 -0
- package/src/theme/slot-recipes/pagination.ts +41 -0
- package/src/theme/slot-recipes/popover.ts +78 -0
- package/src/theme/slot-recipes/progress-bar.ts +39 -0
- package/src/theme/slot-recipes/progress-indicator.ts +22 -0
- package/src/theme/slot-recipes/radio-card.ts +112 -0
- package/src/theme/slot-recipes/radio.ts +80 -0
- package/src/theme/slot-recipes/select.ts +243 -0
- package/src/theme/slot-recipes/stepper.ts +92 -0
- package/src/theme/slot-recipes/switch.ts +147 -0
- package/src/theme/slot-recipes/table.ts +200 -0
- package/src/theme/slot-recipes/tabs.ts +169 -0
- package/src/theme/slot-recipes/toast.ts +56 -0
- package/src/theme/slot-recipes/travel-tag.ts +192 -0
- package/src/theme/tokens/animation-styles.ts +50 -0
- package/src/theme/tokens/animations.ts +22 -0
- package/src/theme/tokens/aspect-ratios.ts +22 -0
- package/src/theme/tokens/blurs.ts +28 -0
- package/src/theme/tokens/borders.ts +26 -0
- package/src/theme/{foundations → tokens}/breakpoints.ts +0 -1
- package/src/theme/tokens/colors.ts +10 -0
- package/src/theme/tokens/config.ts +10 -0
- package/src/theme/tokens/cursor.ts +28 -0
- package/src/theme/tokens/durations.ts +25 -0
- package/src/theme/tokens/easings.ts +16 -0
- package/src/theme/tokens/font-sizes.ts +30 -0
- package/src/theme/tokens/font-weights.ts +31 -0
- package/src/theme/tokens/fonts.ts +8 -0
- package/src/theme/tokens/global-css.ts +18 -0
- package/src/theme/tokens/index.ts +37 -0
- package/src/theme/tokens/keyframes.ts +255 -0
- package/src/theme/tokens/letter-spacings.ts +19 -0
- package/src/theme/tokens/line-heights.ts +19 -0
- package/src/theme/tokens/radii.ts +13 -0
- package/src/theme/tokens/sizes.ts +51 -0
- package/src/theme/tokens/spacing.ts +20 -0
- package/src/theme/tokens/text-styles.ts +89 -0
- package/src/theme/tokens/z-index.ts +17 -0
- package/src/theme/utils/accent-utils.ts +8 -21
- package/src/theme/utils/bg-utils.ts +4 -6
- package/src/theme/utils/brand-utils.ts +6 -19
- package/src/theme/utils/core-utils.ts +91 -0
- package/src/theme/utils/floating-utils.ts +20 -39
- package/src/theme/utils/ghost-utils.ts +7 -21
- package/src/theme/utils/input-utils.ts +32 -37
- package/src/theme/utils/outline-utils.ts +4 -11
- package/src/theme/utils/surface-utils.ts +5 -19
- package/src/theme/utils/types.ts +1 -0
- package/src/toast/index.tsx +1 -1
- package/src/toast/toast.tsx +105 -0
- package/src/transition/index.ts +2 -8
- package/src/typography/Badge.tsx +15 -61
- package/src/typography/Code.tsx +16 -28
- package/src/typography/Heading.tsx +34 -19
- package/src/typography/Text.tsx +9 -6
- package/src/typography/{index.tsx → index.ts} +1 -0
- package/src/util/externals.tsx +13 -27
- package/tsconfig.json +5 -1
- package/src/accordion/Accordion.test.tsx +0 -20
- package/src/alert/BaseAlert.test.tsx +0 -37
- package/src/alert/BaseAlert.tsx +0 -34
- package/src/alert/ClosableAlert.test.tsx +0 -37
- package/src/alert/ClosableAlert.tsx +0 -85
- package/src/alert/ExpandableAlert.test.tsx +0 -84
- package/src/alert/StaticAlert.tsx +0 -33
- package/src/button/Button.test.tsx +0 -23
- package/src/datepicker/TimePicker.test.tsx +0 -74
- package/src/input/FormControl.tsx +0 -2
- package/src/input/FormErrorMessage.tsx +0 -95
- package/src/input/FormLabel.tsx +0 -11
- package/src/input/InfoSelect.tsx +0 -274
- package/src/input/InputElement.tsx +0 -44
- package/src/input/RadioGroup.tsx +0 -47
- package/src/layout/Divider.tsx +0 -27
- package/src/layout/RadioCardGroup.tsx +0 -79
- package/src/layout/Stack.tsx +0 -42
- package/src/loader/SkeletonCircle.tsx +0 -13
- package/src/loader/SkeletonText.tsx +0 -14
- package/src/media-controller/index.test.tsx +0 -59
- package/src/modal/Drawer.tsx +0 -120
- package/src/modal/FullScreenDrawer.tsx +0 -239
- package/src/modal/Modal.tsx +0 -15
- package/src/modal/ModalHeader.tsx +0 -31
- package/src/modal/SimpleDrawer.tsx +0 -51
- package/src/modal/index.tsx +0 -5
- package/src/nudge/WizardNudge.tsx +0 -107
- package/src/theme/components/accordion.ts +0 -102
- package/src/theme/components/alert-expandable.ts +0 -125
- package/src/theme/components/alert-service.ts +0 -98
- package/src/theme/components/alert.ts +0 -71
- package/src/theme/components/badge.ts +0 -109
- package/src/theme/components/breadcrumb.ts +0 -60
- package/src/theme/components/button.ts +0 -125
- package/src/theme/components/card-select.ts +0 -117
- package/src/theme/components/checkbox.ts +0 -88
- package/src/theme/components/choice-chip.ts +0 -161
- package/src/theme/components/close-button.ts +0 -48
- package/src/theme/components/code.ts +0 -17
- package/src/theme/components/datepicker.ts +0 -198
- package/src/theme/components/divider.ts +0 -50
- package/src/theme/components/drawer.ts +0 -95
- package/src/theme/components/fab.ts +0 -109
- package/src/theme/components/form-label.ts +0 -17
- package/src/theme/components/form.ts +0 -27
- package/src/theme/components/index.ts +0 -45
- package/src/theme/components/info-select.ts +0 -85
- package/src/theme/components/info-tag.ts +0 -63
- package/src/theme/components/input.ts +0 -28
- package/src/theme/components/line-icon.ts +0 -129
- package/src/theme/components/link.ts +0 -78
- package/src/theme/components/list.ts +0 -23
- package/src/theme/components/listbox.ts +0 -77
- package/src/theme/components/media-controller-button.ts +0 -97
- package/src/theme/components/modal.ts +0 -96
- package/src/theme/components/numeric-stepper.ts +0 -65
- package/src/theme/components/pagination.ts +0 -74
- package/src/theme/components/popover.ts +0 -68
- package/src/theme/components/pressable-card.ts +0 -72
- package/src/theme/components/progress-bar.ts +0 -47
- package/src/theme/components/progress-indicator.ts +0 -44
- package/src/theme/components/radio-card.ts +0 -134
- package/src/theme/components/radio.ts +0 -68
- package/src/theme/components/select.ts +0 -74
- package/src/theme/components/skeleton.ts +0 -40
- package/src/theme/components/static-card.ts +0 -82
- package/src/theme/components/stepper.ts +0 -100
- package/src/theme/components/switch.ts +0 -112
- package/src/theme/components/table.ts +0 -161
- package/src/theme/components/tabs.ts +0 -135
- package/src/theme/components/textarea.ts +0 -33
- package/src/theme/components/toast.ts +0 -28
- package/src/theme/components/travel-tag.ts +0 -256
- package/src/theme/foundations/borders.ts +0 -11
- package/src/theme/foundations/colors.ts +0 -12
- package/src/theme/foundations/config.ts +0 -5
- package/src/theme/foundations/fontSizes.ts +0 -29
- package/src/theme/foundations/fontWeights.ts +0 -5
- package/src/theme/foundations/fonts.ts +0 -7
- package/src/theme/foundations/index.ts +0 -15
- package/src/theme/foundations/lineHeights.ts +0 -6
- package/src/theme/foundations/radii.ts +0 -12
- package/src/theme/foundations/shadows.ts +0 -8
- package/src/theme/foundations/sizes.ts +0 -36
- package/src/theme/foundations/spacing.ts +0 -31
- package/src/theme/foundations/styles.ts +0 -12
- package/src/theme/foundations/textStyles.ts +0 -74
- package/src/theme/foundations/zIndices.ts +0 -17
- package/src/theme/utils/base-utils.ts +0 -104
- package/src/theme/utils/focus-utils.ts +0 -10
- package/src/toast/ActionToast.test.tsx +0 -22
- package/src/toast/ActionToast.tsx +0 -28
- package/src/toast/BaseToast.test.tsx +0 -27
- package/src/toast/BaseToast.tsx +0 -75
- package/src/toast/ClosableToast.test.tsx +0 -17
- package/src/toast/ClosableToast.tsx +0 -40
- package/src/toast/useToast.tsx +0 -121
- package/src/tooltip/Tooltip.tsx +0 -70
- package/src/tooltip/index.tsx +0 -1
@@ -1,48 +1,50 @@
|
|
1
|
+
"use client";
|
1
2
|
import {
|
2
3
|
chakra,
|
4
|
+
RecipeVariantProps,
|
3
5
|
useControllableState,
|
4
|
-
|
5
|
-
useMultiStyleConfig,
|
6
|
+
useSlotRecipe,
|
6
7
|
} from "@chakra-ui/react";
|
7
|
-
import React, { useRef } from "react";
|
8
|
-
import {
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
8
|
+
import React, { PropsWithChildren, useRef } from "react";
|
9
|
+
import { BoxProps, createTexts, IconButton, useTranslation } from "..";
|
10
|
+
import { numericStepperRecipe } from "../theme/slot-recipes/numeric-stepper";
|
11
|
+
import { Field } from "./Field";
|
12
|
+
|
13
|
+
type NumericStepperVariants = RecipeVariantProps<typeof numericStepperRecipe>;
|
14
|
+
|
15
|
+
export type NumericStepperProps = BoxProps &
|
16
|
+
PropsWithChildren<NumericStepperVariants> & {
|
17
|
+
children: React.ReactNode;
|
18
|
+
/** The name of the input field */
|
19
|
+
name?: string;
|
20
|
+
/** The current value */
|
21
|
+
value?: number;
|
22
|
+
/** A default value, if uncontrolled */
|
23
|
+
defaultValue?: number;
|
24
|
+
/** Callback for when the value changes */
|
25
|
+
onChange?: (value: number) => void;
|
26
|
+
/** Optional minimum value. Defaults to 0 */
|
27
|
+
minValue?: number;
|
28
|
+
/** Optional maximum value. Defaults to 99 */
|
29
|
+
maxValue?: number;
|
30
|
+
/** Whether the stepper is disabled or not */
|
31
|
+
disabled?: boolean;
|
32
|
+
/** Whether to show input field or not */
|
33
|
+
withInput?: boolean;
|
34
|
+
/** The amount to increase/decrease when pressing +/- */
|
35
|
+
stepSize?: number;
|
36
|
+
/** Whether to show the number input when value is zero */
|
37
|
+
showZero?: boolean;
|
38
|
+
/** Name added to the aria-label of subtract and add buttons. */
|
39
|
+
ariaLabelContext?: { singular: string; plural: string };
|
40
|
+
} & Omit<BoxProps, "onChange">;
|
16
41
|
|
17
|
-
type NumericStepperProps = {
|
18
|
-
/** The name of the input field */
|
19
|
-
name?: string;
|
20
|
-
/** The current value */
|
21
|
-
value?: number;
|
22
|
-
/** A default value, if uncontrolled */
|
23
|
-
defaultValue?: number;
|
24
|
-
/** Callback for when the value changes */
|
25
|
-
onChange?: (value: number) => void;
|
26
|
-
/** Optional minimum value. Defaults to 0 */
|
27
|
-
minValue?: number;
|
28
|
-
/** Optional maximum value. Defaults to 99 */
|
29
|
-
maxValue?: number;
|
30
|
-
/** Whether the stepper is disabled or not */
|
31
|
-
isDisabled?: boolean;
|
32
|
-
/** Whether to show input field or not */
|
33
|
-
withInput?: boolean;
|
34
|
-
/** The amount to increase/decrease when pressing +/- */
|
35
|
-
stepSize?: number;
|
36
|
-
/** Whether to show the number input when value is zero */
|
37
|
-
showZero?: boolean;
|
38
|
-
/** Name added to the aria-label of subtract and add buttons. */
|
39
|
-
ariaLabelContext?: { singular: string; plural: string };
|
40
|
-
} & Omit<BoxProps, "onChange">;
|
41
42
|
/** A simple stepper component for integer values
|
42
43
|
*
|
43
44
|
* Allows you to choose a given integer value, like for example the number of
|
44
45
|
* adults on your journey.
|
45
46
|
*
|
47
|
+
* @example
|
46
48
|
* ```tsx
|
47
49
|
* <NumericStepper value={value} onChange={setValue} />
|
48
50
|
* ```
|
@@ -53,128 +55,137 @@ type NumericStepperProps = {
|
|
53
55
|
* <NumericStepper value={value} onChange={setValue} minValue={1} maxValue={10} stepSize={3} />
|
54
56
|
* ```
|
55
57
|
*
|
56
|
-
* You can use the NumericStepper inside of a
|
58
|
+
* You can use the NumericStepper inside of a Field component to get IDs etc linked up automatically:
|
57
59
|
*
|
58
60
|
* ```tsx
|
59
|
-
* <
|
60
|
-
* <FormLabel>Number of adults</FormLabel>
|
61
|
-
* <NumericStepper />
|
62
|
-
* </FormControl>
|
61
|
+
* <NumericStepper />
|
63
62
|
* ```
|
63
|
+
* @see https://spor.vy.no/components/numeric-stepper
|
64
64
|
*/
|
65
|
-
export function NumericStepper({
|
66
|
-
name: nameProp,
|
67
|
-
id: idProp,
|
68
|
-
value: valueProp,
|
69
|
-
defaultValue = 1,
|
70
|
-
onChange: onChangeProp,
|
71
|
-
minValue = 0,
|
72
|
-
maxValue = 99,
|
73
|
-
isDisabled,
|
74
|
-
withInput = true,
|
75
|
-
stepSize = 1,
|
76
|
-
showZero = false,
|
77
|
-
ariaLabelContext = { singular: "", plural: "" },
|
78
|
-
...boxProps
|
79
|
-
}: NumericStepperProps) {
|
80
|
-
const addButtonRef = useRef<HTMLButtonElement>(null);
|
81
|
-
const { t } = useTranslation();
|
82
|
-
const styles = useMultiStyleConfig("NumericStepper", {});
|
83
|
-
const [value, onChange] = useControllableState<number>({
|
84
|
-
value: valueProp,
|
85
|
-
onChange: onChangeProp,
|
86
|
-
defaultValue,
|
87
|
-
});
|
88
|
-
const formControlProps = useFormControl({ id: idProp, isDisabled });
|
89
|
-
const clampedStepSize = Math.max(Math.min(stepSize, 10), 1);
|
90
65
|
|
91
|
-
|
92
|
-
|
93
|
-
|
66
|
+
export const NumericStepper = React.forwardRef<
|
67
|
+
HTMLDivElement,
|
68
|
+
NumericStepperProps
|
69
|
+
>(
|
70
|
+
(
|
71
|
+
{
|
72
|
+
name: nameProp,
|
73
|
+
id: idProp,
|
74
|
+
value: valueProp,
|
75
|
+
defaultValue = 1,
|
76
|
+
onChange: onChangeProp,
|
77
|
+
minValue = 0,
|
78
|
+
maxValue = 99,
|
79
|
+
disabled,
|
80
|
+
withInput = true,
|
81
|
+
stepSize = 1,
|
82
|
+
showZero = false,
|
83
|
+
ariaLabelContext = { singular: "", plural: "" },
|
84
|
+
}: NumericStepperProps,
|
85
|
+
ref,
|
86
|
+
) => {
|
87
|
+
const addButtonRef = useRef<HTMLButtonElement>(null);
|
88
|
+
const { t } = useTranslation();
|
89
|
+
const recipe = useSlotRecipe({ recipe: numericStepperRecipe });
|
90
|
+
const styles = recipe();
|
91
|
+
const [value, onChange] = useControllableState<number>({
|
92
|
+
value: valueProp,
|
93
|
+
onChange: onChangeProp,
|
94
|
+
defaultValue,
|
95
|
+
});
|
96
|
+
const clampedStepSize = Math.max(Math.min(stepSize, 10), 1);
|
94
97
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
/>
|
115
|
-
{withInput ? (
|
116
|
-
<chakra.input
|
117
|
-
type="number"
|
118
|
-
min={minValue}
|
119
|
-
max={maxValue}
|
120
|
-
name={nameProp}
|
121
|
-
value={value}
|
122
|
-
{...formControlProps}
|
123
|
-
id={!showZero && value === 0 ? undefined : formControlProps.id}
|
124
|
-
sx={styles.input}
|
125
|
-
width={`${Math.max(value.toString().length + 1, 3)}ch`}
|
126
|
-
visibility={!showZero && value === 0 ? "hidden" : "visible"}
|
127
|
-
aria-live="assertive"
|
128
|
-
aria-label={
|
129
|
-
ariaLabelContext.plural !== ""
|
130
|
-
? t(texts.currentNumberAriaLabel(ariaLabelContext.plural))
|
131
|
-
: ""
|
132
|
-
}
|
133
|
-
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
134
|
-
const numericInput = Number(e.target.value);
|
135
|
-
if (Number.isNaN(numericInput)) {
|
136
|
-
return;
|
137
|
-
}
|
138
|
-
onChange(Math.max(Math.min(numericInput, maxValue), minValue));
|
139
|
-
if (
|
140
|
-
!showZero &&
|
141
|
-
Math.max(Math.min(numericInput, maxValue), minValue) === 0
|
142
|
-
) {
|
98
|
+
const focusOnAddButton = () => {
|
99
|
+
addButtonRef.current?.focus();
|
100
|
+
};
|
101
|
+
|
102
|
+
return (
|
103
|
+
<Field css={styles.root} flexDirection="row" width="auto">
|
104
|
+
<VerySmallButton
|
105
|
+
icon={<SubtractIcon stepLabel={clampedStepSize} />}
|
106
|
+
aria-label={t(
|
107
|
+
texts.decrementButtonAriaLabel(
|
108
|
+
clampedStepSize,
|
109
|
+
stepSize === 1
|
110
|
+
? ariaLabelContext.singular
|
111
|
+
: ariaLabelContext.plural,
|
112
|
+
),
|
113
|
+
)}
|
114
|
+
onClick={() => {
|
115
|
+
onChange(Math.max(value - clampedStepSize, minValue));
|
116
|
+
if (Math.max(value - clampedStepSize, minValue) <= minValue) {
|
143
117
|
focusOnAddButton();
|
144
118
|
}
|
145
119
|
}}
|
120
|
+
visibility={value <= minValue ? "hidden" : "visible"}
|
121
|
+
disabled={disabled}
|
122
|
+
id={value <= minValue ? undefined : idProp}
|
146
123
|
/>
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
124
|
+
{withInput ? (
|
125
|
+
<chakra.input
|
126
|
+
min={minValue}
|
127
|
+
max={maxValue}
|
128
|
+
name={nameProp}
|
129
|
+
value={value}
|
130
|
+
disabled={disabled}
|
131
|
+
id={!showZero && value === 0 ? undefined : idProp}
|
132
|
+
css={styles.input}
|
133
|
+
width={`${Math.max(value.toString().length + 1, 3)}ch`}
|
134
|
+
visibility={!showZero && value === 0 ? "hidden" : "visible"}
|
135
|
+
aria-live="assertive"
|
136
|
+
aria-label={
|
137
|
+
ariaLabelContext.plural !== ""
|
138
|
+
? t(texts.currentNumberAriaLabel(ariaLabelContext.plural))
|
139
|
+
: ""
|
140
|
+
}
|
141
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
142
|
+
const numericInput = Number(e.target.value);
|
143
|
+
if (Number.isNaN(numericInput)) {
|
144
|
+
return;
|
145
|
+
}
|
146
|
+
onChange(Math.max(Math.min(numericInput, maxValue), minValue));
|
147
|
+
if (
|
148
|
+
!showZero &&
|
149
|
+
Math.max(Math.min(numericInput, maxValue), minValue) === 0
|
150
|
+
) {
|
151
|
+
focusOnAddButton();
|
152
|
+
}
|
153
|
+
}}
|
154
|
+
/>
|
155
|
+
) : (
|
156
|
+
<chakra.text
|
157
|
+
css={styles}
|
158
|
+
visibility={!showZero && value === 0 ? "hidden" : "visible"}
|
159
|
+
aria-live="assertive"
|
160
|
+
aria-label={
|
161
|
+
ariaLabelContext.plural !== ""
|
162
|
+
? t(texts.currentNumberAriaLabel(ariaLabelContext.plural))
|
163
|
+
: ""
|
164
|
+
}
|
165
|
+
>
|
166
|
+
{value}
|
167
|
+
</chakra.text>
|
169
168
|
)}
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
169
|
+
<VerySmallButton
|
170
|
+
ref={addButtonRef}
|
171
|
+
icon={<AddIcon stepLabel={clampedStepSize} />}
|
172
|
+
aria-label={t(
|
173
|
+
texts.incrementButtonAriaLabel(
|
174
|
+
clampedStepSize,
|
175
|
+
stepSize === 1
|
176
|
+
? ariaLabelContext.singular
|
177
|
+
: ariaLabelContext.plural,
|
178
|
+
),
|
179
|
+
)}
|
180
|
+
onClick={() => onChange(Math.min(value + clampedStepSize, maxValue))}
|
181
|
+
visibility={value >= maxValue ? "hidden" : "visible"}
|
182
|
+
disabled={disabled}
|
183
|
+
id={value >= maxValue ? undefined : idProp}
|
184
|
+
/>
|
185
|
+
</Field>
|
186
|
+
);
|
187
|
+
},
|
188
|
+
);
|
178
189
|
|
179
190
|
type VerySmallButtonProps = {
|
180
191
|
/** The icon to render */
|
@@ -186,18 +197,23 @@ type VerySmallButtonProps = {
|
|
186
197
|
/** Whether or not the button is hidden */
|
187
198
|
visibility?: "visible" | "hidden";
|
188
199
|
/** Whether or not the button is disabled */
|
189
|
-
|
200
|
+
disabled?: boolean;
|
190
201
|
/** The ID of the button */
|
191
202
|
id?: string;
|
192
203
|
};
|
204
|
+
|
193
205
|
/** Internal override for extra small icon buttons */
|
194
|
-
const VerySmallButton = React.forwardRef
|
195
|
-
|
206
|
+
const VerySmallButton = React.forwardRef<
|
207
|
+
HTMLButtonElement,
|
208
|
+
VerySmallButtonProps
|
209
|
+
>((props, ref) => {
|
210
|
+
const recipe = useSlotRecipe({ recipe: numericStepperRecipe });
|
211
|
+
const styles = recipe({ colorPalette: "default" });
|
196
212
|
return (
|
197
213
|
<IconButton
|
198
214
|
variant="primary"
|
199
215
|
size="xs"
|
200
|
-
|
216
|
+
css={styles.button}
|
201
217
|
ref={ref}
|
202
218
|
{...props}
|
203
219
|
/>
|
@@ -206,16 +222,9 @@ const VerySmallButton = React.forwardRef((props: VerySmallButtonProps, ref) => {
|
|
206
222
|
|
207
223
|
type IconPropTypes = BoxProps & { stepLabel: number };
|
208
224
|
|
209
|
-
const SubtractIcon = ({ stepLabel
|
225
|
+
const SubtractIcon = ({ stepLabel }: IconPropTypes) => (
|
210
226
|
<>
|
211
|
-
<
|
212
|
-
as="svg"
|
213
|
-
viewBox="0 0 30 30"
|
214
|
-
width="24"
|
215
|
-
height="24"
|
216
|
-
stroke="currentColor"
|
217
|
-
{...props}
|
218
|
-
>
|
227
|
+
<chakra.svg as="svg" viewBox="0 0 30 30" stroke="currentColor">
|
219
228
|
<line
|
220
229
|
x1="9"
|
221
230
|
y1="15"
|
@@ -223,24 +232,18 @@ const SubtractIcon = ({ stepLabel, ...props }: IconPropTypes) => (
|
|
223
232
|
y2="15"
|
224
233
|
strokeWidth="1.5"
|
225
234
|
strokeLinecap="round"
|
235
|
+
preserveAspectRatio="xMidYMid meet"
|
226
236
|
/>
|
227
|
-
</
|
237
|
+
</chakra.svg>
|
228
238
|
{stepLabel > 1 && (
|
229
239
|
<chakra.span paddingRight="1">{stepLabel.toString()}</chakra.span>
|
230
240
|
)}
|
231
241
|
</>
|
232
242
|
);
|
233
243
|
|
234
|
-
const AddIcon = ({ stepLabel
|
244
|
+
const AddIcon = ({ stepLabel }: IconPropTypes) => (
|
235
245
|
<>
|
236
|
-
<
|
237
|
-
as="svg"
|
238
|
-
viewBox="0 0 30 30"
|
239
|
-
width="24"
|
240
|
-
height="24"
|
241
|
-
stroke="currentColor"
|
242
|
-
{...props}
|
243
|
-
>
|
246
|
+
<chakra.svg as="svg" viewBox="0 0 30 30" stroke="currentColor">
|
244
247
|
<line
|
245
248
|
x1="9"
|
246
249
|
y1="15"
|
@@ -257,8 +260,7 @@ const AddIcon = ({ stepLabel, ...props }: IconPropTypes) => (
|
|
257
260
|
strokeWidth="1.5"
|
258
261
|
strokeLinecap="round"
|
259
262
|
/>
|
260
|
-
</
|
261
|
-
|
263
|
+
</chakra.svg>
|
262
264
|
{stepLabel > 1 && (
|
263
265
|
<chakra.span paddingRight="1">{stepLabel.toString()}</chakra.span>
|
264
266
|
)}
|
@@ -1,61 +1,108 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
useFormControlContext,
|
7
|
-
} from "@chakra-ui/react";
|
8
|
-
import React, { useId } from "react";
|
9
|
-
import {
|
10
|
-
FormLabel,
|
11
|
-
InputGroup,
|
12
|
-
InputLeftElement,
|
13
|
-
InputProps,
|
14
|
-
InputRightElement,
|
15
|
-
} from ".";
|
1
|
+
"use client";
|
2
|
+
|
3
|
+
import { Button, useControllableState } from "@chakra-ui/react";
|
4
|
+
import React, { forwardRef } from "react";
|
5
|
+
import { ButtonProps, Input, InputProps } from "..";
|
16
6
|
import { createTexts, useTranslation } from "..";
|
7
|
+
import { InputGroupProps } from "./InputGroup";
|
8
|
+
|
9
|
+
export interface PasswordVisibilityProps {
|
10
|
+
/** Default visibility state */
|
11
|
+
defaultVisible?: boolean;
|
12
|
+
/** Visibility state */
|
13
|
+
visible?: boolean;
|
14
|
+
/** Callback for when the visibility state changes */
|
15
|
+
onVisibleChange?: (visible: boolean) => void;
|
16
|
+
}
|
17
|
+
|
18
|
+
export interface PasswordInputProps
|
19
|
+
extends InputProps,
|
20
|
+
PasswordVisibilityProps {
|
21
|
+
rootProps?: InputGroupProps;
|
22
|
+
}
|
23
|
+
|
24
|
+
/**
|
25
|
+
* A password input field with a visibility toggle.
|
26
|
+
*
|
27
|
+
* ```tsx
|
28
|
+
* <PasswordInput label="Password" />
|
29
|
+
* ```
|
30
|
+
*
|
31
|
+
* You can also control the visibility state:
|
32
|
+
*
|
33
|
+
* ```tsx
|
34
|
+
* <PasswordInput label="Password" visible={visible} onVisibleChange={setVisible} />
|
35
|
+
* ```
|
36
|
+
*
|
37
|
+
* You can also set the default visibility state:
|
38
|
+
*
|
39
|
+
* ```tsx
|
40
|
+
* <PasswordInput label="Password" defaultVisible />
|
41
|
+
* ```
|
42
|
+
*
|
43
|
+
* @see https://spor.vy.no/components/password-input
|
44
|
+
*/
|
45
|
+
|
46
|
+
export const PasswordInput = forwardRef<HTMLInputElement, PasswordInputProps>(
|
47
|
+
(props, ref) => {
|
48
|
+
const {
|
49
|
+
rootProps,
|
50
|
+
defaultVisible,
|
51
|
+
visible: visibleProp,
|
52
|
+
onVisibleChange,
|
53
|
+
label,
|
54
|
+
startElement,
|
55
|
+
...rest
|
56
|
+
} = props;
|
57
|
+
|
58
|
+
const [visible, setVisible] = useControllableState({
|
59
|
+
value: visibleProp,
|
60
|
+
defaultValue: defaultVisible || false,
|
61
|
+
onChange: onVisibleChange,
|
62
|
+
});
|
17
63
|
|
18
|
-
export type PasswordInputProps = InputProps;
|
19
|
-
export const PasswordInput = forwardRef<PasswordInputProps, "input">(
|
20
|
-
({ leftIcon, id, label, size, ...props }, ref) => {
|
21
|
-
const { isOpen: isShowingPassword, onToggle } = useDisclosure();
|
22
64
|
const { t } = useTranslation();
|
23
|
-
|
24
|
-
const autoGeneratedId = `password-input-${useId()}`;
|
25
|
-
const inputId = id ?? formControlProps?.id ?? autoGeneratedId;
|
65
|
+
|
26
66
|
return (
|
27
|
-
<
|
28
|
-
{
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
placeholder=" " // This is needed to make the label work as expected
|
35
|
-
type={isShowingPassword ? "text" : "password"}
|
36
|
-
paddingRight={10}
|
37
|
-
paddingLeft={leftIcon ? 7 : undefined}
|
38
|
-
ref={ref}
|
39
|
-
data-attachable
|
40
|
-
/>
|
41
|
-
<FormLabel htmlFor={inputId} pointerEvents="none">
|
42
|
-
{label}
|
43
|
-
</FormLabel>
|
44
|
-
<InputRightElement width="fit-content">
|
45
|
-
<Button
|
67
|
+
<Input
|
68
|
+
ref={ref}
|
69
|
+
startElement={startElement && startElement}
|
70
|
+
label={label}
|
71
|
+
type={visible ? "text" : "password"}
|
72
|
+
endElement={
|
73
|
+
<VisibilityTrigger
|
46
74
|
variant="ghost"
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
75
|
+
disabled={rest.disabled}
|
76
|
+
onPointerDown={(e) => {
|
77
|
+
if (rest.disabled) return;
|
78
|
+
if (e.button !== 0) return;
|
79
|
+
e.preventDefault();
|
80
|
+
setVisible(!visible);
|
81
|
+
}}
|
54
82
|
>
|
55
|
-
{
|
56
|
-
</
|
57
|
-
|
58
|
-
|
83
|
+
{visible ? t(texts.hidePassword) : t(texts.showPassword)}
|
84
|
+
</VisibilityTrigger>
|
85
|
+
}
|
86
|
+
{...rest}
|
87
|
+
/>
|
88
|
+
);
|
89
|
+
},
|
90
|
+
);
|
91
|
+
|
92
|
+
const VisibilityTrigger = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
93
|
+
(props, ref) => {
|
94
|
+
return (
|
95
|
+
<Button
|
96
|
+
ref={ref}
|
97
|
+
type="button"
|
98
|
+
fontWeight="normal"
|
99
|
+
size="sm"
|
100
|
+
borderRadius="sm"
|
101
|
+
marginRight={1}
|
102
|
+
{...props}
|
103
|
+
>
|
104
|
+
{props.children}
|
105
|
+
</Button>
|
59
106
|
);
|
60
107
|
},
|
61
108
|
);
|