diff --git a/.prettierignore b/.prettierignore index 6767fd1ec..cd9643d1d 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,4 +1,4 @@ *.hbs pnpm-lock.yaml -./app/docs/.velite/* -./app/docs/public/* \ No newline at end of file +app/docs/.velite/ +app/docs/public/ \ No newline at end of file diff --git a/app/docs/CLAUDE.md b/app/docs/CLAUDE.md deleted file mode 100644 index a08dd7952..000000000 --- a/app/docs/CLAUDE.md +++ /dev/null @@ -1,385 +0,0 @@ -# CLAUDE.md - Documentation Site Design System Guide - -이 파일은 `/app/docs` 디렉토리에서 작업할 때 Claude Code에게 디자인 시스템 가이드를 제공합니다. - -## PandaCSS 설정 개요 - -이 프로젝트는 **PandaCSS**를 사용하여 타입 세이프하고 일관성 있는 스타일링을 구현합니다. - -### 설정 파일 위치 - -- `panda.config.ts` - PandaCSS 메인 설정 파일 -- `preset.ts` - 커스텀 디자인 토큰 및 시맨틱 컬러 시스템 정의 -- `styled-system/` - **자동 생성 폴더 (절대 수정 금지!)** - -### 핵심 설정 - -```typescript -{ - preflight: true, // CSS reset 적용 - jsxFramework: "react", - outdir: "styled-system", // 자동 생성 디렉토리 - presets: [ - preset(), // panda-animation 프리셋 - "@pandacss/preset-panda", // PandaCSS 기본 프리셋 - defaultPreset // 커스텀 프리셋 (preset.ts) - ] -} -``` - -### 다크 모드 조건 - -```typescript -conditions: { - light: "[data-color-mode=light] &", - dark: "[data-color-mode=dark] &" -} -``` - -## 디자인 토큰 시스템 - -### 1. 시맨틱 컬러 토큰 - -모든 컬러는 **시맨틱 토큰**을 우선적으로 사용합니다. 직접적인 컬러 값(예: `#eeeeee`) 사용을 피하고, 의미를 담은 토큰을 사용하세요. -시멘틱 토큰은 다크 모드에 대응되는 컬러가 존재합니다. 다크 모드일 때 변경이 불필요하다면 일반 토큰(예: `blue.600`)을 사용하세요 - -#### 배경 및 기본 컬러 - -```typescript -// 메인 배경 -background: "background" // white / slate.950 -foreground: "foreground" // slate.900 / slate.50 - -// 카드 및 팝오버 -card: "card" // white / slate.900 -card.foreground: "card.foreground" // slate.900 / slate.50 -popover: "popover" // white / slate.900 -popover.foreground: "popover.foreground" // slate.900 / slate.50 -``` - -#### 인터랙티브 컬러 (버튼, 링크 등) - -**사용 패턴**: 각 컬러는 `.DEFAULT`와 `.foreground`를 가집니다. - -- `.DEFAULT`: 배경 컬러로 사용 -- `.foreground`: 해당 배경 위의 텍스트 컬러로 사용 - -```typescript -// Primary (주요 액션) -bg: "primary" // slate.800 / slate.400 -color: "primary.foreground" // white / slate.950 - -// Secondary (보조 액션) -bg: "secondary" // gray.600 / gray.400 -color: "secondary.foreground" // white / gray.950 - -// Destructive (삭제, 위험한 액션) -bg: "destructive" // red.600 / red.400 -color: "destructive.foreground" // white / slate.950 - -// Muted (비활성, 보조 정보) -bg: "muted" // slate.100 / slate.800 -color: "muted.foreground" // slate.500 / slate.400 - -// Accent (강조) -bg: "accent" // gray.200 / gray.700 -color: "accent.foreground" // gray.900 / gray.50 -``` - -#### 보더 및 입력 필드 - -```typescript -border: "border" // slate.200 / slate.800 -border: "input" // gray.200 / gray.800 -ring: "ring" // slate.500 / slate.400 - -// 시맨틱 보더 토큰 (borders 객체) -border: "base" // 1px solid {colors.border} -border: "input" // 1px solid {colors.input} -border: "primary" // 1px solid {colors.primary} -border: "destructive" // 1px solid {colors.destructive} -``` - -### 2. 타이포그래피 토큰 (textStyles) - -**중요**: 하드코딩된 fontSize/fontWeight 대신 `textStyle` 속성을 사용하세요. - -#### Display (히어로 섹션, 대형 제목) - -```typescript -textStyle: "display1" // 72px (4.5rem), bold, loose line-height -textStyle: "display2" // 60px (3.75rem), bold, relaxed line-height -``` - -#### Title (섹션 제목) - -```typescript -textStyle: "title1" // 36px (2.25rem), semibold -textStyle: "title2" // 24px (1.5rem), semibold -textStyle: "title3" // 20px (1.25rem), bold -``` - -#### Heading (서브 헤딩) - -```typescript -textStyle: "heading1" // 18px (1.125rem), semibold -textStyle: "heading2" // 16px (1rem), semibold -``` - -#### Body (본문 텍스트) - -```typescript -textStyle: "body1" // 16px (1rem), normal, relaxed line-height -textStyle: "body2" // 14px (0.875rem), normal -``` - -#### Label & Caption (UI 요소, 작은 텍스트) - -```typescript -textStyle: "label1" // 14px (0.875rem), semibold, wide letter-spacing -textStyle: "label2" // 12px (0.75rem), semibold, wide letter-spacing -textStyle: "caption1" // 12px (0.75rem), semibold, tight line-height -textStyle: "caption2" // 10px (0.625rem), semibold, tight line-height -``` - -### 3. 간격 및 크기 토큰 - -PandaCSS 기본 spacing scale을 사용합니다 (0.25rem = 4px 단위): - -```typescript -// 간격 -px: "4" // 16px (1rem) -py: "2" // 8px (0.5rem) -gap: "3" // 12px (0.75rem) -m: "6" // 24px (1.5rem) - -// 크기 -w: "full" // 100% -h: "12" // 48px (3rem) -minH: "10" // 40px (2.5rem) -``` - -### 4. Border Radius 토큰 - -```typescript -rounded: "radius" // 8px (0.5rem) - 커스텀 토큰 -rounded: "sm" // PandaCSS 기본값 -rounded: "md" // PandaCSS 기본값 -rounded: "lg" // PandaCSS 기본값 -rounded: "xl" // PandaCSS 기본값 -``` - -### 5. 커스텀 유틸리티 - -#### fluidFontSize (반응형 폰트) - -viewport에 따라 부드럽게 크기가 변하는 폰트 사이즈: - -```typescript -fluidFontSize: "1rem" // 360px~640px 구간에서 유동적으로 변화 -// clamp(1rem, calc(...), 1.5rem)으로 변환됨 -``` - -## 스타일링 패턴 및 규칙 - -### ✅ 올바른 사용 예시 - -```typescript -import { css } from "styled-system/css" - -// 1. 시맨틱 토큰 사용 -const button = css({ - bg: "primary", - color: "primary.foreground", - textStyle: "label1", - px: "4", - py: "2", - rounded: "md", - _hover: { - bg: "primary/90", // 투명도 수식어 - }, - _disabled: { - opacity: "0.5", - cursor: "not-allowed", - }, -}) - -// 2. 다크모드 대응 -const card = css({ - bg: "card", - color: "card.foreground", - border: "base", - p: "6", - rounded: "lg", -}) - -// 3. 텍스트 스타일 -const heading = css({ - textStyle: "title1", - color: "foreground", - mb: "4", -}) - -// 4. 반응형 디자인 -const container = css({ - px: { base: "4", md: "6", lg: "8" }, - maxW: "1200px", - mx: "auto", -}) -``` - -### ❌ 피해야 할 패턴 - -```typescript -// ❌ 하드코딩된 값 -const bad = css({ - fontSize: "16px", // ✅ textStyle: "body1" - color: "#1e293b", // ✅ color: "foreground" - padding: "12px 16px", // ✅ px: "4", py: "3" - borderRadius: "8px", // ✅ rounded: "md" 또는 "radius" -}) - -// ❌ 직접적인 컬러 토큰 (시맨틱 컨텍스트가 있을 때) -const bad2 = css({ - bg: "blue.600", // ✅ bg: "primary" - color: "slate.900", // ✅ color: "foreground" -}) - -// ❌ ring 속성 사용 (이 디자인 시스템에서는 사용 안 함) -const bad3 = css({ - ring: "2px", // ✅ outline: "none", borderColor: "primary" -}) -``` - -### 상태 변형 패턴 - -```typescript -// Hover/Focus/Active 상태 -const interactive = css({ - bg: "primary", - _hover: { bg: "primary/90" }, // 투명도 수식어 - _focus: { - outline: "none", - borderColor: "primary", - }, - _active: { transform: "scale(0.98)" }, - _disabled: { opacity: "0.5" }, -}) - -// 다크모드 조건 -const themed = css({ - color: { base: "slate.900", _dark: "slate.50" }, -}) -``` - -## 컴포넌트 작성 가이드 - -### 1. 시맨틱 토큰 우선 순위 - -1. **최우선**: 시맨틱 토큰 (`primary`, `destructive`, `muted` 등) -2. **차선**: 기본 컬러 토큰 (`slate.600`, `blue.500` 등) -3. **절대 금지**: 하드코딩된 값 (`#2563eb`, `16px` 등) - -### 2. 텍스트 컬러 선택 가이드 - -| 배경 타입 | 사용할 텍스트 컬러 | -| ------------- | ------------------------ | -| `background` | `foreground` | -| `card` | `card.foreground` | -| `primary` | `primary.foreground` | -| `destructive` | `destructive.foreground` | -| `muted` | `muted.foreground` | -| `accent` | `accent.foreground` | - -### 3. Focus 상태 처리 - -이 디자인 시스템에서는 **`ring` 대신 `outline`과 `borderColor` 조합**을 사용합니다: - -```typescript -const input = css({ - border: "input", - _focus: { - outline: "none", - borderColor: "primary", - }, -}) -``` - -### 4. 반응형 breakpoints - -```typescript -const breakpoints = { - sm: "0px", // Mobile first - md: "640px", // Tablet - lg: "1024px", // Desktop -} - -// 사용 예시 -const responsive = css({ - fontSize: { base: "14px", md: "16px", lg: "18px" }, - px: { base: "4", md: "6", lg: "8" }, -}) -``` - -## styled-system 폴더 관리 - -### ⚠️ 중요: 절대 수동 편집 금지! - -`styled-system/` 폴더는 PandaCSS가 자동으로 생성하는 산출물입니다. - -- **자동 생성**: `panda codegen` 또는 `pnpm prepare` 실행 시 생성/재생성 -- **Git 무시**: `.gitignore`에 포함되어 있음 -- **수정 방법**: `panda.config.ts` 또는 `preset.ts` 파일을 수정 후 재생성 - -### 코드 생성 명령어 - -```bash -# styled-system 폴더 재생성 -pnpm panda codegen - -# 또는 prepare 스크립트 (빌드 전 자동 실행) -pnpm prepare -``` - -### import 경로 - -```typescript -// styled-system에서 import -import { css } from "styled-system/css" -import { flex, stack } from "styled-system/patterns" -``` - -## 애니메이션 - -`panda-animation` 프리셋이 포함되어 있어 100+ 애니메이션 키프레임을 사용할 수 있습니다: - -```typescript -const animated = css({ - animation: "fadeIn 0.3s ease-in", - _hover: { - animation: "pulse 1s infinite", - }, -}) -``` - -사용 가능한 애니메이션: `fadeIn`, `fadeOut`, `slideInUp`, `slideInDown`, `bounce`, `pulse`, `shake`, `wobble` 등 - -## 체크리스트 - -새로운 컴포넌트나 페이지를 작성할 때 다음을 확인하세요: - -- [ ] 시맨틱 토큰을 우선적으로 사용했는가? -- [ ] `textStyle`을 사용하여 타이포그래피를 정의했는가? -- [ ] 하드코딩된 컬러/사이즈 값이 없는가? -- [ ] 배경과 텍스트 컬러가 올바르게 매칭되는가? (예: `primary` + `primary.foreground`) -- [ ] 다크모드에서도 올바르게 표시되는가? -- [ ] Focus 상태가 `outline`과 `borderColor`로 처리되었는가? -- [ ] 반응형 디자인이 필요한 경우 breakpoints를 사용했는가? -- [ ] `styled-system/` 폴더를 직접 수정하지 않았는가? - -## 참고 자료 - -- PandaCSS 공식 문서: https://panda-css.com -- 프로젝트 루트 CLAUDE.md: `/Users/jh/jds/CLAUDE.md` -- 토큰 정의: `preset.ts` (app/docs/preset.ts) -- 설정 파일: `panda.config.ts` (app/docs/panda.config.ts) diff --git a/app/docs/content/docs/components/textfield.mdx b/app/docs/content/docs/components/textfield.mdx index 3690dcdbb..636c6fcda 100644 --- a/app/docs/content/docs/components/textfield.mdx +++ b/app/docs/content/docs/components/textfield.mdx @@ -28,12 +28,7 @@ TextField 컴포넌트는 입력 필드, 라벨, 도움말 텍스트를 포함 ### Props - + ', - }, - }, -}) - -export const semanticColors = defineSemanticTokens.colors({ - background: { - value: { base: "{colors.white}", _dark: "{colors.slate.950}" }, - }, - foreground: { - value: { base: "{colors.slate.900}", _dark: "{colors.slate.50}" }, - }, - card: { - DEFAULT: { - value: { base: "{colors.white}", _dark: "{colors.slate.900}" }, - }, - foreground: { - value: { base: "{colors.slate.900}", _dark: "{colors.slate.50}" }, - }, - }, - popover: { - DEFAULT: { - value: { base: "{colors.white}", _dark: "{colors.slate.900}" }, - }, - foreground: { - value: { base: "{colors.slate.900}", _dark: "{colors.slate.50}" }, - }, - }, - primary: { - DEFAULT: { - value: { base: "{colors.blue.600}", _dark: "{colors.blue.400}" }, - }, - foreground: { - value: { base: "{colors.white}", _dark: "{colors.blue.950}" }, - }, - }, - secondary: { - DEFAULT: { - value: { base: "{colors.indigo.600}", _dark: "{colors.indigo.400}" }, - }, - foreground: { - value: { base: "{colors.white}", _dark: "{colors.indigo.950}" }, - }, - }, - muted: { - DEFAULT: { - value: { base: "{colors.slate.100}", _dark: "{colors.slate.800}" }, - }, - foreground: { - value: { base: "{colors.slate.600}", _dark: "{colors.slate.400}" }, - }, - }, - accent: { - DEFAULT: { - value: { base: "{colors.sky.100}", _dark: "{colors.sky.800}" }, - }, - foreground: { - value: { base: "{colors.sky.900}", _dark: "{colors.sky.50}" }, - }, - }, - destructive: { - DEFAULT: { - value: { base: "{colors.red.600}", _dark: "{colors.red.400}" }, - }, - foreground: { - value: { base: "{colors.white}", _dark: "{colors.red.950}" }, - }, - }, - border: { - value: { base: "{colors.slate.200}", _dark: "{colors.slate.800}" }, - }, - input: { - value: { base: "{colors.slate.200}", _dark: "{colors.slate.800}" }, - }, - ring: { - value: { base: "{colors.blue.500}", _dark: "{colors.blue.400}" }, - }, - overlay: { - value: { base: "rgba(0, 0, 0, 0.08)", _dark: "rgba(0, 0, 0, 0.5)" }, - }, - code: { - DEFAULT: { - value: { base: "{colors.slate.50}", _dark: "{colors.slate.900}" }, - }, - foreground: { - value: { base: "{colors.slate.700}", _dark: "{colors.slate.300}" }, - }, - }, - subtle: { - DEFAULT: { - value: { base: "{colors.slate.50}", _dark: "{colors.slate.900/50}" }, - }, - }, -}) - -const borders = defineSemanticTokens.borders({ - base: { value: "1px solid {colors.border}" }, - input: { value: "1px solid {colors.input}" }, - primary: { value: "1px solid {colors.primary}" }, - destructive: { value: "1px solid {colors.destructive}" }, -}) - -const shadows = defineSemanticTokens.shadows({ - card: { - value: { - base: "0 1px 3px 0 rgb(0 0 0 / 0.04), 0 1px 2px -1px rgb(0 0 0 / 0.04)", - _dark: "0 1px 3px 0 rgb(0 0 0 / 0.2)", - }, - }, - elevated: { - value: { - base: "0 4px 6px -1px rgb(0 0 0 / 0.06), 0 2px 4px -2px rgb(0 0 0 / 0.06)", - _dark: "0 4px 6px -1px rgb(0 0 0 / 0.3)", - }, - }, -}) - -export const textStyles = defineTextStyles({ - // Display 계열 - display1: { - value: { - fontWeight: "{fontWeights.bold}", - fontSize: "4.5rem", // 6xl - lineHeight: "{lineHeights.loose}", - letterSpacing: "{letterSpacings.tight}", - }, - }, - display2: { - value: { - fontWeight: "{fontWeights.bold}", - fontSize: "3.75rem", // 5xl - lineHeight: "{lineHeights.relaxed}", - letterSpacing: "{letterSpacings.tight}", - }, - }, - // Title 계열 - title1: { - value: { - fontWeight: "semibold", - fontSize: "2.25rem", // 3xl - lineHeight: "{lineHeights.normal}", - letterSpacing: "{letterSpacings.tight}", - }, - }, - title2: { - value: { - fontWeight: "semibold", - fontSize: "1.5rem", // 2xl - lineHeight: "{lineHeights.snug}", - letterSpacing: "{letterSpacings.tight}", - }, - }, - title3: { - value: { - fontWeight: "{fontWeights.bold}", - fontSize: "1.25rem", // xl - lineHeight: "{lineHeights.snug}", - letterSpacing: "{letterSpacings.tight}", - }, - }, - // Heading 계열 - heading1: { - value: { - fontWeight: "{fontWeights.semibold}", - fontSize: "1.125rem", // lg - lineHeight: "{lineHeights.normal}", - letterSpacing: "{letterSpacings.normal}", - }, - }, - heading2: { - value: { - fontWeight: "{fontWeights.semibold}", - fontSize: "1rem", // md - lineHeight: "{lineHeights.normal}", - letterSpacing: "{letterSpacings.normal}", - }, - }, - // Body 계열 - body1: { - value: { - fontWeight: "{fontWeights.normal}", - fontSize: "1rem", // md - lineHeight: "{lineHeights.relaxed}", - letterSpacing: "{letterSpacings.normal}", - }, - }, - body2: { - value: { - fontWeight: "{fontWeights.normal}", - fontSize: "0.875rem", // sm - lineHeight: "{lineHeights.normal}", - letterSpacing: "{letterSpacings.normal}", - }, - }, - // Label/Caption 계열 - label1: { - value: { - fontWeight: "{fontWeights.semibold}", - fontSize: "0.875rem", // sm - lineHeight: "{lineHeights.snug}", - letterSpacing: "{letterSpacings.wide}", - }, - }, - label2: { - value: { - fontWeight: "{fontWeights.semibold}", - fontSize: "0.75rem", // xs - lineHeight: "{lineHeights.snug}", - letterSpacing: "{letterSpacings.wide}", - }, - }, - caption1: { - value: { - fontWeight: "{fontWeights.semibold}", - fontSize: "0.75rem", // xs - lineHeight: "{lineHeights.tight}", - letterSpacing: "{letterSpacings.wide}", - }, - }, - caption2: { - value: { - fontWeight: "{fontWeights.semibold}", - fontSize: "0.625rem", // 2xs - lineHeight: "{lineHeights.tight}", - letterSpacing: "{letterSpacings.wide}", - }, - }, -}) - -export const defaultPreset = definePreset({ - name: "default", - globalCss: { - html: { - background: "background", - color: "foreground", - }, - }, - theme: { - extend: { - textStyles, - tokens: { - radii, - assets, - }, - semanticTokens: { - colors: semanticColors, - borders, - shadows, - }, - }, - }, - utilities: { - extend: { - fluidFontSize: { - shorthand: "fluidFontSize", - values: "fontSizes", - transform(value) { - if (!value.endsWith("rem")) { - return value - } - try { - const min = parseFloat(value) * 16 //html 1rem - const max = min + 0.5 * 16 //min +0.5rem - - const [minRange, maxRange] = [360, 640] - const slopePxPerVw = (100 * (max - min)) / (maxRange - minRange) - const intercept = (min - slopePxPerVw * (minRange / 100)) / 16 - const calcPart = `calc(${intercept.toFixed(4)}rem + ${slopePxPerVw.toFixed(4)}vw)` - - return { - fontSize: `clamp(${value}, ${calcPart}, ${max / 16}rem)`, - } - } catch (error) { - console.error( - "[fluidFontSize] Error transforming value:", - error, - value, - ) - return { - fontSize: value, - } - } - }, - }, - }, - }, -}) diff --git a/app/docs/public/accordion.json b/app/docs/public/accordion.json index f60d923a3..5233858a0 100644 --- a/app/docs/public/accordion.json +++ b/app/docs/public/accordion.json @@ -1 +1 @@ -{"name":"Accordion","dependencies":["@radix-ui/react-accordion"],"files":[{"name":"index.tsx","content":"import * as AccordionPrimitive from \"@radix-ui/react-accordion\"\nimport { createStyleContext } from \"@utils/createStyleContext\"\nimport { accordion, type AccordionVariantProps } from \"@styled-system/recipes\"\nimport {\n type Assign,\n type ComponentProps,\n type HTMLStyledProps,\n} from \"@styled-system/types\"\n\nconst { withProvider, withContext } = createStyleContext(accordion)\n\nexport const Root = withProvider<\n HTMLDivElement,\n Assign<\n Assign<\n HTMLStyledProps<\"div\">,\n ComponentProps\n >,\n AccordionVariantProps\n >\n>(AccordionPrimitive.Root, \"root\")\n\nexport const Header = withContext<\n HTMLDivElement,\n Assign<\n HTMLStyledProps<\"div\">,\n ComponentProps\n >\n>(AccordionPrimitive.Header, \"header\")\n\nexport const Item = withContext<\n HTMLDivElement,\n Assign, ComponentProps>\n>(AccordionPrimitive.Item, \"item\")\n\nexport const Trigger = withContext<\n HTMLButtonElement,\n Assign<\n HTMLStyledProps<\"button\">,\n ComponentProps\n >\n>(AccordionPrimitive.Trigger, \"trigger\")\n\nexport const Content = withContext<\n HTMLDivElement,\n Assign<\n HTMLStyledProps<\"div\">,\n ComponentProps\n >\n>(AccordionPrimitive.Content, \"content\")\n"},{"name":"recipe.ts","content":"import { defineSafe } from \"@utils/defineSafe\"\n\nexport const accordionRecipe = defineSafe.slotRecipe({\n className: \"accordion\",\n description: \"Styles for the Accordion component\",\n slots: [\"root\", \"item\", \"header\", \"trigger\", \"content\"],\n base: {\n item: {\n borderBottom: \"base\",\n },\n header: {\n display: \"flex\",\n },\n trigger: {\n display: \"flex\",\n flex: \"1\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n py: \"4\",\n fontWeight: \"medium\",\n transition: \"all\",\n cursor: \"pointer\",\n\n _hover: {\n textDecoration: \"underline\",\n },\n\n \"& > svg\": {\n h: \"4\",\n w: \"4\",\n flexShrink: \"0\",\n transition: \"transform\",\n transitionDuration: \"normal\",\n },\n\n \"&[data-state=open] > svg\": {\n transform: \"rotate(180deg)\",\n },\n },\n content: {\n overflow: \"hidden\",\n textStyle: \"sm\",\n transition: \"all\",\n\n \"&[data-state=closed]\": {\n animationName: \"accordion-up_radix\",\n animationDuration: \"normal\",\n },\n\n \"&[data-state=open]\": {\n animationName: \"accordion-down_radix\",\n animationDuration: \"normal\",\n },\n\n \"& > div\": {\n pb: \"4\",\n pt: \"0\",\n },\n },\n },\n variants: {\n variant: {\n sm: {\n trigger: {\n color: \"amber.700\",\n },\n content: {\n color: \"pink.300\",\n },\n },\n },\n },\n})\n"}]} \ No newline at end of file +{"name":"accordion","dependencies":["lucide-react","radix-ui","tailwind-variants"],"files":[{"name":"index.tsx","content":"import { createStyleContext } from \"@utils/createStyleContext\"\nimport { ChevronDown } from \"lucide-react\"\nimport { Accordion as AccordionPrimitive } from \"radix-ui\"\nimport { type ComponentPropsWithoutRef, ElementRef, forwardRef } from \"react\"\n\nimport { recipe } from \"./recipe\"\n\nconst { withContext, withProvider } = createStyleContext(recipe)\n\nexport const Root = withProvider(AccordionPrimitive.Root, \"root\")\n\nexport const Item = withContext(AccordionPrimitive.Item, \"item\")\n\nexport const Header = withContext(AccordionPrimitive.Header, \"header\")\n\nconst ContentWrapper = withContext(\"div\", \"contentWrapper\")\n\nconst ContentPrimitive = forwardRef<\n ElementRef,\n ComponentPropsWithoutRef\n>(({ children, ...props }, ref) => {\n return (\n \n {children}\n \n )\n})\n\nexport const Content = withContext(ContentPrimitive, \"content\")\n\nconst TriggerPrimitive = forwardRef<\n ElementRef,\n ComponentPropsWithoutRef\n>(({ children, ...props }, ref) => {\n return (\n
\n \n {children}\n \n \n
\n )\n})\n\nexport const Trigger = withContext(TriggerPrimitive, \"trigger\")\n","type":"ui"},{"name":"recipe.ts","content":"import { tv, type VariantProps } from \"tailwind-variants\"\n\nexport const recipe = tv({\n slots: {\n root: \"\",\n item: \"\",\n header: \"flex\",\n trigger: [\n \"flex flex-1 cursor-pointer items-center justify-between\",\n \"px-1 py-2 text-base/normal font-semibold text-base-content\",\n \"transition-colors\",\n `\n [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:transition-transform\n [&>svg]:duration-200\n `,\n \"data-[state=open]:[&>svg]:rotate-180\",\n ],\n content: [\n \"overflow-hidden text-sm/normal text-base-content/60\",\n \"transition-all\",\n \"data-[state=closed]:animate-accordion-up\",\n \"data-[state=open]:animate-accordion-down\",\n ],\n contentWrapper: \"px-1 py-1\",\n },\n variants: {\n variant: {\n outline: {\n trigger: \"hover:text-primary\",\n item: \"border-b border-base-300\",\n },\n subtle: {\n item: `\n rounded-md\n data-[state=open]:bg-neutral\n `,\n header: \"rounded-md\",\n trigger: `\n rounded-md\n hover:rounded-md hover:bg-neutral\n `,\n },\n },\n },\n defaultVariants: {\n variant: \"outline\",\n },\n})\n\nexport type AccordionVariants = VariantProps\n","type":"ui"}]} \ No newline at end of file diff --git a/app/docs/public/animatebutton.json b/app/docs/public/animatebutton.json index 3759911c0..c985c2222 100644 --- a/app/docs/public/animatebutton.json +++ b/app/docs/public/animatebutton.json @@ -1 +1 @@ -{"name":"animateButton","dependencies":["framer-motion","@/component/button/ui"],"files":[{"name":"index.tsx","content":"import { token } from \"@styled-system/tokens\"\nimport { motion, type Variants } from \"framer-motion\"\nimport type { ComponentProps, ComponentRef } from \"react\"\nimport { forwardRef } from \"react\"\n\nimport { Button } from \"@/component/button/ui\"\n\nexport type AnimateButtonProps = {\n animate: keyof typeof variants\n trigger?: \"hover\" | \"click\"\n initialAnimation?: keyof typeof variants\n} & ComponentProps\n\nconst variants = {\n initial: {\n scale: 1,\n y: 0,\n x: 0,\n transition: {\n ease: \"easeInOut\",\n },\n },\n pulse: {\n scale: [1, 1.05, 1, 1.05, 1],\n transition: {\n duration: parseFloat(token(\"durations.slower\")) / 1000 || 0.5,\n ease: \"easeInOut\",\n times: [0, 0.25, 0.5, 0.75, 1],\n },\n },\n bounce: {\n y: [0, -6, 0, -3, 0],\n transition: {\n duration: parseFloat(token(\"durations.slower\")) / 1000 || 0.5,\n ease: \"easeOut\",\n times: [0, 0.25, 0.5, 0.75, 1],\n },\n },\n shake: {\n x: [0, -6, 6, -3, 3, 0],\n transition: {\n duration: parseFloat(token(\"durations.slower\")) / 1000 || 0.5,\n ease: \"easeInOut\",\n },\n },\n press: {\n scale: 0.95,\n transition: {\n type: \"spring\",\n stiffness: 400,\n damping: 15,\n },\n },\n} satisfies Variants\n\nexport const AnimateButton = forwardRef<\n ComponentRef,\n AnimateButtonProps\n>((props, ref) => {\n const { animate, trigger = \"hover\", initialAnimation, ...restProps } = props\n\n return (\n \n )\n})\n","type":"ui"}]} \ No newline at end of file +{"name":"animateButton","dependencies":["framer-motion","@/component/button/ui"],"files":[{"name":"index.tsx","content":"import { motion, type Variants } from \"framer-motion\"\nimport type { ComponentProps, ComponentRef } from \"react\"\nimport { forwardRef } from \"react\"\n\nimport { Button } from \"@/component/button/ui\"\n\nexport type AnimateButtonProps = {\n animate: keyof typeof variants\n trigger?: \"hover\" | \"click\"\n initialAnimation?: keyof typeof variants\n} & ComponentProps\n\nconst DURATION = 0.5\n\nconst variants = {\n initial: {\n scale: 1,\n y: 0,\n x: 0,\n transition: {\n ease: \"easeInOut\",\n },\n },\n pulse: {\n scale: [1, 1.05, 1, 1.05, 1],\n transition: {\n duration: DURATION,\n ease: \"easeInOut\",\n times: [0, 0.25, 0.5, 0.75, 1],\n },\n },\n bounce: {\n y: [0, -6, 0, -3, 0],\n transition: {\n duration: DURATION,\n ease: \"easeOut\",\n times: [0, 0.25, 0.5, 0.75, 1],\n },\n },\n shake: {\n x: [0, -6, 6, -3, 3, 0],\n transition: {\n duration: DURATION,\n ease: \"easeInOut\",\n },\n },\n press: {\n scale: 0.95,\n transition: {\n type: \"spring\",\n stiffness: 400,\n damping: 15,\n },\n },\n} satisfies Variants\n\nexport const AnimateButton = forwardRef<\n ComponentRef,\n AnimateButtonProps\n>((props, ref) => {\n const { animate, trigger = \"hover\", initialAnimation, ...restProps } = props\n\n return (\n \n )\n})\n","type":"ui"}]} \ No newline at end of file diff --git a/app/docs/public/avatar.json b/app/docs/public/avatar.json index d61f4fa93..592def9bd 100644 --- a/app/docs/public/avatar.json +++ b/app/docs/public/avatar.json @@ -1 +1 @@ -{"name":"Avatar","dependencies":["@radix-ui/react-avatar"],"files":[{"name":"index.tsx","content":"import * as AvatarPrimitive from \"@radix-ui/react-avatar\"\nimport { createStyleContext } from \"@utils/createStyleContext\"\nimport { avatar } from \"@styled-system/recipes\"\nimport type {\n Assign,\n HTMLStyledProps,\n ComponentProps,\n} from \"@styled-system/types\"\n\nconst { withProvider, withContext } = createStyleContext(avatar)\n\nexport const Root = withProvider<\n HTMLSpanElement,\n Assign, ComponentProps>\n>(AvatarPrimitive.Root, \"root\")\n\nexport const Image = withContext<\n HTMLImageElement,\n Assign, ComponentProps>\n>(AvatarPrimitive.Image, \"image\")\n\nexport const Fallback = withContext<\n HTMLSpanElement,\n Assign<\n HTMLStyledProps<\"span\">,\n ComponentProps\n >\n>(AvatarPrimitive.Fallback, \"fallback\")\n"},{"name":"recipe.ts","content":"import { defineSafe } from \"@utils/defineSafe\"\n\nexport const avatarRecipe = defineSafe.slotRecipe({\n className: \"avatar\",\n description: \"Styles for the Avatar component\",\n slots: [\"root\", \"image\", \"fallback\"],\n base: {\n root: {\n position: \"relative\",\n display: \"flex\",\n h: \"12\",\n w: \"12\",\n flexShrink: \"0\",\n overflow: \"hidden\",\n rounded: \"full\",\n },\n image: {\n aspectRatio: \"square\",\n h: \"full\",\n w: \"full\",\n },\n fallback: {\n display: \"flex\",\n h: \"full\",\n w: \"full\",\n alignItems: \"center\",\n justifyContent: \"center\",\n rounded: \"full\",\n bg: \"muted\",\n },\n },\n})\n"}]} \ No newline at end of file +{"name":"avatar","dependencies":["radix-ui","tailwind-variants"],"files":[{"name":"index.tsx","content":"import { createStyleContext } from \"@utils/createStyleContext\"\nimport { Avatar as AvatarPrimitive } from \"radix-ui\"\n\nimport { recipe } from \"./recipe\"\n\nconst { withContext, withProvider } = createStyleContext(recipe)\n\nexport const Root = withProvider(AvatarPrimitive.Root, \"root\")\n\nexport const Image = withContext(AvatarPrimitive.Image, \"image\")\n\nexport const Fallback = withContext(AvatarPrimitive.Fallback, \"fallback\")\n","type":"ui"},{"name":"recipe.ts","content":"import { tv } from \"tailwind-variants\"\n\nexport const recipe = tv({\n slots: {\n root: \"relative flex size-12 shrink-0 overflow-hidden rounded-full\",\n image: \"aspect-square size-full\",\n fallback:\n \"flex size-full items-center justify-center rounded-full bg-neutral\",\n },\n})\n","type":"ui"}]} \ No newline at end of file diff --git a/app/docs/public/button.json b/app/docs/public/button.json index a516f15e3..49cbb56ca 100644 --- a/app/docs/public/button.json +++ b/app/docs/public/button.json @@ -1 +1 @@ -{"name":"Button","dependencies":["@radix-ui/react-slot"],"files":[{"name":"index.tsx","content":"import { styled, type HTMLStyledProps } from \"@styled-system/jsx\"\nimport { button } from \"@styled-system/recipes\"\nimport * as React from \"react\"\nimport { Slot } from \"@radix-ui/react-slot\"\n\nexport type BaseButtonProps = React.ComponentPropsWithoutRef<\"button\"> & {\n asChild?: boolean\n}\n\nexport const BaseButton = React.forwardRef(\n ({ asChild, ...props }, ref) => {\n const Comp = asChild ? Slot : \"button\"\n\n return \n },\n)\n\nBaseButton.displayName = \"\"\n\nexport const Button = styled(BaseButton, button)\nexport type ButtonProps = HTMLStyledProps\n"},{"name":"recipe.ts","content":"import { defineSafe } from \"@utils/defineSafe\"\n\nexport const buttonRecipe = defineSafe.recipe({\n className: \"button\",\n description: \"Styles for the Button component\",\n base: {\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n rounded: \"md\",\n textStyle: \"sm\",\n fontWeight: \"medium\",\n transition: \"colors\",\n cursor: \"pointer\",\n gap: \"2\",\n _focusVisible: {\n outlineColor: \"background\",\n outlineWidth: \"2px\",\n ringColor: \"ring\",\n ringOffset: \"2\",\n },\n\n _disabled: {\n cursor: \"not-allowed\",\n opacity: \"50%\",\n },\n },\n variants: {\n variant: {\n default: {\n bg: \"primary\",\n color: \"primary.foreground\",\n\n _hover: {\n bg: \"primary/90\",\n },\n },\n destructive: {\n bg: \"destructive\",\n color: \"destructive.foreground\",\n\n _hover: {\n bg: \"destructive/90\",\n },\n },\n outline: {\n border: \"input\",\n bg: \"background\",\n\n _hover: {\n bg: \"accent\",\n color: \"accent.foreground\",\n },\n },\n secondary: {\n bg: \"secondary\",\n color: \"secondary.foreground\",\n\n _hover: {\n bga: \"secondary/90\",\n },\n },\n ghost: {\n _hover: {\n bg: \"accent\",\n color: \"accent.foreground\",\n },\n },\n link: {\n color: \"primary\",\n textUnderlineOffset: \"4px\",\n\n _hover: {\n textDecoration: \"underline\",\n },\n },\n },\n size: {\n default: {\n h: \"10\",\n px: \"4\",\n py: \"2\",\n },\n sm: {\n h: \"9\",\n rounded: \"md\",\n px: \"3\",\n },\n lg: {\n h: \"11\",\n rounded: \"md\",\n px: \"8\",\n },\n icon: {\n h: \"10\",\n w: \"10\",\n },\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n})\n"}]} \ No newline at end of file +{"name":"button","dependencies":["radix-ui","tailwind-variants"],"files":[{"name":"index.tsx","content":"import { cn } from \"@utils/cn\"\nimport { Slot } from \"radix-ui\"\nimport type { ComponentPropsWithoutRef } from \"react\"\nimport { forwardRef } from \"react\"\n\nimport { type ButtonVariantProps, recipe } from \"./recipe\"\n\nexport type ButtonProps = ComponentPropsWithoutRef<\"button\"> & {\n asChild?: boolean\n} & ButtonVariantProps\n\nexport const Button = forwardRef(\n ({ asChild, className, size, variant, ...props }, ref) => {\n const Comp = asChild ? Slot.Root : \"button\"\n return (\n \n )\n },\n)\n\nButton.displayName = \"Button\"\n","type":"ui"},{"name":"recipe.ts","content":"import { tv, type VariantProps } from \"tailwind-variants\"\n\nexport type ButtonVariantProps = VariantProps\n\nexport const recipe = tv({\n base: [\n \"inline-flex shrink-0 items-center justify-center gap-1\",\n \"min-h-9 cursor-pointer rounded-md whitespace-nowrap\",\n \"text-sm/snug font-semibold tracking-wide\",\n \"[&_svg]:shrink-0\",\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n ],\n variants: {\n size: {\n sm: `\n h-9 px-3 py-1.5 text-xs font-semibold\n [&_svg]:size-3.5\n `,\n md: `\n h-10 px-4 py-2\n [&_svg]:size-4\n `,\n lg: `\n h-11 px-5 py-2\n [&_svg]:size-5\n `,\n },\n variant: {\n primary: `\n bg-primary text-primary-content\n hover:not-disabled:brightness-90\n `,\n secondary: `\n bg-secondary text-secondary-content\n hover:not-disabled:brightness-90\n `,\n destructive: `\n bg-error text-error-content\n hover:not-disabled:brightness-90\n `,\n outline: `\n border border-base-300 bg-base-100 text-base-content\n hover:not-disabled:bg-base-200\n `,\n link: `\n text-primary underline-offset-2\n hover:not-disabled:underline\n `,\n },\n },\n defaultVariants: {\n size: \"md\",\n variant: \"primary\",\n },\n})\n","type":"ui"}]} \ No newline at end of file diff --git a/app/docs/public/calendar.json b/app/docs/public/calendar.json index 50e78f7e6..7698548ef 100644 --- a/app/docs/public/calendar.json +++ b/app/docs/public/calendar.json @@ -1 +1 @@ -{"name":"calendar","dependencies":["lucide-react","radix-ui/internal"],"files":[{"name":"index.tsx","content":"\"use client\"\n\nimport { css, cx } from \"@styled-system/css\"\nimport { ChevronLeft, ChevronRight } from \"lucide-react\"\nimport { Context, useControllableState } from \"radix-ui/internal\"\nimport {\n type ComponentPropsWithoutRef,\n forwardRef,\n type ReactNode,\n useCallback,\n useMemo,\n} from \"react\"\n\nimport { recipe } from \"./recipe\"\n\ntype DateFormat = {\n year: number\n month: number\n day: number\n daysInMonth: number\n daysInPrevMonth: number\n startWeek: number //0 | 1 | 2 | 3 | 4 | 5 | 6\n nextMonthStartWeek: number //0 | 1 | 2 | 3 | 4 | 5 | 6\n}\n\nexport interface CalendarContextType {\n value: DateFormat\n weekStart: 0 | 1\n locale: Intl.LocalesArgument\n\n onMonthChange: (amount: number) => void\n onYearChange: (amount: number) => void\n selectedValue: Array\n handleDayClick: (date: Date) => void\n}\n\nconst contextScopeName = \"calendar\"\n\nconst [CalendarProvider, useCalendarContext] =\n Context.createContext(contextScopeName)\n\ntype RangeDate = Array\n\ninterface CalendarBaseProps extends ComponentPropsWithoutRef<\"div\"> {\n children?: ReactNode\n weekStart?: 0 | 1\n locale?: Intl.LocalesArgument\n}\n\ninterface CalendarViewProps {\n viewDate?: Date // 제어: 현재 보여지는 월/년 기준 날짜\n defaultViewDate?: Date // 비제어: 초기 보여지는 월/년 기준 날짜\n onViewDateChange?: (date: Date) => void // viewDate 변경 시 콜백\n}\n\nexport interface CalendarRootProps\n extends CalendarBaseProps,\n CalendarViewProps {\n type: \"single\"\n\n date?: Date\n defaultDate?: Date\n onDateChange?: (date: Date) => void\n}\n\nexport interface CalendarRangeProps\n extends CalendarBaseProps,\n CalendarViewProps {\n type: \"range\" // 구별자\n range?: RangeDate // 제어: 선택된 날짜 범위\n defaultRange?: RangeDate // 비제어: 초기 선택된 날짜 범위\n onRangeChange?: (range: RangeDate | undefined) => void // range 변경 시 콜백\n}\n\nexport const Root = (props: CalendarRootProps | CalendarRangeProps) => {\n const { type = \"single\", ...rest } = props\n const singleProps = rest as CalendarRootProps\n const rangeProps = rest as CalendarRangeProps\n if (type === \"single\") {\n return \n } else {\n return \n }\n}\n\nexport const RangeCalendar = forwardRef(\n (\n {\n className,\n children,\n range,\n defaultRange,\n onRangeChange,\n viewDate,\n defaultViewDate,\n onViewDateChange,\n weekStart = 0,\n locale = \"en-US\",\n ...props\n },\n ref,\n ) => {\n const [rangeValue = [], setRangeValue] = useControllableState({\n prop: range,\n defaultProp: defaultRange,\n onChange: onRangeChange,\n })\n\n const [viewDateValue = new Date(), setViewDateValue] = useControllableState(\n {\n prop: viewDate,\n defaultProp: defaultViewDate,\n onChange: onViewDateChange,\n },\n )\n\n const onMonthChange = useCallback(\n (amount: number) => {\n const newDate = new Date(viewDateValue)\n newDate.setMonth(newDate.getMonth() + amount)\n setViewDateValue(newDate)\n },\n [viewDateValue, setViewDateValue],\n )\n\n const onYearChange = useCallback(\n (amount: number) => {\n const newDate = new Date(viewDateValue)\n newDate.setFullYear(newDate.getFullYear() + amount)\n setViewDateValue(newDate)\n },\n [viewDateValue, setViewDateValue],\n )\n\n const dateFormat = useMemo(() => {\n const currentDate = new Date(viewDateValue)\n const firstDay = new Date(\n currentDate.getFullYear(),\n currentDate.getMonth(),\n 1,\n )\n const lastDay = new Date(\n currentDate.getFullYear(),\n currentDate.getMonth() + 1,\n 0,\n )\n const prevMonthLastDay = new Date(\n currentDate.getFullYear(),\n currentDate.getMonth(),\n 0,\n )\n const nextMonthFirstDay = new Date(\n currentDate.getFullYear(),\n currentDate.getMonth() + 1,\n 1,\n )\n\n return {\n year: currentDate.getFullYear(),\n month: currentDate.getMonth() + 1,\n day: currentDate.getDate(),\n daysInMonth: lastDay.getDate(),\n startWeek: (firstDay.getDay() - weekStart + 7) % 7,\n daysInPrevMonth: prevMonthLastDay.getDate(),\n nextMonthStartWeek: (nextMonthFirstDay.getDay() - weekStart + 7) % 7,\n }\n }, [viewDateValue, weekStart])\n\n const handleDayClick = useCallback(\n (clickedDate: Date) => {\n // 시간을 0으로 설정하여 날짜만 비교 (정확한 비교 위함)\n const normalizedClickedDate = new Date(clickedDate)\n normalizedClickedDate.setHours(0, 0, 0, 0)\n // 현재 상태(prevRange)를 받아 다음 상태를 반환하는 함수형 업데이트 사용\n setRangeValue((prevRange) => {\n const [start, end] = prevRange || [null, null]\n\n // 시작 날짜의 시간도 0으로 설정 (비교를 위해)\n const normalizedStart = start ? new Date(start) : null\n if (normalizedStart) normalizedStart.setHours(0, 0, 0, 0)\n\n // 1. 시작 날짜가 없는 경우: 클릭한 날짜를 새 시작 날짜로 설정\n if (!normalizedStart) {\n return [clickedDate, null] // 원본 Date 객체 저장\n }\n\n // 2. 시작 날짜만 있고 종료 날짜는 없는 경우:\n if (normalizedStart && !end) {\n // 클릭한 날짜가 시작 날짜보다 이전이면 -> 클릭한 날짜를 새 시작 날짜로 설정 (범위 리셋)\n if (normalizedClickedDate < normalizedStart) {\n return [clickedDate, null]\n }\n // 클릭한 날짜가 시작 날짜와 같거나 이후면 -> 클릭한 날짜를 종료 날짜로 설정\n else {\n // 시작 날짜와 동일한 날짜를 클릭하면 종료 날짜도 시작 날짜와 동일하게 설정할지,\n // 아니면 아무것도 안할지 정책 결정 필요 (여기선 종료로 설정)\n return [start, clickedDate] // 원본 Date 객체 저장\n }\n }\n\n // 3. 시작 날짜와 종료 날짜가 모두 있는 경우: 클릭한 날짜를 새 시작 날짜로 설정 (범위 리셋)\n if (normalizedStart && end) {\n return [clickedDate, null]\n }\n })\n },\n [setRangeValue],\n )\n\n return (\n
\n \n {children}\n \n
\n )\n },\n)\n\nexport const SingleCalendar = forwardRef(\n (\n {\n className,\n children,\n date,\n defaultDate,\n onDateChange,\n viewDate,\n defaultViewDate,\n onViewDateChange,\n weekStart = 0,\n locale = \"en-US\",\n ...props\n },\n ref,\n ) => {\n const [dateValue = new Date(), setDateValue] = useControllableState({\n prop: viewDate,\n defaultProp: defaultViewDate,\n onChange: onViewDateChange,\n }) //화면에 보여지는 날짜\n\n const [selectedValue = null, setSelectedValue] = useControllableState({\n prop: date,\n defaultProp: defaultDate,\n onChange: onDateChange,\n })\n const onMonthChange = useCallback(\n (amount: number) => {\n const newDate = new Date(dateValue)\n newDate.setMonth(newDate.getMonth() + amount)\n setDateValue(newDate)\n },\n [dateValue, setDateValue],\n )\n\n const onYearChange = useCallback(\n (amount: number) => {\n const newDate = new Date(dateValue)\n newDate.setFullYear(newDate.getFullYear() + amount)\n setDateValue(newDate)\n },\n [dateValue, setDateValue],\n )\n\n const dateFormat = useMemo(() => {\n const currentDate = new Date(dateValue)\n const firstDay = new Date(\n currentDate.getFullYear(),\n currentDate.getMonth(),\n 1,\n )\n const lastDay = new Date(\n currentDate.getFullYear(),\n currentDate.getMonth() + 1,\n 0,\n )\n const prevMonthLastDay = new Date(\n currentDate.getFullYear(),\n currentDate.getMonth(),\n 0,\n )\n const nextMonthFirstDay = new Date(\n currentDate.getFullYear(),\n currentDate.getMonth() + 1,\n 1,\n )\n\n return {\n year: currentDate.getFullYear(),\n month: currentDate.getMonth() + 1,\n day: currentDate.getDate(),\n daysInMonth: lastDay.getDate(),\n startWeek: (firstDay.getDay() - weekStart + 7) % 7,\n daysInPrevMonth: prevMonthLastDay.getDate(),\n nextMonthStartWeek: (nextMonthFirstDay.getDay() - weekStart + 7) % 7,\n }\n }, [dateValue, weekStart])\n\n const styles = recipe.raw()\n return (\n setSelectedValue(clickedDate)}\n >\n
\n {children}\n
\n \n )\n },\n)\n\ninterface HeaderProps {\n month?: Intl.DateTimeFormatOptions[\"month\"]\n year?: Intl.DateTimeFormatOptions[\"year\"]\n render?: (date: Date, locale: Intl.LocalesArgument) => string\n className?: string\n}\n\nexport const Header = ({\n className,\n month = \"long\",\n year = \"numeric\",\n render,\n}: HeaderProps) => {\n const { value, onMonthChange, locale } = useCalendarContext(contextScopeName)\n const styles = recipe.raw()\n\n const monthAndYear = new Intl.DateTimeFormat(locale, {\n month: month,\n year: year,\n }).format(new Date(value.year, value.month - 1))\n\n return (\n
\n onMonthChange(-1)}\n aria-label=\"Go To Previous month\"\n >\n \n \n
\n {render\n ? render(new Date(value.year, value.month - 1), locale)\n : `${monthAndYear}`}\n
\n onMonthChange(1)}\n aria-label=\"Go To Next month\"\n >\n \n \n
\n )\n}\n\ninterface WeekdayProps {\n format?: \"short\" | \"long\"\n className?: string\n}\n\nexport const Weekday = ({ className, format = \"short\" }: WeekdayProps) => {\n const { weekStart, locale } = useCalendarContext(contextScopeName)\n const styles = recipe.raw()\n\n return (\n
\n {getWeekdays(weekStart, locale, format).map((day, index) => (\n
\n {day}\n
\n ))}\n
\n )\n}\n\ninterface DaysProps {\n showOutsideDays?: boolean\n className?: string\n}\n\ninterface DayButtonProps extends ComponentPropsWithoutRef<\"button\"> {\n day: number\n month: number\n year: number\n isHidden: boolean\n isOutsideMonth: boolean\n className?: string\n}\n\nconst DayButton = ({\n className,\n day,\n month,\n year,\n isHidden,\n isOutsideMonth,\n ...props\n}: DayButtonProps) => {\n const styles = recipe.raw()\n\n return (\n \n {!isHidden && day}\n \n )\n}\n\nexport const Days = ({ className, showOutsideDays = true }: DaysProps) => {\n const { value, handleDayClick } = useCalendarContext(contextScopeName)\n const styles = recipe.raw()\n const weeks = useMemo(() => {\n const days: Array<{ day: number; isCurrentMonth: boolean }> = []\n\n for (let i = value.startWeek; i > 0; i--) {\n days.push({\n day: value.daysInPrevMonth - i + 1,\n isCurrentMonth: false,\n })\n }\n\n for (let i = 1; i <= value.daysInMonth; i++) {\n days.push({\n day: i,\n isCurrentMonth: true,\n })\n }\n\n for (let i = 1; i <= 6 - value.nextMonthStartWeek + 1; i++) {\n days.push({\n day: i,\n isCurrentMonth: false,\n })\n }\n\n const weeks = []\n for (let i = 0; i < days.length; i += 7) {\n weeks.push(days.slice(i, i + 7))\n }\n\n return weeks\n }, [\n value.startWeek,\n value.daysInMonth,\n value.daysInPrevMonth,\n value.nextMonthStartWeek,\n ])\n\n return (\n \n \n {weeks.map((week, weekIndex) => (\n \n {week.map((day, dayIndex) => {\n const isHidden = !showOutsideDays && !day.isCurrentMonth\n const month = day.isCurrentMonth\n ? value.month\n : weekIndex === 0\n ? value.month - 1\n : value.month + 1\n\n return (\n \n \n handleDayClick(new Date(value.year, month - 1, day.day))\n }\n />\n \n )\n })}\n \n ))}\n \n
\n )\n}\n\nfunction getWeekdays(\n weekStart: 0 | 1,\n locale: Intl.LocalesArgument,\n format: Intl.DateTimeFormatOptions[\"weekday\"] = \"short\",\n): string[] {\n const dayIndices = [0, 1, 2, 3, 4, 5, 6]\n\n const orderedDayIndices = [\n ...dayIndices.slice(weekStart),\n ...dayIndices.slice(0, weekStart),\n ]\n\n return orderedDayIndices.map((dayIndex) => {\n const date = new Date()\n date.setDate(date.getDate() - date.getDay() + dayIndex)\n\n return new Intl.DateTimeFormat(locale, { weekday: format }).format(date)\n })\n}\n","type":"ui"},{"name":"recipe.ts","content":"import { sva } from \"@styled-system/css\"\n\nexport const recipe = sva({\n slots: [\n \"root\",\n \"header\",\n \"title\",\n \"navButton\",\n \"weekday\",\n \"daysGrid\",\n \"weekRow\",\n \"dayCell\",\n ],\n base: {\n root: {\n display: \"flex\",\n flexDirection: \"column\",\n borderRadius: \"md\",\n boxShadow: \"sm\",\n bg: \"white\",\n color: \"black\",\n overflow: \"hidden\",\n },\n header: {\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n padding: \"3\",\n },\n title: {\n textStyle: \"xl\",\n },\n navButton: {\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n width: \"8\",\n height: \"8\",\n borderRadius: \"full\",\n color: \"gray.500\",\n cursor: \"pointer\",\n _hover: {\n opacity: 0.8,\n bg: \"gray.100\",\n },\n _active: {\n opacity: 0.6,\n },\n _disabled: {\n opacity: 0.4,\n cursor: \"not-allowed\",\n _hover: {\n bg: \"transparent\",\n },\n },\n \"& svg\": {\n width: \"4\",\n height: \"4\",\n },\n },\n weekday: {\n display: \"flex\",\n justifyContent: \"space-between\",\n padding: \"2\",\n },\n daysGrid: {\n display: \"flex\",\n flexDirection: \"column\",\n padding: \"2\",\n },\n weekRow: {\n display: \"flex\",\n width: \"full\",\n justifyContent: \"space-between\",\n marginBottom: \"1\",\n \"&:last-child\": {\n marginBottom: \"0\",\n },\n },\n dayCell: {\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n minWidth: \"4\",\n minHeight: \"4\",\n borderRadius: \"md\",\n fontSize: \"sm\",\n cursor: \"pointer\",\n _hover: {\n opacity: 0.8,\n bg: \"gray.100\",\n },\n _active: {\n opacity: 0.6,\n },\n },\n },\n})\n","type":"ui"}]} \ No newline at end of file +{"name":"calendar","dependencies":["lucide-react","radix-ui/internal","tailwind-variants"],"files":[{"name":"index.tsx","content":"\"use client\"\n\nimport { cn } from \"@utils/cn\"\nimport { ChevronLeft, ChevronRight } from \"lucide-react\"\nimport { Context, useControllableState } from \"radix-ui/internal\"\nimport {\n type ComponentPropsWithoutRef,\n forwardRef,\n type ReactNode,\n useCallback,\n useMemo,\n} from \"react\"\n\nimport { recipe } from \"./recipe\"\n\ntype DateFormat = {\n year: number\n month: number\n day: number\n daysInMonth: number\n daysInPrevMonth: number\n startWeek: number //0 | 1 | 2 | 3 | 4 | 5 | 6\n nextMonthStartWeek: number //0 | 1 | 2 | 3 | 4 | 5 | 6\n}\n\nexport interface CalendarContextType {\n value: DateFormat\n weekStart: 0 | 1\n locale: Intl.LocalesArgument\n\n onMonthChange: (amount: number) => void\n onYearChange: (amount: number) => void\n selectedValue: Array\n handleDayClick: (date: Date) => void\n}\n\nconst contextScopeName = \"calendar\"\n\nconst [CalendarProvider, useCalendarContext] =\n Context.createContext(contextScopeName)\n\ntype RangeDate = Array\n\ninterface CalendarBaseProps extends ComponentPropsWithoutRef<\"div\"> {\n children?: ReactNode\n weekStart?: 0 | 1\n locale?: Intl.LocalesArgument\n}\n\ninterface CalendarViewProps {\n viewDate?: Date\n defaultViewDate?: Date\n onViewDateChange?: (date: Date) => void\n}\n\nexport interface CalendarRootProps\n extends CalendarBaseProps,\n CalendarViewProps {\n type: \"single\"\n\n date?: Date\n defaultDate?: Date\n onDateChange?: (date: Date) => void\n}\n\nexport interface CalendarRangeProps\n extends CalendarBaseProps,\n CalendarViewProps {\n type: \"range\"\n range?: RangeDate\n defaultRange?: RangeDate\n onRangeChange?: (range: RangeDate | undefined) => void\n}\n\nexport const Root = (props: CalendarRootProps | CalendarRangeProps) => {\n const { type = \"single\", ...rest } = props\n const singleProps = rest as CalendarRootProps\n const rangeProps = rest as CalendarRangeProps\n if (type === \"single\") {\n return \n } else {\n return \n }\n}\n\nexport const RangeCalendar = forwardRef(\n (\n {\n className,\n children,\n range,\n defaultRange,\n onRangeChange,\n viewDate,\n defaultViewDate,\n onViewDateChange,\n weekStart = 0,\n locale = \"en-US\",\n ...props\n },\n ref,\n ) => {\n const [rangeValue = [], setRangeValue] = useControllableState({\n prop: range,\n defaultProp: defaultRange,\n onChange: onRangeChange,\n })\n\n const [viewDateValue = new Date(), setViewDateValue] = useControllableState(\n {\n prop: viewDate,\n defaultProp: defaultViewDate,\n onChange: onViewDateChange,\n },\n )\n\n const onMonthChange = useCallback(\n (amount: number) => {\n const newDate = new Date(viewDateValue)\n newDate.setMonth(newDate.getMonth() + amount)\n setViewDateValue(newDate)\n },\n [viewDateValue, setViewDateValue],\n )\n\n const onYearChange = useCallback(\n (amount: number) => {\n const newDate = new Date(viewDateValue)\n newDate.setFullYear(newDate.getFullYear() + amount)\n setViewDateValue(newDate)\n },\n [viewDateValue, setViewDateValue],\n )\n\n const dateFormat = useMemo(() => {\n const currentDate = new Date(viewDateValue)\n const firstDay = new Date(\n currentDate.getFullYear(),\n currentDate.getMonth(),\n 1,\n )\n const lastDay = new Date(\n currentDate.getFullYear(),\n currentDate.getMonth() + 1,\n 0,\n )\n const prevMonthLastDay = new Date(\n currentDate.getFullYear(),\n currentDate.getMonth(),\n 0,\n )\n const nextMonthFirstDay = new Date(\n currentDate.getFullYear(),\n currentDate.getMonth() + 1,\n 1,\n )\n\n return {\n year: currentDate.getFullYear(),\n month: currentDate.getMonth() + 1,\n day: currentDate.getDate(),\n daysInMonth: lastDay.getDate(),\n startWeek: (firstDay.getDay() - weekStart + 7) % 7,\n daysInPrevMonth: prevMonthLastDay.getDate(),\n nextMonthStartWeek: (nextMonthFirstDay.getDay() - weekStart + 7) % 7,\n }\n }, [viewDateValue, weekStart])\n\n const handleDayClick = useCallback(\n (clickedDate: Date) => {\n const normalizedClickedDate = new Date(clickedDate)\n normalizedClickedDate.setHours(0, 0, 0, 0)\n setRangeValue((prevRange) => {\n const [start, end] = prevRange || [null, null]\n\n const normalizedStart = start ? new Date(start) : null\n if (normalizedStart) normalizedStart.setHours(0, 0, 0, 0)\n\n if (!normalizedStart) {\n return [clickedDate, null]\n }\n\n if (normalizedStart && !end) {\n if (normalizedClickedDate < normalizedStart) {\n return [clickedDate, null]\n } else {\n return [start, clickedDate]\n }\n }\n\n if (normalizedStart && end) {\n return [clickedDate, null]\n }\n })\n },\n [setRangeValue],\n )\n\n return (\n
\n \n {children}\n \n
\n )\n },\n)\n\nexport const SingleCalendar = forwardRef(\n (\n {\n className,\n children,\n date,\n defaultDate,\n onDateChange,\n viewDate,\n defaultViewDate,\n onViewDateChange,\n weekStart = 0,\n locale = \"en-US\",\n ...props\n },\n ref,\n ) => {\n const [dateValue = new Date(), setDateValue] = useControllableState({\n prop: viewDate,\n defaultProp: defaultViewDate,\n onChange: onViewDateChange,\n })\n\n const [selectedValue = null, setSelectedValue] = useControllableState({\n prop: date,\n defaultProp: defaultDate,\n onChange: onDateChange,\n })\n const onMonthChange = useCallback(\n (amount: number) => {\n const newDate = new Date(dateValue)\n newDate.setMonth(newDate.getMonth() + amount)\n setDateValue(newDate)\n },\n [dateValue, setDateValue],\n )\n\n const onYearChange = useCallback(\n (amount: number) => {\n const newDate = new Date(dateValue)\n newDate.setFullYear(newDate.getFullYear() + amount)\n setDateValue(newDate)\n },\n [dateValue, setDateValue],\n )\n\n const dateFormat = useMemo(() => {\n const currentDate = new Date(dateValue)\n const firstDay = new Date(\n currentDate.getFullYear(),\n currentDate.getMonth(),\n 1,\n )\n const lastDay = new Date(\n currentDate.getFullYear(),\n currentDate.getMonth() + 1,\n 0,\n )\n const prevMonthLastDay = new Date(\n currentDate.getFullYear(),\n currentDate.getMonth(),\n 0,\n )\n const nextMonthFirstDay = new Date(\n currentDate.getFullYear(),\n currentDate.getMonth() + 1,\n 1,\n )\n\n return {\n year: currentDate.getFullYear(),\n month: currentDate.getMonth() + 1,\n day: currentDate.getDate(),\n daysInMonth: lastDay.getDate(),\n startWeek: (firstDay.getDay() - weekStart + 7) % 7,\n daysInPrevMonth: prevMonthLastDay.getDate(),\n nextMonthStartWeek: (nextMonthFirstDay.getDay() - weekStart + 7) % 7,\n }\n }, [dateValue, weekStart])\n\n const styles = recipe()\n return (\n setSelectedValue(clickedDate)}\n >\n
\n {children}\n
\n \n )\n },\n)\n\ninterface HeaderProps {\n month?: Intl.DateTimeFormatOptions[\"month\"]\n year?: Intl.DateTimeFormatOptions[\"year\"]\n render?: (date: Date, locale: Intl.LocalesArgument) => string\n className?: string\n}\n\nexport const Header = ({\n className,\n month = \"long\",\n year = \"numeric\",\n render,\n}: HeaderProps) => {\n const { value, onMonthChange, locale } = useCalendarContext(contextScopeName)\n const styles = recipe()\n\n const monthAndYear = new Intl.DateTimeFormat(locale, {\n month: month,\n year: year,\n }).format(new Date(value.year, value.month - 1))\n\n return (\n
\n onMonthChange(-1)}\n aria-label=\"Go To Previous month\"\n >\n \n \n
\n {render\n ? render(new Date(value.year, value.month - 1), locale)\n : `${monthAndYear}`}\n
\n onMonthChange(1)}\n aria-label=\"Go To Next month\"\n >\n \n \n
\n )\n}\n\ninterface WeekdayProps {\n format?: \"short\" | \"long\"\n className?: string\n}\n\nexport const Weekday = ({ className, format = \"short\" }: WeekdayProps) => {\n const { weekStart, locale } = useCalendarContext(contextScopeName)\n const styles = recipe()\n\n return (\n
\n {getWeekdays(weekStart, locale, format).map((day, index) => (\n
\n {day}\n
\n ))}\n
\n )\n}\n\ninterface DaysProps {\n showOutsideDays?: boolean\n className?: string\n}\n\ninterface DayButtonProps extends ComponentPropsWithoutRef<\"button\"> {\n day: number\n month: number\n year: number\n isHidden: boolean\n isOutsideMonth: boolean\n className?: string\n}\n\nconst DayButton = ({\n className,\n day,\n month,\n year,\n isHidden,\n isOutsideMonth,\n ...props\n}: DayButtonProps) => {\n const styles = recipe()\n\n return (\n \n {!isHidden && day}\n \n )\n}\n\nexport const Days = ({ className, showOutsideDays = true }: DaysProps) => {\n const { value, handleDayClick } = useCalendarContext(contextScopeName)\n const styles = recipe()\n const weeks = useMemo(() => {\n const days: Array<{ day: number; isCurrentMonth: boolean }> = []\n\n for (let i = value.startWeek; i > 0; i--) {\n days.push({\n day: value.daysInPrevMonth - i + 1,\n isCurrentMonth: false,\n })\n }\n\n for (let i = 1; i <= value.daysInMonth; i++) {\n days.push({\n day: i,\n isCurrentMonth: true,\n })\n }\n\n for (let i = 1; i <= 6 - value.nextMonthStartWeek + 1; i++) {\n days.push({\n day: i,\n isCurrentMonth: false,\n })\n }\n\n const weeks = []\n for (let i = 0; i < days.length; i += 7) {\n weeks.push(days.slice(i, i + 7))\n }\n\n return weeks\n }, [\n value.startWeek,\n value.daysInMonth,\n value.daysInPrevMonth,\n value.nextMonthStartWeek,\n ])\n\n return (\n \n \n {weeks.map((week, weekIndex) => (\n \n {week.map((day, dayIndex) => {\n const isHidden = !showOutsideDays && !day.isCurrentMonth\n const month = day.isCurrentMonth\n ? value.month\n : weekIndex === 0\n ? value.month - 1\n : value.month + 1\n\n return (\n \n \n handleDayClick(new Date(value.year, month - 1, day.day))\n }\n />\n \n )\n })}\n \n ))}\n \n
\n )\n}\n\nfunction getWeekdays(\n weekStart: 0 | 1,\n locale: Intl.LocalesArgument,\n format: Intl.DateTimeFormatOptions[\"weekday\"] = \"short\",\n): string[] {\n const dayIndices = [0, 1, 2, 3, 4, 5, 6]\n\n const orderedDayIndices = [\n ...dayIndices.slice(weekStart),\n ...dayIndices.slice(0, weekStart),\n ]\n\n return orderedDayIndices.map((dayIndex) => {\n const date = new Date()\n date.setDate(date.getDate() - date.getDay() + dayIndex)\n\n return new Intl.DateTimeFormat(locale, { weekday: format }).format(date)\n })\n}\n","type":"ui"},{"name":"recipe.ts","content":"import { tv } from \"tailwind-variants\"\n\nexport const recipe = tv({\n slots: {\n root: `\n flex flex-col overflow-hidden rounded-md bg-base-100 text-base-content\n shadow-sm\n `,\n header: \"flex items-center justify-between p-3\",\n title: \"text-xl\",\n navButton: [\n \"flex size-8 cursor-pointer items-center justify-center rounded-full\",\n `\n text-base-content/60\n hover:bg-base-200 hover:opacity-80\n `,\n \"active:opacity-60\",\n `\n disabled:cursor-not-allowed disabled:opacity-40\n disabled:hover:bg-transparent\n `,\n \"[&_svg]:size-4\",\n ],\n weekday: \"flex justify-between p-2\",\n daysGrid: \"flex flex-col p-2\",\n weekRow: `\n mb-1 flex w-full justify-between\n last:mb-0\n `,\n dayCell: [\n \"flex min-h-4 min-w-4 items-center justify-center\",\n \"cursor-pointer rounded-md text-sm\",\n `\n hover:bg-base-200 hover:opacity-80\n active:opacity-60\n `,\n ],\n },\n})\n","type":"ui"}]} \ No newline at end of file diff --git a/app/docs/public/checkbox.json b/app/docs/public/checkbox.json index 90eecfa32..4428155ac 100644 --- a/app/docs/public/checkbox.json +++ b/app/docs/public/checkbox.json @@ -1 +1 @@ -{"name":"checkbox","dependencies":["radix-ui/internal"],"files":[{"name":"index.tsx","content":"import { cx } from \"@styled-system/css\"\nimport { useComposedRefs, useControllableState } from \"radix-ui/internal\"\nimport {\n type ComponentPropsWithoutRef,\n forwardRef,\n useEffect,\n useId,\n useRef,\n} from \"react\"\n\nimport { checkboxRecipe, type CheckboxVariants } from \"./recipe\"\n\ntype CheckboxInputProps = Omit, \"size\"> & {\n indeterminate?: boolean\n onCheckedChange?: (checked: boolean) => void\n}\n\ntype CheckboxLabelProps = {\n label?: string\n}\n\ntype CheckboxProps = CheckboxInputProps & CheckboxVariants & CheckboxLabelProps\n\nexport const Checkbox = forwardRef(\n (props, ref) => {\n const [variantProps, componentProps] =\n checkboxRecipe.splitVariantProps(props)\n\n const { label, indeterminate, onCheckedChange, onChange, ...inputProps } =\n componentProps\n\n const {\n root,\n label: labelClassName,\n input,\n text,\n } = checkboxRecipe(variantProps)\n\n const [isChecked = false, setIsChecked] = useControllableState({\n prop: inputProps.checked,\n defaultProp: inputProps.defaultChecked,\n onChange: onCheckedChange,\n })\n\n const inputRef = useRef(null)\n\n const initialChecked = useRef(isChecked)\n\n const id = useId()\n\n useEffect(() => {\n if (!inputRef.current) {\n return\n }\n inputRef.current.indeterminate = indeterminate ?? false\n }, [indeterminate])\n\n useEffect(() => {\n const form = inputRef.current?.form\n if (form) {\n const reset = () => setIsChecked(initialChecked.current)\n form.addEventListener(\"reset\", reset)\n return () => {\n form.removeEventListener(\"reset\", reset)\n }\n }\n }, [setIsChecked])\n\n const composedRefs = useComposedRefs(ref, inputRef)\n\n return (\n
\n \n
\n )\n },\n)\n\nCheckbox.displayName = \"Checkbox\"\n","type":"ui"},{"name":"recipe.ts","content":"import { type RecipeVariantProps, sva } from \"@styled-system/css\"\n\nconst checkmark = {\n square:\n \"url('data:image/svg+xml;charset=utf-8,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 24 24%27%3E%3Cpath d=%27M10.41 17.854c-.34 0-.679-.113-.905-.453l-5.203-5.203c-.566-.565-.566-1.357 0-1.923.566-.565 1.357-.565 1.923 0l4.185 4.185 7.465-7.465c.566-.565 1.357-.565 1.923 0 .566.566.566 1.358 0 1.923l-8.37 8.37c-.34.453-.679.566-1.018.566z%27 fill=%27%23fff%27/%3E%3C/svg%3E')\",\n ghost:\n \"url('data:image/svg+xml;charset=utf-8,%3Csvg xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22 viewBox%3D%220 0 24 24%22%3E%3Cpath d%3D%22M10.41 17.854c-.34 0-.679-.113-.905-.453l-5.203-5.203c-.566-.565-.566-1.357 0-1.923.566-.565 1.357-.565 1.923 0l4.185 4.185 7.465-7.465c.566-.565 1.357-.565 1.923 0 .566.566.566 1.358 0 1.923l-8.37 8.37c-.34.453-.679.566-1.018.566z%22 fill%3D%22%23808080%22/%3E%3C/svg%3E')\",\n indeterminate_square:\n \"url('data:image/svg+xml;charset=utf-8,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 24 24%27%3E%3Crect x=%276%27 y=%2710.5%27 width=%2712%27 height=%273%27 fill=%27%23fff%27/%3E%3C/svg%3E')\",\n indeterminate_ghost:\n \"url('data:image/svg+xml;charset=utf-8,%3Csvg xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22 viewBox%3D%220 0 24 24%22%3E%3Crect x%3D%226%22 y%3D%2210.5%22 width%3D%2212%22 height%3D%223%22 fill%3D%22%23808080%22/%3E%3C/svg%3E')\",\n}\n\nexport const checkboxRecipe = sva({\n slots: [\"root\", \"label\", \"input\", \"text\"],\n base: {\n root: {\n position: \"relative\",\n display: \"flex\",\n },\n label: {\n display: \"inline-flex\",\n alignItems: \"center\",\n position: \"relative\",\n maxWidth: \"full\",\n minHeight: \"9\",\n paddingLeft: \"8\",\n lineHeight: \"relaxed\",\n },\n input: {\n border: \"none\",\n cursor: \"pointer\",\n position: \"absolute\",\n left: \"0\",\n margin: \"0\",\n padding: \"0\",\n w: \"0\",\n h: \"0\",\n top: \"[50%]\",\n _before: {\n left: \"0\",\n content: '\"\"',\n position: \"absolute\",\n transition: \"colors\",\n transform: \"translateY(-50%)\",\n },\n _after: {\n content: '\"\"',\n position: \"absolute\",\n transform: \"translateY(-50%)\",\n },\n _disabled: {\n cursor: \"not-allowed\",\n },\n },\n text: {\n textStyle: \"label1\",\n cursor: \"pointer\",\n _peerDisabled: {\n cursor: \"not-allowed\",\n },\n },\n },\n variants: {\n size: {\n md: {\n input: {\n _before: {\n width: \"6\",\n height: \"6\",\n },\n _after: {\n width: \"6\",\n height: \"6\",\n },\n },\n text: {\n paddingLeft: \"1\",\n },\n },\n lg: {\n input: {\n _before: {\n width: \"7\",\n height: \"7\",\n },\n _after: {\n width: \"7\",\n height: \"7\",\n },\n },\n text: {\n paddingLeft: \"2\",\n },\n },\n },\n variant: {\n square: {\n input: {\n _before: {\n border: \"base\",\n borderRadius: \"lg\",\n bgColor: \"background\",\n },\n _after: {\n backgroundImage: checkmark.square,\n backgroundRepeat: \"no-repeat\",\n backgroundPosition: \"center\",\n opacity: 0,\n },\n _checked: {\n _before: {\n bg: \"primary\",\n border: \"none\",\n },\n _after: {\n opacity: 1,\n },\n },\n _indeterminate: {\n _before: {\n bg: \"primary\",\n border: \"none\",\n },\n _after: {\n backgroundImage: checkmark.indeterminate_square,\n opacity: 1,\n },\n },\n },\n },\n ghost: {\n input: {\n _before: {\n border: \"none\",\n bgColor: \"transparent\",\n },\n _after: {\n backgroundImage: \"none\",\n maskImage: checkmark.ghost,\n bgColor: \"foreground.muted\",\n opacity: 1,\n },\n _checked: {\n _before: {\n bgColor: \"transparent\",\n },\n _after: {\n bgColor: \"foreground.primary\",\n },\n },\n _indeterminate: {\n _before: {\n bgColor: \"transparent\",\n },\n _after: {\n backgroundImage: \"none\",\n maskImage: checkmark.indeterminate_ghost,\n bgColor: \"foreground.primary\",\n opacity: 1,\n },\n },\n },\n },\n },\n },\n defaultVariants: {\n size: \"md\",\n variant: \"square\",\n },\n})\n\nexport type CheckboxVariants = RecipeVariantProps\n","type":"ui"}]} \ No newline at end of file +{"name":"checkbox","dependencies":["radix-ui/internal","tailwind-variants"],"files":[{"name":"index.tsx","content":"import { cn } from \"@utils/cn\"\nimport { useComposedRefs, useControllableState } from \"radix-ui/internal\"\nimport {\n type ComponentPropsWithoutRef,\n forwardRef,\n useEffect,\n useId,\n useRef,\n} from \"react\"\n\nimport { checkboxRecipe, type CheckboxVariants } from \"./recipe\"\n\ntype CheckboxInputProps = Omit, \"size\"> & {\n indeterminate?: boolean\n onCheckedChange?: (checked: boolean) => void\n}\n\ntype CheckboxLabelProps = {\n label?: string\n}\n\ntype CheckboxProps = CheckboxInputProps & CheckboxVariants & CheckboxLabelProps\n\nexport const Checkbox = forwardRef(\n (props, ref) => {\n const {\n label,\n indeterminate,\n onCheckedChange,\n onChange,\n size,\n variant,\n ...inputProps\n } = props\n\n const styles = checkboxRecipe({ size, variant })\n\n const [isChecked = false, setIsChecked] = useControllableState({\n prop: inputProps.checked,\n defaultProp: inputProps.defaultChecked,\n onChange: onCheckedChange,\n })\n\n const inputRef = useRef(null)\n\n const initialChecked = useRef(isChecked)\n\n const id = useId()\n\n useEffect(() => {\n if (!inputRef.current) {\n return\n }\n inputRef.current.indeterminate = indeterminate ?? false\n }, [indeterminate])\n\n useEffect(() => {\n const form = inputRef.current?.form\n if (form) {\n const reset = () => setIsChecked(initialChecked.current)\n form.addEventListener(\"reset\", reset)\n return () => {\n form.removeEventListener(\"reset\", reset)\n }\n }\n }, [setIsChecked])\n\n const composedRefs = useComposedRefs(ref, inputRef)\n\n return (\n
\n \n
\n )\n },\n)\n\nCheckbox.displayName = \"Checkbox\"\n","type":"ui"},{"name":"recipe.ts","content":"import { tv, type VariantProps } from \"tailwind-variants\"\n\nexport const checkboxRecipe = tv({\n slots: {\n root: \"relative flex\",\n label: `\n relative inline-flex min-h-9 max-w-full items-center pl-8 leading-relaxed\n `,\n input: [\n \"absolute top-1/2 left-0 m-0 size-0 cursor-pointer border-none p-0\",\n `\n before:absolute before:left-0 before:-translate-y-1/2\n before:transition-colors before:content-['']\n `,\n \"after:absolute after:-translate-y-1/2 after:content-['']\",\n \"disabled:cursor-not-allowed\",\n ],\n text: `\n cursor-pointer text-sm/snug font-semibold tracking-wide\n peer-disabled:cursor-not-allowed\n `,\n },\n variants: {\n size: {\n md: {\n input: `\n before:size-6\n after:size-6\n `,\n text: \"pl-1\",\n },\n lg: {\n input: `\n before:size-7\n after:size-7\n `,\n text: \"pl-2\",\n },\n },\n variant: {\n square: {\n input: [\n `\n before:rounded-lg before:border before:border-base-300\n before:bg-base-100\n `,\n `\n after:bg-(image:--checkmark-square) after:bg-center\n after:bg-no-repeat after:opacity-0\n `,\n `\n checked:before:border-none checked:before:bg-primary\n checked:after:opacity-100\n `,\n `indeterminate:before:border-none indeterminate:before:bg-primary`,\n `\n indeterminate:after:bg-(image:--checkmark-indeterminate-square)\n indeterminate:after:opacity-100\n `,\n ],\n },\n ghost: {\n input: [\n \"before:border-none before:bg-transparent\",\n `\n after:bg-base-content/60 after:bg-none\n after:mask-(--checkmark-ghost) after:opacity-100\n `,\n `\n checked:before:bg-transparent\n checked:after:bg-primary\n `,\n `\n indeterminate:before:bg-transparent\n indeterminate:after:bg-primary indeterminate:after:bg-none\n indeterminate:after:mask-(--checkmark-indeterminate-ghost)\n indeterminate:after:opacity-100\n `,\n ],\n },\n },\n },\n defaultVariants: {\n size: \"md\",\n variant: \"square\",\n },\n})\n\nexport type CheckboxVariants = VariantProps\n","type":"ui"}]} \ No newline at end of file diff --git a/app/docs/public/chip.json b/app/docs/public/chip.json index 7cda18c33..aa2a9ffec 100644 --- a/app/docs/public/chip.json +++ b/app/docs/public/chip.json @@ -1 +1 @@ -{"name":"chip","dependencies":["radix-ui"],"files":[{"name":"index.tsx","content":"import { css, cx } from \"@styled-system/css\"\nimport { Slot } from \"radix-ui\"\nimport { type ComponentPropsWithoutRef, forwardRef } from \"react\"\n\nimport { type ChipVariantProps, recipe } from \"./recipe\"\n\nexport type ChipProps = ComponentPropsWithoutRef<\"button\"> &\n ChipVariantProps & {\n asChild?: boolean\n }\n\nexport const Chip = forwardRef(\n ({ asChild, className, children, ...props }, ref) => {\n const Comp = asChild ? Slot.Root : \"button\"\n const [variantProps, componentProps] = recipe.splitVariantProps(props)\n const styles = recipe.raw(variantProps)\n\n return (\n \n {children}\n \n )\n },\n)\n\nChip.displayName = \"Chip\"\n","type":"ui"},{"name":"recipe.ts","content":"import { cva, type RecipeVariantProps } from \"@styled-system/css\"\n\nexport type ChipVariantProps = RecipeVariantProps\nexport const recipe = cva({\n base: {\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n borderRadius: \"full\",\n transition: \"colors\",\n cursor: \"pointer\",\n _focusVisible: {\n ringWidth: \"1\",\n ringColor: \"ring\",\n ringOffset: \"1\",\n },\n \"& svg\": {\n flexShrink: 0,\n },\n _disabled: {\n opacity: 0.5,\n cursor: \"not-allowed\",\n },\n },\n variants: {\n variant: {\n filled: {\n bg: \"secondary\",\n color: \"secondary.foreground\",\n _hover: {\n bg: \"secondary/80\",\n },\n },\n outlined: {\n border: \"base\",\n bg: \"background\",\n color: \"foreground\",\n _hover: {\n bg: \"secondary\",\n },\n },\n },\n size: {\n sm: {\n height: \"7\",\n fontSize: \"sm\",\n \"& svg\": {\n width: \"3\",\n height: \"3\",\n },\n },\n md: {\n height: \"9\",\n fontSize: \"lg\",\n \"& svg\": {\n width: \"4\",\n height: \"4\",\n },\n },\n },\n layout: {\n withText: {},\n iconOnly: {\n px: \"2\",\n \"& svg\": {\n m: \"0\",\n },\n },\n },\n },\n compoundVariants: [\n {\n layout: \"iconOnly\",\n size: \"sm\",\n css: {\n width: \"7\",\n height: \"7\",\n },\n },\n {\n layout: \"iconOnly\",\n size: \"md\",\n css: {\n width: \"9\",\n height: \"9\",\n },\n },\n {\n layout: \"withText\",\n size: \"sm\",\n css: {\n px: \"2\",\n py: \"1\",\n gap: \"1\",\n },\n },\n {\n layout: \"withText\",\n size: \"md\",\n css: {\n px: \"3\",\n py: \"2\",\n gap: \"2\",\n },\n },\n ],\n defaultVariants: {\n variant: \"filled\",\n size: \"md\",\n layout: \"withText\",\n },\n})\n","type":"ui"}]} \ No newline at end of file +{"name":"chip","dependencies":["radix-ui","tailwind-variants"],"files":[{"name":"index.tsx","content":"import { cn } from \"@utils/cn\"\nimport { Slot } from \"radix-ui\"\nimport { type ComponentPropsWithoutRef, forwardRef } from \"react\"\n\nimport { type ChipVariantProps, recipe } from \"./recipe\"\n\nexport type ChipProps = ComponentPropsWithoutRef<\"button\"> &\n ChipVariantProps & {\n asChild?: boolean\n }\n\nexport const Chip = forwardRef(\n ({ asChild, className, children, size, variant, layout, ...props }, ref) => {\n const Comp = asChild ? Slot.Root : \"button\"\n return (\n \n {children}\n \n )\n },\n)\n\nChip.displayName = \"Chip\"\n","type":"ui"},{"name":"recipe.ts","content":"import { tv, type VariantProps } from \"tailwind-variants\"\n\nexport type ChipVariantProps = VariantProps\n\nexport const recipe = tv({\n base: [\n \"inline-flex shrink-0 items-center justify-center\",\n \"cursor-pointer rounded-full whitespace-nowrap transition-colors\",\n \"[&_svg]:shrink-0\",\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n ],\n variants: {\n variant: {\n filled: `\n bg-secondary text-secondary-content\n hover:not-disabled:brightness-90\n `,\n outlined: `\n border border-base-300 bg-base-100 text-base-content\n hover:not-disabled:bg-base-200\n `,\n },\n size: {\n sm: `\n h-7 text-xs/snug font-semibold tracking-wide\n [&_svg]:size-3\n `,\n md: `\n h-9 text-sm/snug font-semibold tracking-wide\n [&_svg]:size-4\n `,\n },\n layout: {\n withText: \"\",\n iconOnly: `\n px-2\n [&_svg]:m-0\n `,\n },\n },\n compoundVariants: [\n { layout: \"iconOnly\", size: \"sm\", class: \"size-7\" },\n { layout: \"iconOnly\", size: \"md\", class: \"size-9\" },\n { layout: \"withText\", size: \"sm\", class: \"gap-1 px-2 py-1\" },\n { layout: \"withText\", size: \"md\", class: \"gap-2 px-3 py-2\" },\n ],\n defaultVariants: {\n variant: \"filled\",\n size: \"md\",\n layout: \"withText\",\n },\n})\n","type":"ui"}]} \ No newline at end of file diff --git a/app/docs/public/dialog.json b/app/docs/public/dialog.json index 4b2a4cbdf..fc04c8f74 100644 --- a/app/docs/public/dialog.json +++ b/app/docs/public/dialog.json @@ -1 +1 @@ -{"name":"dialog","dependencies":["lucide-react","radix-ui"],"files":[{"name":"index.tsx","content":"import { css } from \"@styled-system/css\"\nimport { createStyleContext } from \"@utils/createStyleContext\"\nimport { X } from \"lucide-react\"\nimport { Dialog } from \"radix-ui\"\nimport {\n type ComponentProps,\n type ComponentPropsWithoutRef,\n type ElementRef,\n forwardRef,\n} from \"react\"\n\nimport { recipe } from \"./recipe\"\n\nconst { withRootProvider, withContext } = createStyleContext(recipe)\n\nexport const Root = withRootProvider(Dialog.Root)\n\nexport const Portal = withRootProvider(Dialog.Portal)\nexport const Overlay = withContext<\n ElementRef,\n Dialog.DialogOverlayProps\n>(Dialog.Overlay, \"overlay\")\n\nexport const Close = withContext<\n ElementRef,\n Dialog.DialogCloseProps\n>(Dialog.Close, \"close\")\n\nexport const ContentPrimitive = forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>(({ children, ...props }, ref) => (\n \n \n \n {children}\n \n \n Close\n \n \n \n))\n\nexport const Content = withContext<\n ElementRef,\n ComponentProps\n>(ContentPrimitive, \"content\")\n\nexport const Trigger = withContext<\n ElementRef,\n Dialog.DialogTriggerProps\n>(Dialog.Trigger, \"trigger\")\n\nexport const Header = withContext<\n HTMLDivElement,\n ComponentPropsWithoutRef<\"div\">\n>(\"div\", \"header\")\n\nexport const Footer = withContext<\n HTMLDivElement,\n ComponentPropsWithoutRef<\"div\">\n>(\"div\", \"footer\")\n\nexport const Title = withContext<\n ElementRef,\n Dialog.DialogTitleProps\n>(Dialog.Title, \"title\")\n\nexport const Description = withContext<\n ElementRef,\n Dialog.DialogDescriptionProps\n>(Dialog.Description, \"description\")\n","type":"ui"},{"name":"recipe.ts","content":"import { sva } from \"@styled-system/css\"\n\nexport const recipe = sva({\n slots: [\n \"root\",\n \"trigger\",\n \"portal\",\n \"overlay\",\n \"close\",\n \"content\",\n \"header\",\n \"footer\",\n \"title\",\n \"description\",\n ],\n base: {\n overlay: {\n position: \"fixed\",\n inset: \"0\",\n bg: \"black/90\",\n },\n content: {\n bg: \"background\",\n border: \"input\",\n borderRadius: \"md\",\n position: \"fixed\",\n top: \"[50%]\",\n left: \"[50%]\",\n transform: \"translate(-50%,-50%)\",\n width: \"[100%]\",\n maxWidth: \"[32rem]\",\n maxHeight: \"[85vh]\",\n padding: \"4\",\n \"&[data-state=open]\": {\n animationName: \"contentShow\",\n animationDuration: \"slow\",\n },\n\n \"&[data-state=closed]\": {\n animationName: \"fadeOut\",\n animationDuration: \"slow\",\n },\n },\n close: {\n position: \"absolute\",\n right: \"4\",\n top: \"4\",\n rounded: \"sm\",\n opacity: \"0.7\",\n transition: \"opacity\",\n cursor: \"pointer\",\n\n _hover: {\n opacity: \"1\",\n },\n\n _focus: {\n ringWidth: \"1\",\n ringColor: \"ring\",\n ringOffset: \"1\",\n },\n\n _disabled: {\n pointerEvents: \"none\",\n },\n\n \"&[data-state=open]\": {\n bg: \"accent\",\n color: \"muted.foreground\",\n },\n },\n header: {\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"1.5\",\n },\n footer: {\n display: \"flex\",\n flexDirection: \"column-reverse\",\n },\n title: {\n fontSize: \"lg\",\n fontWeight: \"semibold\",\n lineHeight: \"none\",\n letterSpacing: \"tight\",\n },\n description: {\n fontSize: \"sm\",\n color: \"foreground\",\n },\n },\n})\n","type":"ui"}]} \ No newline at end of file +{"name":"dialog","dependencies":["lucide-react","radix-ui","tailwind-variants"],"files":[{"name":"index.tsx","content":"import { createStyleContext } from \"@utils/createStyleContext\"\nimport { X } from \"lucide-react\"\nimport { Dialog } from \"radix-ui\"\nimport { forwardRef } from \"react\"\n\nimport { recipe } from \"./recipe\"\n\nconst { withContext, withRootProvider } = createStyleContext(recipe)\n\nexport const Root = withRootProvider(Dialog.Root)\n\nexport const Portal = withContext(Dialog.Portal, \"portal\")\n\nexport const Overlay = withContext(Dialog.Overlay, \"overlay\")\n\nexport const Header = withContext(\"div\", \"header\")\n\nconst CloseIcon = withContext(Dialog.Close, \"close\")\n\nexport const Close = Dialog.Close //pure logic for close dialog\n\nexport const ContentPrimitive = forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef & {\n closeIcon?: boolean\n }\n>(({ children, closeIcon = true, ...props }, ref) => (\n \n \n \n {children}\n {closeIcon && (\n \n \n Close\n \n )}\n \n \n))\n\nexport const Content = withContext(ContentPrimitive, \"content\")\n\nexport const Trigger = withContext(Dialog.Trigger, \"trigger\")\n\nexport const Footer = withContext(\"div\", \"footer\")\n\nexport const Title = withContext(Dialog.Title, \"title\")\n\nexport const Description = withContext(Dialog.Description, \"description\")\n","type":"ui"},{"name":"recipe.ts","content":"import { tv } from \"tailwind-variants\"\n\nexport const recipe = tv({\n slots: {\n root: \"\",\n trigger: \"\",\n portal: \"\",\n overlay: \"fixed inset-0 bg-black/50\",\n close: [\n \"absolute top-4 right-4 cursor-pointer rounded-sm opacity-70\",\n `\n transition-opacity\n hover:opacity-100\n `,\n \"disabled:pointer-events-none\",\n ],\n content: [\n \"fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2\",\n \"z-1000 max-h-[85vh] w-full max-w-lg\",\n \"border border-base-200 bg-base-200 text-base-content\",\n \"overflow-auto rounded-md p-4\",\n ],\n header: `\n flex flex-col gap-1.5 text-base/normal font-semibold text-base-content\n `,\n footer: \"flex flex-col-reverse text-base-content/60\",\n title: \"text-lg/normal font-semibold\",\n description: \"text-base/relaxed text-base-content\",\n },\n})\n","type":"ui"}]} \ No newline at end of file diff --git a/app/docs/public/preset.json b/app/docs/public/preset.json index f03b9acfa..c4e6293c9 100644 --- a/app/docs/public/preset.json +++ b/app/docs/public/preset.json @@ -1 +1,5 @@ -{"name":"preset.ts","dependencies":[],"file":"\"import {\\n defineGlobalStyles,\\n definePreset,\\n defineSemanticTokens,\\n defineTokens,\\n} from \\\"@pandacss/dev\\\"\\n\\nconst globalCss = defineGlobalStyles({\\n \\\"html, body\\\": {\\n w: \\\"full\\\",\\n h: \\\"full\\\",\\n },\\n})\\n\\nconst radii = defineTokens.radii({\\n radius: { value: \\\"0.5rem\\\" },\\n})\\n\\nexport const semanticColors = defineSemanticTokens.colors({\\n background: {\\n value: { base: \\\"hsl(0 0% 100%)\\\", _dark: \\\"hsl(222.2 84% 4.9%)\\\" },\\n },\\n foreground: {\\n value: { base: \\\"hsl(222.2 84% 4.9%)\\\", _dark: \\\"hsl(210 40% 98%)\\\" },\\n },\\n card: {\\n DEFAULT: {\\n value: { base: \\\"hsl(0 0% 100%)\\\", _dark: \\\"hsl(222.2 84% 4.9%)\\\" },\\n },\\n foreground: {\\n value: { base: \\\"hsl(222.2 84% 4.9%)\\\", _dark: \\\"hsl(210 40% 98%)\\\" },\\n },\\n },\\n popover: {\\n DEFAULT: {\\n value: { base: \\\"hsl(0 0% 100%)\\\", _dark: \\\"hsl(222.2 84% 4.9%)\\\" },\\n },\\n foreground: {\\n value: { base: \\\"hsl(222.2 84% 4.9%)\\\", _dark: \\\"hsl(210 40% 98%)\\\" },\\n },\\n },\\n primary: {\\n DEFAULT: {\\n value: { base: \\\"hsl(222.2 47.4% 11.2%)\\\", _dark: \\\"hsl(210 40% 98%)\\\" },\\n },\\n foreground: {\\n value: { base: \\\"hsl(210 40% 98%)\\\", _dark: \\\"hsl(222.2 47.4% 11.2%)\\\" },\\n },\\n },\\n secondary: {\\n DEFAULT: {\\n value: { base: \\\"hsl(210 40% 96.1%)\\\", _dark: \\\"hsl(217.2 32.6% 17.5%)\\\" },\\n },\\n foreground: {\\n value: { base: \\\"hsl(222.2 47.4% 11.2%)\\\", _dark: \\\"hsl(210 40% 98%)\\\" },\\n },\\n },\\n muted: {\\n DEFAULT: {\\n value: { base: \\\"hsl(210 40% 96.1%)\\\", _dark: \\\"hsl(217.2 32.6% 17.5%)\\\" },\\n },\\n foreground: {\\n value: {\\n base: \\\"hsl(215.4 16.3% 46.9%)\\\",\\n _dark: \\\"hsl(215 20.2% 65.1%)\\\",\\n },\\n },\\n },\\n accent: {\\n DEFAULT: {\\n value: { base: \\\"hsl(210 40% 96.1%)\\\", _dark: \\\"hsl(217.2 32.6% 17.5%)\\\" },\\n },\\n foreground: {\\n value: { base: \\\"hsl(222.2 47.4% 11.2%)\\\", _dark: \\\"hsl(210 40% 98%)\\\" },\\n },\\n },\\n destructive: {\\n DEFAULT: {\\n value: { base: \\\"hsl(0 84.2% 60.2%)\\\", _dark: \\\"hsl(0 62.8% 30.6%)\\\" },\\n },\\n foreground: {\\n value: { base: \\\"hsl(210 40% 98%)\\\", _dark: \\\"hsl(210 40% 98%)\\\" },\\n },\\n },\\n border: {\\n value: {\\n base: \\\"hsl(214.3 31.8% 91.4%)\\\",\\n _dark: \\\"hsl(217.2 32.6% 17.5%)\\\",\\n },\\n },\\n input: {\\n value: {\\n base: \\\"hsl(214.3 31.8% 91.4%)\\\",\\n _dark: \\\"hsl(217.2 32.6% 17.5%)\\\",\\n },\\n },\\n ring: {\\n value: { base: \\\"hsl(222.2 84% 4.9%)\\\", _dark: \\\"hsl(212.7 26.8% 83.9%)\\\" },\\n },\\n})\\n\\nconst borders = defineSemanticTokens.borders({\\n base: { value: \\\"1px solid {colors.border}\\\" },\\n input: { value: \\\"1px solid {colors.input}\\\" },\\n primary: { value: \\\"1px solid {colors.primary}\\\" },\\n destructive: { value: \\\"1px solid {colors.destructive}\\\" },\\n})\\n\\nexport const radius = defineSemanticTokens.radii({\\n xl: { value: `calc({radii.radius} + 4px)` },\\n lg: { value: `{radii.radius}` },\\n md: { value: `calc({radii.radius} - 2px)` },\\n sm: { value: \\\"calc({radii.radius} - 4px)\\\" },\\n})\\n\\nexport const defaultPreset = definePreset({\\n name: \\\"default\\\",\\n globalCss,\\n theme: {\\n extend: {\\n tokens: {\\n radii,\\n },\\n semanticTokens: {\\n colors: semanticColors,\\n radii: radius,\\n borders: borders,\\n },\\n },\\n },\\n staticCss: {\\n recipes: \\\"*\\\",\\n },\\n})\\n\""} \ No newline at end of file +{ + "name": "preset.ts", + "dependencies": [], + "file": "\"import {\\n defineGlobalStyles,\\n definePreset,\\n defineSemanticTokens,\\n defineTokens,\\n} from \\\"@pandacss/dev\\\"\\n\\nconst globalCss = defineGlobalStyles({\\n \\\"html, body\\\": {\\n w: \\\"full\\\",\\n h: \\\"full\\\",\\n },\\n})\\n\\nconst radii = defineTokens.radii({\\n radius: { value: \\\"0.5rem\\\" },\\n})\\n\\nexport const semanticColors = defineSemanticTokens.colors({\\n background: {\\n value: { base: \\\"hsl(0 0% 100%)\\\", _dark: \\\"hsl(222.2 84% 4.9%)\\\" },\\n },\\n foreground: {\\n value: { base: \\\"hsl(222.2 84% 4.9%)\\\", _dark: \\\"hsl(210 40% 98%)\\\" },\\n },\\n card: {\\n DEFAULT: {\\n value: { base: \\\"hsl(0 0% 100%)\\\", _dark: \\\"hsl(222.2 84% 4.9%)\\\" },\\n },\\n foreground: {\\n value: { base: \\\"hsl(222.2 84% 4.9%)\\\", _dark: \\\"hsl(210 40% 98%)\\\" },\\n },\\n },\\n popover: {\\n DEFAULT: {\\n value: { base: \\\"hsl(0 0% 100%)\\\", _dark: \\\"hsl(222.2 84% 4.9%)\\\" },\\n },\\n foreground: {\\n value: { base: \\\"hsl(222.2 84% 4.9%)\\\", _dark: \\\"hsl(210 40% 98%)\\\" },\\n },\\n },\\n primary: {\\n DEFAULT: {\\n value: { base: \\\"hsl(222.2 47.4% 11.2%)\\\", _dark: \\\"hsl(210 40% 98%)\\\" },\\n },\\n foreground: {\\n value: { base: \\\"hsl(210 40% 98%)\\\", _dark: \\\"hsl(222.2 47.4% 11.2%)\\\" },\\n },\\n },\\n secondary: {\\n DEFAULT: {\\n value: { base: \\\"hsl(210 40% 96.1%)\\\", _dark: \\\"hsl(217.2 32.6% 17.5%)\\\" },\\n },\\n foreground: {\\n value: { base: \\\"hsl(222.2 47.4% 11.2%)\\\", _dark: \\\"hsl(210 40% 98%)\\\" },\\n },\\n },\\n muted: {\\n DEFAULT: {\\n value: { base: \\\"hsl(210 40% 96.1%)\\\", _dark: \\\"hsl(217.2 32.6% 17.5%)\\\" },\\n },\\n foreground: {\\n value: {\\n base: \\\"hsl(215.4 16.3% 46.9%)\\\",\\n _dark: \\\"hsl(215 20.2% 65.1%)\\\",\\n },\\n },\\n },\\n accent: {\\n DEFAULT: {\\n value: { base: \\\"hsl(210 40% 96.1%)\\\", _dark: \\\"hsl(217.2 32.6% 17.5%)\\\" },\\n },\\n foreground: {\\n value: { base: \\\"hsl(222.2 47.4% 11.2%)\\\", _dark: \\\"hsl(210 40% 98%)\\\" },\\n },\\n },\\n destructive: {\\n DEFAULT: {\\n value: { base: \\\"hsl(0 84.2% 60.2%)\\\", _dark: \\\"hsl(0 62.8% 30.6%)\\\" },\\n },\\n foreground: {\\n value: { base: \\\"hsl(210 40% 98%)\\\", _dark: \\\"hsl(210 40% 98%)\\\" },\\n },\\n },\\n border: {\\n value: {\\n base: \\\"hsl(214.3 31.8% 91.4%)\\\",\\n _dark: \\\"hsl(217.2 32.6% 17.5%)\\\",\\n },\\n },\\n input: {\\n value: {\\n base: \\\"hsl(214.3 31.8% 91.4%)\\\",\\n _dark: \\\"hsl(217.2 32.6% 17.5%)\\\",\\n },\\n },\\n ring: {\\n value: { base: \\\"hsl(222.2 84% 4.9%)\\\", _dark: \\\"hsl(212.7 26.8% 83.9%)\\\" },\\n },\\n})\\n\\nconst borders = defineSemanticTokens.borders({\\n base: { value: \\\"1px solid {colors.border}\\\" },\\n input: { value: \\\"1px solid {colors.input}\\\" },\\n primary: { value: \\\"1px solid {colors.primary}\\\" },\\n destructive: { value: \\\"1px solid {colors.destructive}\\\" },\\n})\\n\\nexport const radius = defineSemanticTokens.radii({\\n xl: { value: `calc({radii.radius} + 4px)` },\\n lg: { value: `{radii.radius}` },\\n md: { value: `calc({radii.radius} - 2px)` },\\n sm: { value: \\\"calc({radii.radius} - 4px)\\\" },\\n})\\n\\nexport const defaultPreset = definePreset({\\n name: \\\"default\\\",\\n globalCss,\\n theme: {\\n extend: {\\n tokens: {\\n radii,\\n },\\n semanticTokens: {\\n colors: semanticColors,\\n radii: radius,\\n borders: borders,\\n },\\n },\\n },\\n staticCss: {\\n recipes: \\\"*\\\",\\n },\\n})\\n\"" +} diff --git a/app/docs/public/select.json b/app/docs/public/select.json index 9bf77f4ed..82d6977c5 100644 --- a/app/docs/public/select.json +++ b/app/docs/public/select.json @@ -1 +1 @@ -{"name":"Select","dependencies":["@radix-ui/react-select"],"files":[{"name":"index.tsx","content":"import * as React from \"react\"\nimport * as SelectPrimitive from \"@radix-ui/react-select\"\nimport { createStyleContext } from \"@utils/createStyleContext\"\nimport { type HTMLStyledProps } from \"@styled-system/jsx\"\nimport { select } from \"@styled-system/recipes\"\nimport type { Assign, ComponentProps } from \"@styled-system/types\"\n\nconst { withProvider, withContext } = createStyleContext(select)\n\nexport const SelectTrigger = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>(({ children, ...props }, ref) => (\n \n {children}\n \n \n))\nSelectTrigger.displayName = SelectPrimitive.Trigger.displayName\n\nexport const SelectContent = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>(({ children, position = \"popper\", ...props }, ref) => (\n \n \n {children}\n \n \n))\nSelectContent.displayName = SelectPrimitive.Content.displayName\n\nexport const Indicator = withContext(\n SelectPrimitive.ItemIndicator,\n \"itemIndicator\",\n)\n\nexport const SelectItem = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>(({ children, ...props }, ref) => (\n \n \n {children}\n \n))\nSelectItem.displayName = SelectPrimitive.Item.displayName\n\nexport type RootProps = React.ComponentProps\nexport const Root = withProvider<\n HTMLDivElement,\n Assign, RootProps>\n>(SelectPrimitive.Root, \"root\")\n\nexport const Group = withContext<\n HTMLDivElement,\n Assign, ComponentProps>\n>(SelectPrimitive.Group, \"group\")\n\nexport const Value = withContext<\n HTMLSpanElement,\n Assign, ComponentProps>\n>(SelectPrimitive.Value, \"value\")\n\nexport const Trigger = withContext<\n HTMLButtonElement,\n Assign, ComponentProps>\n>(SelectTrigger, \"trigger\")\n\nexport const Content = withContext<\n HTMLDivElement,\n Assign, ComponentProps>\n>(SelectContent, \"content\")\n\nexport const Viewport = withContext<\n HTMLDivElement,\n Assign<\n HTMLStyledProps<\"div\">,\n ComponentProps\n >\n>(SelectPrimitive.Viewport, \"viewport\")\n\nexport const Label = withContext<\n HTMLDivElement,\n Assign, ComponentProps>\n>(SelectPrimitive.Label, \"label\")\n\nexport const Item = withContext<\n HTMLDivElement,\n Assign, ComponentProps>\n>(SelectItem, \"item\")\n\nexport const Separator = withContext<\n HTMLDivElement,\n Assign<\n HTMLStyledProps<\"div\">,\n ComponentProps\n >\n>(SelectPrimitive.Separator, \"separator\")\n"},{"name":"recipe.ts","content":"import { defineSafe } from \"@utils/defineSafe\"\n\nexport const selectRecipe = defineSafe.slotRecipe({\n className: \"select\",\n description: \"Styles for the Select component\",\n slots: [\n \"root\",\n \"group\",\n \"value\",\n \"trigger\",\n \"viewport\",\n \"content\",\n \"label\",\n \"item\",\n \"itemIndicator\",\n \"separator\",\n ],\n base: {\n trigger: {\n display: \"flex\",\n h: \"10\",\n w: \"full\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n rounded: \"md\",\n border: \"input\",\n bg: \"transparent\",\n px: \"3\",\n py: \"2\",\n textStyle: \"sm\",\n cursor: \"pointer\",\n ringColor: \"background\",\n\n _placeholder: {\n color: \"muted.foreground\",\n },\n\n _focus: {\n ringWidth: \"1\",\n ringColor: \"ring\",\n outlineOffset: \"2\",\n },\n\n _disabled: {\n cursor: \"not-allowed\",\n opacity: \"0.5\",\n },\n },\n viewport: {\n \"&[data-position=popper]\": {\n h: \"var(--radix-select-trigger-height)\",\n w: \"full\",\n minW: \"var(--radix-select-trigger-width)\",\n },\n },\n content: {\n position: \"relative\",\n zIndex: 50,\n minW: \"full\",\n overflow: \"hidden\",\n rounded: \"md\",\n border: \"base\",\n bg: \"popover\",\n color: \"popover.foreground\",\n shadow: \"md\",\n\n // \"&[data-state=open]\": {\n // animateIn: true,\n // fadeIn: 0,\n // zoomIn: 95,\n // },\n\n // \"&[data-state=closed]\": {\n // animateOut: true,\n // fadeOut: 0,\n // zoomOut: 95,\n // },\n\n // \"&[data-side=top]\": {\n // slideInFromBottom: \"2\",\n // },\n\n // \"&[data-side=bottom]\": {\n // slideInFromTop: \"2\",\n // },\n\n // \"&[data-side=left]\": {\n // slideInFromRight: \"2\",\n // },\n\n // \"&[data-side=right]\": {\n // slideInFromLeft: \"2\",\n // },\n\n \"&[data-position=popper]\": {\n \"&[data-side=top]\": {\n translateY: \"-1\",\n },\n\n \"&[data-side=bottom]\": {\n translateY: \"1\",\n },\n\n \"&[data-side=left]\": {\n translateX: \"-1\",\n },\n\n \"&[data-side=right]\": {\n translateX: \"1\",\n },\n },\n },\n label: {\n py: \"1.5\",\n pl: \"8\",\n pr: \"2\",\n textStyle: \"sm\",\n fontWeight: \"semibold\",\n },\n item: {\n position: \"relative\",\n display: \"flex\",\n cursor: \"default\",\n userSelect: \"none\",\n alignItems: \"center\",\n rounded: \"sm\",\n py: \"1.5\",\n pl: \"8\",\n pr: \"2\",\n textStyle: \"sm\",\n\n _focus: {\n bg: \"accent\",\n color: \"accent.foreground\",\n },\n\n [\"&[data-disabled]\"]: {\n pointerEvents: \"none\",\n opacity: \"0.5\",\n },\n },\n itemIndicator: {\n position: \"absolute\",\n left: \"2\",\n display: \"flex\",\n h: \"3.5\",\n w: \"3.5\",\n alignItems: \"center\",\n justifyContent: \"center\",\n },\n separator: {\n mx: \"-1\",\n my: \"1\",\n h: \"1\",\n bg: \"muted\",\n },\n },\n})\n"}]} \ No newline at end of file +{"name":"select","dependencies":["radix-ui","tailwind-variants"],"files":[{"name":"index.tsx","content":"import { createStyleContext } from \"@utils/createStyleContext\"\nimport { Select as SelectPrimitive } from \"radix-ui\"\nimport {\n type ComponentPropsWithoutRef,\n type ElementRef,\n forwardRef,\n} from \"react\"\n\nimport { recipe } from \"./recipe\"\n\nconst { withContext, withRootProvider } = createStyleContext(recipe)\n\nconst BaseTrigger = forwardRef<\n ElementRef,\n ComponentPropsWithoutRef\n>(({ children, ...props }, ref) => (\n \n {children}\n \n \n))\n\nBaseTrigger.displayName = SelectPrimitive.Trigger.displayName\n\nexport const Root = withRootProvider(SelectPrimitive.Root)\nexport const Group = SelectPrimitive.Group\nexport const Value = SelectPrimitive.Value\n\nexport const Trigger = withContext(BaseTrigger, \"trigger\")\n\nexport const ContentPrimitive = forwardRef<\n ElementRef,\n ComponentPropsWithoutRef\n>(({ position = \"popper\", children, ...props }, ref) => {\n return (\n \n \n {children}\n \n \n )\n})\n\nexport const Content = withContext(ContentPrimitive, \"content\")\n\nexport const Viewport = withContext(SelectPrimitive.Viewport, \"viewport\")\n\nexport const ItemPrimitive = forwardRef<\n ElementRef,\n ComponentPropsWithoutRef\n>(({ children, ...props }, ref) => {\n return (\n \n \n {children}\n \n )\n})\n\nexport const Item = withContext(ItemPrimitive, \"item\")\n\nexport const Label = withContext(SelectPrimitive.Label, \"label\")\n\nexport const Separator = withContext(SelectPrimitive.Separator, \"separator\")\n\nexport const Indicator = withContext(\n SelectPrimitive.ItemIndicator,\n \"itemIndicator\",\n)\n","type":"ui"},{"name":"recipe.ts","content":"import { tv } from \"tailwind-variants\"\n\nexport const recipe = tv({\n slots: {\n root: \"\",\n group: \"\",\n value: \"\",\n trigger: [\n \"flex h-10 w-full items-center justify-between px-2\",\n \"cursor-pointer rounded-md border border-base-200\",\n \"text-sm/snug font-semibold tracking-wide\",\n `\n bg-base-100\n placeholder:text-base-content\n `,\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n ],\n viewport: `\n data-[position=popper]:h-(--radix-select-trigger-height)\n data-[position=popper]:w-full\n data-[position=popper]:min-w-(--radix-select-trigger-width)\n `,\n content: [\n \"relative z-100 min-w-full overflow-hidden\",\n \"rounded-md border border-base-300 shadow-md\",\n \"max-h-96 bg-base-100 py-1 text-base-content\",\n \"data-[position=popper]:data-[side=top]:-translate-y-1\",\n \"data-[position=popper]:data-[side=bottom]:translate-y-1\",\n \"data-[position=popper]:data-[side=left]:-translate-x-1\",\n \"data-[position=popper]:data-[side=right]:translate-x-1\",\n ],\n label: \"py-1.5 pr-2 pl-8 text-sm/snug font-semibold tracking-wide\",\n item: [\n \"relative flex items-center rounded-sm py-1.5 pr-2 pl-8 text-sm\",\n \"hover:bg-base-300\",\n \"data-disabled:pointer-events-none data-disabled:opacity-50\",\n ],\n itemIndicator: \"absolute left-2 flex size-3.5 items-center justify-center\",\n separator: \"-mx-1 my-1 h-px bg-base-300\",\n },\n})\n","type":"ui"}]} \ No newline at end of file diff --git a/app/docs/public/slider.json b/app/docs/public/slider.json index aae28791c..a1a4e81ce 100644 --- a/app/docs/public/slider.json +++ b/app/docs/public/slider.json @@ -1 +1 @@ -{"name":"Slider","dependencies":["@radix-ui/react-slider"],"files":[{"name":"index.tsx","content":"import { slider } from \"@styled-system/recipes\"\nimport type {\n ComponentProps,\n HTMLStyledProps,\n Assign,\n} from \"@styled-system/types\"\nimport { createStyleContext } from \"@utils/createStyleContext\"\nimport * as BaseSlider from \"@radix-ui/react-slider\"\n\nconst { withProvider, withContext } = createStyleContext(slider)\n\nexport const Root = withProvider<\n HTMLDivElement,\n Assign, ComponentProps>\n>(BaseSlider.Root, \"root\")\n\nexport const Track = withContext<\n HTMLSpanElement,\n Assign, ComponentProps>\n>(BaseSlider.Track, \"track\")\n\nexport const Range = withContext<\n HTMLSpanElement,\n Assign, ComponentProps>\n>(BaseSlider.Range, \"range\")\n\nexport const Thumb = withContext<\n HTMLSpanElement,\n Assign, ComponentProps>\n>(BaseSlider.Thumb, \"thumb\")\n"},{"name":"recipe.ts","content":"import { defineSafe } from \"@utils/defineSafe\"\n\nexport const sliderRecipe = defineSafe.slotRecipe({\n className: \"slider\",\n description: \"Styles for the Slider component\",\n slots: [\"root\", \"track\", \"range\", \"thumb\"],\n base: {\n root: {\n position: \"relative\",\n display: \"flex\",\n w: \"full\",\n touchAction: \"none\",\n userSelect: \"none\",\n alignItems: \"center\",\n },\n track: {\n position: \"relative\",\n h: \"2\",\n w: \"full\",\n flexGrow: \"1\",\n overflow: \"hidden\",\n rounded: \"full\",\n bg: \"secondary\",\n },\n range: {\n position: \"absolute\",\n h: \"full\",\n bg: \"primary\",\n },\n thumb: {\n display: \"block\",\n h: \"5\",\n w: \"5\",\n cursor: \"pointer\",\n rounded: \"full\",\n borderWidth: \"2px\",\n borderStyle: \"solid\",\n borderColor: \"primary\",\n bg: \"background\",\n ringColor: \"background\",\n transition: \"colors\",\n\n _focusVisible: {\n outline: \"primary\",\n outlineOffset: \"2\",\n ringWidth: \"2\",\n ringColor: \"ring\",\n ringOffset: \"2\",\n },\n\n _disabled: {\n pointerEvents: \"none\",\n opacity: \"0.5\",\n },\n },\n },\n})\n"}]} \ No newline at end of file +{"name":"slider","dependencies":["radix-ui","tailwind-variants"],"files":[{"name":"index.tsx","content":"import { createStyleContext } from \"@utils/createStyleContext\"\nimport { Slider as SliderPrimitive } from \"radix-ui\"\n\nimport { recipe } from \"./recipe\"\n\nconst { withContext, withProvider } = createStyleContext(recipe)\n\nexport const Root = withProvider(SliderPrimitive.Root, \"root\")\n\nexport const Track = withContext(SliderPrimitive.Track, \"track\")\n\nexport const Range = withContext(SliderPrimitive.Range, \"range\")\n\nexport const Thumb = withContext(SliderPrimitive.Thumb, \"thumb\")\n","type":"ui"},{"name":"recipe.ts","content":"import { tv } from \"tailwind-variants\"\n\nexport const recipe = tv({\n slots: {\n root: \"relative flex touch-none items-center select-none\",\n track: \"relative h-2 w-full grow overflow-hidden rounded-full bg-neutral\",\n range: \"absolute h-full bg-primary\",\n thumb: [\n `\n block size-5 cursor-pointer rounded-full border-2 border-primary\n bg-neutral\n `,\n \"disabled:pointer-events-none disabled:opacity-50\",\n ],\n },\n})\n","type":"ui"}]} \ No newline at end of file diff --git a/app/docs/public/tabs.json b/app/docs/public/tabs.json index b66146b56..2b217f52d 100644 --- a/app/docs/public/tabs.json +++ b/app/docs/public/tabs.json @@ -1 +1 @@ -{"name":"Tabs","dependencies":["@radix-ui/react-tabs"],"files":[{"name":"index.tsx","content":"import * as TabsPrimitive from \"@radix-ui/react-tabs\"\nimport { createStyleContext } from \"@utils/createStyleContext\"\nimport { tabs } from \"@styled-system/recipes\"\nimport type {\n Assign,\n HTMLStyledProps,\n ComponentProps,\n} from \"@styled-system/types\"\n\nconst { withProvider, withContext } = createStyleContext(tabs)\n\nexport const Root = withProvider<\n HTMLDivElement,\n Assign, ComponentProps>\n>(TabsPrimitive.Root, \"root\")\nexport const List = withContext<\n HTMLDivElement,\n Assign, ComponentProps>\n>(TabsPrimitive.List, \"list\")\nexport const Trigger = withContext<\n HTMLButtonElement,\n Assign<\n HTMLStyledProps<\"button\">,\n ComponentProps\n >\n>(TabsPrimitive.Trigger, \"trigger\")\nexport const Content = withContext<\n HTMLDivElement,\n Assign, ComponentProps>\n>(TabsPrimitive.Content, \"content\")\n"},{"name":"recipe.ts","content":"import { defineSafe } from \"@utils/defineSafe\"\n\nexport const tabsRecipe = defineSafe.slotRecipe({\n className: \"tabs\",\n description: \"Styles for the Tabs component\",\n slots: [\"root\", \"list\", \"trigger\", \"content\"],\n base: {\n list: {\n display: \"inline-flex\",\n h: \"10\",\n alignItems: \"center\",\n justifyContent: \"center\",\n rounded: \"md\",\n bg: \"muted\",\n p: \"1\",\n color: \"muted.foreground\",\n },\n trigger: {\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n whiteSpace: \"nowrap\",\n rounded: \"sm\",\n px: \"3\",\n py: \"1.5\",\n textStyle: \"sm\",\n fontWeight: \"medium\",\n transition: \"all\",\n cursor: \"pointer\",\n ringColor: \"background\",\n\n _focusVisible: {\n outlineWidth: \"2px\",\n outlineOffset: \"2\",\n ringWidth: \"2\",\n ringColor: \"ring\",\n offset: \"2\",\n },\n\n _disabled: {\n pointerEvents: \"none\",\n opacity: \"50%\",\n },\n\n \"&[data-state=active]\": {\n bg: \"background\",\n color: \"foreground\",\n shadow: \"sm\",\n },\n },\n content: {\n mt: \"2\",\n ringColor: \"background\",\n\n _focusVisible: {\n outlineWidth: \"2\",\n outlineOffset: \"2\",\n ringWidth: \"2\",\n ringColor: \"ring\",\n },\n },\n },\n})\n"}]} \ No newline at end of file +{"name":"tabs","dependencies":["radix-ui","tailwind-variants"],"files":[{"name":"index.tsx","content":"import { createStyleContext } from \"@utils/createStyleContext\"\nimport { Tabs as TabsPrimitive } from \"radix-ui\"\n\nimport { recipe } from \"./recipe\"\n\nconst { withContext, withProvider } = createStyleContext(recipe)\n\nexport const Root = withProvider(TabsPrimitive.Root, \"root\")\n\nexport const List = withContext(TabsPrimitive.List, \"list\")\n\nexport const Trigger = withContext(TabsPrimitive.Trigger, \"trigger\")\n\nexport const Content = withContext(TabsPrimitive.Content, \"content\")\n","type":"ui"},{"name":"recipe.ts","content":"import { tv } from \"tailwind-variants\"\n\nexport const recipe = tv({\n slots: {\n root: \"\",\n list: `\n inline-flex h-10 items-center justify-center rounded-md bg-base-200 p-1\n text-base-content\n `,\n trigger: [\n \"inline-flex items-center justify-center whitespace-nowrap\",\n \"cursor-pointer rounded-sm px-3 py-1.5 text-sm font-medium transition-all\",\n \"disabled:pointer-events-none disabled:opacity-50\",\n `\n data-[state=active]:bg-base-100 data-[state=active]:text-base-content\n data-[state=active]:shadow-sm\n `,\n ],\n content: \"mt-2\",\n },\n})\n","type":"ui"}]} \ No newline at end of file diff --git a/app/docs/public/textfield.json b/app/docs/public/textfield.json index 8daf7dc3a..78bbaced9 100644 --- a/app/docs/public/textfield.json +++ b/app/docs/public/textfield.json @@ -1 +1 @@ -{"name":"textfield","dependencies":[],"files":[{"name":"index.tsx","content":"import { css, cx } from \"@styled-system/css\"\nimport type { ComponentPropsWithoutRef, ReactNode } from \"react\"\nimport { forwardRef } from \"react\"\n\nimport { recipe, TextFieldVariantProps } from \"./recipe\"\n\nconst getHelperText = (id: string) => `${id}-helper-text`\n\ntype TextFieldProps = Omit<\n ComponentPropsWithoutRef<\"input\">,\n \"size\" | \"width\"\n> & {\n leadingAddon?: ReactNode\n trailingAddon?: ReactNode\n label?: string\n helperText?: string\n required?: boolean\n id: string\n} & TextFieldVariantProps\n\nexport const TextField = forwardRef(\n (props, ref) => {\n const [variantProps, componentProps] = recipe.splitVariantProps(props)\n\n const {\n className,\n leadingAddon,\n trailingAddon,\n label,\n helperText,\n id,\n required = false,\n ...rest\n } = componentProps\n\n const styles = recipe.raw(variantProps)\n return (\n
\n {label && (\n
\n \n
\n )}\n\n
\n {leadingAddon &&
{leadingAddon}
}\n\n \n\n {trailingAddon && (\n
{trailingAddon}
\n )}\n
\n\n {helperText && (\n \n {helperText}\n
\n )}\n \n )\n },\n)\n\nTextField.displayName = \"TextField\"\n","type":"ui"},{"name":"recipe.ts","content":"import { RecipeVariantProps, sva } from \"@styled-system/css\"\n\nexport const recipe = sva({\n slots: [\"root\", \"heading\", \"container\", \"input\", \"trailingButton\", \"helper\"],\n base: {\n root: {\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"1.5\",\n width: \"full\",\n },\n heading: {\n textStyle: \"label1\",\n \"& label.required\": {\n \"&::after\": {\n color: \"destructive\",\n content: \"'*'\",\n fontSize: \"sm\",\n marginLeft: \"0.5\",\n },\n },\n },\n container: {\n alignItems: \"center\",\n border: \"input\",\n borderRadius: \"md\",\n shadow: \"sm\",\n display: \"flex\",\n gap: \"1.5\",\n minHeight: \"12\",\n padding: \"3\",\n width: \"full\",\n transition: \"colors\",\n transitionTimingFunction: \"in\",\n transitionDuration: \"fast\",\n _disabled: {\n cursor: \"not-allowed\",\n },\n _focusWithin: {\n borderColor: \"primary\",\n },\n },\n input: {\n border: \"none\",\n outline: \"none\",\n flex: \"1\",\n _disabled: {\n cursor: \"not-allowed\",\n },\n },\n trailingButton: {\n display: \"flex\",\n alignItems: \"center\",\n whiteSpace: \"nowrap\",\n maxHeight: \"2\",\n },\n helper: {\n alignSelf: \"stretch\",\n color: \"neutral.foreground\",\n textStyle: \"caption2\",\n },\n },\n variants: {\n status: {\n normal: {},\n negative: {\n container: {\n borderColor: \"destructive\",\n _focusWithin: {\n borderColor: \"destructive\",\n },\n },\n helper: {\n color: \"destructive\",\n },\n },\n },\n },\n defaultVariants: {\n status: \"normal\",\n },\n})\n\nexport type TextFieldVariantProps = RecipeVariantProps\n","type":"ui"}]} \ No newline at end of file +{"name":"textfield","dependencies":["tailwind-variants"],"files":[{"name":"index.tsx","content":"import { cn } from \"@utils/cn\"\nimport type { ComponentPropsWithoutRef, ReactNode } from \"react\"\nimport { forwardRef } from \"react\"\n\nimport { recipe, type TextFieldVariantProps } from \"./recipe\"\n\nconst getHelperText = (id: string) => `${id}-helper-text`\n\ntype TextFieldProps = Omit<\n ComponentPropsWithoutRef<\"input\">,\n \"size\" | \"width\"\n> & {\n leadingAddon?: ReactNode\n trailingAddon?: ReactNode\n label?: string\n helperText?: string\n required?: boolean\n id: string\n} & TextFieldVariantProps\n\nexport const TextField = forwardRef(\n (props, ref) => {\n const {\n className,\n leadingAddon,\n trailingAddon,\n label,\n helperText,\n id,\n required = false,\n status,\n ...rest\n } = props\n\n const styles = recipe({ status })\n return (\n
\n {label && (\n
\n \n
\n )}\n\n
\n {leadingAddon &&
{leadingAddon}
}\n\n \n\n {trailingAddon && (\n
{trailingAddon}
\n )}\n
\n\n {helperText && (\n \n {helperText}\n
\n )}\n \n )\n },\n)\n\nTextField.displayName = \"TextField\"\n","type":"ui"},{"name":"recipe.ts","content":"import { tv, type VariantProps } from \"tailwind-variants\"\n\nexport const recipe = tv({\n slots: {\n root: \"flex w-full flex-col gap-1.5\",\n heading: \"text-sm/snug font-semibold tracking-wide\",\n container: [\n \"flex min-h-12 w-full items-center gap-1.5 p-3\",\n \"rounded-md border border-base-200 shadow-sm\",\n \"transition-colors duration-150 ease-in\",\n \"has-disabled:cursor-not-allowed\",\n \"focus-within:border-primary\",\n ],\n input: `\n flex-1 border-none outline-none\n disabled:cursor-not-allowed\n `,\n trailingButton: \"flex max-h-2 items-center whitespace-nowrap\",\n helper: `\n self-stretch text-[0.625rem] leading-tight font-semibold tracking-wide\n text-base-content/60\n `,\n },\n variants: {\n status: {\n normal: {},\n negative: {\n container: `\n border-error\n focus-within:border-error\n `,\n helper: \"text-error\",\n },\n },\n },\n defaultVariants: {\n status: \"normal\",\n },\n})\n\nexport type TextFieldVariantProps = VariantProps\n","type":"ui"}]} \ No newline at end of file diff --git a/app/docs/src/app/(main)/layout.tsx b/app/docs/src/app/(main)/layout.tsx index 73cb868d3..cef188c27 100644 --- a/app/docs/src/app/(main)/layout.tsx +++ b/app/docs/src/app/(main)/layout.tsx @@ -1,16 +1,5 @@ import type { ReactNode } from "react" export default function Layout({ children }: { children: ReactNode }) { - return ( - <> - {/* // px="2" - // py="16" - // h="[50%]" - // className={flex({ - // direction: "column", - // gap: "5", - // })} */} - {children} - - ) + return <>{children} } diff --git a/app/docs/src/app/(main)/page.tsx b/app/docs/src/app/(main)/page.tsx index 99c803b45..2f0d5186e 100644 --- a/app/docs/src/app/(main)/page.tsx +++ b/app/docs/src/app/(main)/page.tsx @@ -1,6 +1,5 @@ "use client" -import { css } from "@styled-system/css" import { ArrowRightIcon, Terminal } from "lucide-react" import Link from "next/link" @@ -9,99 +8,54 @@ import { Button } from "@/components/button" export default function MainPage() { return (
{/* Badge */} Design System CLI {/* Title */}

Create your project using CLI

{/* Subtitle */} -

+

PandaCSS 기반의 타입 세이프한 컴포넌트를 CLI로 쉽게 설치하세요. shadcn/ui 스타일의 개발 경험을 제공합니다.

{/* CLI Command */}
npx @jongh/cli init @@ -109,12 +63,10 @@ export default function MainPage() { {/* Button Group */}
diff --git a/app/docs/src/components/commandCodeBlock.tsx b/app/docs/src/components/commandCodeBlock.tsx index 736ab3be3..bcb9fb888 100644 --- a/app/docs/src/components/commandCodeBlock.tsx +++ b/app/docs/src/components/commandCodeBlock.tsx @@ -1,6 +1,3 @@ -import { css, cx } from "@styled-system/css" -import { flex } from "@styled-system/patterns" - import * as Tabs from "@/components/tabs" const packageManagers = ["npm", "pnpm", "yarn"] as const @@ -12,23 +9,12 @@ interface CommandCodeBlockProps { export function CommandCodeBlock({ command }: CommandCodeBlockProps) { return ( - + {packageManagers.map((pm) => ( - + {pm} ))} @@ -37,19 +23,10 @@ export function CommandCodeBlock({ command }: CommandCodeBlockProps) { -
+          {/* eslint-disable-next-line better-tailwindcss/no-unknown-classes */}
+          
             
           
diff --git a/app/docs/src/components/dialog/index.tsx b/app/docs/src/components/dialog/index.tsx index 29d5e807a..f372ac305 100644 --- a/app/docs/src/components/dialog/index.tsx +++ b/app/docs/src/components/dialog/index.tsx @@ -1,8 +1,7 @@ "use client" -import { css } from "@styled-system/css" import { X } from "lucide-react" import { Dialog } from "radix-ui" -import { type ComponentProps } from "react" +import { type ComponentProps, forwardRef } from "react" import { createStyleContext } from "@/utils/createStyleContext" @@ -12,60 +11,38 @@ const { withRootProvider, withContext } = createStyleContext(recipe) export const Root = withRootProvider(Dialog.Root) -export const Portal = withRootProvider(Dialog.Portal) -export const Overlay = withContext< - React.ComponentType, - Dialog.DialogOverlayProps ->(Dialog.Overlay, "overlay") +export const Portal = withContext(Dialog.Portal, "portal") -export const Close = withContext< - React.ComponentType, - Dialog.DialogCloseProps ->(Dialog.Close, "close") +export const Overlay = withContext(Dialog.Overlay, "overlay") -export const ContentPrimitive = ({ - children, - ref, - ...props -}: ComponentProps) => ( +export const Close = withContext(Dialog.Close, "close") + +export const ContentPrimitive = forwardRef< + HTMLDivElement, + ComponentProps +>(({ children, ...props }, ref) => ( {children} - Close + Close -) +)) + +ContentPrimitive.displayName = "ContentPrimitive" -export const Content = withContext< - React.ComponentType, - ComponentProps ->(ContentPrimitive, "content") +export const Content = withContext(ContentPrimitive, "content") -export const Trigger = withContext< - React.ComponentType, - Dialog.DialogTriggerProps ->(Dialog.Trigger, "trigger") +export const Trigger = withContext(Dialog.Trigger, "trigger") -export const Header = withContext>( - "div", - "header", -) +export const Header = withContext("div", "header") -export const Footer = withContext>( - "div", - "footer", -) +export const Footer = withContext("div", "footer") -export const Title = withContext< - React.ComponentType, - Dialog.DialogTitleProps ->(Dialog.Title, "title") +export const Title = withContext(Dialog.Title, "title") -export const Description = withContext< - React.ComponentType, - Dialog.DialogDescriptionProps ->(Dialog.Description, "description") +export const Description = withContext(Dialog.Description, "description") diff --git a/app/docs/src/components/dialog/recipe.ts b/app/docs/src/components/dialog/recipe.ts index 255024be4..a1ae1cb6e 100644 --- a/app/docs/src/components/dialog/recipe.ts +++ b/app/docs/src/components/dialog/recipe.ts @@ -1,92 +1,28 @@ -import { sva } from "@styled-system/css" +import { tv } from "tailwind-variants" -export const recipe = sva({ - slots: [ - "root", - "trigger", - "portal", - "overlay", - "close", - "content", - "header", - "footer", - "title", - "description", - ], - base: { - overlay: { - position: "fixed", - inset: "0", - bg: "black/8", - }, - content: { - bg: "background", - border: "input", - borderRadius: "md", - position: "fixed", - top: "[50%]", - left: "[50%]", - transform: "translate(-50%,-50%)", - width: "[100%]", - maxWidth: "[32rem]", - maxHeight: "[85vh]", - padding: "4", - "&[data-state=open]": { - animationName: "contentShow", - animationDuration: "slow", - }, - - "&[data-state=closed]": { - animationName: "fadeOut", - animationDuration: "slow", - }, - }, - close: { - position: "absolute", - right: "4", - top: "4", - rounded: "sm", - opacity: "0.7", - transition: "opacity", - cursor: "pointer", - - _hover: { - opacity: "1", - }, - - _focus: { - ringWidth: "1", - ringColor: "ring", - ringOffset: "1", - }, - - _disabled: { - pointerEvents: "none", - }, - - "&[data-state=open]": { - bg: "accent", - color: "muted.foreground", - }, - }, - header: { - display: "flex", - flexDirection: "column", - gap: "1.5", - }, - footer: { - display: "flex", - flexDirection: "column-reverse", - }, - title: { - fontSize: "lg", - fontWeight: "semibold", - lineHeight: "none", - letterSpacing: "tight", - }, - description: { - fontSize: "sm", - color: "foreground", - }, +export const recipe = tv({ + slots: { + root: "", + trigger: "", + portal: "", + overlay: "fixed inset-0 bg-black/8", + close: [ + "absolute top-4 right-4 cursor-pointer rounded-sm opacity-70", + ` + transition-opacity + hover:opacity-100 + `, + "disabled:pointer-events-none", + "data-[state=open]:bg-accent data-[state=open]:text-base-content", + ], + content: [ + "fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2", + "max-h-[85vh] w-full max-w-lg", + "rounded-md border border-base-300 bg-base-100 p-4", + ], + header: "flex flex-col gap-1.5", + footer: "flex flex-col-reverse", + title: "text-lg leading-none font-semibold tracking-tight", + description: "text-sm text-base-content", }, }) diff --git a/app/docs/src/components/header.tsx b/app/docs/src/components/header.tsx index 50658a45b..654e7c57c 100644 --- a/app/docs/src/components/header.tsx +++ b/app/docs/src/components/header.tsx @@ -1,6 +1,3 @@ -import { css } from "@styled-system/css" -import { Flex } from "@styled-system/jsx" -import { flex } from "@styled-system/patterns" import Link from "next/link" import { ReactNode } from "react" @@ -19,63 +16,37 @@ interface HeaderProps { export function Header({ logo, navItems, icons }: HeaderProps) { return (
- + {logo} - {navItems.map((item) => (
  • {item.label}
  • ))} -
    - {icons.map((icon) => icon)} +
    +
    {icons.map((icon) => icon)}
    ) } diff --git a/app/docs/src/components/icons/copy.tsx b/app/docs/src/components/icons/copy.tsx index 100bf9270..5d344e549 100644 --- a/app/docs/src/components/icons/copy.tsx +++ b/app/docs/src/components/icons/copy.tsx @@ -24,6 +24,7 @@ const Copy = ( > + {/* eslint-disable-next-line better-tailwindcss/no-unknown-classes */} ) diff --git a/app/docs/src/components/propsTable/index.tsx b/app/docs/src/components/propsTable/index.tsx index dcbb2e9f4..a54e6f55e 100644 --- a/app/docs/src/components/propsTable/index.tsx +++ b/app/docs/src/components/propsTable/index.tsx @@ -19,16 +19,16 @@ export function PropsTable({ children, data }: PropsTableProps) { const styles = propsTableRecipe() return ( -
    - - - - - - +
    +
    PropTypeDescription
    + + + + + - + {data ? data.map((prop) => ( @@ -53,22 +53,22 @@ function PropRowInternal({ styles, }: PropRowInternalProps) { return ( - - + - - + ) } @@ -82,22 +82,22 @@ export function PropRow({ }: PropRowProps) { const styles = propsTableRecipe() return ( - - + - - + ) } diff --git a/app/docs/src/components/propsTable/recipe.ts b/app/docs/src/components/propsTable/recipe.ts index 2ab5a29cb..f4db057f9 100644 --- a/app/docs/src/components/propsTable/recipe.ts +++ b/app/docs/src/components/propsTable/recipe.ts @@ -1,65 +1,27 @@ -import { sva } from "@styled-system/css" +import { tv } from "tailwind-variants" -export const propsTableRecipe = sva({ - slots: ["container", "table", "thead", "tbody", "tr", "th", "td", "code"], - base: { - container: { - width: "100%", - overflowX: "auto", - my: "6", - border: "1px solid", - borderColor: "border", - rounded: "lg", - overflow: "hidden", - shadow: "card", - }, - table: { - width: "100%", - borderCollapse: "collapse", - fontSize: "sm", - }, - thead: { - bg: "muted", - }, - tbody: { - bg: "card", - }, - tr: { - borderBottom: "1px solid", - borderBottomColor: "border", - transition: "background", - transitionDuration: "fast", - _last: { - borderBottom: "none", - }, - _hover: { - bg: "muted/30", - }, - }, - th: { - px: "4", - py: "3", - textAlign: "left", - fontWeight: "semibold", - color: "foreground", - textStyle: "label1", - }, - td: { - px: "4", - py: "3", - color: "foreground", - textStyle: "body2", - verticalAlign: "top", - }, - code: { - bg: "muted", - color: "primary", - px: "1.5", - py: "0.5", - rounded: "sm", - fontSize: "0.875em", - fontFamily: "mono", - fontWeight: "medium", - }, +export const propsTableRecipe = tv({ + slots: { + container: ` + my-6 w-full overflow-hidden overflow-x-auto rounded-lg border + border-base-300 shadow-md + `, + table: "w-full border-collapse text-sm", + thead: "bg-base-200", + tbody: "bg-base-200", + tr: ` + border-b border-base-300 transition-colors duration-150 + last:border-b-0 + hover:bg-base-200/30 + `, + th: ` + px-4 py-3 text-left text-sm/snug font-semibold tracking-wide + text-base-content + `, + td: "px-4 py-3 align-top text-sm/normal text-base-content", + code: ` + rounded-sm bg-base-200 px-1.5 py-0.5 font-mono text-[0.875em] font-medium + text-primary + `, }, }) diff --git a/app/docs/src/components/select/index.tsx b/app/docs/src/components/select/index.tsx index c37de8ca7..47b9f3c25 100644 --- a/app/docs/src/components/select/index.tsx +++ b/app/docs/src/components/select/index.tsx @@ -1,121 +1,79 @@ -import { css, cx } from "@styled-system/css" import { Select as SelectPrimitive } from "radix-ui" -import type { ComponentProps } from "react" +import { + type ComponentPropsWithoutRef, + type ElementRef, + forwardRef, +} from "react" + +import { createStyleContext } from "@/utils/createStyleContext" import { recipe } from "./recipe" -const BaseTrigger = ({ - children, - ...props -}: ComponentProps) => ( - +const { withContext, withRootProvider } = createStyleContext(recipe) + +const BaseTrigger = forwardRef< + ElementRef, + ComponentPropsWithoutRef +>(({ children, ...props }, ref) => ( + {children} -) +)) BaseTrigger.displayName = SelectPrimitive.Trigger.displayName -export const Root = SelectPrimitive.Root +export const Root = withRootProvider(SelectPrimitive.Root) export const Group = SelectPrimitive.Group export const Value = SelectPrimitive.Value -export const Trigger = ({ - className, - ...props -}: ComponentProps) => { - const styles = recipe.raw() - return ( - - ) -} - -export const Content = ({ - className, - position = "popper", - children, - ...props -}: ComponentProps) => { - const styles = recipe.raw() +export const Trigger = withContext(BaseTrigger, "trigger") + +export const ContentPrimitive = forwardRef< + ElementRef, + ComponentPropsWithoutRef +>(({ position = "popper", children, ...props }, ref) => { return ( {children} ) -} +}) -export const Viewport = ({ - className, - ...props -}: ComponentProps) => { - const styles = recipe.raw() - return ( - - ) -} - -export const Item = ({ - className, - children, - ...props -}: ComponentProps) => { - const styles = recipe.raw() +ContentPrimitive.displayName = "ContentPrimitive" + +export const Content = withContext(ContentPrimitive, "content") + +export const Viewport = withContext(SelectPrimitive.Viewport, "viewport") + +export const ItemPrimitive = forwardRef< + ElementRef, + ComponentPropsWithoutRef +>(({ children, ...props }, ref) => { return ( - + {children} ) -} +}) -export const Label = ({ - className, - ...props -}: ComponentProps) => { - const styles = recipe.raw() - return ( - - ) -} +ItemPrimitive.displayName = "ItemPrimitive" -export const Separator = ({ - className, - ...props -}: ComponentProps) => { - const styles = recipe.raw() - return ( - - ) -} +export const Item = withContext(ItemPrimitive, "item") -export const Indicator = ({ - className, - ...props -}: ComponentProps) => { - const styles = recipe.raw() - return ( - - ) -} +export const Label = withContext(SelectPrimitive.Label, "label") + +export const Separator = withContext(SelectPrimitive.Separator, "separator") + +export const Indicator = withContext( + SelectPrimitive.ItemIndicator, + "itemIndicator", +) diff --git a/app/docs/src/components/select/recipe.ts b/app/docs/src/components/select/recipe.ts index 9f33c7c36..0121afba9 100644 --- a/app/docs/src/components/select/recipe.ts +++ b/app/docs/src/components/select/recipe.ts @@ -1,142 +1,39 @@ -import { sva } from "@styled-system/css" - -export const recipe = sva({ - className: "select", - slots: [ - "root", - "group", - "value", - "trigger", - "viewport", - "content", - "label", - "item", - "itemIndicator", - "separator", - ], - base: { - trigger: { - display: "flex", - h: "10", - w: "full", - alignItems: "center", - justifyContent: "space-between", - rounded: "md", - border: "input", - bg: "transparent", - px: "3", - py: "2", - textStyle: "sm", - cursor: "pointer", - ringColor: "background", - - _placeholder: { - color: "muted.foreground", - }, - - _focus: { - ringWidth: "1", - ringColor: "ring", - outlineOffset: "1", - }, - - _disabled: { - cursor: "not-allowed", - opacity: "0.5", - }, - }, - viewport: { - "&:is([data-position=popper])": { - h: "var(--radix-select-trigger-height)", - w: "full", - minW: "var(--radix-select-trigger-width)", - }, - }, - content: { - position: "relative", - zIndex: 50, - minW: "full", - overflow: "hidden", - rounded: "md", - border: "base", - bg: "popover", - color: "popover.foreground", - shadow: "md", - maxH: "96", - - "&:is([data-state=open])": { - animationName: "fadeIn", - animationState: "ease-in", - animationDuration: "slowest", - }, - - "&:is(&[data-state=closed])": { - animationName: "slideInDown", - animationState: "ease-in", - animationDuration: "slowest", - }, - - "&:is([data-position=popper])": { - "&:is([data-side=top])": { - translateY: "-1", - }, - - "&:is([data-side=bottom])": { - translateY: "1", - }, - - "&:is([data-side=left])": { - translateX: "-1", - }, - - "&:is([data-side=right])": { - translateX: "1", - }, - }, - }, - label: { - py: "1.5", - pl: "8", - pr: "2", - textStyle: "sm", - fontWeight: "semibold", - }, - item: { - position: "relative", - display: "flex", - cursor: "default", - userSelect: "none", - alignItems: "center", - rounded: "sm", - py: "1.5", - pl: "8", - pr: "2", - textStyle: "sm", - - _focus: { - bg: "accent", - color: "accent.foreground", - }, - - ["&[data-disabled]"]: { - pointerEvents: "none", - opacity: "0.5", - }, - }, - itemIndicator: { - position: "absolute", - left: "2", - display: "flex", - h: "3.5", - w: "3.5", - alignItems: "center", - justifyContent: "center", - }, - separator: { - mx: "-1", - my: "1", - h: "1", - bg: "muted", - }, +import { tv } from "tailwind-variants" + +export const recipe = tv({ + slots: { + root: "", + group: "", + value: "", + trigger: [ + "flex h-10 w-full items-center justify-between px-3 py-2", + "cursor-pointer rounded-md border border-base-300 bg-transparent", + "text-sm", + "placeholder:text-base-content/60", + "disabled:cursor-not-allowed disabled:opacity-50", + ], + viewport: ` + data-[position=popper]:h-(--radix-select-trigger-height) + data-[position=popper]:w-full + data-[position=popper]:min-w-(--radix-select-trigger-width) + `, + content: [ + "relative z-50 min-w-full overflow-hidden", + "rounded-md border border-base-300 shadow-md", + "max-h-96 bg-base-100 text-base-content", + "data-[position=popper]:data-[side=top]:-translate-y-1", + "data-[position=popper]:data-[side=bottom]:translate-y-1", + "data-[position=popper]:data-[side=left]:-translate-x-1", + "data-[position=popper]:data-[side=right]:translate-x-1", + ], + label: "py-1.5 pr-2 pl-8 text-sm font-semibold", + item: [ + "relative flex items-center rounded-sm py-1.5 pr-2 pl-8 text-sm", + "cursor-default select-none", + "focus:bg-accent focus:text-accent-content", + "data-disabled:pointer-events-none data-disabled:opacity-50", + ], + itemIndicator: "absolute left-2 flex size-3.5 items-center justify-center", + separator: "-mx-1 my-1 h-px bg-base-200", }, }) diff --git a/app/docs/src/components/tabs/index.tsx b/app/docs/src/components/tabs/index.tsx index c6d89edd4..36f0960cd 100644 --- a/app/docs/src/components/tabs/index.tsx +++ b/app/docs/src/components/tabs/index.tsx @@ -1,65 +1,19 @@ -import { css, cx } from "@styled-system/css" import { Tabs as TabsPrimitive } from "radix-ui" -import type { ComponentProps } from "react" + +import { createStyleContext } from "@/utils/createStyleContext" import { recipe } from "./recipe" -export const Root = ({ - className, - ...props -}: ComponentProps) => { - const styles = recipe.raw() - return ( - - ) -} +const { withContext, withProvider } = createStyleContext(recipe) +export const Root = withProvider(TabsPrimitive.Root, "root") Root.displayName = "Tabs" -export const List = ({ - className, - ...props -}: ComponentProps) => { - const styles = recipe.raw() - return ( - - ) -} - +export const List = withContext(TabsPrimitive.List, "list") List.displayName = "Tabs.List" -export const Trigger = ({ - className, - ...props -}: ComponentProps) => { - const styles = recipe.raw() - return ( - - ) -} - +export const Trigger = withContext(TabsPrimitive.Trigger, "trigger") Trigger.displayName = "Tabs.Trigger" -export const Content = ({ - className, - ...props -}: ComponentProps) => { - const styles = recipe.raw() - return ( - - ) -} - +export const Content = withContext(TabsPrimitive.Content, "content") Content.displayName = "Tabs.Content" diff --git a/app/docs/src/components/tabs/recipe.ts b/app/docs/src/components/tabs/recipe.ts index 381f65409..bea667ccf 100644 --- a/app/docs/src/components/tabs/recipe.ts +++ b/app/docs/src/components/tabs/recipe.ts @@ -1,59 +1,21 @@ -import { sva } from "@styled-system/css/sva.mjs" +import { tv } from "tailwind-variants" -export const recipe = sva({ - slots: ["root", "list", "trigger", "content"], - base: { - list: { - display: "inline-flex", - h: "10", - alignItems: "center", - justifyContent: "center", - rounded: "md", - bg: "muted", - p: "1", - color: "muted.foreground", - }, - trigger: { - display: "inline-flex", - alignItems: "center", - justifyContent: "center", - whiteSpace: "nowrap", - rounded: "sm", - px: "3", - py: "1.5", - textStyle: "sm", - fontWeight: "medium", - transition: "all", - cursor: "pointer", - ringColor: "background", - - _focusVisible: { - ringWidth: "1", - ringColor: "ring", - ringOffset: "1", - }, - - _disabled: { - pointerEvents: "none", - opacity: "50%", - }, - - "&:is([data-state=active])": { - bg: "background", - color: "foreground", - shadow: "sm", - }, - }, - content: { - mt: "2", - ringColor: "background", - - _focusVisible: { - outlineWidth: "2", - outlineOffset: "2", - ringWidth: "2", - ringColor: "ring", - }, - }, +export const recipe = tv({ + slots: { + root: "", + list: ` + inline-flex h-10 items-center justify-center rounded-md bg-base-200 p-1 + text-base-content + `, + trigger: [ + "inline-flex items-center justify-center whitespace-nowrap", + "cursor-pointer rounded-sm px-3 py-1.5 text-sm font-medium transition-all", + "disabled:pointer-events-none disabled:opacity-50", + ` + data-[state=active]:bg-base-100 data-[state=active]:text-base-content + data-[state=active]:shadow-sm + `, + ], + content: "mt-2", }, }) diff --git a/app/docs/src/components/textfield/index.tsx b/app/docs/src/components/textfield/index.tsx index d476d66e3..bfc713321 100644 --- a/app/docs/src/components/textfield/index.tsx +++ b/app/docs/src/components/textfield/index.tsx @@ -1,7 +1,8 @@ -import { css, cx } from "@styled-system/css" import type { ComponentProps, ReactNode } from "react" -import { recipe, TextFieldVariantProps } from "./recipe" +import { cn } from "@/utils/cn" + +import { recipe, type TextFieldVariantProps } from "./recipe" const getHelperText = (id: string) => `${id}-helper-text` @@ -16,8 +17,6 @@ export type TextFieldProps = Omit, "size" | "width"> & { } & TextFieldVariantProps export const TextField = (props: TextFieldProps) => { - const [variantProps, componentProps] = recipe.splitVariantProps(props) - const { className, leadingAddon, @@ -27,41 +26,49 @@ export const TextField = (props: TextFieldProps) => { id, required = false, ref, + status, ...rest - } = componentProps + } = props - const styles = recipe.raw(variantProps) + const styles = recipe({ status }) return ( -
    +
    {label && ( -
    -
    PropTypeDescription
    - +
    + {name} {required && *} {defaultValue && (
    - Default: {defaultValue} + Default: {defaultValue}
    )}
    - {type} + + {type} {description}{description}
    - +
    + {name} {required && *} {defaultValue && (
    - Default: {defaultValue} + Default: {defaultValue}
    )}
    - {type} + + {type} {description}{description}
    +
    {weeks.map((week, weekIndex) => ( - + {week.map((day, dayIndex) => { const isHidden = !showOutsideDays && !day.isCurrentMonth const month = day.isCurrentMonth @@ -488,7 +477,7 @@ export const Days = ({ className, showOutsideDays = true }: DaysProps) => { return (
    ( (props, ref) => { - const [variantProps, componentProps] = - checkboxRecipe.splitVariantProps(props) - - const { label, indeterminate, onCheckedChange, onChange, ...inputProps } = - componentProps - const { - root, - label: labelClassName, - input, - text, - } = checkboxRecipe(variantProps) + label, + indeterminate, + onCheckedChange, + onChange, + size, + variant, + ...inputProps + } = props + + const styles = checkboxRecipe({ size, variant }) const [isChecked = false, setIsChecked] = useControllableState({ prop: inputProps.checked, @@ -69,12 +68,12 @@ export const Checkbox = forwardRef( const composedRefs = useComposedRefs(ref, inputRef) return ( -
    -