From 22afa7e1a5a2c51ad33de7cf9b3fe7b1f126cc77 Mon Sep 17 00:00:00 2001 From: ollyhearn Date: Sun, 31 May 2026 17:49:29 +0300 Subject: [PATCH] feat: structure --- .storybook/main.ts | 8 +- .storybook/preview-head.html | 12 + .storybook/preview.tsx | 5 +- src/components/alert-dialog/index.tsx | 57 ++ src/components/badge/index.tsx | 44 ++ src/components/button/index.tsx | 27 + src/components/callout/index.tsx | 19 + src/components/card/index.tsx | 6 + src/components/dialog/index.tsx | 55 ++ src/components/icon-button/index.tsx | 25 + src/components/list/index.tsx | 17 + src/components/menu/index.tsx | 56 ++ src/components/progress/index.tsx | 16 + src/components/scroll-area/index.tsx | 22 + src/components/segmented-control/index.tsx | 39 ++ src/components/select/index.tsx | 42 ++ src/components/selection/index.tsx | 53 ++ src/components/slider/index.tsx | 28 + src/components/spinner/index.tsx | 45 ++ src/components/table/index.tsx | 24 + src/components/tabs/index.tsx | 28 + src/components/text-field/index.tsx | 26 + src/components/tooltip/index.tsx | 42 ++ src/components/ui.tsx | 700 +-------------------- src/components/utils.ts | 2 + src/components/window/index.tsx | 26 + src/stories/AlertDialog.stories.tsx | 64 ++ src/stories/Dialog.stories.tsx | 54 ++ src/styles/components.css | 2 +- 29 files changed, 860 insertions(+), 684 deletions(-) create mode 100644 .storybook/preview-head.html create mode 100644 src/components/alert-dialog/index.tsx create mode 100644 src/components/badge/index.tsx create mode 100644 src/components/button/index.tsx create mode 100644 src/components/callout/index.tsx create mode 100644 src/components/card/index.tsx create mode 100644 src/components/dialog/index.tsx create mode 100644 src/components/icon-button/index.tsx create mode 100644 src/components/list/index.tsx create mode 100644 src/components/menu/index.tsx create mode 100644 src/components/progress/index.tsx create mode 100644 src/components/scroll-area/index.tsx create mode 100644 src/components/segmented-control/index.tsx create mode 100644 src/components/select/index.tsx create mode 100644 src/components/selection/index.tsx create mode 100644 src/components/slider/index.tsx create mode 100644 src/components/spinner/index.tsx create mode 100644 src/components/table/index.tsx create mode 100644 src/components/tabs/index.tsx create mode 100644 src/components/text-field/index.tsx create mode 100644 src/components/tooltip/index.tsx create mode 100644 src/components/utils.ts create mode 100644 src/components/window/index.tsx create mode 100644 src/stories/AlertDialog.stories.tsx create mode 100644 src/stories/Dialog.stories.tsx diff --git a/.storybook/main.ts b/.storybook/main.ts index f5bf784..98a0d2a 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -4,6 +4,7 @@ import type { StorybookConfig } from 'storybook-react-rsbuild'; const config: StorybookConfig = { stories: ['../src/**/*.mdx', '../src/**/*.stories.@(ts|tsx)'], addons: ['@storybook/addon-docs'], + staticDirs: ['../src/assets'], framework: { name: 'storybook-react-rsbuild', options: {}, @@ -13,9 +14,12 @@ const config: StorybookConfig = { reactDocgen: 'react-docgen-typescript', reactDocgenTypescriptOptions: { shouldExtractLiteralValuesFromEnum: true, - // Keep our own props; drop the noise inherited from node_modules. + // Keep our own props + Radix primitives; drop other node_modules noise. propFilter: (prop) => - prop.parent ? !/node_modules/.test(prop.parent.fileName) : true, + prop.parent + ? !/node_modules/.test(prop.parent.fileName) || + /node_modules\/radix-ui/.test(prop.parent.fileName) + : true, }, }, }; diff --git a/.storybook/preview-head.html b/.storybook/preview-head.html new file mode 100644 index 0000000..6d62926 --- /dev/null +++ b/.storybook/preview-head.html @@ -0,0 +1,12 @@ + + + + diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index cc693e6..7c144f8 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -2,8 +2,9 @@ import { useEffect, type ReactNode } from 'react'; import type { Preview, Decorator } from 'storybook-react-rsbuild'; import { Tooltip } from 'radix-ui'; -/* The shipped library surface, exactly as a consumer would load it. */ -import '../src/styles/fonts.css'; +/* The shipped library surface, exactly as a consumer would load it. + Fonts are loaded via preview-head.html (Google Fonts link + Anta @font-face) + to avoid bundler inlining the @import url() mid-stylesheet. */ import '../src/styles/index.css'; /* Storybook-only canvas styling (background, docs blocks). */ import './preview.css'; diff --git a/src/components/alert-dialog/index.tsx b/src/components/alert-dialog/index.tsx new file mode 100644 index 0000000..02b06be --- /dev/null +++ b/src/components/alert-dialog/index.tsx @@ -0,0 +1,57 @@ +import { type ReactNode } from 'react'; +import { AlertDialog as RAlertDialog } from 'radix-ui'; +import { Button } from '../button'; + +export const AlertDialog = ({ + trigger, + title, + description, + cancelLabel = 'Cancel', + actionLabel = 'Confirm', + destructive, + onAction, + open, + defaultOpen, + onOpenChange, +}: { + trigger?: ReactNode; + title: string; + description?: ReactNode; + cancelLabel?: string; + actionLabel?: string; + destructive?: boolean; + onAction?: () => void; + open?: boolean; + defaultOpen?: boolean; + onOpenChange?: (o: boolean) => void; +}) => ( + + {trigger && {trigger}} + + + + + {title} + + {description && ( + + {description} + + )} +
+ + + + + + +
+
+
+
+); diff --git a/src/components/badge/index.tsx b/src/components/badge/index.tsx new file mode 100644 index 0000000..b6c9526 --- /dev/null +++ b/src/components/badge/index.tsx @@ -0,0 +1,44 @@ +import { type ComponentPropsWithoutRef, type ReactNode } from 'react'; +import { cx } from '../utils'; + +type BadgeVariant = 'lime' | 'ember' | 'neutral' | 'outline'; + +export const Badge = ({ + variant = 'neutral', + dot, + className, + children, + ...props +}: ComponentPropsWithoutRef<'span'> & { + variant?: BadgeVariant; + dot?: boolean; +}) => ( + + {children} + +); + +export const Chip = ({ + children, + onRemove, +}: { + children: ReactNode; + onRemove?: () => void; +}) => ( + + {children} + {onRemove && ( + + )} + +); diff --git a/src/components/button/index.tsx b/src/components/button/index.tsx new file mode 100644 index 0000000..9e8390c --- /dev/null +++ b/src/components/button/index.tsx @@ -0,0 +1,27 @@ +import { forwardRef, type ComponentPropsWithoutRef } from 'react'; +import { cx } from '../utils'; + +export type BtnVariant = 'key' | 'primary' | 'ember' | 'ghost'; + +type ButtonProps = ComponentPropsWithoutRef<'button'> & { + variant?: BtnVariant; + size?: 'sm'; + iconOnly?: boolean; +}; + +export const Button = forwardRef( + ({ variant = 'key', size, iconOnly, className, ...props }, ref) => ( + + + +); diff --git a/src/components/spinner/index.tsx b/src/components/spinner/index.tsx new file mode 100644 index 0000000..684fb6f --- /dev/null +++ b/src/components/spinner/index.tsx @@ -0,0 +1,45 @@ +import { useId, type ComponentPropsWithoutRef } from 'react'; +import { cx } from '../utils'; + +export const Spinner = ({ + size, + className, + ...props +}: ComponentPropsWithoutRef<'span'> & { size?: 'sm' | 'lg' }) => { + const gid = `modern-sk-groove-${useId()}`; + return ( + + + + + + + + + + + + + ); +}; diff --git a/src/components/table/index.tsx b/src/components/table/index.tsx new file mode 100644 index 0000000..f5f7f9b --- /dev/null +++ b/src/components/table/index.tsx @@ -0,0 +1,24 @@ +import { type ComponentPropsWithoutRef } from 'react'; +import { cx } from '../utils'; + +export const Table = ({ children, ...props }: ComponentPropsWithoutRef<'table'>) => ( +
+ + {children} +
+
+); + +export const THead = (p: ComponentPropsWithoutRef<'thead'>) => ; +export const TBody = (p: ComponentPropsWithoutRef<'tbody'>) => ; + +export const Tr = ({ + selected, + className, + ...props +}: ComponentPropsWithoutRef<'tr'> & { selected?: boolean }) => ( + +); + +export const Th = (p: ComponentPropsWithoutRef<'th'>) => ; +export const Td = (p: ComponentPropsWithoutRef<'td'>) => ; diff --git a/src/components/tabs/index.tsx b/src/components/tabs/index.tsx new file mode 100644 index 0000000..df63206 --- /dev/null +++ b/src/components/tabs/index.tsx @@ -0,0 +1,28 @@ +import { type ComponentPropsWithoutRef } from 'react'; +import { Tabs as RTabs } from 'radix-ui'; +import { cx } from '../utils'; + +export const Tabs = RTabs.Root; + +export const TabsList = ({ + items, + className, + ...props +}: { items: Array<{ value: string; label: string }> } & Omit< + ComponentPropsWithoutRef, + 'children' +>) => ( + + {items.map((it) => ( + + {it.label} + + ))} + +); + +export const TabsContent = RTabs.Content; diff --git a/src/components/text-field/index.tsx b/src/components/text-field/index.tsx new file mode 100644 index 0000000..17666fb --- /dev/null +++ b/src/components/text-field/index.tsx @@ -0,0 +1,26 @@ +import { forwardRef, type ComponentPropsWithoutRef, type ReactNode } from 'react'; +import { cx } from '../utils'; + +export const TextField = forwardRef>( + ({ className, ...props }, ref) => ( + + ), +); +TextField.displayName = 'TextField'; + +export const TextArea = forwardRef>( + ({ className, ...props }, ref) => ( +