@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
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
+
import { inputVariants } from './Input.js'
|
|
4
|
+
|
|
3
5
|
export interface SearchInputProps {
|
|
4
6
|
value: string
|
|
5
7
|
onChange: (value: string) => void
|
|
@@ -10,11 +12,12 @@ export function SearchInput({ value, onChange, placeholder = 'Search...' }: Sear
|
|
|
10
12
|
return (
|
|
11
13
|
<div className="relative flex-1">
|
|
12
14
|
<svg
|
|
13
|
-
className="absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-[var(--muted
|
|
15
|
+
className="absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-[var(--muted)]"
|
|
14
16
|
fill="none"
|
|
15
17
|
viewBox="0 0 24 24"
|
|
16
18
|
stroke="currentColor"
|
|
17
19
|
strokeWidth={2}
|
|
20
|
+
aria-hidden
|
|
18
21
|
>
|
|
19
22
|
<path
|
|
20
23
|
strokeLinecap="round"
|
|
@@ -27,12 +30,13 @@ export function SearchInput({ value, onChange, placeholder = 'Search...' }: Sear
|
|
|
27
30
|
value={value}
|
|
28
31
|
onChange={(e) => onChange(e.target.value)}
|
|
29
32
|
placeholder={placeholder}
|
|
30
|
-
className=
|
|
33
|
+
className={inputVariants({ class: 'py-2 pr-8 pl-9' })}
|
|
31
34
|
/>
|
|
32
35
|
{value && (
|
|
33
36
|
<button
|
|
37
|
+
type="button"
|
|
34
38
|
onClick={() => onChange('')}
|
|
35
|
-
className="absolute top-1/2 right-2 -translate-y-1/2 rounded p-0.5 text-[var(--muted
|
|
39
|
+
className="absolute top-1/2 right-2 -translate-y-1/2 rounded p-0.5 text-[var(--muted)] hover:text-[var(--txt)]"
|
|
36
40
|
aria-label="Clear search"
|
|
37
41
|
>
|
|
38
42
|
<svg
|
|
@@ -41,12 +45,13 @@ export function SearchInput({ value, onChange, placeholder = 'Search...' }: Sear
|
|
|
41
45
|
viewBox="0 0 24 24"
|
|
42
46
|
stroke="currentColor"
|
|
43
47
|
strokeWidth={2}
|
|
48
|
+
aria-hidden
|
|
44
49
|
>
|
|
45
50
|
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
|
|
46
51
|
</svg>
|
|
47
52
|
</button>
|
|
48
53
|
)}
|
|
49
|
-
<kbd className="pointer-events-none absolute top-1/2 right-8 hidden -translate-y-1/2 rounded border border-[var(--
|
|
54
|
+
<kbd className="pointer-events-none absolute top-1/2 right-8 hidden -translate-y-1/2 rounded border border-[var(--bdr)] px-1.5 py-0.5 text-[10px] text-[var(--muted)] sm:inline-block">
|
|
50
55
|
⌘K
|
|
51
56
|
</kbd>
|
|
52
57
|
</div>
|
|
@@ -1,25 +1,22 @@
|
|
|
1
1
|
import { forwardRef, type SelectHTMLAttributes, type ReactNode } from 'react'
|
|
2
2
|
import { cv, type VariantProps } from '../../lib/cv.js'
|
|
3
|
+
import { formControlBase, formControlState } from './form-control.js'
|
|
3
4
|
|
|
4
|
-
const select = cv(
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
default: 'border-[var(--border)]',
|
|
16
|
-
invalid: 'border-[var(--destructive)] focus-visible:ring-[var(--destructive)]',
|
|
17
|
-
valid: 'border-emerald-500 focus-visible:ring-emerald-500',
|
|
18
|
-
},
|
|
5
|
+
const select = cv(`${formControlBase} appearance-none pr-8`, {
|
|
6
|
+
variants: {
|
|
7
|
+
size: {
|
|
8
|
+
sm: 'py-1.5 pr-7 pl-3 text-[length:var(--fs-sub)]',
|
|
9
|
+
md: '',
|
|
10
|
+
lg: 'py-2.5 pr-9 pl-3',
|
|
11
|
+
},
|
|
12
|
+
state: {
|
|
13
|
+
default: formControlState.default,
|
|
14
|
+
invalid: formControlState.invalid,
|
|
15
|
+
valid: formControlState.valid,
|
|
19
16
|
},
|
|
20
|
-
defaultVariants: { size: 'md', state: 'default' },
|
|
21
17
|
},
|
|
22
|
-
|
|
18
|
+
defaultVariants: { size: 'md', state: 'default' },
|
|
19
|
+
})
|
|
23
20
|
|
|
24
21
|
export type SelectVariants = VariantProps<typeof select>
|
|
25
22
|
|
|
@@ -39,7 +36,7 @@ export const Select = forwardRef<HTMLSelectElement, SelectProps>(function Select
|
|
|
39
36
|
</select>
|
|
40
37
|
<svg
|
|
41
38
|
aria-hidden
|
|
42
|
-
className="pointer-events-none absolute top-1/2 right-2 h-4 w-4 -translate-y-1/2 text-[var(--muted
|
|
39
|
+
className="pointer-events-none absolute top-1/2 right-2 h-4 w-4 -translate-y-1/2 text-[var(--muted)]"
|
|
43
40
|
viewBox="0 0 20 20"
|
|
44
41
|
fill="currentColor"
|
|
45
42
|
>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { HTMLAttributes, ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Design-system stat pill (`.stat-pill`): a compact card-surface chip with a
|
|
5
|
+
* bold count and a muted label, used in page headers to summarize list
|
|
6
|
+
* contents (e.g. "12 Total · 8 Active · 4 Inactive").
|
|
7
|
+
*/
|
|
8
|
+
export interface StatPillProps extends HTMLAttributes<HTMLSpanElement> {
|
|
9
|
+
/** The count/value, rendered bold in `--txt` (override color via valueClassName). */
|
|
10
|
+
value: ReactNode
|
|
11
|
+
/** Muted label shown after the value. */
|
|
12
|
+
label: ReactNode
|
|
13
|
+
/** Optional leading icon. */
|
|
14
|
+
icon?: ReactNode
|
|
15
|
+
/** Extra classes for the value (e.g. `text-[var(--suc)]` for an Active count). */
|
|
16
|
+
valueClassName?: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function StatPill({
|
|
20
|
+
value,
|
|
21
|
+
label,
|
|
22
|
+
icon,
|
|
23
|
+
valueClassName,
|
|
24
|
+
className,
|
|
25
|
+
...rest
|
|
26
|
+
}: StatPillProps) {
|
|
27
|
+
return (
|
|
28
|
+
<span
|
|
29
|
+
className={`inline-flex items-center gap-[5px] rounded-[6px] border border-[var(--bdr)] bg-[var(--card)] px-2.5 py-1 text-[length:var(--fs-sub)] text-[var(--sub)] ${className ?? ''}`}
|
|
30
|
+
{...rest}
|
|
31
|
+
>
|
|
32
|
+
{icon}
|
|
33
|
+
<b className={`font-semibold text-[var(--txt)] ${valueClassName ?? ''}`}>{value}</b>
|
|
34
|
+
{label}
|
|
35
|
+
</span>
|
|
36
|
+
)
|
|
37
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { HTMLAttributes, ReactNode } from 'react'
|
|
2
|
+
import { cv, type VariantProps } from '../../lib/cv.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Canonical status chip from the design system (`.status` + `.status-dot`):
|
|
6
|
+
* a rectangular 5px-radius chip with a 5px dot, 11.5px medium text, and
|
|
7
|
+
* tint backgrounds per tone. This is the single source of truth for
|
|
8
|
+
* document / entity statuses across Posts, Pages, Forms, SEO, and tables —
|
|
9
|
+
* do not roll per-view status pills.
|
|
10
|
+
*/
|
|
11
|
+
const statusBadge = cv(
|
|
12
|
+
'inline-flex items-center gap-[5px] rounded-[5px] px-[9px] py-[3px] text-[length:var(--fs-hint)] font-medium whitespace-nowrap',
|
|
13
|
+
{
|
|
14
|
+
variants: {
|
|
15
|
+
tone: {
|
|
16
|
+
/** Published / Active — success tint (`.s-pub`). */
|
|
17
|
+
success: 'bg-[var(--suc-l)] text-[var(--suc)]',
|
|
18
|
+
/** Scheduled / Pending — warning tint (`.s-sch`). */
|
|
19
|
+
warning: 'bg-[var(--warn-l)] text-[var(--warn)]',
|
|
20
|
+
/** Errors / failing states. */
|
|
21
|
+
danger: 'bg-[var(--err-l)] text-[var(--err)]',
|
|
22
|
+
/** Informational states. */
|
|
23
|
+
info: 'bg-[var(--info-l)] text-[var(--info)]',
|
|
24
|
+
/** Draft — page background + border (`.s-dft`). */
|
|
25
|
+
neutral: 'border border-[var(--bdr)] bg-[var(--bg)] text-[var(--sub)]',
|
|
26
|
+
/** Archived — dimmest treatment (`.s-arc`). */
|
|
27
|
+
muted: 'border border-[var(--bdr)] bg-[var(--bg)] text-[var(--muted)]',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
defaultVariants: { tone: 'neutral' },
|
|
31
|
+
},
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
const DOT_CLASS: Record<NonNullable<StatusBadgeVariants['tone']>, string> = {
|
|
35
|
+
success: 'bg-[var(--suc)]',
|
|
36
|
+
warning: 'bg-[var(--warn)]',
|
|
37
|
+
danger: 'bg-[var(--err)]',
|
|
38
|
+
info: 'bg-[var(--info)]',
|
|
39
|
+
neutral: 'bg-[var(--muted)]',
|
|
40
|
+
muted: 'bg-[var(--muted)]',
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type StatusBadgeVariants = VariantProps<typeof statusBadge>
|
|
44
|
+
export type StatusBadgeTone = NonNullable<StatusBadgeVariants['tone']>
|
|
45
|
+
|
|
46
|
+
export interface StatusBadgeProps extends HTMLAttributes<HTMLSpanElement>, StatusBadgeVariants {
|
|
47
|
+
/** Hide the leading dot (rare — e.g. numeric chips). */
|
|
48
|
+
dot?: boolean
|
|
49
|
+
children: ReactNode
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function StatusBadge({ tone, dot = true, className, children, ...rest }: StatusBadgeProps) {
|
|
53
|
+
return (
|
|
54
|
+
<span className={statusBadge({ tone, class: className })} {...rest}>
|
|
55
|
+
{dot && (
|
|
56
|
+
<span
|
|
57
|
+
aria-hidden
|
|
58
|
+
className={`h-[5px] w-[5px] shrink-0 rounded-full ${DOT_CLASS[tone ?? 'neutral']}`}
|
|
59
|
+
/>
|
|
60
|
+
)}
|
|
61
|
+
{children}
|
|
62
|
+
</span>
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export { statusBadge as statusBadgeVariants }
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import * as Switch from '@radix-ui/react-switch'
|
|
4
|
+
import { cv, type VariantProps } from '../../lib/cv.js'
|
|
5
|
+
|
|
6
|
+
const toggleTrack = cv(
|
|
7
|
+
'relative inline-flex shrink-0 cursor-pointer rounded-[var(--r-pill)] transition-colors duration-[var(--motion-base)] focus-visible:ring-2 focus-visible:ring-[var(--ring)] focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50',
|
|
8
|
+
{
|
|
9
|
+
variants: {
|
|
10
|
+
variant: {
|
|
11
|
+
/** Design system default — accent when on. */
|
|
12
|
+
accent: 'bg-[var(--bdr)] data-[state=checked]:bg-[var(--acc)]',
|
|
13
|
+
/** Semantic enable/disable — success green when on (tables, script tags). */
|
|
14
|
+
enabled: 'bg-[var(--bdr)] data-[state=checked]:bg-[var(--suc)]',
|
|
15
|
+
},
|
|
16
|
+
size: {
|
|
17
|
+
/** Matches `.tog-track` in the design system handoff (38×21). */
|
|
18
|
+
md: 'h-[21px] w-[38px]',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
defaultVariants: {
|
|
22
|
+
variant: 'accent',
|
|
23
|
+
size: 'md',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
const toggleThumb = cv(
|
|
29
|
+
'pointer-events-none block rounded-full bg-white shadow-[0_1px_3px_rgba(0,0,0,0.2)] transition-transform duration-[var(--motion-base)]',
|
|
30
|
+
{
|
|
31
|
+
variants: {
|
|
32
|
+
size: {
|
|
33
|
+
md: 'h-4 w-4 translate-x-0.5 data-[state=checked]:translate-x-[17.5px]',
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
defaultVariants: {
|
|
37
|
+
size: 'md',
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
export type ToggleVariants = VariantProps<typeof toggleTrack>
|
|
43
|
+
|
|
44
|
+
export interface ToggleProps extends ToggleVariants {
|
|
45
|
+
checked: boolean
|
|
46
|
+
onCheckedChange: (checked: boolean) => void
|
|
47
|
+
disabled?: boolean
|
|
48
|
+
id?: string
|
|
49
|
+
'aria-label'?: string
|
|
50
|
+
'aria-labelledby'?: string
|
|
51
|
+
'aria-describedby'?: string
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Design-system toggle (`.tog-track` / `.tog-thumb`). Use `variant="enabled"`
|
|
56
|
+
* for on/off tables where green communicates active state.
|
|
57
|
+
*/
|
|
58
|
+
export function Toggle({
|
|
59
|
+
checked,
|
|
60
|
+
onCheckedChange,
|
|
61
|
+
disabled,
|
|
62
|
+
variant,
|
|
63
|
+
size,
|
|
64
|
+
id,
|
|
65
|
+
'aria-label': ariaLabel,
|
|
66
|
+
'aria-labelledby': ariaLabelledBy,
|
|
67
|
+
'aria-describedby': ariaDescribedBy,
|
|
68
|
+
}: ToggleProps) {
|
|
69
|
+
return (
|
|
70
|
+
<Switch.Root
|
|
71
|
+
id={id}
|
|
72
|
+
checked={checked}
|
|
73
|
+
onCheckedChange={onCheckedChange}
|
|
74
|
+
disabled={disabled}
|
|
75
|
+
aria-label={ariaLabel}
|
|
76
|
+
aria-labelledby={ariaLabelledBy}
|
|
77
|
+
aria-describedby={ariaDescribedBy}
|
|
78
|
+
className={toggleTrack({ variant, size })}
|
|
79
|
+
>
|
|
80
|
+
<Switch.Thumb className={toggleThumb({ size })} />
|
|
81
|
+
</Switch.Root>
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export { toggleTrack as toggleTrackVariants, toggleThumb as toggleThumbVariants }
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared form-control surface styles (`.inp` in the design system handoff).
|
|
3
|
+
* Imported by Input and Select so geometry stays aligned.
|
|
4
|
+
*/
|
|
5
|
+
export const formControlBase =
|
|
6
|
+
'w-full rounded-[var(--r-control)] border border-[var(--bdr)] bg-[var(--bg)] px-3 py-[9px] text-[length:var(--fs-body)] text-[var(--txt)] outline-none transition-[border-color] duration-150 placeholder:text-[var(--muted)] focus:border-[var(--acc-m)] focus-visible:border-[var(--acc-m)] disabled:cursor-not-allowed disabled:opacity-50'
|
|
7
|
+
|
|
8
|
+
export const formControlState = {
|
|
9
|
+
default: '',
|
|
10
|
+
invalid: 'border-[var(--err)] focus:border-[var(--err)] focus-visible:border-[var(--err)]',
|
|
11
|
+
valid: 'border-[var(--suc)] focus:border-[var(--suc)] focus-visible:border-[var(--suc)]',
|
|
12
|
+
} as const
|
|
@@ -34,5 +34,14 @@ export { Pagination } from './Pagination.js'
|
|
|
34
34
|
export type { PaginationProps } from './Pagination.js'
|
|
35
35
|
export { SearchInput } from './SearchInput.js'
|
|
36
36
|
export type { SearchInputProps } from './SearchInput.js'
|
|
37
|
+
export { Toggle, toggleTrackVariants, toggleThumbVariants } from './Toggle.js'
|
|
38
|
+
export type { ToggleProps, ToggleVariants } from './Toggle.js'
|
|
39
|
+
export { StatusBadge, statusBadgeVariants } from './StatusBadge.js'
|
|
40
|
+
export type { StatusBadgeProps, StatusBadgeVariants, StatusBadgeTone } from './StatusBadge.js'
|
|
41
|
+
export { FilterPill } from './FilterPill.js'
|
|
42
|
+
export type { FilterPillProps } from './FilterPill.js'
|
|
43
|
+
export { StatPill } from './StatPill.js'
|
|
44
|
+
export type { StatPillProps } from './StatPill.js'
|
|
45
|
+
export { formControlBase, formControlState } from './form-control.js'
|
|
37
46
|
export { CommandPalette } from './CommandPalette.js'
|
|
38
47
|
export type { CommandPaletteProps } from './CommandPalette.js'
|
package/src/fields/PropInput.tsx
CHANGED
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
safeJsonStringify,
|
|
39
39
|
switchUnionVariant,
|
|
40
40
|
} from './component-block-helpers.js'
|
|
41
|
+
import { Toggle } from '../components/ui/Toggle.js'
|
|
41
42
|
|
|
42
43
|
export { defaultForType }
|
|
43
44
|
|
|
@@ -249,21 +250,11 @@ function BooleanInput({
|
|
|
249
250
|
return (
|
|
250
251
|
<div className="flex items-center justify-between text-sm">
|
|
251
252
|
<span>{label}</span>
|
|
252
|
-
<
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
aria-
|
|
256
|
-
|
|
257
|
-
className={`relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors ${
|
|
258
|
-
boolVal ? 'bg-[var(--primary)]' : 'bg-[var(--muted)]'
|
|
259
|
-
}`}
|
|
260
|
-
>
|
|
261
|
-
<span
|
|
262
|
-
className={`bg-card pointer-events-none inline-block h-5 w-5 rounded-full shadow-sm ring-0 transition-transform ${
|
|
263
|
-
boolVal ? 'translate-x-5' : 'translate-x-0'
|
|
264
|
-
}`}
|
|
265
|
-
/>
|
|
266
|
-
</button>
|
|
253
|
+
<Toggle
|
|
254
|
+
checked={boolVal}
|
|
255
|
+
onCheckedChange={(v) => onChange(v)}
|
|
256
|
+
aria-label={typeof label === 'string' ? label : undefined}
|
|
257
|
+
/>
|
|
267
258
|
</div>
|
|
268
259
|
)
|
|
269
260
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
+
import { Toggle } from '../components/ui/Toggle.js'
|
|
4
|
+
|
|
3
5
|
export interface ToggleFieldProps {
|
|
4
6
|
label: string
|
|
5
7
|
value?: boolean
|
|
@@ -14,21 +16,7 @@ export function ToggleField({ label, value = false, onChange, helpText }: Toggle
|
|
|
14
16
|
<span className="text-sm font-medium">{label}</span>
|
|
15
17
|
{helpText && <p className="text-xs text-[var(--muted-foreground)]">{helpText}</p>}
|
|
16
18
|
</div>
|
|
17
|
-
<
|
|
18
|
-
type="button"
|
|
19
|
-
role="switch"
|
|
20
|
-
aria-checked={value}
|
|
21
|
-
onClick={() => onChange(!value)}
|
|
22
|
-
className={`relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors ${
|
|
23
|
-
value ? 'bg-[var(--primary)]' : 'bg-[var(--muted)]'
|
|
24
|
-
}`}
|
|
25
|
-
>
|
|
26
|
-
<span
|
|
27
|
-
className={`bg-card pointer-events-none inline-block h-5 w-5 rounded-full shadow-sm ring-0 transition-transform ${
|
|
28
|
-
value ? 'translate-x-5' : 'translate-x-0'
|
|
29
|
-
}`}
|
|
30
|
-
/>
|
|
31
|
-
</button>
|
|
19
|
+
<Toggle checked={value} onCheckedChange={onChange} aria-label={label} />
|
|
32
20
|
</div>
|
|
33
21
|
)
|
|
34
22
|
}
|
package/src/layout/Header.tsx
CHANGED
|
@@ -8,6 +8,7 @@ import { CommandPalette } from '../components/CommandPalette.js'
|
|
|
8
8
|
import { NotificationBell } from '../components/NotificationBell.js'
|
|
9
9
|
import { useTheme } from '../components/ThemeProvider.js'
|
|
10
10
|
import { LocaleSwitcher } from '../components/LocaleSwitcher.js'
|
|
11
|
+
import { inputVariants } from '../components/ui/Input.js'
|
|
11
12
|
|
|
12
13
|
export interface HeaderProps {
|
|
13
14
|
onToggleSidebar: () => void
|
|
@@ -51,7 +52,7 @@ export function Header({
|
|
|
51
52
|
|
|
52
53
|
return (
|
|
53
54
|
<>
|
|
54
|
-
<header className="border-border bg-
|
|
55
|
+
<header className="border-sidebar-border bg-sidebar flex h-[var(--tbh)] items-center gap-3 border-b px-4">
|
|
55
56
|
<button
|
|
56
57
|
onClick={onToggleSidebar}
|
|
57
58
|
className="hover:bg-accent rounded-lg p-2 transition-colors md:hidden"
|
|
@@ -77,7 +78,10 @@ export function Header({
|
|
|
77
78
|
onKeyDown={(e) => {
|
|
78
79
|
if (e.key === 'Escape') (e.target as HTMLInputElement).blur()
|
|
79
80
|
}}
|
|
80
|
-
className=
|
|
81
|
+
className={inputVariants({
|
|
82
|
+
class:
|
|
83
|
+
'h-9 w-full max-w-md py-1.5 pr-14 pl-9 focus-visible:ring-0 focus-visible:ring-offset-0',
|
|
84
|
+
})}
|
|
81
85
|
/>
|
|
82
86
|
{/* Keyboard hint pill. Hidden on narrow widths to avoid overlap. */}
|
|
83
87
|
<span
|
package/src/layout/Sidebar.tsx
CHANGED
|
@@ -253,13 +253,13 @@ export function Sidebar({
|
|
|
253
253
|
|
|
254
254
|
return (
|
|
255
255
|
<aside
|
|
256
|
-
className={`bg-sidebar border-sidebar-border flex h-full flex-col border-r transition-
|
|
257
|
-
collapsed ? 'w-
|
|
256
|
+
className={`bg-sidebar border-sidebar-border flex h-full flex-col border-r transition-[width] duration-[var(--motion-layout)] ${
|
|
257
|
+
collapsed ? 'w-[var(--sbw-col)]' : 'w-[var(--sbw)]'
|
|
258
258
|
}`}
|
|
259
259
|
>
|
|
260
260
|
{/* Brand header — no toggle button here anymore; it now lives in the footer. */}
|
|
261
261
|
<div
|
|
262
|
-
className={`border-sidebar-border flex h-
|
|
262
|
+
className={`border-sidebar-border flex h-[var(--tbh)] shrink-0 items-center border-b px-4 ${
|
|
263
263
|
collapsed ? 'justify-center' : ''
|
|
264
264
|
}`}
|
|
265
265
|
>
|
|
@@ -78,11 +78,7 @@ export function PageContainer({
|
|
|
78
78
|
<header className="flex flex-col gap-3 md:flex-row md:items-start md:justify-between">
|
|
79
79
|
{(title || description) && (
|
|
80
80
|
<div className="min-w-0">
|
|
81
|
-
{title &&
|
|
82
|
-
<h1 className="text-foreground truncate text-2xl font-medium tracking-tight">
|
|
83
|
-
{title}
|
|
84
|
-
</h1>
|
|
85
|
-
)}
|
|
81
|
+
{title && <h1 className="text-foreground truncate">{title}</h1>}
|
|
86
82
|
{description && <p className="text-muted-foreground mt-1 text-sm">{description}</p>}
|
|
87
83
|
</div>
|
|
88
84
|
)}
|
|
@@ -49,13 +49,20 @@ export const tokens = {
|
|
|
49
49
|
|
|
50
50
|
radius: {
|
|
51
51
|
none: 0,
|
|
52
|
-
sm:
|
|
53
|
-
md:
|
|
54
|
-
lg:
|
|
52
|
+
sm: 6,
|
|
53
|
+
md: 8,
|
|
54
|
+
lg: 10,
|
|
55
55
|
xl: 12,
|
|
56
|
-
full:
|
|
56
|
+
full: 99,
|
|
57
57
|
} as const satisfies Record<RadiusToken, number>,
|
|
58
58
|
|
|
59
|
+
/** Layout dimensions from the design system (px). */
|
|
60
|
+
layout: {
|
|
61
|
+
sidebarWidth: 224,
|
|
62
|
+
sidebarWidthCollapsed: 60,
|
|
63
|
+
topbarHeight: 56,
|
|
64
|
+
} as const,
|
|
65
|
+
|
|
59
66
|
// Breakpoints mirror Tailwind defaults so primitives can compose with
|
|
60
67
|
// utility classes without surprises. Use `matchMedia(`(min-width: …`)` to
|
|
61
68
|
// sync JS-driven layout decisions with these values.
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* ActuateCMS design tokens — scoped to the admin root.
|
|
3
|
+
* Source: design-system/tokens.json (v1.0).
|
|
4
|
+
* Appearance accent overrides set --acc / --primary at runtime.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
.actuate-admin {
|
|
8
|
+
/* Surfaces */
|
|
9
|
+
--bg: #f5f4fa;
|
|
10
|
+
--sb: #ffffff;
|
|
11
|
+
--card: #ffffff;
|
|
12
|
+
--bdr: #e8e6f2;
|
|
13
|
+
|
|
14
|
+
/* Text */
|
|
15
|
+
--txt: #1a1827;
|
|
16
|
+
--sub: #6b6878;
|
|
17
|
+
--muted: #b0adbe;
|
|
18
|
+
|
|
19
|
+
/* Accent */
|
|
20
|
+
--acc: #7c3aed;
|
|
21
|
+
--acc-l: #ede9fe;
|
|
22
|
+
--acc-m: #c4b5fd;
|
|
23
|
+
|
|
24
|
+
/* Semantic */
|
|
25
|
+
--suc: #059669;
|
|
26
|
+
--suc-l: #ecfdf5;
|
|
27
|
+
--warn: #d97706;
|
|
28
|
+
--warn-l: #fffbeb;
|
|
29
|
+
--err: #dc2626;
|
|
30
|
+
--err-l: #fef2f2;
|
|
31
|
+
--info: #0891b2;
|
|
32
|
+
--info-l: #e0f7fa;
|
|
33
|
+
|
|
34
|
+
/* AI */
|
|
35
|
+
--ai1: #7c3aed;
|
|
36
|
+
--ai2: #c026d3;
|
|
37
|
+
--ai-soft: linear-gradient(135deg, rgba(124, 58, 237, 0.09), rgba(192, 38, 211, 0.07));
|
|
38
|
+
|
|
39
|
+
/* Layout */
|
|
40
|
+
--sbw: 224px;
|
|
41
|
+
--sbw-col: 60px;
|
|
42
|
+
--tbh: 56px;
|
|
43
|
+
--r: 10px;
|
|
44
|
+
--r-control: 8px;
|
|
45
|
+
--r-sm: 6px;
|
|
46
|
+
--r-pill: 99px;
|
|
47
|
+
|
|
48
|
+
/* Elevation */
|
|
49
|
+
--shd: 0 1px 2px rgba(0, 0, 0, 0.05), 0 1px 4px rgba(0, 0, 0, 0.04);
|
|
50
|
+
--shd2: 0 4px 16px rgba(0, 0, 0, 0.1);
|
|
51
|
+
|
|
52
|
+
/* Typography */
|
|
53
|
+
--font-sans: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
54
|
+
--font-mono: 'DM Mono', ui-monospace, 'SFMono-Regular', monospace;
|
|
55
|
+
--fs-page: 21px;
|
|
56
|
+
--fs-card: 14px;
|
|
57
|
+
--fs-body: 13px;
|
|
58
|
+
--fs-sub: 12px;
|
|
59
|
+
--fs-hint: 11.5px;
|
|
60
|
+
--fs-label: 10px;
|
|
61
|
+
|
|
62
|
+
/* Motion */
|
|
63
|
+
--motion-fast: 0.12s;
|
|
64
|
+
--motion-base: 0.18s;
|
|
65
|
+
--motion-layout: 0.22s cubic-bezier(0.4, 0, 0.2, 1);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.dark .actuate-admin,
|
|
69
|
+
.actuate-admin.dark,
|
|
70
|
+
.actuate-admin[data-theme='dark'] {
|
|
71
|
+
--bg: #0d0c18;
|
|
72
|
+
--sb: #141222;
|
|
73
|
+
--card: #1c1a2e;
|
|
74
|
+
--bdr: #2c2a42;
|
|
75
|
+
|
|
76
|
+
--txt: #eae8f5;
|
|
77
|
+
--sub: #8e8ba8;
|
|
78
|
+
--muted: #504e68;
|
|
79
|
+
|
|
80
|
+
--acc: #9d71f0;
|
|
81
|
+
--acc-l: #231950;
|
|
82
|
+
--acc-m: #5e3eaa;
|
|
83
|
+
|
|
84
|
+
--suc: #10b981;
|
|
85
|
+
--suc-l: #022c22;
|
|
86
|
+
--warn: #fbbf24;
|
|
87
|
+
--warn-l: #1a1000;
|
|
88
|
+
--err: #f87171;
|
|
89
|
+
--err-l: #200808;
|
|
90
|
+
--info: #38bdf8;
|
|
91
|
+
--info-l: #0a2838;
|
|
92
|
+
|
|
93
|
+
--ai-soft: linear-gradient(135deg, rgba(157, 113, 240, 0.16), rgba(192, 38, 211, 0.12));
|
|
94
|
+
|
|
95
|
+
--shd: 0 1px 2px rgba(0, 0, 0, 0.3), 0 1px 4px rgba(0, 0, 0, 0.2);
|
|
96
|
+
--shd2: 0 4px 20px rgba(0, 0, 0, 0.45);
|
|
97
|
+
}
|