@quadrokit/ui 0.1.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +1 -1
  2. package/dist/components/button.d.ts +12 -0
  3. package/dist/components/button.d.ts.map +1 -0
  4. package/dist/components/button.mjs +32 -0
  5. package/dist/components/card.d.ts +7 -0
  6. package/dist/components/card.d.ts.map +1 -0
  7. package/dist/components/card.mjs +13 -0
  8. package/dist/components/input.d.ts +5 -0
  9. package/dist/components/input.d.ts.map +1 -0
  10. package/dist/components/input.mjs +7 -0
  11. package/dist/components/label.d.ts +3 -0
  12. package/dist/components/label.d.ts.map +1 -0
  13. package/dist/components/label.mjs +7 -0
  14. package/dist/components/theme-toolbar.d.ts +2 -0
  15. package/dist/components/theme-toolbar.d.ts.map +1 -0
  16. package/dist/components/theme-toolbar.mjs +12 -0
  17. package/dist/index.d.ts +9 -0
  18. package/dist/index.d.ts.map +1 -0
  19. package/dist/index.mjs +8 -0
  20. package/dist/lib/utils.d.ts +3 -0
  21. package/dist/lib/utils.d.ts.map +1 -0
  22. package/dist/lib/utils.mjs +5 -0
  23. package/dist/tailwind-preset.d.ts +4 -0
  24. package/dist/tailwind-preset.d.ts.map +1 -0
  25. package/dist/tailwind-preset.mjs +48 -0
  26. package/dist/theme/theme-provider.d.ts +18 -0
  27. package/dist/theme/theme-provider.d.ts.map +1 -0
  28. package/dist/theme/theme-provider.mjs +55 -0
  29. package/dist/theme/themes.d.ts +4 -0
  30. package/dist/theme/themes.d.ts.map +1 -0
  31. package/dist/theme/themes.mjs +7 -0
  32. package/package.json +14 -7
  33. package/src/components/button.tsx +0 -45
  34. package/src/components/card.tsx +0 -50
  35. package/src/components/input.tsx +0 -22
  36. package/src/components/label.tsx +0 -18
  37. package/src/components/theme-toolbar.tsx +0 -46
  38. package/src/index.ts +0 -22
  39. package/src/lib/utils.ts +0 -6
  40. package/src/theme/theme-provider.tsx +0 -101
  41. package/src/theme/themes.ts +0 -9
  42. package/tailwind-preset.ts +0 -51
  43. package/tsconfig.json +0 -9
  44. /package/{src → dist}/styles.css +0 -0
package/README.md CHANGED
@@ -9,7 +9,7 @@ Shared **React** UI for QuadroKit templates: Tailwind **preset**, global **CSS v
9
9
 
10
10
  ```ts
11
11
  import preset from '@quadrokit/ui/tailwind-preset';
12
- export default { presets: [preset], content: [/* your files */, '../../node_modules/@quadrokit/ui/src/**/*.{ts,tsx}'] };
12
+ export default { presets: [preset], content: [/* your files */, '../../node_modules/@quadrokit/ui/dist/**/*.{mjs,js,ts,tsx}'] };
13
13
  ```
14
14
 
15
15
  3. Import styles once (e.g. in `main.tsx`):
