@actuate-media/cms-admin 0.63.2 → 0.65.0
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/CHANGELOG.md +20 -0
- package/design-system/README.md +53 -0
- package/design-system/tokens.css +114 -0
- package/design-system/tokens.json +149 -0
- package/design-system/tokens.scss +108 -0
- package/design-system/tokens.tailwind.cjs +80 -0
- package/dist/__tests__/components/ui-primitives.test.d.ts +2 -0
- package/dist/__tests__/components/ui-primitives.test.d.ts.map +1 -0
- package/dist/__tests__/components/ui-primitives.test.js +45 -0
- package/dist/__tests__/components/ui-primitives.test.js.map +1 -0
- package/dist/__tests__/layout/primitives.test.js +9 -2
- package/dist/__tests__/layout/primitives.test.js.map +1 -1
- package/dist/__tests__/views/updates-tab.render.test.js +1 -1
- package/dist/__tests__/views/updates-tab.render.test.js.map +1 -1
- package/dist/actuate-admin.css +1 -1
- package/dist/components/EnabledSwitch.d.ts +2 -5
- package/dist/components/EnabledSwitch.d.ts.map +1 -1
- package/dist/components/EnabledSwitch.js +4 -7
- package/dist/components/EnabledSwitch.js.map +1 -1
- package/dist/components/SEOPanel.d.ts.map +1 -1
- package/dist/components/SEOPanel.js +2 -1
- package/dist/components/SEOPanel.js.map +1 -1
- package/dist/components/ThemeProvider.d.ts.map +1 -1
- package/dist/components/ThemeProvider.js +5 -3
- package/dist/components/ThemeProvider.js.map +1 -1
- package/dist/components/forms/FieldsPanel.js +2 -2
- package/dist/components/forms/FieldsPanel.js.map +1 -1
- package/dist/components/forms/NotificationsPanel.js +2 -2
- package/dist/components/forms/NotificationsPanel.js.map +1 -1
- package/dist/components/forms/WebhooksPanel.d.ts.map +1 -1
- package/dist/components/forms/WebhooksPanel.js +2 -2
- package/dist/components/forms/WebhooksPanel.js.map +1 -1
- package/dist/components/forms/primitives.d.ts.map +1 -1
- package/dist/components/forms/primitives.js +5 -8
- package/dist/components/forms/primitives.js.map +1 -1
- package/dist/components/posts/badges.d.ts.map +1 -1
- package/dist/components/posts/badges.js +7 -13
- package/dist/components/posts/badges.js.map +1 -1
- package/dist/components/seo/RedirectEditorDrawer.d.ts.map +1 -1
- package/dist/components/seo/RedirectEditorDrawer.js +2 -1
- package/dist/components/seo/RedirectEditorDrawer.js.map +1 -1
- package/dist/components/seo/SeoEditorDrawer.d.ts.map +1 -1
- package/dist/components/seo/SeoEditorDrawer.js +3 -2
- package/dist/components/seo/SeoEditorDrawer.js.map +1 -1
- package/dist/components/seo/primitives.d.ts.map +1 -1
- package/dist/components/seo/primitives.js +11 -15
- package/dist/components/seo/primitives.js.map +1 -1
- package/dist/components/ui/Button.d.ts +1 -1
- package/dist/components/ui/Button.js +1 -1
- package/dist/components/ui/Button.js.map +1 -1
- package/dist/components/ui/Card.d.ts +6 -5
- package/dist/components/ui/Card.d.ts.map +1 -1
- package/dist/components/ui/Card.js +20 -18
- package/dist/components/ui/Card.js.map +1 -1
- package/dist/components/ui/FilterPill.d.ts +14 -0
- package/dist/components/ui/FilterPill.d.ts.map +1 -0
- package/dist/components/ui/FilterPill.js +8 -0
- package/dist/components/ui/FilterPill.js.map +1 -0
- package/dist/components/ui/Input.d.ts +2 -1
- package/dist/components/ui/Input.d.ts.map +1 -1
- package/dist/components/ui/Input.js +9 -8
- package/dist/components/ui/Input.js.map +1 -1
- package/dist/components/ui/SearchInput.d.ts.map +1 -1
- package/dist/components/ui/SearchInput.js +2 -1
- package/dist/components/ui/SearchInput.js.map +1 -1
- package/dist/components/ui/Select.d.ts.map +1 -1
- package/dist/components/ui/Select.js +9 -10
- package/dist/components/ui/Select.js.map +1 -1
- package/dist/components/ui/StatPill.d.ts +18 -0
- package/dist/components/ui/StatPill.d.ts.map +1 -0
- package/dist/components/ui/StatPill.js +5 -0
- package/dist/components/ui/StatPill.js.map +1 -0
- package/dist/components/ui/StatusBadge.d.ts +25 -0
- package/dist/components/ui/StatusBadge.d.ts.map +1 -0
- package/dist/components/ui/StatusBadge.js +41 -0
- package/dist/components/ui/StatusBadge.js.map +1 -0
- package/dist/components/ui/Toggle.d.ts +31 -0
- package/dist/components/ui/Toggle.d.ts.map +1 -0
- package/dist/components/ui/Toggle.js +41 -0
- package/dist/components/ui/Toggle.js.map +1 -0
- package/dist/components/ui/form-control.d.ts +11 -0
- package/dist/components/ui/form-control.d.ts.map +1 -0
- package/dist/components/ui/form-control.js +11 -0
- package/dist/components/ui/form-control.js.map +1 -0
- package/dist/components/ui/index.d.ts +9 -0
- package/dist/components/ui/index.d.ts.map +1 -1
- package/dist/components/ui/index.js +5 -0
- package/dist/components/ui/index.js.map +1 -1
- package/dist/fields/PropInput.d.ts.map +1 -1
- package/dist/fields/PropInput.js +2 -1
- package/dist/fields/PropInput.js.map +1 -1
- package/dist/fields/ToggleField.d.ts.map +1 -1
- package/dist/fields/ToggleField.js +2 -1
- package/dist/fields/ToggleField.js.map +1 -1
- package/dist/layout/Header.d.ts.map +1 -1
- package/dist/layout/Header.js +5 -2
- package/dist/layout/Header.js.map +1 -1
- package/dist/layout/Sidebar.js +1 -1
- package/dist/layout/Sidebar.js.map +1 -1
- package/dist/layout/primitives/PageContainer.d.ts.map +1 -1
- package/dist/layout/primitives/PageContainer.js +1 -1
- package/dist/layout/primitives/PageContainer.js.map +1 -1
- package/dist/layout/primitives/tokens.d.ts +10 -4
- package/dist/layout/primitives/tokens.d.ts.map +1 -1
- package/dist/layout/primitives/tokens.js +10 -4
- package/dist/layout/primitives/tokens.js.map +1 -1
- package/dist/views/Dashboard.js +1 -1
- package/dist/views/Dashboard.js.map +1 -1
- package/dist/views/FormEditor.d.ts.map +1 -1
- package/dist/views/FormEditor.js +2 -1
- package/dist/views/FormEditor.js.map +1 -1
- package/dist/views/FormEntries.js +1 -1
- package/dist/views/FormEntries.js.map +1 -1
- package/dist/views/FormSubmissions.js +1 -1
- package/dist/views/FormSubmissions.js.map +1 -1
- package/dist/views/Forms.js +3 -3
- package/dist/views/Forms.js.map +1 -1
- package/dist/views/MediaBrowser.d.ts.map +1 -1
- package/dist/views/MediaBrowser.js +3 -2
- package/dist/views/MediaBrowser.js.map +1 -1
- package/dist/views/Pages/PageTagManager.d.ts.map +1 -1
- package/dist/views/Pages/PageTagManager.js +1 -1
- package/dist/views/Pages/PageTagManager.js.map +1 -1
- package/dist/views/Pages/PagesListView.js +1 -1
- package/dist/views/Pages/PagesListView.js.map +1 -1
- package/dist/views/Pages.js +1 -1
- package/dist/views/Pages.js.map +1 -1
- package/dist/views/Posts/NewPostPicker.d.ts.map +1 -1
- package/dist/views/Posts/NewPostPicker.js +1 -1
- package/dist/views/Posts/NewPostPicker.js.map +1 -1
- package/dist/views/Posts/NewPostTypeInstructions.d.ts.map +1 -1
- package/dist/views/Posts/NewPostTypeInstructions.js +1 -1
- package/dist/views/Posts/NewPostTypeInstructions.js.map +1 -1
- package/dist/views/Posts/PostTypesView.d.ts.map +1 -1
- package/dist/views/Posts/PostTypesView.js +1 -1
- package/dist/views/Posts/PostTypesView.js.map +1 -1
- package/dist/views/Posts/PostsListView.js +1 -1
- package/dist/views/Posts/PostsListView.js.map +1 -1
- package/dist/views/Profile.js +1 -1
- package/dist/views/Profile.js.map +1 -1
- package/dist/views/Redirects.js +1 -1
- package/dist/views/Redirects.js.map +1 -1
- package/dist/views/SEO.js +1 -1
- package/dist/views/SEO.js.map +1 -1
- package/dist/views/ScriptTagEditor.d.ts.map +1 -1
- package/dist/views/ScriptTagEditor.js +1 -1
- package/dist/views/ScriptTagEditor.js.map +1 -1
- package/dist/views/ScriptTags.d.ts.map +1 -1
- package/dist/views/ScriptTags.js +2 -1
- package/dist/views/ScriptTags.js.map +1 -1
- package/dist/views/Settings.js +1 -1
- package/dist/views/Settings.js.map +1 -1
- package/dist/views/Users.d.ts.map +1 -1
- package/dist/views/Users.js +2 -6
- package/dist/views/Users.js.map +1 -1
- package/dist/views/page-builder/BlockEditor.d.ts.map +1 -1
- package/dist/views/page-builder/BlockEditor.js +2 -2
- package/dist/views/page-builder/BlockEditor.js.map +1 -1
- package/dist/views/page-builder/BottomBar.d.ts.map +1 -1
- package/dist/views/page-builder/BottomBar.js +2 -2
- package/dist/views/page-builder/BottomBar.js.map +1 -1
- package/dist/views/page-builder/NodeSettings.js +3 -3
- package/dist/views/page-builder/NodeSettings.js.map +1 -1
- package/dist/views/page-builder/PageTemplates.js +1 -1
- package/dist/views/page-builder/PageTemplates.js.map +1 -1
- package/dist/views/page-builder/SavedSections.js +2 -2
- package/dist/views/page-builder/SavedSections.js.map +1 -1
- package/dist/views/page-builder/Widgets.js +1 -1
- package/dist/views/page-builder/Widgets.js.map +1 -1
- package/dist/views/page-editor/SectionInspector.d.ts.map +1 -1
- package/dist/views/page-editor/SectionInspector.js +2 -1
- package/dist/views/page-editor/SectionInspector.js.map +1 -1
- package/dist/views/post-editor/PostHeaderPanel.d.ts.map +1 -1
- package/dist/views/post-editor/PostHeaderPanel.js +2 -1
- package/dist/views/post-editor/PostHeaderPanel.js.map +1 -1
- package/dist/views/seo/TechnicalTab.d.ts.map +1 -1
- package/dist/views/seo/TechnicalTab.js +2 -2
- package/dist/views/seo/TechnicalTab.js.map +1 -1
- package/dist/views/settings/components.d.ts.map +1 -1
- package/dist/views/settings/components.js +4 -2
- package/dist/views/settings/components.js.map +1 -1
- package/package.json +9 -4
- package/src/__tests__/components/ui-primitives.test.tsx +58 -0
- package/src/__tests__/layout/primitives.test.ts +10 -2
- package/src/__tests__/views/updates-tab.render.test.tsx +2 -1
- package/src/components/EnabledSwitch.tsx +4 -22
- package/src/components/SEOPanel.tsx +2 -11
- package/src/components/ThemeProvider.tsx +5 -3
- package/src/components/forms/FieldsPanel.tsx +4 -6
- package/src/components/forms/NotificationsPanel.tsx +2 -9
- package/src/components/forms/WebhooksPanel.tsx +4 -6
- package/src/components/forms/primitives.tsx +6 -16
- package/src/components/posts/badges.tsx +8 -21
- package/src/components/seo/RedirectEditorDrawer.tsx +12 -16
- package/src/components/seo/SeoEditorDrawer.tsx +16 -24
- package/src/components/seo/primitives.tsx +18 -34
- package/src/components/ui/Button.tsx +1 -1
- package/src/components/ui/Card.tsx +35 -18
- package/src/components/ui/FilterPill.tsx +43 -0
- package/src/components/ui/Input.tsx +15 -17
- package/src/components/ui/SearchInput.tsx +9 -4
- package/src/components/ui/Select.tsx +15 -18
- package/src/components/ui/StatPill.tsx +37 -0
- package/src/components/ui/StatusBadge.tsx +66 -0
- package/src/components/ui/Toggle.tsx +85 -0
- package/src/components/ui/form-control.ts +12 -0
- package/src/components/ui/index.ts +9 -0
- package/src/fields/PropInput.tsx +6 -15
- package/src/fields/ToggleField.tsx +3 -15
- package/src/layout/Header.tsx +6 -2
- package/src/layout/Sidebar.tsx +3 -3
- package/src/layout/primitives/PageContainer.tsx +1 -5
- package/src/layout/primitives/tokens.ts +11 -4
- package/src/styles/actuate-tokens.css +97 -0
- package/src/styles/theme.css +88 -156
- package/src/views/Dashboard.tsx +1 -1
- package/src/views/FormEditor.tsx +6 -9
- package/src/views/FormEntries.tsx +1 -1
- package/src/views/FormSubmissions.tsx +1 -1
- package/src/views/Forms.tsx +5 -7
- package/src/views/MediaBrowser.tsx +4 -7
- package/src/views/Pages/PageTagManager.tsx +1 -3
- package/src/views/Pages/PagesListView.tsx +1 -1
- package/src/views/Pages.tsx +1 -1
- package/src/views/Posts/NewPostPicker.tsx +1 -3
- package/src/views/Posts/NewPostTypeInstructions.tsx +1 -3
- package/src/views/Posts/PostTypesView.tsx +1 -3
- package/src/views/Posts/PostsListView.tsx +1 -1
- package/src/views/Profile.tsx +1 -1
- package/src/views/Redirects.tsx +1 -1
- package/src/views/SEO.tsx +1 -1
- package/src/views/ScriptTagEditor.tsx +1 -3
- package/src/views/ScriptTags.tsx +17 -1
- package/src/views/Settings.tsx +1 -1
- package/src/views/Users.tsx +6 -18
- package/src/views/page-builder/BlockEditor.tsx +3 -6
- package/src/views/page-builder/BottomBar.tsx +3 -6
- package/src/views/page-builder/NodeSettings.tsx +7 -16
- package/src/views/page-builder/PageTemplates.tsx +1 -1
- package/src/views/page-builder/SavedSections.tsx +2 -2
- package/src/views/page-builder/Widgets.tsx +1 -1
- package/src/views/page-editor/SectionInspector.tsx +9 -21
- package/src/views/post-editor/PostHeaderPanel.tsx +10 -20
- package/src/views/seo/TechnicalTab.tsx +3 -6
- package/src/views/settings/components.tsx +19 -26
|
@@ -89,7 +89,7 @@ const ThemeContext = createContext<ThemeContextValue>({
|
|
|
89
89
|
setAccent: () => {},
|
|
90
90
|
density: 'default',
|
|
91
91
|
setDensity: () => {},
|
|
92
|
-
font: '
|
|
92
|
+
font: 'System UI',
|
|
93
93
|
setFont: () => {},
|
|
94
94
|
reduceMotion: false,
|
|
95
95
|
setReduceMotion: () => {},
|
|
@@ -126,6 +126,8 @@ function applyColorMode(resolved: 'light' | 'dark') {
|
|
|
126
126
|
const el = adminRootEl()
|
|
127
127
|
if (!el) return
|
|
128
128
|
el.classList.toggle('dark', resolved === 'dark')
|
|
129
|
+
// Mirror handoff tokens.css selector so unscoped handoff styles can target the admin root.
|
|
130
|
+
el.setAttribute('data-theme', resolved)
|
|
129
131
|
}
|
|
130
132
|
|
|
131
133
|
function applyAccent(accent: string, resolved: 'light' | 'dark') {
|
|
@@ -166,7 +168,7 @@ export function ThemeProvider({ children, defaultDarkMode }: ThemeProviderProps)
|
|
|
166
168
|
const [resolvedTheme, setResolvedTheme] = useState<'light' | 'dark'>('light')
|
|
167
169
|
const [accent, setAccentState] = useState('purple')
|
|
168
170
|
const [density, setDensityState] = useState<SidebarDensity>('default')
|
|
169
|
-
const [font, setFontState] = useState('
|
|
171
|
+
const [font, setFontState] = useState('System UI')
|
|
170
172
|
const [reduceMotion, setReduceMotionState] = useState(false)
|
|
171
173
|
const [adminBrand, setAdminBrandState] = useState<AdminBrandCache>(DEFAULT_BRAND)
|
|
172
174
|
|
|
@@ -187,7 +189,7 @@ export function ThemeProvider({ children, defaultDarkMode }: ThemeProviderProps)
|
|
|
187
189
|
|
|
188
190
|
const storedAccent = localStorage.getItem(KEY_ACCENT) || 'purple'
|
|
189
191
|
const storedDensity = (localStorage.getItem(KEY_DENSITY) as SidebarDensity | null) || 'default'
|
|
190
|
-
const storedFont = localStorage.getItem(KEY_FONT) || '
|
|
192
|
+
const storedFont = localStorage.getItem(KEY_FONT) || 'System UI'
|
|
191
193
|
const storedMotion = localStorage.getItem(KEY_MOTION) === 'true'
|
|
192
194
|
let storedBrand = DEFAULT_BRAND
|
|
193
195
|
try {
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
} from '@dnd-kit/sortable'
|
|
24
24
|
import { CSS } from '@dnd-kit/utilities'
|
|
25
25
|
import { ChevronDown, GripVertical, Plus, Trash2 } from 'lucide-react'
|
|
26
|
-
import
|
|
26
|
+
import { Toggle } from '../ui/Toggle.js'
|
|
27
27
|
import { useId, useState } from 'react'
|
|
28
28
|
import type { FormField, FormFieldType } from '../../lib/forms-service.js'
|
|
29
29
|
import { FieldTypeBadge, FormsEmptyState } from './primitives.js'
|
|
@@ -353,14 +353,12 @@ function FieldEditor({
|
|
|
353
353
|
<label htmlFor={reqId} className="text-foreground text-sm font-medium">
|
|
354
354
|
Required
|
|
355
355
|
</label>
|
|
356
|
-
<
|
|
356
|
+
<Toggle
|
|
357
357
|
id={reqId}
|
|
358
358
|
checked={field.required}
|
|
359
359
|
onCheckedChange={(v) => onUpdate({ required: v })}
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
<Switch.Thumb className="bg-background block h-3.5 w-3.5 translate-x-0.5 rounded-full shadow-sm transition-transform data-[state=checked]:translate-x-[14px]" />
|
|
363
|
-
</Switch.Root>
|
|
360
|
+
aria-labelledby={reqId}
|
|
361
|
+
/>
|
|
364
362
|
</div>
|
|
365
363
|
</div>
|
|
366
364
|
)
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* autoresponder). Email delivery uses the configured email adapter when enabled.
|
|
7
7
|
*/
|
|
8
8
|
import { useEffect, useState } from 'react'
|
|
9
|
-
import
|
|
9
|
+
import { Toggle } from '../ui/Toggle.js'
|
|
10
10
|
import { toast } from 'sonner'
|
|
11
11
|
import {
|
|
12
12
|
fetchNotificationSettings,
|
|
@@ -210,14 +210,7 @@ function ToggleRow({
|
|
|
210
210
|
<span className="text-foreground block text-sm font-medium">{label}</span>
|
|
211
211
|
<span className="text-muted-foreground block text-sm">{help}</span>
|
|
212
212
|
</span>
|
|
213
|
-
<
|
|
214
|
-
checked={checked}
|
|
215
|
-
onCheckedChange={onChange}
|
|
216
|
-
aria-label={label}
|
|
217
|
-
className="bg-destructive data-[state=checked]:bg-success focus-visible:ring-ring relative h-[18px] w-8 shrink-0 rounded-full transition-colors focus-visible:ring-2 focus-visible:outline-none"
|
|
218
|
-
>
|
|
219
|
-
<Switch.Thumb className="bg-background block h-3.5 w-3.5 translate-x-0.5 rounded-full shadow-sm transition-transform data-[state=checked]:translate-x-[14px]" />
|
|
220
|
-
</Switch.Root>
|
|
213
|
+
<Toggle checked={checked} onCheckedChange={onChange} aria-label={label} />
|
|
221
214
|
</div>
|
|
222
215
|
)
|
|
223
216
|
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* (Zapier Catch Hook, Make, custom CRM endpoints) and guides editors through
|
|
6
6
|
* the default Zapier → Webhooks by Zapier → Catch Hook setup.
|
|
7
7
|
*/
|
|
8
|
-
import
|
|
8
|
+
import { Toggle } from '../ui/Toggle.js'
|
|
9
9
|
import { ExternalLink, Loader2, Plus, Trash2, Zap } from 'lucide-react'
|
|
10
10
|
import { useEffect, useState } from 'react'
|
|
11
11
|
import { toast } from 'sonner'
|
|
@@ -265,14 +265,12 @@ export function WebhooksPanel({ formId, formSlug }: { formId: string; formSlug:
|
|
|
265
265
|
</p>
|
|
266
266
|
</div>
|
|
267
267
|
<div className="flex shrink-0 flex-wrap items-center gap-2">
|
|
268
|
-
<
|
|
268
|
+
<Toggle
|
|
269
269
|
checked={webhook.enabled}
|
|
270
270
|
onCheckedChange={(v) => void toggleEnabled(webhook, v)}
|
|
271
271
|
aria-label={`Enable webhook ${webhook.url}`}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
<Switch.Thumb className="bg-background block h-3.5 w-3.5 translate-x-0.5 rounded-full shadow-sm transition-transform data-[state=checked]:translate-x-[14px]" />
|
|
275
|
-
</Switch.Root>
|
|
272
|
+
variant="enabled"
|
|
273
|
+
/>
|
|
276
274
|
<button
|
|
277
275
|
type="button"
|
|
278
276
|
className={btnSecondary}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import type { ReactNode } from 'react'
|
|
11
11
|
import { AlertTriangle, ChevronLeft, ChevronRight, Loader2 } from 'lucide-react'
|
|
12
|
+
import { StatusBadge, type StatusBadgeTone } from '../ui/StatusBadge.js'
|
|
12
13
|
import type { FormFieldType, FormStatus } from '../../lib/forms-service.js'
|
|
13
14
|
|
|
14
15
|
// ─── Field-type badges ─────────────────────────────────────────────────────
|
|
@@ -70,26 +71,15 @@ export function FieldTypeBadge({ type }: { type: FormFieldType }) {
|
|
|
70
71
|
|
|
71
72
|
// ─── Status badges ─────────────────────────────────────────────────────────
|
|
72
73
|
|
|
73
|
-
const STATUS_META: Record<FormStatus, { label: string;
|
|
74
|
-
active: { label: 'Active',
|
|
75
|
-
draft: { label: 'Draft',
|
|
76
|
-
archived: {
|
|
77
|
-
label: 'Archived',
|
|
78
|
-
cls: 'bg-muted text-muted-foreground',
|
|
79
|
-
dot: 'bg-muted-foreground',
|
|
80
|
-
},
|
|
74
|
+
const STATUS_META: Record<FormStatus, { label: string; tone: StatusBadgeTone }> = {
|
|
75
|
+
active: { label: 'Active', tone: 'success' },
|
|
76
|
+
draft: { label: 'Draft', tone: 'warning' },
|
|
77
|
+
archived: { label: 'Archived', tone: 'muted' },
|
|
81
78
|
}
|
|
82
79
|
|
|
83
80
|
export function FormStatusBadge({ status }: { status: FormStatus }) {
|
|
84
81
|
const meta = STATUS_META[status] ?? STATUS_META.draft
|
|
85
|
-
return
|
|
86
|
-
<span
|
|
87
|
-
className={`inline-flex items-center gap-1.5 rounded-full px-2.5 py-0.5 text-xs font-medium ${meta.cls}`}
|
|
88
|
-
>
|
|
89
|
-
<span className={`h-1.5 w-1.5 rounded-full ${meta.dot}`} aria-hidden />
|
|
90
|
-
{meta.label}
|
|
91
|
-
</span>
|
|
92
|
-
)
|
|
82
|
+
return <StatusBadge tone={meta.tone}>{meta.label}</StatusBadge>
|
|
93
83
|
}
|
|
94
84
|
|
|
95
85
|
/** Small red count badge for unread submissions. Hidden when count is 0. */
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
import type { ReactNode } from 'react'
|
|
4
|
+
import { StatusBadge, type StatusBadgeTone } from '../ui/StatusBadge.js'
|
|
4
5
|
import type { PostStatus } from '../../lib/posts-service.js'
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -65,32 +66,18 @@ const STATUS_LABEL: Record<PostStatus, string> = {
|
|
|
65
66
|
ARCHIVED: 'Archived',
|
|
66
67
|
}
|
|
67
68
|
|
|
68
|
-
const
|
|
69
|
-
PUBLISHED: '
|
|
70
|
-
DRAFT: '
|
|
71
|
-
SCHEDULED: '
|
|
72
|
-
ARCHIVED: '
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const STATUS_GLYPH: Record<PostStatus, string> = {
|
|
76
|
-
PUBLISHED: '●',
|
|
77
|
-
DRAFT: '○',
|
|
78
|
-
SCHEDULED: '◐',
|
|
79
|
-
ARCHIVED: '◌',
|
|
69
|
+
const STATUS_TONE: Record<PostStatus, StatusBadgeTone> = {
|
|
70
|
+
PUBLISHED: 'success',
|
|
71
|
+
DRAFT: 'neutral',
|
|
72
|
+
SCHEDULED: 'warning',
|
|
73
|
+
ARCHIVED: 'muted',
|
|
80
74
|
}
|
|
81
75
|
|
|
82
76
|
export function PostStatusBadge({ status }: { status: PostStatus }) {
|
|
83
|
-
const p = paletteFor(STATUS_PALETTE[status])
|
|
84
77
|
return (
|
|
85
|
-
<
|
|
86
|
-
className={`inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 text-xs font-medium ${p.bg} ${p.text}`}
|
|
87
|
-
aria-label={`Status: ${STATUS_LABEL[status]}`}
|
|
88
|
-
>
|
|
89
|
-
<span aria-hidden className="text-[10px] leading-none">
|
|
90
|
-
{STATUS_GLYPH[status]}
|
|
91
|
-
</span>
|
|
78
|
+
<StatusBadge tone={STATUS_TONE[status]} aria-label={`Status: ${STATUS_LABEL[status]}`}>
|
|
92
79
|
{STATUS_LABEL[status]}
|
|
93
|
-
</
|
|
80
|
+
</StatusBadge>
|
|
94
81
|
)
|
|
95
82
|
}
|
|
96
83
|
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
} from '../../lib/seo-service.js'
|
|
12
12
|
import { Drawer } from './Drawer.js'
|
|
13
13
|
import { btnPrimary, btnSecondary } from './primitives.js'
|
|
14
|
+
import { Toggle } from '../ui/Toggle.js'
|
|
14
15
|
|
|
15
16
|
const STATUS_CODES: { value: RedirectStatusCode; label: string }[] = [
|
|
16
17
|
{ value: 301, label: '301 — Permanent' },
|
|
@@ -197,19 +198,17 @@ export function RedirectEditorDrawer({
|
|
|
197
198
|
</div>
|
|
198
199
|
|
|
199
200
|
<div className="border-border space-y-3 rounded-lg border p-4">
|
|
200
|
-
<
|
|
201
|
+
<div className="flex items-center justify-between gap-3">
|
|
201
202
|
<span className="text-foreground text-sm font-medium">Enabled</span>
|
|
202
|
-
<
|
|
203
|
-
type="checkbox"
|
|
204
|
-
role="switch"
|
|
205
|
-
aria-checked={!!form.enabled}
|
|
203
|
+
<Toggle
|
|
206
204
|
checked={!!form.enabled}
|
|
207
|
-
|
|
208
|
-
|
|
205
|
+
onCheckedChange={(v) => update({ enabled: v })}
|
|
206
|
+
variant="enabled"
|
|
207
|
+
aria-label="Enabled"
|
|
209
208
|
/>
|
|
210
|
-
</
|
|
209
|
+
</div>
|
|
211
210
|
{!isGone && (
|
|
212
|
-
<
|
|
211
|
+
<div className="flex items-center justify-between gap-3">
|
|
213
212
|
<span className="min-w-0">
|
|
214
213
|
<span className="text-foreground block text-sm font-medium">
|
|
215
214
|
Preserve query string
|
|
@@ -218,15 +217,12 @@ export function RedirectEditorDrawer({
|
|
|
218
217
|
Append the original query params to the destination.
|
|
219
218
|
</span>
|
|
220
219
|
</span>
|
|
221
|
-
<
|
|
222
|
-
type="checkbox"
|
|
223
|
-
role="switch"
|
|
224
|
-
aria-checked={!!form.preserveQuery}
|
|
220
|
+
<Toggle
|
|
225
221
|
checked={!!form.preserveQuery}
|
|
226
|
-
|
|
227
|
-
|
|
222
|
+
onCheckedChange={(v) => update({ preserveQuery: v })}
|
|
223
|
+
aria-label="Preserve query string"
|
|
228
224
|
/>
|
|
229
|
-
</
|
|
225
|
+
</div>
|
|
230
226
|
)}
|
|
231
227
|
</div>
|
|
232
228
|
</div>
|
|
@@ -17,6 +17,7 @@ import { fixBrandVoiceContent } from '../../lib/brand-voice-client.js'
|
|
|
17
17
|
import { BrandVoiceAlignmentBadge } from '../BrandVoiceAlignmentBadge.js'
|
|
18
18
|
import { Drawer } from './Drawer.js'
|
|
19
19
|
import { SeoLoading, SeoErrorState, btnPrimary, btnSecondary, gradeMeta } from './primitives.js'
|
|
20
|
+
import { Toggle } from '../ui/Toggle.js'
|
|
20
21
|
|
|
21
22
|
const META_TITLE_MAX = 60
|
|
22
23
|
const META_DESC_MAX = 160
|
|
@@ -422,38 +423,32 @@ export function SeoEditorDrawer({
|
|
|
422
423
|
|
|
423
424
|
{/* Indexing toggles */}
|
|
424
425
|
<div className="border-border space-y-3 rounded-lg border p-4">
|
|
425
|
-
<
|
|
426
|
+
<div className="flex items-center justify-between gap-3">
|
|
426
427
|
<span className="min-w-0">
|
|
427
428
|
<span className="text-foreground block text-sm font-medium">No-index</span>
|
|
428
429
|
<span className="text-muted-foreground block text-sm">
|
|
429
430
|
Hide this page from search engines.
|
|
430
431
|
</span>
|
|
431
432
|
</span>
|
|
432
|
-
<
|
|
433
|
-
type="checkbox"
|
|
434
|
-
role="switch"
|
|
435
|
-
aria-checked={!!form.noindex}
|
|
433
|
+
<Toggle
|
|
436
434
|
checked={!!form.noindex}
|
|
437
|
-
|
|
438
|
-
|
|
435
|
+
onCheckedChange={(v) => update({ noindex: v })}
|
|
436
|
+
aria-label="No-index"
|
|
439
437
|
/>
|
|
440
|
-
</
|
|
441
|
-
<
|
|
438
|
+
</div>
|
|
439
|
+
<div className="flex items-center justify-between gap-3">
|
|
442
440
|
<span className="min-w-0">
|
|
443
441
|
<span className="text-foreground block text-sm font-medium">No-follow</span>
|
|
444
442
|
<span className="text-muted-foreground block text-sm">
|
|
445
443
|
Tell crawlers not to follow links on this page.
|
|
446
444
|
</span>
|
|
447
445
|
</span>
|
|
448
|
-
<
|
|
449
|
-
type="checkbox"
|
|
450
|
-
role="switch"
|
|
451
|
-
aria-checked={!!form.nofollow}
|
|
446
|
+
<Toggle
|
|
452
447
|
checked={!!form.nofollow}
|
|
453
|
-
|
|
454
|
-
|
|
448
|
+
onCheckedChange={(v) => update({ nofollow: v })}
|
|
449
|
+
aria-label="No-follow"
|
|
455
450
|
/>
|
|
456
|
-
</
|
|
451
|
+
</div>
|
|
457
452
|
</div>
|
|
458
453
|
|
|
459
454
|
{/* llms.txt curation */}
|
|
@@ -499,22 +494,19 @@ export function SeoEditorDrawer({
|
|
|
499
494
|
placeholder="Defaults to the content type"
|
|
500
495
|
/>
|
|
501
496
|
</Field>
|
|
502
|
-
<
|
|
497
|
+
<div className="flex items-center justify-between gap-3">
|
|
503
498
|
<span className="min-w-0">
|
|
504
499
|
<span className="text-foreground block text-sm font-medium">Optional</span>
|
|
505
500
|
<span className="text-muted-foreground block text-sm">
|
|
506
501
|
Group under “Optional” — agents skip these first when context is tight.
|
|
507
502
|
</span>
|
|
508
503
|
</span>
|
|
509
|
-
<
|
|
510
|
-
type="checkbox"
|
|
511
|
-
role="switch"
|
|
512
|
-
aria-checked={!!form.llmsOptional}
|
|
504
|
+
<Toggle
|
|
513
505
|
checked={!!form.llmsOptional}
|
|
514
|
-
|
|
515
|
-
|
|
506
|
+
onCheckedChange={(v) => update({ llmsOptional: v })}
|
|
507
|
+
aria-label="Optional in llms.txt"
|
|
516
508
|
/>
|
|
517
|
-
</
|
|
509
|
+
</div>
|
|
518
510
|
</div>
|
|
519
511
|
|
|
520
512
|
{/* Score summary */}
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import type { ReactNode } from 'react'
|
|
9
9
|
import { AlertTriangle, Loader2 } from 'lucide-react'
|
|
10
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../ui/Card.js'
|
|
11
|
+
import { StatusBadge, type StatusBadgeTone } from '../ui/StatusBadge.js'
|
|
10
12
|
import type { SeoGrade, SeoSeverity } from '../../lib/seo-service.js'
|
|
11
13
|
|
|
12
14
|
// ─── Grade + severity styling ────────────────────────────────────────
|
|
@@ -72,6 +74,14 @@ export function severityMeta(severity: SeoSeverity): GradeMeta {
|
|
|
72
74
|
|
|
73
75
|
// ─── Badges + bars ───────────────────────────────────────────────────
|
|
74
76
|
|
|
77
|
+
const SEO_TONE: Record<'good' | 'fair' | 'poor' | 'critical' | 'neutral', StatusBadgeTone> = {
|
|
78
|
+
good: 'success',
|
|
79
|
+
fair: 'warning',
|
|
80
|
+
poor: 'danger',
|
|
81
|
+
critical: 'danger',
|
|
82
|
+
neutral: 'neutral',
|
|
83
|
+
}
|
|
84
|
+
|
|
75
85
|
export function SeoStatusBadge({
|
|
76
86
|
label,
|
|
77
87
|
tone,
|
|
@@ -79,33 +89,7 @@ export function SeoStatusBadge({
|
|
|
79
89
|
label: string
|
|
80
90
|
tone: 'good' | 'fair' | 'poor' | 'critical' | 'neutral'
|
|
81
91
|
}) {
|
|
82
|
-
|
|
83
|
-
tone === 'good'
|
|
84
|
-
? 'bg-success/10 text-success'
|
|
85
|
-
: tone === 'fair'
|
|
86
|
-
? 'bg-warning/10 text-warning'
|
|
87
|
-
: tone === 'poor' || tone === 'critical'
|
|
88
|
-
? 'bg-destructive/10 text-destructive'
|
|
89
|
-
: 'bg-muted text-muted-foreground'
|
|
90
|
-
return (
|
|
91
|
-
<span
|
|
92
|
-
className={`inline-flex items-center gap-1.5 rounded-full px-2.5 py-0.5 text-xs font-medium ${cls}`}
|
|
93
|
-
>
|
|
94
|
-
<span
|
|
95
|
-
className={`h-1.5 w-1.5 rounded-full ${
|
|
96
|
-
tone === 'good'
|
|
97
|
-
? 'bg-success'
|
|
98
|
-
: tone === 'fair'
|
|
99
|
-
? 'bg-warning'
|
|
100
|
-
: tone === 'neutral'
|
|
101
|
-
? 'bg-muted-foreground'
|
|
102
|
-
: 'bg-destructive'
|
|
103
|
-
}`}
|
|
104
|
-
aria-hidden
|
|
105
|
-
/>
|
|
106
|
-
{label}
|
|
107
|
-
</span>
|
|
108
|
-
)
|
|
92
|
+
return <StatusBadge tone={SEO_TONE[tone]}>{label}</StatusBadge>
|
|
109
93
|
}
|
|
110
94
|
|
|
111
95
|
/** A horizontal score bar (0-100), coloured by grade. */
|
|
@@ -159,18 +143,18 @@ export function SectionCard({
|
|
|
159
143
|
className?: string
|
|
160
144
|
}) {
|
|
161
145
|
return (
|
|
162
|
-
<
|
|
146
|
+
<Card padding="none" className={className} role="region">
|
|
163
147
|
{(title || action) && (
|
|
164
|
-
<
|
|
148
|
+
<CardHeader className="flex items-center justify-between gap-3">
|
|
165
149
|
<div className="min-w-0">
|
|
166
|
-
{title && <
|
|
167
|
-
{description && <
|
|
150
|
+
{title && <CardTitle>{title}</CardTitle>}
|
|
151
|
+
{description && <CardDescription>{description}</CardDescription>}
|
|
168
152
|
</div>
|
|
169
153
|
{action}
|
|
170
|
-
</
|
|
154
|
+
</CardHeader>
|
|
171
155
|
)}
|
|
172
|
-
<
|
|
173
|
-
</
|
|
156
|
+
<CardContent className={title || action ? 'pt-0' : ''}>{children}</CardContent>
|
|
157
|
+
</Card>
|
|
174
158
|
)
|
|
175
159
|
}
|
|
176
160
|
|
|
@@ -5,7 +5,7 @@ import { cv, type VariantProps } from '../../lib/cv.js'
|
|
|
5
5
|
|
|
6
6
|
const button = cv(
|
|
7
7
|
// Base classes apply to every variant.
|
|
8
|
-
'focus-visible:ring-offset-background inline-flex items-center justify-center gap-2 rounded-[var(--
|
|
8
|
+
'focus-visible:ring-offset-background inline-flex items-center justify-center gap-2 rounded-[var(--r-control)] font-medium transition-colors duration-[var(--motion-fast)] focus-visible:ring-2 focus-visible:ring-[var(--ring)] focus-visible:ring-offset-2 focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50',
|
|
9
9
|
{
|
|
10
10
|
variants: {
|
|
11
11
|
variant: {
|
|
@@ -2,29 +2,31 @@ import { forwardRef, type HTMLAttributes, type ReactNode } from 'react'
|
|
|
2
2
|
import { cv, type VariantProps } from '../../lib/cv.js'
|
|
3
3
|
|
|
4
4
|
const card = cv(
|
|
5
|
-
'rounded-[var(--
|
|
5
|
+
'overflow-hidden rounded-[var(--r)] border border-[var(--bdr)] bg-[var(--card)] text-[var(--txt)]',
|
|
6
6
|
{
|
|
7
7
|
variants: {
|
|
8
8
|
padding: {
|
|
9
9
|
none: 'p-0',
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
/** Stat / compact surfaces (`.dstat`). */
|
|
11
|
+
sm: 'px-4 py-3.5',
|
|
12
|
+
/** Simple padded panel (`.dcard-b`). */
|
|
13
|
+
md: 'px-[18px] py-4',
|
|
14
|
+
lg: 'px-[18px] py-6',
|
|
13
15
|
},
|
|
14
16
|
elevation: {
|
|
15
17
|
flat: 'shadow-none',
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
lg: 'shadow-lg',
|
|
18
|
+
resting: 'shadow-[var(--shd)]',
|
|
19
|
+
elevated: 'shadow-[var(--shd2)]',
|
|
19
20
|
},
|
|
20
21
|
},
|
|
21
|
-
defaultVariants: { padding: '
|
|
22
|
+
defaultVariants: { padding: 'none', elevation: 'resting' },
|
|
22
23
|
},
|
|
23
24
|
)
|
|
24
25
|
|
|
25
26
|
export type CardVariants = VariantProps<typeof card>
|
|
26
27
|
|
|
27
|
-
const INTERACTIVE_CLASS =
|
|
28
|
+
const INTERACTIVE_CLASS =
|
|
29
|
+
'motion-safe:transition-shadow motion-safe:duration-[var(--motion-fast)] hover:shadow-[var(--shd2)] focus-within:shadow-[var(--shd2)]'
|
|
28
30
|
|
|
29
31
|
export interface CardProps extends HTMLAttributes<HTMLDivElement>, CardVariants {
|
|
30
32
|
/** Apply hover/focus elevation transitions (e.g. for clickable cards). */
|
|
@@ -44,12 +46,20 @@ export const Card = forwardRef<HTMLDivElement, CardProps>(function Card(
|
|
|
44
46
|
)
|
|
45
47
|
})
|
|
46
48
|
|
|
47
|
-
/**
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
/** Header slot — `.dcard-h` in the design system handoff. */
|
|
50
|
+
export function CardHeader({
|
|
51
|
+
className = '',
|
|
52
|
+
bordered = true,
|
|
53
|
+
children,
|
|
54
|
+
...rest
|
|
55
|
+
}: HTMLAttributes<HTMLDivElement> & { bordered?: boolean }) {
|
|
51
56
|
return (
|
|
52
|
-
<div
|
|
57
|
+
<div
|
|
58
|
+
className={[bordered ? 'border-b border-[var(--bdr)] px-[18px] py-[14px]' : '', className]
|
|
59
|
+
.filter(Boolean)
|
|
60
|
+
.join(' ')}
|
|
61
|
+
{...rest}
|
|
62
|
+
>
|
|
53
63
|
{children}
|
|
54
64
|
</div>
|
|
55
65
|
)
|
|
@@ -62,7 +72,7 @@ export function CardTitle({
|
|
|
62
72
|
}: HTMLAttributes<HTMLHeadingElement>) {
|
|
63
73
|
return (
|
|
64
74
|
<h3
|
|
65
|
-
className={`text-
|
|
75
|
+
className={`text-[length:var(--fs-card)] font-semibold text-[var(--txt)] ${className}`}
|
|
66
76
|
{...rest}
|
|
67
77
|
>
|
|
68
78
|
{children}
|
|
@@ -76,15 +86,19 @@ export function CardDescription({
|
|
|
76
86
|
...rest
|
|
77
87
|
}: HTMLAttributes<HTMLParagraphElement>) {
|
|
78
88
|
return (
|
|
79
|
-
<p className={`text-
|
|
89
|
+
<p className={`mt-px text-[length:var(--fs-hint)] text-[var(--muted)] ${className}`} {...rest}>
|
|
80
90
|
{children}
|
|
81
91
|
</p>
|
|
82
92
|
)
|
|
83
93
|
}
|
|
84
94
|
|
|
95
|
+
/** Body slot — `.dcard-b` in the design system handoff. */
|
|
85
96
|
export function CardContent({ className = '', children, ...rest }: HTMLAttributes<HTMLDivElement>) {
|
|
86
97
|
return (
|
|
87
|
-
<div
|
|
98
|
+
<div
|
|
99
|
+
className={`px-[18px] py-4 text-[length:var(--fs-body)] leading-[1.55] text-[var(--sub)] ${className}`}
|
|
100
|
+
{...rest}
|
|
101
|
+
>
|
|
88
102
|
{children}
|
|
89
103
|
</div>
|
|
90
104
|
)
|
|
@@ -92,7 +106,10 @@ export function CardContent({ className = '', children, ...rest }: HTMLAttribute
|
|
|
92
106
|
|
|
93
107
|
export function CardFooter({ className = '', children, ...rest }: HTMLAttributes<HTMLDivElement>) {
|
|
94
108
|
return (
|
|
95
|
-
<div
|
|
109
|
+
<div
|
|
110
|
+
className={`flex items-center justify-end gap-2 border-t border-[var(--bdr)] px-[18px] py-3 ${className}`}
|
|
111
|
+
{...rest}
|
|
112
|
+
>
|
|
96
113
|
{children}
|
|
97
114
|
</div>
|
|
98
115
|
)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import type { ButtonHTMLAttributes, ReactNode } from 'react'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Design-system filter pill (`.pill` / `.pill.act`): bordered card-surface
|
|
7
|
+
* chip that fills with the accent color when active. Used for status / role /
|
|
8
|
+
* category filter rows above list views.
|
|
9
|
+
*/
|
|
10
|
+
export interface FilterPillProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'type'> {
|
|
11
|
+
active?: boolean
|
|
12
|
+
/** Optional trailing count rendered in tabular numerals. */
|
|
13
|
+
count?: number
|
|
14
|
+
children: ReactNode
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function FilterPill({
|
|
18
|
+
active = false,
|
|
19
|
+
count,
|
|
20
|
+
className,
|
|
21
|
+
children,
|
|
22
|
+
...rest
|
|
23
|
+
}: FilterPillProps) {
|
|
24
|
+
return (
|
|
25
|
+
<button
|
|
26
|
+
type="button"
|
|
27
|
+
aria-pressed={active}
|
|
28
|
+
className={`inline-flex items-center gap-1.5 rounded-[7px] border px-3 py-1.5 text-[12.5px] font-medium transition-colors duration-[var(--motion-fast)] focus-visible:ring-2 focus-visible:ring-[var(--ring)] focus-visible:outline-none ${
|
|
29
|
+
active
|
|
30
|
+
? 'border-[var(--acc)] bg-[var(--acc)] text-white'
|
|
31
|
+
: 'border-[var(--bdr)] bg-[var(--card)] text-[var(--sub)] hover:border-[var(--acc-m)] hover:text-[var(--acc)]'
|
|
32
|
+
} ${className ?? ''}`}
|
|
33
|
+
{...rest}
|
|
34
|
+
>
|
|
35
|
+
{children}
|
|
36
|
+
{count !== undefined && (
|
|
37
|
+
<span className={`tabular-nums ${active ? 'text-white/80' : 'text-[var(--muted)]'}`}>
|
|
38
|
+
{count}
|
|
39
|
+
</span>
|
|
40
|
+
)}
|
|
41
|
+
</button>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
@@ -1,24 +1,22 @@
|
|
|
1
1
|
import { forwardRef, type InputHTMLAttributes } from 'react'
|
|
2
2
|
import { cv, type VariantProps } from '../../lib/cv.js'
|
|
3
|
+
import { formControlBase, formControlState } from './form-control.js'
|
|
3
4
|
|
|
4
|
-
const input = cv(
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
invalid: 'border-[var(--destructive)] focus-visible:ring-[var(--destructive)]',
|
|
16
|
-
valid: 'border-emerald-500 focus-visible:ring-emerald-500',
|
|
17
|
-
},
|
|
5
|
+
const input = cv(formControlBase, {
|
|
6
|
+
variants: {
|
|
7
|
+
size: {
|
|
8
|
+
sm: 'py-1.5 text-[length:var(--fs-sub)]',
|
|
9
|
+
md: '',
|
|
10
|
+
lg: 'py-2.5 text-[length:var(--fs-body)]',
|
|
11
|
+
},
|
|
12
|
+
state: {
|
|
13
|
+
default: formControlState.default,
|
|
14
|
+
invalid: formControlState.invalid,
|
|
15
|
+
valid: formControlState.valid,
|
|
18
16
|
},
|
|
19
|
-
defaultVariants: { size: 'md', state: 'default' },
|
|
20
17
|
},
|
|
21
|
-
|
|
18
|
+
defaultVariants: { size: 'md', state: 'default' },
|
|
19
|
+
})
|
|
22
20
|
|
|
23
21
|
export type InputVariants = VariantProps<typeof input>
|
|
24
22
|
|
|
@@ -32,4 +30,4 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(function Input(
|
|
|
32
30
|
return <input ref={ref} className={input({ size, state, class: className })} {...rest} />
|
|
33
31
|
})
|
|
34
32
|
|
|
35
|
-
export { input as inputVariants }
|
|
33
|
+
export { input as inputVariants, formControlBase, formControlState }
|