@fracazo/design-system 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DESIGN.md +361 -8
- package/README.md +27 -5
- package/dist/src/cn.d.ts +8 -0
- package/dist/src/cn.js +11 -0
- package/dist/src/index.d.ts +26 -0
- package/dist/src/index.js +26 -0
- package/dist/src/ui/accordion.d.ts +14 -0
- package/dist/src/ui/accordion.js +25 -0
- package/dist/src/ui/button.d.ts +27 -0
- package/dist/src/ui/button.js +67 -0
- package/dist/src/ui/calendar.d.ts +16 -0
- package/dist/src/ui/calendar.js +84 -0
- package/dist/src/ui/card.d.ts +17 -0
- package/dist/src/ui/card.js +32 -0
- package/dist/src/ui/checkbox.d.ts +12 -0
- package/dist/src/ui/checkbox.js +17 -0
- package/dist/src/ui/dialog.d.ts +23 -0
- package/dist/src/ui/dialog.js +44 -0
- package/dist/src/ui/form.d.ts +33 -0
- package/dist/src/ui/form.js +68 -0
- package/dist/src/ui/input.d.ts +12 -0
- package/dist/src/ui/input.js +19 -0
- package/dist/src/ui/label.d.ts +11 -0
- package/dist/src/ui/label.js +15 -0
- package/dist/src/ui/popover.d.ts +17 -0
- package/dist/src/ui/popover.js +33 -0
- package/dist/src/ui/progress.d.ts +11 -0
- package/dist/src/ui/progress.js +15 -0
- package/dist/src/ui/radio-group.d.ts +12 -0
- package/dist/src/ui/radio-group.js +19 -0
- package/dist/src/ui/select.d.ts +23 -0
- package/dist/src/ui/select.js +46 -0
- package/dist/src/ui/sheet.d.ts +23 -0
- package/dist/src/ui/sheet.js +49 -0
- package/dist/src/ui/sortable-list.d.ts +26 -0
- package/dist/src/ui/sortable-list.js +92 -0
- package/dist/src/ui/tabs.d.ts +17 -0
- package/dist/src/ui/tabs.js +31 -0
- package/dist/src/ui/textarea.d.ts +10 -0
- package/dist/src/ui/textarea.js +13 -0
- package/package.json +36 -2
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Dialog as SheetPrimitive } from "radix-ui";
|
|
3
|
+
/**
|
|
4
|
+
* Sliding panel (Radix). In this product it is used exclusively as a
|
|
5
|
+
* mobile bottom sheet.
|
|
6
|
+
*
|
|
7
|
+
* Use for: supplementary mobile content: plan preview, question info,
|
|
8
|
+
* did-you-know detail. Callers add rounded-t-xl and a max height.
|
|
9
|
+
* Avoid when: content must persist beside the page on desktop; render an
|
|
10
|
+
* aside instead (see DidYouKnowBar's breakpoint split).
|
|
11
|
+
*/
|
|
12
|
+
declare function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>): React.JSX.Element;
|
|
13
|
+
declare function SheetTrigger({ ...props }: React.ComponentProps<typeof SheetPrimitive.Trigger>): React.JSX.Element;
|
|
14
|
+
declare function SheetClose({ ...props }: React.ComponentProps<typeof SheetPrimitive.Close>): React.JSX.Element;
|
|
15
|
+
declare function SheetContent({ className, children, side, showCloseButton, ...props }: React.ComponentProps<typeof SheetPrimitive.Content> & {
|
|
16
|
+
side?: "top" | "right" | "bottom" | "left";
|
|
17
|
+
showCloseButton?: boolean;
|
|
18
|
+
}): React.JSX.Element;
|
|
19
|
+
declare function SheetHeader({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
20
|
+
declare function SheetFooter({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
21
|
+
declare function SheetTitle({ className, ...props }: React.ComponentProps<typeof SheetPrimitive.Title>): React.JSX.Element;
|
|
22
|
+
declare function SheetDescription({ className, ...props }: React.ComponentProps<typeof SheetPrimitive.Description>): React.JSX.Element;
|
|
23
|
+
export { Sheet, SheetTrigger, SheetClose, SheetContent, SheetHeader, SheetFooter, SheetTitle, SheetDescription, };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { XIcon } from "lucide-react";
|
|
4
|
+
import { Dialog as SheetPrimitive } from "radix-ui";
|
|
5
|
+
import { cn } from "../cn.js";
|
|
6
|
+
/**
|
|
7
|
+
* Sliding panel (Radix). In this product it is used exclusively as a
|
|
8
|
+
* mobile bottom sheet.
|
|
9
|
+
*
|
|
10
|
+
* Use for: supplementary mobile content: plan preview, question info,
|
|
11
|
+
* did-you-know detail. Callers add rounded-t-xl and a max height.
|
|
12
|
+
* Avoid when: content must persist beside the page on desktop; render an
|
|
13
|
+
* aside instead (see DidYouKnowBar's breakpoint split).
|
|
14
|
+
*/
|
|
15
|
+
function Sheet({ ...props }) {
|
|
16
|
+
return _jsx(SheetPrimitive.Root, { "data-slot": "sheet", ...props });
|
|
17
|
+
}
|
|
18
|
+
function SheetTrigger({ ...props }) {
|
|
19
|
+
return _jsx(SheetPrimitive.Trigger, { "data-slot": "sheet-trigger", ...props });
|
|
20
|
+
}
|
|
21
|
+
function SheetClose({ ...props }) {
|
|
22
|
+
return _jsx(SheetPrimitive.Close, { "data-slot": "sheet-close", ...props });
|
|
23
|
+
}
|
|
24
|
+
function SheetPortal({ ...props }) {
|
|
25
|
+
return _jsx(SheetPrimitive.Portal, { "data-slot": "sheet-portal", ...props });
|
|
26
|
+
}
|
|
27
|
+
function SheetOverlay({ className, ...props }) {
|
|
28
|
+
return (_jsx(SheetPrimitive.Overlay, { "data-slot": "sheet-overlay", className: cn("data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50", className), ...props }));
|
|
29
|
+
}
|
|
30
|
+
function SheetContent({ className, children, side = "right", showCloseButton = true, ...props }) {
|
|
31
|
+
return (_jsxs(SheetPortal, { children: [_jsx(SheetOverlay, {}), _jsxs(SheetPrimitive.Content, { "data-slot": "sheet-content", className: cn("bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500", side === "right" &&
|
|
32
|
+
"data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm", side === "left" &&
|
|
33
|
+
"data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm", side === "top" &&
|
|
34
|
+
"data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b", side === "bottom" &&
|
|
35
|
+
"data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t", className), ...props, children: [children, showCloseButton && (_jsxs(SheetPrimitive.Close, { className: "ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none", children: [_jsx(XIcon, { className: "size-4" }), _jsx("span", { className: "sr-only", children: "Close" })] }))] })] }));
|
|
36
|
+
}
|
|
37
|
+
function SheetHeader({ className, ...props }) {
|
|
38
|
+
return (_jsx("div", { "data-slot": "sheet-header", className: cn("flex flex-col gap-1.5 p-4", className), ...props }));
|
|
39
|
+
}
|
|
40
|
+
function SheetFooter({ className, ...props }) {
|
|
41
|
+
return (_jsx("div", { "data-slot": "sheet-footer", className: cn("mt-auto flex flex-col gap-2 p-4", className), ...props }));
|
|
42
|
+
}
|
|
43
|
+
function SheetTitle({ className, ...props }) {
|
|
44
|
+
return (_jsx(SheetPrimitive.Title, { "data-slot": "sheet-title", className: cn("text-foreground font-semibold", className), ...props }));
|
|
45
|
+
}
|
|
46
|
+
function SheetDescription({ className, ...props }) {
|
|
47
|
+
return (_jsx(SheetPrimitive.Description, { "data-slot": "sheet-description", className: cn("text-muted-foreground text-sm", className), ...props }));
|
|
48
|
+
}
|
|
49
|
+
export { Sheet, SheetTrigger, SheetClose, SheetContent, SheetHeader, SheetFooter, SheetTitle, SheetDescription, };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface SortableItem {
|
|
2
|
+
id: string;
|
|
3
|
+
label: string;
|
|
4
|
+
}
|
|
5
|
+
export interface SortableListProps {
|
|
6
|
+
/** All available items (unranked pool) */
|
|
7
|
+
available: SortableItem[];
|
|
8
|
+
/** Currently ranked items (in order) */
|
|
9
|
+
ranked: SortableItem[];
|
|
10
|
+
/** Maximum items in the ranked list */
|
|
11
|
+
maxRanked: number;
|
|
12
|
+
/** Called when ranked list changes */
|
|
13
|
+
onRankedChange: (ranked: SortableItem[]) => void;
|
|
14
|
+
/** Label for the ranked zone */
|
|
15
|
+
rankedLabel?: string;
|
|
16
|
+
/** Label for the available zone */
|
|
17
|
+
availableLabel?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Drag-to-rank with two zones: a capped ranked list and a tap-to-add pool.
|
|
21
|
+
* Touch, mouse and keyboard support; drag is scoped to the grip handle.
|
|
22
|
+
*
|
|
23
|
+
* Use for: ranking a small capped set, like the Top 5 priorities editor.
|
|
24
|
+
* Avoid when: order does not matter; use MultiSelectCardGroup.
|
|
25
|
+
*/
|
|
26
|
+
export declare function SortableList({ available, ranked, maxRanked, onRankedChange, rankedLabel, availableLabel, }: SortableListProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
// =============================================================================
|
|
4
|
+
// SortableList
|
|
5
|
+
//
|
|
6
|
+
// Reusable drag-to-rank component for ranking a small capped set.
|
|
7
|
+
// Two zones: "Ranked" (sortable via @dnd-kit, max N items) and
|
|
8
|
+
// "Available" (tap to add). Touch + mouse + keyboard support.
|
|
9
|
+
//
|
|
10
|
+
// Drag listeners are scoped to the grip handle only so that taps on
|
|
11
|
+
// the remove button and item body work normally on touch devices.
|
|
12
|
+
// =============================================================================
|
|
13
|
+
import { useCallback, useId } from 'react';
|
|
14
|
+
import { DndContext, closestCenter, KeyboardSensor, PointerSensor, TouchSensor, useSensor, useSensors, } from '@dnd-kit/core';
|
|
15
|
+
import { SortableContext, sortableKeyboardCoordinates, useSortable, verticalListSortingStrategy, arrayMove, } from '@dnd-kit/sortable';
|
|
16
|
+
import { CSS } from '@dnd-kit/utilities';
|
|
17
|
+
import { cn } from '../cn.js';
|
|
18
|
+
// -----------------------------------------------------------------------------
|
|
19
|
+
// Drag Handle (visual grip icon; only this element receives drag listeners)
|
|
20
|
+
// -----------------------------------------------------------------------------
|
|
21
|
+
function DragHandle({ listeners, attributes, }) {
|
|
22
|
+
return (_jsx("button", { type: "button", className: "flex h-8 w-8 shrink-0 cursor-grab items-center justify-center rounded text-muted-foreground touch-none hover:text-foreground active:cursor-grabbing", "aria-label": "Drag to reorder", ...attributes, ...listeners, children: _jsxs("svg", { className: "h-4 w-4", viewBox: "0 0 16 16", fill: "currentColor", "aria-hidden": "true", children: [_jsx("circle", { cx: "5", cy: "3", r: "1.5" }), _jsx("circle", { cx: "11", cy: "3", r: "1.5" }), _jsx("circle", { cx: "5", cy: "8", r: "1.5" }), _jsx("circle", { cx: "11", cy: "8", r: "1.5" }), _jsx("circle", { cx: "5", cy: "13", r: "1.5" }), _jsx("circle", { cx: "11", cy: "13", r: "1.5" })] }) }));
|
|
23
|
+
}
|
|
24
|
+
// -----------------------------------------------------------------------------
|
|
25
|
+
// Sortable Item (ranked zone)
|
|
26
|
+
// -----------------------------------------------------------------------------
|
|
27
|
+
function SortableRankedItem({ item, rank, onRemove, }) {
|
|
28
|
+
const { attributes, listeners, setNodeRef, transform, transition, isDragging, } = useSortable({ id: item.id });
|
|
29
|
+
const style = {
|
|
30
|
+
transform: CSS.Transform.toString(transform),
|
|
31
|
+
transition,
|
|
32
|
+
};
|
|
33
|
+
return (_jsxs("div", { ref: setNodeRef, style: style, className: cn('flex items-center gap-2 rounded-lg border bg-card p-3 select-none', isDragging
|
|
34
|
+
? 'z-50 shadow-lg border-primary ring-2 ring-primary/20'
|
|
35
|
+
: 'border-border'), children: [_jsx("span", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-primary text-primary-foreground text-sm font-semibold", "aria-hidden": "true", children: rank }), _jsx(DragHandle, { listeners: listeners, attributes: attributes }), _jsx("span", { className: "flex-1 text-sm font-medium", children: item.label }), _jsx("button", { type: "button", onClick: onRemove, className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full text-muted-foreground hover:bg-destructive/10 hover:text-destructive transition-colors", "aria-label": `Remove ${item.label} from ranked list`, children: _jsx("svg", { className: "h-4 w-4", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "2", children: _jsx("path", { d: "M4 4l8 8M12 4l-8 8" }) }) })] }));
|
|
36
|
+
}
|
|
37
|
+
// -----------------------------------------------------------------------------
|
|
38
|
+
// Available Item (unranked pool)
|
|
39
|
+
// -----------------------------------------------------------------------------
|
|
40
|
+
function AvailableItem({ item, onAdd, disabled, }) {
|
|
41
|
+
return (_jsxs("button", { type: "button", onClick: onAdd, disabled: disabled, className: cn('flex w-full items-center gap-3 rounded-lg border border-dashed p-3 text-left transition-colors', disabled
|
|
42
|
+
? 'border-border/50 text-muted-foreground/50 cursor-not-allowed'
|
|
43
|
+
: 'border-border text-foreground hover:border-primary hover:bg-primary/5 cursor-pointer'), "aria-label": `Add ${item.label} to ranked list`, children: [_jsx("span", { className: cn('flex h-7 w-7 shrink-0 items-center justify-center rounded-full border text-sm', disabled
|
|
44
|
+
? 'border-border/50 text-muted-foreground/50'
|
|
45
|
+
: 'border-primary/30 text-primary'), "aria-hidden": "true", children: "+" }), _jsx("span", { className: "flex-1 text-sm", children: item.label })] }));
|
|
46
|
+
}
|
|
47
|
+
// -----------------------------------------------------------------------------
|
|
48
|
+
// Main Component
|
|
49
|
+
// -----------------------------------------------------------------------------
|
|
50
|
+
/**
|
|
51
|
+
* Drag-to-rank with two zones: a capped ranked list and a tap-to-add pool.
|
|
52
|
+
* Touch, mouse and keyboard support; drag is scoped to the grip handle.
|
|
53
|
+
*
|
|
54
|
+
* Use for: ranking a small capped set, like the Top 5 priorities editor.
|
|
55
|
+
* Avoid when: order does not matter; use MultiSelectCardGroup.
|
|
56
|
+
*/
|
|
57
|
+
export function SortableList({ available, ranked, maxRanked, onRankedChange, rankedLabel = 'Your top priorities', availableLabel = 'Available priorities', }) {
|
|
58
|
+
const dndId = useId();
|
|
59
|
+
const isFull = ranked.length >= maxRanked;
|
|
60
|
+
// Sensors: pointer (mouse), touch, keyboard
|
|
61
|
+
const sensors = useSensors(useSensor(PointerSensor, {
|
|
62
|
+
activationConstraint: { distance: 5 },
|
|
63
|
+
}), useSensor(TouchSensor, {
|
|
64
|
+
activationConstraint: { delay: 150, tolerance: 5 },
|
|
65
|
+
}), useSensor(KeyboardSensor, {
|
|
66
|
+
coordinateGetter: sortableKeyboardCoordinates,
|
|
67
|
+
}));
|
|
68
|
+
// Unranked items = available items not already in ranked
|
|
69
|
+
const rankedIds = new Set(ranked.map((r) => r.id));
|
|
70
|
+
const unranked = available.filter((a) => !rankedIds.has(a.id));
|
|
71
|
+
const handleDragEnd = useCallback((event) => {
|
|
72
|
+
const { active, over } = event;
|
|
73
|
+
if (!over || active.id === over.id)
|
|
74
|
+
return;
|
|
75
|
+
const oldIndex = ranked.findIndex((r) => r.id === active.id);
|
|
76
|
+
const newIndex = ranked.findIndex((r) => r.id === over.id);
|
|
77
|
+
if (oldIndex === -1 || newIndex === -1)
|
|
78
|
+
return;
|
|
79
|
+
onRankedChange(arrayMove(ranked, oldIndex, newIndex));
|
|
80
|
+
}, [ranked, onRankedChange]);
|
|
81
|
+
const handleAdd = useCallback((item) => {
|
|
82
|
+
if (isFull)
|
|
83
|
+
return;
|
|
84
|
+
onRankedChange([...ranked, item]);
|
|
85
|
+
}, [ranked, isFull, onRankedChange]);
|
|
86
|
+
const handleRemove = useCallback((id) => {
|
|
87
|
+
onRankedChange(ranked.filter((r) => r.id !== id));
|
|
88
|
+
}, [ranked, onRankedChange]);
|
|
89
|
+
return (_jsxs("div", { className: "space-y-6", children: [_jsxs("div", { children: [_jsxs("div", { className: "flex items-center justify-between mb-2", children: [_jsx("h3", { className: "text-sm font-medium text-foreground", children: rankedLabel }), _jsxs("span", { className: "text-xs text-muted-foreground tabular-nums", children: [ranked.length, "/", maxRanked] })] }), ranked.length === 0 ? (_jsx("div", { className: "rounded-lg border-2 border-dashed border-border/60 p-6 text-center", children: _jsxs("p", { className: "text-sm text-muted-foreground", children: ["Tap items below to add your top ", maxRanked] }) })) : (_jsx(DndContext, { id: dndId, sensors: sensors, collisionDetection: closestCenter, onDragEnd: handleDragEnd, children: _jsx(SortableContext, { items: ranked.map((r) => r.id), strategy: verticalListSortingStrategy, children: _jsx("div", { className: "space-y-2", role: "list", "aria-label": "Ranked priorities", children: ranked.map((item, index) => (_jsx(SortableRankedItem, { item: item, rank: index + 1, onRemove: () => handleRemove(item.id) }, item.id))) }) }) }))] }), unranked.length > 0 && (_jsxs("div", { children: [_jsx("h3", { className: "text-sm font-medium text-muted-foreground mb-1", children: availableLabel }), _jsx("p", { className: "text-xs text-muted-foreground mb-2", children: isFull
|
|
90
|
+
? `You\u2019ve chosen ${maxRanked}. Remove one above to swap in another.`
|
|
91
|
+
: `Choose up to ${maxRanked}. ${maxRanked - ranked.length} remaining.` }), _jsx("div", { className: "space-y-2", role: "list", "aria-label": "Available priorities", children: unranked.map((item) => (_jsx(AvailableItem, { item: item, onAdd: () => handleAdd(item), disabled: isFull }, item.id))) })] }))] }));
|
|
92
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Tabs as TabsPrimitive } from "radix-ui";
|
|
3
|
+
/**
|
|
4
|
+
* Segmented tab switcher (Radix) for swapping between views of the same thing.
|
|
5
|
+
*
|
|
6
|
+
* Use for: two or three peer views a reader flips between (overview against a
|
|
7
|
+
* detailed comparison), where every view is worth the same weight.
|
|
8
|
+
* Avoid when: the second view is secondary detail (use Accordion), or the
|
|
9
|
+
* content must exist for search engines; inactive panels are unmounted.
|
|
10
|
+
* Variants: none. The list renders as a pill-track segmented control; the
|
|
11
|
+
* active trigger lifts onto the card surface.
|
|
12
|
+
*/
|
|
13
|
+
declare function Tabs({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Root>): React.JSX.Element;
|
|
14
|
+
declare function TabsList({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.List>): React.JSX.Element;
|
|
15
|
+
declare function TabsTrigger({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Trigger>): React.JSX.Element;
|
|
16
|
+
declare function TabsContent({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Content>): React.JSX.Element;
|
|
17
|
+
export { Tabs, TabsList, TabsTrigger, TabsContent };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { Tabs as TabsPrimitive } from "radix-ui";
|
|
4
|
+
import { cn } from "../cn.js";
|
|
5
|
+
/**
|
|
6
|
+
* Segmented tab switcher (Radix) for swapping between views of the same thing.
|
|
7
|
+
*
|
|
8
|
+
* Use for: two or three peer views a reader flips between (overview against a
|
|
9
|
+
* detailed comparison), where every view is worth the same weight.
|
|
10
|
+
* Avoid when: the second view is secondary detail (use Accordion), or the
|
|
11
|
+
* content must exist for search engines; inactive panels are unmounted.
|
|
12
|
+
* Variants: none. The list renders as a pill-track segmented control; the
|
|
13
|
+
* active trigger lifts onto the card surface.
|
|
14
|
+
*/
|
|
15
|
+
function Tabs({ className, ...props }) {
|
|
16
|
+
return (_jsx(TabsPrimitive.Root, { "data-slot": "tabs", className: cn("flex flex-col gap-6", className), ...props }));
|
|
17
|
+
}
|
|
18
|
+
function TabsList({ className, ...props }) {
|
|
19
|
+
return (_jsx(TabsPrimitive.List, { "data-slot": "tabs-list", className: cn(
|
|
20
|
+
// p-1 on a rounded-20 track puts the triggers on rounded-2xl, per the
|
|
21
|
+
// concentric rule (md + p-2.5 is the documented neighbour; this pairing
|
|
22
|
+
// holds the same relationship one step in).
|
|
23
|
+
"inline-flex w-fit items-center gap-1 rounded-20 bg-secondary p-1", className), ...props }));
|
|
24
|
+
}
|
|
25
|
+
function TabsTrigger({ className, ...props }) {
|
|
26
|
+
return (_jsx(TabsPrimitive.Trigger, { "data-slot": "tabs-trigger", className: cn("cursor-pointer inline-flex items-center justify-center rounded-2xl px-4 py-2 text-[14.5px] font-medium whitespace-nowrap", "text-muted-foreground transition-[background-color,color,box-shadow]", "hover:text-foreground", "focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50", "disabled:pointer-events-none disabled:opacity-50", "data-[state=active]:bg-card data-[state=active]:text-foreground data-[state=active]:shadow-warm-sm", className), ...props }));
|
|
27
|
+
}
|
|
28
|
+
function TabsContent({ className, ...props }) {
|
|
29
|
+
return (_jsx(TabsPrimitive.Content, { "data-slot": "tabs-content", className: cn("outline-none", className), ...props }));
|
|
30
|
+
}
|
|
31
|
+
export { Tabs, TabsList, TabsTrigger, TabsContent };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Multi-line free text with content-based auto-grow.
|
|
4
|
+
*
|
|
5
|
+
* Use for: long-form answers ("anything else your team should know").
|
|
6
|
+
* Avoid when: the answer is one line; use Input, which keeps mobile
|
|
7
|
+
* keyboards and validation simpler.
|
|
8
|
+
*/
|
|
9
|
+
declare function Textarea({ className, ...props }: React.ComponentProps<"textarea">): React.JSX.Element;
|
|
10
|
+
export { Textarea };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../cn.js";
|
|
3
|
+
/**
|
|
4
|
+
* Multi-line free text with content-based auto-grow.
|
|
5
|
+
*
|
|
6
|
+
* Use for: long-form answers ("anything else your team should know").
|
|
7
|
+
* Avoid when: the answer is one line; use Input, which keeps mobile
|
|
8
|
+
* keyboards and validation simpler.
|
|
9
|
+
*/
|
|
10
|
+
function Textarea({ className, ...props }) {
|
|
11
|
+
return (_jsx("textarea", { "data-slot": "textarea", className: cn("border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-card px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm", className), ...props }));
|
|
12
|
+
}
|
|
13
|
+
export { Textarea };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fracazo/design-system",
|
|
3
|
-
"version": "0.1
|
|
4
|
-
"description": "Roles, brand contract and
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "Roles, brand contract, guardrails and shadcn-based components for a warm, evidence-led product design system. Each product supplies a brand file; the system stays the same.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Alex Fracazo",
|
|
7
7
|
"repository": {
|
|
@@ -20,6 +20,14 @@
|
|
|
20
20
|
"DESIGN.md"
|
|
21
21
|
],
|
|
22
22
|
"exports": {
|
|
23
|
+
".": {
|
|
24
|
+
"types": "./dist/src/index.d.ts",
|
|
25
|
+
"default": "./dist/src/index.js"
|
|
26
|
+
},
|
|
27
|
+
"./ui/*": {
|
|
28
|
+
"types": "./dist/src/ui/*.d.ts",
|
|
29
|
+
"default": "./dist/src/ui/*.js"
|
|
30
|
+
},
|
|
23
31
|
"./roles.css": "./css/roles.css",
|
|
24
32
|
"./eslint": {
|
|
25
33
|
"types": "./dist/guardrails/eslint.d.ts",
|
|
@@ -32,12 +40,38 @@
|
|
|
32
40
|
"ds-build-brand-css": "dist/guardrails/build-brand-css.js"
|
|
33
41
|
},
|
|
34
42
|
"peerDependencies": {
|
|
43
|
+
"@dnd-kit/core": "^6.3.1",
|
|
44
|
+
"@dnd-kit/sortable": "^10.0.0",
|
|
45
|
+
"@dnd-kit/utilities": "^3.2.2",
|
|
46
|
+
"class-variance-authority": "^0.7.1",
|
|
47
|
+
"clsx": "^2.1.1",
|
|
48
|
+
"lucide-react": "^0.564.0",
|
|
49
|
+
"radix-ui": "^1.4.3",
|
|
50
|
+
"react": "^19",
|
|
51
|
+
"react-day-picker": "^9.13.2",
|
|
52
|
+
"react-hook-form": "^7.71.1",
|
|
53
|
+
"tailwind-merge": "^3.4.1",
|
|
35
54
|
"tailwindcss": "^4"
|
|
36
55
|
},
|
|
37
56
|
"devDependencies": {
|
|
57
|
+
"@dnd-kit/core": "^6.3.1",
|
|
58
|
+
"@dnd-kit/sortable": "^10.0.0",
|
|
59
|
+
"@dnd-kit/utilities": "^3.2.2",
|
|
38
60
|
"@types/node": "^22",
|
|
61
|
+
"@types/react": "^19",
|
|
62
|
+
"class-variance-authority": "^0.7.1",
|
|
63
|
+
"clsx": "^2.1.1",
|
|
64
|
+
"lucide-react": "^0.564.0",
|
|
65
|
+
"radix-ui": "^1.4.3",
|
|
66
|
+
"react": "^19",
|
|
67
|
+
"react-day-picker": "^9.13.2",
|
|
68
|
+
"react-hook-form": "^7.71.1",
|
|
69
|
+
"tailwind-merge": "^3.4.1",
|
|
39
70
|
"typescript": "^5"
|
|
40
71
|
},
|
|
72
|
+
"sideEffects": [
|
|
73
|
+
"*.css"
|
|
74
|
+
],
|
|
41
75
|
"scripts": {
|
|
42
76
|
"build": "tsc -p tsconfig.json",
|
|
43
77
|
"check": "tsc -p tsconfig.json --noEmit"
|