@actuate-media/cms-admin 0.63.1 → 0.64.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 +16 -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__/layout/primitives.test.js +9 -2
- package/dist/__tests__/layout/primitives.test.js.map +1 -1
- package/dist/actuate-admin.css +1 -1
- package/dist/components/EnabledSwitch.d.ts +10 -0
- package/dist/components/EnabledSwitch.d.ts.map +1 -0
- package/dist/components/EnabledSwitch.js +8 -0
- package/dist/components/EnabledSwitch.js.map +1 -0
- 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/seo/primitives.d.ts.map +1 -1
- package/dist/components/seo/primitives.js +2 -1
- 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/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/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 +3 -0
- package/dist/components/ui/index.d.ts.map +1 -1
- package/dist/components/ui/index.js +2 -0
- package/dist/components/ui/index.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/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/Forms.js +2 -2
- package/dist/views/Forms.js.map +1 -1
- package/dist/views/ScriptTagEditor.d.ts.map +1 -1
- package/dist/views/ScriptTagEditor.js +2 -1
- package/dist/views/ScriptTagEditor.js.map +1 -1
- package/dist/views/ScriptTags.d.ts.map +1 -1
- package/dist/views/ScriptTags.js +29 -8
- package/dist/views/ScriptTags.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/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 +2 -1
- package/dist/views/settings/components.js.map +1 -1
- package/package.json +8 -3
- package/src/__tests__/layout/primitives.test.ts +10 -2
- package/src/components/EnabledSwitch.tsx +16 -0
- 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/seo/primitives.tsx +8 -7
- package/src/components/ui/Button.tsx +1 -1
- package/src/components/ui/Card.tsx +35 -18
- 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/Toggle.tsx +85 -0
- package/src/components/ui/form-control.ts +12 -0
- package/src/components/ui/index.ts +3 -0
- package/src/layout/Header.tsx +6 -2
- package/src/layout/Sidebar.tsx +3 -3
- 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/Forms.tsx +4 -6
- package/src/views/ScriptTagEditor.tsx +6 -12
- package/src/views/ScriptTags.tsx +40 -19
- package/src/views/page-builder/BottomBar.tsx +3 -6
- package/src/views/seo/TechnicalTab.tsx +3 -6
- package/src/views/settings/components.tsx +11 -10
|
@@ -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
|
)
|
|
@@ -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 }
|
|
@@ -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,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,8 @@ 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 { formControlBase, formControlState } from './form-control.js'
|
|
37
40
|
export { CommandPalette } from './CommandPalette.js'
|
|
38
41
|
export type { CommandPaletteProps } from './CommandPalette.js'
|
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
|
>
|
|
@@ -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
|
+
}
|