@drawnagency/primitives 0.1.42 → 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 +8 -3
- 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";
|
|
@@ -149,13 +150,17 @@ export function BugReportFAB({ siteId }: Props) {
|
|
|
149
150
|
setIsSubmitting(true);
|
|
150
151
|
|
|
151
152
|
try {
|
|
152
|
-
const supabase = createClient(
|
|
153
|
+
const supabase = createClient<Database>(
|
|
153
154
|
env("SUPABASE_URL"),
|
|
154
155
|
env("SUPABASE_ANON_KEY"),
|
|
155
156
|
);
|
|
156
157
|
|
|
157
158
|
const { data: userData } = await supabase.auth.getUser();
|
|
158
|
-
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
|
+
}
|
|
159
164
|
|
|
160
165
|
const { error } = await supabase.from("bug_reports").insert({
|
|
161
166
|
site_id: siteId,
|
|
@@ -164,7 +169,7 @@ export function BugReportFAB({ siteId }: Props) {
|
|
|
164
169
|
is_critical: isCritical,
|
|
165
170
|
description: description.trim(),
|
|
166
171
|
images: images.map((img) => img.dataUri),
|
|
167
|
-
context: capturedContextRef.current,
|
|
172
|
+
context: capturedContextRef.current as Json,
|
|
168
173
|
});
|
|
169
174
|
|
|
170
175
|
if (error) {
|
|
@@ -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()
|