@drawnagency/primitives 0.1.12 → 0.1.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-Q7OKHD6I.js → chunk-46QI4FDZ.js} +1 -1
- package/dist/{chunk-PHCEJP7I.js → chunk-EAEX6DS7.js} +4 -1
- package/dist/{chunk-2YYC2VJY.js → chunk-P24YUT3O.js} +1 -1
- package/dist/components/editor/AudienceIndicator.d.ts +9 -0
- package/dist/components/editor/AudienceIndicator.d.ts.map +1 -0
- package/dist/components/editor/IndicatorPill.d.ts +18 -0
- package/dist/components/editor/IndicatorPill.d.ts.map +1 -0
- package/dist/components/editor/SectionWrapper.d.ts.map +1 -1
- package/dist/components/editor/StatusIndicator.d.ts +29 -0
- package/dist/components/editor/StatusIndicator.d.ts.map +1 -0
- package/dist/components/editor/index.d.ts +3 -2
- package/dist/components/editor/index.d.ts.map +1 -1
- package/dist/components/sections/register-schemas.d.ts +1 -1
- package/dist/components/sections/register-schemas.d.ts.map +1 -1
- package/dist/components/sections/register.d.ts +1 -1
- package/dist/components/sections/register.d.ts.map +1 -1
- package/dist/components/shared/Popover.d.ts +1 -1
- package/dist/components/shared/Popover.d.ts.map +1 -1
- package/dist/components/shell/BuildStatusIndicator.d.ts +9 -0
- package/dist/components/shell/BuildStatusIndicator.d.ts.map +1 -0
- package/dist/components/shell/EditorShell.d.ts.map +1 -1
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/useBuildStatus.d.ts +11 -0
- package/dist/hooks/useBuildStatus.d.ts.map +1 -0
- package/dist/hooks/useEditorPublish.d.ts +2 -1
- package/dist/hooks/useEditorPublish.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/lib/index.js +2 -2
- package/dist/schemas/index.js +2 -2
- package/dist/schemas/site-config.d.ts +6 -4
- package/dist/schemas/site-config.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/editor/{AudiencePicker.tsx → AudienceIndicator.tsx} +25 -49
- package/src/components/editor/IndicatorPill.tsx +119 -0
- package/src/components/editor/SectionWrapper.tsx +18 -14
- package/src/components/editor/StatusIndicator.tsx +148 -0
- package/src/components/editor/index.ts +3 -2
- package/src/components/sections/register-schemas.ts +8 -2
- package/src/components/sections/register.ts +8 -2
- package/src/components/shared/Popover.tsx +26 -4
- package/src/components/shared/PopoverItem.tsx +1 -1
- package/src/components/shared/SplitButton.tsx +2 -2
- package/src/components/shell/BuildStatusIndicator.tsx +67 -0
- package/src/components/shell/EditorShell.tsx +23 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useBuildStatus.ts +139 -0
- package/src/hooks/useEditorPublish.ts +6 -2
- package/src/schemas/site-config.ts +5 -1
- package/dist/components/editor/AudiencePicker.d.ts +0 -9
- package/dist/components/editor/AudiencePicker.d.ts.map +0 -1
- package/dist/components/editor/StatusBadge.d.ts +0 -7
- package/dist/components/editor/StatusBadge.d.ts.map +0 -1
- package/dist/components/editor/StatusDots.d.ts +0 -25
- package/dist/components/editor/StatusDots.d.ts.map +0 -1
- package/dist/components/editor/StatusPicker.d.ts +0 -9
- package/dist/components/editor/StatusPicker.d.ts.map +0 -1
- package/src/components/editor/StatusBadge.tsx +0 -30
- package/src/components/editor/StatusDots.tsx +0 -131
- package/src/components/editor/StatusPicker.tsx +0 -86
|
@@ -2,9 +2,13 @@ import { z } from "zod";
|
|
|
2
2
|
import { MediaConfigSchema } from "./media";
|
|
3
3
|
import { HexColorSchema } from "./shared";
|
|
4
4
|
|
|
5
|
+
const StatusSchema = z.enum(["draft", "live", "archived", "published"]).transform(
|
|
6
|
+
(val) => val === "published" ? "live" as const : val,
|
|
7
|
+
);
|
|
8
|
+
|
|
5
9
|
export const SectionMetaSchema = z.object({
|
|
6
10
|
type: z.string(),
|
|
7
|
-
status:
|
|
11
|
+
status: StatusSchema,
|
|
8
12
|
access: z.array(z.string()),
|
|
9
13
|
});
|
|
10
14
|
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { Audience } from "../../auth/types";
|
|
2
|
-
interface Props {
|
|
3
|
-
access: string[];
|
|
4
|
-
audiences: Audience[];
|
|
5
|
-
onChange: (access: string[]) => void;
|
|
6
|
-
}
|
|
7
|
-
export declare function AudiencePicker({ access, audiences, onChange }: Props): import("react/jsx-runtime").JSX.Element;
|
|
8
|
-
export {};
|
|
9
|
-
//# sourceMappingURL=AudiencePicker.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AudiencePicker.d.ts","sourceRoot":"","sources":["../../../src/components/editor/AudiencePicker.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEjD,UAAU,KAAK;IACb,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;CACtC;AAED,wBAAgB,cAAc,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,KAAK,2CAyFpE"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"StatusBadge.d.ts","sourceRoot":"","sources":["../../../src/components/editor/StatusBadge.tsx"],"names":[],"mappings":"AAIA,UAAU,gBAAgB;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAQD,wBAAgB,WAAW,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,gBAAgB,2CAc9D"}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
type StatusColor = "draft" | "live" | "archived" | "modified";
|
|
2
|
-
interface DotDef {
|
|
3
|
-
color: StatusColor;
|
|
4
|
-
}
|
|
5
|
-
interface DeriveInput {
|
|
6
|
-
mainStatus: string | null;
|
|
7
|
-
savedStatus: string;
|
|
8
|
-
contentDiffers: boolean;
|
|
9
|
-
isLocalOnly: boolean;
|
|
10
|
-
}
|
|
11
|
-
interface DeriveResult {
|
|
12
|
-
dots: DotDef[];
|
|
13
|
-
label: "unsaved" | null;
|
|
14
|
-
description: string;
|
|
15
|
-
}
|
|
16
|
-
export declare function deriveStatusDots({ mainStatus, savedStatus, contentDiffers, isLocalOnly }: DeriveInput): DeriveResult;
|
|
17
|
-
interface StatusDotsProps {
|
|
18
|
-
mainStatus: string | null;
|
|
19
|
-
savedStatus: string;
|
|
20
|
-
contentDiffers: boolean;
|
|
21
|
-
isLocalOnly: boolean;
|
|
22
|
-
}
|
|
23
|
-
export declare function StatusDots(props: StatusDotsProps): import("react/jsx-runtime").JSX.Element;
|
|
24
|
-
export {};
|
|
25
|
-
//# sourceMappingURL=StatusDots.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"StatusDots.d.ts","sourceRoot":"","sources":["../../../src/components/editor/StatusDots.tsx"],"names":[],"mappings":"AAIA,KAAK,WAAW,GAAG,OAAO,GAAG,MAAM,GAAG,UAAU,GAAG,UAAU,CAAC;AAE9D,UAAU,MAAM;IACd,KAAK,EAAE,WAAW,CAAC;CACpB;AAED,UAAU,WAAW;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,OAAO,CAAC;IACxB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,UAAU,YAAY;IACpB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,KAAK,EAAE,SAAS,GAAG,IAAI,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;CACrB;AAqBD,wBAAgB,gBAAgB,CAAC,EAAE,UAAU,EAAE,WAAW,EAAE,cAAc,EAAE,WAAW,EAAE,EAAE,WAAW,GAAG,YAAY,CA8CpH;AAED,UAAU,eAAe;IACvB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,OAAO,CAAC;IACxB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,eAAe,2CAiChD"}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
type Status = "draft" | "live" | "archived";
|
|
2
|
-
interface Props {
|
|
3
|
-
status: Status;
|
|
4
|
-
dirty?: boolean;
|
|
5
|
-
onChange: (status: Status) => void;
|
|
6
|
-
}
|
|
7
|
-
export declare function StatusPicker({ status, dirty, onChange }: Props): import("react/jsx-runtime").JSX.Element;
|
|
8
|
-
export {};
|
|
9
|
-
//# sourceMappingURL=StatusPicker.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"StatusPicker.d.ts","sourceRoot":"","sources":["../../../src/components/editor/StatusPicker.tsx"],"names":[],"mappings":"AAMA,KAAK,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,UAAU,CAAC;AAE5C,UAAU,KAAK;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC;AAgBD,wBAAgB,YAAY,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,KAAK,2CAyD9D"}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { cn } from "../../lib/cn";
|
|
2
|
-
|
|
3
|
-
type Status = "live" | "draft" | "archived";
|
|
4
|
-
|
|
5
|
-
interface StatusBadgeProps {
|
|
6
|
-
status: string;
|
|
7
|
-
dirty?: boolean;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const statusClasses: Record<Status, string> = {
|
|
11
|
-
live: "bg-status-live-bg text-status-live-text",
|
|
12
|
-
draft: "bg-status-draft-bg text-status-draft-text",
|
|
13
|
-
archived: "bg-status-archived-bg text-status-archived-text",
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
export function StatusBadge({ status, dirty }: StatusBadgeProps) {
|
|
17
|
-
return (
|
|
18
|
-
<span
|
|
19
|
-
className={cn(
|
|
20
|
-
"rounded-full px-2 py-0.5 text-xs font-medium",
|
|
21
|
-
statusClasses[status as Status] ?? statusClasses.archived,
|
|
22
|
-
)}
|
|
23
|
-
>
|
|
24
|
-
{status}
|
|
25
|
-
{dirty && (
|
|
26
|
-
<span className="ml-1 opacity-70">· Unsaved</span>
|
|
27
|
-
)}
|
|
28
|
-
</span>
|
|
29
|
-
);
|
|
30
|
-
}
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
import { useRef, useState } from "react";
|
|
2
|
-
import { cn } from "../../lib/cn";
|
|
3
|
-
import { Popover } from "../shared/Popover";
|
|
4
|
-
|
|
5
|
-
type StatusColor = "draft" | "live" | "archived" | "modified";
|
|
6
|
-
|
|
7
|
-
interface DotDef {
|
|
8
|
-
color: StatusColor;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
interface DeriveInput {
|
|
12
|
-
mainStatus: string | null;
|
|
13
|
-
savedStatus: string;
|
|
14
|
-
contentDiffers: boolean;
|
|
15
|
-
isLocalOnly: boolean;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
interface DeriveResult {
|
|
19
|
-
dots: DotDef[];
|
|
20
|
-
label: "unsaved" | null;
|
|
21
|
-
description: string;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const dotColorClasses: Record<StatusColor, string> = {
|
|
25
|
-
live: "bg-green-500",
|
|
26
|
-
draft: "bg-gray-400",
|
|
27
|
-
archived: "bg-white border-gray-300",
|
|
28
|
-
modified: "bg-orange-400",
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
const descriptions: Record<string, string> = {
|
|
32
|
-
"live": "Live — synced",
|
|
33
|
-
"live,modified": "Live — unpublished content edits",
|
|
34
|
-
"live,draft": "Live on site, changed to draft",
|
|
35
|
-
"live,archived": "Live on site, will be hidden on publish",
|
|
36
|
-
"draft": "Draft — editor only",
|
|
37
|
-
"draft,live": "Draft, will become live on publish",
|
|
38
|
-
"archived": "Archived",
|
|
39
|
-
"archived,live": "Archived, will become live on publish",
|
|
40
|
-
"modified": "Unpublished content edits",
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
export function deriveStatusDots({ mainStatus, savedStatus, contentDiffers, isLocalOnly }: DeriveInput): DeriveResult {
|
|
44
|
-
const label: "unsaved" | null = isLocalOnly ? "unsaved" : null;
|
|
45
|
-
|
|
46
|
-
// New section — no main status
|
|
47
|
-
if (!mainStatus) {
|
|
48
|
-
return {
|
|
49
|
-
dots: [{ color: savedStatus as StatusColor }],
|
|
50
|
-
label,
|
|
51
|
-
description: `${savedStatus} — new section`,
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Status changed between main and saved
|
|
56
|
-
if (mainStatus !== savedStatus) {
|
|
57
|
-
const key = `${mainStatus},${savedStatus}`;
|
|
58
|
-
return {
|
|
59
|
-
dots: [{ color: mainStatus as StatusColor }, { color: savedStatus as StatusColor }],
|
|
60
|
-
label,
|
|
61
|
-
description: descriptions[key] ?? `${mainStatus} → ${savedStatus}`,
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Same status — check content differences
|
|
66
|
-
if (contentDiffers) {
|
|
67
|
-
// Only show orange (modified) dot for live sections
|
|
68
|
-
if (mainStatus === "live") {
|
|
69
|
-
return {
|
|
70
|
-
dots: [{ color: "live" }, { color: "modified" }],
|
|
71
|
-
label,
|
|
72
|
-
description: descriptions["live,modified"]!,
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
// Draft/archived with content changes — no second dot
|
|
76
|
-
return {
|
|
77
|
-
dots: [{ color: mainStatus as StatusColor }],
|
|
78
|
-
label,
|
|
79
|
-
description: `${mainStatus} — with edits`,
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Fully synced
|
|
84
|
-
return {
|
|
85
|
-
dots: [{ color: mainStatus as StatusColor }],
|
|
86
|
-
label: null,
|
|
87
|
-
description: descriptions[mainStatus] ?? mainStatus,
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
interface StatusDotsProps {
|
|
92
|
-
mainStatus: string | null;
|
|
93
|
-
savedStatus: string;
|
|
94
|
-
contentDiffers: boolean;
|
|
95
|
-
isLocalOnly: boolean;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export function StatusDots(props: StatusDotsProps) {
|
|
99
|
-
const { dots, label, description } = deriveStatusDots(props);
|
|
100
|
-
const [open, setOpen] = useState(false);
|
|
101
|
-
const buttonRef = useRef<HTMLButtonElement>(null);
|
|
102
|
-
|
|
103
|
-
return (
|
|
104
|
-
<div className="relative">
|
|
105
|
-
<button
|
|
106
|
-
ref={buttonRef}
|
|
107
|
-
type="button"
|
|
108
|
-
onClick={() => setOpen((v) => !v)}
|
|
109
|
-
className="cursor-pointer inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 text-xs font-medium border border-base-200 hover:bg-base-accent"
|
|
110
|
-
aria-label={description}
|
|
111
|
-
>
|
|
112
|
-
<span className="flex -space-x-1">
|
|
113
|
-
{dots.map((dot, i) => (
|
|
114
|
-
<span
|
|
115
|
-
key={i}
|
|
116
|
-
aria-hidden="true"
|
|
117
|
-
className={cn("h-3 w-3 rounded-full border border-base", dotColorClasses[dot.color])}
|
|
118
|
-
/>
|
|
119
|
-
))}
|
|
120
|
-
</span>
|
|
121
|
-
{label && <span className="opacity-70">(unsaved)</span>}
|
|
122
|
-
</button>
|
|
123
|
-
|
|
124
|
-
<Popover isOpen={open} onClose={() => setOpen(false)} anchorRef={buttonRef} align="end" className="w-56">
|
|
125
|
-
<div className="px-3 py-2 text-xs text-base-contrast">
|
|
126
|
-
{description}
|
|
127
|
-
</div>
|
|
128
|
-
</Popover>
|
|
129
|
-
</div>
|
|
130
|
-
);
|
|
131
|
-
}
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import { useRef, useState } from "react";
|
|
2
|
-
import { Check } from "lucide-react";
|
|
3
|
-
import { cn } from "../../lib/cn";
|
|
4
|
-
import { Popover } from "../shared/Popover";
|
|
5
|
-
import { PopoverItem } from "../shared/PopoverItem";
|
|
6
|
-
|
|
7
|
-
type Status = "draft" | "live" | "archived";
|
|
8
|
-
|
|
9
|
-
interface Props {
|
|
10
|
-
status: Status;
|
|
11
|
-
dirty?: boolean;
|
|
12
|
-
onChange: (status: Status) => void;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const STATUSES: Status[] = ["draft", "live", "archived"];
|
|
16
|
-
|
|
17
|
-
const statusClasses: Record<Status, string> = {
|
|
18
|
-
live: "bg-status-live-bg text-status-live-text",
|
|
19
|
-
draft: "bg-status-draft-bg text-status-draft-text",
|
|
20
|
-
archived: "bg-status-archived-bg text-status-archived-text",
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
const dotClasses: Record<Status, string> = {
|
|
24
|
-
live: "bg-green-500",
|
|
25
|
-
draft: "bg-gray-400",
|
|
26
|
-
archived: "bg-white",
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
export function StatusPicker({ status, dirty, onChange }: Props) {
|
|
30
|
-
const [open, setOpen] = useState(false);
|
|
31
|
-
const buttonRef = useRef<HTMLButtonElement>(null);
|
|
32
|
-
|
|
33
|
-
function handleSelect(next: Status) {
|
|
34
|
-
if (next !== status) onChange(next);
|
|
35
|
-
setOpen(false);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return (
|
|
39
|
-
<div className="relative">
|
|
40
|
-
<button
|
|
41
|
-
ref={buttonRef}
|
|
42
|
-
type="button"
|
|
43
|
-
onClick={() => setOpen((v) => !v)}
|
|
44
|
-
aria-haspopup="true"
|
|
45
|
-
aria-expanded={open}
|
|
46
|
-
className={cn(
|
|
47
|
-
"cursor-pointer rounded-full px-2 py-0.5 text-xs font-medium",
|
|
48
|
-
statusClasses[status] ?? statusClasses.archived,
|
|
49
|
-
)}
|
|
50
|
-
>
|
|
51
|
-
{status}
|
|
52
|
-
{dirty && <span className="ml-1 opacity-70">· Unsaved</span>}
|
|
53
|
-
</button>
|
|
54
|
-
|
|
55
|
-
<Popover
|
|
56
|
-
isOpen={open}
|
|
57
|
-
onClose={() => setOpen(false)}
|
|
58
|
-
anchorRef={buttonRef}
|
|
59
|
-
align="end"
|
|
60
|
-
className="w-44"
|
|
61
|
-
>
|
|
62
|
-
<ul role="radiogroup" aria-label="Section status" className="py-1">
|
|
63
|
-
{STATUSES.map((s) => {
|
|
64
|
-
const checked = s === status;
|
|
65
|
-
return (
|
|
66
|
-
<li key={s}>
|
|
67
|
-
<PopoverItem
|
|
68
|
-
role="radio"
|
|
69
|
-
aria-checked={checked}
|
|
70
|
-
onClick={() => handleSelect(s)}
|
|
71
|
-
>
|
|
72
|
-
<span
|
|
73
|
-
aria-hidden="true"
|
|
74
|
-
className={cn("h-3 w-3 shrink-0 rounded-full border border-base-200", dotClasses[s])}
|
|
75
|
-
/>
|
|
76
|
-
<span className="flex-1 text-sm font-medium capitalize text-base-contrast">{s}</span>
|
|
77
|
-
{checked && <Check size={14} strokeWidth={3} className="text-primary" />}
|
|
78
|
-
</PopoverItem>
|
|
79
|
-
</li>
|
|
80
|
-
);
|
|
81
|
-
})}
|
|
82
|
-
</ul>
|
|
83
|
-
</Popover>
|
|
84
|
-
</div>
|
|
85
|
-
);
|
|
86
|
-
}
|