ruler-components 0.1.0 → 0.1.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.
- package/dist/index.d.mts +51 -0
- package/dist/index.d.ts +51 -0
- package/dist/index.js +534 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +498 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +21 -2
- package/AGENTS.md +0 -9
- package/CLAUDE.md +0 -1
- package/app/docs/[slug]/page.tsx +0 -147
- package/app/docs/layout.tsx +0 -26
- package/app/docs/page.tsx +0 -148
- package/app/favicon.ico +0 -0
- package/app/globals.css +0 -168
- package/app/icon.png +0 -0
- package/app/layout.tsx +0 -90
- package/app/opengraph-image.tsx +0 -10
- package/app/page.tsx +0 -34
- package/app/twitter-image.tsx +0 -10
- package/components/docs/accordion-doc-page.tsx +0 -352
- package/components/docs/alert-doc-page.tsx +0 -293
- package/components/docs/allinput-doc-page.tsx +0 -183
- package/components/docs/avatar-doc-page.tsx +0 -424
- package/components/docs/badge-doc-page.tsx +0 -363
- package/components/docs/button-doc-page.tsx +0 -406
- package/components/docs/card-doc-page.tsx +0 -364
- package/components/docs/checkbox-doc-page.tsx +0 -277
- package/components/docs/colors-doc-page.tsx +0 -430
- package/components/docs/dialog-doc-page.tsx +0 -364
- package/components/docs/docs-footer.tsx +0 -11
- package/components/docs/docs-mobile-menu-bar.tsx +0 -22
- package/components/docs/docs-mobile-nav-context.tsx +0 -27
- package/components/docs/docs-page.tsx +0 -263
- package/components/docs/docs-search.tsx +0 -303
- package/components/docs/docs-sidebar.tsx +0 -165
- package/components/docs/docs-topbar.tsx +0 -32
- package/components/docs/dropdown-doc-page.tsx +0 -522
- package/components/docs/getting-started-doc-page.tsx +0 -205
- package/components/docs/label-doc-page.tsx +0 -173
- package/components/docs/loaders-doc-page.tsx +0 -244
- package/components/docs/menu-doc-page.tsx +0 -373
- package/components/docs/modal-doc-page.tsx +0 -436
- package/components/docs/next-steps.tsx +0 -53
- package/components/docs/pagination-doc-page.tsx +0 -316
- package/components/docs/progress-doc-page.tsx +0 -387
- package/components/docs/radio-doc-page.tsx +0 -270
- package/components/docs/skeleton-doc-page.tsx +0 -230
- package/components/docs/stepper-doc-page.tsx +0 -309
- package/components/docs/switch-doc-page.tsx +0 -264
- package/components/docs/table-doc-page.tsx +0 -689
- package/components/docs/tabs-doc-page.tsx +0 -359
- package/components/docs/theming-doc-page.tsx +0 -243
- package/components/docs/tooltip-doc-page.tsx +0 -357
- package/components/docs/usage-section.tsx +0 -21
- package/components/icons/discord.tsx +0 -9
- package/components/icons/github.tsx +0 -9
- package/components/icons/x.tsx +0 -9
- package/components/installation-section.tsx +0 -34
- package/components/landing/feature-highlights.tsx +0 -105
- package/components/landing/footer-strip.tsx +0 -45
- package/components/landing/hero-card.tsx +0 -14
- package/components/landing/hero-spotlight.tsx +0 -126
- package/components/landing/hero.tsx +0 -64
- package/components/landing/navbar.tsx +0 -44
- package/components/landing/quick-start-section.tsx +0 -59
- package/components/landing/site-footer.tsx +0 -55
- package/components/theme-provider.tsx +0 -60
- package/components/ui/accordion.tsx +0 -174
- package/components/ui/alert.tsx +0 -103
- package/components/ui/allinput.tsx +0 -210
- package/components/ui/avatar.tsx +0 -179
- package/components/ui/badge.tsx +0 -52
- package/components/ui/button.tsx +0 -75
- package/components/ui/card.tsx +0 -105
- package/components/ui/checkbox.tsx +0 -130
- package/components/ui/code-block.tsx +0 -60
- package/components/ui/data-table.tsx +0 -235
- package/components/ui/dialog.tsx +0 -173
- package/components/ui/dropdown.tsx +0 -78
- package/components/ui/feature-card.tsx +0 -38
- package/components/ui/input.tsx +0 -44
- package/components/ui/label.tsx +0 -110
- package/components/ui/loaders.tsx +0 -139
- package/components/ui/menu.tsx +0 -118
- package/components/ui/modal.tsx +0 -399
- package/components/ui/package-manager-tabs.tsx +0 -40
- package/components/ui/pagination.tsx +0 -200
- package/components/ui/progress.tsx +0 -134
- package/components/ui/radio.tsx +0 -262
- package/components/ui/skeleton.tsx +0 -102
- package/components/ui/steppers.tsx +0 -309
- package/components/ui/switch.tsx +0 -153
- package/components/ui/table.tsx +0 -58
- package/components/ui/tabs.tsx +0 -166
- package/components/ui/theme-toggle.tsx +0 -60
- package/components/ui/tooltip.tsx +0 -238
- package/data/color-palette.ts +0 -35
- package/data/docs-nav.ts +0 -84
- package/eslint.config.mjs +0 -18
- package/index.ts +0 -8
- package/lib/cn.ts +0 -4
- package/lib/color.ts +0 -85
- package/lib/og-image.tsx +0 -138
- package/lib/theme.ts +0 -33
- package/next.config.ts +0 -7
- package/postcss.config.mjs +0 -7
- package/public/dark.png +0 -0
- package/public/dark1.png +0 -0
- package/public/dark2.png +0 -0
- package/public/file.svg +0 -1
- package/public/globe.svg +0 -1
- package/public/light.png +0 -0
- package/public/light1.png +0 -0
- package/public/next.svg +0 -1
- package/public/vercel.svg +0 -1
- package/public/video/hero-demo.mp4 +0 -0
- package/public/window.svg +0 -1
- package/tsconfig.json +0 -34
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import React from "react";
|
|
4
|
-
import { Mail, TextCursorInput } from "lucide-react";
|
|
5
|
-
import {
|
|
6
|
-
InputStandard,
|
|
7
|
-
InputFilled,
|
|
8
|
-
InputUnderline,
|
|
9
|
-
InputFloating,
|
|
10
|
-
InputSearch,
|
|
11
|
-
InputPassword,
|
|
12
|
-
InputAddon,
|
|
13
|
-
InputIcon,
|
|
14
|
-
InputOTP,
|
|
15
|
-
InputAutocomplete,
|
|
16
|
-
} from "@/components/ui/allinput";
|
|
17
|
-
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
|
18
|
-
import {
|
|
19
|
-
DocsAccessibility,
|
|
20
|
-
DocsApiSection,
|
|
21
|
-
DocsPage,
|
|
22
|
-
DocsPageHeader,
|
|
23
|
-
DocsSection,
|
|
24
|
-
DocsSectionGrid,
|
|
25
|
-
DocsSimpleTable,
|
|
26
|
-
} from "@/components/docs/docs-page";
|
|
27
|
-
|
|
28
|
-
const COMPONENT_ROWS = [
|
|
29
|
-
{ name: "InputStandard", description: "Bordered outline field — the default choice for most forms." },
|
|
30
|
-
{ name: "InputFilled", description: "Borderless, filled surface inspired by Material Design." },
|
|
31
|
-
{ name: "InputUnderline", description: "Minimal single-line border that animates in on focus." },
|
|
32
|
-
{ name: "InputFloating", description: "Label starts inline and floats above the value on focus." },
|
|
33
|
-
{ name: "InputSearch", description: "Pill-shaped field with a leading search icon." },
|
|
34
|
-
{ name: "InputPassword", description: "Masked field with a show/hide visibility toggle." },
|
|
35
|
-
{ name: "InputAddon", description: "Wraps the field with static prefix and/or suffix text." },
|
|
36
|
-
{ name: "InputIcon", description: "Places a leading or trailing icon inside the field." },
|
|
37
|
-
{ name: "InputOTP", description: "Four-cell segmented field for one-time passcodes." },
|
|
38
|
-
{ name: "InputAutocomplete", description: "Text field paired with a type-ahead suggestion list." },
|
|
39
|
-
];
|
|
40
|
-
|
|
41
|
-
const ACCESSIBILITY_ITEMS = [
|
|
42
|
-
"Always pair an input with a visible or programmatically associated <label>.",
|
|
43
|
-
"Use aria-label on icon-only controls, like the password visibility toggle.",
|
|
44
|
-
"Communicate validation errors with text, not color alone.",
|
|
45
|
-
"OTP and segmented fields should expose each cell as a distinct, labeled input.",
|
|
46
|
-
"Preserve native input behavior — don't block paste, autofill, or keyboard navigation.",
|
|
47
|
-
];
|
|
48
|
-
|
|
49
|
-
export function InputDocPage() {
|
|
50
|
-
return (
|
|
51
|
-
<DocsPage>
|
|
52
|
-
<DocsPageHeader
|
|
53
|
-
icon={TextCursorInput}
|
|
54
|
-
title="Input"
|
|
55
|
-
description="Professional UI inputs with distinct visual identities — from bordered fields to floating labels and segmented codes."
|
|
56
|
-
/>
|
|
57
|
-
|
|
58
|
-
<DocsSection first title="Foundation" description="Two dependable base styles for everyday forms.">
|
|
59
|
-
<DocsSectionGrid>
|
|
60
|
-
<Card>
|
|
61
|
-
<CardHeader>
|
|
62
|
-
<CardTitle className="text-base">Outlined</CardTitle>
|
|
63
|
-
<CardDescription>The default bordered field for most forms.</CardDescription>
|
|
64
|
-
</CardHeader>
|
|
65
|
-
<CardContent>
|
|
66
|
-
<InputStandard placeholder="Email address" />
|
|
67
|
-
</CardContent>
|
|
68
|
-
</Card>
|
|
69
|
-
<Card>
|
|
70
|
-
<CardHeader>
|
|
71
|
-
<CardTitle className="text-base">Filled</CardTitle>
|
|
72
|
-
<CardDescription>A soft, borderless surface with a filled background.</CardDescription>
|
|
73
|
-
</CardHeader>
|
|
74
|
-
<CardContent>
|
|
75
|
-
<InputFilled placeholder="Your full name" />
|
|
76
|
-
</CardContent>
|
|
77
|
-
</Card>
|
|
78
|
-
</DocsSectionGrid>
|
|
79
|
-
</DocsSection>
|
|
80
|
-
|
|
81
|
-
<DocsSection title="Minimal & Floating" description="Understated styles that keep the focus on content.">
|
|
82
|
-
<DocsSectionGrid>
|
|
83
|
-
<Card>
|
|
84
|
-
<CardHeader>
|
|
85
|
-
<CardTitle className="text-base">Underline</CardTitle>
|
|
86
|
-
<CardDescription>A single border that animates in on focus.</CardDescription>
|
|
87
|
-
</CardHeader>
|
|
88
|
-
<CardContent>
|
|
89
|
-
<InputUnderline placeholder="Company" />
|
|
90
|
-
</CardContent>
|
|
91
|
-
</Card>
|
|
92
|
-
<Card>
|
|
93
|
-
<CardHeader>
|
|
94
|
-
<CardTitle className="text-base">Floating Label</CardTitle>
|
|
95
|
-
<CardDescription>The label lifts above the value once focused or filled.</CardDescription>
|
|
96
|
-
</CardHeader>
|
|
97
|
-
<CardContent>
|
|
98
|
-
<InputFloating label="Phone" placeholder=" " />
|
|
99
|
-
</CardContent>
|
|
100
|
-
</Card>
|
|
101
|
-
</DocsSectionGrid>
|
|
102
|
-
</DocsSection>
|
|
103
|
-
|
|
104
|
-
<DocsSection title="Search & Security" description="Purpose-built fields for lookup and sensitive data entry.">
|
|
105
|
-
<DocsSectionGrid>
|
|
106
|
-
<Card>
|
|
107
|
-
<CardHeader>
|
|
108
|
-
<CardTitle className="text-base">Search</CardTitle>
|
|
109
|
-
<CardDescription>Pill-shaped with a leading search icon.</CardDescription>
|
|
110
|
-
</CardHeader>
|
|
111
|
-
<CardContent>
|
|
112
|
-
<InputSearch placeholder="Search..." />
|
|
113
|
-
</CardContent>
|
|
114
|
-
</Card>
|
|
115
|
-
<Card>
|
|
116
|
-
<CardHeader>
|
|
117
|
-
<CardTitle className="text-base">Password</CardTitle>
|
|
118
|
-
<CardDescription>Includes a toggle to reveal the typed value.</CardDescription>
|
|
119
|
-
</CardHeader>
|
|
120
|
-
<CardContent>
|
|
121
|
-
<InputPassword placeholder="Password" />
|
|
122
|
-
</CardContent>
|
|
123
|
-
</Card>
|
|
124
|
-
</DocsSectionGrid>
|
|
125
|
-
</DocsSection>
|
|
126
|
-
|
|
127
|
-
<DocsSection title="Addons & Icons" description="Add context with affixes or a leading/trailing icon.">
|
|
128
|
-
<DocsSectionGrid>
|
|
129
|
-
<Card>
|
|
130
|
-
<CardHeader>
|
|
131
|
-
<CardTitle className="text-base">With Addons</CardTitle>
|
|
132
|
-
<CardDescription>Static prefix and suffix text framed around the value.</CardDescription>
|
|
133
|
-
</CardHeader>
|
|
134
|
-
<CardContent>
|
|
135
|
-
<InputAddon prefix="https://" suffix=".io" placeholder="domain" />
|
|
136
|
-
</CardContent>
|
|
137
|
-
</Card>
|
|
138
|
-
<Card>
|
|
139
|
-
<CardHeader>
|
|
140
|
-
<CardTitle className="text-base">With Icon</CardTitle>
|
|
141
|
-
<CardDescription>A leading icon adds meaning at a glance.</CardDescription>
|
|
142
|
-
</CardHeader>
|
|
143
|
-
<CardContent>
|
|
144
|
-
<InputIcon icon={Mail} placeholder="Support email" />
|
|
145
|
-
</CardContent>
|
|
146
|
-
</Card>
|
|
147
|
-
</DocsSectionGrid>
|
|
148
|
-
</DocsSection>
|
|
149
|
-
|
|
150
|
-
<DocsSection
|
|
151
|
-
title="Advanced Patterns"
|
|
152
|
-
description="Segmented codes and intelligent type-ahead suggestions."
|
|
153
|
-
className="pb-28"
|
|
154
|
-
>
|
|
155
|
-
<DocsSectionGrid>
|
|
156
|
-
<Card>
|
|
157
|
-
<CardHeader>
|
|
158
|
-
<CardTitle className="text-base">OTP / Code</CardTitle>
|
|
159
|
-
<CardDescription>Four-cell segmented field for verification codes.</CardDescription>
|
|
160
|
-
</CardHeader>
|
|
161
|
-
<CardContent>
|
|
162
|
-
<InputOTP />
|
|
163
|
-
</CardContent>
|
|
164
|
-
</Card>
|
|
165
|
-
<Card>
|
|
166
|
-
<CardHeader>
|
|
167
|
-
<CardTitle className="text-base">Autocomplete</CardTitle>
|
|
168
|
-
<CardDescription>Suggests matching values as the user types.</CardDescription>
|
|
169
|
-
</CardHeader>
|
|
170
|
-
<CardContent>
|
|
171
|
-
<InputAutocomplete placeholder="Search a company..." suggestions={["Apple", "Amazon", "Adobe"]} />
|
|
172
|
-
</CardContent>
|
|
173
|
-
</Card>
|
|
174
|
-
</DocsSectionGrid>
|
|
175
|
-
</DocsSection>
|
|
176
|
-
|
|
177
|
-
<DocsApiSection>
|
|
178
|
-
<DocsSimpleTable title="Available Components" rows={COMPONENT_ROWS} />
|
|
179
|
-
<DocsAccessibility items={ACCESSIBILITY_ITEMS} />
|
|
180
|
-
</DocsApiSection>
|
|
181
|
-
</DocsPage>
|
|
182
|
-
);
|
|
183
|
-
}
|
|
@@ -1,424 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { useState } from "react";
|
|
4
|
-
import { Check, CircleUserRound, Copy, Mail, UserPlus } from "lucide-react";
|
|
5
|
-
import { Avatar, AvatarGroup, type AvatarProps } from "@/components/ui/avatar";
|
|
6
|
-
import { Badge } from "@/components/ui/badge";
|
|
7
|
-
import { Button } from "@/components/ui/button";
|
|
8
|
-
import { Tooltip } from "@/components/ui/tooltip";
|
|
9
|
-
import {
|
|
10
|
-
Card,
|
|
11
|
-
CardContent,
|
|
12
|
-
CardDescription,
|
|
13
|
-
CardFooter,
|
|
14
|
-
CardHeader,
|
|
15
|
-
CardTitle,
|
|
16
|
-
} from "@/components/ui/card";
|
|
17
|
-
import {
|
|
18
|
-
DocsAccessibility,
|
|
19
|
-
DocsApiSection,
|
|
20
|
-
DocsApiTable,
|
|
21
|
-
DocsDemoPanel,
|
|
22
|
-
DocsPage,
|
|
23
|
-
DocsPageHeader,
|
|
24
|
-
DocsSection,
|
|
25
|
-
DocsSectionGrid,
|
|
26
|
-
} from "@/components/docs/docs-page";
|
|
27
|
-
import { cn } from "@/lib/cn";
|
|
28
|
-
|
|
29
|
-
const API_ROWS = [
|
|
30
|
-
{ prop: "src", type: "string", default: "—", description: "Image URL for the avatar" },
|
|
31
|
-
{ prop: "alt", type: "string", default: "—", description: "Accessible label for the image" },
|
|
32
|
-
{ prop: "name", type: "string", default: "—", description: "Used for initials fallback and alt text" },
|
|
33
|
-
{ prop: "size", type: '"sm" | "md" | "lg" | "xl"', default: '"md"', description: "The size of the avatar" },
|
|
34
|
-
{ prop: "variant", type: '"default" | "outline"', default: '"default"', description: "Ring style for stacked groups" },
|
|
35
|
-
{ prop: "status", type: '"online" | "offline" | "busy" | null', default: "null", description: "Optional presence indicator" },
|
|
36
|
-
{ prop: "className", type: "string", default: "—", description: "Additional CSS classes" },
|
|
37
|
-
];
|
|
38
|
-
|
|
39
|
-
const GROUP_API_ROWS = [
|
|
40
|
-
{ prop: "max", type: "number", default: "5", description: "Maximum avatars shown before +N overflow" },
|
|
41
|
-
{ prop: "size", type: '"sm" | "md" | "lg" | "xl"', default: '"md"', description: "Size applied to overflow badge" },
|
|
42
|
-
{ prop: "children", type: "ReactNode", default: "—", description: "Avatar elements to display" },
|
|
43
|
-
];
|
|
44
|
-
|
|
45
|
-
const ACCESSIBILITY_ITEMS = [
|
|
46
|
-
"Always provide alt text or a name for image avatars.",
|
|
47
|
-
"Use initials fallback when no image is available.",
|
|
48
|
-
"Status indicators are decorative — convey status in text when important.",
|
|
49
|
-
"Avatar groups should include a text label describing the group.",
|
|
50
|
-
"Ensure sufficient contrast for initials on fallback backgrounds.",
|
|
51
|
-
];
|
|
52
|
-
|
|
53
|
-
const TEAM = [
|
|
54
|
-
{ name: "Zuhaib Khan", src: "https://i.pravatar.cc/150?img=11", role: "Lead Designer" },
|
|
55
|
-
{ name: "Aisha Malik", src: "https://i.pravatar.cc/150?img=5", role: "Product Manager" },
|
|
56
|
-
{ name: "James Chen", src: "https://i.pravatar.cc/150?img=12", role: "Engineer" },
|
|
57
|
-
{ name: "Sara Patel", src: "https://i.pravatar.cc/150?img=9", role: "Research" },
|
|
58
|
-
{ name: "Leo Martin", src: "https://i.pravatar.cc/150?img=15", role: "Marketing" },
|
|
59
|
-
{ name: "Nina Rossi", src: "https://i.pravatar.cc/150?img=20", role: "Support" },
|
|
60
|
-
];
|
|
61
|
-
|
|
62
|
-
type Size = NonNullable<AvatarProps["size"]>;
|
|
63
|
-
type Variant = NonNullable<AvatarProps["variant"]>;
|
|
64
|
-
type Status = AvatarProps["status"];
|
|
65
|
-
|
|
66
|
-
const SIZES: Size[] = ["sm", "md", "lg", "xl"];
|
|
67
|
-
const VARIANTS: Variant[] = ["default", "outline"];
|
|
68
|
-
const STATUSES: { value: Status; label: string }[] = [
|
|
69
|
-
{ value: null, label: "None" },
|
|
70
|
-
{ value: "online", label: "Online" },
|
|
71
|
-
{ value: "busy", label: "Busy" },
|
|
72
|
-
{ value: "offline", label: "Offline" },
|
|
73
|
-
];
|
|
74
|
-
|
|
75
|
-
function codeFor(size: Size, variant: Variant, status: Status, showPhoto: boolean) {
|
|
76
|
-
const lines = [
|
|
77
|
-
showPhoto ? `src="https://i.pravatar.cc/150?img=5"` : null,
|
|
78
|
-
`name="Aisha Malik"`,
|
|
79
|
-
size !== "md" ? `size="${size}"` : null,
|
|
80
|
-
variant !== "default" ? `variant="${variant}"` : null,
|
|
81
|
-
status ? `status="${status}"` : null,
|
|
82
|
-
].filter(Boolean);
|
|
83
|
-
return `<Avatar\n ${lines.join("\n ")}\n/>`;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export function AvatarDocPage() {
|
|
87
|
-
const [size, setSize] = useState<Size>("xl");
|
|
88
|
-
const [variant, setVariant] = useState<Variant>("default");
|
|
89
|
-
const [status, setStatus] = useState<Status>("online");
|
|
90
|
-
const [showPhoto, setShowPhoto] = useState(true);
|
|
91
|
-
const [copied, setCopied] = useState(false);
|
|
92
|
-
|
|
93
|
-
const snippet = codeFor(size, variant, status, showPhoto);
|
|
94
|
-
|
|
95
|
-
function copySnippet() {
|
|
96
|
-
navigator.clipboard.writeText(snippet).catch(() => {});
|
|
97
|
-
setCopied(true);
|
|
98
|
-
setTimeout(() => setCopied(false), 1200);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
return (
|
|
102
|
-
<DocsPage>
|
|
103
|
-
<DocsPageHeader
|
|
104
|
-
icon={CircleUserRound}
|
|
105
|
-
title="Avatar"
|
|
106
|
-
description="Represents a user or entity with a photo, initials fallback, and optional presence status."
|
|
107
|
-
/>
|
|
108
|
-
|
|
109
|
-
<DocsSection
|
|
110
|
-
first
|
|
111
|
-
title="Playground"
|
|
112
|
-
description="Every size, variant, and status — live, with the exact markup to match."
|
|
113
|
-
>
|
|
114
|
-
<div className="overflow-hidden rounded-2xl border border-zinc-200 shadow-[0_2px_8px_rgba(0,0,0,0.04)] dark:border-zinc-800 dark:shadow-[0_2px_8px_rgba(0,0,0,0.2)]">
|
|
115
|
-
<div className="relative flex min-h-[11rem] items-center justify-center overflow-hidden bg-zinc-50 dark:bg-zinc-900/40">
|
|
116
|
-
<div
|
|
117
|
-
aria-hidden
|
|
118
|
-
className="pointer-events-none absolute inset-0 opacity-[0.4] dark:opacity-[0.15]"
|
|
119
|
-
style={{
|
|
120
|
-
backgroundImage:
|
|
121
|
-
"linear-gradient(to right, rgb(161 161 170 / 0.35) 1px, transparent 1px), linear-gradient(to bottom, rgb(161 161 170 / 0.35) 1px, transparent 1px)",
|
|
122
|
-
backgroundSize: "24px 24px",
|
|
123
|
-
}}
|
|
124
|
-
/>
|
|
125
|
-
<div key={`${size}-${variant}-${status}-${showPhoto}`} className="relative animate-[pagination-pop_0.25s_ease-out]">
|
|
126
|
-
<Avatar
|
|
127
|
-
src={showPhoto ? "https://i.pravatar.cc/150?img=5" : undefined}
|
|
128
|
-
name="Aisha Malik"
|
|
129
|
-
size={size}
|
|
130
|
-
variant={variant}
|
|
131
|
-
status={status}
|
|
132
|
-
/>
|
|
133
|
-
</div>
|
|
134
|
-
</div>
|
|
135
|
-
|
|
136
|
-
<div className="flex flex-col gap-5 border-t border-zinc-200 p-5 dark:border-zinc-800">
|
|
137
|
-
<div className="flex flex-wrap items-start justify-between gap-6">
|
|
138
|
-
<div>
|
|
139
|
-
<p className="mb-2 text-xs font-medium uppercase tracking-wide text-zinc-400 dark:text-zinc-500">
|
|
140
|
-
Size
|
|
141
|
-
</p>
|
|
142
|
-
<div className="flex flex-wrap items-center gap-1 rounded-lg border border-zinc-200 bg-zinc-50 p-1 dark:border-zinc-800 dark:bg-zinc-900/60">
|
|
143
|
-
{SIZES.map((s) => (
|
|
144
|
-
<button
|
|
145
|
-
key={s}
|
|
146
|
-
type="button"
|
|
147
|
-
onClick={() => setSize(s)}
|
|
148
|
-
className={cn(
|
|
149
|
-
"rounded-md px-3 py-1.5 text-xs font-medium uppercase tracking-wide transition-colors",
|
|
150
|
-
size === s
|
|
151
|
-
? "bg-white text-zinc-900 shadow-sm dark:bg-zinc-800 dark:text-white"
|
|
152
|
-
: "text-zinc-500 hover:text-zinc-800 dark:text-zinc-400 dark:hover:text-zinc-200"
|
|
153
|
-
)}
|
|
154
|
-
>
|
|
155
|
-
{s}
|
|
156
|
-
</button>
|
|
157
|
-
))}
|
|
158
|
-
</div>
|
|
159
|
-
</div>
|
|
160
|
-
|
|
161
|
-
<div>
|
|
162
|
-
<p className="mb-2 text-xs font-medium uppercase tracking-wide text-zinc-400 dark:text-zinc-500">
|
|
163
|
-
Status
|
|
164
|
-
</p>
|
|
165
|
-
<div className="flex flex-wrap items-center gap-1 rounded-lg border border-zinc-200 bg-zinc-50 p-1 dark:border-zinc-800 dark:bg-zinc-900/60">
|
|
166
|
-
{STATUSES.map((s) => (
|
|
167
|
-
<button
|
|
168
|
-
key={s.label}
|
|
169
|
-
type="button"
|
|
170
|
-
onClick={() => setStatus(s.value)}
|
|
171
|
-
className={cn(
|
|
172
|
-
"rounded-md px-3 py-1.5 text-xs font-medium transition-colors",
|
|
173
|
-
status === s.value
|
|
174
|
-
? "bg-white text-zinc-900 shadow-sm dark:bg-zinc-800 dark:text-white"
|
|
175
|
-
: "text-zinc-500 hover:text-zinc-800 dark:text-zinc-400 dark:hover:text-zinc-200"
|
|
176
|
-
)}
|
|
177
|
-
>
|
|
178
|
-
{s.label}
|
|
179
|
-
</button>
|
|
180
|
-
))}
|
|
181
|
-
</div>
|
|
182
|
-
</div>
|
|
183
|
-
|
|
184
|
-
<div>
|
|
185
|
-
<p className="mb-2 text-xs font-medium uppercase tracking-wide text-zinc-400 dark:text-zinc-500">
|
|
186
|
-
Variant
|
|
187
|
-
</p>
|
|
188
|
-
<div className="flex flex-wrap items-center gap-1 rounded-lg border border-zinc-200 bg-zinc-50 p-1 dark:border-zinc-800 dark:bg-zinc-900/60">
|
|
189
|
-
{VARIANTS.map((v) => (
|
|
190
|
-
<button
|
|
191
|
-
key={v}
|
|
192
|
-
type="button"
|
|
193
|
-
onClick={() => setVariant(v)}
|
|
194
|
-
className={cn(
|
|
195
|
-
"rounded-md px-3 py-1.5 text-xs font-medium capitalize transition-colors",
|
|
196
|
-
variant === v
|
|
197
|
-
? "bg-white text-zinc-900 shadow-sm dark:bg-zinc-800 dark:text-white"
|
|
198
|
-
: "text-zinc-500 hover:text-zinc-800 dark:text-zinc-400 dark:hover:text-zinc-200"
|
|
199
|
-
)}
|
|
200
|
-
>
|
|
201
|
-
{v}
|
|
202
|
-
</button>
|
|
203
|
-
))}
|
|
204
|
-
</div>
|
|
205
|
-
</div>
|
|
206
|
-
|
|
207
|
-
<div>
|
|
208
|
-
<p className="mb-2 text-xs font-medium uppercase tracking-wide text-zinc-400 dark:text-zinc-500">
|
|
209
|
-
Photo
|
|
210
|
-
</p>
|
|
211
|
-
<button
|
|
212
|
-
type="button"
|
|
213
|
-
onClick={() => setShowPhoto((v) => !v)}
|
|
214
|
-
className={cn(
|
|
215
|
-
"flex h-[2.125rem] items-center gap-2 rounded-lg border px-3 text-xs font-medium transition-colors",
|
|
216
|
-
showPhoto
|
|
217
|
-
? "border-zinc-900 bg-zinc-900 text-white dark:border-white dark:bg-white dark:text-zinc-900"
|
|
218
|
-
: "border-zinc-200 text-zinc-500 hover:border-zinc-300 dark:border-zinc-700 dark:text-zinc-400"
|
|
219
|
-
)}
|
|
220
|
-
>
|
|
221
|
-
{showPhoto ? "Photo" : "Initials"}
|
|
222
|
-
</button>
|
|
223
|
-
</div>
|
|
224
|
-
</div>
|
|
225
|
-
|
|
226
|
-
<div className="flex items-center justify-between gap-3 rounded-xl bg-zinc-900 px-4 py-3 dark:bg-black">
|
|
227
|
-
<pre className="overflow-x-auto font-mono text-[13px] leading-relaxed text-zinc-100">
|
|
228
|
-
<code>{snippet}</code>
|
|
229
|
-
</pre>
|
|
230
|
-
<Tooltip content={copied ? "Copied!" : "Copy code"}>
|
|
231
|
-
<button
|
|
232
|
-
type="button"
|
|
233
|
-
onClick={copySnippet}
|
|
234
|
-
className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg text-zinc-400 transition-colors hover:bg-white/10 hover:text-white"
|
|
235
|
-
>
|
|
236
|
-
{copied ? <Check className="h-4 w-4 text-emerald-400" /> : <Copy className="h-4 w-4" />}
|
|
237
|
-
</button>
|
|
238
|
-
</Tooltip>
|
|
239
|
-
</div>
|
|
240
|
-
</div>
|
|
241
|
-
</div>
|
|
242
|
-
</DocsSection>
|
|
243
|
-
|
|
244
|
-
<DocsSection
|
|
245
|
-
title="Team Directory"
|
|
246
|
-
description="Combine avatars, metadata, and actions in a single scannable surface."
|
|
247
|
-
>
|
|
248
|
-
<Card className="max-w-2xl">
|
|
249
|
-
<CardHeader className="border-b border-zinc-100 dark:border-zinc-800">
|
|
250
|
-
<div className="flex flex-wrap items-start justify-between gap-4">
|
|
251
|
-
<div>
|
|
252
|
-
<CardTitle>Design team</CardTitle>
|
|
253
|
-
<CardDescription>6 members · 4 active now</CardDescription>
|
|
254
|
-
</div>
|
|
255
|
-
<Badge variant="success">Collaborating</Badge>
|
|
256
|
-
</div>
|
|
257
|
-
</CardHeader>
|
|
258
|
-
<CardContent className="flex flex-col gap-6 pt-6 sm:flex-row sm:items-center sm:justify-between">
|
|
259
|
-
<div>
|
|
260
|
-
<AvatarGroup max={5} size="md">
|
|
261
|
-
{TEAM.map((member) => (
|
|
262
|
-
<Tooltip key={member.name} content={`${member.name} · ${member.role}`}>
|
|
263
|
-
<Avatar src={member.src} name={member.name} variant="outline" />
|
|
264
|
-
</Tooltip>
|
|
265
|
-
))}
|
|
266
|
-
</AvatarGroup>
|
|
267
|
-
<p className="mt-3 text-sm text-zinc-600 dark:text-zinc-300">
|
|
268
|
-
Zuhaib, Aisha, James, Sara, and 2 more
|
|
269
|
-
</p>
|
|
270
|
-
</div>
|
|
271
|
-
<Button size="sm">
|
|
272
|
-
<UserPlus className="h-4 w-4" />
|
|
273
|
-
Invite
|
|
274
|
-
</Button>
|
|
275
|
-
</CardContent>
|
|
276
|
-
</Card>
|
|
277
|
-
</DocsSection>
|
|
278
|
-
|
|
279
|
-
<DocsSection
|
|
280
|
-
title="Photos & Initials"
|
|
281
|
-
description="Gradient initials automatically match each name when no photo is available."
|
|
282
|
-
>
|
|
283
|
-
<DocsDemoPanel>
|
|
284
|
-
{[
|
|
285
|
-
{ name: "Zuhaib Khan", src: "https://i.pravatar.cc/150?img=11", status: "online" as const },
|
|
286
|
-
{ name: "Aisha Malik", status: "online" as const },
|
|
287
|
-
{ name: "James Chen", status: null },
|
|
288
|
-
{ name: "Sara Patel", status: "busy" as const },
|
|
289
|
-
{ name: "Leo Martin", status: "offline" as const },
|
|
290
|
-
].map((person) => (
|
|
291
|
-
<Tooltip key={person.name} content={person.name}>
|
|
292
|
-
<div className="flex flex-col items-center gap-2.5">
|
|
293
|
-
<Avatar src={person.src} name={person.name} size="lg" status={person.status} />
|
|
294
|
-
<span className="text-xs font-medium text-zinc-500 dark:text-zinc-400">
|
|
295
|
-
{person.name.split(" ")[0]}
|
|
296
|
-
</span>
|
|
297
|
-
</div>
|
|
298
|
-
</Tooltip>
|
|
299
|
-
))}
|
|
300
|
-
</DocsDemoPanel>
|
|
301
|
-
</DocsSection>
|
|
302
|
-
|
|
303
|
-
<DocsSection>
|
|
304
|
-
<DocsSectionGrid className="lg:grid-cols-2">
|
|
305
|
-
<Card>
|
|
306
|
-
<CardHeader>
|
|
307
|
-
<CardTitle className="text-base">Presence</CardTitle>
|
|
308
|
-
<CardDescription>Status dots with a soft pulse for active users.</CardDescription>
|
|
309
|
-
</CardHeader>
|
|
310
|
-
<CardContent>
|
|
311
|
-
<div className="grid grid-cols-3 gap-4">
|
|
312
|
-
{[
|
|
313
|
-
{ label: "Online", status: "online" as const, src: "https://i.pravatar.cc/150?img=8", name: "Online" },
|
|
314
|
-
{ label: "Busy", status: "busy" as const, name: "Busy User" },
|
|
315
|
-
{ label: "Offline", status: "offline" as const, name: "Offline User" },
|
|
316
|
-
].map((item) => (
|
|
317
|
-
<div
|
|
318
|
-
key={item.label}
|
|
319
|
-
className="flex flex-col items-center gap-3 rounded-xl border border-zinc-100 bg-zinc-50/80 p-4 dark:border-zinc-800 dark:bg-zinc-900/40"
|
|
320
|
-
>
|
|
321
|
-
<Avatar src={item.src} name={item.name} size="lg" status={item.status} />
|
|
322
|
-
<span className="text-xs font-medium text-zinc-600 dark:text-zinc-300">{item.label}</span>
|
|
323
|
-
</div>
|
|
324
|
-
))}
|
|
325
|
-
</div>
|
|
326
|
-
</CardContent>
|
|
327
|
-
</Card>
|
|
328
|
-
|
|
329
|
-
<Card>
|
|
330
|
-
<CardHeader>
|
|
331
|
-
<CardTitle className="text-base">Stacked Group</CardTitle>
|
|
332
|
-
<CardDescription>Hover avatars to preview — overflow shows +N count.</CardDescription>
|
|
333
|
-
</CardHeader>
|
|
334
|
-
<CardContent>
|
|
335
|
-
<AvatarGroup max={4} size="lg">
|
|
336
|
-
{TEAM.map((member) => (
|
|
337
|
-
<Tooltip key={member.name} content={`${member.name} · ${member.role}`}>
|
|
338
|
-
<Avatar src={member.src} name={member.name} variant="outline" />
|
|
339
|
-
</Tooltip>
|
|
340
|
-
))}
|
|
341
|
-
</AvatarGroup>
|
|
342
|
-
<div className="mt-5 flex items-center justify-between rounded-xl border border-zinc-100 bg-zinc-50/80 px-4 py-3 dark:border-zinc-800 dark:bg-zinc-900/40">
|
|
343
|
-
<p className="text-sm font-medium text-zinc-800 dark:text-zinc-100">6 teammates</p>
|
|
344
|
-
<Badge variant="outline">+2 hidden</Badge>
|
|
345
|
-
</div>
|
|
346
|
-
</CardContent>
|
|
347
|
-
</Card>
|
|
348
|
-
</DocsSectionGrid>
|
|
349
|
-
</DocsSection>
|
|
350
|
-
|
|
351
|
-
<DocsSection
|
|
352
|
-
title="Profile Card"
|
|
353
|
-
description="Layer avatars over a header band for profile-style layouts."
|
|
354
|
-
>
|
|
355
|
-
<Card className="max-w-md overflow-hidden">
|
|
356
|
-
<div className="h-24 bg-gradient-to-br from-zinc-100 via-zinc-50 to-zinc-200 dark:from-zinc-800 dark:via-zinc-900 dark:to-zinc-950" />
|
|
357
|
-
<CardContent className="relative px-6 pb-6 pt-0">
|
|
358
|
-
<Avatar
|
|
359
|
-
src="https://i.pravatar.cc/150?img=11"
|
|
360
|
-
name="Zuhaib Khan"
|
|
361
|
-
size="xl"
|
|
362
|
-
status="online"
|
|
363
|
-
className="absolute -top-12 ring-4 ring-white dark:ring-zinc-900"
|
|
364
|
-
/>
|
|
365
|
-
<div className="pt-14">
|
|
366
|
-
<div className="flex flex-wrap items-center gap-2">
|
|
367
|
-
<h3 className="text-lg font-semibold tracking-tight text-zinc-900 dark:text-white">
|
|
368
|
-
Zuhaib Khan
|
|
369
|
-
</h3>
|
|
370
|
-
<Badge variant="success">Pro</Badge>
|
|
371
|
-
</div>
|
|
372
|
-
<p className="mt-1 text-sm text-zinc-500 dark:text-zinc-400">Product Designer · San Francisco</p>
|
|
373
|
-
<div className="mt-4 flex flex-wrap gap-2">
|
|
374
|
-
<Button size="sm">View profile</Button>
|
|
375
|
-
<Button variant="ghost" size="sm">
|
|
376
|
-
<Mail className="h-4 w-4" />
|
|
377
|
-
Message
|
|
378
|
-
</Button>
|
|
379
|
-
</div>
|
|
380
|
-
</div>
|
|
381
|
-
</CardContent>
|
|
382
|
-
</Card>
|
|
383
|
-
</DocsSection>
|
|
384
|
-
|
|
385
|
-
<DocsSection
|
|
386
|
-
title="Member List"
|
|
387
|
-
description="Compact rows with hover states for settings and team management."
|
|
388
|
-
>
|
|
389
|
-
<Card className="max-w-md">
|
|
390
|
-
<CardHeader className="border-b border-zinc-100 pb-4 dark:border-zinc-800">
|
|
391
|
-
<CardTitle className="text-base">Workspace members</CardTitle>
|
|
392
|
-
<CardDescription>Manage roles and access levels.</CardDescription>
|
|
393
|
-
</CardHeader>
|
|
394
|
-
<CardContent className="p-0">
|
|
395
|
-
{TEAM.slice(0, 4).map((member) => (
|
|
396
|
-
<div
|
|
397
|
-
key={member.name}
|
|
398
|
-
className="flex items-center gap-3 border-b border-zinc-100 px-6 py-3.5 transition-colors last:border-0 hover:bg-zinc-50 dark:border-zinc-800 dark:hover:bg-zinc-900/50"
|
|
399
|
-
>
|
|
400
|
-
<Avatar src={member.src} name={member.name} size="md" status="online" />
|
|
401
|
-
<div className="min-w-0 flex-1">
|
|
402
|
-
<p className="truncate text-sm font-medium text-zinc-900 dark:text-white">
|
|
403
|
-
{member.name}
|
|
404
|
-
</p>
|
|
405
|
-
<p className="text-xs text-zinc-500 dark:text-zinc-400">{member.role}</p>
|
|
406
|
-
</div>
|
|
407
|
-
<Badge variant="outline">Editor</Badge>
|
|
408
|
-
</div>
|
|
409
|
-
))}
|
|
410
|
-
</CardContent>
|
|
411
|
-
<CardFooter className="border-t border-zinc-100 dark:border-zinc-800">
|
|
412
|
-
<Button variant="ghost" size="sm" className="w-full">View all members</Button>
|
|
413
|
-
</CardFooter>
|
|
414
|
-
</Card>
|
|
415
|
-
</DocsSection>
|
|
416
|
-
|
|
417
|
-
<DocsApiSection>
|
|
418
|
-
<DocsApiTable title="API Reference — Avatar" rows={API_ROWS} />
|
|
419
|
-
<DocsApiTable title="API Reference — AvatarGroup" rows={GROUP_API_ROWS} />
|
|
420
|
-
<DocsAccessibility items={ACCESSIBILITY_ITEMS} />
|
|
421
|
-
</DocsApiSection>
|
|
422
|
-
</DocsPage>
|
|
423
|
-
);
|
|
424
|
-
}
|