@realiizlabs/admin 0.8.1 → 0.9.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/auth-ui/index.cjs +17 -1
- package/dist/auth-ui/index.cjs.map +1 -1
- package/dist/auth-ui/index.js +1 -1
- package/dist/{chunk-CE2DRQBD.js → chunk-FTD4WE3V.js} +19 -3
- package/dist/chunk-FTD4WE3V.js.map +1 -0
- package/dist/forms-ui/index.cjs +243 -36
- package/dist/forms-ui/index.cjs.map +1 -1
- package/dist/forms-ui/index.d.cts +69 -12
- package/dist/forms-ui/index.d.ts +69 -12
- package/dist/forms-ui/index.js +226 -36
- package/dist/forms-ui/index.js.map +1 -1
- package/dist/git/index.cjs +18 -2
- package/dist/git/index.cjs.map +1 -1
- package/dist/git/index.js +18 -2
- package/dist/git/index.js.map +1 -1
- package/dist/media/index.cjs +95 -0
- package/dist/media/index.cjs.map +1 -0
- package/dist/media/index.d.cts +65 -0
- package/dist/media/index.d.ts +65 -0
- package/dist/media/index.js +85 -0
- package/dist/media/index.js.map +1 -0
- package/dist/shell/index.cjs +31 -24
- package/dist/shell/index.cjs.map +1 -1
- package/dist/shell/index.d.cts +6 -3
- package/dist/shell/index.d.ts +6 -3
- package/dist/shell/index.js +31 -24
- package/dist/shell/index.js.map +1 -1
- package/package.json +6 -1
- package/dist/chunk-CE2DRQBD.js.map +0 -1
package/dist/auth-ui/index.cjs
CHANGED
|
@@ -37,7 +37,23 @@ var FORM_CSS = `
|
|
|
37
37
|
.realiiz-form__helper{display:flex;justify-content:space-between;gap:12px;font-size:12px;color:var(--_muted);margin-top:4px;min-height:1em}
|
|
38
38
|
.realiiz-form__count{margin-left:auto;font-variant-numeric:tabular-nums}
|
|
39
39
|
.realiiz-form__error{color:var(--_danger);font-size:13px;margin-top:4px}
|
|
40
|
-
.realiiz-
|
|
40
|
+
.realiiz-image{border:1px dashed var(--_border);border-radius:var(--_radius);padding:16px 18px;background:#fff;outline:none;transition:border-color .15s}
|
|
41
|
+
.realiiz-image--over{border-color:var(--_accent);border-style:solid}
|
|
42
|
+
.realiiz-image--invalid{border-color:var(--_danger)}
|
|
43
|
+
.realiiz-image--busy{opacity:.7}
|
|
44
|
+
.realiiz-image__empty{display:flex;align-items:center;gap:14px;flex-wrap:wrap;color:var(--_muted)}
|
|
45
|
+
.realiiz-image__icon{flex-shrink:0;color:var(--_muted)}
|
|
46
|
+
.realiiz-image__or{font-size:13px}
|
|
47
|
+
.realiiz-image__has{display:flex;align-items:flex-start;gap:16px}
|
|
48
|
+
.realiiz-image__thumb{display:block;max-height:160px;max-width:min(320px,55%);width:auto;object-fit:cover;border-radius:8px;border:1px solid var(--_border);background:#f6f8fa}
|
|
49
|
+
.realiiz-image__meta{display:flex;flex-direction:column;gap:10px;min-width:0}
|
|
50
|
+
.realiiz-image__detail{font-size:13px;color:var(--_muted);word-break:break-all}
|
|
51
|
+
.realiiz-image__actions{display:flex;align-items:center;gap:14px}
|
|
52
|
+
.realiiz-image__remove{font:inherit;font-size:13px;background:none;border:0;padding:0;color:var(--_muted);text-decoration:underline;text-underline-offset:3px;cursor:pointer}
|
|
53
|
+
.realiiz-image__remove:hover{color:var(--_danger)}
|
|
54
|
+
.realiiz-image__hint{font-size:12px;color:var(--_muted);margin-top:10px}
|
|
55
|
+
.realiiz-image .realiiz-form__error{margin-top:8px}
|
|
56
|
+
@media (max-width:720px){.realiiz-image__has{flex-direction:column}.realiiz-image__thumb{max-width:100%}}
|
|
41
57
|
.realiiz-form__alert{border:1px solid var(--_danger);border-radius:var(--_radius);padding:12px 14px;margin:0 0 20px;color:var(--_danger)}
|
|
42
58
|
.realiiz-form__alert ul{margin:6px 0 0;padding-left:18px}
|
|
43
59
|
.realiiz-form__actions{display:flex;gap:10px;justify-content:flex-end;flex-wrap:wrap;margin-top:8px}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/forms-ui/styles.ts","../../src/auth-ui/MagicLinkForm.tsx","../../src/auth-ui/SignOutButton.tsx"],"names":["useMemo","createBrowserClient","useState","jsxs","jsx"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Theming hooks, same convention as BookingWidget: a root class, BEM elements,\n * and --realiiz-form-* custom properties with fallbacks so the form is usable\n * with zero host CSS and fully themeable with a dozen variables.\n */\n\nexport const FORM_CSS = `\n.realiiz-form{\n --_fg:var(--realiiz-form-fg,#111);\n --_muted:var(--realiiz-form-muted,#666);\n --_border:var(--realiiz-form-border,#cfcfcf);\n --_accent:var(--realiiz-form-accent,#111);\n --_accent-fg:var(--realiiz-form-accent-fg,#fff);\n --_danger:var(--realiiz-form-danger,#b3261e);\n --_radius:var(--realiiz-form-radius,6px);\n --_font:var(--realiiz-form-font,system-ui,-apple-system,\"Segoe UI\",Roboto,sans-serif);\n --_ring:var(--realiiz-form-focus-ring,0 0 0 3px rgba(0,0,0,.15));\n box-sizing:border-box;max-width:100%;color:var(--_fg);font-family:var(--_font);font-size:15px;line-height:1.4;\n}\n.realiiz-form *,.realiiz-form *::before,.realiiz-form *::after{box-sizing:inherit}\n.realiiz-form fieldset{border:0;margin:0 0 20px;padding:0;min-width:0}\n.realiiz-form legend{font-weight:600;font-size:13px;letter-spacing:.02em;text-transform:uppercase;color:var(--_muted);margin-bottom:12px;padding:0}\n.realiiz-form__field{display:block;margin:0 0 16px}\n.realiiz-form__label{display:block;font-weight:500;margin-bottom:6px}\n.realiiz-form__required{color:var(--_danger);margin-left:3px}\n.realiiz-form__control{display:block;width:100%;max-width:100%;font:inherit;color:inherit;background:#fff;border:1px solid var(--_border);border-radius:var(--_radius);padding:9px 11px;min-height:40px}\n.realiiz-form__control:focus{outline:none;border-color:var(--_accent);box-shadow:var(--_ring)}\n.realiiz-form__control--textarea{min-height:110px;resize:vertical}\n.realiiz-form__control--body{min-height:280px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:14px}\n.realiiz-form__control--invalid{border-color:var(--_danger)}\n.realiiz-form__check{display:flex;align-items:center;gap:8px;font-weight:500}\n.realiiz-form__check input{width:18px;height:18px;margin:0}\n.realiiz-form__helper{display:flex;justify-content:space-between;gap:12px;font-size:12px;color:var(--_muted);margin-top:4px;min-height:1em}\n.realiiz-form__count{margin-left:auto;font-variant-numeric:tabular-nums}\n.realiiz-form__error{color:var(--_danger);font-size:13px;margin-top:4px}\n.realiiz-form__current{font-size:12px;color:var(--_muted);margin-top:4px;word-break:break-all}\n.realiiz-form__alert{border:1px solid var(--_danger);border-radius:var(--_radius);padding:12px 14px;margin:0 0 20px;color:var(--_danger)}\n.realiiz-form__alert ul{margin:6px 0 0;padding-left:18px}\n.realiiz-form__actions{display:flex;gap:10px;justify-content:flex-end;flex-wrap:wrap;margin-top:8px}\n.realiiz-form__help{text-decoration:underline dotted;text-decoration-color:color-mix(in srgb,currentColor 45%,transparent);text-underline-offset:4px;cursor:help}\n.realiiz-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n.realiiz-form__footer{max-width:920px}\n.realiiz-form__bar{display:flex;align-items:center;gap:10px;margin-top:16px;padding:12px 0;border-top:1px solid var(--_border);background:#fff}\n.realiiz-form__status{margin-right:auto;font-size:.85rem;color:var(--_muted)}\n.realiiz-form__bar[data-dirty] .realiiz-form__status{color:var(--_fg)}\n.realiiz-form__button{font:inherit;font-weight:600;border-radius:var(--_radius);padding:10px 16px;border:1px solid var(--_border);background:#fff;color:var(--_fg);cursor:pointer;min-height:40px}\n.realiiz-form__button--primary{background:var(--_accent);color:var(--_accent-fg);border-color:var(--_accent)}\n.realiiz-form__button:disabled{opacity:.6;cursor:default}\n.realiiz-md{--_md-head:#fafbfd;--_md-hover:#f4f8fd;--_md-hover-line:#bcd0ea;--_md-on:#eef4fb;--_md-tip:#1f2933;position:relative}\n.realiiz-md__mode{display:inline-flex;align-self:flex-start;width:max-content;gap:2px;padding:3px;background:color-mix(in srgb,var(--_border) 40%,transparent);border-radius:8px;margin-bottom:8px}\n.realiiz-md__mode button{font:inherit;font-size:.8rem;padding:5px 13px;border:0;border-radius:6px;background:transparent;color:var(--_muted);cursor:pointer}\n.realiiz-md__mode button[aria-selected=\"true\"]{background:#fff;color:var(--_fg);box-shadow:0 1px 2px rgba(0,0,0,.12)}\n.realiiz-md__panel{position:relative;z-index:0;display:flex;flex-direction:column;border:1px solid var(--_border);border-radius:10px;background:#fff;overflow:hidden}\n.realiiz-md__panel:focus-within{border-color:var(--_md-hover-line)}\n.realiiz-md__head{position:relative;z-index:5;display:flex;align-items:center;gap:2px;flex-wrap:wrap;padding:5px 7px;background:var(--_md-head);border-bottom:1px solid var(--_border);flex-shrink:0}\n.realiiz-md__tools{display:flex;align-items:center;gap:3px;flex-wrap:wrap}\n.realiiz-md__spacer{flex:1}\n.realiiz-md__head button{position:relative;min-width:31px;height:28px;padding:0 9px;display:inline-flex;align-items:center;justify-content:center;background:#fff;border:1px solid var(--_border);border-radius:6px;font-family:inherit;font-size:.82rem;color:var(--_fg);cursor:pointer;line-height:1}\n.realiiz-md__head button:hover:not(:disabled){border-color:var(--_md-hover-line);background:var(--_md-hover)}\n.realiiz-md__head button:active{background:var(--_md-on)}\n.realiiz-md__head button.on{background:var(--_md-on);border-color:var(--_accent);color:var(--_accent);font-weight:600}\n.realiiz-md__ico{width:15px;height:15px;display:block}\n.realiiz-md__dot{font-size:1.05rem;line-height:1}\n.realiiz-md__sep{width:1px;height:17px;background:var(--_border);margin:0 5px}\n.realiiz-md__insert{display:inline-flex;align-items:center;gap:3px;border-left:1px solid var(--_border);padding-left:8px;margin-left:6px}\n.realiiz-md__insert button{background:#f6f8fa;color:var(--_muted);padding:0;width:31px}\n.realiiz-md__insert button:hover{color:var(--_accent)}\n.realiiz-md__lab{font-size:.74rem;color:var(--_muted);margin-right:3px}\n.realiiz-md__head button[data-tip]::after{content:attr(data-tip);position:absolute;top:calc(100% + 6px);left:0;background:var(--_md-tip);color:#fff;font-size:.72rem;padding:4px 8px;border-radius:5px;white-space:nowrap;opacity:0;pointer-events:none;transition:opacity .12s;z-index:20}\n.realiiz-md__head button[data-tip]:hover::after,.realiiz-md__head button[data-tip]:focus-visible::after{opacity:1}\n.realiiz-md__expand[data-tip]::after{left:auto;right:0}\n.realiiz-md__tgwrap{position:relative;display:inline-flex}\n.realiiz-md__tgrid{position:absolute;top:calc(100% + 6px);left:0;background:#fff;border:1px solid var(--_border);border-radius:9px;padding:9px;box-shadow:0 10px 28px rgba(0,0,0,.14);z-index:7;display:block}\n.realiiz-md__tgcells{display:grid;grid-template-columns:repeat(6,17px);gap:3px}\n.realiiz-md__tgcells i{width:17px;height:15px;border:1px solid var(--_border);border-radius:2px;background:#fff;cursor:pointer;display:block}\n.realiiz-md__tgcells i.hot{background:#dbe9fb;border-color:var(--_accent)}\n.realiiz-md__tglab{display:block;margin-top:8px;font-size:.74rem;color:var(--_muted);text-align:center;white-space:nowrap}\n.realiiz-md .realiiz-md__ta{flex:1 1 auto;border:0;border-radius:0;resize:vertical;background:#fff;max-width:none;padding:16px 18px;line-height:1.65}\n.realiiz-md .realiiz-md__ta:focus{outline:none;box-shadow:none}\n.realiiz-md__preview{padding:20px 24px;background:#fff}\n.realiiz-md__note{padding:4px 2px}\n.realiiz-md--fs{position:fixed;inset:0;z-index:60;background:#fff;padding:14px 22px 22px;overflow:auto;display:flex;flex-direction:column}\n.realiiz-md--fs .realiiz-md__panel{flex:1;min-height:0}\n.realiiz-md--fs .realiiz-md__ta,.realiiz-md--fs .realiiz-md__preview{flex:1;min-height:0;overflow:auto;resize:none}\n.realiiz-prose{max-width:68ch;line-height:1.65}\n.realiiz-chips{display:flex;flex-wrap:wrap;gap:6px;align-items:center;min-height:40px;padding:5px 8px;background:#fff;border:1px solid var(--_border);border-radius:var(--_radius)}\n.realiiz-chips:focus-within{border-color:var(--_accent);box-shadow:var(--_ring)}\n.realiiz-chip{display:inline-flex;align-items:center;gap:4px;padding:2px 4px 2px 9px;border:1px solid var(--_border);border-radius:999px;font-size:.8rem}\n.realiiz-chip button{font:inherit;line-height:1;width:18px;height:18px;border-radius:50%;border:0;background:transparent;color:var(--_muted);cursor:pointer}\n.realiiz-chips__input{flex:1;min-width:120px;border:0;background:transparent;font:inherit;color:inherit;outline:none;padding:4px}\n.realiiz-chips__count{margin-left:auto;color:var(--_muted);font-size:.75rem}\n.realiiz-gauge{display:inline-flex;align-items:center;gap:10px;font-size:.75rem;color:var(--_muted)}\n.realiiz-gauge__bar{position:relative;width:140px;height:6px;border-radius:3px;background:var(--_border);overflow:hidden}\n.realiiz-gauge__zone{position:absolute;top:0;bottom:0;background:rgba(21,128,61,.28)}\n.realiiz-gauge__fill{position:absolute;top:0;bottom:0;left:0;background:var(--_muted);border-radius:3px;transition:width .12s}\n.realiiz-gauge__text b{font-weight:600;color:inherit}\n.realiiz-gauge--ok{color:#15803d}.realiiz-gauge--ok .realiiz-gauge__fill{background:#15803d}\n.realiiz-gauge--over{color:var(--_danger)}.realiiz-gauge--over .realiiz-gauge__fill{background:var(--_danger)}\n`;\n","/**\n * MagicLinkForm — one field, one button, and the right words for each outcome.\n *\n * The audience is a non-technical client. Two outcomes matter:\n * sent → tell them where the link went and that it expires in an hour\n * uninvited → signups are off, so Supabase refuses unknown emails with a\n * \"Signups not allowed\" error. That string is a support ticket;\n * \"this email isn't set up for this site\" is not.\n *\n * `shouldCreateUser: false` restates the dashboard setting in code — the flag\n * a reviewer greps for.\n */\n\nimport { createBrowserClient } from \"@supabase/ssr\";\nimport { useMemo, useState, type FormEvent } from \"react\";\nimport { FORM_CSS } from \"../forms-ui/styles\";\n\nexport interface MagicLinkFormProps {\n url: string;\n anonKey: string;\n /** The site's callback, e.g. https://5degrees.org/admin/auth/callback?next=/admin */\n redirectTo: string;\n /** Shown in the heading: \"Sign in to {siteName}\". */\n siteName: string;\n className?: string;\n}\n\ntype State = { kind: \"idle\" } | { kind: \"sending\" } | { kind: \"sent\"; email: string } | { kind: \"error\"; message: string; uninvited: boolean };\n\nexport const UNINVITED_MESSAGE =\n \"This email address isn't set up for this site yet. Ask the site owner to send you an invite.\";\n\nexport function isUninvitedError(message: string): boolean {\n return /signups? not allowed|not allowed|user not found|invalid login/i.test(message);\n}\n\nexport function MagicLinkForm({ url, anonKey, redirectTo, siteName, className }: MagicLinkFormProps) {\n const supabase = useMemo(() => createBrowserClient(url, anonKey), [url, anonKey]);\n const [email, setEmail] = useState(\"\");\n const [state, setState] = useState<State>({ kind: \"idle\" });\n\n const submit = async (e: FormEvent) => {\n e.preventDefault();\n if (state.kind === \"sending\") return;\n const address = email.trim().toLowerCase();\n setState({ kind: \"sending\" });\n const { error } = await supabase.auth.signInWithOtp({\n email: address,\n options: { emailRedirectTo: redirectTo, shouldCreateUser: false },\n });\n if (error) {\n const uninvited = isUninvitedError(error.message);\n setState({ kind: \"error\", message: uninvited ? UNINVITED_MESSAGE : error.message, uninvited });\n return;\n }\n setState({ kind: \"sent\", email: address });\n };\n\n const root = [\"realiiz-form\", \"realiiz-signin\", className].filter(Boolean).join(\" \");\n\n if (state.kind === \"sent\") {\n return (\n <div className={root} data-realiiz-signin=\"sent\">\n <style>{FORM_CSS}</style>\n <h2 className=\"realiiz-signin__heading\">Check your email</h2>\n <p>\n We sent a sign-in link to <strong>{state.email}</strong>. It expires in an hour.\n </p>\n <button type=\"button\" className=\"realiiz-form__button\" onClick={() => setState({ kind: \"idle\" })}>\n Use a different address\n </button>\n </div>\n );\n }\n\n const sending = state.kind === \"sending\";\n const fieldError = state.kind === \"error\" && !state.uninvited ? state.message : null;\n\n return (\n <form className={root} onSubmit={submit} noValidate data-realiiz-signin=\"form\">\n <style>{FORM_CSS}</style>\n <h2 className=\"realiiz-signin__heading\">Sign in to {siteName}</h2>\n\n {state.kind === \"error\" && state.uninvited && (\n <div className=\"realiiz-form__alert\" role=\"alert\">{state.message}</div>\n )}\n\n <div className=\"realiiz-form__field\">\n <label className=\"realiiz-form__label\" htmlFor=\"realiiz-signin-email\">Email address</label>\n <input\n id=\"realiiz-signin-email\"\n className={[\"realiiz-form__control\", fieldError && \"realiiz-form__control--invalid\"].filter(Boolean).join(\" \")}\n type=\"email\"\n name=\"email\"\n autoComplete=\"email\"\n required\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n aria-invalid={fieldError ? true : undefined}\n aria-describedby={fieldError ? \"realiiz-signin-email-error\" : undefined}\n />\n {fieldError && (\n <div className=\"realiiz-form__error\" id=\"realiiz-signin-email-error\">{fieldError}</div>\n )}\n </div>\n\n <div className=\"realiiz-form__actions\">\n <button type=\"submit\" className=\"realiiz-form__button realiiz-form__button--primary\" disabled={sending || email.trim() === \"\"}>\n {sending ? \"Sending…\" : \"Send me a sign-in link\"}\n </button>\n </div>\n </form>\n );\n}\n","/**\n * SignOutButton — a plain POST form. No JavaScript needed, so it works before\n * hydration and inside a server component. The site's route (ADMIN-04) calls\n * signOut() from @realiizlabs/admin/auth and redirects.\n */\n\nexport interface SignOutButtonProps {\n /** Default \"/admin/auth/signout\". */\n actionPath?: string;\n className?: string;\n label?: string;\n}\n\nexport function SignOutButton({ actionPath = \"/admin/auth/signout\", className, label = \"Sign out\" }: SignOutButtonProps) {\n return (\n <form method=\"post\" action={actionPath} className={[\"realiiz-signout\", className].filter(Boolean).join(\" \")}>\n <button type=\"submit\" className=\"realiiz-form__button\">{label}</button>\n </form>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/forms-ui/styles.ts","../../src/auth-ui/MagicLinkForm.tsx","../../src/auth-ui/SignOutButton.tsx"],"names":["useMemo","createBrowserClient","useState","jsxs","jsx"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Theming hooks, same convention as BookingWidget: a root class, BEM elements,\n * and --realiiz-form-* custom properties with fallbacks so the form is usable\n * with zero host CSS and fully themeable with a dozen variables.\n */\n\nexport const FORM_CSS = `\n.realiiz-form{\n --_fg:var(--realiiz-form-fg,#111);\n --_muted:var(--realiiz-form-muted,#666);\n --_border:var(--realiiz-form-border,#cfcfcf);\n --_accent:var(--realiiz-form-accent,#111);\n --_accent-fg:var(--realiiz-form-accent-fg,#fff);\n --_danger:var(--realiiz-form-danger,#b3261e);\n --_radius:var(--realiiz-form-radius,6px);\n --_font:var(--realiiz-form-font,system-ui,-apple-system,\"Segoe UI\",Roboto,sans-serif);\n --_ring:var(--realiiz-form-focus-ring,0 0 0 3px rgba(0,0,0,.15));\n box-sizing:border-box;max-width:100%;color:var(--_fg);font-family:var(--_font);font-size:15px;line-height:1.4;\n}\n.realiiz-form *,.realiiz-form *::before,.realiiz-form *::after{box-sizing:inherit}\n.realiiz-form fieldset{border:0;margin:0 0 20px;padding:0;min-width:0}\n.realiiz-form legend{font-weight:600;font-size:13px;letter-spacing:.02em;text-transform:uppercase;color:var(--_muted);margin-bottom:12px;padding:0}\n.realiiz-form__field{display:block;margin:0 0 16px}\n.realiiz-form__label{display:block;font-weight:500;margin-bottom:6px}\n.realiiz-form__required{color:var(--_danger);margin-left:3px}\n.realiiz-form__control{display:block;width:100%;max-width:100%;font:inherit;color:inherit;background:#fff;border:1px solid var(--_border);border-radius:var(--_radius);padding:9px 11px;min-height:40px}\n.realiiz-form__control:focus{outline:none;border-color:var(--_accent);box-shadow:var(--_ring)}\n.realiiz-form__control--textarea{min-height:110px;resize:vertical}\n.realiiz-form__control--body{min-height:280px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:14px}\n.realiiz-form__control--invalid{border-color:var(--_danger)}\n.realiiz-form__check{display:flex;align-items:center;gap:8px;font-weight:500}\n.realiiz-form__check input{width:18px;height:18px;margin:0}\n.realiiz-form__helper{display:flex;justify-content:space-between;gap:12px;font-size:12px;color:var(--_muted);margin-top:4px;min-height:1em}\n.realiiz-form__count{margin-left:auto;font-variant-numeric:tabular-nums}\n.realiiz-form__error{color:var(--_danger);font-size:13px;margin-top:4px}\n.realiiz-image{border:1px dashed var(--_border);border-radius:var(--_radius);padding:16px 18px;background:#fff;outline:none;transition:border-color .15s}\n.realiiz-image--over{border-color:var(--_accent);border-style:solid}\n.realiiz-image--invalid{border-color:var(--_danger)}\n.realiiz-image--busy{opacity:.7}\n.realiiz-image__empty{display:flex;align-items:center;gap:14px;flex-wrap:wrap;color:var(--_muted)}\n.realiiz-image__icon{flex-shrink:0;color:var(--_muted)}\n.realiiz-image__or{font-size:13px}\n.realiiz-image__has{display:flex;align-items:flex-start;gap:16px}\n.realiiz-image__thumb{display:block;max-height:160px;max-width:min(320px,55%);width:auto;object-fit:cover;border-radius:8px;border:1px solid var(--_border);background:#f6f8fa}\n.realiiz-image__meta{display:flex;flex-direction:column;gap:10px;min-width:0}\n.realiiz-image__detail{font-size:13px;color:var(--_muted);word-break:break-all}\n.realiiz-image__actions{display:flex;align-items:center;gap:14px}\n.realiiz-image__remove{font:inherit;font-size:13px;background:none;border:0;padding:0;color:var(--_muted);text-decoration:underline;text-underline-offset:3px;cursor:pointer}\n.realiiz-image__remove:hover{color:var(--_danger)}\n.realiiz-image__hint{font-size:12px;color:var(--_muted);margin-top:10px}\n.realiiz-image .realiiz-form__error{margin-top:8px}\n@media (max-width:720px){.realiiz-image__has{flex-direction:column}.realiiz-image__thumb{max-width:100%}}\n.realiiz-form__alert{border:1px solid var(--_danger);border-radius:var(--_radius);padding:12px 14px;margin:0 0 20px;color:var(--_danger)}\n.realiiz-form__alert ul{margin:6px 0 0;padding-left:18px}\n.realiiz-form__actions{display:flex;gap:10px;justify-content:flex-end;flex-wrap:wrap;margin-top:8px}\n.realiiz-form__help{text-decoration:underline dotted;text-decoration-color:color-mix(in srgb,currentColor 45%,transparent);text-underline-offset:4px;cursor:help}\n.realiiz-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n.realiiz-form__footer{max-width:920px}\n.realiiz-form__bar{display:flex;align-items:center;gap:10px;margin-top:16px;padding:12px 0;border-top:1px solid var(--_border);background:#fff}\n.realiiz-form__status{margin-right:auto;font-size:.85rem;color:var(--_muted)}\n.realiiz-form__bar[data-dirty] .realiiz-form__status{color:var(--_fg)}\n.realiiz-form__button{font:inherit;font-weight:600;border-radius:var(--_radius);padding:10px 16px;border:1px solid var(--_border);background:#fff;color:var(--_fg);cursor:pointer;min-height:40px}\n.realiiz-form__button--primary{background:var(--_accent);color:var(--_accent-fg);border-color:var(--_accent)}\n.realiiz-form__button:disabled{opacity:.6;cursor:default}\n.realiiz-md{--_md-head:#fafbfd;--_md-hover:#f4f8fd;--_md-hover-line:#bcd0ea;--_md-on:#eef4fb;--_md-tip:#1f2933;position:relative}\n.realiiz-md__mode{display:inline-flex;align-self:flex-start;width:max-content;gap:2px;padding:3px;background:color-mix(in srgb,var(--_border) 40%,transparent);border-radius:8px;margin-bottom:8px}\n.realiiz-md__mode button{font:inherit;font-size:.8rem;padding:5px 13px;border:0;border-radius:6px;background:transparent;color:var(--_muted);cursor:pointer}\n.realiiz-md__mode button[aria-selected=\"true\"]{background:#fff;color:var(--_fg);box-shadow:0 1px 2px rgba(0,0,0,.12)}\n.realiiz-md__panel{position:relative;z-index:0;display:flex;flex-direction:column;border:1px solid var(--_border);border-radius:10px;background:#fff;overflow:hidden}\n.realiiz-md__panel:focus-within{border-color:var(--_md-hover-line)}\n.realiiz-md__head{position:relative;z-index:5;display:flex;align-items:center;gap:2px;flex-wrap:wrap;padding:5px 7px;background:var(--_md-head);border-bottom:1px solid var(--_border);flex-shrink:0}\n.realiiz-md__tools{display:flex;align-items:center;gap:3px;flex-wrap:wrap}\n.realiiz-md__spacer{flex:1}\n.realiiz-md__head button{position:relative;min-width:31px;height:28px;padding:0 9px;display:inline-flex;align-items:center;justify-content:center;background:#fff;border:1px solid var(--_border);border-radius:6px;font-family:inherit;font-size:.82rem;color:var(--_fg);cursor:pointer;line-height:1}\n.realiiz-md__head button:hover:not(:disabled){border-color:var(--_md-hover-line);background:var(--_md-hover)}\n.realiiz-md__head button:active{background:var(--_md-on)}\n.realiiz-md__head button.on{background:var(--_md-on);border-color:var(--_accent);color:var(--_accent);font-weight:600}\n.realiiz-md__ico{width:15px;height:15px;display:block}\n.realiiz-md__dot{font-size:1.05rem;line-height:1}\n.realiiz-md__sep{width:1px;height:17px;background:var(--_border);margin:0 5px}\n.realiiz-md__insert{display:inline-flex;align-items:center;gap:3px;border-left:1px solid var(--_border);padding-left:8px;margin-left:6px}\n.realiiz-md__insert button{background:#f6f8fa;color:var(--_muted);padding:0;width:31px}\n.realiiz-md__insert button:hover{color:var(--_accent)}\n.realiiz-md__lab{font-size:.74rem;color:var(--_muted);margin-right:3px}\n.realiiz-md__head button[data-tip]::after{content:attr(data-tip);position:absolute;top:calc(100% + 6px);left:0;background:var(--_md-tip);color:#fff;font-size:.72rem;padding:4px 8px;border-radius:5px;white-space:nowrap;opacity:0;pointer-events:none;transition:opacity .12s;z-index:20}\n.realiiz-md__head button[data-tip]:hover::after,.realiiz-md__head button[data-tip]:focus-visible::after{opacity:1}\n.realiiz-md__expand[data-tip]::after{left:auto;right:0}\n.realiiz-md__tgwrap{position:relative;display:inline-flex}\n.realiiz-md__tgrid{position:absolute;top:calc(100% + 6px);left:0;background:#fff;border:1px solid var(--_border);border-radius:9px;padding:9px;box-shadow:0 10px 28px rgba(0,0,0,.14);z-index:7;display:block}\n.realiiz-md__tgcells{display:grid;grid-template-columns:repeat(6,17px);gap:3px}\n.realiiz-md__tgcells i{width:17px;height:15px;border:1px solid var(--_border);border-radius:2px;background:#fff;cursor:pointer;display:block}\n.realiiz-md__tgcells i.hot{background:#dbe9fb;border-color:var(--_accent)}\n.realiiz-md__tglab{display:block;margin-top:8px;font-size:.74rem;color:var(--_muted);text-align:center;white-space:nowrap}\n.realiiz-md .realiiz-md__ta{flex:1 1 auto;border:0;border-radius:0;resize:vertical;background:#fff;max-width:none;padding:16px 18px;line-height:1.65}\n.realiiz-md .realiiz-md__ta:focus{outline:none;box-shadow:none}\n.realiiz-md__preview{padding:20px 24px;background:#fff}\n.realiiz-md__note{padding:4px 2px}\n.realiiz-md--fs{position:fixed;inset:0;z-index:60;background:#fff;padding:14px 22px 22px;overflow:auto;display:flex;flex-direction:column}\n.realiiz-md--fs .realiiz-md__panel{flex:1;min-height:0}\n.realiiz-md--fs .realiiz-md__ta,.realiiz-md--fs .realiiz-md__preview{flex:1;min-height:0;overflow:auto;resize:none}\n.realiiz-prose{max-width:68ch;line-height:1.65}\n.realiiz-chips{display:flex;flex-wrap:wrap;gap:6px;align-items:center;min-height:40px;padding:5px 8px;background:#fff;border:1px solid var(--_border);border-radius:var(--_radius)}\n.realiiz-chips:focus-within{border-color:var(--_accent);box-shadow:var(--_ring)}\n.realiiz-chip{display:inline-flex;align-items:center;gap:4px;padding:2px 4px 2px 9px;border:1px solid var(--_border);border-radius:999px;font-size:.8rem}\n.realiiz-chip button{font:inherit;line-height:1;width:18px;height:18px;border-radius:50%;border:0;background:transparent;color:var(--_muted);cursor:pointer}\n.realiiz-chips__input{flex:1;min-width:120px;border:0;background:transparent;font:inherit;color:inherit;outline:none;padding:4px}\n.realiiz-chips__count{margin-left:auto;color:var(--_muted);font-size:.75rem}\n.realiiz-gauge{display:inline-flex;align-items:center;gap:10px;font-size:.75rem;color:var(--_muted)}\n.realiiz-gauge__bar{position:relative;width:140px;height:6px;border-radius:3px;background:var(--_border);overflow:hidden}\n.realiiz-gauge__zone{position:absolute;top:0;bottom:0;background:rgba(21,128,61,.28)}\n.realiiz-gauge__fill{position:absolute;top:0;bottom:0;left:0;background:var(--_muted);border-radius:3px;transition:width .12s}\n.realiiz-gauge__text b{font-weight:600;color:inherit}\n.realiiz-gauge--ok{color:#15803d}.realiiz-gauge--ok .realiiz-gauge__fill{background:#15803d}\n.realiiz-gauge--over{color:var(--_danger)}.realiiz-gauge--over .realiiz-gauge__fill{background:var(--_danger)}\n`;\n","/**\n * MagicLinkForm — one field, one button, and the right words for each outcome.\n *\n * The audience is a non-technical client. Two outcomes matter:\n * sent → tell them where the link went and that it expires in an hour\n * uninvited → signups are off, so Supabase refuses unknown emails with a\n * \"Signups not allowed\" error. That string is a support ticket;\n * \"this email isn't set up for this site\" is not.\n *\n * `shouldCreateUser: false` restates the dashboard setting in code — the flag\n * a reviewer greps for.\n */\n\nimport { createBrowserClient } from \"@supabase/ssr\";\nimport { useMemo, useState, type FormEvent } from \"react\";\nimport { FORM_CSS } from \"../forms-ui/styles\";\n\nexport interface MagicLinkFormProps {\n url: string;\n anonKey: string;\n /** The site's callback, e.g. https://5degrees.org/admin/auth/callback?next=/admin */\n redirectTo: string;\n /** Shown in the heading: \"Sign in to {siteName}\". */\n siteName: string;\n className?: string;\n}\n\ntype State = { kind: \"idle\" } | { kind: \"sending\" } | { kind: \"sent\"; email: string } | { kind: \"error\"; message: string; uninvited: boolean };\n\nexport const UNINVITED_MESSAGE =\n \"This email address isn't set up for this site yet. Ask the site owner to send you an invite.\";\n\nexport function isUninvitedError(message: string): boolean {\n return /signups? not allowed|not allowed|user not found|invalid login/i.test(message);\n}\n\nexport function MagicLinkForm({ url, anonKey, redirectTo, siteName, className }: MagicLinkFormProps) {\n const supabase = useMemo(() => createBrowserClient(url, anonKey), [url, anonKey]);\n const [email, setEmail] = useState(\"\");\n const [state, setState] = useState<State>({ kind: \"idle\" });\n\n const submit = async (e: FormEvent) => {\n e.preventDefault();\n if (state.kind === \"sending\") return;\n const address = email.trim().toLowerCase();\n setState({ kind: \"sending\" });\n const { error } = await supabase.auth.signInWithOtp({\n email: address,\n options: { emailRedirectTo: redirectTo, shouldCreateUser: false },\n });\n if (error) {\n const uninvited = isUninvitedError(error.message);\n setState({ kind: \"error\", message: uninvited ? UNINVITED_MESSAGE : error.message, uninvited });\n return;\n }\n setState({ kind: \"sent\", email: address });\n };\n\n const root = [\"realiiz-form\", \"realiiz-signin\", className].filter(Boolean).join(\" \");\n\n if (state.kind === \"sent\") {\n return (\n <div className={root} data-realiiz-signin=\"sent\">\n <style>{FORM_CSS}</style>\n <h2 className=\"realiiz-signin__heading\">Check your email</h2>\n <p>\n We sent a sign-in link to <strong>{state.email}</strong>. It expires in an hour.\n </p>\n <button type=\"button\" className=\"realiiz-form__button\" onClick={() => setState({ kind: \"idle\" })}>\n Use a different address\n </button>\n </div>\n );\n }\n\n const sending = state.kind === \"sending\";\n const fieldError = state.kind === \"error\" && !state.uninvited ? state.message : null;\n\n return (\n <form className={root} onSubmit={submit} noValidate data-realiiz-signin=\"form\">\n <style>{FORM_CSS}</style>\n <h2 className=\"realiiz-signin__heading\">Sign in to {siteName}</h2>\n\n {state.kind === \"error\" && state.uninvited && (\n <div className=\"realiiz-form__alert\" role=\"alert\">{state.message}</div>\n )}\n\n <div className=\"realiiz-form__field\">\n <label className=\"realiiz-form__label\" htmlFor=\"realiiz-signin-email\">Email address</label>\n <input\n id=\"realiiz-signin-email\"\n className={[\"realiiz-form__control\", fieldError && \"realiiz-form__control--invalid\"].filter(Boolean).join(\" \")}\n type=\"email\"\n name=\"email\"\n autoComplete=\"email\"\n required\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n aria-invalid={fieldError ? true : undefined}\n aria-describedby={fieldError ? \"realiiz-signin-email-error\" : undefined}\n />\n {fieldError && (\n <div className=\"realiiz-form__error\" id=\"realiiz-signin-email-error\">{fieldError}</div>\n )}\n </div>\n\n <div className=\"realiiz-form__actions\">\n <button type=\"submit\" className=\"realiiz-form__button realiiz-form__button--primary\" disabled={sending || email.trim() === \"\"}>\n {sending ? \"Sending…\" : \"Send me a sign-in link\"}\n </button>\n </div>\n </form>\n );\n}\n","/**\n * SignOutButton — a plain POST form. No JavaScript needed, so it works before\n * hydration and inside a server component. The site's route (ADMIN-04) calls\n * signOut() from @realiizlabs/admin/auth and redirects.\n */\n\nexport interface SignOutButtonProps {\n /** Default \"/admin/auth/signout\". */\n actionPath?: string;\n className?: string;\n label?: string;\n}\n\nexport function SignOutButton({ actionPath = \"/admin/auth/signout\", className, label = \"Sign out\" }: SignOutButtonProps) {\n return (\n <form method=\"post\" action={actionPath} className={[\"realiiz-signout\", className].filter(Boolean).join(\" \")}>\n <button type=\"submit\" className=\"realiiz-form__button\">{label}</button>\n </form>\n );\n}\n"]}
|
package/dist/auth-ui/index.js
CHANGED
|
@@ -28,7 +28,23 @@ var FORM_CSS = `
|
|
|
28
28
|
.realiiz-form__helper{display:flex;justify-content:space-between;gap:12px;font-size:12px;color:var(--_muted);margin-top:4px;min-height:1em}
|
|
29
29
|
.realiiz-form__count{margin-left:auto;font-variant-numeric:tabular-nums}
|
|
30
30
|
.realiiz-form__error{color:var(--_danger);font-size:13px;margin-top:4px}
|
|
31
|
-
.realiiz-
|
|
31
|
+
.realiiz-image{border:1px dashed var(--_border);border-radius:var(--_radius);padding:16px 18px;background:#fff;outline:none;transition:border-color .15s}
|
|
32
|
+
.realiiz-image--over{border-color:var(--_accent);border-style:solid}
|
|
33
|
+
.realiiz-image--invalid{border-color:var(--_danger)}
|
|
34
|
+
.realiiz-image--busy{opacity:.7}
|
|
35
|
+
.realiiz-image__empty{display:flex;align-items:center;gap:14px;flex-wrap:wrap;color:var(--_muted)}
|
|
36
|
+
.realiiz-image__icon{flex-shrink:0;color:var(--_muted)}
|
|
37
|
+
.realiiz-image__or{font-size:13px}
|
|
38
|
+
.realiiz-image__has{display:flex;align-items:flex-start;gap:16px}
|
|
39
|
+
.realiiz-image__thumb{display:block;max-height:160px;max-width:min(320px,55%);width:auto;object-fit:cover;border-radius:8px;border:1px solid var(--_border);background:#f6f8fa}
|
|
40
|
+
.realiiz-image__meta{display:flex;flex-direction:column;gap:10px;min-width:0}
|
|
41
|
+
.realiiz-image__detail{font-size:13px;color:var(--_muted);word-break:break-all}
|
|
42
|
+
.realiiz-image__actions{display:flex;align-items:center;gap:14px}
|
|
43
|
+
.realiiz-image__remove{font:inherit;font-size:13px;background:none;border:0;padding:0;color:var(--_muted);text-decoration:underline;text-underline-offset:3px;cursor:pointer}
|
|
44
|
+
.realiiz-image__remove:hover{color:var(--_danger)}
|
|
45
|
+
.realiiz-image__hint{font-size:12px;color:var(--_muted);margin-top:10px}
|
|
46
|
+
.realiiz-image .realiiz-form__error{margin-top:8px}
|
|
47
|
+
@media (max-width:720px){.realiiz-image__has{flex-direction:column}.realiiz-image__thumb{max-width:100%}}
|
|
32
48
|
.realiiz-form__alert{border:1px solid var(--_danger);border-radius:var(--_radius);padding:12px 14px;margin:0 0 20px;color:var(--_danger)}
|
|
33
49
|
.realiiz-form__alert ul{margin:6px 0 0;padding-left:18px}
|
|
34
50
|
.realiiz-form__actions{display:flex;gap:10px;justify-content:flex-end;flex-wrap:wrap;margin-top:8px}
|
|
@@ -94,5 +110,5 @@ var FORM_CSS = `
|
|
|
94
110
|
`;
|
|
95
111
|
|
|
96
112
|
export { FORM_CSS };
|
|
97
|
-
//# sourceMappingURL=chunk-
|
|
98
|
-
//# sourceMappingURL=chunk-
|
|
113
|
+
//# sourceMappingURL=chunk-FTD4WE3V.js.map
|
|
114
|
+
//# sourceMappingURL=chunk-FTD4WE3V.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/forms-ui/styles.ts"],"names":[],"mappings":";AAMO,IAAM,QAAA,GAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA","file":"chunk-FTD4WE3V.js","sourcesContent":["/**\n * Theming hooks, same convention as BookingWidget: a root class, BEM elements,\n * and --realiiz-form-* custom properties with fallbacks so the form is usable\n * with zero host CSS and fully themeable with a dozen variables.\n */\n\nexport const FORM_CSS = `\n.realiiz-form{\n --_fg:var(--realiiz-form-fg,#111);\n --_muted:var(--realiiz-form-muted,#666);\n --_border:var(--realiiz-form-border,#cfcfcf);\n --_accent:var(--realiiz-form-accent,#111);\n --_accent-fg:var(--realiiz-form-accent-fg,#fff);\n --_danger:var(--realiiz-form-danger,#b3261e);\n --_radius:var(--realiiz-form-radius,6px);\n --_font:var(--realiiz-form-font,system-ui,-apple-system,\"Segoe UI\",Roboto,sans-serif);\n --_ring:var(--realiiz-form-focus-ring,0 0 0 3px rgba(0,0,0,.15));\n box-sizing:border-box;max-width:100%;color:var(--_fg);font-family:var(--_font);font-size:15px;line-height:1.4;\n}\n.realiiz-form *,.realiiz-form *::before,.realiiz-form *::after{box-sizing:inherit}\n.realiiz-form fieldset{border:0;margin:0 0 20px;padding:0;min-width:0}\n.realiiz-form legend{font-weight:600;font-size:13px;letter-spacing:.02em;text-transform:uppercase;color:var(--_muted);margin-bottom:12px;padding:0}\n.realiiz-form__field{display:block;margin:0 0 16px}\n.realiiz-form__label{display:block;font-weight:500;margin-bottom:6px}\n.realiiz-form__required{color:var(--_danger);margin-left:3px}\n.realiiz-form__control{display:block;width:100%;max-width:100%;font:inherit;color:inherit;background:#fff;border:1px solid var(--_border);border-radius:var(--_radius);padding:9px 11px;min-height:40px}\n.realiiz-form__control:focus{outline:none;border-color:var(--_accent);box-shadow:var(--_ring)}\n.realiiz-form__control--textarea{min-height:110px;resize:vertical}\n.realiiz-form__control--body{min-height:280px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:14px}\n.realiiz-form__control--invalid{border-color:var(--_danger)}\n.realiiz-form__check{display:flex;align-items:center;gap:8px;font-weight:500}\n.realiiz-form__check input{width:18px;height:18px;margin:0}\n.realiiz-form__helper{display:flex;justify-content:space-between;gap:12px;font-size:12px;color:var(--_muted);margin-top:4px;min-height:1em}\n.realiiz-form__count{margin-left:auto;font-variant-numeric:tabular-nums}\n.realiiz-form__error{color:var(--_danger);font-size:13px;margin-top:4px}\n.realiiz-image{border:1px dashed var(--_border);border-radius:var(--_radius);padding:16px 18px;background:#fff;outline:none;transition:border-color .15s}\n.realiiz-image--over{border-color:var(--_accent);border-style:solid}\n.realiiz-image--invalid{border-color:var(--_danger)}\n.realiiz-image--busy{opacity:.7}\n.realiiz-image__empty{display:flex;align-items:center;gap:14px;flex-wrap:wrap;color:var(--_muted)}\n.realiiz-image__icon{flex-shrink:0;color:var(--_muted)}\n.realiiz-image__or{font-size:13px}\n.realiiz-image__has{display:flex;align-items:flex-start;gap:16px}\n.realiiz-image__thumb{display:block;max-height:160px;max-width:min(320px,55%);width:auto;object-fit:cover;border-radius:8px;border:1px solid var(--_border);background:#f6f8fa}\n.realiiz-image__meta{display:flex;flex-direction:column;gap:10px;min-width:0}\n.realiiz-image__detail{font-size:13px;color:var(--_muted);word-break:break-all}\n.realiiz-image__actions{display:flex;align-items:center;gap:14px}\n.realiiz-image__remove{font:inherit;font-size:13px;background:none;border:0;padding:0;color:var(--_muted);text-decoration:underline;text-underline-offset:3px;cursor:pointer}\n.realiiz-image__remove:hover{color:var(--_danger)}\n.realiiz-image__hint{font-size:12px;color:var(--_muted);margin-top:10px}\n.realiiz-image .realiiz-form__error{margin-top:8px}\n@media (max-width:720px){.realiiz-image__has{flex-direction:column}.realiiz-image__thumb{max-width:100%}}\n.realiiz-form__alert{border:1px solid var(--_danger);border-radius:var(--_radius);padding:12px 14px;margin:0 0 20px;color:var(--_danger)}\n.realiiz-form__alert ul{margin:6px 0 0;padding-left:18px}\n.realiiz-form__actions{display:flex;gap:10px;justify-content:flex-end;flex-wrap:wrap;margin-top:8px}\n.realiiz-form__help{text-decoration:underline dotted;text-decoration-color:color-mix(in srgb,currentColor 45%,transparent);text-underline-offset:4px;cursor:help}\n.realiiz-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n.realiiz-form__footer{max-width:920px}\n.realiiz-form__bar{display:flex;align-items:center;gap:10px;margin-top:16px;padding:12px 0;border-top:1px solid var(--_border);background:#fff}\n.realiiz-form__status{margin-right:auto;font-size:.85rem;color:var(--_muted)}\n.realiiz-form__bar[data-dirty] .realiiz-form__status{color:var(--_fg)}\n.realiiz-form__button{font:inherit;font-weight:600;border-radius:var(--_radius);padding:10px 16px;border:1px solid var(--_border);background:#fff;color:var(--_fg);cursor:pointer;min-height:40px}\n.realiiz-form__button--primary{background:var(--_accent);color:var(--_accent-fg);border-color:var(--_accent)}\n.realiiz-form__button:disabled{opacity:.6;cursor:default}\n.realiiz-md{--_md-head:#fafbfd;--_md-hover:#f4f8fd;--_md-hover-line:#bcd0ea;--_md-on:#eef4fb;--_md-tip:#1f2933;position:relative}\n.realiiz-md__mode{display:inline-flex;align-self:flex-start;width:max-content;gap:2px;padding:3px;background:color-mix(in srgb,var(--_border) 40%,transparent);border-radius:8px;margin-bottom:8px}\n.realiiz-md__mode button{font:inherit;font-size:.8rem;padding:5px 13px;border:0;border-radius:6px;background:transparent;color:var(--_muted);cursor:pointer}\n.realiiz-md__mode button[aria-selected=\"true\"]{background:#fff;color:var(--_fg);box-shadow:0 1px 2px rgba(0,0,0,.12)}\n.realiiz-md__panel{position:relative;z-index:0;display:flex;flex-direction:column;border:1px solid var(--_border);border-radius:10px;background:#fff;overflow:hidden}\n.realiiz-md__panel:focus-within{border-color:var(--_md-hover-line)}\n.realiiz-md__head{position:relative;z-index:5;display:flex;align-items:center;gap:2px;flex-wrap:wrap;padding:5px 7px;background:var(--_md-head);border-bottom:1px solid var(--_border);flex-shrink:0}\n.realiiz-md__tools{display:flex;align-items:center;gap:3px;flex-wrap:wrap}\n.realiiz-md__spacer{flex:1}\n.realiiz-md__head button{position:relative;min-width:31px;height:28px;padding:0 9px;display:inline-flex;align-items:center;justify-content:center;background:#fff;border:1px solid var(--_border);border-radius:6px;font-family:inherit;font-size:.82rem;color:var(--_fg);cursor:pointer;line-height:1}\n.realiiz-md__head button:hover:not(:disabled){border-color:var(--_md-hover-line);background:var(--_md-hover)}\n.realiiz-md__head button:active{background:var(--_md-on)}\n.realiiz-md__head button.on{background:var(--_md-on);border-color:var(--_accent);color:var(--_accent);font-weight:600}\n.realiiz-md__ico{width:15px;height:15px;display:block}\n.realiiz-md__dot{font-size:1.05rem;line-height:1}\n.realiiz-md__sep{width:1px;height:17px;background:var(--_border);margin:0 5px}\n.realiiz-md__insert{display:inline-flex;align-items:center;gap:3px;border-left:1px solid var(--_border);padding-left:8px;margin-left:6px}\n.realiiz-md__insert button{background:#f6f8fa;color:var(--_muted);padding:0;width:31px}\n.realiiz-md__insert button:hover{color:var(--_accent)}\n.realiiz-md__lab{font-size:.74rem;color:var(--_muted);margin-right:3px}\n.realiiz-md__head button[data-tip]::after{content:attr(data-tip);position:absolute;top:calc(100% + 6px);left:0;background:var(--_md-tip);color:#fff;font-size:.72rem;padding:4px 8px;border-radius:5px;white-space:nowrap;opacity:0;pointer-events:none;transition:opacity .12s;z-index:20}\n.realiiz-md__head button[data-tip]:hover::after,.realiiz-md__head button[data-tip]:focus-visible::after{opacity:1}\n.realiiz-md__expand[data-tip]::after{left:auto;right:0}\n.realiiz-md__tgwrap{position:relative;display:inline-flex}\n.realiiz-md__tgrid{position:absolute;top:calc(100% + 6px);left:0;background:#fff;border:1px solid var(--_border);border-radius:9px;padding:9px;box-shadow:0 10px 28px rgba(0,0,0,.14);z-index:7;display:block}\n.realiiz-md__tgcells{display:grid;grid-template-columns:repeat(6,17px);gap:3px}\n.realiiz-md__tgcells i{width:17px;height:15px;border:1px solid var(--_border);border-radius:2px;background:#fff;cursor:pointer;display:block}\n.realiiz-md__tgcells i.hot{background:#dbe9fb;border-color:var(--_accent)}\n.realiiz-md__tglab{display:block;margin-top:8px;font-size:.74rem;color:var(--_muted);text-align:center;white-space:nowrap}\n.realiiz-md .realiiz-md__ta{flex:1 1 auto;border:0;border-radius:0;resize:vertical;background:#fff;max-width:none;padding:16px 18px;line-height:1.65}\n.realiiz-md .realiiz-md__ta:focus{outline:none;box-shadow:none}\n.realiiz-md__preview{padding:20px 24px;background:#fff}\n.realiiz-md__note{padding:4px 2px}\n.realiiz-md--fs{position:fixed;inset:0;z-index:60;background:#fff;padding:14px 22px 22px;overflow:auto;display:flex;flex-direction:column}\n.realiiz-md--fs .realiiz-md__panel{flex:1;min-height:0}\n.realiiz-md--fs .realiiz-md__ta,.realiiz-md--fs .realiiz-md__preview{flex:1;min-height:0;overflow:auto;resize:none}\n.realiiz-prose{max-width:68ch;line-height:1.65}\n.realiiz-chips{display:flex;flex-wrap:wrap;gap:6px;align-items:center;min-height:40px;padding:5px 8px;background:#fff;border:1px solid var(--_border);border-radius:var(--_radius)}\n.realiiz-chips:focus-within{border-color:var(--_accent);box-shadow:var(--_ring)}\n.realiiz-chip{display:inline-flex;align-items:center;gap:4px;padding:2px 4px 2px 9px;border:1px solid var(--_border);border-radius:999px;font-size:.8rem}\n.realiiz-chip button{font:inherit;line-height:1;width:18px;height:18px;border-radius:50%;border:0;background:transparent;color:var(--_muted);cursor:pointer}\n.realiiz-chips__input{flex:1;min-width:120px;border:0;background:transparent;font:inherit;color:inherit;outline:none;padding:4px}\n.realiiz-chips__count{margin-left:auto;color:var(--_muted);font-size:.75rem}\n.realiiz-gauge{display:inline-flex;align-items:center;gap:10px;font-size:.75rem;color:var(--_muted)}\n.realiiz-gauge__bar{position:relative;width:140px;height:6px;border-radius:3px;background:var(--_border);overflow:hidden}\n.realiiz-gauge__zone{position:absolute;top:0;bottom:0;background:rgba(21,128,61,.28)}\n.realiiz-gauge__fill{position:absolute;top:0;bottom:0;left:0;background:var(--_muted);border-radius:3px;transition:width .12s}\n.realiiz-gauge__text b{font-weight:600;color:inherit}\n.realiiz-gauge--ok{color:#15803d}.realiiz-gauge--ok .realiiz-gauge__fill{background:#15803d}\n.realiiz-gauge--over{color:var(--_danger)}.realiiz-gauge--over .realiiz-gauge__fill{background:var(--_danger)}\n`;\n"]}
|
package/dist/forms-ui/index.cjs
CHANGED
|
@@ -310,16 +310,7 @@ function Control(p) {
|
|
|
310
310
|
case "boolean":
|
|
311
311
|
return /* @__PURE__ */ jsxRuntime.jsx("input", { ...common, type: "checkbox", checked: p.value === true, onChange: (e) => p.onChange(e.target.checked) });
|
|
312
312
|
case "image":
|
|
313
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
314
|
-
"input",
|
|
315
|
-
{
|
|
316
|
-
...common,
|
|
317
|
-
type: "file",
|
|
318
|
-
accept: "image/*",
|
|
319
|
-
className: cls,
|
|
320
|
-
onChange: (e) => p.onChange(e.target.files?.[0] ?? null)
|
|
321
|
-
}
|
|
322
|
-
);
|
|
313
|
+
return /* @__PURE__ */ jsxRuntime.jsx("input", { ...common, type: "text", className: cls, value: str, placeholder: p.spec.placeholder ?? "/images/picture.webp", onChange: (e) => p.onChange(e.target.value) });
|
|
323
314
|
case "date":
|
|
324
315
|
return /* @__PURE__ */ jsxRuntime.jsx("input", { ...common, type: "date", className: cls, value: str, onChange: (e) => p.onChange(e.target.value) });
|
|
325
316
|
case "number":
|
|
@@ -365,6 +356,151 @@ function Control(p) {
|
|
|
365
356
|
);
|
|
366
357
|
}
|
|
367
358
|
}
|
|
359
|
+
function useImages() {
|
|
360
|
+
const [images, setImages] = react.useState({});
|
|
361
|
+
const latest = react.useRef(images);
|
|
362
|
+
latest.current = images;
|
|
363
|
+
const urls = react.useRef(/* @__PURE__ */ new Set());
|
|
364
|
+
react.useEffect(() => {
|
|
365
|
+
const set = urls.current;
|
|
366
|
+
return () => {
|
|
367
|
+
for (const u of set) URL.revokeObjectURL(u);
|
|
368
|
+
};
|
|
369
|
+
}, []);
|
|
370
|
+
const keep = react.useCallback((field, r, previous) => {
|
|
371
|
+
const previewUrl = r.previewUrl ?? URL.createObjectURL(r.blob);
|
|
372
|
+
if (!r.previewUrl) urls.current.add(previewUrl);
|
|
373
|
+
const picked = { path: r.path, blob: r.blob, previewUrl, width: r.width, height: r.height, previous };
|
|
374
|
+
setImages((m) => ({ ...m, [field]: picked }));
|
|
375
|
+
return picked;
|
|
376
|
+
}, []);
|
|
377
|
+
const remove = react.useCallback((field) => {
|
|
378
|
+
const previous = latest.current[field]?.previous;
|
|
379
|
+
setImages((m) => {
|
|
380
|
+
const rest = { ...m };
|
|
381
|
+
delete rest[field];
|
|
382
|
+
return rest;
|
|
383
|
+
});
|
|
384
|
+
return previous;
|
|
385
|
+
}, []);
|
|
386
|
+
const toSubmission = react.useCallback(() => {
|
|
387
|
+
return Object.fromEntries(Object.entries(images).map(([k, v]) => [k, { path: v.path, blob: v.blob }]));
|
|
388
|
+
}, [images]);
|
|
389
|
+
return { images, keep, remove, toSubmission };
|
|
390
|
+
}
|
|
391
|
+
function formatBytes(n) {
|
|
392
|
+
if (n < 1024 * 1024) return `${Math.max(1, Math.round(n / 1024))} KB`;
|
|
393
|
+
return `${(n / (1024 * 1024)).toFixed(1)} MB`;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// src/forms-ui/md/upload.ts
|
|
397
|
+
var IMAGE_ACCEPT = "image/jpeg,image/png,image/webp,image/gif";
|
|
398
|
+
function pickFile(accept = IMAGE_ACCEPT) {
|
|
399
|
+
return new Promise((resolve) => {
|
|
400
|
+
const input = document.createElement("input");
|
|
401
|
+
input.type = "file";
|
|
402
|
+
input.accept = accept;
|
|
403
|
+
input.style.display = "none";
|
|
404
|
+
document.body.appendChild(input);
|
|
405
|
+
const done = () => {
|
|
406
|
+
resolve(input.files?.[0]);
|
|
407
|
+
input.remove();
|
|
408
|
+
};
|
|
409
|
+
input.addEventListener("change", done, { once: true });
|
|
410
|
+
window.addEventListener("focus", () => setTimeout(() => {
|
|
411
|
+
if (document.body.contains(input)) done();
|
|
412
|
+
}, 400), { once: true });
|
|
413
|
+
input.click();
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
function imageFromDrop(e) {
|
|
417
|
+
return Array.from(e.dataTransfer?.files ?? []).find((f) => f.type.startsWith("image/"));
|
|
418
|
+
}
|
|
419
|
+
function imageFromPaste(e) {
|
|
420
|
+
const item = Array.from(e.clipboardData?.items ?? []).find((i) => i.kind === "file" && i.type.startsWith("image/"));
|
|
421
|
+
return item?.getAsFile() ?? void 0;
|
|
422
|
+
}
|
|
423
|
+
function messageOf(err) {
|
|
424
|
+
if (err && typeof err === "object" && err.name === "MediaError") return err.message;
|
|
425
|
+
return "That picture couldn\u2019t be prepared. Try a different file.";
|
|
426
|
+
}
|
|
427
|
+
var IMAGE_HINT = "JPG, PNG or WebP, up to 10MB. We shrink it for the web.";
|
|
428
|
+
function ImageInput(p) {
|
|
429
|
+
const [busy, setBusy] = react.useState(false);
|
|
430
|
+
const [error, setError] = react.useState(null);
|
|
431
|
+
const [over, setOver] = react.useState(false);
|
|
432
|
+
const zone = react.useRef(null);
|
|
433
|
+
const take = async (file) => {
|
|
434
|
+
if (!file || busy || p.disabled) return;
|
|
435
|
+
setBusy(true);
|
|
436
|
+
setError(null);
|
|
437
|
+
try {
|
|
438
|
+
await p.onPick(file);
|
|
439
|
+
} catch (err) {
|
|
440
|
+
setError(messageOf(err));
|
|
441
|
+
} finally {
|
|
442
|
+
setBusy(false);
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
const choose = async () => take(await pickFile());
|
|
446
|
+
const onDrop = (e) => {
|
|
447
|
+
e.preventDefault();
|
|
448
|
+
setOver(false);
|
|
449
|
+
void take(imageFromDrop(e));
|
|
450
|
+
};
|
|
451
|
+
const onPaste = (e) => {
|
|
452
|
+
const f = imageFromPaste(e);
|
|
453
|
+
if (f) {
|
|
454
|
+
e.preventDefault();
|
|
455
|
+
void take(f);
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
const src = p.picked?.previewUrl ?? (p.value || null);
|
|
459
|
+
const detail = p.picked ? [p.picked.width && p.picked.height ? `${p.picked.width} \xD7 ${p.picked.height}` : null, formatBytes(p.picked.blob.size)].filter(Boolean).join(" \xB7 ") : p.value;
|
|
460
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
461
|
+
"div",
|
|
462
|
+
{
|
|
463
|
+
ref: zone,
|
|
464
|
+
className: ["realiiz-image", over && "realiiz-image--over", p.invalid && "realiiz-image--invalid", busy && "realiiz-image--busy"].filter(Boolean).join(" "),
|
|
465
|
+
"data-field": p.name,
|
|
466
|
+
onDragOver: (e) => {
|
|
467
|
+
e.preventDefault();
|
|
468
|
+
if (!over) setOver(true);
|
|
469
|
+
},
|
|
470
|
+
onDragLeave: () => setOver(false),
|
|
471
|
+
onDrop,
|
|
472
|
+
onPaste,
|
|
473
|
+
onBlur: p.onBlur,
|
|
474
|
+
tabIndex: -1,
|
|
475
|
+
children: [
|
|
476
|
+
/* @__PURE__ */ jsxRuntime.jsx("input", { id: p.id, name: p.name, type: "text", value: p.value, readOnly: true, className: "realiiz-sr-only", "aria-busy": busy || void 0 }),
|
|
477
|
+
src ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "realiiz-image__has", children: [
|
|
478
|
+
/* @__PURE__ */ jsxRuntime.jsx("img", { src, alt: "", className: "realiiz-image__thumb", "data-testid": "realiiz-image-thumb" }),
|
|
479
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "realiiz-image__meta", children: [
|
|
480
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "realiiz-image__detail", children: busy ? "Shrinking\u2026" : detail }),
|
|
481
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "realiiz-image__actions", children: [
|
|
482
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "realiiz-form__button", onClick: choose, disabled: busy || p.disabled, children: "Replace" }),
|
|
483
|
+
p.picked && p.onRemove && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "realiiz-image__remove", onClick: p.onRemove, disabled: busy, children: "Remove" })
|
|
484
|
+
] })
|
|
485
|
+
] })
|
|
486
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "realiiz-image__empty", children: [
|
|
487
|
+
/* @__PURE__ */ jsxRuntime.jsx(PictureIcon, {}),
|
|
488
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "realiiz-form__button", onClick: choose, disabled: busy || p.disabled, children: busy ? "Shrinking\u2026" : "Choose a picture" }),
|
|
489
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "realiiz-image__or", children: "or drag one here" })
|
|
490
|
+
] }),
|
|
491
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "realiiz-image__hint", children: IMAGE_HINT }),
|
|
492
|
+
error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "realiiz-form__error", role: "alert", children: error })
|
|
493
|
+
]
|
|
494
|
+
}
|
|
495
|
+
);
|
|
496
|
+
}
|
|
497
|
+
function PictureIcon() {
|
|
498
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "28", height: "28", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", className: "realiiz-image__icon", children: [
|
|
499
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "3", y: "5", width: "18", height: "14", rx: "2" }),
|
|
500
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "9", cy: "10", r: "1.6" }),
|
|
501
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 16l-5-5-8 8" })
|
|
502
|
+
] });
|
|
503
|
+
}
|
|
368
504
|
var defaultLabel = (label, help) => /* @__PURE__ */ jsxRuntime.jsx("span", { className: "realiiz-form__help", title: help, children: label });
|
|
369
505
|
function LengthGauge({ length: raw, range, suffix }) {
|
|
370
506
|
const [min, max] = range;
|
|
@@ -397,7 +533,19 @@ function Field(p) {
|
|
|
397
533
|
const isCheck = p.spec.input === "boolean";
|
|
398
534
|
const isTags = p.spec.input === "tags";
|
|
399
535
|
const labelText = p.spec.help ? (p.renderLabel ?? defaultLabel)(p.spec.label, p.spec.help) : p.spec.label;
|
|
400
|
-
const control =
|
|
536
|
+
const control = p.spec.input === "image" && p.onPickImage ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
537
|
+
ImageInput,
|
|
538
|
+
{
|
|
539
|
+
id,
|
|
540
|
+
name: p.spec.name,
|
|
541
|
+
value: typeof p.value === "string" ? p.value : "",
|
|
542
|
+
picked: p.picked,
|
|
543
|
+
invalid,
|
|
544
|
+
onPick: p.onPickImage,
|
|
545
|
+
onRemove: p.onRemoveImage,
|
|
546
|
+
onBlur: p.onBlur
|
|
547
|
+
}
|
|
548
|
+
) : isTags ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
401
549
|
ChipsInput,
|
|
402
550
|
{
|
|
403
551
|
id,
|
|
@@ -424,10 +572,6 @@ function Field(p) {
|
|
|
424
572
|
] }),
|
|
425
573
|
control
|
|
426
574
|
] }),
|
|
427
|
-
p.spec.input === "image" && p.current && !(p.value instanceof File) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "realiiz-form__current", children: [
|
|
428
|
-
"Current: ",
|
|
429
|
-
p.current
|
|
430
|
-
] }),
|
|
431
575
|
isText && (p.spec.recommended || typeof max === "number") && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "realiiz-form__helper", children: [
|
|
432
576
|
p.spec.recommended ? /* @__PURE__ */ jsxRuntime.jsx(LengthGauge, { length: len, range: p.spec.recommended, suffix: p.spec.suffix }) : /* @__PURE__ */ jsxRuntime.jsx("span", {}),
|
|
433
577
|
typeof max === "number" && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "realiiz-form__count", children: [
|
|
@@ -762,7 +906,7 @@ var TABLE_OPS = [
|
|
|
762
906
|
{ op: "colAfter", label: "+ Col", tip: "Insert a column to the right" },
|
|
763
907
|
{ op: "colDel", label: "\u2212 Col", tip: "Delete this column" }
|
|
764
908
|
];
|
|
765
|
-
function MarkdownEditor({ id, name = "body", value, onChange, placeholder, renderPreview, previewClassName, minHeight = 320, termComponent }) {
|
|
909
|
+
function MarkdownEditor({ id, name = "body", value, onChange, placeholder, renderPreview, previewClassName, minHeight = 320, termComponent, onUploadImage, previewSrc }) {
|
|
766
910
|
const [mode, setMode] = react.useState(() => renderPreview && value.trim() ? "preview" : "edit");
|
|
767
911
|
const [preview, setPreview] = react.useState(null);
|
|
768
912
|
const [loading, setLoading] = react.useState(false);
|
|
@@ -770,6 +914,7 @@ function MarkdownEditor({ id, name = "body", value, onChange, placeholder, rende
|
|
|
770
914
|
const [on, setOn] = react.useState(() => /* @__PURE__ */ new Set());
|
|
771
915
|
const [inTable, setInTable] = react.useState(false);
|
|
772
916
|
const [note, setNote] = react.useState(null);
|
|
917
|
+
const [uploading, setUploading] = react.useState(false);
|
|
773
918
|
const ta = react.useRef(null);
|
|
774
919
|
const sync = react.useCallback(() => {
|
|
775
920
|
const el = ta.current;
|
|
@@ -816,6 +961,37 @@ function MarkdownEditor({ id, name = "body", value, onChange, placeholder, rende
|
|
|
816
961
|
setNote(null);
|
|
817
962
|
commit(r);
|
|
818
963
|
};
|
|
964
|
+
const upload = async (file) => {
|
|
965
|
+
const el = ta.current;
|
|
966
|
+
if (!file || !onUploadImage || !el || uploading) return;
|
|
967
|
+
setUploading(true);
|
|
968
|
+
setNote(null);
|
|
969
|
+
const a = el.selectionStart, b = el.selectionEnd;
|
|
970
|
+
try {
|
|
971
|
+
const { path } = await onUploadImage(file);
|
|
972
|
+
const alt = value.slice(a, b) || "description";
|
|
973
|
+
const text = value.slice(0, a) + `` + value.slice(b);
|
|
974
|
+
commit({ text, a: a + 2, b: a + 2 + alt.length });
|
|
975
|
+
} catch (err) {
|
|
976
|
+
setNote(messageOf(err));
|
|
977
|
+
} finally {
|
|
978
|
+
setUploading(false);
|
|
979
|
+
}
|
|
980
|
+
};
|
|
981
|
+
const onDrop = (e) => {
|
|
982
|
+
const f = imageFromDrop(e);
|
|
983
|
+
if (f && onUploadImage) {
|
|
984
|
+
e.preventDefault();
|
|
985
|
+
void upload(f);
|
|
986
|
+
}
|
|
987
|
+
};
|
|
988
|
+
const onPaste = (e) => {
|
|
989
|
+
const f = imageFromPaste(e);
|
|
990
|
+
if (f && onUploadImage) {
|
|
991
|
+
e.preventDefault();
|
|
992
|
+
void upload(f);
|
|
993
|
+
}
|
|
994
|
+
};
|
|
819
995
|
const onKey = (e) => {
|
|
820
996
|
if (e.key === "Escape" && expanded) {
|
|
821
997
|
e.preventDefault();
|
|
@@ -854,7 +1030,7 @@ function MarkdownEditor({ id, name = "body", value, onChange, placeholder, rende
|
|
|
854
1030
|
let live = true;
|
|
855
1031
|
setLoading(true);
|
|
856
1032
|
renderPreview(value).then((r) => {
|
|
857
|
-
if (live) setPreview(r);
|
|
1033
|
+
if (live) setPreview("html" in r ? { html: swapPreviewSrc(r.html, previewSrc) } : r);
|
|
858
1034
|
}).catch((err) => {
|
|
859
1035
|
if (live) setPreview({ error: err instanceof Error ? err.message : String(err) });
|
|
860
1036
|
}).finally(() => {
|
|
@@ -863,11 +1039,13 @@ function MarkdownEditor({ id, name = "body", value, onChange, placeholder, rende
|
|
|
863
1039
|
return () => {
|
|
864
1040
|
live = false;
|
|
865
1041
|
};
|
|
866
|
-
}, [mode, value, renderPreview]);
|
|
1042
|
+
}, [mode, value, renderPreview, previewSrc]);
|
|
867
1043
|
const termTool = termComponent ? { id: "term", title: `Tooltip \u2014 wraps the selection in <${termComponent}>`, apply: toggleTerm(termComponent) } : null;
|
|
868
1044
|
const btn = (toolId) => {
|
|
869
1045
|
const t = toolId === "term" && termTool ? termTool : toolById(toolId);
|
|
870
|
-
|
|
1046
|
+
const uploads = t.id === "image" && Boolean(onUploadImage);
|
|
1047
|
+
const title = uploads ? uploading ? "Shrinking\u2026" : "Picture \u2014 choose a file, or drop/paste one into the text" : t.title;
|
|
1048
|
+
return /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", "data-tool": t.id, "data-tip": title, "aria-label": title, "aria-pressed": on.has(t.id) || void 0, "aria-busy": uploads && uploading ? true : void 0, className: on.has(t.id) ? "on" : void 0, disabled: uploads && uploading, onMouseDown: (e) => e.preventDefault(), onClick: () => uploads ? pickFile().then(upload) : run(t.apply), children: MD_ICONS[t.id] }, t.id);
|
|
871
1049
|
};
|
|
872
1050
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: ["realiiz-md", expanded && "realiiz-md--fs"].filter(Boolean).join(" "), children: [
|
|
873
1051
|
renderPreview && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "realiiz-md__mode", role: "tablist", "aria-label": "Editor mode", children: [
|
|
@@ -924,6 +1102,8 @@ function MarkdownEditor({ id, name = "body", value, onChange, placeholder, rende
|
|
|
924
1102
|
onKeyUp: sync,
|
|
925
1103
|
onClick: sync,
|
|
926
1104
|
onKeyDown: onKey,
|
|
1105
|
+
onDrop,
|
|
1106
|
+
onPaste,
|
|
927
1107
|
spellCheck: true
|
|
928
1108
|
}
|
|
929
1109
|
) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "realiiz-md__preview", style: expanded ? void 0 : { minHeight }, "aria-live": "polite", "aria-busy": loading, children: [
|
|
@@ -935,9 +1115,13 @@ function MarkdownEditor({ id, name = "body", value, onChange, placeholder, rende
|
|
|
935
1115
|
!loading && preview && "html" in preview && /* @__PURE__ */ jsxRuntime.jsx("div", { className: ["realiiz-prose", previewClassName].filter(Boolean).join(" "), dangerouslySetInnerHTML: { __html: preview.html } })
|
|
936
1116
|
] })
|
|
937
1117
|
] }),
|
|
938
|
-
note && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "realiiz-form__helper realiiz-md__note", role: "status", children: note })
|
|
1118
|
+
(note || uploading) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "realiiz-form__helper realiiz-md__note", role: "status", children: uploading ? "Shrinking\u2026" : note })
|
|
939
1119
|
] });
|
|
940
1120
|
}
|
|
1121
|
+
function swapPreviewSrc(html, map) {
|
|
1122
|
+
if (!map || Object.keys(map).length === 0) return html;
|
|
1123
|
+
return html.replace(/(<img\b[^>]*\bsrc=")([^"]+)(")/g, (m, pre, src, post) => map[src] ? `${pre}${map[src]}${post}` : m);
|
|
1124
|
+
}
|
|
941
1125
|
function commonPrefix(a, b) {
|
|
942
1126
|
const n = Math.min(a.length, b.length);
|
|
943
1127
|
let i = 0;
|
|
@@ -1023,7 +1207,23 @@ var FORM_CSS = `
|
|
|
1023
1207
|
.realiiz-form__helper{display:flex;justify-content:space-between;gap:12px;font-size:12px;color:var(--_muted);margin-top:4px;min-height:1em}
|
|
1024
1208
|
.realiiz-form__count{margin-left:auto;font-variant-numeric:tabular-nums}
|
|
1025
1209
|
.realiiz-form__error{color:var(--_danger);font-size:13px;margin-top:4px}
|
|
1026
|
-
.realiiz-
|
|
1210
|
+
.realiiz-image{border:1px dashed var(--_border);border-radius:var(--_radius);padding:16px 18px;background:#fff;outline:none;transition:border-color .15s}
|
|
1211
|
+
.realiiz-image--over{border-color:var(--_accent);border-style:solid}
|
|
1212
|
+
.realiiz-image--invalid{border-color:var(--_danger)}
|
|
1213
|
+
.realiiz-image--busy{opacity:.7}
|
|
1214
|
+
.realiiz-image__empty{display:flex;align-items:center;gap:14px;flex-wrap:wrap;color:var(--_muted)}
|
|
1215
|
+
.realiiz-image__icon{flex-shrink:0;color:var(--_muted)}
|
|
1216
|
+
.realiiz-image__or{font-size:13px}
|
|
1217
|
+
.realiiz-image__has{display:flex;align-items:flex-start;gap:16px}
|
|
1218
|
+
.realiiz-image__thumb{display:block;max-height:160px;max-width:min(320px,55%);width:auto;object-fit:cover;border-radius:8px;border:1px solid var(--_border);background:#f6f8fa}
|
|
1219
|
+
.realiiz-image__meta{display:flex;flex-direction:column;gap:10px;min-width:0}
|
|
1220
|
+
.realiiz-image__detail{font-size:13px;color:var(--_muted);word-break:break-all}
|
|
1221
|
+
.realiiz-image__actions{display:flex;align-items:center;gap:14px}
|
|
1222
|
+
.realiiz-image__remove{font:inherit;font-size:13px;background:none;border:0;padding:0;color:var(--_muted);text-decoration:underline;text-underline-offset:3px;cursor:pointer}
|
|
1223
|
+
.realiiz-image__remove:hover{color:var(--_danger)}
|
|
1224
|
+
.realiiz-image__hint{font-size:12px;color:var(--_muted);margin-top:10px}
|
|
1225
|
+
.realiiz-image .realiiz-form__error{margin-top:8px}
|
|
1226
|
+
@media (max-width:720px){.realiiz-image__has{flex-direction:column}.realiiz-image__thumb{max-width:100%}}
|
|
1027
1227
|
.realiiz-form__alert{border:1px solid var(--_danger);border-radius:var(--_radius);padding:12px 14px;margin:0 0 20px;color:var(--_danger)}
|
|
1028
1228
|
.realiiz-form__alert ul{margin:6px 0 0;padding-left:18px}
|
|
1029
1229
|
.realiiz-form__actions{display:flex;gap:10px;justify-content:flex-end;flex-wrap:wrap;margin-top:8px}
|
|
@@ -1089,7 +1289,6 @@ var FORM_CSS = `
|
|
|
1089
1289
|
`;
|
|
1090
1290
|
|
|
1091
1291
|
// src/forms-ui/values.ts
|
|
1092
|
-
var PENDING_UPLOAD_PREFIX = "pending-upload/";
|
|
1093
1292
|
function toControl(spec, raw) {
|
|
1094
1293
|
if (raw === void 0 || raw === null) return spec.input === "boolean" ? false : "";
|
|
1095
1294
|
switch (spec.input) {
|
|
@@ -1108,7 +1307,6 @@ function toControl(spec, raw) {
|
|
|
1108
1307
|
}
|
|
1109
1308
|
function toFrontmatter(specs, state, hiddenValues) {
|
|
1110
1309
|
const frontmatter = { ...hiddenValues };
|
|
1111
|
-
const files = {};
|
|
1112
1310
|
for (const spec of specs) {
|
|
1113
1311
|
if (spec.role === "hidden" || spec.role === "body") continue;
|
|
1114
1312
|
const v = state[spec.name];
|
|
@@ -1117,11 +1315,6 @@ function toFrontmatter(specs, state, hiddenValues) {
|
|
|
1117
1315
|
else if (spec.required) frontmatter[spec.name] = false;
|
|
1118
1316
|
continue;
|
|
1119
1317
|
}
|
|
1120
|
-
if (v instanceof File) {
|
|
1121
|
-
files[spec.name] = v;
|
|
1122
|
-
frontmatter[spec.name] = PENDING_UPLOAD_PREFIX + v.name;
|
|
1123
|
-
continue;
|
|
1124
|
-
}
|
|
1125
1318
|
if (Array.isArray(v)) {
|
|
1126
1319
|
if (v.length || spec.required) frontmatter[spec.name] = v;
|
|
1127
1320
|
continue;
|
|
@@ -1144,13 +1337,13 @@ function toFrontmatter(specs, state, hiddenValues) {
|
|
|
1144
1337
|
frontmatter[spec.name] = s;
|
|
1145
1338
|
}
|
|
1146
1339
|
}
|
|
1147
|
-
return
|
|
1340
|
+
return frontmatter;
|
|
1148
1341
|
}
|
|
1149
1342
|
var LEAVE_MESSAGE = "You have unsaved changes. Leave this page and lose them?";
|
|
1150
1343
|
function slugify(s) {
|
|
1151
1344
|
return s.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
1152
1345
|
}
|
|
1153
|
-
function ContentForm({ entry, initialValues = {}, initialBody = "", onSubmit, onCancel, submitLabel = "Save", className, showBody = true, bodyLabel = "Content", renderPreview, previewClassName, termComponent, stickyBar = false, status, barExtra, footer, renderLabel, derive, guardUnsaved = true }) {
|
|
1346
|
+
function ContentForm({ entry, initialValues = {}, initialBody = "", onSubmit, onCancel, submitLabel = "Save", className, showBody = true, bodyLabel = "Content", renderPreview, previewClassName, termComponent, onPickImage, onUploadImage, previewSrc, stickyBar = false, status, barExtra, footer, renderLabel, derive, guardUnsaved = true }) {
|
|
1154
1347
|
const formId = react.useId();
|
|
1155
1348
|
const specs = react.useMemo(() => walkSchema(entry.schema, entry), [entry]);
|
|
1156
1349
|
const visible = react.useMemo(() => specs.filter((s) => s.role !== "hidden" && s.role !== "body"), [specs]);
|
|
@@ -1178,6 +1371,16 @@ function ContentForm({ entry, initialValues = {}, initialBody = "", onSubmit, on
|
|
|
1178
1371
|
update(name, v);
|
|
1179
1372
|
};
|
|
1180
1373
|
const [body, setBody] = react.useState(initialBody);
|
|
1374
|
+
const pictures = useImages();
|
|
1375
|
+
const pickImage = onPickImage ? (field) => async (file) => {
|
|
1376
|
+
const r = await onPickImage(field, file);
|
|
1377
|
+
pictures.keep(field, r, typeof state[field] === "string" ? state[field] : "");
|
|
1378
|
+
onFieldChange(field, r.path);
|
|
1379
|
+
} : void 0;
|
|
1380
|
+
const removeImage = (field) => () => {
|
|
1381
|
+
const previous = pictures.remove(field);
|
|
1382
|
+
onFieldChange(field, previous ?? "");
|
|
1383
|
+
};
|
|
1181
1384
|
const dirty = body !== initialBody || specs.some((s) => !sameValue(state[s.name], initialState[s.name]));
|
|
1182
1385
|
const [issues, setIssues] = react.useState([]);
|
|
1183
1386
|
const [attempted, setAttempted] = react.useState(false);
|
|
@@ -1227,7 +1430,7 @@ function ContentForm({ entry, initialValues = {}, initialBody = "", onSubmit, on
|
|
|
1227
1430
|
};
|
|
1228
1431
|
}, [guardUnsaved, dirty, submitting]);
|
|
1229
1432
|
const validate = react.useCallback(() => {
|
|
1230
|
-
const
|
|
1433
|
+
const frontmatter = toFrontmatter(specs, state, hiddenValues);
|
|
1231
1434
|
const result = entry.schema.safeParse(frontmatter);
|
|
1232
1435
|
setIssues(result.success ? [] : mapIssues(result.error.issues, specs));
|
|
1233
1436
|
return result;
|
|
@@ -1238,10 +1441,9 @@ function ContentForm({ entry, initialValues = {}, initialBody = "", onSubmit, on
|
|
|
1238
1441
|
setAttempted(true);
|
|
1239
1442
|
const result = validate();
|
|
1240
1443
|
if (!result.success) return;
|
|
1241
|
-
const { files } = toFrontmatter(specs, state, hiddenValues);
|
|
1242
1444
|
setSubmitting(true);
|
|
1243
1445
|
try {
|
|
1244
|
-
await onSubmit({ frontmatter: result.data, body,
|
|
1446
|
+
await onSubmit({ frontmatter: result.data, body, images: pictures.toSubmission() });
|
|
1245
1447
|
} finally {
|
|
1246
1448
|
setSubmitting(false);
|
|
1247
1449
|
}
|
|
@@ -1263,9 +1465,11 @@ function ContentForm({ entry, initialValues = {}, initialBody = "", onSubmit, on
|
|
|
1263
1465
|
spec: s,
|
|
1264
1466
|
value: state[s.name],
|
|
1265
1467
|
issues: byField.get(s.name) ?? [],
|
|
1266
|
-
current: typeof initialValues[s.name] === "string" ? initialValues[s.name] : void 0,
|
|
1267
1468
|
options: optionsFor(s),
|
|
1268
1469
|
renderLabel,
|
|
1470
|
+
picked: pictures.images[s.name],
|
|
1471
|
+
onPickImage: pickImage?.(s.name),
|
|
1472
|
+
onRemoveImage: removeImage(s.name),
|
|
1269
1473
|
onChange: (v) => onFieldChange(s.name, v),
|
|
1270
1474
|
onBlur: () => {
|
|
1271
1475
|
if (attempted) validate();
|
|
@@ -1286,7 +1490,9 @@ function ContentForm({ entry, initialValues = {}, initialBody = "", onSubmit, on
|
|
|
1286
1490
|
placeholder: bodySpec?.placeholder ?? "Write in Markdown. Headings with ##, links as [text](url).",
|
|
1287
1491
|
renderPreview,
|
|
1288
1492
|
previewClassName,
|
|
1289
|
-
termComponent
|
|
1493
|
+
termComponent,
|
|
1494
|
+
onUploadImage,
|
|
1495
|
+
previewSrc
|
|
1290
1496
|
}
|
|
1291
1497
|
)
|
|
1292
1498
|
] }),
|
|
@@ -1306,9 +1512,10 @@ function sameValue(a, b) {
|
|
|
1306
1512
|
|
|
1307
1513
|
exports.ChipsInput = ChipsInput;
|
|
1308
1514
|
exports.ContentForm = ContentForm;
|
|
1515
|
+
exports.IMAGE_HINT = IMAGE_HINT;
|
|
1516
|
+
exports.ImageInput = ImageInput;
|
|
1309
1517
|
exports.LengthGauge = LengthGauge;
|
|
1310
1518
|
exports.MarkdownEditor = MarkdownEditor;
|
|
1311
|
-
exports.PENDING_UPLOAD_PREFIX = PENDING_UPLOAD_PREFIX;
|
|
1312
1519
|
exports.TOOLS = TOOLS;
|
|
1313
1520
|
exports.slugify = slugify;
|
|
1314
1521
|
//# sourceMappingURL=index.cjs.map
|