@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.
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 +8 -3
  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";
@@ -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 ?? null;
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
- // 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()