@vygruppen/spor-react 1.3.4 → 2.0.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 -10
- package/CHANGELOG.md +31 -0
- package/dist/index.d.ts +6720 -27
- package/dist/index.js +14143 -140
- package/dist/index.mjs +13822 -27
- package/package.json +19 -31
- package/src/accordion/Accordion.test.tsx +20 -0
- package/src/accordion/Accordion.tsx +62 -0
- package/src/accordion/AccordionContext.tsx +27 -0
- package/src/accordion/Expandable.tsx +157 -0
- package/src/accordion/index.tsx +2 -0
- package/src/alert/AlertIcon.tsx +75 -0
- package/src/alert/BaseAlert.test.tsx +37 -0
- package/src/alert/BaseAlert.tsx +21 -0
- package/src/alert/ClosableAlert.test.tsx +37 -0
- package/src/alert/ClosableAlert.tsx +75 -0
- package/src/alert/ExpandableAlert.test.tsx +84 -0
- package/src/alert/ExpandableAlert.tsx +84 -0
- package/src/alert/StaticAlert.tsx +25 -0
- package/src/alert/index.tsx +3 -0
- package/src/button/Button.test.tsx +23 -0
- package/src/button/Button.tsx +162 -0
- package/src/button/ButtonGroup.tsx +43 -0
- package/src/button/CloseButton.tsx +63 -0
- package/src/button/FloatingActionButton.tsx +113 -0
- package/src/button/IconButton.tsx +63 -0
- package/src/button/index.tsx +5 -0
- package/src/card/Card.tsx +59 -0
- package/src/card/index.tsx +1 -0
- package/src/datepicker/Calendar.tsx +32 -0
- package/src/datepicker/CalendarCell.tsx +74 -0
- package/src/datepicker/CalendarGrid.tsx +76 -0
- package/src/datepicker/CalendarHeader.tsx +153 -0
- package/src/datepicker/CalendarNavigationButton.tsx +26 -0
- package/src/datepicker/CalendarTriggerButton.tsx +36 -0
- package/src/datepicker/DateField.tsx +51 -0
- package/src/datepicker/DatePicker.tsx +153 -0
- package/src/datepicker/DateRangePicker.tsx +171 -0
- package/src/datepicker/DateTimeSegment.tsx +56 -0
- package/src/datepicker/RangeCalendar.tsx +35 -0
- package/src/datepicker/StyledField.tsx +31 -0
- package/src/datepicker/TimeField.tsx +46 -0
- package/src/datepicker/TimePicker.test.tsx +74 -0
- package/src/datepicker/TimePicker.tsx +196 -0
- package/src/datepicker/index.tsx +4 -0
- package/src/datepicker/utils.ts +33 -0
- package/src/i18n/index.tsx +38 -0
- package/src/image/index.tsx +2 -0
- package/src/index.tsx +25 -26
- package/src/input/CardSelect.tsx +165 -0
- package/src/input/Checkbox.tsx +24 -0
- package/src/input/CheckboxGroup.tsx +43 -0
- package/src/input/ChoiceChip.tsx +102 -0
- package/src/input/Dialog.tsx +29 -0
- package/src/input/FormControl.tsx +11 -0
- package/src/input/FormErrorMessage.tsx +91 -0
- package/src/input/FormLabel.tsx +11 -0
- package/src/input/InfoSelect.tsx +209 -0
- package/src/input/Input.tsx +59 -0
- package/src/input/InputElement.tsx +45 -0
- package/src/input/ListBox.tsx +123 -0
- package/src/input/NativeSelect.tsx +38 -0
- package/src/input/PasswordInput.tsx +70 -0
- package/src/input/Popover.tsx +70 -0
- package/src/input/Radio.tsx +34 -0
- package/src/input/RadioGroup.tsx +47 -0
- package/src/input/SearchInput.tsx +89 -0
- package/src/input/Switch.tsx +40 -0
- package/src/input/Textarea.tsx +98 -0
- package/src/input/index.tsx +20 -0
- package/src/layout/Divider.tsx +26 -0
- package/src/layout/Stack.tsx +42 -0
- package/src/layout/index.tsx +28 -0
- package/src/linjetag/InfoTag.tsx +54 -0
- package/src/linjetag/LineIcon.tsx +44 -0
- package/src/linjetag/TravelTag.tsx +121 -0
- package/src/linjetag/icons.tsx +80 -0
- package/src/linjetag/index.tsx +3 -0
- package/src/linjetag/types.d.ts +24 -0
- package/src/link/TextLink.tsx +45 -0
- package/src/link/index.tsx +1 -0
- package/src/loader/ClientOnly.tsx +29 -0
- package/src/loader/ColorInlineLoader.tsx +27 -0
- package/src/loader/ColorSpinner.tsx +44 -0
- package/src/loader/ContentLoader.tsx +27 -0
- package/src/loader/DarkFullScreenLoader.tsx +23 -0
- package/src/loader/DarkInlineLoader.tsx +25 -0
- package/src/loader/DarkSpinner.tsx +43 -0
- package/src/loader/LightFullScreenLoader.tsx +23 -0
- package/src/loader/LightInlineLoader.tsx +25 -0
- package/src/loader/LightSpinner.tsx +41 -0
- package/src/loader/Lottie.tsx +10 -0
- package/src/loader/ProgressBar.tsx +128 -0
- package/src/loader/ProgressLoader.tsx +140 -0
- package/src/loader/Skeleton.tsx +16 -0
- package/src/loader/SkeletonCircle.tsx +13 -0
- package/src/loader/SkeletonText.tsx +10 -0
- package/src/loader/index.tsx +14 -0
- package/src/loader/useHydrated.tsx +34 -0
- package/src/loader/useRotatingLabel.tsx +22 -0
- package/src/logo/VyLogo.tsx +101 -0
- package/src/logo/index.tsx +1 -0
- package/src/media-controller/JumpButton.tsx +69 -0
- package/src/media-controller/PlayPauseButton.tsx +67 -0
- package/src/media-controller/SkipButton.tsx +66 -0
- package/src/media-controller/icons.tsx +80 -0
- package/src/media-controller/index.test.tsx +59 -0
- package/src/media-controller/index.tsx +3 -0
- package/src/modal/Drawer.tsx +122 -0
- package/src/modal/Modal.tsx +15 -0
- package/src/modal/ModalHeader.tsx +31 -0
- package/src/modal/SimpleDrawer.tsx +44 -0
- package/src/modal/index.tsx +4 -0
- package/src/popover/PopoverWizardBody.tsx +91 -0
- package/src/popover/SimplePopover.tsx +75 -0
- package/src/popover/WizardPopover.tsx +61 -0
- package/src/popover/index.tsx +23 -0
- package/src/provider/SporProvider.tsx +67 -0
- package/src/provider/index.tsx +1 -0
- package/src/stepper/Stepper.tsx +115 -0
- package/src/stepper/StepperContext.tsx +55 -0
- package/src/stepper/StepperStep.tsx +48 -0
- package/src/stepper/index.tsx +2 -0
- package/src/tab/Tabs.tsx +20 -0
- package/src/tab/index.tsx +9 -0
- package/src/table/Table.tsx +58 -0
- package/src/table/index.tsx +19 -0
- package/src/theme/components/accordion.ts +143 -0
- package/src/theme/components/alert.ts +59 -0
- package/src/theme/components/badge.ts +109 -0
- package/src/theme/components/button.ts +217 -0
- package/src/theme/components/card-select.ts +158 -0
- package/src/theme/components/card.ts +174 -0
- package/src/theme/components/checkbox.ts +90 -0
- package/src/theme/components/choice-chip.ts +79 -0
- package/src/theme/components/close-button.ts +56 -0
- package/src/theme/components/code.ts +17 -0
- package/src/theme/components/datepicker.ts +194 -0
- package/src/theme/components/drawer.ts +92 -0
- package/src/theme/components/fab.ts +111 -0
- package/src/theme/components/form-label.ts +17 -0
- package/src/theme/components/form.ts +27 -0
- package/src/theme/components/index.ts +34 -0
- package/src/theme/components/info-select.ts +91 -0
- package/src/theme/components/info-tag.ts +49 -0
- package/src/theme/components/input.ts +97 -0
- package/src/theme/components/line-icon.ts +121 -0
- package/src/theme/components/link.ts +155 -0
- package/src/theme/components/listbox.ts +52 -0
- package/src/theme/components/media-controller-button.ts +134 -0
- package/src/theme/components/modal.ts +93 -0
- package/src/theme/components/popover.ts +63 -0
- package/src/theme/components/radio.ts +64 -0
- package/src/theme/components/select.ts +52 -0
- package/src/theme/components/skeleton.ts +40 -0
- package/src/theme/components/stepper.ts +230 -0
- package/src/theme/components/switch.ts +227 -0
- package/src/theme/components/table.ts +163 -0
- package/src/theme/components/tabs.ts +282 -0
- package/src/theme/components/textarea.ts +14 -0
- package/src/theme/components/toast.ts +28 -0
- package/src/theme/components/travel-tag.ts +267 -0
- package/src/theme/font-faces.ts +66 -0
- package/src/theme/foundations/borders.ts +11 -0
- package/src/theme/foundations/breakpoints.ts +9 -0
- package/src/theme/foundations/colors.ts +10 -0
- package/src/theme/foundations/config.ts +5 -0
- package/src/theme/foundations/fontSizes.ts +29 -0
- package/src/theme/foundations/fontWeights.ts +5 -0
- package/src/theme/foundations/fonts.ts +7 -0
- package/src/theme/foundations/index.ts +14 -0
- package/src/theme/foundations/lineHeights.ts +5 -0
- package/src/theme/foundations/radii.ts +12 -0
- package/src/theme/foundations/shadows.ts +8 -0
- package/src/theme/foundations/sizes.ts +34 -0
- package/src/theme/foundations/spacing.ts +30 -0
- package/src/theme/foundations/textStyles.ts +60 -0
- package/src/theme/foundations/zIndices.ts +17 -0
- package/src/theme/index.ts +14 -0
- package/src/theme/utils/box-shadow-utils.ts +44 -0
- package/src/theme/utils/focus-utils.ts +16 -0
- package/src/toast/ActionToast.test.tsx +22 -0
- package/src/toast/ActionToast.tsx +28 -0
- package/src/toast/BaseToast.test.tsx +27 -0
- package/src/toast/BaseToast.tsx +75 -0
- package/src/toast/ClosableToast.test.tsx +17 -0
- package/src/toast/ClosableToast.tsx +40 -0
- package/src/toast/index.tsx +1 -0
- package/src/toast/useToast.tsx +99 -0
- package/src/typography/Badge.tsx +68 -0
- package/src/typography/Code.tsx +32 -0
- package/src/typography/Heading.tsx +32 -0
- package/src/typography/Text.tsx +26 -0
- package/src/typography/index.tsx +4 -0
- package/src/util/externals.tsx +23 -0
- package/src/util/index.tsx +1 -0
package/src/index.tsx
CHANGED
@@ -1,27 +1,26 @@
|
|
1
1
|
export { extendTheme } from "@chakra-ui/react";
|
2
|
-
export * from "@vygruppen/spor-
|
3
|
-
export * from "
|
4
|
-
export * from "
|
5
|
-
export * from "
|
6
|
-
export * from "
|
7
|
-
export * from "
|
8
|
-
export * from "
|
9
|
-
export * from "
|
10
|
-
export * from "
|
11
|
-
export * from "
|
12
|
-
export * from "
|
13
|
-
export * from "
|
14
|
-
export * from "
|
15
|
-
export * from "
|
16
|
-
export * from "
|
17
|
-
export * from "
|
18
|
-
export * from "
|
19
|
-
export * from "
|
20
|
-
export * from "
|
21
|
-
export * from "
|
22
|
-
export * from "
|
23
|
-
export * from "
|
24
|
-
export * from "
|
25
|
-
export * from "
|
26
|
-
export * from "
|
27
|
-
export * from "@vygruppen/spor-util-react";
|
2
|
+
export * as tokens from "@vygruppen/spor-design-tokens";
|
3
|
+
export * from "./accordion";
|
4
|
+
export * from "./alert";
|
5
|
+
export * from "./button";
|
6
|
+
export * from "./card";
|
7
|
+
export * from "./datepicker";
|
8
|
+
export * from "./i18n";
|
9
|
+
export * from "./image";
|
10
|
+
export * from "./input";
|
11
|
+
export * from "./layout";
|
12
|
+
export * from "./linjetag";
|
13
|
+
export * from "./link";
|
14
|
+
export * from "./loader";
|
15
|
+
export * from "./logo";
|
16
|
+
export * from "./media-controller";
|
17
|
+
export * from "./modal";
|
18
|
+
export * from "./popover";
|
19
|
+
export * from "./provider";
|
20
|
+
export * from "./stepper";
|
21
|
+
export * from "./tab";
|
22
|
+
export * from "./table";
|
23
|
+
export * from "./theme";
|
24
|
+
export * from "./toast";
|
25
|
+
export * from "./typography";
|
26
|
+
export * from "./util";
|
@@ -0,0 +1,165 @@
|
|
1
|
+
import {
|
2
|
+
Box,
|
3
|
+
BoxProps,
|
4
|
+
chakra,
|
5
|
+
Flex,
|
6
|
+
forwardRef,
|
7
|
+
useMultiStyleConfig,
|
8
|
+
} from "@chakra-ui/react";
|
9
|
+
import {
|
10
|
+
DropdownDownFill18Icon,
|
11
|
+
DropdownDownFill24Icon,
|
12
|
+
} from "@vygruppen/spor-icon-react";
|
13
|
+
import React, { useEffect, useRef, useState } from "react";
|
14
|
+
import { AriaPositionProps, useButton, useOverlayTrigger } from "react-aria";
|
15
|
+
import { useOverlayTriggerState } from "react-stately";
|
16
|
+
import { Card } from "..";
|
17
|
+
import { Dialog } from "./Dialog";
|
18
|
+
import { Popover } from "./Popover";
|
19
|
+
|
20
|
+
type CardSelectProps = BoxProps & {
|
21
|
+
/** The design of the trigger button.
|
22
|
+
*
|
23
|
+
* - `ghost` is a transparent button with text
|
24
|
+
* - `outline` is a button with a border and text
|
25
|
+
* - `card` is a button with a drop shadow (like a card) and text
|
26
|
+
*/
|
27
|
+
variant: "ghost" | "outline" | "card";
|
28
|
+
/** The size of the trigger button */
|
29
|
+
size: "sm" | "md" | "lg";
|
30
|
+
/** Whether the card select is open / active, if controlled */
|
31
|
+
isOpen?: boolean;
|
32
|
+
/** The default state of the card select. Defaults to false (closed) */
|
33
|
+
defaultOpen?: boolean;
|
34
|
+
/** Callback for when the card select opens or closes. */
|
35
|
+
onToggle?: (isOpen: boolean) => void;
|
36
|
+
/** The text of the trigger button */
|
37
|
+
label: string;
|
38
|
+
/** An optional trigger button icon, rendered to the left of the label */
|
39
|
+
icon?: React.ReactNode;
|
40
|
+
/** The content of the card select */
|
41
|
+
children: React.ReactNode;
|
42
|
+
/** The horizontalOffset of the popover card */
|
43
|
+
crossOffset?: number;
|
44
|
+
/** The position of the popover card */
|
45
|
+
placement?: AriaPositionProps["placement"];
|
46
|
+
/** Whether or not to show the chevron. Defaults to true */
|
47
|
+
withChevron?: boolean;
|
48
|
+
};
|
49
|
+
|
50
|
+
/**
|
51
|
+
* A card select component.
|
52
|
+
*
|
53
|
+
* This component consists of a trigger button and a card select popover. The trigger button has several different variants and sizes, and can have an optional icon.
|
54
|
+
*
|
55
|
+
* ```tsx
|
56
|
+
* <CardSelect label="Languages" variant="card" size="md">
|
57
|
+
* <LanguageSettings />
|
58
|
+
* </CardSelect>
|
59
|
+
* ```
|
60
|
+
*
|
61
|
+
* @see https://spor.vy.no/komponenter/card-select
|
62
|
+
*/
|
63
|
+
export const CardSelect = forwardRef<CardSelectProps, "button">(
|
64
|
+
(
|
65
|
+
{
|
66
|
+
variant,
|
67
|
+
size,
|
68
|
+
isOpen: externalIsOpen,
|
69
|
+
defaultOpen = false,
|
70
|
+
onToggle,
|
71
|
+
label,
|
72
|
+
icon,
|
73
|
+
children,
|
74
|
+
width = "fit-content",
|
75
|
+
crossOffset = 0,
|
76
|
+
placement = "bottom",
|
77
|
+
withChevron = true,
|
78
|
+
...props
|
79
|
+
},
|
80
|
+
externalRef
|
81
|
+
) => {
|
82
|
+
const internalRef = useRef<HTMLButtonElement>(null);
|
83
|
+
const triggerRef = (externalRef ??
|
84
|
+
internalRef) as React.RefObject<HTMLButtonElement>;
|
85
|
+
|
86
|
+
const state = useOverlayTriggerState({
|
87
|
+
isOpen: externalIsOpen,
|
88
|
+
onOpenChange: onToggle,
|
89
|
+
defaultOpen,
|
90
|
+
});
|
91
|
+
const { triggerProps, overlayProps } = useOverlayTrigger(
|
92
|
+
{ type: "dialog" },
|
93
|
+
state,
|
94
|
+
triggerRef
|
95
|
+
);
|
96
|
+
|
97
|
+
const { buttonProps } = useButton(triggerProps, triggerRef);
|
98
|
+
|
99
|
+
const styles = useMultiStyleConfig("CardSelect", { variant, size });
|
100
|
+
useForceRerender(state.isOpen);
|
101
|
+
|
102
|
+
const ChevronIcon =
|
103
|
+
size === "sm" ? DropdownDownFill18Icon : DropdownDownFill24Icon;
|
104
|
+
|
105
|
+
return (
|
106
|
+
<Box {...props}>
|
107
|
+
<chakra.button
|
108
|
+
type="button"
|
109
|
+
ref={triggerRef}
|
110
|
+
sx={styles.trigger}
|
111
|
+
{...buttonProps}
|
112
|
+
width={width}
|
113
|
+
>
|
114
|
+
<Flex gap={1.5} alignItems="center">
|
115
|
+
{icon}
|
116
|
+
<Box as="span">{label}</Box>
|
117
|
+
{withChevron ? (
|
118
|
+
<ChevronIcon
|
119
|
+
transform={state.isOpen ? "rotate(180deg)" : "none"}
|
120
|
+
/>
|
121
|
+
) : null}
|
122
|
+
</Flex>
|
123
|
+
</chakra.button>
|
124
|
+
{state.isOpen && (
|
125
|
+
<Popover
|
126
|
+
state={state}
|
127
|
+
triggerRef={triggerRef}
|
128
|
+
offset={size === "sm" ? 6 : 12}
|
129
|
+
crossOffset={crossOffset}
|
130
|
+
placement={placement}
|
131
|
+
>
|
132
|
+
<Card
|
133
|
+
colorScheme="white"
|
134
|
+
size="lg"
|
135
|
+
sx={styles.card}
|
136
|
+
{...overlayProps}
|
137
|
+
>
|
138
|
+
<Dialog aria-label={label}>{children}</Dialog>
|
139
|
+
</Card>
|
140
|
+
</Popover>
|
141
|
+
)}
|
142
|
+
</Box>
|
143
|
+
);
|
144
|
+
}
|
145
|
+
);
|
146
|
+
|
147
|
+
/**
|
148
|
+
* Hold my beer.
|
149
|
+
*
|
150
|
+
* This is a workaround for a "bug" in react-aria where the overlay doesn't
|
151
|
+
* calculate the placement correctly for some reason.
|
152
|
+
*
|
153
|
+
* This is a hack, which forces React to rerender the component one extra time
|
154
|
+
* after the state changes from closed to open.
|
155
|
+
*
|
156
|
+
* There is probably a better way to do this, but I could not come up with one.
|
157
|
+
*/
|
158
|
+
function useForceRerender(shouldRerender: boolean) {
|
159
|
+
const [_, update] = useState(false);
|
160
|
+
useEffect(() => {
|
161
|
+
if (shouldRerender) {
|
162
|
+
update((x) => !x);
|
163
|
+
}
|
164
|
+
}, [shouldRerender]);
|
165
|
+
}
|
@@ -0,0 +1,24 @@
|
|
1
|
+
import {
|
2
|
+
Checkbox as ChakraCheckbox,
|
3
|
+
CheckboxProps as ChakraCheckboxProps,
|
4
|
+
forwardRef,
|
5
|
+
} from "@chakra-ui/react";
|
6
|
+
import React from "react";
|
7
|
+
|
8
|
+
export type CheckboxProps = ChakraCheckboxProps;
|
9
|
+
/**
|
10
|
+
* Creates a checkbox.
|
11
|
+
*
|
12
|
+
* The checkbox contains its own label, which is passed as a children prop:
|
13
|
+
*
|
14
|
+
* ```tsx
|
15
|
+
* <Checkbox>Accept the terms</Checkbox>
|
16
|
+
* ```
|
17
|
+
*
|
18
|
+
* Unlike regular inputs, it doesn't require its own `FormControl`.
|
19
|
+
*
|
20
|
+
* You can group several of these together with a `CheckboxGroup`.
|
21
|
+
*/
|
22
|
+
export const Checkbox = forwardRef<CheckboxProps, "input">((props, ref) => {
|
23
|
+
return <ChakraCheckbox {...props} ref={ref} />;
|
24
|
+
});
|
@@ -0,0 +1,43 @@
|
|
1
|
+
import {
|
2
|
+
CheckboxGroup as ChakraCheckboxGroup,
|
3
|
+
CheckboxGroupProps as ChakraCheckboxGroupProps,
|
4
|
+
Stack,
|
5
|
+
StackDirection,
|
6
|
+
} from "@chakra-ui/react";
|
7
|
+
import React from "react";
|
8
|
+
|
9
|
+
export type CheckboxGroupProps = Exclude<
|
10
|
+
ChakraCheckboxGroupProps,
|
11
|
+
"colorScheme" | "size" | "variant"
|
12
|
+
> & { direction?: StackDirection };
|
13
|
+
/**
|
14
|
+
* Used to group several checkboxes together. You can pass the default value, as well as whether or not they're all disabled
|
15
|
+
*
|
16
|
+
* ```tsx
|
17
|
+
* <CheckboxGroup isDisabled defaultValue={['red', 'blue']}>
|
18
|
+
* <Checkbox value="red">Red</Checkbox>
|
19
|
+
* <Checkbox value="blue">Blue</Checkbox>
|
20
|
+
* <Checkbox value="green">Green</Checkbox>
|
21
|
+
* </CheckboxGroup>
|
22
|
+
* ```
|
23
|
+
*
|
24
|
+
* By default, checkboxes in a CheckboxGroup show up horizontally. If you want them to show up vertically, please specify the `direction="column"` prop.
|
25
|
+
*
|
26
|
+
* ```tsx
|
27
|
+
* <CheckboxGroup direction="column">
|
28
|
+
* <Checkbox>Economy</Checkbox>
|
29
|
+
* <Checkbox>Business</Checkbox>
|
30
|
+
* <Checkbox>First Class</Checkbox>
|
31
|
+
* </CheckboxGroup>
|
32
|
+
*/
|
33
|
+
export const CheckboxGroup = ({
|
34
|
+
direction = "row",
|
35
|
+
children,
|
36
|
+
...props
|
37
|
+
}: CheckboxGroupProps) => {
|
38
|
+
return (
|
39
|
+
<ChakraCheckboxGroup {...props}>
|
40
|
+
<Stack direction={direction}>{children}</Stack>
|
41
|
+
</ChakraCheckboxGroup>
|
42
|
+
);
|
43
|
+
};
|
@@ -0,0 +1,102 @@
|
|
1
|
+
import {
|
2
|
+
chakra,
|
3
|
+
forwardRef,
|
4
|
+
useCheckbox,
|
5
|
+
useMultiStyleConfig,
|
6
|
+
} from "@chakra-ui/react";
|
7
|
+
import { dataAttr } from "@chakra-ui/utils";
|
8
|
+
import { CloseOutline24Icon } from "@vygruppen/spor-icon-react";
|
9
|
+
import React, { ChangeEvent, useId } from "react";
|
10
|
+
|
11
|
+
export type ChoiceChipProps = {
|
12
|
+
onChange?: (value: ChangeEvent<HTMLInputElement>) => void;
|
13
|
+
isChecked?: boolean;
|
14
|
+
defaultChecked?: boolean;
|
15
|
+
/** The button text */
|
16
|
+
children: React.ReactNode;
|
17
|
+
icon?: {
|
18
|
+
default: React.ReactNode;
|
19
|
+
checked: React.ReactNode;
|
20
|
+
};
|
21
|
+
size?: "sm" | "md" | "lg" | "xl";
|
22
|
+
variant?: "icon" | "choice" | "filter";
|
23
|
+
};
|
24
|
+
/**
|
25
|
+
* Choice chips are checkboxes that look like selectable buttons.
|
26
|
+
*
|
27
|
+
* Choice chips are available in four different sizes - `sm`, `md`, `lg` and `xl`.
|
28
|
+
*
|
29
|
+
* ```tsx
|
30
|
+
* <Stack flexDirection="row">
|
31
|
+
* <ChoiceChip size="lg">Bus</ChoiceChip>
|
32
|
+
* <ChoiceChip size="lg">Train</ChoiceChip>
|
33
|
+
* </Stack>
|
34
|
+
* ```
|
35
|
+
*
|
36
|
+
* There are also three different variants - `icon`, `choice` and `filter`.
|
37
|
+
*
|
38
|
+
* ```tsx
|
39
|
+
* <Stack flexDirection="row">
|
40
|
+
* <ChoiceChip variant="icon" icon={<Bus24Icon />}>Bus</ChoiceChip>
|
41
|
+
* <ChoiceChip variant="choice" icon={<Bus24Icon />}>Bus</ChoiceChip>
|
42
|
+
* <ChoiceChip variant="filter" icon={<Bus24Icon />}>Bus</ChoiceChip>
|
43
|
+
* </Stack>
|
44
|
+
*
|
45
|
+
* You can add an icon as well, if you want to!
|
46
|
+
*
|
47
|
+
* ```tsx
|
48
|
+
* <Stack flexDirection="row">
|
49
|
+
* <ChoiceChip size="md" icon={<BusIcon />}>Bus</ChoiceChip>
|
50
|
+
* <ChoiceChip size="lg" icon={<TrainIcon />}>Train</ChoiceChip>
|
51
|
+
* </Stack>
|
52
|
+
* ```
|
53
|
+
*/
|
54
|
+
export const ChoiceChip = forwardRef((props: ChoiceChipProps, ref) => {
|
55
|
+
const { children, icon, size = "md", variant = "choice" } = props;
|
56
|
+
|
57
|
+
const {
|
58
|
+
state,
|
59
|
+
getInputProps,
|
60
|
+
getCheckboxProps,
|
61
|
+
getRootProps,
|
62
|
+
getLabelProps,
|
63
|
+
} = useCheckbox(props);
|
64
|
+
const styles = useMultiStyleConfig("ChoiceChip", {
|
65
|
+
size,
|
66
|
+
variant,
|
67
|
+
icon,
|
68
|
+
hasLabel: Boolean(children),
|
69
|
+
});
|
70
|
+
|
71
|
+
const id = `choice-chip-${useId()}`;
|
72
|
+
|
73
|
+
return (
|
74
|
+
<chakra.label
|
75
|
+
htmlFor={id}
|
76
|
+
{...getRootProps()}
|
77
|
+
aria-label={String(children)}
|
78
|
+
>
|
79
|
+
<chakra.input {...getInputProps({}, ref)} id={id} />
|
80
|
+
<chakra.div
|
81
|
+
{...getLabelProps()}
|
82
|
+
__css={styles.container}
|
83
|
+
data-checked={dataAttr(state.isChecked)}
|
84
|
+
data-hover={dataAttr(state.isHovered)}
|
85
|
+
data-focus={dataAttr(state.isFocused)}
|
86
|
+
>
|
87
|
+
{icon && (
|
88
|
+
<chakra.span __css={styles.icon}>
|
89
|
+
{state.isChecked ? icon.checked : icon.default}
|
90
|
+
</chakra.span>
|
91
|
+
)}
|
92
|
+
|
93
|
+
<chakra.span __css={styles.label} {...getCheckboxProps()}>
|
94
|
+
{variant !== "icon" && children}
|
95
|
+
</chakra.span>
|
96
|
+
{variant === "filter" && state.isChecked && (
|
97
|
+
<CloseOutline24Icon ml={1.5} />
|
98
|
+
)}
|
99
|
+
</chakra.div>
|
100
|
+
</chakra.label>
|
101
|
+
);
|
102
|
+
});
|
@@ -0,0 +1,29 @@
|
|
1
|
+
import { Box, Heading } from "@chakra-ui/react";
|
2
|
+
import React, { useRef } from "react";
|
3
|
+
import { AriaDialogProps, useDialog } from "react-aria";
|
4
|
+
|
5
|
+
type DialogProps = AriaDialogProps & {
|
6
|
+
/** Optional title tag
|
7
|
+
*
|
8
|
+
* Important! If you don't pass a title, you must pass an `aria-label` prop to the dialog.
|
9
|
+
*/
|
10
|
+
title?: string;
|
11
|
+
/** The content of the dialog */
|
12
|
+
children: React.ReactNode;
|
13
|
+
};
|
14
|
+
/** Internal component for creating card select popovers */
|
15
|
+
export const Dialog = ({ title, children, ...props }: DialogProps) => {
|
16
|
+
const ref = useRef<HTMLDivElement>(null);
|
17
|
+
const { dialogProps, titleProps } = useDialog(props, ref);
|
18
|
+
|
19
|
+
return (
|
20
|
+
<Box {...dialogProps} ref={ref} outline="none">
|
21
|
+
{title && (
|
22
|
+
<Heading as="h3" {...titleProps}>
|
23
|
+
{title}
|
24
|
+
</Heading>
|
25
|
+
)}
|
26
|
+
{children}
|
27
|
+
</Box>
|
28
|
+
);
|
29
|
+
};
|
@@ -0,0 +1,11 @@
|
|
1
|
+
import {
|
2
|
+
FormControl as ChakraFormControl,
|
3
|
+
FormControlProps as ChakraFormControlProps,
|
4
|
+
forwardRef,
|
5
|
+
} from "@chakra-ui/react";
|
6
|
+
import React from "react";
|
7
|
+
|
8
|
+
export type FormControlProps = ChakraFormControlProps;
|
9
|
+
export const FormControl = forwardRef<FormControlProps, "div">((props, ref) => {
|
10
|
+
return <ChakraFormControl {...props} ref={ref} />;
|
11
|
+
});
|
@@ -0,0 +1,91 @@
|
|
1
|
+
import { Box, BoxProps, useFormControlContext } from "@chakra-ui/react";
|
2
|
+
import React from "react";
|
3
|
+
|
4
|
+
export type FormErrorMessageProps = {
|
5
|
+
/**
|
6
|
+
* The error message itself.
|
7
|
+
*/
|
8
|
+
children: React.ReactNode;
|
9
|
+
};
|
10
|
+
/** An error message
|
11
|
+
*
|
12
|
+
* This component is used to show error messages related to a form input.
|
13
|
+
* It should _always_ be used inside of a `FormControl` component.
|
14
|
+
*
|
15
|
+
* It will only render if the `isInvalid` prop of its enclosing `FormControl` component is set to true.
|
16
|
+
*
|
17
|
+
* ```tsx
|
18
|
+
* <FormControl isInvalid={email === ""}>
|
19
|
+
* <Input label="E-mail" value={email} onChange={updateEmail} />
|
20
|
+
* <FormErrorMessage>This field is required</FormErrorMessage>
|
21
|
+
* </FormControl>
|
22
|
+
* ```
|
23
|
+
*
|
24
|
+
* If you're using a `FormHelperText` (or any other components inside of the `FormControl`), make sure to render the `FormErrorMessage` before those.
|
25
|
+
* ```tsx
|
26
|
+
* <FormControl isInvalid={email === ""}>
|
27
|
+
* <Input label="E-mail" value={email} onChange={updateEmail} />
|
28
|
+
* <FormErrorMessage>This field is required</FormErrorMessage>
|
29
|
+
* <FormHelperText>We don't share your email</FormHelperText>
|
30
|
+
* </FormControl>
|
31
|
+
* ```
|
32
|
+
*
|
33
|
+
* @see https://spor.vy.no/komponenter/skjemaelementer
|
34
|
+
*/
|
35
|
+
export const FormErrorMessage = ({ children }: FormErrorMessageProps) => {
|
36
|
+
const formControlContext = useFormControlContext();
|
37
|
+
if (!formControlContext) {
|
38
|
+
throw new Error(
|
39
|
+
"FormErrorMessage must be used within a FormControl component"
|
40
|
+
);
|
41
|
+
}
|
42
|
+
if (!formControlContext.isInvalid) {
|
43
|
+
return null;
|
44
|
+
}
|
45
|
+
const { ref, ...errorMessageProps } =
|
46
|
+
formControlContext.getErrorMessageProps();
|
47
|
+
return (
|
48
|
+
<Box position="relative" ref={ref}>
|
49
|
+
<Box
|
50
|
+
borderRadius="xs"
|
51
|
+
backgroundColor="lightRed"
|
52
|
+
color="darkGrey"
|
53
|
+
paddingX={1.5}
|
54
|
+
paddingY={1}
|
55
|
+
textStyle="xs"
|
56
|
+
width="fit-content"
|
57
|
+
position="absolute"
|
58
|
+
top={-1.5}
|
59
|
+
left={3}
|
60
|
+
zIndex="popover"
|
61
|
+
maxWidth="50ch"
|
62
|
+
{...errorMessageProps}
|
63
|
+
>
|
64
|
+
<Arrow position="absolute" top="-0.25em" left="1em" />
|
65
|
+
{children}
|
66
|
+
</Box>
|
67
|
+
</Box>
|
68
|
+
);
|
69
|
+
};
|
70
|
+
|
71
|
+
/** The arrow of the error message box */
|
72
|
+
const Arrow = (props: BoxProps) => {
|
73
|
+
return (
|
74
|
+
<Box
|
75
|
+
{...(props as any)}
|
76
|
+
as="svg"
|
77
|
+
width="16"
|
78
|
+
height="16"
|
79
|
+
viewBox="0 0 16 16"
|
80
|
+
fill="none"
|
81
|
+
transform="rotate(45deg)"
|
82
|
+
>
|
83
|
+
<Box
|
84
|
+
as="path"
|
85
|
+
fill="lightRed"
|
86
|
+
d="M 0
|
87
|
+
0 Q 2.4 6 0 12 Q 6 9.6 12 12 Q 9.6 6 12 0 Q 6 2.4 0 0 z"
|
88
|
+
/>
|
89
|
+
</Box>
|
90
|
+
);
|
91
|
+
};
|
@@ -0,0 +1,11 @@
|
|
1
|
+
import {
|
2
|
+
FormLabel as ChakraFormLabel,
|
3
|
+
FormLabelProps as ChakraFormLabelProps,
|
4
|
+
forwardRef,
|
5
|
+
} from "@chakra-ui/react";
|
6
|
+
import React from "react";
|
7
|
+
|
8
|
+
export type FormLabelProps = ChakraFormLabelProps;
|
9
|
+
export const FormLabel = forwardRef<FormLabelProps, "label">((props, ref) => {
|
10
|
+
return <ChakraFormLabel {...props} ref={ref} />;
|
11
|
+
});
|