@drawnagency/primitives 0.1.41 → 0.1.43
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-62OWSJ7V.js → chunk-5XYUO4HP.js} +1 -1
- package/dist/{chunk-A4RARGF2.js → chunk-BJ6FYGYP.js} +2 -0
- package/dist/{chunk-BU52OBPW.js → chunk-P3HO76OS.js} +1 -1
- package/dist/components/editor/SectionWrapper.d.ts.map +1 -1
- package/dist/components/sections/Button/index.d.ts.map +1 -1
- package/dist/components/sections/Colors/index.d.ts.map +1 -1
- package/dist/components/sections/DoDontList/index.d.ts.map +1 -1
- package/dist/components/sections/DoDontMediaGrid/index.d.ts.map +1 -1
- package/dist/components/sections/IconList/index.d.ts.map +1 -1
- package/dist/components/sections/LinkHeading/index.d.ts.map +1 -1
- package/dist/components/sections/MediaGrid/index.d.ts.map +1 -1
- package/dist/components/sections/Prose/index.d.ts.map +1 -1
- package/dist/components/sections/SplitContent/index.d.ts.map +1 -1
- package/dist/components/sections/SubHeading/index.d.ts.map +1 -1
- package/dist/components/sections/SubSubHeading/index.d.ts.map +1 -1
- package/dist/components/shell/BugReportFAB.d.ts.map +1 -1
- package/dist/components/shell/EditorShell.d.ts.map +1 -1
- package/dist/components/shell/SectionSkeleton.d.ts.map +1 -1
- package/dist/components/shell/SectionTypePicker.d.ts +4 -3
- package/dist/components/shell/SectionTypePicker.d.ts.map +1 -1
- package/dist/components/shell/SiteSettingsDisplay.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/lib/index.js +2 -2
- package/dist/lib/registry.d.ts +2 -2
- package/dist/lib/registry.d.ts.map +1 -1
- package/dist/schemas/index.js +2 -2
- package/dist/schemas/site-config.d.ts +2 -0
- package/dist/schemas/site-config.d.ts.map +1 -1
- package/dist/types/database.d.ts +509 -0
- package/dist/types/database.d.ts.map +1 -0
- package/dist/types/database.js +12 -0
- package/package.json +5 -1
- package/src/components/editor/SectionWrapper.tsx +9 -4
- package/src/components/sections/Button/index.tsx +2 -0
- package/src/components/sections/Colors/index.tsx +2 -0
- package/src/components/sections/DoDontList/index.tsx +2 -0
- package/src/components/sections/DoDontMediaGrid/index.tsx +2 -0
- package/src/components/sections/IconList/index.tsx +2 -0
- package/src/components/sections/LinkHeading/index.tsx +2 -0
- package/src/components/sections/MediaGrid/index.tsx +2 -0
- package/src/components/sections/Prose/index.tsx +2 -0
- package/src/components/sections/SplitContent/index.tsx +2 -0
- package/src/components/sections/SubHeading/index.tsx +2 -0
- package/src/components/sections/SubSubHeading/index.tsx +2 -0
- package/src/components/shell/BugReportFAB.tsx +12 -6
- package/src/components/shell/EditorShell.tsx +3 -3
- package/src/components/shell/SectionSkeleton.tsx +15 -14
- package/src/components/shell/SectionTypePicker.tsx +15 -11
- package/src/components/shell/SiteSettingsDisplay.tsx +12 -0
- package/src/lib/registry.ts +2 -2
- package/src/schemas/site-config.ts +2 -0
- package/src/types/database.ts +565 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { defineSection } from "../../../lib/registry";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
import { List } from "lucide-react";
|
|
3
4
|
import IconList from "./IconList";
|
|
4
5
|
import { IconListSettings } from "./IconListSettings";
|
|
5
6
|
|
|
@@ -18,6 +19,7 @@ const schema = z.object({
|
|
|
18
19
|
export default defineSection({
|
|
19
20
|
type: "icon_list",
|
|
20
21
|
label: "Icon List",
|
|
22
|
+
icon: <List size={18} />,
|
|
21
23
|
schema,
|
|
22
24
|
component: ({ content, options, onChange }) => (
|
|
23
25
|
<IconList
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { defineSection } from "../../../lib/registry";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
import { Heading1 } from "lucide-react";
|
|
3
4
|
import { HeadingSection } from "../../primitives/HeadingSection";
|
|
4
5
|
|
|
5
6
|
const schema = z.object({
|
|
@@ -10,6 +11,7 @@ const schema = z.object({
|
|
|
10
11
|
export default defineSection({
|
|
11
12
|
type: "link_heading",
|
|
12
13
|
label: "Link Heading",
|
|
14
|
+
icon: <Heading1 size={18} />,
|
|
13
15
|
schema,
|
|
14
16
|
navRole: "h1",
|
|
15
17
|
component: ({ content, onChange }) => (
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { defineSection } from "../../../lib/registry";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
import { LayoutGrid } from "lucide-react";
|
|
3
4
|
import { MediaReferenceSchema } from "../../../schemas/shared";
|
|
4
5
|
import { MediaGridOptionsSchema } from "../../../schemas/media-grid-options";
|
|
5
6
|
import MediaGrid from "./MediaGrid";
|
|
@@ -16,6 +17,7 @@ const schema = z.object({
|
|
|
16
17
|
export default defineSection({
|
|
17
18
|
type: "media_grid",
|
|
18
19
|
label: "Media Grid",
|
|
20
|
+
icon: <LayoutGrid size={18} />,
|
|
19
21
|
schema,
|
|
20
22
|
component: ({ content, options, onChange, openModal }) => (
|
|
21
23
|
<MediaGrid
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { defineSection } from "../../../lib/registry";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
import { AlignLeft } from "lucide-react";
|
|
3
4
|
import Prose from "./Prose";
|
|
4
5
|
import { stripHtmlToPlainText, truncate } from "../../../lib/text";
|
|
5
6
|
|
|
@@ -11,6 +12,7 @@ const schema = z.object({
|
|
|
11
12
|
export default defineSection({
|
|
12
13
|
type: "prose",
|
|
13
14
|
label: "Prose",
|
|
15
|
+
icon: <AlignLeft size={18} />,
|
|
14
16
|
schema,
|
|
15
17
|
component: ({ content, onChange }) => (
|
|
16
18
|
<Prose body={content.content.body} onChange={onChange ? (c) => onChange(c as typeof content) : undefined} />
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { defineSection } from "../../../lib/registry";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
import { Columns2 } from "lucide-react";
|
|
3
4
|
import SplitContent from "./SplitContent";
|
|
4
5
|
import { stripHtmlToPlainText, truncate } from "../../../lib/text";
|
|
5
6
|
|
|
@@ -18,6 +19,7 @@ const schema = z.object({
|
|
|
18
19
|
export default defineSection({
|
|
19
20
|
type: "split_content",
|
|
20
21
|
label: "Split Content",
|
|
22
|
+
icon: <Columns2 size={18} />,
|
|
21
23
|
schema,
|
|
22
24
|
component: ({ content, options, onChange }) => (
|
|
23
25
|
<SplitContent
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { defineSection } from "../../../lib/registry";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
import { Heading2 } from "lucide-react";
|
|
3
4
|
import { HeadingSection } from "../../primitives/HeadingSection";
|
|
4
5
|
|
|
5
6
|
const schema = z.object({
|
|
@@ -10,6 +11,7 @@ const schema = z.object({
|
|
|
10
11
|
export default defineSection({
|
|
11
12
|
type: "sub_heading",
|
|
12
13
|
label: "Sub Heading",
|
|
14
|
+
icon: <Heading2 size={18} />,
|
|
13
15
|
schema,
|
|
14
16
|
navRole: "h2",
|
|
15
17
|
component: ({ content, onChange }) => (
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { defineSection } from "../../../lib/registry";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
import { Heading3 } from "lucide-react";
|
|
3
4
|
import { HeadingSection } from "../../primitives/HeadingSection";
|
|
4
5
|
|
|
5
6
|
const schema = z.object({
|
|
@@ -10,6 +11,7 @@ const schema = z.object({
|
|
|
10
11
|
export default defineSection({
|
|
11
12
|
type: "sub_sub_heading",
|
|
12
13
|
label: "Sub Sub Heading",
|
|
14
|
+
icon: <Heading3 size={18} />,
|
|
13
15
|
schema,
|
|
14
16
|
navRole: "h3",
|
|
15
17
|
component: ({ content, onChange }) => (
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useState, useCallback, useRef, useEffect } from "react";
|
|
2
2
|
import { createClient } from "@supabase/supabase-js";
|
|
3
|
+
import type { Database, Json } from "../../types/database";
|
|
3
4
|
import { EditorModal } from "./EditorModal";
|
|
4
5
|
import { useEditorContext } from "./EditorContext";
|
|
5
6
|
import { env } from "../../lib/env";
|
|
@@ -8,13 +9,14 @@ interface Props {
|
|
|
8
9
|
siteId: string;
|
|
9
10
|
}
|
|
10
11
|
|
|
11
|
-
type Category = "Visual" | "Unexpected Behavior" | "Media / Media Library" | "Save / Publish" | "Other";
|
|
12
|
+
type Category = "Visual" | "Unexpected Behavior" | "Media / Media Library" | "Save / Publish" | "Feature Request" | "Other";
|
|
12
13
|
|
|
13
14
|
const CATEGORIES: Category[] = [
|
|
14
15
|
"Visual",
|
|
15
16
|
"Unexpected Behavior",
|
|
16
17
|
"Media / Media Library",
|
|
17
18
|
"Save / Publish",
|
|
19
|
+
"Feature Request",
|
|
18
20
|
"Other",
|
|
19
21
|
];
|
|
20
22
|
|
|
@@ -148,13 +150,17 @@ export function BugReportFAB({ siteId }: Props) {
|
|
|
148
150
|
setIsSubmitting(true);
|
|
149
151
|
|
|
150
152
|
try {
|
|
151
|
-
const supabase = createClient(
|
|
153
|
+
const supabase = createClient<Database>(
|
|
152
154
|
env("SUPABASE_URL"),
|
|
153
155
|
env("SUPABASE_ANON_KEY"),
|
|
154
156
|
);
|
|
155
157
|
|
|
156
158
|
const { data: userData } = await supabase.auth.getUser();
|
|
157
|
-
const userId = userData?.user?.id
|
|
159
|
+
const userId = userData?.user?.id;
|
|
160
|
+
if (!userId) {
|
|
161
|
+
setSubmitError("You must be signed in to report a bug.");
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
158
164
|
|
|
159
165
|
const { error } = await supabase.from("bug_reports").insert({
|
|
160
166
|
site_id: siteId,
|
|
@@ -163,7 +169,7 @@ export function BugReportFAB({ siteId }: Props) {
|
|
|
163
169
|
is_critical: isCritical,
|
|
164
170
|
description: description.trim(),
|
|
165
171
|
images: images.map((img) => img.dataUri),
|
|
166
|
-
context: capturedContextRef.current,
|
|
172
|
+
context: capturedContextRef.current as Json,
|
|
167
173
|
});
|
|
168
174
|
|
|
169
175
|
if (error) {
|
|
@@ -245,7 +251,7 @@ export function BugReportFAB({ siteId }: Props) {
|
|
|
245
251
|
} mt-0.5`}
|
|
246
252
|
/>
|
|
247
253
|
</button>
|
|
248
|
-
<span className="text-sm text-base-contrast">
|
|
254
|
+
<span className="text-sm text-base-contrast">Critical / Blocking</span>
|
|
249
255
|
</div>
|
|
250
256
|
|
|
251
257
|
{/* Description */}
|
|
@@ -328,7 +334,7 @@ export function BugReportFAB({ siteId }: Props) {
|
|
|
328
334
|
disabled={isSubmitting}
|
|
329
335
|
className="w-full rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-contrast hover:opacity-90 transition-opacity disabled:opacity-60"
|
|
330
336
|
>
|
|
331
|
-
{isSubmitting ? "Submitting..." : "Submit
|
|
337
|
+
{isSubmitting ? "Submitting..." : "Submit"}
|
|
332
338
|
</button>
|
|
333
339
|
</div>
|
|
334
340
|
</EditorModal>
|
|
@@ -192,6 +192,8 @@ export default function EditorShell({
|
|
|
192
192
|
root.style.setProperty("--font-heading", `${config.headingFont}, system-ui, sans-serif`);
|
|
193
193
|
root.style.setProperty("--font-body", `${config.bodyFont}, system-ui, sans-serif`);
|
|
194
194
|
root.style.setProperty("--heading-text-transform", config.uppercaseHeadings ? "uppercase" : "none");
|
|
195
|
+
root.style.setProperty("--subheading-text-transform", config.uppercaseSubheadings ? "uppercase" : "none");
|
|
196
|
+
root.style.setProperty("--nav-text-transform", config.uppercaseNavHeadings ? "uppercase" : "none");
|
|
195
197
|
|
|
196
198
|
if (config.googleFontsUrl) {
|
|
197
199
|
if (fontLinkRef.current?.href !== config.googleFontsUrl) {
|
|
@@ -885,9 +887,7 @@ function EditorContent({
|
|
|
885
887
|
getAllSections().map((def) => ({
|
|
886
888
|
type: def.type,
|
|
887
889
|
label: def.label,
|
|
888
|
-
icon: def.icon
|
|
889
|
-
? () => <>{def.icon}</>
|
|
890
|
-
: () => null,
|
|
890
|
+
icon: def.icon,
|
|
891
891
|
})),
|
|
892
892
|
[],
|
|
893
893
|
);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState, useEffect } from "react";
|
|
1
|
+
import { useState, useEffect, useRef } from "react";
|
|
2
2
|
import { SectionTypePicker, type TypeOption } from "./SectionTypePicker";
|
|
3
3
|
import { SectionLayout } from "../sections/SectionLayout";
|
|
4
4
|
import { cn } from "../../lib/cn";
|
|
@@ -11,10 +11,13 @@ interface SectionSkeletonProps {
|
|
|
11
11
|
|
|
12
12
|
export function SectionSkeleton({ types, onSelect, onDismiss }: SectionSkeletonProps) {
|
|
13
13
|
const [isVisible, setIsVisible] = useState(false);
|
|
14
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
14
15
|
|
|
15
16
|
useEffect(() => {
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
requestAnimationFrame(() => {
|
|
18
|
+
setIsVisible(true);
|
|
19
|
+
containerRef.current?.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
20
|
+
});
|
|
18
21
|
}, []);
|
|
19
22
|
|
|
20
23
|
useEffect(() => {
|
|
@@ -30,24 +33,22 @@ export function SectionSkeleton({ types, onSelect, onDismiss }: SectionSkeletonP
|
|
|
30
33
|
return (
|
|
31
34
|
<SectionLayout type="skeleton" status="draft">
|
|
32
35
|
<div
|
|
36
|
+
ref={containerRef}
|
|
33
37
|
className={cn(
|
|
34
38
|
"relative transition-all duration-200",
|
|
35
39
|
isVisible ? "opacity-100" : "opacity-0 -translate-y-2",
|
|
36
40
|
)}
|
|
37
41
|
>
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
<span className="text-sm text-base-contrast-light/50">New section</span>
|
|
42
|
+
<div className="flex h-16 items-center justify-center rounded-lg bg-base-accent">
|
|
43
|
+
<span className="text-sm text-base-contrast-light/50">Choose a section type</span>
|
|
41
44
|
</div>
|
|
42
45
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
/>
|
|
50
|
-
</div>
|
|
46
|
+
<SectionTypePicker
|
|
47
|
+
types={types}
|
|
48
|
+
onSelect={onSelect}
|
|
49
|
+
onClose={onDismiss}
|
|
50
|
+
containerRef={containerRef}
|
|
51
|
+
/>
|
|
51
52
|
</div>
|
|
52
53
|
</SectionLayout>
|
|
53
54
|
);
|
|
@@ -1,44 +1,48 @@
|
|
|
1
|
-
import { useEffect, useRef, type
|
|
1
|
+
import { useEffect, useRef, type ReactNode, type RefObject } from "react";
|
|
2
2
|
|
|
3
3
|
export interface TypeOption {
|
|
4
4
|
type: string;
|
|
5
5
|
label: string;
|
|
6
|
-
icon
|
|
6
|
+
icon?: ReactNode;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
interface SectionTypePickerProps {
|
|
10
10
|
types: TypeOption[];
|
|
11
11
|
onSelect: (type: string) => void;
|
|
12
12
|
onClose: () => void;
|
|
13
|
+
containerRef?: RefObject<HTMLElement | null>;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
export function SectionTypePicker({ types, onSelect, onClose }: SectionTypePickerProps) {
|
|
16
|
+
export function SectionTypePicker({ types, onSelect, onClose, containerRef }: SectionTypePickerProps) {
|
|
16
17
|
const panelRef = useRef<HTMLDivElement>(null);
|
|
18
|
+
const dismissRef = containerRef ?? panelRef;
|
|
17
19
|
|
|
18
20
|
useEffect(() => {
|
|
19
21
|
function handleMouseDown(e: MouseEvent) {
|
|
20
|
-
if (
|
|
22
|
+
if (dismissRef.current && !dismissRef.current.contains(e.target as Node)) {
|
|
21
23
|
onClose();
|
|
22
24
|
}
|
|
23
25
|
}
|
|
24
26
|
document.addEventListener("mousedown", handleMouseDown);
|
|
25
27
|
return () => document.removeEventListener("mousedown", handleMouseDown);
|
|
26
|
-
}, [onClose]);
|
|
28
|
+
}, [onClose, dismissRef]);
|
|
27
29
|
|
|
28
30
|
return (
|
|
29
31
|
<div
|
|
30
32
|
ref={panelRef}
|
|
31
|
-
className="
|
|
33
|
+
className="z-50 mt-3 grid grid-cols-1 gap-2 sm:grid-cols-2 lg:grid-cols-3"
|
|
32
34
|
>
|
|
33
|
-
{types.map(({ type, label, icon
|
|
35
|
+
{types.map(({ type, label, icon }) => (
|
|
34
36
|
<button
|
|
35
37
|
key={type}
|
|
36
|
-
className="cursor-pointer flex
|
|
38
|
+
className="cursor-pointer flex items-center gap-3 rounded-lg border border-base-200 bg-base px-4 py-3 text-left text-sm text-base-contrast-light transition-colors hover:border-base-300 hover:bg-base-accent hover:text-base-contrast"
|
|
37
39
|
onClick={() => onSelect(type)}
|
|
38
40
|
>
|
|
39
|
-
|
|
40
|
-
<
|
|
41
|
-
|
|
41
|
+
{icon && (
|
|
42
|
+
<span className="flex h-5 w-5 shrink-0 items-center justify-center text-base-contrast-light/60">
|
|
43
|
+
{icon}
|
|
44
|
+
</span>
|
|
45
|
+
)}
|
|
42
46
|
<span>{label}</span>
|
|
43
47
|
</button>
|
|
44
48
|
))}
|
|
@@ -75,6 +75,18 @@ export function SiteSettingsDisplay({ siteConfig, onChange }: Props) {
|
|
|
75
75
|
label="Uppercase headings"
|
|
76
76
|
/>
|
|
77
77
|
|
|
78
|
+
<Checkbox
|
|
79
|
+
checked={siteConfig.uppercaseSubheadings}
|
|
80
|
+
onChange={(v) => update({ uppercaseSubheadings: v })}
|
|
81
|
+
label="Uppercase subheadings"
|
|
82
|
+
/>
|
|
83
|
+
|
|
84
|
+
<Checkbox
|
|
85
|
+
checked={siteConfig.uppercaseNavHeadings}
|
|
86
|
+
onChange={(v) => update({ uppercaseNavHeadings: v })}
|
|
87
|
+
label="Uppercase nav headings"
|
|
88
|
+
/>
|
|
89
|
+
|
|
78
90
|
<FontPicker
|
|
79
91
|
label="Body font"
|
|
80
92
|
value={siteConfig.bodyFont}
|
package/src/lib/registry.ts
CHANGED
|
@@ -95,7 +95,7 @@ export interface WrapperProps {
|
|
|
95
95
|
export interface SectionDefinition<T = unknown> {
|
|
96
96
|
type: string;
|
|
97
97
|
label: string;
|
|
98
|
-
icon?:
|
|
98
|
+
icon?: ReactNode;
|
|
99
99
|
schema: ZodType<T>;
|
|
100
100
|
component: ComponentType<SectionProps<T>>;
|
|
101
101
|
defaults: () => T;
|
|
@@ -113,7 +113,7 @@ export interface SectionDefinition<T = unknown> {
|
|
|
113
113
|
type DefineSectionInput<S extends ZodType> = {
|
|
114
114
|
type: string;
|
|
115
115
|
label: string;
|
|
116
|
-
icon?:
|
|
116
|
+
icon?: ReactNode;
|
|
117
117
|
schema: S;
|
|
118
118
|
component: ComponentType<SectionProps<z.infer<S>>>;
|
|
119
119
|
defaults: () => z.infer<S>;
|
|
@@ -38,6 +38,8 @@ export const SiteConfigSchema = z.object({
|
|
|
38
38
|
headingFont: z.string().default("system-ui"),
|
|
39
39
|
bodyFont: z.string().default("system-ui"),
|
|
40
40
|
uppercaseHeadings: z.boolean().default(true),
|
|
41
|
+
uppercaseSubheadings: z.boolean().default(true),
|
|
42
|
+
uppercaseNavHeadings: z.boolean().default(true),
|
|
41
43
|
googleFontsUrl: z.string()
|
|
42
44
|
.refine(url => url.startsWith("https://fonts.googleapis.com/"), "Must be a Google Fonts URL")
|
|
43
45
|
.nullable()
|