@@ -0,0 +1,12 @@
1
+ import { type VariantProps } from 'class-variance-authority';
2
+ import type { ButtonHTMLAttributes } from 'react';
3
+ declare const variants: (props?: ({
4
+ variant?: "default" | "secondary" | "outline" | "ghost" | "destructive" | "link" | null | undefined;
5
+ size?: "default" | "sm" | "lg" | "icon" | null | undefined;
6
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
7
+ export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof variants> {
8
+ asChild?: boolean;
9
+ }
10
+ export declare const Button: import("react").ForwardRefExoticComponent<ButtonProps & import("react").RefAttributes<HTMLButtonElement>>;
11
+ export {};
12
+ //# sourceMappingURL=button.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"button.d.ts","sourceRoot":"","sources":["../../src/components/button.tsx"],"names":[],"mappings":"AACA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AACjE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAA;AAIjD,QAAA,MAAM,QAAQ;;;8EAwBb,CAAA;AAED,MAAM,WAAW,WACf,SAAQ,oBAAoB,CAAC,iBAAiB,CAAC,EAC7C,YAAY,CAAC,OAAO,QAAQ,CAAC;IAC/B,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,eAAO,MAAM,MAAM,2GAKlB,CAAA"}
@@ -0,0 +1,32 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Slot } from '@radix-ui/react-slot';
3
+ import { cva } from 'class-variance-authority';
4
+ import { forwardRef } from 'react';
5
+ import { cn } from '../lib/utils.mjs';
6
+ const variants = cva('inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0', {
7
+ variants: {
8
+ variant: {
9
+ default: 'bg-primary text-primary-foreground shadow hover:bg-primary/90',
10
+ secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
11
+ outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
12
+ ghost: 'hover:bg-accent hover:text-accent-foreground',
13
+ destructive: 'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90',
14
+ link: 'text-primary underline-offset-4 hover:underline',
15
+ },
16
+ size: {
17
+ default: 'h-9 px-4 py-2',
18
+ sm: 'h-8 rounded-md px-3 text-xs',
19
+ lg: 'h-10 rounded-md px-8',
20
+ icon: 'h-9 w-9',
21
+ },
22
+ },
23
+ defaultVariants: {
24
+ variant: 'default',
25
+ size: 'default',
26
+ },
27
+ });
28
+ export const Button = forwardRef(({ className, variant, size, asChild = false, ...props }, ref) => {
29
+ const Comp = asChild ? Slot : 'button';
30
+ return _jsx(Comp, { className: cn(variants({ variant, size, className })), ref: ref, ...props });
31
+ });
32
+ Button.displayName = 'Button';
@@ -0,0 +1,7 @@
1
+ import type { HTMLAttributes } from 'react';
2
+ export declare const Card: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLDivElement> & import("react").RefAttributes<HTMLDivElement>>;
3
+ export declare const CardHeader: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLDivElement> & import("react").RefAttributes<HTMLDivElement>>;
4
+ export declare const CardTitle: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLHeadingElement> & import("react").RefAttributes<HTMLHeadingElement>>;
5
+ export declare const CardDescription: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLParagraphElement> & import("react").RefAttributes<HTMLParagraphElement>>;
6
+ export declare const CardContent: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLDivElement> & import("react").RefAttributes<HTMLDivElement>>;
7
+ //# sourceMappingURL=card.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"card.d.ts","sourceRoot":"","sources":["../../src/components/card.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAA;AAI3C,eAAO,MAAM,IAAI,2HAWhB,CAAA;AAGD,eAAO,MAAM,UAAU,2HAItB,CAAA;AAGD,eAAO,MAAM,SAAS,mIAQrB,CAAA;AAGD,eAAO,MAAM,eAAe,uIAK1B,CAAA;AAGF,eAAO,MAAM,WAAW,2HAIvB,CAAA"}
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { forwardRef } from 'react';
3
+ import { cn } from '../lib/utils.mjs';
4
+ export const Card = forwardRef(({ className, ...props }, ref) => (_jsx("div", { ref: ref, className: cn('rounded-xl border border-border bg-card text-card-foreground shadow-sm', className), ...props })));
5
+ Card.displayName = 'Card';
6
+ export const CardHeader = forwardRef(({ className, ...props }, ref) => (_jsx("div", { ref: ref, className: cn('flex flex-col space-y-1.5 p-6', className), ...props })));
7
+ CardHeader.displayName = 'CardHeader';
8
+ export const CardTitle = forwardRef(({ className, ...props }, ref) => (_jsx("h3", { ref: ref, className: cn('font-semibold leading-none tracking-tight', className), ...props })));
9
+ CardTitle.displayName = 'CardTitle';
10
+ export const CardDescription = forwardRef(({ className, ...props }, ref) => (_jsx("p", { ref: ref, className: cn('text-sm text-muted-foreground', className), ...props })));
11
+ CardDescription.displayName = 'CardDescription';
12
+ export const CardContent = forwardRef(({ className, ...props }, ref) => (_jsx("div", { ref: ref, className: cn('p-6 pt-0', className), ...props })));
13
+ CardContent.displayName = 'CardContent';
@@ -0,0 +1,5 @@
1
+ import type { InputHTMLAttributes } from 'react';
2
+ export interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
3
+ }
4
+ export declare const Input: import("react").ForwardRefExoticComponent<InputProps & import("react").RefAttributes<HTMLInputElement>>;
5
+ //# sourceMappingURL=input.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../src/components/input.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,OAAO,CAAA;AAIhD,MAAM,WAAW,UAAW,SAAQ,mBAAmB,CAAC,gBAAgB,CAAC;CAAG;AAE5E,eAAO,MAAM,KAAK,yGAcjB,CAAA"}
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { forwardRef } from 'react';
3
+ import { cn } from '../lib/utils.mjs';
4
+ export const Input = forwardRef(({ className, type, ...props }, ref) => {
5
+ return (_jsx("input", { type: type, className: cn('flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50', className), ref: ref, ...props }));
6
+ });
7
+ Input.displayName = 'Input';
@@ -0,0 +1,3 @@
1
+ import type { LabelHTMLAttributes } from 'react';
2
+ export declare const Label: import("react").ForwardRefExoticComponent<LabelHTMLAttributes<HTMLLabelElement> & import("react").RefAttributes<HTMLLabelElement>>;
3
+ //# sourceMappingURL=label.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"label.d.ts","sourceRoot":"","sources":["../../src/components/label.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,OAAO,CAAA;AAIhD,eAAO,MAAM,KAAK,oIAYjB,CAAA"}
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { forwardRef } from 'react';
3
+ import { cn } from '../lib/utils.mjs';
4
+ export const Label = forwardRef(({ className, ...props }, ref) => (
5
+ // biome-ignore lint/a11y/noLabelWithoutControl: shadcn-style primitive; use htmlFor at call sites
6
+ _jsx("label", { ref: ref, className: cn('text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70', className), ...props })));
7
+ Label.displayName = 'Label';
@@ -0,0 +1,2 @@
1
+ export declare function ThemeToolbar(): import("react/jsx-runtime").JSX.Element;
2
+ //# sourceMappingURL=theme-toolbar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme-toolbar.d.ts","sourceRoot":"","sources":["../../src/components/theme-toolbar.tsx"],"names":[],"mappings":"AAIA,wBAAgB,YAAY,4CAyC3B"}
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useTheme } from '../theme/theme-provider.mjs';
3
+ import { quadroThemes, themeLabels } from '../theme/themes.mjs';
4
+ import { Button } from './button.mjs';
5
+ export function ThemeToolbar() {
6
+ const { preset, setPreset, colorMode, setColorMode, resolvedMode } = useTheme();
7
+ return (_jsxs("div", { className: "flex flex-wrap items-center gap-2 rounded-lg border border-border bg-card/60 p-2 text-sm backdrop-blur", children: [_jsx("span", { className: "text-muted-foreground px-1", children: "Theme" }), quadroThemes.map((t) => (_jsx(Button, { type: "button", size: "sm", variant: preset === t ? 'default' : 'outline', onClick: () => setPreset(t), children: themeLabels[t] }, t))), _jsx("span", { className: "mx-2 hidden h-4 w-px bg-border sm:inline-block" }), _jsx("span", { className: "text-muted-foreground px-1", children: "Mode" }), [
8
+ ['light', 'Light'],
9
+ ['dark', 'Dark'],
10
+ ['system', 'System'],
11
+ ].map(([m, label]) => (_jsx(Button, { type: "button", size: "sm", variant: colorMode === m ? 'default' : 'ghost', onClick: () => setColorMode(m), children: label }, m))), _jsx("span", { className: "ml-auto text-xs text-muted-foreground", children: resolvedMode === 'dark' ? 'Dark' : 'Light' })] }));
12
+ }
@@ -0,0 +1,9 @@
1
+ export { Button, type ButtonProps } from './components/button.js';
2
+ export { Card, CardContent, CardDescription, CardHeader, CardTitle, } from './components/card.js';
3
+ export { Input, type InputProps } from './components/input.js';
4
+ export { Label } from './components/label.js';
5
+ export { ThemeToolbar } from './components/theme-toolbar.js';
6
+ export { cn } from './lib/utils.js';
7
+ export { type ColorMode, ThemeProvider, useTheme, } from './theme/theme-provider.js';
8
+ export { type QuadroThemePreset, quadroThemes, themeLabels, } from './theme/themes.js';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAA;AACjE,OAAO,EACL,IAAI,EACJ,WAAW,EACX,eAAe,EACf,UAAU,EACV,SAAS,GACV,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,KAAK,EAAE,KAAK,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAC9D,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAA;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAA;AAC5D,OAAO,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAA;AACnC,OAAO,EACL,KAAK,SAAS,EACd,aAAa,EACb,QAAQ,GACT,MAAM,2BAA2B,CAAA;AAClC,OAAO,EACL,KAAK,iBAAiB,EACtB,YAAY,EACZ,WAAW,GACZ,MAAM,mBAAmB,CAAA"}
package/dist/index.mjs ADDED
@@ -0,0 +1,8 @@
1
+ export { Button } from './components/button.mjs';
2
+ export { Card, CardContent, CardDescription, CardHeader, CardTitle, } from './components/card.mjs';
3
+ export { Input } from './components/input.mjs';
4
+ export { Label } from './components/label.mjs';
5
+ export { ThemeToolbar } from './components/theme-toolbar.mjs';
6
+ export { cn } from './lib/utils.mjs';
7
+ export { ThemeProvider, useTheme, } from './theme/theme-provider.mjs';
8
+ export { quadroThemes, themeLabels, } from './theme/themes.mjs';
@@ -0,0 +1,3 @@
1
+ import { type ClassValue } from 'clsx';
2
+ export declare function cn(...inputs: ClassValue[]): string;
3
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/lib/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,UAAU,EAAQ,MAAM,MAAM,CAAA;AAG5C,wBAAgB,EAAE,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,UAEzC"}
@@ -0,0 +1,5 @@
1
+ import { clsx } from 'clsx';
2
+ import { twMerge } from 'tailwind-merge';
3
+ export function cn(...inputs) {
4
+ return twMerge(clsx(inputs));
5
+ }
@@ -0,0 +1,4 @@
1
+ import type { Config } from 'tailwindcss';
2
+ declare const preset: Partial<Config>;
3
+ export default preset;
4
+ //# sourceMappingURL=tailwind-preset.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tailwind-preset.d.ts","sourceRoot":"","sources":["../src/tailwind-preset.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC,QAAA,MAAM,MAAM,EAAE,OAAO,CAAC,MAAM,CA8C3B,CAAA;AAED,eAAe,MAAM,CAAA"}
@@ -0,0 +1,48 @@
1
+ const preset = {
2
+ darkMode: 'class',
3
+ theme: {
4
+ extend: {
5
+ borderRadius: {
6
+ lg: 'var(--radius)',
7
+ md: 'calc(var(--radius) - 2px)',
8
+ sm: 'calc(var(--radius) - 4px)',
9
+ },
10
+ colors: {
11
+ border: 'hsl(var(--border) / <alpha-value>)',
12
+ input: 'hsl(var(--input) / <alpha-value>)',
13
+ ring: 'hsl(var(--ring) / <alpha-value>)',
14
+ background: 'hsl(var(--background) / <alpha-value>)',
15
+ foreground: 'hsl(var(--foreground) / <alpha-value>)',
16
+ primary: {
17
+ DEFAULT: 'hsl(var(--primary) / <alpha-value>)',
18
+ foreground: 'hsl(var(--primary-foreground) / <alpha-value>)',
19
+ },
20
+ secondary: {
21
+ DEFAULT: 'hsl(var(--secondary) / <alpha-value>)',
22
+ foreground: 'hsl(var(--secondary-foreground) / <alpha-value>)',
23
+ },
24
+ destructive: {
25
+ DEFAULT: 'hsl(var(--destructive) / <alpha-value>)',
26
+ foreground: 'hsl(var(--destructive-foreground) / <alpha-value>)',
27
+ },
28
+ muted: {
29
+ DEFAULT: 'hsl(var(--muted) / <alpha-value>)',
30
+ foreground: 'hsl(var(--muted-foreground) / <alpha-value>)',
31
+ },
32
+ accent: {
33
+ DEFAULT: 'hsl(var(--accent) / <alpha-value>)',
34
+ foreground: 'hsl(var(--accent-foreground) / <alpha-value>)',
35
+ },
36
+ popover: {
37
+ DEFAULT: 'hsl(var(--popover) / <alpha-value>)',
38
+ foreground: 'hsl(var(--popover-foreground) / <alpha-value>)',
39
+ },
40
+ card: {
41
+ DEFAULT: 'hsl(var(--card) / <alpha-value>)',
42
+ foreground: 'hsl(var(--card-foreground) / <alpha-value>)',
43
+ },
44
+ },
45
+ },
46
+ },
47
+ };
48
+ export default preset;
@@ -0,0 +1,18 @@
1
+ import { type ReactNode } from 'react';
2
+ import type { QuadroThemePreset } from './themes.js';
3
+ export type ColorMode = 'light' | 'dark' | 'system';
4
+ interface ThemeContextValue {
5
+ preset: QuadroThemePreset;
6
+ setPreset: (p: QuadroThemePreset) => void;
7
+ colorMode: ColorMode;
8
+ setColorMode: (m: ColorMode) => void;
9
+ resolvedMode: 'light' | 'dark';
10
+ }
11
+ export declare function ThemeProvider({ children, defaultPreset, defaultColorMode, }: {
12
+ children: ReactNode;
13
+ defaultPreset?: QuadroThemePreset;
14
+ defaultColorMode?: ColorMode;
15
+ }): import("react/jsx-runtime").JSX.Element;
16
+ export declare function useTheme(): ThemeContextValue;
17
+ export {};
18
+ //# sourceMappingURL=theme-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme-provider.d.ts","sourceRoot":"","sources":["../../src/theme/theme-provider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,SAAS,EAMf,MAAM,OAAO,CAAA;AACd,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAEpD,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAA;AAEnD,UAAU,iBAAiB;IACzB,MAAM,EAAE,iBAAiB,CAAA;IACzB,SAAS,EAAE,CAAC,CAAC,EAAE,iBAAiB,KAAK,IAAI,CAAA;IACzC,SAAS,EAAE,SAAS,CAAA;IACpB,YAAY,EAAE,CAAC,CAAC,EAAE,SAAS,KAAK,IAAI,CAAA;IACpC,YAAY,EAAE,OAAO,GAAG,MAAM,CAAA;CAC/B;AAyBD,wBAAgB,aAAa,CAAC,EAC5B,QAAQ,EACR,aAAuB,EACvB,gBAA2B,GAC5B,EAAE;IACD,QAAQ,EAAE,SAAS,CAAA;IACnB,aAAa,CAAC,EAAE,iBAAiB,CAAA;IACjC,gBAAgB,CAAC,EAAE,SAAS,CAAA;CAC7B,2CAwCA;AAED,wBAAgB,QAAQ,IAAI,iBAAiB,CAM5C"}
@@ -0,0 +1,55 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createContext, useCallback, useContext, useEffect, useMemo, useState, } from 'react';
3
+ const ThemeContext = createContext(null);
4
+ const STORAGE_KEY = 'quadrokit:theme';
5
+ const MODE_KEY = 'quadrokit:color-mode';
6
+ function readStored(key, fallback) {
7
+ if (typeof window === 'undefined') {
8
+ return fallback;
9
+ }
10
+ try {
11
+ const raw = localStorage.getItem(key);
12
+ return raw ? JSON.parse(raw) : fallback;
13
+ }
14
+ catch {
15
+ return fallback;
16
+ }
17
+ }
18
+ function applyDom(preset, resolved) {
19
+ const root = document.documentElement;
20
+ root.dataset.quadroTheme = preset;
21
+ root.classList.toggle('dark', resolved === 'dark');
22
+ }
23
+ export function ThemeProvider({ children, defaultPreset = 'ocean', defaultColorMode = 'system', }) {
24
+ const [preset, setPresetState] = useState(() => readStored(STORAGE_KEY, defaultPreset));
25
+ const [colorMode, setColorModeState] = useState(() => readStored(MODE_KEY, defaultColorMode));
26
+ const [systemDark, setSystemDark] = useState(false);
27
+ useEffect(() => {
28
+ const mq = window.matchMedia('(prefers-color-scheme: dark)');
29
+ const sync = () => setSystemDark(mq.matches);
30
+ sync();
31
+ mq.addEventListener('change', sync);
32
+ return () => mq.removeEventListener('change', sync);
33
+ }, []);
34
+ const resolvedMode = colorMode === 'system' ? (systemDark ? 'dark' : 'light') : colorMode;
35
+ useEffect(() => {
36
+ applyDom(preset, resolvedMode);
37
+ }, [preset, resolvedMode]);
38
+ const setPreset = useCallback((p) => {
39
+ setPresetState(p);
40
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(p));
41
+ }, []);
42
+ const setColorMode = useCallback((m) => {
43
+ setColorModeState(m);
44
+ localStorage.setItem(MODE_KEY, JSON.stringify(m));
45
+ }, []);
46
+ const value = useMemo(() => ({ preset, setPreset, colorMode, setColorMode, resolvedMode }), [preset, setPreset, colorMode, setColorMode, resolvedMode]);
47
+ return _jsx(ThemeContext.Provider, { value: value, children: children });
48
+ }
49
+ export function useTheme() {
50
+ const ctx = useContext(ThemeContext);
51
+ if (!ctx) {
52
+ throw new Error('useTheme must be used within ThemeProvider');
53
+ }
54
+ return ctx;
55
+ }
@@ -0,0 +1,4 @@
1
+ export declare const quadroThemes: readonly ["ocean", "forest", "sunset", "mono"];
2
+ export type QuadroThemePreset = (typeof quadroThemes)[number];
3
+ export declare const themeLabels: Record<QuadroThemePreset, string>;
4
+ //# sourceMappingURL=themes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"themes.d.ts","sourceRoot":"","sources":["../../src/theme/themes.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,YAAY,gDAAiD,CAAA;AAC1E,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAA;AAE7D,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,iBAAiB,EAAE,MAAM,CAKzD,CAAA"}
@@ -0,0 +1,7 @@
1
+ export const quadroThemes = ['ocean', 'forest', 'sunset', 'mono'];
2
+ export const themeLabels = {
3
+ ocean: 'Ocean',
4
+ forest: 'Forest',
5
+ sunset: 'Sunset',
6
+ mono: 'Mono',
7
+ };
package/package.json CHANGED
@@ -1,23 +1,30 @@
1
1
  {
2
2
  "name": "@quadrokit/ui",
3
- "version": "0.1.0",
3
+ "version": "0.2.2",
4
4
  "private": false,
5
5
  "type": "module",
6
+ "main": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
6
8
  "sideEffects": [
7
- "**/*.css"
9
+ "./dist/styles.css"
8
10
  ],
9
11
  "exports": {
10
12
  ".": {
11
- "types": "./src/index.ts",
12
- "import": "./src/index.ts"
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.mjs"
13
15
  },
14
- "./styles.css": "./src/styles.css",
16
+ "./styles.css": "./dist/styles.css",
15
17
  "./tailwind-preset": {
16
- "types": "./tailwind-preset.ts",
17
- "import": "./tailwind-preset.ts"
18
+ "types": "./dist/tailwind-preset.d.ts",
19
+ "import": "./dist/tailwind-preset.mjs"
18
20
  }
19
21
  },
22
+ "files": [
23
+ "dist"
24
+ ],
20
25
  "scripts": {
26
+ "build": "tsc -p tsconfig.build.json && bun ../../scripts/rename-dist-js-to-mjs.ts dist && cp src/styles.css dist/styles.css",
27
+ "prepublishOnly": "bun run build",
21
28
  "typecheck": "tsc -p tsconfig.json --noEmit"
22
29
  },
23
30
  "dependencies": {
@@ -1,45 +0,0 @@
1
- import { Slot } from '@radix-ui/react-slot'
2
- import { cva, type VariantProps } from 'class-variance-authority'
3
- import type { ButtonHTMLAttributes } from 'react'
4
- import { forwardRef } from 'react'
5
- import { cn } from '../lib/utils.js'
6
-
7
- const variants = cva(
8
- 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
9
- {
10
- variants: {
11
- variant: {
12
- default: 'bg-primary text-primary-foreground shadow hover:bg-primary/90',
13
- secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
14
- outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
15
- ghost: 'hover:bg-accent hover:text-accent-foreground',
16
- destructive: 'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90',
17
- link: 'text-primary underline-offset-4 hover:underline',
18
- },
19
- size: {
20
- default: 'h-9 px-4 py-2',
21
- sm: 'h-8 rounded-md px-3 text-xs',
22
- lg: 'h-10 rounded-md px-8',
23
- icon: 'h-9 w-9',
24
- },
25
- },
26
- defaultVariants: {
27
- variant: 'default',
28
- size: 'default',
29
- },
30
- }
31
- )
32
-
33
- export interface ButtonProps
34
- extends ButtonHTMLAttributes<HTMLButtonElement>,
35
- VariantProps<typeof variants> {
36
- asChild?: boolean
37
- }
38
-
39
- export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
40
- ({ className, variant, size, asChild = false, ...props }, ref) => {
41
- const Comp = asChild ? Slot : 'button'
42
- return <Comp className={cn(variants({ variant, size, className }))} ref={ref} {...props} />
43
- }
44
- )
45
- Button.displayName = 'Button'
@@ -1,50 +0,0 @@
1
- import type { HTMLAttributes } from 'react'
2
- import { forwardRef } from 'react'
3
- import { cn } from '../lib/utils.js'
4
-
5
- export const Card = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
6
- ({ className, ...props }, ref) => (
7
- <div
8
- ref={ref}
9
- className={cn(
10
- 'rounded-xl border border-border bg-card text-card-foreground shadow-sm',
11
- className
12
- )}
13
- {...props}
14
- />
15
- )
16
- )
17
- Card.displayName = 'Card'
18
-
19
- export const CardHeader = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
20
- ({ className, ...props }, ref) => (
21
- <div ref={ref} className={cn('flex flex-col space-y-1.5 p-6', className)} {...props} />
22
- )
23
- )
24
- CardHeader.displayName = 'CardHeader'
25
-
26
- export const CardTitle = forwardRef<HTMLHeadingElement, HTMLAttributes<HTMLHeadingElement>>(
27
- ({ className, ...props }, ref) => (
28
- <h3
29
- ref={ref}
30
- className={cn('font-semibold leading-none tracking-tight', className)}
31
- {...props}
32
- />
33
- )
34
- )
35
- CardTitle.displayName = 'CardTitle'
36
-
37
- export const CardDescription = forwardRef<
38
- HTMLParagraphElement,
39
- HTMLAttributes<HTMLParagraphElement>
40
- >(({ className, ...props }, ref) => (
41
- <p ref={ref} className={cn('text-sm text-muted-foreground', className)} {...props} />
42
- ))
43
- CardDescription.displayName = 'CardDescription'
44
-
45
- export const CardContent = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
46
- ({ className, ...props }, ref) => (
47
- <div ref={ref} className={cn('p-6 pt-0', className)} {...props} />
48
- )
49
- )
50
- CardContent.displayName = 'CardContent'
@@ -1,22 +0,0 @@
1
- import type { InputHTMLAttributes } from 'react'
2
- import { forwardRef } from 'react'
3
- import { cn } from '../lib/utils.js'
4
-
5
- export interface InputProps extends InputHTMLAttributes<HTMLInputElement> {}
6
-
7
- export const Input = forwardRef<HTMLInputElement, InputProps>(
8
- ({ className, type, ...props }, ref) => {
9
- return (
10
- <input
11
- type={type}
12
- className={cn(
13
- 'flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50',
14
- className
15
- )}
16
- ref={ref}
17
- {...props}
18
- />
19
- )
20
- }
21
- )
22
- Input.displayName = 'Input'
@@ -1,18 +0,0 @@
1
- import type { LabelHTMLAttributes } from 'react'
2
- import { forwardRef } from 'react'
3
- import { cn } from '../lib/utils.js'
4
-
5
- export const Label = forwardRef<HTMLLabelElement, LabelHTMLAttributes<HTMLLabelElement>>(
6
- ({ className, ...props }, ref) => (
7
- // biome-ignore lint/a11y/noLabelWithoutControl: shadcn-style primitive; use htmlFor at call sites
8
- <label
9
- ref={ref}
10
- className={cn(
11
- 'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70',
12
- className
13
- )}
14
- {...props}
15
- />
16
- )
17
- )
18
- Label.displayName = 'Label'
@@ -1,46 +0,0 @@
1
- import { useTheme } from '../theme/theme-provider.js'
2
- import { type QuadroThemePreset, quadroThemes, themeLabels } from '../theme/themes.js'
3
- import { Button } from './button.js'
4
-
5
- export function ThemeToolbar() {
6
- const { preset, setPreset, colorMode, setColorMode, resolvedMode } = useTheme()
7
-
8
- return (
9
- <div className="flex flex-wrap items-center gap-2 rounded-lg border border-border bg-card/60 p-2 text-sm backdrop-blur">
10
- <span className="text-muted-foreground px-1">Theme</span>
11
- {quadroThemes.map((t: QuadroThemePreset) => (
12
- <Button
13
- key={t}
14
- type="button"
15
- size="sm"
16
- variant={preset === t ? 'default' : 'outline'}
17
- onClick={() => setPreset(t)}
18
- >
19
- {themeLabels[t]}
20
- </Button>
21
- ))}
22
- <span className="mx-2 hidden h-4 w-px bg-border sm:inline-block" />
23
- <span className="text-muted-foreground px-1">Mode</span>
24
- {(
25
- [
26
- ['light', 'Light'],
27
- ['dark', 'Dark'],
28
- ['system', 'System'],
29
- ] as const
30
- ).map(([m, label]) => (
31
- <Button
32
- key={m}
33
- type="button"
34
- size="sm"
35
- variant={colorMode === m ? 'default' : 'ghost'}
36
- onClick={() => setColorMode(m)}
37
- >
38
- {label}
39
- </Button>
40
- ))}
41
- <span className="ml-auto text-xs text-muted-foreground">
42
- {resolvedMode === 'dark' ? 'Dark' : 'Light'}
43
- </span>
44
- </div>
45
- )
46
- }
package/src/index.ts DELETED
@@ -1,22 +0,0 @@
1
- export { Button, type ButtonProps } from './components/button.js'
2
- export {
3
- Card,
4
- CardContent,
5
- CardDescription,
6
- CardHeader,
7
- CardTitle,
8
- } from './components/card.js'
9
- export { Input, type InputProps } from './components/input.js'
10
- export { Label } from './components/label.js'
11
- export { ThemeToolbar } from './components/theme-toolbar.js'
12
- export { cn } from './lib/utils.js'
13
- export {
14
- type ColorMode,
15
- ThemeProvider,
16
- useTheme,
17
- } from './theme/theme-provider.js'
18
- export {
19
- type QuadroThemePreset,
20
- quadroThemes,
21
- themeLabels,
22
- } from './theme/themes.js'
package/src/lib/utils.ts DELETED
@@ -1,6 +0,0 @@
1
- import { type ClassValue, clsx } from 'clsx'
2
- import { twMerge } from 'tailwind-merge'
3
-
4
- export function cn(...inputs: ClassValue[]) {
5
- return twMerge(clsx(inputs))
6
- }
@@ -1,101 +0,0 @@
1
- import {
2
- createContext,
3
- type ReactNode,
4
- useCallback,
5
- useContext,
6
- useEffect,
7
- useMemo,
8
- useState,
9
- } from 'react'
10
- import type { QuadroThemePreset } from './themes.js'
11
-
12
- export type ColorMode = 'light' | 'dark' | 'system'
13
-
14
- interface ThemeContextValue {
15
- preset: QuadroThemePreset
16
- setPreset: (p: QuadroThemePreset) => void
17
- colorMode: ColorMode
18
- setColorMode: (m: ColorMode) => void
19
- resolvedMode: 'light' | 'dark'
20
- }
21
-
22
- const ThemeContext = createContext<ThemeContextValue | null>(null)
23
-
24
- const STORAGE_KEY = 'quadrokit:theme'
25
- const MODE_KEY = 'quadrokit:color-mode'
26
-
27
- function readStored<T>(key: string, fallback: T): T {
28
- if (typeof window === 'undefined') {
29
- return fallback
30
- }
31
- try {
32
- const raw = localStorage.getItem(key)
33
- return raw ? (JSON.parse(raw) as T) : fallback
34
- } catch {
35
- return fallback
36
- }
37
- }
38
-
39
- function applyDom(preset: QuadroThemePreset, resolved: 'light' | 'dark') {
40
- const root = document.documentElement
41
- root.dataset.quadroTheme = preset
42
- root.classList.toggle('dark', resolved === 'dark')
43
- }
44
-
45
- export function ThemeProvider({
46
- children,
47
- defaultPreset = 'ocean',
48
- defaultColorMode = 'system',
49
- }: {
50
- children: ReactNode
51
- defaultPreset?: QuadroThemePreset
52
- defaultColorMode?: ColorMode
53
- }) {
54
- const [preset, setPresetState] = useState<QuadroThemePreset>(() =>
55
- readStored(STORAGE_KEY, defaultPreset)
56
- )
57
- const [colorMode, setColorModeState] = useState<ColorMode>(() =>
58
- readStored(MODE_KEY, defaultColorMode)
59
- )
60
- const [systemDark, setSystemDark] = useState(false)
61
-
62
- useEffect(() => {
63
- const mq = window.matchMedia('(prefers-color-scheme: dark)')
64
- const sync = () => setSystemDark(mq.matches)
65
- sync()
66
- mq.addEventListener('change', sync)
67
- return () => mq.removeEventListener('change', sync)
68
- }, [])
69
-
70
- const resolvedMode: 'light' | 'dark' =
71
- colorMode === 'system' ? (systemDark ? 'dark' : 'light') : colorMode
72
-
73
- useEffect(() => {
74
- applyDom(preset, resolvedMode)
75
- }, [preset, resolvedMode])
76
-
77
- const setPreset = useCallback((p: QuadroThemePreset) => {
78
- setPresetState(p)
79
- localStorage.setItem(STORAGE_KEY, JSON.stringify(p))
80
- }, [])
81
-
82
- const setColorMode = useCallback((m: ColorMode) => {
83
- setColorModeState(m)
84
- localStorage.setItem(MODE_KEY, JSON.stringify(m))
85
- }, [])
86
-
87
- const value = useMemo(
88
- () => ({ preset, setPreset, colorMode, setColorMode, resolvedMode }),
89
- [preset, setPreset, colorMode, setColorMode, resolvedMode]
90
- )
91
-
92
- return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>
93
- }
94
-
95
- export function useTheme(): ThemeContextValue {
96
- const ctx = useContext(ThemeContext)
97
- if (!ctx) {
98
- throw new Error('useTheme must be used within ThemeProvider')
99
- }
100
- return ctx
101
- }
@@ -1,9 +0,0 @@
1
- export const quadroThemes = ['ocean', 'forest', 'sunset', 'mono'] as const
2
- export type QuadroThemePreset = (typeof quadroThemes)[number]
3
-
4
- export const themeLabels: Record<QuadroThemePreset, string> = {
5
- ocean: 'Ocean',
6
- forest: 'Forest',
7
- sunset: 'Sunset',
8
- mono: 'Mono',
9
- }
@@ -1,51 +0,0 @@
1
- import type { Config } from 'tailwindcss'
2
-
3
- const preset: Partial<Config> = {
4
- darkMode: 'class',
5
- theme: {
6
- extend: {
7
- borderRadius: {
8
- lg: 'var(--radius)',
9
- md: 'calc(var(--radius) - 2px)',
10
- sm: 'calc(var(--radius) - 4px)',
11
- },
12
- colors: {
13
- border: 'hsl(var(--border) / <alpha-value>)',
14
- input: 'hsl(var(--input) / <alpha-value>)',
15
- ring: 'hsl(var(--ring) / <alpha-value>)',
16
- background: 'hsl(var(--background) / <alpha-value>)',
17
- foreground: 'hsl(var(--foreground) / <alpha-value>)',
18
- primary: {
19
- DEFAULT: 'hsl(var(--primary) / <alpha-value>)',
20
- foreground: 'hsl(var(--primary-foreground) / <alpha-value>)',
21
- },
22
- secondary: {
23
- DEFAULT: 'hsl(var(--secondary) / <alpha-value>)',
24
- foreground: 'hsl(var(--secondary-foreground) / <alpha-value>)',
25
- },
26
- destructive: {
27
- DEFAULT: 'hsl(var(--destructive) / <alpha-value>)',
28
- foreground: 'hsl(var(--destructive-foreground) / <alpha-value>)',
29
- },
30
- muted: {
31
- DEFAULT: 'hsl(var(--muted) / <alpha-value>)',
32
- foreground: 'hsl(var(--muted-foreground) / <alpha-value>)',
33
- },
34
- accent: {
35
- DEFAULT: 'hsl(var(--accent) / <alpha-value>)',
36
- foreground: 'hsl(var(--accent-foreground) / <alpha-value>)',
37
- },
38
- popover: {
39
- DEFAULT: 'hsl(var(--popover) / <alpha-value>)',
40
- foreground: 'hsl(var(--popover-foreground) / <alpha-value>)',
41
- },
42
- card: {
43
- DEFAULT: 'hsl(var(--card) / <alpha-value>)',
44
- foreground: 'hsl(var(--card-foreground) / <alpha-value>)',
45
- },
46
- },
47
- },
48
- },
49
- }
50
-
51
- export default preset
package/tsconfig.json DELETED
@@ -1,9 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.base.json",
3
- "compilerOptions": {
4
- "rootDir": "src",
5
- "noEmit": true,
6
- "lib": ["ES2022", "DOM", "DOM.Iterable"]
7
- },
8
- "include": ["src/**/*.ts", "src/**/*.tsx"]
9
- }
File without changes