@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.
Files changed (52) hide show
  1. package/dist/{chunk-62OWSJ7V.js → chunk-5XYUO4HP.js} +1 -1
  2. package/dist/{chunk-A4RARGF2.js → chunk-BJ6FYGYP.js} +2 -0
  3. package/dist/{chunk-BU52OBPW.js → chunk-P3HO76OS.js} +1 -1
  4. package/dist/components/editor/SectionWrapper.d.ts.map +1 -1
  5. package/dist/components/sections/Button/index.d.ts.map +1 -1
  6. package/dist/components/sections/Colors/index.d.ts.map +1 -1
  7. package/dist/components/sections/DoDontList/index.d.ts.map +1 -1
  8. package/dist/components/sections/DoDontMediaGrid/index.d.ts.map +1 -1
  9. package/dist/components/sections/IconList/index.d.ts.map +1 -1
  10. package/dist/components/sections/LinkHeading/index.d.ts.map +1 -1
  11. package/dist/components/sections/MediaGrid/index.d.ts.map +1 -1
  12. package/dist/components/sections/Prose/index.d.ts.map +1 -1
  13. package/dist/components/sections/SplitContent/index.d.ts.map +1 -1
  14. package/dist/components/sections/SubHeading/index.d.ts.map +1 -1
  15. package/dist/components/sections/SubSubHeading/index.d.ts.map +1 -1
  16. package/dist/components/shell/BugReportFAB.d.ts.map +1 -1
  17. package/dist/components/shell/EditorShell.d.ts.map +1 -1
  18. package/dist/components/shell/SectionSkeleton.d.ts.map +1 -1
  19. package/dist/components/shell/SectionTypePicker.d.ts +4 -3
  20. package/dist/components/shell/SectionTypePicker.d.ts.map +1 -1
  21. package/dist/components/shell/SiteSettingsDisplay.d.ts.map +1 -1
  22. package/dist/index.js +3 -3
  23. package/dist/lib/index.js +2 -2
  24. package/dist/lib/registry.d.ts +2 -2
  25. package/dist/lib/registry.d.ts.map +1 -1
  26. package/dist/schemas/index.js +2 -2
  27. package/dist/schemas/site-config.d.ts +2 -0
  28. package/dist/schemas/site-config.d.ts.map +1 -1
  29. package/dist/types/database.d.ts +509 -0
  30. package/dist/types/database.d.ts.map +1 -0
  31. package/dist/types/database.js +12 -0
  32. package/package.json +5 -1
  33. package/src/components/editor/SectionWrapper.tsx +9 -4
  34. package/src/components/sections/Button/index.tsx +2 -0
  35. package/src/components/sections/Colors/index.tsx +2 -0
  36. package/src/components/sections/DoDontList/index.tsx +2 -0
  37. package/src/components/sections/DoDontMediaGrid/index.tsx +2 -0
  38. package/src/components/sections/IconList/index.tsx +2 -0
  39. package/src/components/sections/LinkHeading/index.tsx +2 -0
  40. package/src/components/sections/MediaGrid/index.tsx +2 -0
  41. package/src/components/sections/Prose/index.tsx +2 -0
  42. package/src/components/sections/SplitContent/index.tsx +2 -0
  43. package/src/components/sections/SubHeading/index.tsx +2 -0
  44. package/src/components/sections/SubSubHeading/index.tsx +2 -0
  45. package/src/components/shell/BugReportFAB.tsx +12 -6
  46. package/src/components/shell/EditorShell.tsx +3 -3
  47. package/src/components/shell/SectionSkeleton.tsx +15 -14
  48. package/src/components/shell/SectionTypePicker.tsx +15 -11
  49. package/src/components/shell/SiteSettingsDisplay.tsx +12 -0
  50. package/src/lib/registry.ts +2 -2
  51. package/src/schemas/site-config.ts +2 -0
  52. 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 ?? null;
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">This is blocking my work</span>
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 Report"}
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
- // Trigger animation on next frame
17
- requestAnimationFrame(() => setIsVisible(true));
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
- {/* Placeholder block */}
39
- <div className="flex h-20 items-center justify-center rounded-lg bg-base-accent">
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
- {/* Type picker — centered below skeleton */}
44
- <div className="relative flex justify-center">
45
- <SectionTypePicker
46
- types={types}
47
- onSelect={onSelect}
48
- onClose={onDismiss}
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 ComponentType } from "react";
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: ComponentType;
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 (panelRef.current && !panelRef.current.contains(e.target as Node)) {
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="absolute z-50 mt-1 w-64 rounded-lg border border-base-200 bg-base p-2 shadow-lg"
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: Icon }) => (
35
+ {types.map(({ type, label, icon }) => (
34
36
  <button
35
37
  key={type}
36
- className="cursor-pointer flex w-full items-center gap-3 rounded-md px-3 py-2 text-left text-sm text-base-contrast-light hover:bg-base-accent hover:text-base-contrast"
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
- <span className="flex h-6 w-6 items-center justify-center text-base">
40
- <Icon />
41
- </span>
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}
@@ -95,7 +95,7 @@ export interface WrapperProps {
95
95
  export interface SectionDefinition<T = unknown> {
96
96
  type: string;
97
97
  label: string;
98
- icon?: string;
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?: string;
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()