@frontlit/page-builder 0.1.0-alpha.1 → 0.1.0-alpha.3

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 (116) hide show
  1. package/README.md +38 -2
  2. package/dist/blocks/index.d.ts +35 -21
  3. package/dist/blocks/index.js +4 -3
  4. package/dist/builder/index.d.ts +8 -11
  5. package/dist/builder/index.js +7 -5
  6. package/dist/{button-C3VrMJkr.d.ts → button-OLKN3wPf.d.ts} +3 -3
  7. package/dist/{chunk-7W6Q4WA7.js → chunk-36QHCIPM.js} +252 -282
  8. package/dist/chunk-36QHCIPM.js.map +1 -0
  9. package/dist/{chunk-GTY75HW2.js → chunk-ATNM5JHI.js} +2 -2
  10. package/dist/chunk-ATNM5JHI.js.map +1 -0
  11. package/dist/chunk-BBI7UHOA.js +56 -0
  12. package/dist/chunk-BBI7UHOA.js.map +1 -0
  13. package/dist/{chunk-MO76KZY2.js → chunk-GIKTOAQS.js} +661 -423
  14. package/dist/chunk-GIKTOAQS.js.map +1 -0
  15. package/dist/chunk-HWOVSHIW.js +4 -0
  16. package/dist/{chunk-RUYOL7JZ.js.map → chunk-HWOVSHIW.js.map} +1 -1
  17. package/dist/{chunk-SSQWYMQR.js → chunk-IU4KATI2.js} +74 -129
  18. package/dist/chunk-IU4KATI2.js.map +1 -0
  19. package/dist/chunk-J7Z4UW45.js +3418 -0
  20. package/dist/chunk-J7Z4UW45.js.map +1 -0
  21. package/dist/{chunk-7KIX4WAO.js → chunk-K4WCLHMS.js} +1435 -1382
  22. package/dist/chunk-K4WCLHMS.js.map +1 -0
  23. package/dist/chunk-XU7ALPGQ.js +22 -0
  24. package/dist/chunk-XU7ALPGQ.js.map +1 -0
  25. package/dist/components/index.d.ts +33 -8
  26. package/dist/components/index.js +43 -2
  27. package/dist/components/index.js.map +1 -1
  28. package/dist/data-slots.d.ts +3 -3
  29. package/dist/data-slots.js +1 -1
  30. package/dist/fonts/archivo/archivo-latin-variable.woff2 +0 -0
  31. package/dist/fonts/hanken-grotesk/hanken-grotesk-latin-variable.woff2 +0 -0
  32. package/dist/fonts/jetbrains-mono/jetbrains-mono-latin-variable.woff2 +0 -0
  33. package/dist/fonts/spline-sans-mono/spline-sans-mono-latin-variable.woff2 +0 -0
  34. package/dist/heading-DQCxQt38.d.ts +20 -0
  35. package/dist/image-Bk8kPXwP.d.ts +52 -0
  36. package/dist/index.d.ts +18 -11
  37. package/dist/index.js +9 -7
  38. package/dist/media-CnLImjVC.d.ts +21 -0
  39. package/dist/models/index.d.ts +6 -6
  40. package/dist/models/index.js +2 -1
  41. package/dist/{page-DfVl_aHX.d.ts → page-B5Gmm-Ys.d.ts} +2 -3
  42. package/dist/primitives/index.d.ts +287 -61
  43. package/dist/primitives/index.js +2 -1
  44. package/dist/renderer/index.d.ts +12 -8
  45. package/dist/renderer/index.js +6 -4
  46. package/dist/{section-background-BgOCp5l3.d.ts → section-background-6V_LCi5e.d.ts} +1 -1
  47. package/dist/server/index.d.ts +103 -0
  48. package/dist/server/index.js +2649 -0
  49. package/dist/server/index.js.map +1 -0
  50. package/dist/server/islands/code-snippet.d.ts +17 -0
  51. package/dist/server/islands/code-snippet.js +72 -0
  52. package/dist/server/islands/code-snippet.js.map +1 -0
  53. package/dist/server/islands/email-form.d.ts +50 -0
  54. package/dist/server/islands/email-form.js +406 -0
  55. package/dist/server/islands/email-form.js.map +1 -0
  56. package/dist/server/islands/header-mobile-menu.d.ts +22 -0
  57. package/dist/server/islands/header-mobile-menu.js +422 -0
  58. package/dist/server/islands/header-mobile-menu.js.map +1 -0
  59. package/dist/server/islands/inline-embed.d.ts +28 -0
  60. package/dist/server/islands/inline-embed.js +84 -0
  61. package/dist/server/islands/inline-embed.js.map +1 -0
  62. package/dist/server/islands/marquee-track.d.ts +18 -0
  63. package/dist/server/islands/marquee-track.js +67 -0
  64. package/dist/server/islands/marquee-track.js.map +1 -0
  65. package/dist/server/islands/nav-link.d.ts +37 -0
  66. package/dist/server/islands/nav-link.js +67 -0
  67. package/dist/server/islands/nav-link.js.map +1 -0
  68. package/dist/server/islands/support-form.d.ts +17 -0
  69. package/dist/server/islands/support-form.js +469 -0
  70. package/dist/server/islands/support-form.js.map +1 -0
  71. package/dist/server/islands/theme-mode-switcher.d.ts +38 -0
  72. package/dist/server/islands/theme-mode-switcher.js +169 -0
  73. package/dist/server/islands/theme-mode-switcher.js.map +1 -0
  74. package/dist/server/islands/video-with-preview.d.ts +13 -0
  75. package/dist/server/islands/video-with-preview.js +269 -0
  76. package/dist/server/islands/video-with-preview.js.map +1 -0
  77. package/dist/server/primitives.d.ts +200 -0
  78. package/dist/server/primitives.js +888 -0
  79. package/dist/server/primitives.js.map +1 -0
  80. package/dist/server/schema.d.ts +11308 -0
  81. package/dist/server/schema.js +6179 -0
  82. package/dist/server/schema.js.map +1 -0
  83. package/dist/server/theme-CF3fIKvw.d.ts +9 -0
  84. package/dist/{theme-style-39nxXQy0.d.ts → server/theme-style-5_AGy-4V.d.ts} +43 -1
  85. package/dist/server/theme-style-BPaMwk88.d.ts +183 -0
  86. package/dist/server/theme.d.ts +28 -0
  87. package/dist/{chunk-RBJBODQP.js → server/theme.js} +903 -1243
  88. package/dist/server/theme.js.map +1 -0
  89. package/dist/server/widget-YH9BrOwY.d.ts +77 -0
  90. package/dist/styles.css +4016 -8317
  91. package/dist/theme-style-BVd5tOsc.d.ts +183 -0
  92. package/dist/theme.css +186 -191
  93. package/dist/{widget--uEOpdq3.d.ts → widget-CpE2HdgU.d.ts} +25 -18
  94. package/package.json +71 -11
  95. package/schema/page.v1.json +3855 -0
  96. package/schema/site.v1.json +3811 -0
  97. package/dist/alignment-dZDzN96G.d.ts +0 -5
  98. package/dist/chunk-7KIX4WAO.js.map +0 -1
  99. package/dist/chunk-7W6Q4WA7.js.map +0 -1
  100. package/dist/chunk-GTY75HW2.js.map +0 -1
  101. package/dist/chunk-MO76KZY2.js.map +0 -1
  102. package/dist/chunk-RBJBODQP.js.map +0 -1
  103. package/dist/chunk-RUYOL7JZ.js +0 -4
  104. package/dist/chunk-SSQWYMQR.js.map +0 -1
  105. package/dist/fonts/montserrat/montserrat-latin-100-normal.woff2 +0 -0
  106. package/dist/fonts/montserrat/montserrat-latin-200-normal.woff2 +0 -0
  107. package/dist/fonts/montserrat/montserrat-latin-300-normal.woff2 +0 -0
  108. package/dist/fonts/montserrat/montserrat-latin-400-normal.woff2 +0 -0
  109. package/dist/fonts/montserrat/montserrat-latin-500-normal.woff2 +0 -0
  110. package/dist/fonts/montserrat/montserrat-latin-600-normal.woff2 +0 -0
  111. package/dist/fonts/montserrat/montserrat-latin-700-normal.woff2 +0 -0
  112. package/dist/fonts/montserrat/montserrat-latin-800-normal.woff2 +0 -0
  113. package/dist/fonts/montserrat/montserrat-latin-900-normal.woff2 +0 -0
  114. package/dist/fonts/source-sans-3/source-sans-3-latin-400-normal.woff2 +0 -0
  115. package/dist/image-D7GlnOio.d.ts +0 -30
  116. package/dist/media-Njv378j7.d.ts +0 -13
