@tekki-forge/runtime-admin 0.1.0 → 0.3.0
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/admin-dashboard.d.ts +16 -0
- package/dist/admin-dashboard.js +60 -0
- package/dist/admin-dashboard.js.map +1 -0
- package/dist/admin-orgs.d.ts +14 -0
- package/dist/admin-orgs.js +75 -0
- package/dist/admin-orgs.js.map +1 -0
- package/dist/admin-shell.d.ts +55 -0
- package/dist/admin-shell.js +141 -0
- package/dist/admin-shell.js.map +1 -0
- package/dist/admin.js +39 -6
- package/dist/admin.js.map +1 -1
- package/dist/app-pages.d.ts +19 -0
- package/dist/app-pages.js +109 -0
- package/dist/app-pages.js.map +1 -0
- package/dist/auth-pages.js +7 -2
- package/dist/auth-pages.js.map +1 -1
- package/dist/charts.d.ts +98 -2
- package/dist/charts.js +264 -24
- package/dist/charts.js.map +1 -1
- package/dist/context.js +32 -1
- package/dist/context.js.map +1 -1
- package/dist/dashboard.d.ts +63 -8
- package/dist/dashboard.js +180 -36
- package/dist/dashboard.js.map +1 -1
- package/dist/entity-icons.d.ts +3 -0
- package/dist/entity-icons.js +105 -0
- package/dist/entity-icons.js.map +1 -0
- package/dist/i18n/messages/de.js +751 -0
- package/dist/i18n/messages/de.js.map +1 -1
- package/dist/i18n/messages/en.js +751 -0
- package/dist/i18n/messages/en.js.map +1 -1
- package/dist/i18n/messages/es.js +751 -0
- package/dist/i18n/messages/es.js.map +1 -1
- package/dist/i18n/messages/fr.js +751 -0
- package/dist/i18n/messages/fr.js.map +1 -1
- package/dist/i18n/messages/pt.js +751 -0
- package/dist/i18n/messages/pt.js.map +1 -1
- package/dist/index.d.ts +9 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -1
- package/dist/schema-editor.js +2 -1
- package/dist/schema-editor.js.map +1 -1
- package/dist/shell.js +37 -34
- package/dist/shell.js.map +1 -1
- package/dist/site/editor/api.d.ts +74 -0
- package/dist/site/editor/api.js +93 -0
- package/dist/site/editor/api.js.map +1 -0
- package/dist/site/editor/content-page.d.ts +4 -0
- package/dist/site/editor/content-page.js +65 -0
- package/dist/site/editor/content-page.js.map +1 -0
- package/dist/site/editor/inbox-tab.d.ts +4 -0
- package/dist/site/editor/inbox-tab.js +88 -0
- package/dist/site/editor/inbox-tab.js.map +1 -0
- package/dist/site/editor/index.d.ts +1 -0
- package/dist/site/editor/index.js +3 -0
- package/dist/site/editor/index.js.map +1 -0
- package/dist/site/editor/model.d.ts +145 -0
- package/dist/site/editor/model.js +408 -0
- package/dist/site/editor/model.js.map +1 -0
- package/dist/site/editor/page-editor.d.ts +10 -0
- package/dist/site/editor/page-editor.js +257 -0
- package/dist/site/editor/page-editor.js.map +1 -0
- package/dist/site/editor/page-settings.d.ts +9 -0
- package/dist/site/editor/page-settings.js +105 -0
- package/dist/site/editor/page-settings.js.map +1 -0
- package/dist/site/editor/pages-tab.d.ts +5 -0
- package/dist/site/editor/pages-tab.js +108 -0
- package/dist/site/editor/pages-tab.js.map +1 -0
- package/dist/site/editor/posts-tab.d.ts +6 -0
- package/dist/site/editor/posts-tab.js +139 -0
- package/dist/site/editor/posts-tab.js.map +1 -0
- package/dist/site/editor/preview.d.ts +32 -0
- package/dist/site/editor/preview.js +104 -0
- package/dist/site/editor/preview.js.map +1 -0
- package/dist/site/editor/prop-fields.d.ts +53 -0
- package/dist/site/editor/prop-fields.js +233 -0
- package/dist/site/editor/prop-fields.js.map +1 -0
- package/dist/site/editor/section-list.d.ts +14 -0
- package/dist/site/editor/section-list.js +110 -0
- package/dist/site/editor/section-list.js.map +1 -0
- package/dist/site/editor/section-picker.d.ts +12 -0
- package/dist/site/editor/section-picker.js +44 -0
- package/dist/site/editor/section-picker.js.map +1 -0
- package/dist/site/editor/widgets.d.ts +84 -0
- package/dist/site/editor/widgets.js +130 -0
- package/dist/site/editor/widgets.js.map +1 -0
- package/dist/site/format.d.ts +31 -0
- package/dist/site/format.js +106 -0
- package/dist/site/format.js.map +1 -0
- package/dist/site/forms.d.ts +43 -0
- package/dist/site/forms.js +155 -0
- package/dist/site/forms.js.map +1 -0
- package/dist/site/header.d.ts +32 -0
- package/dist/site/header.js +76 -0
- package/dist/site/header.js.map +1 -0
- package/dist/site/icons.d.ts +10 -0
- package/dist/site/icons.js +28 -0
- package/dist/site/icons.js.map +1 -0
- package/dist/site/index.d.ts +11 -0
- package/dist/site/index.js +13 -0
- package/dist/site/index.js.map +1 -0
- package/dist/site/markdown.d.ts +14 -0
- package/dist/site/markdown.js +457 -0
- package/dist/site/markdown.js.map +1 -0
- package/dist/site/page.d.ts +16 -0
- package/dist/site/page.js +65 -0
- package/dist/site/page.js.map +1 -0
- package/dist/site/proxy.d.ts +13 -0
- package/dist/site/proxy.js +43 -0
- package/dist/site/proxy.js.map +1 -0
- package/dist/site/sections.d.ts +43 -0
- package/dist/site/sections.js +560 -0
- package/dist/site/sections.js.map +1 -0
- package/dist/site/server.d.ts +73 -0
- package/dist/site/server.js +235 -0
- package/dist/site/server.js.map +1 -0
- package/dist/site/types.d.ts +137 -0
- package/dist/site/types.js +2 -0
- package/dist/site/types.js.map +1 -0
- package/dist/support.d.ts +29 -0
- package/dist/support.js +165 -0
- package/dist/support.js.map +1 -0
- package/dist/system-config.d.ts +24 -0
- package/dist/system-config.js +37 -0
- package/dist/system-config.js.map +1 -0
- package/dist/system-settings.d.ts +9 -0
- package/dist/system-settings.js +60 -0
- package/dist/system-settings.js.map +1 -0
- package/dist/theme-catalog.d.ts +72 -2
- package/dist/theme-catalog.js +47 -0
- package/dist/theme-catalog.js.map +1 -1
- package/dist/theme-switcher.d.ts +8 -0
- package/dist/theme-switcher.js +39 -4
- package/dist/theme-switcher.js.map +1 -1
- package/dist/theme.js +8 -0
- package/dist/theme.js.map +1 -1
- package/dist/transactions.d.ts +4 -0
- package/dist/transactions.js +60 -0
- package/dist/transactions.js.map +1 -0
- package/dist/ui.js +3 -22
- package/dist/ui.js.map +1 -1
- package/package.json +9 -5
- package/styles.css +1089 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
declare const WIDTHS: {
|
|
3
|
+
readonly md: "max-w-md";
|
|
4
|
+
readonly lg: "max-w-2xl";
|
|
5
|
+
readonly xl: "max-w-5xl";
|
|
6
|
+
};
|
|
7
|
+
/** Modal dialog: traps focus, Escape closes, focus goes back where it was. */
|
|
8
|
+
export declare function EditorDialog({ open, onClose, title, description, children, footer, size }: {
|
|
9
|
+
open: boolean;
|
|
10
|
+
onClose: () => void;
|
|
11
|
+
title: string;
|
|
12
|
+
description?: string;
|
|
13
|
+
children: ReactNode;
|
|
14
|
+
footer?: ReactNode;
|
|
15
|
+
size?: keyof typeof WIDTHS;
|
|
16
|
+
}): import("react").JSX.Element | null;
|
|
17
|
+
/** Side panel over the page (right on wide screens, full width on phones). */
|
|
18
|
+
export declare function Drawer({ open, onClose, title, children, footer }: {
|
|
19
|
+
open: boolean;
|
|
20
|
+
onClose: () => void;
|
|
21
|
+
title: string;
|
|
22
|
+
children: ReactNode;
|
|
23
|
+
footer?: ReactNode;
|
|
24
|
+
}): import("react").JSX.Element | null;
|
|
25
|
+
export interface TabItem<K extends string> {
|
|
26
|
+
key: K;
|
|
27
|
+
label: string;
|
|
28
|
+
icon?: ReactNode;
|
|
29
|
+
badge?: ReactNode;
|
|
30
|
+
}
|
|
31
|
+
/** WAI-ARIA tabs: arrows / Home / End move between tabs (automatic activation). */
|
|
32
|
+
export declare function Tabs<K extends string>({ tabs, value, onChange, label, idPrefix, size }: {
|
|
33
|
+
tabs: TabItem<K>[];
|
|
34
|
+
value: K;
|
|
35
|
+
onChange: (k: K) => void;
|
|
36
|
+
label: string;
|
|
37
|
+
idPrefix: string;
|
|
38
|
+
size?: "sm" | "md";
|
|
39
|
+
}): import("react").JSX.Element;
|
|
40
|
+
export declare function TabPanel({ idPrefix, tab, children, className }: {
|
|
41
|
+
idPrefix: string;
|
|
42
|
+
tab: string;
|
|
43
|
+
children: ReactNode;
|
|
44
|
+
className?: string;
|
|
45
|
+
}): import("react").JSX.Element;
|
|
46
|
+
export declare function Switch({ id, checked, onChange, label, disabled }: {
|
|
47
|
+
id?: string;
|
|
48
|
+
checked: boolean;
|
|
49
|
+
onChange: (v: boolean) => void;
|
|
50
|
+
label?: string;
|
|
51
|
+
disabled?: boolean;
|
|
52
|
+
}): import("react").JSX.Element;
|
|
53
|
+
/** A small coloured pill (status, origin, conflict). */
|
|
54
|
+
export declare function Pill({ tone, children, title }: {
|
|
55
|
+
tone: "success" | "muted" | "warning" | "danger" | "info";
|
|
56
|
+
children: ReactNode;
|
|
57
|
+
title?: string;
|
|
58
|
+
}): import("react").JSX.Element;
|
|
59
|
+
export type SaveState = "idle" | "dirty" | "saving" | "saved" | "error" | "invalid";
|
|
60
|
+
/** "Saving…" / "Saved" / "Couldn't save" next to the editor's title; announced politely. */
|
|
61
|
+
export declare function SaveIndicator({ state, message }: {
|
|
62
|
+
state: SaveState;
|
|
63
|
+
message?: string;
|
|
64
|
+
}): import("react").JSX.Element;
|
|
65
|
+
/** A visually hidden polite live region; `announce(text)` reads text out (reorders, adds, removals). */
|
|
66
|
+
export declare function useAnnouncer(): [ReactNode, (text: string) => void];
|
|
67
|
+
/** Confirmation in an accessible dialog (focus starts on Cancel, the safe choice). */
|
|
68
|
+
export declare function Confirm({ open, title, message, confirmLabel, danger, loading, onConfirm, onClose }: {
|
|
69
|
+
open: boolean;
|
|
70
|
+
title: string;
|
|
71
|
+
message: ReactNode;
|
|
72
|
+
confirmLabel: string;
|
|
73
|
+
danger?: boolean;
|
|
74
|
+
loading?: boolean;
|
|
75
|
+
onConfirm: () => void;
|
|
76
|
+
onClose: () => void;
|
|
77
|
+
}): import("react").JSX.Element;
|
|
78
|
+
/** Text with a live "12 / 160" counter (SEO fields). */
|
|
79
|
+
export declare function Counter({ value, max, recommended }: {
|
|
80
|
+
value: string;
|
|
81
|
+
max: number;
|
|
82
|
+
recommended?: number;
|
|
83
|
+
}): import("react").JSX.Element;
|
|
84
|
+
export {};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
/*
|
|
4
|
+
* Small building blocks of the website editor: an accessible dialog/drawer (focus trap, focus return), a keyboard
|
|
5
|
+
* tablist, a switch, status badges, the save indicator and a polite live region for announcements.
|
|
6
|
+
*/
|
|
7
|
+
import { CircleAlert, CircleCheck, CloudUpload, LoaderCircle, X } from "lucide-react";
|
|
8
|
+
import { useCallback, useEffect, useId, useRef, useState } from "react";
|
|
9
|
+
import { useT } from "../../context.js";
|
|
10
|
+
import { cn } from "../../ui.js";
|
|
11
|
+
const FOCUSABLE = 'a[href], button:not([disabled]), input:not([disabled]):not([type="hidden"]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
12
|
+
function useFocusTrap(open, onClose) {
|
|
13
|
+
const ref = useRef(null);
|
|
14
|
+
const closeRef = useRef(onClose);
|
|
15
|
+
closeRef.current = onClose;
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
if (!open)
|
|
18
|
+
return;
|
|
19
|
+
const previous = document.activeElement;
|
|
20
|
+
const node = ref.current;
|
|
21
|
+
// Focus the first field (or the dialog itself) once it is on screen.
|
|
22
|
+
const first = node?.querySelector("[data-autofocus]") ?? node?.querySelector("input, select, textarea") ?? node;
|
|
23
|
+
first?.focus();
|
|
24
|
+
const onKey = (e) => {
|
|
25
|
+
if (e.key === "Escape") {
|
|
26
|
+
// Let an inner control (a picked-up row) handle Escape first.
|
|
27
|
+
if (e.defaultPrevented)
|
|
28
|
+
return;
|
|
29
|
+
e.stopPropagation();
|
|
30
|
+
closeRef.current();
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (e.key !== "Tab" || !node)
|
|
34
|
+
return;
|
|
35
|
+
const items = [...node.querySelectorAll(FOCUSABLE)].filter((el) => el.offsetParent !== null || el === document.activeElement);
|
|
36
|
+
if (!items.length)
|
|
37
|
+
return;
|
|
38
|
+
const firstItem = items[0];
|
|
39
|
+
const last = items[items.length - 1];
|
|
40
|
+
if (e.shiftKey && (document.activeElement === firstItem || document.activeElement === node)) {
|
|
41
|
+
e.preventDefault();
|
|
42
|
+
last.focus();
|
|
43
|
+
}
|
|
44
|
+
else if (!e.shiftKey && document.activeElement === last) {
|
|
45
|
+
e.preventDefault();
|
|
46
|
+
firstItem.focus();
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
node?.addEventListener("keydown", onKey);
|
|
50
|
+
return () => {
|
|
51
|
+
node?.removeEventListener("keydown", onKey);
|
|
52
|
+
if (previous && document.contains(previous))
|
|
53
|
+
previous.focus();
|
|
54
|
+
};
|
|
55
|
+
}, [open]);
|
|
56
|
+
return ref;
|
|
57
|
+
}
|
|
58
|
+
const WIDTHS = { md: "max-w-md", lg: "max-w-2xl", xl: "max-w-5xl" };
|
|
59
|
+
/** Modal dialog: traps focus, Escape closes, focus goes back where it was. */
|
|
60
|
+
export function EditorDialog({ open, onClose, title, description, children, footer, size = "md" }) {
|
|
61
|
+
const t = useT();
|
|
62
|
+
const ref = useFocusTrap(open, onClose);
|
|
63
|
+
const titleId = useId();
|
|
64
|
+
if (!open)
|
|
65
|
+
return null;
|
|
66
|
+
return (_jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center p-4", children: [_jsx("div", { className: "forge-overlay absolute inset-0", onClick: onClose, "aria-hidden": true }), _jsxs("div", { ref: ref, role: "dialog", "aria-modal": "true", "aria-labelledby": titleId, tabIndex: -1, className: cn("forge-dialog forge-pop relative flex max-h-[90vh] w-full flex-col outline-none", WIDTHS[size]), children: [_jsxs("div", { className: "flex items-start justify-between gap-4 border-b border-line px-5 py-3", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("h2", { id: titleId, className: "text-sm font-semibold text-ink", children: title }), description && _jsx("p", { className: "mt-0.5 text-xs text-ink-muted", children: description })] }), _jsx("button", { type: "button", onClick: onClose, className: "forge-btn forge-btn-ghost p-1", "aria-label": t("common.close"), children: _jsx(X, { className: "size-4", "aria-hidden": true }) })] }), _jsx("div", { className: "min-h-0 overflow-y-auto px-5 py-4 text-sm text-ink", children: children }), footer && _jsx("div", { className: "flex flex-wrap justify-end gap-2 border-t border-line px-5 py-3", children: footer })] })] }));
|
|
67
|
+
}
|
|
68
|
+
/** Side panel over the page (right on wide screens, full width on phones). */
|
|
69
|
+
export function Drawer({ open, onClose, title, children, footer }) {
|
|
70
|
+
const t = useT();
|
|
71
|
+
const ref = useFocusTrap(open, onClose);
|
|
72
|
+
const titleId = useId();
|
|
73
|
+
if (!open)
|
|
74
|
+
return null;
|
|
75
|
+
return (_jsxs("div", { className: "fixed inset-0 z-50 flex justify-end", children: [_jsx("div", { className: "forge-overlay absolute inset-0", onClick: onClose, "aria-hidden": true }), _jsxs("div", { ref: ref, role: "dialog", "aria-modal": "true", "aria-labelledby": titleId, tabIndex: -1, className: "forge-dialog forge-drawer relative flex h-full w-full max-w-md flex-col rounded-none outline-none sm:rounded-l-[var(--forge-radius)]", children: [_jsxs("div", { className: "flex items-center justify-between gap-4 border-b border-line px-5 py-3", children: [_jsx("h2", { id: titleId, className: "text-sm font-semibold text-ink", children: title }), _jsx("button", { type: "button", onClick: onClose, className: "forge-btn forge-btn-ghost p-1", "aria-label": t("common.close"), children: _jsx(X, { className: "size-4", "aria-hidden": true }) })] }), _jsx("div", { className: "min-h-0 flex-1 overflow-y-auto px-5 py-4 text-sm text-ink", children: children }), footer && _jsx("div", { className: "flex flex-wrap justify-end gap-2 border-t border-line px-5 py-3", children: footer })] })] }));
|
|
76
|
+
}
|
|
77
|
+
/** WAI-ARIA tabs: arrows / Home / End move between tabs (automatic activation). */
|
|
78
|
+
export function Tabs({ tabs, value, onChange, label, idPrefix, size = "md" }) {
|
|
79
|
+
const refs = useRef([]);
|
|
80
|
+
const onKey = (e, i) => {
|
|
81
|
+
const next = e.key === "ArrowRight" ? (i + 1) % tabs.length : e.key === "ArrowLeft" ? (i - 1 + tabs.length) % tabs.length : e.key === "Home" ? 0 : e.key === "End" ? tabs.length - 1 : -1;
|
|
82
|
+
if (next < 0)
|
|
83
|
+
return;
|
|
84
|
+
e.preventDefault();
|
|
85
|
+
onChange(tabs[next].key);
|
|
86
|
+
refs.current[next]?.focus();
|
|
87
|
+
};
|
|
88
|
+
return (_jsx("div", { role: "tablist", "aria-label": label, className: cn("forge-input flex max-w-full items-center gap-0.5 overflow-x-auto p-0.5", size === "sm" ? "h-8" : "h-9"), children: tabs.map((tab, i) => (_jsxs("button", { ref: (el) => {
|
|
89
|
+
refs.current[i] = el;
|
|
90
|
+
}, id: `${idPrefix}-tab-${tab.key}`, type: "button", role: "tab", "aria-selected": value === tab.key, "aria-controls": `${idPrefix}-panel-${tab.key}`, tabIndex: value === tab.key ? 0 : -1, onClick: () => onChange(tab.key), onKeyDown: (e) => onKey(e, i), className: cn("forge-focusable flex h-full shrink-0 items-center gap-1.5 rounded px-3 whitespace-nowrap", size === "sm" ? "text-xs" : "text-sm", value === tab.key ? "bg-selected text-ink" : "text-ink-muted hover:text-ink"), children: [tab.icon, tab.label, tab.badge] }, tab.key))) }));
|
|
91
|
+
}
|
|
92
|
+
export function TabPanel({ idPrefix, tab, children, className }) {
|
|
93
|
+
return (_jsx("div", { role: "tabpanel", id: `${idPrefix}-panel-${tab}`, "aria-labelledby": `${idPrefix}-tab-${tab}`, className: className, children: children }));
|
|
94
|
+
}
|
|
95
|
+
export function Switch({ id, checked, onChange, label, disabled }) {
|
|
96
|
+
return (_jsx("button", { id: id, type: "button", role: "switch", "aria-checked": checked, "aria-label": label, disabled: disabled, onClick: () => onChange(!checked), className: "forge-switch relative inline-flex h-6 w-11 shrink-0 items-center disabled:opacity-50", children: _jsx("span", { className: cn("forge-switch-thumb inline-block size-5 transition-transform motion-reduce:transition-none", checked ? "translate-x-5" : "translate-x-0.5") }) }));
|
|
97
|
+
}
|
|
98
|
+
/** A small coloured pill (status, origin, conflict). */
|
|
99
|
+
export function Pill({ tone, children, title }) {
|
|
100
|
+
const color = { success: "var(--forge-success)", muted: "var(--forge-ink-muted)", warning: "var(--forge-accent)", danger: "var(--forge-danger)", info: "var(--forge-info)" }[tone];
|
|
101
|
+
return (_jsx("span", { title: title, className: "forge-badge inline-flex shrink-0 items-center gap-1 px-2 py-0.5 text-xs font-medium whitespace-nowrap", style: { "--tone": color }, children: children }));
|
|
102
|
+
}
|
|
103
|
+
/** "Saving…" / "Saved" / "Couldn't save" next to the editor's title; announced politely. */
|
|
104
|
+
export function SaveIndicator({ state, message }) {
|
|
105
|
+
const t = useT();
|
|
106
|
+
const content = state === "saving" ? (_jsxs(_Fragment, { children: [_jsx(LoaderCircle, { className: "size-3.5 animate-spin motion-reduce:animate-none", "aria-hidden": true }), " ", t("siteAdmin.save.saving")] })) : state === "saved" ? (_jsxs(_Fragment, { children: [_jsx(CircleCheck, { className: "size-3.5 text-[var(--forge-success)]", "aria-hidden": true }), " ", t("siteAdmin.save.saved")] })) : state === "error" ? (_jsxs(_Fragment, { children: [_jsx(CircleAlert, { className: "size-3.5 text-danger", "aria-hidden": true }), " ", message ?? t("siteAdmin.save.error")] })) : state === "invalid" ? (_jsxs(_Fragment, { children: [_jsx(CircleAlert, { className: "size-3.5 text-danger", "aria-hidden": true }), " ", t("siteAdmin.save.invalid")] })) : state === "dirty" ? (_jsxs(_Fragment, { children: [_jsx(CloudUpload, { className: "size-3.5", "aria-hidden": true }), " ", t("siteAdmin.save.pending")] })) : null;
|
|
107
|
+
return (_jsx("span", { role: "status", "aria-live": "polite", className: "inline-flex min-h-5 items-center gap-1.5 text-xs text-ink-muted", children: content }));
|
|
108
|
+
}
|
|
109
|
+
/** A visually hidden polite live region; `announce(text)` reads text out (reorders, adds, removals). */
|
|
110
|
+
export function useAnnouncer() {
|
|
111
|
+
const [text, setText] = useState("");
|
|
112
|
+
const announce = useCallback((next) => {
|
|
113
|
+
// Clear first so the same sentence twice is still announced.
|
|
114
|
+
setText("");
|
|
115
|
+
requestAnimationFrame(() => setText(next));
|
|
116
|
+
}, []);
|
|
117
|
+
const region = (_jsx("div", { "aria-live": "assertive", "aria-atomic": "true", className: "sr-only", children: text }));
|
|
118
|
+
return [region, announce];
|
|
119
|
+
}
|
|
120
|
+
/** Confirmation in an accessible dialog (focus starts on Cancel, the safe choice). */
|
|
121
|
+
export function Confirm({ open, title, message, confirmLabel, danger, loading, onConfirm, onClose }) {
|
|
122
|
+
const t = useT();
|
|
123
|
+
return (_jsx(EditorDialog, { open: open, onClose: onClose, title: title, footer: _jsxs(_Fragment, { children: [_jsx("button", { type: "button", "data-autofocus": true, className: "forge-btn forge-btn-secondary inline-flex h-[var(--forge-control-h)] items-center px-3.5 text-sm font-medium", onClick: onClose, children: t("common.cancel") }), _jsxs("button", { type: "button", disabled: loading, className: cn("forge-btn inline-flex h-[var(--forge-control-h)] items-center gap-1.5 px-3.5 text-sm font-medium", danger ? "forge-btn-danger" : "forge-btn-primary"), onClick: onConfirm, children: [loading && _jsx(LoaderCircle, { className: "size-3.5 animate-spin motion-reduce:animate-none", "aria-hidden": true }), confirmLabel] })] }), children: message }));
|
|
124
|
+
}
|
|
125
|
+
/** Text with a live "12 / 160" counter (SEO fields). */
|
|
126
|
+
export function Counter({ value, max, recommended }) {
|
|
127
|
+
const over = value.length > (recommended ?? max);
|
|
128
|
+
return _jsx("span", { className: cn("text-xs tabular-nums", over ? "text-danger" : "text-ink-subtle"), children: `${value.length} / ${recommended ?? max}` });
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=widgets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"widgets.js","sourceRoot":"","sources":["../../../src/site/editor/widgets.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { EntityIR, FieldIR } from "@tekki-forge/compiler";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
import { type Messages, type TranslateVars } from "../i18n/locale.js";
|
|
4
|
+
import type { SiteApp } from "./types.js";
|
|
5
|
+
export type SiteT = (key: string, vars?: TranslateVars) => string;
|
|
6
|
+
/** The app with its schema texts in `locale` (entity and field labels), and `t` for the website's own words. */
|
|
7
|
+
export declare function siteTranslator<A extends SiteApp>(baseApp: A, locale?: string, messages?: Record<string, Messages>): {
|
|
8
|
+
app: A;
|
|
9
|
+
lang: string;
|
|
10
|
+
t: SiteT;
|
|
11
|
+
};
|
|
12
|
+
/** A date (ISO string) in the visitor's language: "12 March 2026". Date-only values are not shifted by time zones. */
|
|
13
|
+
export declare function formatDate(value: unknown, locale: string, withTime?: boolean): string;
|
|
14
|
+
/** The field of an entity by name or API name ("owner" / "ownerId"). */
|
|
15
|
+
export declare const entityField: (entity: EntityIR, name: string) => FieldIR | undefined;
|
|
16
|
+
/** The value of a field in a row returned by the resource API (keyed by apiName, relations by name). */
|
|
17
|
+
export declare const rowValue: (row: Record<string, unknown>, f: FieldIR | undefined, name: string) => unknown;
|
|
18
|
+
/** An uploaded file or image as the API presents it ({ url, name }), or a plain URL. */
|
|
19
|
+
export declare function fileOf(value: unknown): {
|
|
20
|
+
url: string;
|
|
21
|
+
name: string;
|
|
22
|
+
} | null;
|
|
23
|
+
/** One value as text, formatted by the field's kind: money, numbers, dates, yes/no, enum and relation labels. */
|
|
24
|
+
export declare function formatFieldText(f: FieldIR | undefined, value: unknown, locale: string, t: SiteT): string;
|
|
25
|
+
/** One value as content: links for URLs, e-mails and files, a swatch for colours, text otherwise. */
|
|
26
|
+
export declare function SiteFieldValue({ field, value, locale, t }: {
|
|
27
|
+
field: FieldIR | undefined;
|
|
28
|
+
value: unknown;
|
|
29
|
+
locale: string;
|
|
30
|
+
t: SiteT;
|
|
31
|
+
}): ReactNode;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { localizeSchema } from "@tekki-forge/compiler/i18n";
|
|
3
|
+
import { validHref, validImage } from "@tekki-forge/compiler/site";
|
|
4
|
+
import { uiCatalogs } from "../i18n/catalog.js";
|
|
5
|
+
import { translate } from "../i18n/locale.js";
|
|
6
|
+
/** The app with its schema texts in `locale` (entity and field labels), and `t` for the website's own words. */
|
|
7
|
+
export function siteTranslator(baseApp, locale, messages = {}) {
|
|
8
|
+
const lang = locale ?? baseApp.i18n?.defaultLocale ?? "en";
|
|
9
|
+
const app = lang === (baseApp.i18n?.defaultLocale ?? "en") || !messages[lang] ? baseApp : { ...baseApp, ...localizeSchema(baseApp, {}, messages[lang]).app };
|
|
10
|
+
return { app, lang, t: (key, vars) => translate([...uiCatalogs(lang), messages[lang] ?? {}], lang, key, vars) };
|
|
11
|
+
}
|
|
12
|
+
/** A date (ISO string) in the visitor's language: "12 March 2026". Date-only values are not shifted by time zones. */
|
|
13
|
+
export function formatDate(value, locale, withTime = false) {
|
|
14
|
+
if (typeof value !== "string" && !(value instanceof Date))
|
|
15
|
+
return "";
|
|
16
|
+
const s = String(value instanceof Date ? value.toISOString() : value);
|
|
17
|
+
const dateOnly = /^\d{4}-\d{2}-\d{2}$/.test(s);
|
|
18
|
+
const d = new Date(dateOnly ? `${s}T00:00:00Z` : s);
|
|
19
|
+
if (Number.isNaN(d.getTime()))
|
|
20
|
+
return s;
|
|
21
|
+
try {
|
|
22
|
+
return new Intl.DateTimeFormat(locale, { dateStyle: "medium", ...(withTime && !dateOnly ? { timeStyle: "short" } : {}), ...(dateOnly || !withTime ? { timeZone: "UTC" } : {}) }).format(d);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return s.slice(0, 10);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/** The field of an entity by name or API name ("owner" / "ownerId"). */
|
|
29
|
+
export const entityField = (entity, name) => entity.fields.find((f) => f.name === name || f.apiName === name);
|
|
30
|
+
/** The value of a field in a row returned by the resource API (keyed by apiName, relations by name). */
|
|
31
|
+
export const rowValue = (row, f, name) => (f ? (row[f.name] ?? row[f.apiName]) : row[name]);
|
|
32
|
+
/** An uploaded file or image as the API presents it ({ url, name }), or a plain URL. */
|
|
33
|
+
export function fileOf(value) {
|
|
34
|
+
if (typeof value === "string")
|
|
35
|
+
return validImage(value) ? { url: value, name: value.split("/").pop() ?? value } : null;
|
|
36
|
+
if (value && typeof value === "object" && typeof value.url === "string") {
|
|
37
|
+
const url = value.url;
|
|
38
|
+
return validImage(url) ? { url, name: String(value.name ?? url.split("/").pop() ?? "") } : null;
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
/** One value as text, formatted by the field's kind: money, numbers, dates, yes/no, enum and relation labels. */
|
|
43
|
+
export function formatFieldText(f, value, locale, t) {
|
|
44
|
+
if (value === null || value === undefined || value === "")
|
|
45
|
+
return "";
|
|
46
|
+
if (!f)
|
|
47
|
+
return typeof value === "object" ? "" : String(value);
|
|
48
|
+
try {
|
|
49
|
+
switch (f.kind) {
|
|
50
|
+
case "money":
|
|
51
|
+
return new Intl.NumberFormat(locale, { style: "currency", currency: f.currency ?? "USD" }).format(Number(value));
|
|
52
|
+
case "integer":
|
|
53
|
+
case "decimal":
|
|
54
|
+
return new Intl.NumberFormat(locale, f.scale !== undefined ? { maximumFractionDigits: f.scale } : {}).format(Number(value));
|
|
55
|
+
case "percentage":
|
|
56
|
+
return new Intl.NumberFormat(locale, { style: "percent", maximumFractionDigits: 2 }).format(Number(value) / 100);
|
|
57
|
+
case "boolean":
|
|
58
|
+
return value ? t("site.value.yes") : t("site.value.no");
|
|
59
|
+
case "date":
|
|
60
|
+
return formatDate(value, locale);
|
|
61
|
+
case "datetime":
|
|
62
|
+
return formatDate(value, locale, true);
|
|
63
|
+
case "enum":
|
|
64
|
+
return f.enumValues?.find((v) => v.value === value)?.label ?? String(value);
|
|
65
|
+
case "relation": {
|
|
66
|
+
const label = (v) => (v && typeof v === "object" ? String(v.label ?? "") : "");
|
|
67
|
+
return Array.isArray(value) ? value.map(label).filter(Boolean).join(", ") : label(value);
|
|
68
|
+
}
|
|
69
|
+
case "location": {
|
|
70
|
+
const { lat, lng } = value;
|
|
71
|
+
return typeof lat === "number" && typeof lng === "number" ? `${lat.toFixed(4)}, ${lng.toFixed(4)}` : "";
|
|
72
|
+
}
|
|
73
|
+
case "file":
|
|
74
|
+
case "image":
|
|
75
|
+
return fileOf(value)?.name ?? "";
|
|
76
|
+
case "json":
|
|
77
|
+
return "";
|
|
78
|
+
default:
|
|
79
|
+
return typeof value === "object" ? "" : String(value);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return String(value);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/** One value as content: links for URLs, e-mails and files, a swatch for colours, text otherwise. */
|
|
87
|
+
export function SiteFieldValue({ field, value, locale, t }) {
|
|
88
|
+
if (value === null || value === undefined || value === "")
|
|
89
|
+
return null;
|
|
90
|
+
if (field?.kind === "url" && typeof value === "string" && /^https?:\/\//i.test(value) && validHref(value)) {
|
|
91
|
+
return (_jsx("a", { href: value, rel: "noopener", className: "forge-focusable text-link underline-offset-2 hover:underline", children: value.replace(/^https?:\/\/(www\.)?/i, "").replace(/\/$/, "") }));
|
|
92
|
+
}
|
|
93
|
+
if (field?.kind === "email" && typeof value === "string" && validHref(`mailto:${value}`)) {
|
|
94
|
+
return (_jsx("a", { href: `mailto:${value}`, className: "forge-focusable text-link underline-offset-2 hover:underline", children: value }));
|
|
95
|
+
}
|
|
96
|
+
if (field?.kind === "file") {
|
|
97
|
+
const file = fileOf(value);
|
|
98
|
+
return file ? (_jsx("a", { href: file.url, className: "forge-focusable text-link underline-offset-2 hover:underline", children: file.name })) : null;
|
|
99
|
+
}
|
|
100
|
+
if (field?.kind === "color" && typeof value === "string" && /^#[0-9a-f]{3,8}$/i.test(value)) {
|
|
101
|
+
return (_jsxs("span", { className: "inline-flex items-center gap-1.5", children: [_jsx("span", { className: "size-3 rounded-full ring-1 ring-line", style: { background: value }, "aria-hidden": true }), value] }));
|
|
102
|
+
}
|
|
103
|
+
const text = formatFieldText(field, value, locale, t);
|
|
104
|
+
return text || null;
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/site/format.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export interface SiteFormLabels {
|
|
2
|
+
name: string;
|
|
3
|
+
email: string;
|
|
4
|
+
company: string;
|
|
5
|
+
message: string;
|
|
6
|
+
optional: string;
|
|
7
|
+
submit: string;
|
|
8
|
+
sending: string;
|
|
9
|
+
success: string;
|
|
10
|
+
required: string;
|
|
11
|
+
invalidEmail: string;
|
|
12
|
+
tooLong: string;
|
|
13
|
+
messageTooShort: string;
|
|
14
|
+
error: string;
|
|
15
|
+
rateLimited: string;
|
|
16
|
+
/** Label of the honeypot (never seen by people). */
|
|
17
|
+
honeypot: string;
|
|
18
|
+
}
|
|
19
|
+
export interface ContactFormProps {
|
|
20
|
+
labels: SiteFormLabels;
|
|
21
|
+
/** Ask for a company name. */
|
|
22
|
+
company?: boolean;
|
|
23
|
+
buttonLabel?: string;
|
|
24
|
+
successMessage?: string;
|
|
25
|
+
/** Path of the page the form is on (stored with the message). */
|
|
26
|
+
page?: string;
|
|
27
|
+
endpoint?: string;
|
|
28
|
+
/** Editor preview: shown but not sendable. */
|
|
29
|
+
disabled?: boolean;
|
|
30
|
+
}
|
|
31
|
+
export declare function ContactForm({ labels, company, buttonLabel, successMessage, page, endpoint, disabled }: ContactFormProps): import("react").JSX.Element;
|
|
32
|
+
export interface NewsletterFormProps {
|
|
33
|
+
labels: Pick<SiteFormLabels, "email" | "submit" | "sending" | "success" | "required" | "invalidEmail" | "error" | "rateLimited" | "honeypot">;
|
|
34
|
+
buttonLabel?: string;
|
|
35
|
+
successMessage?: string;
|
|
36
|
+
/** Where the address was collected (stored with it). */
|
|
37
|
+
source?: string;
|
|
38
|
+
/** "row": field and button side by side (from medium widths). */
|
|
39
|
+
layout?: "row" | "stacked";
|
|
40
|
+
endpoint?: string;
|
|
41
|
+
disabled?: boolean;
|
|
42
|
+
}
|
|
43
|
+
export declare function NewsletterForm({ labels, buttonLabel, successMessage, source, layout, endpoint, disabled }: NewsletterFormProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { CircleCheck, LoaderCircle } from "lucide-react";
|
|
4
|
+
import { useEffect, useId, useRef, useState } from "react";
|
|
5
|
+
const EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
6
|
+
async function post(endpoint, body) {
|
|
7
|
+
try {
|
|
8
|
+
const res = await fetch(endpoint, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body), credentials: "same-origin" });
|
|
9
|
+
if (res.ok)
|
|
10
|
+
return { ok: true };
|
|
11
|
+
const data = (await res.json().catch(() => ({})));
|
|
12
|
+
return { ok: false, status: res.status, issues: Array.isArray(data.issues) ? data.issues : [] };
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return { ok: false, status: 0, issues: [] };
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/** The honeypot: off-screen, out of the tab order and hidden from screen readers. Bots fill every field. */
|
|
19
|
+
function Honeypot({ label, value, onChange }) {
|
|
20
|
+
const id = useId();
|
|
21
|
+
return (_jsxs("div", { "aria-hidden": "true", className: "forge-site-honeypot", children: [_jsx("label", { htmlFor: id, children: label }), _jsx("input", { id: id, name: "website", type: "text", tabIndex: -1, autoComplete: "off", value: value, onChange: (e) => onChange(e.target.value) })] }));
|
|
22
|
+
}
|
|
23
|
+
/** When the form appeared (set after hydration so server and client markup match). */
|
|
24
|
+
function useStartedAt() {
|
|
25
|
+
const startedAt = useRef(0);
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
startedAt.current = Date.now();
|
|
28
|
+
}, []);
|
|
29
|
+
return startedAt;
|
|
30
|
+
}
|
|
31
|
+
function Field({ id, label, hint, error, children }) {
|
|
32
|
+
return (_jsxs("div", { className: "grid gap-1.5", children: [_jsxs("label", { htmlFor: id, className: "text-sm font-medium text-ink", children: [label, hint && _jsx("span", { className: "ml-1.5 font-normal text-ink-muted", children: hint })] }), children, error && (_jsx("p", { id: `${id}-error`, className: "forge-site-field-error text-sm", children: error }))] }));
|
|
33
|
+
}
|
|
34
|
+
function Success({ text }) {
|
|
35
|
+
return (_jsxs("span", { className: "flex items-start gap-3", children: [_jsx(CircleCheck, { className: "forge-site-success-icon mt-0.5 size-5 shrink-0", "aria-hidden": true }), _jsx("span", { children: text })] }));
|
|
36
|
+
}
|
|
37
|
+
export function ContactForm({ labels, company = false, buttonLabel, successMessage, page, endpoint = "/api/site/contact", disabled = false }) {
|
|
38
|
+
const id = useId();
|
|
39
|
+
const startedAt = useStartedAt();
|
|
40
|
+
const [values, setValues] = useState({ name: "", email: "", company: "", message: "" });
|
|
41
|
+
const [website, setWebsite] = useState("");
|
|
42
|
+
const [errors, setErrors] = useState({});
|
|
43
|
+
const [tried, setTried] = useState(false);
|
|
44
|
+
const [state, setState] = useState("idle");
|
|
45
|
+
const [failure, setFailure] = useState(null);
|
|
46
|
+
const form = useRef(null);
|
|
47
|
+
const check = (v) => {
|
|
48
|
+
const e = {};
|
|
49
|
+
if (!v.name.trim())
|
|
50
|
+
e.name = labels.required;
|
|
51
|
+
else if (v.name.length > 100)
|
|
52
|
+
e.name = labels.tooLong.replace("{max}", "100");
|
|
53
|
+
if (!v.email.trim())
|
|
54
|
+
e.email = labels.required;
|
|
55
|
+
else if (!EMAIL.test(v.email.trim()) || v.email.length > 200)
|
|
56
|
+
e.email = labels.invalidEmail;
|
|
57
|
+
if (company && v.company.length > 100)
|
|
58
|
+
e.company = labels.tooLong.replace("{max}", "100");
|
|
59
|
+
if (!v.message.trim())
|
|
60
|
+
e.message = labels.required;
|
|
61
|
+
else if (v.message.trim().length < 10)
|
|
62
|
+
e.message = labels.messageTooShort.replace("{min}", "10");
|
|
63
|
+
else if (v.message.length > 5000)
|
|
64
|
+
e.message = labels.tooLong.replace("{max}", "5000");
|
|
65
|
+
return e;
|
|
66
|
+
};
|
|
67
|
+
const set = (k) => (e) => {
|
|
68
|
+
const next = { ...values, [k]: e.target.value };
|
|
69
|
+
setValues(next);
|
|
70
|
+
if (tried)
|
|
71
|
+
setErrors(check(next));
|
|
72
|
+
};
|
|
73
|
+
async function submit(e) {
|
|
74
|
+
e.preventDefault();
|
|
75
|
+
if (disabled || state === "sending")
|
|
76
|
+
return;
|
|
77
|
+
setTried(true);
|
|
78
|
+
setFailure(null);
|
|
79
|
+
const found = check(values);
|
|
80
|
+
setErrors(found);
|
|
81
|
+
const first = ["name", "email", "company", "message"].find((k) => found[k]);
|
|
82
|
+
if (first) {
|
|
83
|
+
form.current?.querySelector(`#${CSS.escape(`${id}-${first}`)}`)?.focus();
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
setState("sending");
|
|
87
|
+
const result = await post(endpoint, {
|
|
88
|
+
name: values.name.trim(),
|
|
89
|
+
email: values.email.trim(),
|
|
90
|
+
...(company && values.company.trim() ? { company: values.company.trim() } : {}),
|
|
91
|
+
message: values.message.trim(),
|
|
92
|
+
...(page ? { page } : {}),
|
|
93
|
+
website,
|
|
94
|
+
startedAt: startedAt.current || Date.now(),
|
|
95
|
+
});
|
|
96
|
+
if (result.ok) {
|
|
97
|
+
setState("done");
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
setState("idle");
|
|
101
|
+
const fieldErrors = {};
|
|
102
|
+
for (const issue of result.issues) {
|
|
103
|
+
const key = issue.path.split(".")[0];
|
|
104
|
+
if (["name", "email", "company", "message"].includes(key))
|
|
105
|
+
fieldErrors[key] = issue.message;
|
|
106
|
+
}
|
|
107
|
+
if (Object.keys(fieldErrors).length)
|
|
108
|
+
setErrors(fieldErrors);
|
|
109
|
+
else
|
|
110
|
+
setFailure(result.status === 429 ? labels.rateLimited : labels.error);
|
|
111
|
+
}
|
|
112
|
+
const done = state === "done";
|
|
113
|
+
const describedBy = (k) => (errors[k] ? `${id}-${k}-error` : undefined);
|
|
114
|
+
return (_jsxs("div", { className: "forge-site-form", children: [!done && (_jsxs("form", { ref: form, noValidate: true, onSubmit: submit, className: "grid gap-5", "aria-busy": state === "sending", children: [_jsxs("div", { className: "grid gap-5 @xl:grid-cols-2", children: [_jsx(Field, { id: `${id}-name`, label: labels.name, error: errors.name, children: _jsx("input", { id: `${id}-name`, name: "name", autoComplete: "name", required: true, maxLength: 100, value: values.name, onChange: set("name"), "aria-invalid": !!errors.name, "aria-describedby": describedBy("name"), className: "forge-input h-11 w-full px-3.5 text-base" }) }), _jsx(Field, { id: `${id}-email`, label: labels.email, error: errors.email, children: _jsx("input", { id: `${id}-email`, name: "email", type: "email", autoComplete: "email", inputMode: "email", required: true, maxLength: 200, value: values.email, onChange: set("email"), "aria-invalid": !!errors.email, "aria-describedby": describedBy("email"), className: "forge-input h-11 w-full px-3.5 text-base" }) })] }), company && (_jsx(Field, { id: `${id}-company`, label: labels.company, hint: labels.optional, error: errors.company, children: _jsx("input", { id: `${id}-company`, name: "company", autoComplete: "organization", maxLength: 100, value: values.company, onChange: set("company"), "aria-invalid": !!errors.company, "aria-describedby": describedBy("company"), className: "forge-input h-11 w-full px-3.5 text-base" }) })), _jsx(Field, { id: `${id}-message`, label: labels.message, error: errors.message, children: _jsx("textarea", { id: `${id}-message`, name: "message", required: true, rows: 6, maxLength: 5000, value: values.message, onChange: set("message"), "aria-invalid": !!errors.message, "aria-describedby": describedBy("message"), className: "forge-input min-h-36 w-full resize-y px-3.5 py-3 text-base" }) }), _jsx(Honeypot, { label: labels.honeypot, value: website, onChange: setWebsite }), failure && (_jsx("p", { role: "alert", className: "forge-alert px-3.5 py-2.5 text-sm", "data-forge-alert": "error", children: failure })), _jsx("div", { children: _jsxs("button", { type: "submit", disabled: disabled || state === "sending", className: "forge-btn forge-btn-primary inline-flex h-11 items-center justify-center gap-2 px-6 text-base font-medium", children: [state === "sending" && _jsx(LoaderCircle, { className: "forge-site-spin size-4", "aria-hidden": true }), state === "sending" ? labels.sending : buttonLabel || labels.submit] }) })] })), _jsx("div", { role: "status", "aria-live": "polite", className: done ? "forge-site-success p-5 text-base" : "sr-only", children: done ? _jsx(Success, { text: successMessage || labels.success }) : "" })] }));
|
|
115
|
+
}
|
|
116
|
+
export function NewsletterForm({ labels, buttonLabel, successMessage, source, layout = "row", endpoint = "/api/site/newsletter", disabled = false }) {
|
|
117
|
+
const id = useId();
|
|
118
|
+
const [email, setEmail] = useState("");
|
|
119
|
+
const [website, setWebsite] = useState("");
|
|
120
|
+
const [error, setError] = useState(null);
|
|
121
|
+
const [state, setState] = useState("idle");
|
|
122
|
+
const [failure, setFailure] = useState(null);
|
|
123
|
+
const input = useRef(null);
|
|
124
|
+
const check = (v) => (!v.trim() ? labels.required : !EMAIL.test(v.trim()) || v.length > 200 ? labels.invalidEmail : null);
|
|
125
|
+
async function submit(e) {
|
|
126
|
+
e.preventDefault();
|
|
127
|
+
if (disabled || state === "sending")
|
|
128
|
+
return;
|
|
129
|
+
setFailure(null);
|
|
130
|
+
const problem = check(email);
|
|
131
|
+
setError(problem);
|
|
132
|
+
if (problem) {
|
|
133
|
+
input.current?.focus();
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
setState("sending");
|
|
137
|
+
const result = await post(endpoint, { email: email.trim(), website, ...(source ? { source } : {}) });
|
|
138
|
+
if (result.ok)
|
|
139
|
+
setState("done");
|
|
140
|
+
else {
|
|
141
|
+
setState("idle");
|
|
142
|
+
if (result.issues.length)
|
|
143
|
+
setError(result.issues[0].message);
|
|
144
|
+
else
|
|
145
|
+
setFailure(result.status === 429 ? labels.rateLimited : labels.error);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const done = state === "done";
|
|
149
|
+
return (_jsxs("div", { className: "forge-site-form w-full", children: [!done && (_jsxs("form", { noValidate: true, onSubmit: submit, "aria-busy": state === "sending", children: [_jsxs("div", { className: layout === "row" ? "flex flex-col gap-2.5 @md:flex-row" : "grid gap-2.5", children: [_jsx("label", { htmlFor: `${id}-email`, className: "sr-only", children: labels.email }), _jsx("input", { ref: input, id: `${id}-email`, name: "email", type: "email", autoComplete: "email", inputMode: "email", required: true, maxLength: 200, placeholder: labels.email, value: email, onChange: (e) => {
|
|
150
|
+
setEmail(e.target.value);
|
|
151
|
+
if (error)
|
|
152
|
+
setError(check(e.target.value));
|
|
153
|
+
}, "aria-invalid": !!error, "aria-describedby": error ? `${id}-error` : undefined, className: "forge-input h-11 w-full min-w-0 flex-1 px-3.5 text-base" }), _jsxs("button", { type: "submit", disabled: disabled || state === "sending", className: "forge-btn forge-btn-primary inline-flex h-11 shrink-0 items-center justify-center gap-2 px-5 text-base font-medium", children: [state === "sending" && _jsx(LoaderCircle, { className: "forge-site-spin size-4", "aria-hidden": true }), state === "sending" ? labels.sending : buttonLabel || labels.submit] })] }), error && (_jsx("p", { id: `${id}-error`, className: "forge-site-field-error mt-2 text-sm", children: error })), _jsx(Honeypot, { label: labels.honeypot, value: website, onChange: setWebsite }), failure && (_jsx("p", { role: "alert", className: "forge-alert mt-3 px-3.5 py-2.5 text-sm", "data-forge-alert": "error", children: failure }))] })), _jsx("div", { role: "status", "aria-live": "polite", className: done ? "forge-site-success p-4 text-base" : "sr-only", children: done ? _jsx(Success, { text: successMessage || labels.success }) : "" })] }));
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=forms.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"forms.js","sourceRoot":"","sources":["../../src/site/forms.tsx"],"names":[],"mappings":"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"}
|