@boxcustodia/library 2.0.0-alpha.10 → 2.0.0-alpha.12
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/dist/index.cjs.js +70 -70
- package/dist/index.css +2 -0
- package/dist/index.d.ts +420 -272
- package/dist/index.es.js +34448 -27816
- package/dist/theme.css +1 -1
- package/package.json +11 -6
- package/src/__doc__/Changelog.mdx +6 -0
- package/src/__doc__/Components.mdx +73 -0
- package/src/__doc__/Examples.tsx +69 -0
- package/src/__doc__/Icons.mdx +41 -0
- package/src/__doc__/Intro.mdx +138 -0
- package/src/__doc__/MCP.mdx +71 -0
- package/src/__doc__/Migration.mdx +475 -0
- package/src/__doc__/Theme.mdx +132 -0
- package/src/__doc__/Types.mdx +252 -0
- package/src/components/alert/alert.stories.tsx +142 -0
- package/src/components/alert/alert.tsx +109 -0
- package/src/components/alert/index.ts +7 -0
- package/src/components/alert-dialog/alert-dialog.stories.tsx +173 -0
- package/src/components/alert-dialog/alert-dialog.test.tsx +49 -0
- package/src/components/alert-dialog/alert-dialog.tsx +265 -0
- package/src/components/alert-dialog/index.ts +1 -0
- package/src/components/auto-complete/auto-complete-primitives.tsx +155 -0
- package/src/components/auto-complete/auto-complete.stories.tsx +241 -0
- package/src/components/auto-complete/auto-complete.tsx +82 -0
- package/src/components/auto-complete/index.ts +2 -0
- package/src/components/avatar/avatar.stories.tsx +84 -0
- package/src/components/avatar/avatar.test.tsx +61 -0
- package/src/components/avatar/avatar.tsx +104 -0
- package/src/components/avatar/index.ts +1 -0
- package/src/components/background-image/background-image.stories.tsx +21 -0
- package/src/components/background-image/background-image.test.tsx +29 -0
- package/src/components/background-image/background-image.tsx +23 -0
- package/src/components/background-image/index.ts +1 -0
- package/src/components/button/button.stories.tsx +396 -0
- package/src/components/button/button.test.tsx +58 -0
- package/src/components/button/button.tsx +31 -0
- package/src/components/button/button.variants.ts +44 -0
- package/src/components/button/components/base-button.tsx +86 -0
- package/src/components/button/components/loader-overlay.tsx +21 -0
- package/src/components/button/components/loading-icon.tsx +47 -0
- package/src/components/button/index.ts +3 -0
- package/src/components/calendar/calendar.model.ts +86 -0
- package/src/components/calendar/calendar.stories.tsx +155 -0
- package/src/components/calendar/calendar.test.tsx +12 -0
- package/src/components/calendar/calendar.tsx +185 -0
- package/src/components/calendar/components/calendar-navigation.tsx +141 -0
- package/src/components/calendar/components/day.tsx +61 -0
- package/src/components/calendar/components/decade-view.tsx +45 -0
- package/src/components/calendar/components/index.ts +6 -0
- package/src/components/calendar/components/month-view.tsx +58 -0
- package/src/components/calendar/components/week-days.tsx +27 -0
- package/src/components/calendar/components/year-view.tsx +29 -0
- package/src/components/calendar/hooks/index.ts +4 -0
- package/src/components/calendar/hooks/use-calendar-navigation.ts +79 -0
- package/src/components/calendar/hooks/use-calendar.ts +90 -0
- package/src/components/calendar/hooks/use-multiple-calendar.ts +34 -0
- package/src/components/calendar/hooks/use-range-calendar.ts +91 -0
- package/src/components/calendar/hooks/use-single-calendar.ts +18 -0
- package/src/components/calendar/index.ts +1 -0
- package/src/components/calendar/utils/typeguards.ts +7 -0
- package/src/components/card/card.stories.tsx +116 -0
- package/src/components/card/card.tsx +74 -0
- package/src/components/card/index.ts +1 -0
- package/src/components/center/center.stories.tsx +81 -0
- package/src/components/center/center.tsx +24 -0
- package/src/components/center/index.ts +1 -0
- package/src/components/checkbox/checkbox.stories.tsx +307 -0
- package/src/components/checkbox/checkbox.tsx +273 -0
- package/src/components/checkbox/index.ts +1 -0
- package/src/components/checkbox-group/checkbox-group.stories.tsx +104 -0
- package/src/components/checkbox-group/checkbox-group.tsx +16 -0
- package/src/components/checkbox-group/index.ts +1 -0
- package/src/components/combobox/combobox.stories.tsx +339 -0
- package/src/components/combobox/combobox.tsx +892 -0
- package/src/components/combobox/index.ts +1 -0
- package/src/components/date-picker/date-input.stories.tsx +158 -0
- package/src/components/date-picker/date-input.tsx +163 -0
- package/src/components/date-picker/date-picker.model.ts +90 -0
- package/src/components/date-picker/date-picker.stories.tsx +200 -0
- package/src/components/date-picker/date-picker.test.tsx +23 -0
- package/src/components/date-picker/date-picker.tsx +298 -0
- package/src/components/date-picker/date-picker.utils.ts +260 -0
- package/src/components/date-picker/index.ts +3 -0
- package/src/components/date-picker/use-date-input-popover.ts +48 -0
- package/src/components/date-picker/use-date-input.ts +125 -0
- package/src/components/dialog/dialog.stories.tsx +171 -0
- package/src/components/dialog/dialog.test.tsx +68 -0
- package/src/components/dialog/dialog.tsx +277 -0
- package/src/components/dialog/index.ts +1 -0
- package/src/components/divider/divider.stories.tsx +70 -0
- package/src/components/divider/divider.test.tsx +22 -0
- package/src/components/divider/divider.tsx +23 -0
- package/src/components/divider/index.ts +1 -0
- package/src/components/dropzone/dropzone.stories.tsx +210 -0
- package/src/components/dropzone/dropzone.tsx +154 -0
- package/src/components/dropzone/file-types.ts +64 -0
- package/src/components/dropzone/index.ts +3 -0
- package/src/components/dropzone/upload-primitives.tsx +310 -0
- package/src/components/dropzone/use-dropzone.ts +122 -0
- package/src/components/empty-state/empty-state.stories.tsx +56 -0
- package/src/components/empty-state/empty-state.tsx +39 -0
- package/src/components/empty-state/index.ts +1 -0
- package/src/components/field/field.stories.tsx +223 -0
- package/src/components/field/field.tsx +229 -0
- package/src/components/field/index.ts +1 -0
- package/src/components/form/form.stories.tsx +594 -0
- package/src/components/form/form.tsx +30 -0
- package/src/components/form/index.ts +1 -0
- package/src/components/heading/heading.stories.tsx +74 -0
- package/src/components/heading/heading.tsx +28 -0
- package/src/components/heading/heading.variants.ts +27 -0
- package/src/components/heading/index.ts +1 -0
- package/src/components/index.ts +46 -0
- package/src/components/input/index.ts +1 -0
- package/src/components/input/input.stories.tsx +104 -0
- package/src/components/input/input.tsx +75 -0
- package/src/components/kbd/index.ts +1 -0
- package/src/components/kbd/kbd.stories.tsx +40 -0
- package/src/components/kbd/kbd.tsx +31 -0
- package/src/components/kbd/kbd.variants.ts +26 -0
- package/src/components/label/index.ts +1 -0
- package/src/components/label/label.stories.tsx +68 -0
- package/src/components/label/label.test.tsx +61 -0
- package/src/components/label/label.tsx +62 -0
- package/src/components/loader/index.ts +1 -0
- package/src/components/loader/loader.stories.tsx +60 -0
- package/src/components/loader/loader.test.tsx +26 -0
- package/src/components/loader/loader.tsx +60 -0
- package/src/components/menu/index.ts +2 -0
- package/src/components/menu/menu-primitives.tsx +248 -0
- package/src/components/menu/menu.stories.tsx +203 -0
- package/src/components/menu/menu.tsx +100 -0
- package/src/components/menu/util/render-menu-item.tsx +54 -0
- package/src/components/multi-select/hooks/use-multi-select.ts +66 -0
- package/src/components/multi-select/index.ts +1 -0
- package/src/components/multi-select/multi-select.stories.tsx +294 -0
- package/src/components/multi-select/multi-select.tsx +300 -0
- package/src/components/multi-select/multi-select.variants.ts +22 -0
- package/src/components/number-input/index.ts +1 -0
- package/src/components/number-input/number-input.stories.tsx +209 -0
- package/src/components/number-input/number-input.test.tsx +87 -0
- package/src/components/number-input/number-input.tsx +230 -0
- package/src/components/pagination/components/pagination-option.tsx +27 -0
- package/src/components/pagination/index.ts +1 -0
- package/src/components/pagination/pagination.stories.tsx +80 -0
- package/src/components/pagination/pagination.test.tsx +76 -0
- package/src/components/pagination/pagination.tsx +102 -0
- package/src/components/password/index.ts +1 -0
- package/src/components/password/password.stories.tsx +104 -0
- package/src/components/password/password.tsx +71 -0
- package/src/components/popover/index.ts +1 -0
- package/src/components/popover/popover.stories.tsx +213 -0
- package/src/components/popover/popover.tsx +203 -0
- package/src/components/progress/index.ts +1 -0
- package/src/components/progress/progress.stories.tsx +124 -0
- package/src/components/progress/progress.test.tsx +25 -0
- package/src/components/progress/progress.tsx +124 -0
- package/src/components/scroll-area/index.ts +1 -0
- package/src/components/scroll-area/scroll-area.stories.tsx +166 -0
- package/src/components/scroll-area/scroll-area.tsx +64 -0
- package/src/components/select/index.ts +1 -0
- package/src/components/select/select.stories.tsx +253 -0
- package/src/components/select/select.tsx +430 -0
- package/src/components/show/index.ts +1 -0
- package/src/components/show/show.stories.tsx +197 -0
- package/src/components/show/show.test.tsx +41 -0
- package/src/components/show/show.tsx +16 -0
- package/src/components/skeleton/index.ts +1 -0
- package/src/components/skeleton/skeleton.stories.tsx +36 -0
- package/src/components/skeleton/skeleton.test.tsx +14 -0
- package/src/components/skeleton/skeleton.tsx +15 -0
- package/src/components/stack/index.ts +1 -0
- package/src/components/stack/stack.stories.tsx +194 -0
- package/src/components/stack/stack.tsx +52 -0
- package/src/components/stepper/Stepper.tsx +190 -0
- package/src/components/stepper/context/stepper-context.tsx +11 -0
- package/src/components/stepper/index.ts +1 -0
- package/src/components/stepper/stepper.stories.tsx +130 -0
- package/src/components/stepper/stepper.test.tsx +91 -0
- package/src/components/switch/index.ts +1 -0
- package/src/components/switch/switch.stories.tsx +122 -0
- package/src/components/switch/switch.test.tsx +30 -0
- package/src/components/switch/switch.tsx +86 -0
- package/src/components/table/index.ts +3 -0
- package/src/components/table/table-primitives.tsx +122 -0
- package/src/components/table/table.model.ts +20 -0
- package/src/components/table/table.stories.tsx +169 -0
- package/src/components/table/table.test.tsx +91 -0
- package/src/components/table/table.tsx +109 -0
- package/src/components/table-pagination/index.ts +2 -0
- package/src/components/table-pagination/table-pagination.model.ts +2 -0
- package/src/components/table-pagination/table-pagination.stories.tsx +23 -0
- package/src/components/table-pagination/table-pagination.test.tsx +32 -0
- package/src/components/table-pagination/table-pagination.tsx +108 -0
- package/src/components/tabs/context/tabs-context.tsx +14 -0
- package/src/components/tabs/index.ts +1 -0
- package/src/components/tabs/tabs.stories.tsx +182 -0
- package/src/components/tabs/tabs.test.tsx +61 -0
- package/src/components/tabs/tabs.tsx +175 -0
- package/src/components/tag/index.ts +2 -0
- package/src/components/tag/tag.stories.tsx +170 -0
- package/src/components/tag/tag.test.tsx +18 -0
- package/src/components/tag/tag.tsx +99 -0
- package/src/components/tag/tag.variants.ts +31 -0
- package/src/components/textarea/index.ts +1 -0
- package/src/components/textarea/textarea.stories.tsx +73 -0
- package/src/components/textarea/textarea.tsx +105 -0
- package/src/components/timeline/index.ts +1 -0
- package/src/components/timeline/timeline-status.ts +5 -0
- package/src/components/timeline/timeline.stories.tsx +84 -0
- package/src/components/timeline/timeline.tsx +147 -0
- package/src/components/toast/index.ts +1 -0
- package/src/components/toast/toast.stories.tsx +392 -0
- package/src/components/toast/toast.test.tsx +50 -0
- package/src/components/toast/toast.tsx +411 -0
- package/src/components/tooltip/index.ts +1 -0
- package/src/components/tooltip/tooltip.stories.tsx +226 -0
- package/src/components/tooltip/tooltip.test.tsx +46 -0
- package/src/components/tooltip/tooltip.tsx +171 -0
- package/src/components/tree/hooks/use-controllable-tree-state.ts +80 -0
- package/src/components/tree/index.ts +2 -0
- package/src/components/tree/tree-primitives.tsx +126 -0
- package/src/components/tree/tree.stories.tsx +468 -0
- package/src/components/tree/tree.tsx +42 -0
- package/src/hooks/index.ts +26 -0
- package/src/hooks/useArray/__doc__/useArray.stories.tsx +100 -0
- package/src/hooks/useArray/__test__/useArray.test.tsx +88 -0
- package/src/hooks/useArray/index.ts +1 -0
- package/src/hooks/useArray/useArray.ts +76 -0
- package/src/hooks/useAsync/__doc__/useAsync.stories.tsx +149 -0
- package/src/hooks/useAsync/__test__/useAsync.test.tsx +68 -0
- package/src/hooks/useAsync/index.ts +1 -0
- package/src/hooks/useAsync/useAsync.ts +58 -0
- package/src/hooks/useClickOutside/__doc__/useClickOutside.stories.tsx +40 -0
- package/src/hooks/useClickOutside/__test__/useClickOutside.test.tsx +33 -0
- package/src/hooks/useClickOutside/index.ts +1 -0
- package/src/hooks/useClickOutside/useClickOutside.ts +26 -0
- package/src/hooks/useClipboard/__doc__/useClipboard.stories.tsx +45 -0
- package/src/hooks/useClipboard/__test__/useClipboard.test.tsx +19 -0
- package/src/hooks/useClipboard/index.ts +1 -0
- package/src/hooks/useClipboard/useClipboard.tsx +28 -0
- package/src/hooks/useDebounceCallback/__doc__/useDebouncedCallback.stories.tsx +84 -0
- package/src/hooks/useDebounceCallback/index.ts +1 -0
- package/src/hooks/useDebounceCallback/useDebouncedCallback.ts +23 -0
- package/src/hooks/useDebounceValue/__doc__/useDebouncedValue.stories.tsx +75 -0
- package/src/hooks/useDebounceValue/index.ts +1 -0
- package/src/hooks/useDebounceValue/useDebouncedValue.ts +17 -0
- package/src/hooks/useDisclosure/__doc__/useDisclosure.stories.tsx +39 -0
- package/src/hooks/useDisclosure/__test__/useDisclosure.test.ts +43 -0
- package/src/hooks/useDisclosure/index.ts +1 -0
- package/src/hooks/useDisclosure/useDisclosure.ts +37 -0
- package/src/hooks/useDocumentTitle/__doc__/useDocumentTitle.stories.tsx +26 -0
- package/src/hooks/useDocumentTitle/index.ts +1 -0
- package/src/hooks/useDocumentTitle/useDocumentTitle.tsx +11 -0
- package/src/hooks/useEventListener/__doc__/useEventListener.stories.tsx +28 -0
- package/src/hooks/useEventListener/__test__/useEventListener.test.tsx +26 -0
- package/src/hooks/useEventListener/index.ts +1 -0
- package/src/hooks/useEventListener/useEventListener.ts +25 -0
- package/src/hooks/useFocusTrap/__doc__/useFocusTrap.stories.tsx +37 -0
- package/src/hooks/useFocusTrap/index.ts +1 -0
- package/src/hooks/useFocusTrap/scopeTab.ts +38 -0
- package/src/hooks/useFocusTrap/tabbable.ts +70 -0
- package/src/hooks/useFocusTrap/useFocusTrap.ts +78 -0
- package/src/hooks/useHotkey/__docs__/useHotkey.stories.tsx +116 -0
- package/src/hooks/useHotkey/__test__/useHotkey.test.tsx +105 -0
- package/src/hooks/useHotkey/__utils__/create-hotkey-listener.ts +25 -0
- package/src/hooks/useHotkey/__utils__/index.ts +3 -0
- package/src/hooks/useHotkey/__utils__/is-input-field.ts +14 -0
- package/src/hooks/useHotkey/__utils__/match-key-modifiers.ts +25 -0
- package/src/hooks/useHotkey/index.ts +1 -0
- package/src/hooks/useHotkey/useHotkey.ts +34 -0
- package/src/hooks/useHover/__doc__/useHover.stories.tsx +41 -0
- package/src/hooks/useHover/__test__/useHover.test.tsx +45 -0
- package/src/hooks/useHover/index.ts +1 -0
- package/src/hooks/useHover/useHover.tsx +40 -0
- package/src/hooks/useIsVisible/__doc__/useIsVisible.stories.tsx +60 -0
- package/src/hooks/useIsVisible/index.ts +1 -0
- package/src/hooks/useIsVisible/useIsVisible.tsx +50 -0
- package/src/hooks/useLocalStorage/__doc__/useLocalStorage.stories.tsx +86 -0
- package/src/hooks/useLocalStorage/__test__/useLocalStorage.test.ts +85 -0
- package/src/hooks/useLocalStorage/index.ts +1 -0
- package/src/hooks/useLocalStorage/useLocalStorage.ts +57 -0
- package/src/hooks/useMediaQuery/__doc__/useMediaQuery.stories.tsx +39 -0
- package/src/hooks/useMediaQuery/index.ts +1 -0
- package/src/hooks/useMediaQuery/useMediaQuery.ts +22 -0
- package/src/hooks/useMemoizedFn/index.ts +1 -0
- package/src/hooks/useMemoizedFn/useMemoizedFn.ts +32 -0
- package/src/hooks/useMutation/__doc__/useMutation.stories.tsx +111 -0
- package/src/hooks/useMutation/__test__/useMutation.test.tsx +83 -0
- package/src/hooks/useMutation/index.ts +1 -0
- package/src/hooks/useMutation/useMutation.tsx +60 -0
- package/src/hooks/useObject/__doc__/useObject.stories.tsx +119 -0
- package/src/hooks/useObject/__test__/useObject.test.tsx +87 -0
- package/src/hooks/useObject/index.ts +1 -0
- package/src/hooks/useObject/useObject.tsx +48 -0
- package/src/hooks/usePagination/__doc__/usePagination.stories.tsx +72 -0
- package/src/hooks/usePagination/__test__/usePagination.test.tsx +98 -0
- package/src/hooks/usePagination/index.ts +2 -0
- package/src/hooks/usePagination/usePagination.tsx +74 -0
- package/src/hooks/usePortal/__doc__/usePortal.stories.tsx +19 -0
- package/src/hooks/usePortal/__test__/usePortal.test.tsx +20 -0
- package/src/hooks/usePortal/index.ts +1 -0
- package/src/hooks/usePortal/usePortal.ts +40 -0
- package/src/hooks/usePreventCloseWindow/__doc__/usePreventCloseWindow.stories.tsx +32 -0
- package/src/hooks/usePreventCloseWindow/index.ts +1 -0
- package/src/hooks/usePreventCloseWindow/usePreventCloseWindow.ts +33 -0
- package/src/hooks/useRangePagination/__test__/useRangePagination.test.tsx +63 -0
- package/src/hooks/useRangePagination/index.ts +2 -0
- package/src/hooks/useRangePagination/useRangePagination.tsx +72 -0
- package/src/hooks/useSelection/__doc__/useSelection.stories.tsx +140 -0
- package/src/hooks/useSelection/__test__/useSelection.test.tsx +57 -0
- package/src/hooks/useSelection/index.ts +1 -0
- package/src/hooks/useSelection/useSelection.ts +121 -0
- package/src/hooks/useStep/__doc__/useStep.stories.tsx +98 -0
- package/src/hooks/useStep/__test__/useStep.test.ts +51 -0
- package/src/hooks/useStep/index.ts +1 -0
- package/src/hooks/useStep/useStep.ts +57 -0
- package/src/hooks/useToggle/__doc__/useToggle.stories.tsx +25 -0
- package/src/hooks/useToggle/__test__/useToggle.test.tsx +43 -0
- package/src/hooks/useToggle/index.ts +1 -0
- package/src/hooks/useToggle/useToggle.ts +16 -0
- package/src/index.ts +6 -0
- package/src/lib/cn.ts +8 -0
- package/src/lib/index.ts +1 -0
- package/src/models/Generic.model.ts +67 -0
- package/src/models/index.ts +1 -0
- package/src/providers/index.ts +2 -0
- package/src/providers/library-provider.tsx +44 -0
- package/src/providers/theme/ThemeProvider.tsx +25 -0
- package/src/providers/theme/index.ts +3 -0
- package/src/providers/theme/types.ts +11 -0
- package/src/providers/theme/useThemeProps.ts +25 -0
- package/src/stores/theme.store.ts +31 -0
- package/src/styles/components.css +4 -0
- package/src/styles/index.css +2 -0
- package/src/styles/library.css +2 -0
- package/src/styles/theme.css +232 -0
- package/src/utils/dates/parseDateRange.utility.ts +39 -0
- package/src/utils/form.tsx +91 -0
- package/src/utils/functions/createSafeContext.ts +17 -0
- package/src/utils/functions/ensureReactElement.tsx +30 -0
- package/src/utils/functions/getFormData.ts +19 -0
- package/src/utils/functions/index.ts +4 -0
- package/src/utils/functions/mergeRefs.ts +18 -0
- package/src/utils/index.ts +3 -0
- package/src/utils/strings/extractInitials.utility.ts +10 -0
- package/src/utils/strings/index.ts +1 -0
- package/src/utils/tests/click.ts +3 -0
- package/src/utils/tests/index.ts +2 -0
- package/src/utils/tests/keyboard.ts +21 -0
- package/src/utils/tests/type.ts +6 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { VariantProps } from "class-variance-authority";
|
|
2
|
+
import { cn } from "../../lib";
|
|
3
|
+
import { HeadingVariants } from "./heading.variants";
|
|
4
|
+
|
|
5
|
+
type Props<C extends React.ElementType> = VariantProps<
|
|
6
|
+
typeof HeadingVariants
|
|
7
|
+
> & {
|
|
8
|
+
as?: C;
|
|
9
|
+
} & Omit<React.ComponentPropsWithoutRef<C>, "as">;
|
|
10
|
+
|
|
11
|
+
export const Heading = <C extends React.ElementType = "h2">({
|
|
12
|
+
className,
|
|
13
|
+
size,
|
|
14
|
+
weight,
|
|
15
|
+
as,
|
|
16
|
+
...props
|
|
17
|
+
}: Props<C>) => {
|
|
18
|
+
const Comp = as || "h2";
|
|
19
|
+
return (
|
|
20
|
+
<Comp
|
|
21
|
+
className={cn(HeadingVariants({ size, weight }), className)}
|
|
22
|
+
{...props}
|
|
23
|
+
data-slot="heading"
|
|
24
|
+
data-size={size}
|
|
25
|
+
data-weight={weight}
|
|
26
|
+
/>
|
|
27
|
+
);
|
|
28
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { cva } from "class-variance-authority";
|
|
2
|
+
|
|
3
|
+
export const HeadingVariants = cva([], {
|
|
4
|
+
variants: {
|
|
5
|
+
size: {
|
|
6
|
+
sm: "text-[14px]",
|
|
7
|
+
md: "text-[16px]",
|
|
8
|
+
lg: "text-[18px]",
|
|
9
|
+
xl: "text-[20px]",
|
|
10
|
+
"2xl": "text-[24px]",
|
|
11
|
+
"3xl": "text-[28px]",
|
|
12
|
+
"4xl": "text-[32px]",
|
|
13
|
+
"5xl": "text-[36px]",
|
|
14
|
+
"6xl": "text-[40px]",
|
|
15
|
+
},
|
|
16
|
+
weight: {
|
|
17
|
+
normal: "font-normal",
|
|
18
|
+
medium: "font-medium",
|
|
19
|
+
semibold: "font-semibold",
|
|
20
|
+
bold: "font-bold",
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
defaultVariants: {
|
|
24
|
+
size: "xl",
|
|
25
|
+
weight: "semibold",
|
|
26
|
+
},
|
|
27
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./heading";
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export * from "./alert-dialog";
|
|
2
|
+
export * from "./auto-complete";
|
|
3
|
+
export * from "./avatar";
|
|
4
|
+
export * from "./background-image";
|
|
5
|
+
export * from "./button";
|
|
6
|
+
export * from "./calendar";
|
|
7
|
+
export * from "./card";
|
|
8
|
+
export * from "./center";
|
|
9
|
+
export * from "./checkbox";
|
|
10
|
+
export * from "./checkbox-group";
|
|
11
|
+
export * from "./combobox";
|
|
12
|
+
export * from "./date-picker";
|
|
13
|
+
export * from "./dialog";
|
|
14
|
+
export * from "./divider";
|
|
15
|
+
export * from "./dropzone";
|
|
16
|
+
export * from "./empty-state";
|
|
17
|
+
export * from "./field";
|
|
18
|
+
export * from "./form";
|
|
19
|
+
export * from "./heading";
|
|
20
|
+
export * from "./input";
|
|
21
|
+
export * from "./kbd";
|
|
22
|
+
export * from "./label";
|
|
23
|
+
export * from "./loader";
|
|
24
|
+
export * from "./menu";
|
|
25
|
+
export * from "./multi-select";
|
|
26
|
+
export * from "./number-input";
|
|
27
|
+
export * from "./pagination";
|
|
28
|
+
export * from "./password";
|
|
29
|
+
export * from "./popover";
|
|
30
|
+
export * from "./progress";
|
|
31
|
+
export * from "./scroll-area";
|
|
32
|
+
export * from "./select";
|
|
33
|
+
export * from "./show";
|
|
34
|
+
export * from "./skeleton";
|
|
35
|
+
export * from "./stack";
|
|
36
|
+
export * from "./stepper";
|
|
37
|
+
export * from "./switch";
|
|
38
|
+
export * from "./table";
|
|
39
|
+
export * from "./table-pagination";
|
|
40
|
+
export * from "./tabs";
|
|
41
|
+
export * from "./tag";
|
|
42
|
+
export * from "./textarea";
|
|
43
|
+
export * from "./timeline";
|
|
44
|
+
export * from "./toast";
|
|
45
|
+
export * from "./tooltip";
|
|
46
|
+
export * from "./tree";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./input";
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { Input, InputPrimitive } from "./input";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Single-line text input built on Base UI. Styles applied directly to the
|
|
7
|
+
* `<input>` element — no wrapper. `aria-invalid` drives error styling;
|
|
8
|
+
* `nativeInput` swaps Base UI for a plain `<input>`.
|
|
9
|
+
*/
|
|
10
|
+
const meta: Meta<typeof Input> = {
|
|
11
|
+
title: "Components/Input",
|
|
12
|
+
component: Input,
|
|
13
|
+
parameters: { layout: "centered" },
|
|
14
|
+
args: {
|
|
15
|
+
placeholder: "Placeholder...",
|
|
16
|
+
},
|
|
17
|
+
argTypes: {
|
|
18
|
+
onChange: { control: false },
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export default meta;
|
|
23
|
+
type Story = StoryObj<typeof Input>;
|
|
24
|
+
|
|
25
|
+
export const Default: Story = {};
|
|
26
|
+
|
|
27
|
+
export const Disabled: Story = {
|
|
28
|
+
args: { disabled: true },
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* `aria-invalid="true"` applies error border and ring — no Field wrapper needed.
|
|
33
|
+
*/
|
|
34
|
+
export const Invalid: Story = {
|
|
35
|
+
args: { "aria-invalid": "true" },
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Hides browser spinner arrows via webkit pseudo-elements and `-moz-appearance`.
|
|
40
|
+
*/
|
|
41
|
+
export const NumberType: Story = {
|
|
42
|
+
args: { type: "number", placeholder: "0" },
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Hides webkit search decoration buttons injected by the browser.
|
|
47
|
+
*/
|
|
48
|
+
export const SearchType: Story = {
|
|
49
|
+
args: { type: "search", placeholder: "Search..." },
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Applies `file:` variant styles — font, spacing, and transparent background
|
|
54
|
+
* on the browser's file-picker button.
|
|
55
|
+
*/
|
|
56
|
+
export const FileType: Story = {
|
|
57
|
+
args: { type: "file" },
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* `onChange` fires with `(value: string, event)` — the string value is the first argument,
|
|
62
|
+
* so no `event.target.value` needed.
|
|
63
|
+
*/
|
|
64
|
+
export const Controlled: Story = {
|
|
65
|
+
render: () => {
|
|
66
|
+
const [value, setValue] = useState("");
|
|
67
|
+
return (
|
|
68
|
+
<div className="flex flex-col gap-2">
|
|
69
|
+
<Input
|
|
70
|
+
value={value}
|
|
71
|
+
onChange={setValue}
|
|
72
|
+
placeholder="Type something..."
|
|
73
|
+
aria-label="Controlled input"
|
|
74
|
+
/>
|
|
75
|
+
<span className="text-sm text-muted-foreground">Value: "{value}"</span>
|
|
76
|
+
</div>
|
|
77
|
+
);
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* `nativeInput` swaps Base UI for a plain `<input>`. Identical appearance —
|
|
83
|
+
* use when you need native input APIs without Base UI overhead.
|
|
84
|
+
*/
|
|
85
|
+
export const NativeInput: Story = {
|
|
86
|
+
args: { nativeInput: true },
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Raw Base UI Input with no styles applied. Use when you need Base UI's field
|
|
91
|
+
* context wiring with full style control.
|
|
92
|
+
*
|
|
93
|
+
* ```tsx
|
|
94
|
+
* <InputPrimitive className="..." />
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
export const Primitive: Story = {
|
|
98
|
+
render: () => (
|
|
99
|
+
<InputPrimitive
|
|
100
|
+
className="w-full rounded-md border border-input px-3 py-2 text-sm outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
101
|
+
placeholder="Raw Base UI Input"
|
|
102
|
+
/>
|
|
103
|
+
),
|
|
104
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Input as BaseInput } from "@base-ui/react/input";
|
|
2
|
+
import { type ChangeEvent, type ComponentPropsWithoutRef } from "react";
|
|
3
|
+
import { cn } from "../../lib";
|
|
4
|
+
|
|
5
|
+
export const inputBaseClasses =
|
|
6
|
+
"h-10 w-full px-3 py-2 rounded-md border border-input bg-background text-sm outline-none transition-shadow placeholder:text-muted-foreground";
|
|
7
|
+
|
|
8
|
+
// ── Composite ─────────────────────────────────────────────────────────────────
|
|
9
|
+
|
|
10
|
+
export type InputProps = Omit<BaseInput.Props, "size" | "onChange"> & {
|
|
11
|
+
onChange?: (value: string, event: ChangeEvent<HTMLInputElement>) => void;
|
|
12
|
+
nativeInput?: boolean;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export function Input({
|
|
16
|
+
className,
|
|
17
|
+
nativeInput = false,
|
|
18
|
+
onChange,
|
|
19
|
+
...props
|
|
20
|
+
}: InputProps) {
|
|
21
|
+
const classes = cn(
|
|
22
|
+
inputBaseClasses,
|
|
23
|
+
"focus-visible:border-ring",
|
|
24
|
+
"aria-invalid:border-error focus-visible:aria-invalid:ring-error/20",
|
|
25
|
+
"has-aria-invalid:border-error has-focus-visible:has-aria-invalid:ring-error/20",
|
|
26
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
27
|
+
props.type === "number" && [
|
|
28
|
+
"[&::-webkit-outer-spin-button]:appearance-none",
|
|
29
|
+
"[&::-webkit-inner-spin-button]:appearance-none",
|
|
30
|
+
"[-moz-appearance:textfield]",
|
|
31
|
+
],
|
|
32
|
+
props.type === "search" && [
|
|
33
|
+
"[&::-webkit-search-cancel-button]:appearance-none",
|
|
34
|
+
"[&::-webkit-search-decoration]:appearance-none",
|
|
35
|
+
"[&::-webkit-search-results-button]:appearance-none",
|
|
36
|
+
"[&::-webkit-search-results-decoration]:appearance-none",
|
|
37
|
+
],
|
|
38
|
+
props.type === "file" && [
|
|
39
|
+
"text-muted-foreground",
|
|
40
|
+
"file:me-3 file:border-0 file:bg-transparent file:font-medium file:text-foreground file:text-sm",
|
|
41
|
+
],
|
|
42
|
+
className,
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
const handleChange = onChange
|
|
46
|
+
? (event: ChangeEvent<HTMLInputElement>) =>
|
|
47
|
+
onChange(event.target.value, event)
|
|
48
|
+
: undefined;
|
|
49
|
+
|
|
50
|
+
if (nativeInput) {
|
|
51
|
+
return (
|
|
52
|
+
<input
|
|
53
|
+
autoComplete="off"
|
|
54
|
+
className={classes}
|
|
55
|
+
data-slot="input"
|
|
56
|
+
onChange={handleChange}
|
|
57
|
+
{...(props as ComponentPropsWithoutRef<"input">)}
|
|
58
|
+
/>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<BaseInput
|
|
64
|
+
autoComplete="off"
|
|
65
|
+
className={classes}
|
|
66
|
+
data-slot="input"
|
|
67
|
+
onChange={handleChange}
|
|
68
|
+
{...props}
|
|
69
|
+
/>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ── Primitive escape hatch ────────────────────────────────────────────────────
|
|
74
|
+
|
|
75
|
+
export { BaseInput as InputPrimitive };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./kbd";
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
+
import { Kbd, KbdGroup } from "../../components";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Keyboard key representation. Use `KbdGroup` to compose multiple `Kbd` keys
|
|
6
|
+
* into a combination — no separator needed between them.
|
|
7
|
+
*/
|
|
8
|
+
const meta: Meta<typeof Kbd> = {
|
|
9
|
+
title: "Components/Kbd",
|
|
10
|
+
component: Kbd,
|
|
11
|
+
args: {
|
|
12
|
+
children: "K",
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export default meta;
|
|
17
|
+
type Story = StoryObj<typeof Kbd>;
|
|
18
|
+
|
|
19
|
+
export const Default: Story = {};
|
|
20
|
+
|
|
21
|
+
export const Group: Story = {
|
|
22
|
+
render: () => (
|
|
23
|
+
<div className="flex gap-2">
|
|
24
|
+
<KbdGroup>
|
|
25
|
+
<Kbd>⌘</Kbd>
|
|
26
|
+
<Kbd>K</Kbd>
|
|
27
|
+
</KbdGroup>
|
|
28
|
+
<KbdGroup>
|
|
29
|
+
<Kbd>⌘</Kbd>
|
|
30
|
+
<Kbd>Shift</Kbd>
|
|
31
|
+
<Kbd>P</Kbd>
|
|
32
|
+
</KbdGroup>
|
|
33
|
+
<KbdGroup>
|
|
34
|
+
<Kbd>Ctrl</Kbd>
|
|
35
|
+
<Kbd>Alt</Kbd>
|
|
36
|
+
<Kbd>Delete</Kbd>
|
|
37
|
+
</KbdGroup>
|
|
38
|
+
</div>
|
|
39
|
+
),
|
|
40
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type * as React from "react";
|
|
2
|
+
import { cn } from "../../lib";
|
|
3
|
+
|
|
4
|
+
export function Kbd({
|
|
5
|
+
className,
|
|
6
|
+
...props
|
|
7
|
+
}: React.ComponentProps<"kbd">): React.ReactElement {
|
|
8
|
+
return (
|
|
9
|
+
<kbd
|
|
10
|
+
className={cn(
|
|
11
|
+
"pointer-events-none inline-flex h-5 min-w-5 select-none items-center justify-center gap-1 rounded bg-muted px-1 font-medium font-sans text-muted-foreground text-xs [&_svg:not([class*='size-'])]:size-3",
|
|
12
|
+
className,
|
|
13
|
+
)}
|
|
14
|
+
data-slot="kbd"
|
|
15
|
+
{...props}
|
|
16
|
+
/>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function KbdGroup({
|
|
21
|
+
className,
|
|
22
|
+
...props
|
|
23
|
+
}: React.ComponentProps<"kbd">): React.ReactElement {
|
|
24
|
+
return (
|
|
25
|
+
<kbd
|
|
26
|
+
className={cn("inline-flex items-center gap-1", className)}
|
|
27
|
+
data-slot="kbd-group"
|
|
28
|
+
{...props}
|
|
29
|
+
/>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { cva } from "class-variance-authority";
|
|
2
|
+
|
|
3
|
+
export const KbdVariants = cva(
|
|
4
|
+
[
|
|
5
|
+
"inline-flex select-none items-center gap-1 rounded border bg-muted px-1.5 font-medium text-muted-foreground",
|
|
6
|
+
],
|
|
7
|
+
{
|
|
8
|
+
variants: {
|
|
9
|
+
variant: {
|
|
10
|
+
default: "border-transparent bg-secondary text-secondary-foreground",
|
|
11
|
+
secondary: "border-transparent bg-primary text-primary-foreground",
|
|
12
|
+
error: "border-transparent bg-error text-error-foreground",
|
|
13
|
+
outline: "text-foreground",
|
|
14
|
+
},
|
|
15
|
+
size: {
|
|
16
|
+
default: "px-1.5 py-0.5 text-xs",
|
|
17
|
+
sm: "px-2 py-0.5 text-sm",
|
|
18
|
+
lg: "px-4 py-1 text-lg",
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
defaultVariants: {
|
|
22
|
+
variant: "default",
|
|
23
|
+
size: "default",
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./label";
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
+
import { Input, Label } from "../../components";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Semantic `<label>` element for form fields.
|
|
6
|
+
* Supports the `render` prop for polymorphic rendering.
|
|
7
|
+
* Pass `tooltip` to render an inline info icon that reveals a tooltip on hover.
|
|
8
|
+
*/
|
|
9
|
+
const meta: Meta<typeof Label> = {
|
|
10
|
+
title: "Components/Label",
|
|
11
|
+
component: Label,
|
|
12
|
+
args: {
|
|
13
|
+
children: "First name",
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export default meta;
|
|
18
|
+
type Story = StoryObj<typeof Label>;
|
|
19
|
+
|
|
20
|
+
export const Default: Story = {};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Primary use case: associate a label with a form control via `htmlFor`.
|
|
24
|
+
* Clicking the label moves focus to the linked input.
|
|
25
|
+
*/
|
|
26
|
+
export const WithInput: Story = {
|
|
27
|
+
render: (args) => (
|
|
28
|
+
<div className="flex flex-col gap-2">
|
|
29
|
+
<Label {...args} htmlFor="name" />
|
|
30
|
+
<Input id="name" placeholder="Enter your name" />
|
|
31
|
+
</div>
|
|
32
|
+
),
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const Required: Story = {
|
|
36
|
+
args: {
|
|
37
|
+
required: true,
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Pass any `ReactNode` as `tooltip` to render an info icon next to the label text.
|
|
43
|
+
* Hovering the icon shows the tooltip content.
|
|
44
|
+
*/
|
|
45
|
+
export const WithTooltip: Story = {
|
|
46
|
+
args: {
|
|
47
|
+
tooltip: "This field is required for billing purposes.",
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The `render` prop replaces the root element while preserving all
|
|
53
|
+
* merged props and children. Useful for custom wrappers or non-label contexts.
|
|
54
|
+
*/
|
|
55
|
+
export const Render: Story = {
|
|
56
|
+
render: (args) => (
|
|
57
|
+
<Label {...args} render={({ children }) => <span>{children}</span>} />
|
|
58
|
+
),
|
|
59
|
+
parameters: {
|
|
60
|
+
docs: {
|
|
61
|
+
source: {
|
|
62
|
+
code: `<Label render={({ children }) => <span>{children}</span>}>
|
|
63
|
+
First name
|
|
64
|
+
</Label>`,
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { render, screen } from "@testing-library/react";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { Label } from "../../components";
|
|
4
|
+
|
|
5
|
+
describe("Label component", () => {
|
|
6
|
+
it("should render correctly", () => {
|
|
7
|
+
render(<Label data-testid="Label" />);
|
|
8
|
+
const component = screen.getByTestId("Label");
|
|
9
|
+
|
|
10
|
+
expect(component).toBeInTheDocument();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("should render as a label element", () => {
|
|
14
|
+
render(<Label>Text</Label>);
|
|
15
|
+
const label = screen.getByText("Text");
|
|
16
|
+
|
|
17
|
+
expect(label.tagName).toBe("LABEL");
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("should render children correctly", () => {
|
|
21
|
+
render(<Label>First name</Label>);
|
|
22
|
+
|
|
23
|
+
expect(screen.getByText("First name")).toBeInTheDocument();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("should have data-slot attribute", () => {
|
|
27
|
+
render(<Label data-testid="label" />);
|
|
28
|
+
const label = screen.getByTestId("label");
|
|
29
|
+
|
|
30
|
+
expect(label).toHaveAttribute("data-slot", "label");
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("should accept className prop", () => {
|
|
34
|
+
const TEST_CLASS = "custom-label";
|
|
35
|
+
render(<Label data-testid="label" className={TEST_CLASS} />);
|
|
36
|
+
const label = screen.getByTestId("label");
|
|
37
|
+
|
|
38
|
+
expect(label).toHaveClass(TEST_CLASS);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("should accept htmlFor prop", () => {
|
|
42
|
+
render(<Label data-testid="label" htmlFor="input-id" />);
|
|
43
|
+
const label = screen.getByTestId("label");
|
|
44
|
+
|
|
45
|
+
expect(label).toHaveAttribute("for", "input-id");
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("should render as a different element via render prop", () => {
|
|
49
|
+
render(
|
|
50
|
+
<Label
|
|
51
|
+
render={({ children }) => <span data-testid="label">{children}</span>}
|
|
52
|
+
>
|
|
53
|
+
Custom render
|
|
54
|
+
</Label>,
|
|
55
|
+
);
|
|
56
|
+
const label = screen.getByTestId("label");
|
|
57
|
+
|
|
58
|
+
expect(label.tagName).toBe("SPAN");
|
|
59
|
+
expect(label).toHaveTextContent("Custom render");
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { mergeProps } from "@base-ui/react/merge-props";
|
|
2
|
+
import { useRender } from "@base-ui/react/use-render";
|
|
3
|
+
import { Info } from "lucide-react";
|
|
4
|
+
import type React from "react";
|
|
5
|
+
import { ReactNode } from "react";
|
|
6
|
+
import { cn } from "../../lib";
|
|
7
|
+
import { Tooltip } from "../tooltip";
|
|
8
|
+
|
|
9
|
+
interface LabelProps extends useRender.ComponentProps<"label"> {
|
|
10
|
+
required?: boolean;
|
|
11
|
+
tooltip?: ReactNode;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function Label({
|
|
15
|
+
className,
|
|
16
|
+
render,
|
|
17
|
+
required,
|
|
18
|
+
tooltip,
|
|
19
|
+
children,
|
|
20
|
+
...props
|
|
21
|
+
}: LabelProps): React.ReactElement {
|
|
22
|
+
const content =
|
|
23
|
+
required || tooltip ? (
|
|
24
|
+
<>
|
|
25
|
+
{children}
|
|
26
|
+
{required && (
|
|
27
|
+
<span aria-hidden className="text-error">
|
|
28
|
+
*
|
|
29
|
+
</span>
|
|
30
|
+
)}
|
|
31
|
+
{tooltip && (
|
|
32
|
+
<Tooltip content={tooltip}>
|
|
33
|
+
<button
|
|
34
|
+
type="button"
|
|
35
|
+
aria-label="More information"
|
|
36
|
+
className="inline-flex cursor-default items-center text-muted-foreground hover:text-foreground transition-all ml-1"
|
|
37
|
+
>
|
|
38
|
+
<Info className="size-3.5" />
|
|
39
|
+
</button>
|
|
40
|
+
</Tooltip>
|
|
41
|
+
)}
|
|
42
|
+
</>
|
|
43
|
+
) : (
|
|
44
|
+
children
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
const baseProps = {
|
|
48
|
+
className: cn(
|
|
49
|
+
"inline-flex items-center font-medium text-sm leading-none",
|
|
50
|
+
"peer-data-disabled:cursor-not-allowed peer-data-disabled:opacity-70",
|
|
51
|
+
className,
|
|
52
|
+
),
|
|
53
|
+
"data-slot": "label",
|
|
54
|
+
children: content,
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
return useRender({
|
|
58
|
+
defaultTagName: "label",
|
|
59
|
+
props: mergeProps<"label">(baseProps, props),
|
|
60
|
+
render,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./loader";
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
+
import { Loader, Stack } from "../../components";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Spinner that signals an in-progress operation. Accepts all native SVG props
|
|
6
|
+
* via `LucideProps` — use `color`, `strokeWidth`, or `className` for one-off overrides.
|
|
7
|
+
* `center` positions the loader fixed at the viewport center.
|
|
8
|
+
*/
|
|
9
|
+
const meta: Meta<typeof Loader> = {
|
|
10
|
+
title: "Components/Loader",
|
|
11
|
+
component: Loader,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export default meta;
|
|
15
|
+
type Story = StoryObj<typeof meta>;
|
|
16
|
+
|
|
17
|
+
export const Default: Story = {};
|
|
18
|
+
|
|
19
|
+
export const Variants: Story = {
|
|
20
|
+
render: () => (
|
|
21
|
+
<Stack>
|
|
22
|
+
<Loader variant="primary" />
|
|
23
|
+
<Loader variant="secondary" />
|
|
24
|
+
<Loader variant="ghost" />
|
|
25
|
+
<Loader variant="error" />
|
|
26
|
+
<Loader variant="success" />
|
|
27
|
+
</Stack>
|
|
28
|
+
),
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const Sizes: Story = {
|
|
32
|
+
render: () => (
|
|
33
|
+
<Stack>
|
|
34
|
+
<Loader size="xs" />
|
|
35
|
+
<Loader size="sm" />
|
|
36
|
+
<Loader size="default" />
|
|
37
|
+
<Loader size="lg" />
|
|
38
|
+
<Loader size="xl" />
|
|
39
|
+
</Stack>
|
|
40
|
+
),
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* `center` uses `fixed inset-0` — it covers the full viewport.
|
|
45
|
+
* Pair it with a backdrop when used inside a modal or overlay.
|
|
46
|
+
*/
|
|
47
|
+
export const Centered: Story = { args: { center: true } };
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* `color` and `strokeWidth` come from `LucideProps` and bypass the variant system.
|
|
51
|
+
* Useful for one-off cases without adding a new variant.
|
|
52
|
+
*/
|
|
53
|
+
export const Custom: Story = {
|
|
54
|
+
args: {
|
|
55
|
+
size: "lg",
|
|
56
|
+
color: "tomato",
|
|
57
|
+
strokeWidth: 1,
|
|
58
|
+
className: "duration-200",
|
|
59
|
+
},
|
|
60
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { render, screen } from "@testing-library/react";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { Loader } from "../../components";
|
|
4
|
+
|
|
5
|
+
describe("Loader component", () => {
|
|
6
|
+
it("should render", () => {
|
|
7
|
+
render(<Loader data-testid="loader" />);
|
|
8
|
+
|
|
9
|
+
const loader = screen.getByTestId("loader");
|
|
10
|
+
expect(loader).toBeInTheDocument();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("should receive custom styles", () => {
|
|
14
|
+
render(<Loader color="red" data-testid="loader" />);
|
|
15
|
+
|
|
16
|
+
const loader = screen.getByTestId("loader");
|
|
17
|
+
expect(loader).toHaveAttribute("stroke", "red");
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("should receive custom size", () => {
|
|
21
|
+
render(<Loader size="lg" data-testid="loader" />);
|
|
22
|
+
|
|
23
|
+
const loader = screen.getByTestId("loader");
|
|
24
|
+
expect(loader).toHaveClass("w-10 h-10");
|
|
25
|
+
});
|
|
26
|
+
});
|