@@ -0,0 +1,67 @@
1
+ "use client";
2
+ "use client";
3
+
4
+ // src/blocks/marquee/marquee-track.tsx
5
+ import { useEffect, useId, useRef, useState } from "react";
6
+
7
+ // src/primitives/lib/utils.ts
8
+ import { clsx } from "clsx";
9
+ import { twMerge } from "tailwind-merge";
10
+ function cn(...inputs) {
11
+ return twMerge(clsx(inputs));
12
+ }
13
+
14
+ // src/blocks/marquee/marquee-track.tsx
15
+ import { jsx, jsxs } from "react/jsx-runtime";
16
+ function MarqueeTrack({
17
+ children,
18
+ speed,
19
+ direction,
20
+ pauseOnHover
21
+ }) {
22
+ const containerRef = useRef(null);
23
+ const contentRef = useRef(null);
24
+ const [containerWidth, setContainerWidth] = useState(0);
25
+ const [contentWidth, setContentWidth] = useState(0);
26
+ const animationName = `pb-marquee-${useId().replace(/[^a-zA-Z0-9]/g, "")}`;
27
+ useEffect(() => {
28
+ const measure = () => {
29
+ setContainerWidth(containerRef.current?.offsetWidth ?? 0);
30
+ setContentWidth(contentRef.current?.offsetWidth ?? 0);
31
+ };
32
+ measure();
33
+ window.addEventListener("resize", measure);
34
+ return () => window.removeEventListener("resize", measure);
35
+ }, [children, speed]);
36
+ const duration = contentWidth > 0 ? contentWidth / speed : 0;
37
+ const duplicatesNeeded = contentWidth > 0 ? Math.max(2, Math.ceil(containerWidth * 2 / contentWidth)) : 2;
38
+ return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "w-full overflow-hidden", children: [
39
+ contentWidth > 0 && /* @__PURE__ */ jsx("style", { children: `
40
+ @keyframes ${animationName} {
41
+ from { transform: translateX(0); }
42
+ to { transform: translateX(-${contentWidth}px); }
43
+ }
44
+ ` }),
45
+ /* @__PURE__ */ jsxs(
46
+ "div",
47
+ {
48
+ className: cn(
49
+ "inline-flex w-max",
50
+ pauseOnHover && "hover:[animation-play-state:paused]"
51
+ ),
52
+ style: contentWidth > 0 ? {
53
+ animation: `${animationName} ${duration}s linear infinite`,
54
+ animationDirection: direction === "left" ? "reverse" : "normal"
55
+ } : void 0,
56
+ children: [
57
+ /* @__PURE__ */ jsx("div", { ref: contentRef, className: "inline-flex shrink-0", children }),
58
+ Array.from({ length: duplicatesNeeded }).map((_, i) => /* @__PURE__ */ jsx("div", { className: "inline-flex shrink-0", "aria-hidden": true, children }, i))
59
+ ]
60
+ }
61
+ )
62
+ ] });
63
+ }
64
+ export {
65
+ MarqueeTrack
66
+ };
67
+ //# sourceMappingURL=marquee-track.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/blocks/marquee/marquee-track.tsx","../../../src/primitives/lib/utils.ts"],"sourcesContent":["\"use client\";\n\nimport React, { useEffect, useId, useRef, useState } from \"react\";\nimport { cn } from \"../../primitives/lib/utils\";\n\n/**\n * Infinite horizontal scroller. Measures the width of one copy of the\n * content, then renders enough duplicate copies to always cover at least\n * twice the container's width, and animates a translateX loop across\n * exactly one content-width — because the duplicates repeat the same\n * sequence, the loop point is visually seamless regardless of how the\n * content width compares to the container width.\n */\nexport function MarqueeTrack({\n children,\n speed,\n direction,\n pauseOnHover,\n}: {\n children: React.ReactNode;\n speed: number;\n direction: \"left\" | \"right\";\n pauseOnHover?: boolean;\n}) {\n const containerRef = useRef<HTMLDivElement>(null);\n const contentRef = useRef<HTMLDivElement>(null);\n const [containerWidth, setContainerWidth] = useState(0);\n const [contentWidth, setContentWidth] = useState(0);\n const animationName = `pb-marquee-${useId().replace(/[^a-zA-Z0-9]/g, \"\")}`;\n\n useEffect(() => {\n const measure = () => {\n setContainerWidth(containerRef.current?.offsetWidth ?? 0);\n setContentWidth(contentRef.current?.offsetWidth ?? 0);\n };\n measure();\n window.addEventListener(\"resize\", measure);\n return () => window.removeEventListener(\"resize\", measure);\n }, [children, speed]);\n\n const duration = contentWidth > 0 ? contentWidth / speed : 0;\n const duplicatesNeeded =\n contentWidth > 0 ? Math.max(2, Math.ceil((containerWidth * 2) / contentWidth)) : 2;\n\n return (\n <div ref={containerRef} className=\"w-full overflow-hidden\">\n {contentWidth > 0 && (\n <style>{`\n @keyframes ${animationName} {\n from { transform: translateX(0); }\n to { transform: translateX(-${contentWidth}px); }\n }\n `}</style>\n )}\n <div\n className={cn(\n \"inline-flex w-max\",\n pauseOnHover && \"hover:[animation-play-state:paused]\",\n )}\n style={\n contentWidth > 0\n ? {\n animation: `${animationName} ${duration}s linear infinite`,\n animationDirection: direction === \"left\" ? \"reverse\" : \"normal\",\n }\n : undefined\n }\n >\n <div ref={contentRef} className=\"inline-flex shrink-0\">\n {children}\n </div>\n {Array.from({ length: duplicatesNeeded }).map((_, i) => (\n <div key={i} className=\"inline-flex shrink-0\" aria-hidden>\n {children}\n </div>\n ))}\n </div>\n </div>\n );\n}\n","import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n"],"mappings":";;;;AAEA,SAAgB,WAAW,OAAO,QAAQ,gBAAgB;;;ACF1D,SAA0B,YAAY;AACtC,SAAS,eAAe;AAEjB,SAAS,MAAM,QAAsB;AAC1C,SAAO,QAAQ,KAAK,MAAM,CAAC;AAC7B;;;AD0CQ,cAOF,YAPE;AAlCD,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,QAAM,eAAe,OAAuB,IAAI;AAChD,QAAM,aAAa,OAAuB,IAAI;AAC9C,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,SAAS,CAAC;AACtD,QAAM,CAAC,cAAc,eAAe,IAAI,SAAS,CAAC;AAClD,QAAM,gBAAgB,cAAc,MAAM,EAAE,QAAQ,iBAAiB,EAAE,CAAC;AAExE,YAAU,MAAM;AACd,UAAM,UAAU,MAAM;AACpB,wBAAkB,aAAa,SAAS,eAAe,CAAC;AACxD,sBAAgB,WAAW,SAAS,eAAe,CAAC;AAAA,IACtD;AACA,YAAQ;AACR,WAAO,iBAAiB,UAAU,OAAO;AACzC,WAAO,MAAM,OAAO,oBAAoB,UAAU,OAAO;AAAA,EAC3D,GAAG,CAAC,UAAU,KAAK,CAAC;AAEpB,QAAM,WAAW,eAAe,IAAI,eAAe,QAAQ;AAC3D,QAAM,mBACJ,eAAe,IAAI,KAAK,IAAI,GAAG,KAAK,KAAM,iBAAiB,IAAK,YAAY,CAAC,IAAI;AAEnF,SACE,qBAAC,SAAI,KAAK,cAAc,WAAU,0BAC/B;AAAA,mBAAe,KACd,oBAAC,WAAO;AAAA,iCACiB,aAAa;AAAA;AAAA,sDAEQ,YAAY;AAAA;AAAA,mBAEhD;AAAA,IAEZ;AAAA,MAAC;AAAA;AAAA,QACC,WAAW;AAAA,UACT;AAAA,UACA,gBAAgB;AAAA,QAClB;AAAA,QACA,OACE,eAAe,IACX;AAAA,UACE,WAAW,GAAG,aAAa,IAAI,QAAQ;AAAA,UACvC,oBAAoB,cAAc,SAAS,YAAY;AAAA,QACzD,IACA;AAAA,QAGN;AAAA,8BAAC,SAAI,KAAK,YAAY,WAAU,wBAC7B,UACH;AAAA,UACC,MAAM,KAAK,EAAE,QAAQ,iBAAiB,CAAC,EAAE,IAAI,CAAC,GAAG,MAChD,oBAAC,SAAY,WAAU,wBAAuB,eAAW,MACtD,YADO,CAEV,CACD;AAAA;AAAA;AAAA,IACH;AAAA,KACF;AAEJ;","names":[]}
@@ -0,0 +1,37 @@
1
+ import * as React from 'react';
2
+
3
+ interface PageBuilderLinkProps {
4
+ href: string;
5
+ children?: React.ReactNode;
6
+ className?: string;
7
+ style?: React.CSSProperties;
8
+ onClick?: React.MouseEventHandler<HTMLAnchorElement>;
9
+ target?: string;
10
+ rel?: string;
11
+ }
12
+ /**
13
+ * Optional host-supplied link. Next.js apps can pass `next/link`; Vite / Remix
14
+ * / plain React hosts can omit it and NavLink will render a native `<a>`.
15
+ */
16
+ type PageBuilderLinkComponent = React.ComponentType<PageBuilderLinkProps>;
17
+ declare function PageBuilderLinkProvider({ component, children, }: {
18
+ component?: PageBuilderLinkComponent | null;
19
+ children: React.ReactNode;
20
+ }): React.JSX.Element;
21
+ declare function usePageBuilderLinkComponent(): PageBuilderLinkComponent;
22
+ interface NavLinkProps {
23
+ href: string;
24
+ children?: React.ReactNode;
25
+ openInSameTab?: boolean;
26
+ style?: React.CSSProperties;
27
+ className?: string;
28
+ onClick?: () => void;
29
+ }
30
+ /**
31
+ * A navigation-aware link used inside block widgets. In-page anchors scroll
32
+ * to the target. Internal paths (`/...`) use the host `linkComponent` when
33
+ * one is provided (e.g. Next.js `Link`); everything else is a native `<a>`.
34
+ */
35
+ declare function NavLink({ href, children, openInSameTab, style, className, onClick, }: NavLinkProps): React.JSX.Element;
36
+
37
+ export { NavLink, type PageBuilderLinkComponent, type PageBuilderLinkProps, PageBuilderLinkProvider, usePageBuilderLinkComponent };
@@ -0,0 +1,67 @@
1
+ "use client";
2
+ "use client";
3
+
4
+ // src/components/nav-link.tsx
5
+ import * as React from "react";
6
+ import { jsx } from "react/jsx-runtime";
7
+ var LINK_CONTEXT_KEY = /* @__PURE__ */ Symbol.for("@frontlit/page-builder/link-context");
8
+ var globalRegistry = globalThis;
9
+ var PageBuilderLinkContext = globalRegistry[LINK_CONTEXT_KEY] ?? (globalRegistry[LINK_CONTEXT_KEY] = React.createContext(null));
10
+ function PageBuilderLinkProvider({
11
+ component,
12
+ children
13
+ }) {
14
+ return /* @__PURE__ */ jsx(PageBuilderLinkContext.Provider, { value: component ?? null, children });
15
+ }
16
+ function usePageBuilderLinkComponent() {
17
+ return React.useContext(PageBuilderLinkContext);
18
+ }
19
+ function NavLink({
20
+ href,
21
+ children,
22
+ openInSameTab = true,
23
+ style,
24
+ className = "",
25
+ onClick
26
+ }) {
27
+ const LinkComponent = usePageBuilderLinkComponent();
28
+ const isInternal = Boolean(href && href.startsWith("/"));
29
+ const isInPageNavigation = Boolean(href && href.startsWith("#"));
30
+ const target = openInSameTab ? void 0 : "_blank";
31
+ const rel = target === "_blank" ? "noopener noreferrer" : void 0;
32
+ const handleClick = () => {
33
+ onClick?.();
34
+ };
35
+ if (!isInPageNavigation && isInternal && LinkComponent) {
36
+ return /* @__PURE__ */ jsx(
37
+ LinkComponent,
38
+ {
39
+ href,
40
+ style,
41
+ className,
42
+ onClick: handleClick,
43
+ target,
44
+ rel,
45
+ children
46
+ }
47
+ );
48
+ }
49
+ return /* @__PURE__ */ jsx(
50
+ "a",
51
+ {
52
+ href,
53
+ style,
54
+ className,
55
+ target,
56
+ rel,
57
+ onClick: handleClick,
58
+ children
59
+ }
60
+ );
61
+ }
62
+ export {
63
+ NavLink,
64
+ PageBuilderLinkProvider,
65
+ usePageBuilderLinkComponent
66
+ };
67
+ //# sourceMappingURL=nav-link.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/nav-link.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\n\nexport interface PageBuilderLinkProps {\n href: string;\n children?: React.ReactNode;\n className?: string;\n style?: React.CSSProperties;\n onClick?: React.MouseEventHandler<HTMLAnchorElement>;\n target?: string;\n rel?: string;\n}\n\n/**\n * Optional host-supplied link. Next.js apps can pass `next/link`; Vite / Remix\n * / plain React hosts can omit it and NavLink will render a native `<a>`.\n */\nexport type PageBuilderLinkComponent = React.ComponentType<PageBuilderLinkProps>;\n\n// Server-rendered pages load this module more than once (each client island is\n// bundled on its own), so the context is shared through the global registry\n// rather than created per copy. Otherwise a provider from one copy would be\n// invisible to a NavLink from another.\nconst LINK_CONTEXT_KEY = Symbol.for(\"@frontlit/page-builder/link-context\");\nconst globalRegistry = globalThis as typeof globalThis & {\n [LINK_CONTEXT_KEY]?: React.Context<PageBuilderLinkComponent | null>;\n};\nconst PageBuilderLinkContext = (globalRegistry[LINK_CONTEXT_KEY] ??=\n React.createContext<PageBuilderLinkComponent | null>(null));\n\nexport function PageBuilderLinkProvider({\n component,\n children,\n}: {\n component?: PageBuilderLinkComponent | null;\n children: React.ReactNode;\n}) {\n return (\n <PageBuilderLinkContext.Provider value={component ?? null}>\n {children}\n </PageBuilderLinkContext.Provider>\n );\n}\n\nexport function usePageBuilderLinkComponent() {\n return React.useContext(PageBuilderLinkContext);\n}\n\ninterface NavLinkProps {\n href: string;\n children?: React.ReactNode;\n openInSameTab?: boolean;\n style?: React.CSSProperties;\n className?: string;\n onClick?: () => void;\n}\n\n/**\n * A navigation-aware link used inside block widgets. In-page anchors scroll\n * to the target. Internal paths (`/...`) use the host `linkComponent` when\n * one is provided (e.g. Next.js `Link`); everything else is a native `<a>`.\n */\nexport function NavLink({\n href,\n children,\n openInSameTab = true,\n style,\n className = \"\",\n onClick,\n}: NavLinkProps) {\n const LinkComponent = usePageBuilderLinkComponent();\n const isInternal = Boolean(href && href.startsWith(\"/\"));\n const isInPageNavigation = Boolean(href && href.startsWith(\"#\"));\n const target = openInSameTab ? undefined : \"_blank\";\n const rel = target === \"_blank\" ? \"noopener noreferrer\" : undefined;\n const handleClick: React.MouseEventHandler<HTMLAnchorElement> = () => {\n onClick?.();\n };\n\n if (!isInPageNavigation && isInternal && LinkComponent) {\n return (\n <LinkComponent\n href={href}\n style={style}\n className={className}\n onClick={handleClick}\n target={target}\n rel={rel}\n >\n {children}\n </LinkComponent>\n );\n }\n\n return (\n <a\n href={href}\n style={style}\n className={className}\n target={target}\n rel={rel}\n onClick={handleClick}\n >\n {children}\n </a>\n );\n}\n"],"mappings":";;;;AAEA,YAAY,WAAW;AAqCnB;AAfJ,IAAM,mBAAmB,uBAAO,IAAI,qCAAqC;AACzE,IAAM,iBAAiB;AAGvB,IAAM,yBAA0B,wEACxB,oBAA+C,IAAI;AAEpD,SAAS,wBAAwB;AAAA,EACtC;AAAA,EACA;AACF,GAGG;AACD,SACE,oBAAC,uBAAuB,UAAvB,EAAgC,OAAO,aAAa,MAClD,UACH;AAEJ;AAEO,SAAS,8BAA8B;AAC5C,SAAa,iBAAW,sBAAsB;AAChD;AAgBO,SAAS,QAAQ;AAAA,EACtB;AAAA,EACA;AAAA,EACA,gBAAgB;AAAA,EAChB;AAAA,EACA,YAAY;AAAA,EACZ;AACF,GAAiB;AACf,QAAM,gBAAgB,4BAA4B;AAClD,QAAM,aAAa,QAAQ,QAAQ,KAAK,WAAW,GAAG,CAAC;AACvD,QAAM,qBAAqB,QAAQ,QAAQ,KAAK,WAAW,GAAG,CAAC;AAC/D,QAAM,SAAS,gBAAgB,SAAY;AAC3C,QAAM,MAAM,WAAW,WAAW,wBAAwB;AAC1D,QAAM,cAA0D,MAAM;AACpE,cAAU;AAAA,EACZ;AAEA,MAAI,CAAC,sBAAsB,cAAc,eAAe;AACtD,WACE;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA,SAAS;AAAA,QACT;AAAA,QACA;AAAA,QAEC;AAAA;AAAA,IACH;AAAA,EAEJ;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,SAAS;AAAA,MAER;AAAA;AAAA,EACH;AAEJ;","names":[]}
@@ -0,0 +1,17 @@
1
+ import React__default from 'react';
2
+ import { T as ThemeStyle } from '../theme-style-5_AGy-4V.js';
3
+ import { P as PageData } from '../widget-YH9BrOwY.js';
4
+
5
+ /**
6
+ * The interactive part of the support-form block: the ticket form and its
7
+ * success message. The block's heading stays server-rendered around it.
8
+ */
9
+ declare function SupportForm({ theme: overriddenTheme, buttonCaption, successMessage, pageData, editing, }: {
10
+ theme: ThemeStyle;
11
+ buttonCaption: string;
12
+ successMessage: string;
13
+ pageData: PageData;
14
+ editing?: boolean;
15
+ }): React__default.JSX.Element;
16
+
17
+ export { SupportForm };
@@ -0,0 +1,469 @@
1
+ "use client";
2
+ "use client";
3
+
4
+ // src/blocks/support-form/form.tsx
5
+ import { useEffect, useRef, useState } from "react";
6
+
7
+ // src/primitives/lib/utils.ts
8
+ import { clsx } from "clsx";
9
+ import { twMerge } from "tailwind-merge";
10
+ function cn(...inputs) {
11
+ return twMerge(clsx(inputs));
12
+ }
13
+
14
+ // src/primitives/components/ui/button.tsx
15
+ import { Slot } from "@radix-ui/react-slot";
16
+ import { cva } from "class-variance-authority";
17
+ import * as React from "react";
18
+ import { jsx } from "react/jsx-runtime";
19
+ var buttonVariants = cva(
20
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
21
+ {
22
+ variants: {
23
+ variant: {
24
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
25
+ destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
26
+ outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
27
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
28
+ ghost: "hover:bg-accent hover:text-accent-foreground",
29
+ link: "text-primary underline-offset-4 hover:underline"
30
+ },
31
+ size: {
32
+ default: "h-10 px-4 py-2",
33
+ sm: "h-9 rounded-md px-3",
34
+ lg: "h-11 rounded-md px-8",
35
+ icon: "h-10 w-10"
36
+ }
37
+ },
38
+ defaultVariants: {
39
+ variant: "default",
40
+ size: "default"
41
+ }
42
+ }
43
+ );
44
+ var Button = React.forwardRef(
45
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
46
+ const Comp = asChild ? Slot : "button";
47
+ return /* @__PURE__ */ jsx(
48
+ Comp,
49
+ {
50
+ className: cn(buttonVariants({ variant, size, className })),
51
+ ref,
52
+ ...props
53
+ }
54
+ );
55
+ }
56
+ );
57
+ Button.displayName = "Button";
58
+
59
+ // src/primitives/button.tsx
60
+ import { jsx as jsx2 } from "react/jsx-runtime";
61
+ var Button2 = ({
62
+ variant = "default",
63
+ size = "default",
64
+ disabled = false,
65
+ children,
66
+ className = "",
67
+ theme,
68
+ ...props
69
+ }) => {
70
+ const buttonStyles = theme?.interactives?.button;
71
+ const typographyStyles = theme?.typography?.button;
72
+ const isIcon = size === "icon";
73
+ const classes = cn(
74
+ // Base styles
75
+ "",
76
+ // Theme typography
77
+ typographyStyles?.fontFamily,
78
+ typographyStyles?.fontSize,
79
+ typographyStyles?.fontWeight,
80
+ typographyStyles?.lineHeight,
81
+ typographyStyles?.letterSpacing,
82
+ typographyStyles?.textTransform,
83
+ typographyStyles?.textDecoration,
84
+ typographyStyles?.textOverflow,
85
+ // Theme interactivity. Icon buttons stay square — CTA padding
86
+ // would stretch them off the size="icon" box.
87
+ !isIcon && buttonStyles?.padding?.x,
88
+ !isIcon && buttonStyles?.padding?.y,
89
+ buttonStyles?.border?.width,
90
+ buttonStyles?.border?.radius,
91
+ buttonStyles?.border?.style,
92
+ // buttonStyles?.border?.color,
93
+ buttonStyles?.shadow,
94
+ // Theme disabled states
95
+ disabled && buttonStyles?.disabled?.opacity,
96
+ disabled && buttonStyles?.disabled?.cursor,
97
+ disabled && buttonStyles?.disabled?.color,
98
+ disabled && buttonStyles?.disabled?.background,
99
+ disabled && buttonStyles?.disabled?.border,
100
+ // User overrides
101
+ buttonStyles?.custom,
102
+ className
103
+ );
104
+ return /* @__PURE__ */ jsx2(
105
+ Button,
106
+ {
107
+ variant,
108
+ size,
109
+ disabled,
110
+ className: classes,
111
+ ...props,
112
+ children
113
+ }
114
+ );
115
+ };
116
+
117
+ // src/primitives/input.tsx
118
+ import React3 from "react";
119
+
120
+ // src/primitives/components/ui/input.tsx
121
+ import * as React2 from "react";
122
+ import { jsx as jsx3 } from "react/jsx-runtime";
123
+ var Input = React2.forwardRef(
124
+ ({ className, type, ...props }, ref) => {
125
+ return /* @__PURE__ */ jsx3(
126
+ "input",
127
+ {
128
+ type,
129
+ className: cn(
130
+ "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
131
+ className
132
+ ),
133
+ ref,
134
+ ...props
135
+ }
136
+ );
137
+ }
138
+ );
139
+ Input.displayName = "Input";
140
+
141
+ // src/primitives/input.tsx
142
+ import { jsx as jsx4 } from "react/jsx-runtime";
143
+ var Input2 = React3.forwardRef(
144
+ ({
145
+ className = "",
146
+ theme,
147
+ error = false,
148
+ /*style = {},*/
149
+ ...props
150
+ }, ref) => {
151
+ const inputStyles = theme?.interactives?.input;
152
+ const typographyStyles = theme?.typography?.input;
153
+ const classes = cn(
154
+ // Base styles
155
+ // "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
156
+ // Theme typography
157
+ typographyStyles?.fontFamily,
158
+ typographyStyles?.fontSize,
159
+ typographyStyles?.fontWeight,
160
+ typographyStyles?.lineHeight,
161
+ typographyStyles?.letterSpacing,
162
+ typographyStyles?.textTransform,
163
+ typographyStyles?.textDecoration,
164
+ typographyStyles?.textOverflow,
165
+ // Theme interactivity
166
+ inputStyles?.padding?.x,
167
+ inputStyles?.padding?.y,
168
+ inputStyles?.border?.width,
169
+ inputStyles?.border?.style,
170
+ inputStyles?.border?.radius,
171
+ // inputStyles?.border?.color,
172
+ inputStyles?.shadow,
173
+ // Theme disabled states
174
+ props.disabled && inputStyles?.disabled?.opacity,
175
+ props.disabled && inputStyles?.disabled?.cursor,
176
+ props.disabled && inputStyles?.disabled?.color,
177
+ props.disabled && inputStyles?.disabled?.background,
178
+ props.disabled && inputStyles?.disabled?.border,
179
+ // Error state
180
+ error && "border-red-300 focus:border-red-500 focus:ring-red-500",
181
+ // User overrides
182
+ inputStyles?.custom,
183
+ className
184
+ );
185
+ return /* @__PURE__ */ jsx4(Input, { ref, className: classes, ...props });
186
+ }
187
+ );
188
+ Input2.displayName = "Input";
189
+
190
+ // src/primitives/label.tsx
191
+ import React5 from "react";
192
+
193
+ // src/primitives/components/ui/label.tsx
194
+ import * as LabelPrimitive from "@radix-ui/react-label";
195
+ import { cva as cva2 } from "class-variance-authority";
196
+ import * as React4 from "react";
197
+ import { jsx as jsx5 } from "react/jsx-runtime";
198
+ var labelVariants = cva2(
199
+ "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
200
+ );
201
+ var Label = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx5(
202
+ LabelPrimitive.Root,
203
+ {
204
+ ref,
205
+ className: cn(labelVariants(), className),
206
+ ...props
207
+ }
208
+ ));
209
+ Label.displayName = LabelPrimitive.Root.displayName;
210
+
211
+ // src/primitives/label.tsx
212
+ import { jsx as jsx6 } from "react/jsx-runtime";
213
+ var Label2 = React5.forwardRef(
214
+ ({ className = "", theme, error = false, ...props }, ref) => {
215
+ const typographyStyles = theme?.typography?.text2;
216
+ const classes = cn(
217
+ // Theme typography
218
+ typographyStyles?.fontFamily,
219
+ typographyStyles?.fontSize,
220
+ typographyStyles?.fontWeight,
221
+ typographyStyles?.lineHeight,
222
+ typographyStyles?.letterSpacing,
223
+ typographyStyles?.textTransform,
224
+ typographyStyles?.textDecoration,
225
+ typographyStyles?.textOverflow,
226
+ // Error state
227
+ error && "text-red-500",
228
+ className
229
+ );
230
+ return /* @__PURE__ */ jsx6(Label, { ref, className: classes, ...props });
231
+ }
232
+ );
233
+ Label2.displayName = "Label";
234
+
235
+ // src/primitives/text2.tsx
236
+ import { jsx as jsx7 } from "react/jsx-runtime";
237
+ var Text2 = ({
238
+ children,
239
+ className = "",
240
+ theme,
241
+ component = "p",
242
+ ...props
243
+ }) => {
244
+ const typographyStyles = theme?.typography?.text2;
245
+ const classes = cn(
246
+ // Base styles
247
+ "text-sm",
248
+ // Theme overrides
249
+ typographyStyles?.fontFamily,
250
+ typographyStyles?.fontSize,
251
+ typographyStyles?.fontWeight,
252
+ typographyStyles?.lineHeight,
253
+ typographyStyles?.letterSpacing,
254
+ typographyStyles?.textTransform,
255
+ typographyStyles?.textDecoration,
256
+ typographyStyles?.textOverflow,
257
+ className
258
+ );
259
+ return component === "p" ? /* @__PURE__ */ jsx7("p", { className: classes, ...props, children }) : /* @__PURE__ */ jsx7("span", { className: classes, ...props, children });
260
+ };
261
+
262
+ // src/blocks/support-form/form.tsx
263
+ import { jsx as jsx8, jsxs } from "react/jsx-runtime";
264
+ function suggestionHref(item) {
265
+ if (item.kind === "blog") return `/blog/${item.slug}`;
266
+ if (item.kind === "changelog") return `/changelog`;
267
+ return `/docs/${item.slug}`;
268
+ }
269
+ async function searchDocumentation(query, pageData) {
270
+ if (!pageData.teamId || !pageData.publicApiBaseUrl || query.trim().length < 3)
271
+ return [];
272
+ try {
273
+ const res = await fetch(
274
+ `${pageData.publicApiBaseUrl}/public/${pageData.teamId}/content/documentation/search?q=${encodeURIComponent(query)}&k=3`
275
+ );
276
+ if (!res.ok) return [];
277
+ const body = await res.json();
278
+ const seen = /* @__PURE__ */ new Set();
279
+ return body.items.filter(
280
+ (item) => seen.has(item.documentId) ? false : (seen.add(item.documentId), true)
281
+ );
282
+ } catch {
283
+ return [];
284
+ }
285
+ }
286
+ async function submitTicket(input, pageData) {
287
+ if (!pageData.teamId || !pageData.publicApiBaseUrl) {
288
+ await new Promise((resolve) => setTimeout(resolve, 600));
289
+ return { success: true };
290
+ }
291
+ try {
292
+ const res = await fetch(
293
+ `${pageData.publicApiBaseUrl}/public/${pageData.teamId}/tickets`,
294
+ {
295
+ method: "POST",
296
+ headers: { "content-type": "application/json" },
297
+ body: JSON.stringify({ ...input, source: "form" })
298
+ }
299
+ );
300
+ return { success: res.ok };
301
+ } catch {
302
+ return { success: false };
303
+ }
304
+ }
305
+ function SupportForm({
306
+ theme: overriddenTheme,
307
+ buttonCaption,
308
+ successMessage,
309
+ pageData,
310
+ editing
311
+ }) {
312
+ const [subject, setSubject] = useState("");
313
+ const [message, setMessage] = useState("");
314
+ const [email, setEmail] = useState("");
315
+ const [name, setName] = useState("");
316
+ const [suggestions, setSuggestions] = useState([]);
317
+ const [isSubmitting, setIsSubmitting] = useState(false);
318
+ const [success, setSuccess] = useState(false);
319
+ const debounceRef = useRef(null);
320
+ useEffect(() => {
321
+ if (editing) return;
322
+ if (debounceRef.current) clearTimeout(debounceRef.current);
323
+ debounceRef.current = setTimeout(() => {
324
+ searchDocumentation(subject, pageData).then(setSuggestions);
325
+ }, 400);
326
+ return () => {
327
+ if (debounceRef.current) clearTimeout(debounceRef.current);
328
+ };
329
+ }, [subject]);
330
+ const onSubmit = async (e) => {
331
+ e.preventDefault();
332
+ if (editing) return;
333
+ setIsSubmitting(true);
334
+ try {
335
+ const result = await submitTicket(
336
+ { subject, message, email, name: name || void 0 },
337
+ pageData
338
+ );
339
+ if (result.success) {
340
+ setSuccess(true);
341
+ setSubject("");
342
+ setMessage("");
343
+ setEmail("");
344
+ setName("");
345
+ setSuggestions([]);
346
+ }
347
+ } finally {
348
+ setIsSubmitting(false);
349
+ }
350
+ };
351
+ const inputStyles = overriddenTheme.interactives.input;
352
+ const inputTypography = overriddenTheme.typography.input;
353
+ const cardStyles = overriddenTheme.interactives.card;
354
+ return success ? /* @__PURE__ */ jsx8(Text2, { theme: overriddenTheme, className: "mt-4", children: successMessage }) : /* @__PURE__ */ jsxs("form", { onSubmit, className: "mt-4 flex w-full max-w-lg flex-col gap-3", children: [
355
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1 items-start", children: [
356
+ /* @__PURE__ */ jsx8(Label2, { theme: overriddenTheme, htmlFor: "subject", children: "Subject" }),
357
+ /* @__PURE__ */ jsx8(
358
+ Input2,
359
+ {
360
+ theme: overriddenTheme,
361
+ id: "subject",
362
+ value: subject,
363
+ onChange: (e) => setSubject(e.target.value),
364
+ placeholder: "What do you need help with?",
365
+ required: true,
366
+ className: "w-full"
367
+ }
368
+ )
369
+ ] }),
370
+ suggestions.length > 0 && /* @__PURE__ */ jsxs(
371
+ "div",
372
+ {
373
+ className: cn(
374
+ "rounded-lg border border-border bg-card p-3 text-sm text-card-foreground shadow-sm",
375
+ cardStyles.border?.width,
376
+ cardStyles.border?.radius,
377
+ cardStyles.border?.style,
378
+ cardStyles.shadow
379
+ ),
380
+ children: [
381
+ /* @__PURE__ */ jsx8("p", { className: "mb-1 font-medium", children: "This might help:" }),
382
+ /* @__PURE__ */ jsx8("ul", { className: "list-inside list-disc", children: suggestions.map((doc) => /* @__PURE__ */ jsx8("li", { children: /* @__PURE__ */ jsx8(
383
+ "a",
384
+ {
385
+ href: suggestionHref(doc),
386
+ target: "_blank",
387
+ rel: "noreferrer",
388
+ className: "underline",
389
+ children: doc.title ?? doc.slug
390
+ }
391
+ ) }, doc.documentId)) })
392
+ ]
393
+ }
394
+ ),
395
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1 items-start", children: [
396
+ /* @__PURE__ */ jsx8(Label2, { theme: overriddenTheme, htmlFor: "message", children: "Message" }),
397
+ /* @__PURE__ */ jsx8(
398
+ "textarea",
399
+ {
400
+ id: "message",
401
+ value: message,
402
+ onChange: (e) => setMessage(e.target.value),
403
+ placeholder: "Describe the issue\u2026",
404
+ required: true,
405
+ rows: 4,
406
+ className: cn(
407
+ "w-full border border-input bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
408
+ inputTypography.fontFamily,
409
+ inputTypography.fontSize,
410
+ inputTypography.fontWeight,
411
+ inputTypography.lineHeight,
412
+ inputTypography.letterSpacing,
413
+ inputStyles.border?.width,
414
+ inputStyles.border?.style,
415
+ inputStyles.border?.radius,
416
+ inputStyles.shadow,
417
+ inputStyles.custom
418
+ )
419
+ }
420
+ )
421
+ ] }),
422
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 sm:!flex-row", children: [
423
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col gap-1 items-start", children: [
424
+ /* @__PURE__ */ jsx8(Label2, { theme: overriddenTheme, htmlFor: "email", children: "Email" }),
425
+ /* @__PURE__ */ jsx8(
426
+ Input2,
427
+ {
428
+ theme: overriddenTheme,
429
+ id: "email",
430
+ type: "email",
431
+ value: email,
432
+ onChange: (e) => setEmail(e.target.value),
433
+ placeholder: "you@example.com",
434
+ required: true,
435
+ className: "w-full"
436
+ }
437
+ )
438
+ ] }),
439
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col gap-1 items-start", children: [
440
+ /* @__PURE__ */ jsx8(Label2, { theme: overriddenTheme, htmlFor: "name", children: "Name (optional)" }),
441
+ /* @__PURE__ */ jsx8(
442
+ Input2,
443
+ {
444
+ theme: overriddenTheme,
445
+ id: "name",
446
+ value: name,
447
+ onChange: (e) => setName(e.target.value),
448
+ placeholder: "Your name",
449
+ className: "w-full"
450
+ }
451
+ )
452
+ ] })
453
+ ] }),
454
+ /* @__PURE__ */ jsx8(
455
+ Button2,
456
+ {
457
+ theme: overriddenTheme,
458
+ type: "submit",
459
+ disabled: !editing && (isSubmitting || !subject || !message || !email),
460
+ className: "self-start",
461
+ children: buttonCaption
462
+ }
463
+ )
464
+ ] });
465
+ }
466
+ export {
467
+ SupportForm
468
+ };
469
+ //# sourceMappingURL=support-form.js.map