@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.
Files changed (60) hide show
  1. package/dist/{chunk-Q7OKHD6I.js → chunk-46QI4FDZ.js} +1 -1
  2. package/dist/{chunk-PHCEJP7I.js → chunk-EAEX6DS7.js} +4 -1
  3. package/dist/{chunk-2YYC2VJY.js → chunk-P24YUT3O.js} +1 -1
  4. package/dist/components/editor/AudienceIndicator.d.ts +9 -0
  5. package/dist/components/editor/AudienceIndicator.d.ts.map +1 -0
  6. package/dist/components/editor/IndicatorPill.d.ts +18 -0
  7. package/dist/components/editor/IndicatorPill.d.ts.map +1 -0
  8. package/dist/components/editor/SectionWrapper.d.ts.map +1 -1
  9. package/dist/components/editor/StatusIndicator.d.ts +29 -0
  10. package/dist/components/editor/StatusIndicator.d.ts.map +1 -0
  11. package/dist/components/editor/index.d.ts +3 -2
  12. package/dist/components/editor/index.d.ts.map +1 -1
  13. package/dist/components/sections/register-schemas.d.ts +1 -1
  14. package/dist/components/sections/register-schemas.d.ts.map +1 -1
  15. package/dist/components/sections/register.d.ts +1 -1
  16. package/dist/components/sections/register.d.ts.map +1 -1
  17. package/dist/components/shared/Popover.d.ts +1 -1
  18. package/dist/components/shared/Popover.d.ts.map +1 -1
  19. package/dist/components/shell/BuildStatusIndicator.d.ts +9 -0
  20. package/dist/components/shell/BuildStatusIndicator.d.ts.map +1 -0
  21. package/dist/components/shell/EditorShell.d.ts.map +1 -1
  22. package/dist/hooks/index.d.ts +1 -0
  23. package/dist/hooks/index.d.ts.map +1 -1
  24. package/dist/hooks/useBuildStatus.d.ts +11 -0
  25. package/dist/hooks/useBuildStatus.d.ts.map +1 -0
  26. package/dist/hooks/useEditorPublish.d.ts +2 -1
  27. package/dist/hooks/useEditorPublish.d.ts.map +1 -1
  28. package/dist/index.js +3 -3
  29. package/dist/lib/index.js +2 -2
  30. package/dist/schemas/index.js +2 -2
  31. package/dist/schemas/site-config.d.ts +6 -4
  32. package/dist/schemas/site-config.d.ts.map +1 -1
  33. package/package.json +1 -1
  34. package/src/components/editor/{AudiencePicker.tsx → AudienceIndicator.tsx} +25 -49
  35. package/src/components/editor/IndicatorPill.tsx +119 -0
  36. package/src/components/editor/SectionWrapper.tsx +18 -14
  37. package/src/components/editor/StatusIndicator.tsx +148 -0
  38. package/src/components/editor/index.ts +3 -2
  39. package/src/components/sections/register-schemas.ts +8 -2
  40. package/src/components/sections/register.ts +8 -2
  41. package/src/components/shared/Popover.tsx +26 -4
  42. package/src/components/shared/PopoverItem.tsx +1 -1
  43. package/src/components/shared/SplitButton.tsx +2 -2
  44. package/src/components/shell/BuildStatusIndicator.tsx +67 -0
  45. package/src/components/shell/EditorShell.tsx +23 -0
  46. package/src/hooks/index.ts +1 -0
  47. package/src/hooks/useBuildStatus.ts +139 -0
  48. package/src/hooks/useEditorPublish.ts +6 -2
  49. package/src/schemas/site-config.ts +5 -1
  50. package/dist/components/editor/AudiencePicker.d.ts +0 -9
  51. package/dist/components/editor/AudiencePicker.d.ts.map +0 -1
  52. package/dist/components/editor/StatusBadge.d.ts +0 -7
  53. package/dist/components/editor/StatusBadge.d.ts.map +0 -1
  54. package/dist/components/editor/StatusDots.d.ts +0 -25
  55. package/dist/components/editor/StatusDots.d.ts.map +0 -1
  56. package/dist/components/editor/StatusPicker.d.ts +0 -9
  57. package/dist/components/editor/StatusPicker.d.ts.map +0 -1
  58. package/src/components/editor/StatusBadge.tsx +0 -30
  59. package/src/components/editor/StatusDots.tsx +0 -131
  60. 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: z.enum(["draft", "live", "archived"]),
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,7 +0,0 @@
1
- interface StatusBadgeProps {
2
- status: string;
3
- dirty?: boolean;
4
- }
5
- export declare function StatusBadge({ status, dirty }: StatusBadgeProps): import("react/jsx-runtime").JSX.Element;
6
- export {};
7
- //# sourceMappingURL=StatusBadge.d.ts.map
@@ -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
- }