@actuate-media/cms-admin 0.10.0 → 0.12.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/LICENSE +21 -21
- package/dist/AdminRoot.d.ts.map +1 -1
- package/dist/AdminRoot.js +8 -5
- package/dist/AdminRoot.js.map +1 -1
- package/dist/__tests__/fields/component-block-helpers.test.d.ts +7 -0
- package/dist/__tests__/fields/component-block-helpers.test.d.ts.map +1 -0
- package/dist/__tests__/fields/component-block-helpers.test.js +592 -0
- package/dist/__tests__/fields/component-block-helpers.test.js.map +1 -0
- package/dist/__tests__/layout/primitives.test.d.ts +2 -0
- package/dist/__tests__/layout/primitives.test.d.ts.map +1 -0
- package/dist/__tests__/layout/primitives.test.js +34 -0
- package/dist/__tests__/layout/primitives.test.js.map +1 -0
- package/dist/__tests__/lib/cv.test.d.ts +2 -0
- package/dist/__tests__/lib/cv.test.d.ts.map +1 -0
- package/dist/__tests__/lib/cv.test.js +66 -0
- package/dist/__tests__/lib/cv.test.js.map +1 -0
- package/dist/actuate-admin.css +1 -1
- package/dist/assets/actuate-logo.d.ts +36 -0
- package/dist/assets/actuate-logo.d.ts.map +1 -0
- package/dist/assets/actuate-logo.js +15 -0
- package/dist/assets/actuate-logo.js.map +1 -0
- package/dist/components/Breadcrumbs.js +2 -2
- package/dist/components/CommandPalette.js +10 -10
- package/dist/components/ContentOverviewChart.js +3 -3
- package/dist/components/ErrorBoundary.js +1 -1
- package/dist/components/FocalPointPicker.js +2 -2
- package/dist/components/FolderTree.js +20 -20
- package/dist/components/LivePreview.js +3 -3
- package/dist/components/LocaleSwitcher.js +1 -1
- package/dist/components/MediaPickerModal.js +4 -4
- package/dist/components/PresenceIndicator.js +1 -1
- package/dist/components/SEOConfigPanel.d.ts +2 -0
- package/dist/components/SEOConfigPanel.d.ts.map +1 -0
- package/dist/components/SEOConfigPanel.js +174 -0
- package/dist/components/SEOConfigPanel.js.map +1 -0
- package/dist/components/SEOPanel.js +9 -9
- package/dist/components/SEOPerformance.js +2 -2
- package/dist/components/SchedulePublishDialog.js +1 -1
- package/dist/components/SharePreviewLinkDialog.js +1 -1
- package/dist/components/TipTapEditor.js +5 -5
- package/dist/components/VersionHistory.js +2 -2
- package/dist/components/ui/Badge.d.ts +33 -3
- package/dist/components/ui/Badge.d.ts.map +1 -1
- package/dist/components/ui/Badge.js +42 -8
- package/dist/components/ui/Badge.js.map +1 -1
- package/dist/components/ui/Button.d.ts +19 -8
- package/dist/components/ui/Button.d.ts.map +1 -1
- package/dist/components/ui/Button.js +35 -14
- package/dist/components/ui/Button.js.map +1 -1
- package/dist/components/ui/Card.d.ts +26 -0
- package/dist/components/ui/Card.d.ts.map +1 -0
- package/dist/components/ui/Card.js +45 -0
- package/dist/components/ui/Card.js.map +1 -0
- package/dist/components/ui/DataTable.js +1 -1
- package/dist/components/ui/Input.d.ts +15 -0
- package/dist/components/ui/Input.d.ts.map +1 -0
- package/dist/components/ui/Input.js +23 -0
- package/dist/components/ui/Input.js.map +1 -0
- package/dist/components/ui/SearchInput.js +1 -1
- package/dist/components/ui/Select.d.ts +16 -0
- package/dist/components/ui/Select.d.ts.map +1 -0
- package/dist/components/ui/Select.js +25 -0
- package/dist/components/ui/Select.js.map +1 -0
- package/dist/components/ui/Toast.js +1 -1
- package/dist/components/ui/index.d.ts +10 -4
- package/dist/components/ui/index.d.ts.map +1 -1
- package/dist/components/ui/index.js +5 -2
- package/dist/components/ui/index.js.map +1 -1
- package/dist/fields/BlockBuilderField.js +3 -3
- package/dist/fields/ComponentBlockField.d.ts +25 -0
- package/dist/fields/ComponentBlockField.d.ts.map +1 -0
- package/dist/fields/ComponentBlockField.js +74 -0
- package/dist/fields/ComponentBlockField.js.map +1 -0
- package/dist/fields/DateField.js +1 -1
- package/dist/fields/FieldRenderer.d.ts +3 -0
- package/dist/fields/FieldRenderer.d.ts.map +1 -1
- package/dist/fields/FieldRenderer.js +3 -1
- package/dist/fields/FieldRenderer.js.map +1 -1
- package/dist/fields/PropInput.d.ts +14 -0
- package/dist/fields/PropInput.d.ts.map +1 -0
- package/dist/fields/PropInput.js +163 -0
- package/dist/fields/PropInput.js.map +1 -0
- package/dist/fields/RelationshipField.js +3 -3
- package/dist/fields/TextField.js +1 -1
- package/dist/fields/component-block-helpers.d.ts +96 -0
- package/dist/fields/component-block-helpers.d.ts.map +1 -0
- package/dist/fields/component-block-helpers.js +323 -0
- package/dist/fields/component-block-helpers.js.map +1 -0
- package/dist/fields/index.d.ts +4 -0
- package/dist/fields/index.d.ts.map +1 -1
- package/dist/fields/index.js +2 -0
- package/dist/fields/index.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/layout/Header.js +1 -1
- package/dist/layout/Layout.d.ts +14 -0
- package/dist/layout/Layout.d.ts.map +1 -1
- package/dist/layout/Layout.js +17 -11
- package/dist/layout/Layout.js.map +1 -1
- package/dist/layout/Sidebar.d.ts.map +1 -1
- package/dist/layout/Sidebar.js +21 -11
- package/dist/layout/Sidebar.js.map +1 -1
- package/dist/layout/primitives/AdminShell.d.ts +43 -0
- package/dist/layout/primitives/AdminShell.d.ts.map +1 -0
- package/dist/layout/primitives/AdminShell.js +51 -0
- package/dist/layout/primitives/AdminShell.js.map +1 -0
- package/dist/layout/primitives/Box.d.ts +19 -0
- package/dist/layout/primitives/Box.d.ts.map +1 -0
- package/dist/layout/primitives/Box.js +12 -0
- package/dist/layout/primitives/Box.js.map +1 -0
- package/dist/layout/primitives/Cluster.d.ts +27 -0
- package/dist/layout/primitives/Cluster.d.ts.map +1 -0
- package/dist/layout/primitives/Cluster.js +37 -0
- package/dist/layout/primitives/Cluster.js.map +1 -0
- package/dist/layout/primitives/Grid.d.ts +45 -0
- package/dist/layout/primitives/Grid.d.ts.map +1 -0
- package/dist/layout/primitives/Grid.js +59 -0
- package/dist/layout/primitives/Grid.js.map +1 -0
- package/dist/layout/primitives/PageContainer.d.ts +36 -0
- package/dist/layout/primitives/PageContainer.d.ts.map +1 -0
- package/dist/layout/primitives/PageContainer.js +41 -0
- package/dist/layout/primitives/PageContainer.js.map +1 -0
- package/dist/layout/primitives/Split.d.ts +34 -0
- package/dist/layout/primitives/Split.d.ts.map +1 -0
- package/dist/layout/primitives/Split.js +27 -0
- package/dist/layout/primitives/Split.js.map +1 -0
- package/dist/layout/primitives/Stack.d.ts +23 -0
- package/dist/layout/primitives/Stack.d.ts.map +1 -0
- package/dist/layout/primitives/Stack.js +34 -0
- package/dist/layout/primitives/Stack.js.map +1 -0
- package/dist/layout/primitives/index.d.ts +30 -0
- package/dist/layout/primitives/index.d.ts.map +1 -0
- package/dist/layout/primitives/index.js +22 -0
- package/dist/layout/primitives/index.js.map +1 -0
- package/dist/layout/primitives/tokens.d.ts +48 -0
- package/dist/layout/primitives/tokens.d.ts.map +1 -0
- package/dist/layout/primitives/tokens.js +54 -0
- package/dist/layout/primitives/tokens.js.map +1 -0
- package/dist/lib/cv.d.ts +53 -0
- package/dist/lib/cv.d.ts.map +1 -0
- package/dist/lib/cv.js +39 -0
- package/dist/lib/cv.js.map +1 -0
- package/dist/views/ApiKeys.js +7 -7
- package/dist/views/CollectionList.js +8 -8
- package/dist/views/Dashboard.d.ts.map +1 -1
- package/dist/views/Dashboard.js +333 -78
- package/dist/views/Dashboard.js.map +1 -1
- package/dist/views/DocumentEdit.js +3 -3
- package/dist/views/ForgotPassword.js +2 -2
- package/dist/views/FormEditor.js +5 -5
- package/dist/views/FormSubmissions.js +6 -6
- package/dist/views/Forms.js +2 -2
- package/dist/views/Login.d.ts +16 -1
- package/dist/views/Login.d.ts.map +1 -1
- package/dist/views/Login.js +17 -7
- package/dist/views/Login.js.map +1 -1
- package/dist/views/MediaBrowser.js +16 -16
- package/dist/views/PageEditor.js +2 -2
- package/dist/views/Pages.js +10 -10
- package/dist/views/PostEditor.js +2 -2
- package/dist/views/Posts.js +4 -4
- package/dist/views/Redirects.js +4 -4
- package/dist/views/ResetPassword.js +2 -2
- package/dist/views/SEO.js +6 -6
- package/dist/views/ScriptTagEditor.js +4 -4
- package/dist/views/ScriptTags.js +2 -2
- package/dist/views/Settings.d.ts.map +1 -1
- package/dist/views/Settings.js +9 -8
- package/dist/views/Settings.js.map +1 -1
- package/dist/views/SetupWizard.js +2 -2
- package/dist/views/Users.js +4 -4
- package/dist/views/page-builder/AIBlockAssist.js +1 -1
- package/dist/views/page-builder/AIGenerateDialog.js +10 -10
- package/dist/views/page-builder/BlockEditor.js +10 -10
- package/dist/views/page-builder/BlockPicker.js +4 -4
- package/dist/views/page-builder/BottomBar.js +1 -1
- package/dist/views/page-builder/BuilderToolbar.js +2 -2
- package/dist/views/page-builder/ContextPanel.js +2 -2
- package/dist/views/page-builder/DesignScore.js +9 -9
- package/dist/views/page-builder/NodeSettings.js +8 -8
- package/dist/views/page-builder/PageBuilder.js +3 -3
- package/dist/views/page-builder/PageSettings.js +1 -1
- package/dist/views/page-builder/PageTemplates.js +2 -2
- package/dist/views/page-builder/SEOPanel.js +13 -13
- package/dist/views/page-builder/SavedSections.js +5 -5
- package/dist/views/page-builder/TemplatePicker.js +2 -2
- package/dist/views/page-builder/block-renderers/CTAPreview.js +5 -5
- package/dist/views/page-builder/block-renderers/CardsPreview.js +1 -1
- package/dist/views/page-builder/block-renderers/CodePreview.js +1 -1
- package/dist/views/page-builder/block-renderers/FAQPreview.js +3 -3
- package/dist/views/page-builder/block-renderers/FallbackPreview.js +1 -1
- package/dist/views/page-builder/block-renderers/FormPreview.js +3 -3
- package/dist/views/page-builder/block-renderers/GalleryPreview.js +5 -5
- package/dist/views/page-builder/block-renderers/HeroPreview.js +3 -3
- package/dist/views/page-builder/block-renderers/ImagePreview.js +3 -3
- package/dist/views/page-builder/block-renderers/TextPreview.js +3 -3
- package/dist/views/page-builder/block-renderers/VideoPreview.js +4 -4
- package/dist/views/page-builder/canvas/BlockRenderer.js +1 -1
- package/dist/views/page-builder/canvas/BuilderCanvas.js +3 -3
- package/dist/views/page-builder/canvas/ColumnRenderer.js +2 -2
- package/dist/views/page-builder/canvas/ContainerRenderer.js +2 -2
- package/dist/views/page-builder/canvas/RowRenderer.js +2 -2
- package/dist/views/page-builder/canvas/SectionRenderer.js +2 -2
- package/package.json +14 -3
- package/src/AdminRoot.tsx +21 -11
- package/src/__tests__/fields/component-block-helpers.test.ts +674 -0
- package/src/__tests__/layout/primitives.test.ts +37 -0
- package/src/__tests__/lib/cv.test.ts +74 -0
- package/src/assets/actuate-logo.tsx +72 -0
- package/src/components/Breadcrumbs.tsx +6 -6
- package/src/components/CommandPalette.tsx +34 -34
- package/src/components/ContentOverviewChart.tsx +3 -3
- package/src/components/ErrorBoundary.tsx +3 -3
- package/src/components/FocalPointPicker.tsx +4 -4
- package/src/components/FolderTree.tsx +38 -38
- package/src/components/LivePreview.tsx +16 -16
- package/src/components/LocaleSwitcher.tsx +7 -7
- package/src/components/MediaPickerModal.tsx +21 -21
- package/src/components/PresenceIndicator.tsx +2 -2
- package/src/components/SEOConfigPanel.tsx +582 -0
- package/src/components/SEOPanel.tsx +46 -46
- package/src/components/SEOPerformance.tsx +21 -21
- package/src/components/SchedulePublishDialog.tsx +4 -4
- package/src/components/SharePreviewLinkDialog.tsx +1 -1
- package/src/components/TipTapEditor.tsx +33 -33
- package/src/components/VersionHistory.tsx +16 -16
- package/src/components/ui/Badge.tsx +66 -14
- package/src/components/ui/Button.tsx +70 -33
- package/src/components/ui/Card.tsx +101 -0
- package/src/components/ui/DataTable.tsx +1 -1
- package/src/components/ui/Input.tsx +35 -0
- package/src/components/ui/SearchInput.tsx +4 -4
- package/src/components/ui/Select.tsx +56 -0
- package/src/components/ui/Toast.tsx +1 -1
- package/src/components/ui/index.ts +18 -4
- package/src/fields/BlockBuilderField.tsx +3 -3
- package/src/fields/ComponentBlockField.tsx +179 -0
- package/src/fields/DateField.tsx +1 -1
- package/src/fields/FieldRenderer.tsx +8 -0
- package/src/fields/PropInput.tsx +552 -0
- package/src/fields/RelationshipField.tsx +10 -10
- package/src/fields/TextField.tsx +1 -1
- package/src/fields/component-block-helpers.ts +341 -0
- package/src/fields/index.ts +4 -0
- package/src/index.ts +35 -0
- package/src/layout/Header.tsx +28 -28
- package/src/layout/Layout.tsx +39 -46
- package/src/layout/Sidebar.tsx +37 -64
- package/src/layout/primitives/AdminShell.tsx +118 -0
- package/src/layout/primitives/Box.tsx +30 -0
- package/src/layout/primitives/Cluster.tsx +74 -0
- package/src/layout/primitives/Grid.tsx +120 -0
- package/src/layout/primitives/PageContainer.tsx +96 -0
- package/src/layout/primitives/Split.tsx +73 -0
- package/src/layout/primitives/Stack.tsx +67 -0
- package/src/layout/primitives/index.ts +36 -0
- package/src/layout/primitives/tokens.ts +76 -0
- package/src/lib/cv.ts +96 -0
- package/src/styles/build-input.css +1 -1
- package/src/views/ApiKeys.tsx +57 -57
- package/src/views/CollectionList.tsx +30 -30
- package/src/views/Dashboard.tsx +737 -186
- package/src/views/DocumentEdit.tsx +9 -9
- package/src/views/ForgotPassword.tsx +18 -18
- package/src/views/FormEditor.tsx +75 -75
- package/src/views/FormSubmissions.tsx +76 -76
- package/src/views/Forms.tsx +27 -27
- package/src/views/Login.tsx +65 -25
- package/src/views/MediaBrowser.tsx +127 -127
- package/src/views/PageEditor.tsx +25 -25
- package/src/views/Pages.tsx +59 -59
- package/src/views/PostEditor.tsx +37 -37
- package/src/views/Posts.tsx +48 -48
- package/src/views/Redirects.tsx +21 -21
- package/src/views/ResetPassword.tsx +28 -28
- package/src/views/SEO.tsx +144 -144
- package/src/views/ScriptTagEditor.tsx +24 -24
- package/src/views/ScriptTags.tsx +10 -10
- package/src/views/Settings.tsx +88 -80
- package/src/views/SetupWizard.tsx +28 -28
- package/src/views/Users.tsx +20 -20
- package/src/views/page-builder/AIBlockAssist.tsx +1 -1
- package/src/views/page-builder/AIGenerateDialog.tsx +63 -63
- package/src/views/page-builder/BlockEditor.tsx +26 -26
- package/src/views/page-builder/BlockPicker.tsx +22 -22
- package/src/views/page-builder/BottomBar.tsx +8 -8
- package/src/views/page-builder/BuilderToolbar.tsx +17 -17
- package/src/views/page-builder/ContextPanel.tsx +3 -3
- package/src/views/page-builder/DesignScore.tsx +21 -21
- package/src/views/page-builder/NodeSettings.tsx +27 -27
- package/src/views/page-builder/PageBuilder.tsx +11 -11
- package/src/views/page-builder/PageSettings.tsx +4 -4
- package/src/views/page-builder/PageTemplates.tsx +18 -18
- package/src/views/page-builder/SEOPanel.tsx +53 -53
- package/src/views/page-builder/SavedSections.tsx +37 -37
- package/src/views/page-builder/TemplatePicker.tsx +17 -17
- package/src/views/page-builder/block-renderers/CTAPreview.tsx +13 -13
- package/src/views/page-builder/block-renderers/CardsPreview.tsx +5 -5
- package/src/views/page-builder/block-renderers/CodePreview.tsx +6 -6
- package/src/views/page-builder/block-renderers/FAQPreview.tsx +13 -13
- package/src/views/page-builder/block-renderers/FallbackPreview.tsx +3 -3
- package/src/views/page-builder/block-renderers/FormPreview.tsx +20 -20
- package/src/views/page-builder/block-renderers/GalleryPreview.tsx +8 -8
- package/src/views/page-builder/block-renderers/HeroPreview.tsx +16 -16
- package/src/views/page-builder/block-renderers/ImagePreview.tsx +4 -4
- package/src/views/page-builder/block-renderers/TextPreview.tsx +14 -14
- package/src/views/page-builder/block-renderers/VideoPreview.tsx +12 -12
- package/src/views/page-builder/canvas/BlockRenderer.tsx +4 -4
- package/src/views/page-builder/canvas/BuilderCanvas.tsx +6 -6
- package/src/views/page-builder/canvas/ColumnRenderer.tsx +3 -3
- package/src/views/page-builder/canvas/ContainerRenderer.tsx +2 -2
- package/src/views/page-builder/canvas/RowRenderer.tsx +2 -2
- package/src/views/page-builder/canvas/SectionRenderer.tsx +2 -2
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { forwardRef, type InputHTMLAttributes } from 'react'
|
|
2
|
+
import { cv, type VariantProps } from '../../lib/cv.js'
|
|
3
|
+
|
|
4
|
+
const input = cv(
|
|
5
|
+
'focus-visible:ring-offset-background flex w-full rounded-[var(--radius)] border bg-[var(--background)] text-[var(--foreground)] file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-[var(--muted-foreground)] focus-visible:ring-2 focus-visible:ring-[var(--ring)] focus-visible:ring-offset-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50',
|
|
6
|
+
{
|
|
7
|
+
variants: {
|
|
8
|
+
size: {
|
|
9
|
+
sm: 'h-8 px-2.5 text-xs',
|
|
10
|
+
md: 'h-9 px-3 text-sm',
|
|
11
|
+
lg: 'h-10 px-4 text-base',
|
|
12
|
+
},
|
|
13
|
+
state: {
|
|
14
|
+
default: 'border-[var(--border)]',
|
|
15
|
+
invalid: 'border-[var(--destructive)] focus-visible:ring-[var(--destructive)]',
|
|
16
|
+
valid: 'border-emerald-500 focus-visible:ring-emerald-500',
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
defaultVariants: { size: 'md', state: 'default' },
|
|
20
|
+
},
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
export type InputVariants = VariantProps<typeof input>
|
|
24
|
+
|
|
25
|
+
export interface InputProps
|
|
26
|
+
extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size'>, InputVariants {}
|
|
27
|
+
|
|
28
|
+
export const Input = forwardRef<HTMLInputElement, InputProps>(function Input(
|
|
29
|
+
{ size, state, className, ...rest },
|
|
30
|
+
ref,
|
|
31
|
+
) {
|
|
32
|
+
return <input ref={ref} className={input({ size, state, class: className })} {...rest} />
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
export { input as inputVariants }
|
|
@@ -10,7 +10,7 @@ export function SearchInput({ value, onChange, placeholder = 'Search...' }: Sear
|
|
|
10
10
|
return (
|
|
11
11
|
<div className="relative flex-1">
|
|
12
12
|
<svg
|
|
13
|
-
className="absolute
|
|
13
|
+
className="absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-[var(--muted-foreground)]"
|
|
14
14
|
fill="none"
|
|
15
15
|
viewBox="0 0 24 24"
|
|
16
16
|
stroke="currentColor"
|
|
@@ -27,12 +27,12 @@ export function SearchInput({ value, onChange, placeholder = 'Search...' }: Sear
|
|
|
27
27
|
value={value}
|
|
28
28
|
onChange={(e) => onChange(e.target.value)}
|
|
29
29
|
placeholder={placeholder}
|
|
30
|
-
className="w-full rounded-md border border-[var(--border)] bg-[var(--input-background)] py-2 pl-9
|
|
30
|
+
className="w-full rounded-md border border-[var(--border)] bg-[var(--input-background)] py-2 pr-8 pl-9 text-sm outline-none focus:ring-2 focus:ring-[var(--ring)]"
|
|
31
31
|
/>
|
|
32
32
|
{value && (
|
|
33
33
|
<button
|
|
34
34
|
onClick={() => onChange('')}
|
|
35
|
-
className="absolute
|
|
35
|
+
className="absolute top-1/2 right-2 -translate-y-1/2 rounded p-0.5 text-[var(--muted-foreground)] hover:text-[var(--foreground)]"
|
|
36
36
|
aria-label="Clear search"
|
|
37
37
|
>
|
|
38
38
|
<svg
|
|
@@ -46,7 +46,7 @@ export function SearchInput({ value, onChange, placeholder = 'Search...' }: Sear
|
|
|
46
46
|
</svg>
|
|
47
47
|
</button>
|
|
48
48
|
)}
|
|
49
|
-
<kbd className="pointer-events-none absolute
|
|
49
|
+
<kbd className="pointer-events-none absolute top-1/2 right-8 hidden -translate-y-1/2 rounded border border-[var(--border)] px-1.5 py-0.5 text-[10px] text-[var(--muted-foreground)] sm:inline-block">
|
|
50
50
|
⌘K
|
|
51
51
|
</kbd>
|
|
52
52
|
</div>
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { forwardRef, type SelectHTMLAttributes, type ReactNode } from 'react'
|
|
2
|
+
import { cv, type VariantProps } from '../../lib/cv.js'
|
|
3
|
+
|
|
4
|
+
const select = cv(
|
|
5
|
+
// Shares geometry with Input so they line up in forms.
|
|
6
|
+
'focus-visible:ring-offset-background flex w-full appearance-none rounded-[var(--radius)] border bg-[var(--background)] pr-8 text-[var(--foreground)] focus-visible:ring-2 focus-visible:ring-[var(--ring)] focus-visible:ring-offset-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50',
|
|
7
|
+
{
|
|
8
|
+
variants: {
|
|
9
|
+
size: {
|
|
10
|
+
sm: 'h-8 pr-7 pl-2.5 text-xs',
|
|
11
|
+
md: 'h-9 pr-8 pl-3 text-sm',
|
|
12
|
+
lg: 'h-10 pr-9 pl-4 text-base',
|
|
13
|
+
},
|
|
14
|
+
state: {
|
|
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
|
+
},
|
|
19
|
+
},
|
|
20
|
+
defaultVariants: { size: 'md', state: 'default' },
|
|
21
|
+
},
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
export type SelectVariants = VariantProps<typeof select>
|
|
25
|
+
|
|
26
|
+
export interface SelectProps
|
|
27
|
+
extends Omit<SelectHTMLAttributes<HTMLSelectElement>, 'size'>, SelectVariants {
|
|
28
|
+
children?: ReactNode
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const Select = forwardRef<HTMLSelectElement, SelectProps>(function Select(
|
|
32
|
+
{ size, state, className, children, ...rest },
|
|
33
|
+
ref,
|
|
34
|
+
) {
|
|
35
|
+
return (
|
|
36
|
+
<div className="relative">
|
|
37
|
+
<select ref={ref} className={select({ size, state, class: className })} {...rest}>
|
|
38
|
+
{children}
|
|
39
|
+
</select>
|
|
40
|
+
<svg
|
|
41
|
+
aria-hidden
|
|
42
|
+
className="pointer-events-none absolute top-1/2 right-2 h-4 w-4 -translate-y-1/2 text-[var(--muted-foreground)]"
|
|
43
|
+
viewBox="0 0 20 20"
|
|
44
|
+
fill="currentColor"
|
|
45
|
+
>
|
|
46
|
+
<path
|
|
47
|
+
fillRule="evenodd"
|
|
48
|
+
d="M5.23 7.21a.75.75 0 011.06.02L10 11.06l3.71-3.83a.75.75 0 111.08 1.04l-4.25 4.39a.75.75 0 01-1.08 0L5.21 8.27a.75.75 0 01.02-1.06z"
|
|
49
|
+
clipRule="evenodd"
|
|
50
|
+
/>
|
|
51
|
+
</svg>
|
|
52
|
+
</div>
|
|
53
|
+
)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
export { select as selectVariants }
|
|
@@ -45,7 +45,7 @@ export function ToastContainer({ toasts, onDismiss }: ToastContainerProps) {
|
|
|
45
45
|
if (toasts.length === 0) return null
|
|
46
46
|
|
|
47
47
|
return (
|
|
48
|
-
<div className="fixed
|
|
48
|
+
<div className="fixed right-4 bottom-4 z-[100] flex flex-col gap-2">
|
|
49
49
|
{toasts.map((toast) => (
|
|
50
50
|
<div
|
|
51
51
|
key={toast.id}
|
|
@@ -1,7 +1,21 @@
|
|
|
1
|
-
export { Button } from './Button.js'
|
|
2
|
-
export type { ButtonProps } from './Button.js'
|
|
3
|
-
export { Badge } from './Badge.js'
|
|
4
|
-
export type { BadgeProps } from './Badge.js'
|
|
1
|
+
export { Button, buttonVariants } from './Button.js'
|
|
2
|
+
export type { ButtonProps, ButtonVariants } from './Button.js'
|
|
3
|
+
export { Badge, badgeVariants, STATUS_TONE, STATUS_LABEL } from './Badge.js'
|
|
4
|
+
export type { BadgeProps, BadgeVariants, DocumentStatus } from './Badge.js'
|
|
5
|
+
export {
|
|
6
|
+
Card,
|
|
7
|
+
CardHeader,
|
|
8
|
+
CardTitle,
|
|
9
|
+
CardDescription,
|
|
10
|
+
CardContent,
|
|
11
|
+
CardFooter,
|
|
12
|
+
cardVariants,
|
|
13
|
+
} from './Card.js'
|
|
14
|
+
export type { CardProps, CardVariants } from './Card.js'
|
|
15
|
+
export { Input, inputVariants } from './Input.js'
|
|
16
|
+
export type { InputProps, InputVariants } from './Input.js'
|
|
17
|
+
export { Select, selectVariants } from './Select.js'
|
|
18
|
+
export type { SelectProps, SelectVariants } from './Select.js'
|
|
5
19
|
export { Avatar } from './Avatar.js'
|
|
6
20
|
export type { AvatarProps } from './Avatar.js'
|
|
7
21
|
export { EmptyState } from './EmptyState.js'
|
|
@@ -74,7 +74,7 @@ function BlockField({ field, value, onChange }: BlockFieldProps) {
|
|
|
74
74
|
<select
|
|
75
75
|
value={(value as string) ?? ''}
|
|
76
76
|
onChange={(e) => onChange(e.target.value)}
|
|
77
|
-
className="w-full rounded-md border border-[var(--border)] bg-[var(--input-background)] px-3 py-2 text-sm outline-none
|
|
77
|
+
className="w-full rounded-md border border-[var(--border)] bg-[var(--input-background)] px-3 py-2 text-sm transition-colors outline-none focus:ring-2 focus:ring-[var(--ring)]"
|
|
78
78
|
>
|
|
79
79
|
<option value="">Select...</option>
|
|
80
80
|
{field.options.map((opt) => (
|
|
@@ -98,7 +98,7 @@ function BlockField({ field, value, onChange }: BlockFieldProps) {
|
|
|
98
98
|
value={(value as string) ?? ''}
|
|
99
99
|
onChange={(e) => onChange(e.target.value)}
|
|
100
100
|
rows={4}
|
|
101
|
-
className="w-full rounded-md border border-[var(--border)] bg-[var(--input-background)] px-3 py-2 text-sm outline-none
|
|
101
|
+
className="w-full rounded-md border border-[var(--border)] bg-[var(--input-background)] px-3 py-2 text-sm transition-colors outline-none focus:ring-2 focus:ring-[var(--ring)]"
|
|
102
102
|
/>
|
|
103
103
|
</div>
|
|
104
104
|
)
|
|
@@ -134,7 +134,7 @@ function BlockField({ field, value, onChange }: BlockFieldProps) {
|
|
|
134
134
|
value={(value as string) ?? ''}
|
|
135
135
|
onChange={(e) => onChange(e.target.value)}
|
|
136
136
|
required={field.required}
|
|
137
|
-
className="w-full rounded-md border border-[var(--border)] bg-[var(--input-background)] px-3 py-2 text-sm outline-none
|
|
137
|
+
className="w-full rounded-md border border-[var(--border)] bg-[var(--input-background)] px-3 py-2 text-sm transition-colors outline-none focus:ring-2 focus:ring-[var(--ring)]"
|
|
138
138
|
/>
|
|
139
139
|
</div>
|
|
140
140
|
)
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Top-level admin field for component-aware blocks. Consumes a
|
|
5
|
+
* {@link Manifest} produced by `@actuate-media/component-blocks`,
|
|
6
|
+
* renders a component picker (when more than one component is allowed)
|
|
7
|
+
* and a recursive props form via {@link PropInput}.
|
|
8
|
+
*
|
|
9
|
+
* Storage shape:
|
|
10
|
+
* { component: 'Hero', props: { title: 'Welcome', alignment: 'center' } }
|
|
11
|
+
*
|
|
12
|
+
* Live validation runs through `validateComponentBlockValue` from
|
|
13
|
+
* `@actuate-media/cms-core`. The whole-value error is shown at the top
|
|
14
|
+
* of the form, and the per-prop error map (parsed out of the message
|
|
15
|
+
* string) is forwarded into `PropInput` so each input can render its
|
|
16
|
+
* own inline error.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { useEffect, useMemo, useRef, useState } from 'react'
|
|
20
|
+
|
|
21
|
+
import type { Manifest } from '@actuate-media/component-blocks'
|
|
22
|
+
|
|
23
|
+
import {
|
|
24
|
+
buildClientValidator,
|
|
25
|
+
getAllowedComponents,
|
|
26
|
+
parsePerPropErrors,
|
|
27
|
+
seedPropsForComponent,
|
|
28
|
+
} from './component-block-helpers.js'
|
|
29
|
+
import { PropInput } from './PropInput.js'
|
|
30
|
+
|
|
31
|
+
export interface ComponentBlockValue {
|
|
32
|
+
component: string
|
|
33
|
+
props: Record<string, unknown>
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface ComponentBlockFieldProps {
|
|
37
|
+
label?: string
|
|
38
|
+
manifest: Manifest
|
|
39
|
+
/** Optional whitelist of component names. */
|
|
40
|
+
allow?: string[]
|
|
41
|
+
/** Default component picked when the field is first used. */
|
|
42
|
+
defaultComponent?: string
|
|
43
|
+
value?: ComponentBlockValue
|
|
44
|
+
onChange: (value: ComponentBlockValue) => void
|
|
45
|
+
/**
|
|
46
|
+
* Optional async validator hook. Receives the proposed value and the
|
|
47
|
+
* field config; should return `true` for ok or a string error.
|
|
48
|
+
* Falls back to a built-in structural validator when not provided —
|
|
49
|
+
* which is what cms-core would do server-side at save time anyway.
|
|
50
|
+
*/
|
|
51
|
+
validate?: (value: ComponentBlockValue) => string | true
|
|
52
|
+
helpText?: string
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function ComponentBlockField({
|
|
56
|
+
label,
|
|
57
|
+
manifest,
|
|
58
|
+
allow,
|
|
59
|
+
defaultComponent,
|
|
60
|
+
value,
|
|
61
|
+
onChange,
|
|
62
|
+
validate,
|
|
63
|
+
helpText,
|
|
64
|
+
}: ComponentBlockFieldProps) {
|
|
65
|
+
const allowedComponents = useMemo(() => getAllowedComponents(manifest, allow), [manifest, allow])
|
|
66
|
+
// Track the initial seed application so we don't repeatedly stomp
|
|
67
|
+
// the editor's prop values on every render. The defaultComponent
|
|
68
|
+
// seed only fires once, when value is undefined on mount.
|
|
69
|
+
const seededRef = useRef(false)
|
|
70
|
+
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
if (seededRef.current) return
|
|
73
|
+
if (value) {
|
|
74
|
+
seededRef.current = true
|
|
75
|
+
return
|
|
76
|
+
}
|
|
77
|
+
const picked =
|
|
78
|
+
allowedComponents.find((c) => c.name === defaultComponent) ?? allowedComponents[0]
|
|
79
|
+
if (!picked) return
|
|
80
|
+
seededRef.current = true
|
|
81
|
+
onChange({ component: picked.name, props: seedPropsForComponent(picked) })
|
|
82
|
+
}, [allowedComponents, defaultComponent, value, onChange])
|
|
83
|
+
|
|
84
|
+
const currentSpec =
|
|
85
|
+
(value && manifest.components.find((c) => c.name === value.component)) ?? allowedComponents[0]
|
|
86
|
+
|
|
87
|
+
// Pure structural validation when caller didn't provide their own.
|
|
88
|
+
// Keeps the form usable even without a live validator wired in.
|
|
89
|
+
const builtinValidate = useMemo(() => buildClientValidator(manifest, allow), [manifest, allow])
|
|
90
|
+
const validator = validate ?? builtinValidate
|
|
91
|
+
|
|
92
|
+
const [errorMessage, setErrorMessage] = useState<string | null>(null)
|
|
93
|
+
const perPropErrors = useMemo(() => parsePerPropErrors(errorMessage), [errorMessage])
|
|
94
|
+
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
if (!value) {
|
|
97
|
+
setErrorMessage(null)
|
|
98
|
+
return
|
|
99
|
+
}
|
|
100
|
+
const result = validator(value)
|
|
101
|
+
setErrorMessage(result === true ? null : result)
|
|
102
|
+
}, [value, validator])
|
|
103
|
+
|
|
104
|
+
function handleComponentChange(componentName: string) {
|
|
105
|
+
const spec = manifest.components.find((c) => c.name === componentName)
|
|
106
|
+
if (!spec) return
|
|
107
|
+
onChange({ component: spec.name, props: seedPropsForComponent(spec) })
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function handlePropChange(propName: string, next: unknown) {
|
|
111
|
+
if (!value) return
|
|
112
|
+
onChange({
|
|
113
|
+
...value,
|
|
114
|
+
props: { ...value.props, [propName]: next },
|
|
115
|
+
})
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (!currentSpec) {
|
|
119
|
+
return (
|
|
120
|
+
<div className="rounded-md border border-[var(--destructive)] bg-red-50 p-3 text-sm text-[var(--destructive)]">
|
|
121
|
+
Component block has no components to choose from.
|
|
122
|
+
</div>
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return (
|
|
127
|
+
<div className="space-y-3">
|
|
128
|
+
{label ? <label className="block text-sm font-medium">{label}</label> : null}
|
|
129
|
+
|
|
130
|
+
{allowedComponents.length > 1 ? (
|
|
131
|
+
<label className="block text-sm">
|
|
132
|
+
<div className="mb-1 text-xs tracking-wide text-[var(--muted-foreground)] uppercase">
|
|
133
|
+
Component
|
|
134
|
+
</div>
|
|
135
|
+
<select
|
|
136
|
+
value={currentSpec.name}
|
|
137
|
+
onChange={(e) => handleComponentChange(e.target.value)}
|
|
138
|
+
className="w-full rounded-md border border-[var(--border)] bg-[var(--input-background)] px-3 py-2 text-sm outline-none focus:ring-2 focus:ring-[var(--ring)]"
|
|
139
|
+
>
|
|
140
|
+
{allowedComponents.map((spec) => (
|
|
141
|
+
<option key={spec.name} value={spec.name}>
|
|
142
|
+
{spec.displayName}
|
|
143
|
+
</option>
|
|
144
|
+
))}
|
|
145
|
+
</select>
|
|
146
|
+
</label>
|
|
147
|
+
) : null}
|
|
148
|
+
|
|
149
|
+
{errorMessage ? (
|
|
150
|
+
<div className="rounded-md border border-[var(--destructive)] bg-red-50 px-3 py-2 text-xs text-[var(--destructive)]">
|
|
151
|
+
{errorMessage}
|
|
152
|
+
</div>
|
|
153
|
+
) : null}
|
|
154
|
+
|
|
155
|
+
<fieldset className="rounded-md border border-[var(--border)] bg-[var(--card)] p-4">
|
|
156
|
+
<legend className="px-1 text-xs tracking-wide text-[var(--muted-foreground)] uppercase">
|
|
157
|
+
{currentSpec.displayName}
|
|
158
|
+
</legend>
|
|
159
|
+
{currentSpec.description ? (
|
|
160
|
+
<p className="mb-3 text-xs text-[var(--muted-foreground)]">{currentSpec.description}</p>
|
|
161
|
+
) : null}
|
|
162
|
+
<div className="space-y-4">
|
|
163
|
+
{currentSpec.props.map((prop) => (
|
|
164
|
+
<PropInput
|
|
165
|
+
key={prop.name}
|
|
166
|
+
prop={prop}
|
|
167
|
+
value={value?.props?.[prop.name]}
|
|
168
|
+
onChange={(next) => handlePropChange(prop.name, next)}
|
|
169
|
+
errors={perPropErrors}
|
|
170
|
+
path={prop.name}
|
|
171
|
+
/>
|
|
172
|
+
))}
|
|
173
|
+
</div>
|
|
174
|
+
</fieldset>
|
|
175
|
+
|
|
176
|
+
{helpText ? <p className="text-xs text-[var(--muted-foreground)]">{helpText}</p> : null}
|
|
177
|
+
</div>
|
|
178
|
+
)
|
|
179
|
+
}
|
package/src/fields/DateField.tsx
CHANGED
|
@@ -25,7 +25,7 @@ export function DateField({ label, value = '', onChange, required, helpText }: D
|
|
|
25
25
|
/>
|
|
26
26
|
<button
|
|
27
27
|
type="button"
|
|
28
|
-
className="absolute
|
|
28
|
+
className="absolute top-1/2 right-2 -translate-y-1/2 text-[var(--muted-foreground)]"
|
|
29
29
|
aria-label="Open calendar"
|
|
30
30
|
>
|
|
31
31
|
<svg
|
|
@@ -13,6 +13,7 @@ import { BlockBuilderField } from './BlockBuilderField.js'
|
|
|
13
13
|
import { GroupField } from './GroupField.js'
|
|
14
14
|
import { NavBuilderField } from './NavBuilderField.js'
|
|
15
15
|
import { NumberField } from './NumberField.js'
|
|
16
|
+
import { ComponentBlockField } from './ComponentBlockField.js'
|
|
16
17
|
|
|
17
18
|
export interface FieldDefinition {
|
|
18
19
|
name: string
|
|
@@ -28,6 +29,9 @@ export interface FieldDefinition {
|
|
|
28
29
|
multi?: boolean
|
|
29
30
|
fields?: FieldDefinition[]
|
|
30
31
|
blocks?: any[]
|
|
32
|
+
manifest?: unknown
|
|
33
|
+
allow?: string[]
|
|
34
|
+
defaultComponent?: string
|
|
31
35
|
}
|
|
32
36
|
|
|
33
37
|
export interface FieldRendererProps {
|
|
@@ -50,6 +54,7 @@ const FIELD_MAP: Record<string, React.ComponentType<any>> = {
|
|
|
50
54
|
group: GroupField,
|
|
51
55
|
nav: NavBuilderField,
|
|
52
56
|
number: NumberField,
|
|
57
|
+
componentBlock: ComponentBlockField,
|
|
53
58
|
}
|
|
54
59
|
|
|
55
60
|
export function FieldRenderer({ field, value, onChange }: FieldRendererProps) {
|
|
@@ -79,6 +84,9 @@ export function FieldRenderer({ field, value, onChange }: FieldRendererProps) {
|
|
|
79
84
|
fields={field.fields}
|
|
80
85
|
blocks={field.blocks}
|
|
81
86
|
name={field.name}
|
|
87
|
+
manifest={field.manifest}
|
|
88
|
+
allow={field.allow}
|
|
89
|
+
defaultComponent={field.defaultComponent}
|
|
82
90
|
/>
|
|
83
91
|
)
|
|
84
92
|
}
|