@canvas-digital/blog-sdk 2.0.4 → 2.0.5
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/components/admin/AdminPostDetail.js +5 -5
- package/dist/components/admin/BrandSettings.js +5 -5
- package/dist/components/admin/BrandSwitcher.js +1 -1
- package/dist/components/admin/Chips.js +5 -5
- package/dist/components/admin/CommentsManager.js +6 -6
- package/dist/components/admin/Dashboard.js +1 -1
- package/dist/components/admin/MediaLibrary.js +5 -5
- package/dist/components/admin/MediaPicker.js +4 -4
- package/dist/components/admin/PostList.js +3 -3
- package/dist/components/admin/PostsTable.d.ts.map +1 -1
- package/dist/components/admin/PostsTable.js +10 -10
- package/dist/components/admin/Shell.js +3 -3
- package/dist/components/admin/TermManager.js +4 -4
- package/dist/components/admin/UsersManager.js +3 -3
- package/dist/components/admin/charts.js +7 -7
- package/dist/components/admin/editor/PostEditor.js +16 -16
- package/dist/components/admin/editor/RichTextEditor.js +8 -8
- package/dist/components/admin/editor/YoastCollapsible.js +2 -2
- package/dist/components/admin/editor/YoastInsightsPanel.js +1 -1
- package/dist/components/admin/editor/YoastInternalLinksPanel.js +1 -1
- package/dist/components/admin/editor/YoastSeoAnalysisPanel.js +6 -6
- package/dist/components/admin/editor/YoastSeoMetabox.js +8 -8
- package/dist/components/admin/editor/YoastSnippetField.js +2 -2
- package/dist/components/admin/internal/States.js +3 -3
- package/dist/components/admin/seo/Ga4Settings.js +6 -6
- package/dist/components/admin/seo/RedirectAnalyticsDashboard.js +1 -1
- package/dist/components/admin/seo/RedirectDetail.js +6 -6
- package/dist/components/admin/seo/RedirectForm.js +1 -1
- package/dist/components/admin/seo/RedirectList.js +4 -4
- package/dist/components/admin/seo/RobotsEditor.js +1 -1
- package/dist/components/admin/seo/shared.js +2 -2
- package/dist/components/admin/tools/ToolsHub.js +4 -4
- package/dist/components/user/BlogCard.js +3 -3
- package/dist/components/user/BlogCategories.js +1 -1
- package/dist/components/user/BlogDetail.js +1 -1
- package/dist/components/user/BlogList.js +1 -1
- package/dist/components/user/BlogSearch.js +2 -2
- package/dist/components/user/BlogTags.js +1 -1
- package/dist/components/user/FeaturedBlogs.js +1 -1
- package/dist/components/user/RelatedBlogs.js +1 -1
- package/dist/components/user/SiteMediaHero.js +1 -1
- package/dist/components/user/detail/ArticleDetailHero.js +5 -5
- package/dist/components/user/detail/ArticleRailDetails.js +2 -2
- package/dist/components/user/detail/CommentSection.js +2 -2
- package/dist/components/user/detail/HeroFadeSlide.js +1 -1
- package/dist/components/user/detail/ReadingProgress.js +1 -1
- package/dist/components/user/detail/ShareRow.js +1 -1
- package/dist/components/user/internal/States.js +4 -4
- package/dist/lib/lexical-doc.js +1 -1
- package/dist/pages/admin/BlogAdmin.js +7 -7
- package/dist/style.css +1018 -1018
- package/package.json +2 -1
|
@@ -74,12 +74,12 @@ export function TermManager({ collection, onOpenPosts, }) {
|
|
|
74
74
|
bio: row.bio,
|
|
75
75
|
});
|
|
76
76
|
};
|
|
77
|
-
return (_jsxs("div", { className: "
|
|
77
|
+
return (_jsxs("div", { className: "blog_split", children: [_jsxs("section", { className: "blog_panel blog_panel-form", children: [_jsx("div", { className: "blog_panel-head", children: _jsxs("h2", { children: ["Add ", labels.one] }) }), _jsxs("div", { className: "blog_panel-body blog_stack", children: [_jsxs("label", { className: "blog_fld-row", children: [_jsx("span", { className: "blog_fld-lbl", children: "Name" }), _jsx("input", { className: "blog_fld", value: fresh.name, onChange: (e) => setFresh((f) => ({ ...f, name: e.target.value })), onKeyDown: (e) => {
|
|
78
78
|
if (e.key === "Enter") {
|
|
79
79
|
e.preventDefault();
|
|
80
80
|
create();
|
|
81
81
|
}
|
|
82
|
-
} })] }), _jsxs("label", { className: "
|
|
82
|
+
} })] }), _jsxs("label", { className: "blog_fld-row", children: [_jsxs("span", { className: "blog_fld-lbl", children: ["Slug ", _jsx("em", { children: "optional \u2014 derived from the name" })] }), _jsx("input", { className: "blog_fld blog_mono", value: fresh.slug, onChange: (e) => setFresh((f) => ({ ...f, slug: e.target.value })) })] }), isCategories ? (_jsxs("label", { className: "blog_fld-row", children: [_jsx("span", { className: "blog_fld-lbl", children: "Description" }), _jsx("textarea", { className: "blog_fld", rows: 3, value: fresh.description, onChange: (e) => setFresh((f) => ({ ...f, description: e.target.value })) })] })) : null, isAuthors ? (_jsxs(_Fragment, { children: [_jsxs("label", { className: "blog_fld-row", children: [_jsx("span", { className: "blog_fld-lbl", children: "Job title" }), _jsx("input", { className: "blog_fld", value: fresh.jobTitle, onChange: (e) => setFresh((f) => ({ ...f, jobTitle: e.target.value })) })] }), _jsxs("label", { className: "blog_fld-row", children: [_jsx("span", { className: "blog_fld-lbl", children: "Bio" }), _jsx("textarea", { className: "blog_fld", rows: 3, value: fresh.bio, onChange: (e) => setFresh((f) => ({ ...f, bio: e.target.value })) })] })] })) : null, _jsxs("button", { type: "button", className: "blog_btn blog_btn-primary", disabled: pending || !fresh.name.trim(), onClick: create, children: ["Add ", labels.one] }), _jsx("p", { className: "blog_note", children: labels.blurb }), !store.activeBrandId ? _jsx("p", { className: "blog_note", children: "Currently viewing all brands \u2014 choose one to add." }) : null] })] }), _jsxs("section", { className: "blog_panel", children: [_jsxs("div", { className: "blog_panel-head", children: [_jsxs("h2", { children: [labels.many, " ", _jsx("span", { className: "blog_hint", children: tenantName })] }), _jsx("span", { className: "blog_hint", children: rows.length })] }), error ? (_jsx("p", { className: "blog_form-error", role: "alert", children: error })) : null, flash ? (_jsx("p", { className: "blog_form-ok", role: "status", children: flash })) : null, _jsx("div", { className: "blog_tw", children: _jsxs("table", { className: "blog_tbl", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "Name" }), _jsx("th", { children: "Slug" }), _jsx("th", { className: "blog_num", children: "Posts" }), _jsx("th", {})] }) }), _jsxs("tbody", { children: [rows.length === 0 ? (_jsx("tr", { children: _jsx("td", { colSpan: 4, children: _jsx("p", { className: "blog_d-empty", children: "Nothing here yet." }) }) })) : null, rows.map((row) => editing === row.id ? (_jsx("tr", { className: "blog_quick-row", children: _jsx("td", { colSpan: 4, children: _jsxs("div", { className: "blog_quick", children: [_jsxs("div", { className: "blog_quick-grid", children: [_jsxs("label", { children: [_jsx("span", { children: "Name" }), _jsx("input", { className: "blog_fld", value: draft.name, onChange: (e) => setDraft((d) => ({ ...d, name: e.target.value })) })] }), _jsxs("label", { children: [_jsx("span", { children: "Slug" }), _jsx("input", { className: "blog_fld blog_mono", value: draft.slug, onChange: (e) => setDraft((d) => ({ ...d, slug: e.target.value })) })] }), isCategories ? (_jsxs("label", { className: "blog_span-2", children: [_jsx("span", { children: "Description" }), _jsx("textarea", { className: "blog_fld", rows: 2, value: draft.description, onChange: (e) => setDraft((d) => ({ ...d, description: e.target.value })) })] })) : null, isAuthors ? (_jsxs(_Fragment, { children: [_jsxs("label", { children: [_jsx("span", { children: "Job title" }), _jsx("input", { className: "blog_fld", value: draft.jobTitle, onChange: (e) => setDraft((d) => ({ ...d, jobTitle: e.target.value })) })] }), _jsxs("label", { className: "blog_span-2", children: [_jsx("span", { children: "Bio" }), _jsx("textarea", { className: "blog_fld", rows: 2, value: draft.bio, onChange: (e) => setDraft((d) => ({ ...d, bio: e.target.value })) })] })] })) : null] }), _jsxs("div", { className: "blog_btn-row", children: [_jsx("button", { type: "button", className: "blog_btn blog_btn-primary", disabled: pending, onClick: () => run(() => store.saveTerm({
|
|
83
83
|
collection,
|
|
84
84
|
id: row.id,
|
|
85
85
|
brandId: row.brandId,
|
|
@@ -88,11 +88,11 @@ export function TermManager({ collection, onOpenPosts, }) {
|
|
|
88
88
|
description: draft.description,
|
|
89
89
|
jobTitle: draft.jobTitle,
|
|
90
90
|
bio: draft.bio,
|
|
91
|
-
}), "Saved."), children: "Save" }), _jsx("button", { type: "button", className: "
|
|
91
|
+
}), "Saved."), children: "Save" }), _jsx("button", { type: "button", className: "blog_btn", onClick: () => setEditing(null), children: "Cancel" })] })] }) }) }, row.id)) : (_jsxs("tr", { children: [_jsxs("td", { children: [_jsx("span", { className: "blog_row-title", children: row.name }), row.jobTitle ? _jsx("span", { className: "blog_cell-sub", children: row.jobTitle }) : null, row.description ? _jsx("span", { className: "blog_cell-sub", children: row.description }) : null, _jsxs("div", { className: "blog_row-actions", children: [_jsx("button", { type: "button", onClick: () => startEdit(row), children: "Edit" }), _jsx("button", { type: "button", className: "blog_danger", onClick: () => {
|
|
92
92
|
if (window.confirm(`Delete “${row.name}”?`)) {
|
|
93
93
|
run(() => store.deleteTerm(collection, row.id), `${row.name} deleted.`);
|
|
94
94
|
}
|
|
95
|
-
}, children: "Delete" })] })] }), _jsx("td", { className: "
|
|
95
|
+
}, children: "Delete" })] })] }), _jsx("td", { className: "blog_mono blog_muted", children: row.slug }), _jsx("td", { className: "blog_num", children: row.count ? (_jsx("button", { type: "button", className: "blog_btn blog_btn-ghost blog_btn-sm", onClick: () => onOpenPosts?.(row.id, row.name), children: row.count })) : (0) }), _jsx("td", {})] }, row.id)))] })] }) })] })] }));
|
|
96
96
|
}
|
|
97
97
|
export function CategoryManager() {
|
|
98
98
|
return _jsx(TermManager, { collection: "categories" });
|
|
@@ -89,8 +89,8 @@ export function UsersManager() {
|
|
|
89
89
|
: d.roles.filter((r) => r.tenant !== tenantId),
|
|
90
90
|
}));
|
|
91
91
|
};
|
|
92
|
-
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "
|
|
92
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "blog_filterbar", children: [_jsx("button", { type: "button", className: "blog_btn blog_btn-primary", onClick: create, children: "\uFF0B Add person" }), _jsxs("span", { className: "blog_note", children: [rows.length, " accounts"] })] }), error && !open ? (_jsx("p", { className: "blog_form-error", role: "alert", children: error })) : null, flash ? (_jsx("p", { className: "blog_form-ok", role: "status", children: flash })) : null, _jsx("section", { className: "blog_panel", children: _jsx("div", { className: "blog_tw", children: _jsxs("table", { className: "blog_tbl", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "Person" }), _jsx("th", { children: "Platform role" }), _jsx("th", { children: "Brand access" }), _jsx("th", {})] }) }), _jsx("tbody", { children: rows.map((row) => (_jsxs("tr", { children: [_jsxs("td", { children: [_jsxs("span", { className: "blog_row-title", children: [row.name, row.isSelf ? _jsx("span", { className: "blog_chip blog_chip-brand", children: "you" }) : null] }), _jsx("span", { className: "blog_cell-sub blog_mono", children: row.email }), _jsxs("div", { className: "blog_row-actions", children: [_jsx("button", { type: "button", onClick: () => edit(row), children: "Edit" }), row.isSelf ? null : (_jsx("button", { type: "button", className: "blog_danger", onClick: () => remove(row), children: "Delete" }))] })] }), _jsx("td", { className: "blog_muted", children: GLOBAL_LABEL[row.globalRole] }), _jsx("td", { children: row.roles.length === 0 ? (_jsx("span", { className: "blog_muted", children: "\u2014" })) : (_jsx("div", { className: "blog_chips", children: row.roles.map((r) => (_jsxs("span", { className: "blog_chip blog_chip-brand", children: [r.tenantName, ": ", r.role.replace("-", " ")] }, `${row.id}-${r.tenant}`))) })) }), _jsx("td", {})] }, row.id))) })] }) }) }), open ? (_jsx("div", { className: "blog_modal-backdrop", onMouseDown: () => setOpen(false), children: _jsxs("div", { className: "blog_modal", role: "dialog", "aria-modal": "true", "aria-label": "Account", onMouseDown: (e) => e.stopPropagation(), children: [_jsxs("header", { className: "blog_modal-head", children: [_jsx("h2", { children: draft.id ? "Edit account" : "Add person" }), _jsx("button", { type: "button", className: "blog_icon-btn", onClick: () => setOpen(false), "aria-label": "Close", children: "\u2715" })] }), _jsxs("div", { className: "blog_modal-body blog_stack", children: [error ? (_jsx("p", { className: "blog_form-error", role: "alert", children: error })) : null, _jsxs("label", { className: "blog_fld-row", children: [_jsx("span", { className: "blog_fld-lbl", children: "Name" }), _jsx("input", { className: "blog_fld", value: draft.name, onChange: (e) => setDraft((d) => ({ ...d, name: e.target.value })) })] }), _jsxs("label", { className: "blog_fld-row", children: [_jsx("span", { className: "blog_fld-lbl", children: "Email" }), _jsx("input", { className: "blog_fld blog_mono", type: "email", value: draft.email, onChange: (e) => setDraft((d) => ({ ...d, email: e.target.value })) })] }), _jsxs("label", { className: "blog_fld-row", children: [_jsxs("span", { className: "blog_fld-lbl", children: [draft.id ? "New password" : "Starting password", " ", _jsx("em", { children: "at least 12 characters" })] }), _jsx("input", { className: "blog_fld", type: "password", autoComplete: "new-password", placeholder: draft.id ? "Leave blank to keep the current one" : "", value: draft.password, onChange: (e) => setDraft((d) => ({ ...d, password: e.target.value })) })] }), _jsxs("label", { className: "blog_fld-row", children: [_jsx("span", { className: "blog_fld-lbl", children: "Platform role" }), _jsxs("select", { className: "blog_fld", value: draft.globalRole, onChange: (e) => setDraft((d) => ({ ...d, globalRole: e.target.value })), children: [_jsx("option", { value: "none", children: "Brand roles only" }), _jsx("option", { value: "analyst", children: "Analyst \u2014 read-only across every brand" }), _jsx("option", { value: "platform-admin", children: "Platform admin \u2014 full control" })] })] }), _jsxs("div", { className: "blog_fld-row", children: [_jsx("span", { className: "blog_fld-lbl", children: "Brand access" }), _jsx("div", { className: "blog_role-grid", children: tenants.map((t) => {
|
|
93
93
|
const current = draft.roles.find((r) => r.tenant === t.id)?.role ?? "";
|
|
94
|
-
return (_jsxs("div", { className: "
|
|
95
|
-
}) })] })] }), _jsxs("footer", { className: "
|
|
94
|
+
return (_jsxs("div", { className: "blog_role-row", children: [_jsx("span", { children: t.name }), _jsxs("select", { className: "blog_fld blog_fld-sm", value: current, onChange: (e) => setRole(t.id, e.target.value), "aria-label": `Role in ${t.name}`, children: [_jsx("option", { value: "", children: "No access" }), TENANT_ROLES.map((role) => (_jsx("option", { value: role, children: role.replace("-", " ") }, role)))] }), _jsx("span", { className: "blog_note", children: current ? ROLE_HELP[current] : "" })] }, t.id));
|
|
95
|
+
}) })] })] }), _jsxs("footer", { className: "blog_modal-foot", children: [_jsx("span", { className: "blog_note", children: "Publishing rights come from the brand role, never from the platform role." }), _jsxs("div", { className: "blog_modal-foot-actions", children: [_jsx("button", { type: "button", className: "blog_btn", onClick: () => setOpen(false), children: "Cancel" }), _jsx("button", { type: "button", className: "blog_btn blog_btn-primary", onClick: submit, disabled: pending, children: pending ? "Saving…" : draft.id ? "Save account" : "Create account" })] })] })] }) })) : null] }));
|
|
96
96
|
}
|
|
@@ -39,7 +39,7 @@ export const TrendArea = ({ data, height = 220 }) => {
|
|
|
39
39
|
setHover(i >= 0 && i < data.length ? i : null);
|
|
40
40
|
};
|
|
41
41
|
const active = hover != null ? data[hover] : null;
|
|
42
|
-
return (_jsxs("div", { className: "
|
|
42
|
+
return (_jsxs("div", { className: "blog_chart-holder", children: [_jsxs("svg", { viewBox: `0 0 ${W} ${H}`, className: "blog_chart-svg", role: "img", "aria-label": `Daily views over the last ${data.length} days`, onMouseMove: onMove, onMouseLeave: () => setHover(null), children: [_jsx("defs", { children: _jsxs("linearGradient", { id: gradId, x1: "0", y1: "0", x2: "0", y2: "1", children: [_jsx("stop", { offset: "0%", stopColor: "var(--viz-1)", stopOpacity: "0.26" }), _jsx("stop", { offset: "100%", stopColor: "var(--viz-1)", stopOpacity: "0.02" })] }) }), ticks.map((t) => (_jsxs("g", { children: [_jsx("line", { x1: pad.l, x2: W - pad.r, y1: y(t), y2: y(t), stroke: "var(--viz-grid)", strokeWidth: "1" }), _jsx("text", { x: pad.l - 9, y: y(t) + 4, textAnchor: "end", className: "blog_axis-label", fill: "var(--viz-muted)", children: fmt.format(Math.round(t)) })] }, t))), _jsx("path", { d: area, fill: `url(#${gradId})` }), _jsx("path", { d: line, fill: "none", stroke: "var(--viz-1)", strokeWidth: "2", strokeLinejoin: "round" }), [0, Math.floor(data.length / 2), data.length - 1].map((i) => (_jsx("text", { x: x(i), y: H - 8, textAnchor: i === 0 ? 'start' : i === data.length - 1 ? 'end' : 'middle', className: "blog_axis-label", fill: "var(--viz-muted)", children: data[i] ? shortDate(data[i].date) : '' }, i))), active && hover != null ? (_jsxs("g", { children: [_jsx("line", { x1: x(hover), x2: x(hover), y1: pad.t, y2: pad.t + ih, stroke: "var(--viz-axis)", strokeWidth: "1" }), _jsx("circle", { cx: x(hover), cy: y(active.views), r: "5", fill: "var(--viz-1)", stroke: "var(--d-surface)", strokeWidth: "2" })] })) : null] }), active && hover != null ? (_jsxs("div", { className: "blog_chart-tip", style: { left: `${(x(hover) / W) * 100}%` }, role: "status", children: [_jsx("div", { className: "blog_tip-date", children: shortDate(active.date) }), _jsxs("div", { className: "blog_tip-row", children: [_jsx("span", { className: "blog_tip-swatch", style: { background: 'var(--viz-1)' } }), fmt.format(active.views), " views"] }), _jsxs("div", { className: "blog_tip-sub", children: [fmt.format(active.uniques), " unique"] })] })) : null] }));
|
|
43
43
|
};
|
|
44
44
|
/* ------------------------------------------------------------------ */
|
|
45
45
|
/* Sparkline for stat cards */
|
|
@@ -51,7 +51,7 @@ export const Sparkline = ({ data }) => {
|
|
|
51
51
|
const pts = data
|
|
52
52
|
.map((v, i) => `${(i / Math.max(1, data.length - 1)) * W},${H - (v / max) * (H - 4) - 2}`)
|
|
53
53
|
.join(' ');
|
|
54
|
-
return (_jsxs("svg", { viewBox: `0 0 ${W} ${H}`, className: "
|
|
54
|
+
return (_jsxs("svg", { viewBox: `0 0 ${W} ${H}`, className: "blog_spark", "aria-hidden": "true", children: [_jsx("polyline", { points: pts, fill: "none", stroke: "var(--viz-1)", strokeWidth: "2", strokeLinejoin: "round", strokeLinecap: "round" }), data.length ? (_jsx("circle", { cx: W, cy: H - (data[data.length - 1] / max) * (H - 4) - 2, r: "2.5", fill: "var(--viz-1)" })) : null] }));
|
|
55
55
|
};
|
|
56
56
|
/* ------------------------------------------------------------------ */
|
|
57
57
|
/* Horizontal bar list — direct-labelled, which is also the contrast */
|
|
@@ -60,11 +60,11 @@ export const Sparkline = ({ data }) => {
|
|
|
60
60
|
export const BarList = ({ items, colorVar = 'var(--viz-1)', unit = '', hrefFor, }) => {
|
|
61
61
|
const max = Math.max(1, ...items.map((i) => i.value));
|
|
62
62
|
if (items.length === 0)
|
|
63
|
-
return _jsx("p", { className: "
|
|
64
|
-
return (_jsx("ul", { className: "
|
|
63
|
+
return _jsx("p", { className: "blog_d-empty", children: "No data yet." });
|
|
64
|
+
return (_jsx("ul", { className: "blog_barlist", children: items.map((item) => {
|
|
65
65
|
const href = hrefFor?.(item.label);
|
|
66
|
-
const label = (_jsxs(_Fragment, { children: [_jsx("span", { className: "
|
|
67
|
-
return (_jsxs("li", { className: "
|
|
66
|
+
const label = (_jsxs(_Fragment, { children: [_jsx("span", { className: "blog_bl-label", title: item.label, children: item.label }), _jsxs("span", { className: "blog_bl-value", children: [fmt.format(item.value), unit] })] }));
|
|
67
|
+
return (_jsxs("li", { className: "blog_bl-row", children: [_jsx("div", { className: "blog_bl-head", children: href ? (_jsx("a", { href: href, className: "blog_bl-link", children: label })) : (label) }), _jsx("div", { className: "blog_bl-track", children: _jsx("div", { className: "blog_bl-fill", style: { width: `${(item.value / max) * 100}%`, background: colorVar } }) })] }, item.label));
|
|
68
68
|
}) }));
|
|
69
69
|
};
|
|
70
70
|
/* ------------------------------------------------------------------ */
|
|
@@ -72,7 +72,7 @@ export const BarList = ({ items, colorVar = 'var(--viz-1)', unit = '', hrefFor,
|
|
|
72
72
|
/* ------------------------------------------------------------------ */
|
|
73
73
|
export const Funnel = ({ stages, }) => {
|
|
74
74
|
const max = Math.max(1, ...stages.map((s) => s.value));
|
|
75
|
-
return (_jsx("ul", { className: "
|
|
75
|
+
return (_jsx("ul", { className: "blog_funnel", children: stages.map((s) => (_jsxs("li", { children: [_jsxs("div", { className: "blog_fn-head", children: [_jsx("span", { className: "blog_fn-label", children: s.label }), _jsx("span", { className: "blog_fn-value", children: fmt.format(s.value) })] }), _jsx("div", { className: "blog_fn-track", children: _jsx("div", { className: "blog_fn-fill", style: {
|
|
76
76
|
width: `${Math.max(2, (s.value / max) * 100)}%`,
|
|
77
77
|
background: `var(--ord-${s.tone})`,
|
|
78
78
|
} }) })] }, s.label))) }));
|
|
@@ -7,7 +7,7 @@ import { analyseReadability, analyseSeo, summariseTone, toneLabel, } from "../..
|
|
|
7
7
|
import { YoastSeoMetabox } from "./YoastSeoMetabox";
|
|
8
8
|
import { RichTextEditor } from "./RichTextEditor";
|
|
9
9
|
import { MediaPicker } from "../MediaPicker";
|
|
10
|
-
const ScoreDot = ({ tone }) => (_jsx("span", { className: `
|
|
10
|
+
const ScoreDot = ({ tone }) => (_jsx("span", { className: `blog_seo-dot blog_seo-dot-${tone}`, "aria-hidden": "true" }));
|
|
11
11
|
export function PostEditor({ postId, onDone, }) {
|
|
12
12
|
const store = useAdminMock();
|
|
13
13
|
const existing = postId ? store.getPost(postId) : undefined;
|
|
@@ -359,23 +359,23 @@ export function PostEditor({ postId, onDone, }) {
|
|
|
359
359
|
: "Immediately";
|
|
360
360
|
const permalink = `/blog/${slug || slugify(title) || "…"}`;
|
|
361
361
|
const categoryOptions = categoryTab === "popular" ? categories.slice(0, 8) : categories;
|
|
362
|
-
return (_jsxs("div", { className: "
|
|
362
|
+
return (_jsxs("div", { className: "blog_ed-layout", children: [_jsxs("div", { className: "blog_ed-main", children: [error ? (_jsx("p", { className: "blog_form-error blog_ed-error", role: "alert", children: error })) : null, flash ? (_jsx("p", { className: "blog_form-ok", role: "status", children: flash })) : null, _jsx("div", { className: "blog_ed-title-wrap", children: _jsx("input", { className: "blog_fld blog_ed-title", value: title, onChange: (e) => {
|
|
363
363
|
setTitle(e.target.value);
|
|
364
364
|
if (!existing && !slugEditing)
|
|
365
365
|
setSlug(slugify(e.target.value));
|
|
366
|
-
}, placeholder: "Add title", "aria-label": "Post title" }) }), _jsxs("div", { className: "
|
|
366
|
+
}, placeholder: "Add title", "aria-label": "Post title" }) }), _jsxs("div", { className: "blog_ed-permalink", children: [_jsx("span", { className: "blog_lbl", children: "Permalink:" }), slugEditing ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "blog_mono", children: "/blog/" }), _jsx("input", { className: "blog_fld blog_mono", style: { maxWidth: 220 }, value: slug, onChange: (e) => setSlug(e.target.value), onBlur: () => {
|
|
367
367
|
setSlug(slugify(slug || title));
|
|
368
368
|
setSlugEditing(false);
|
|
369
|
-
} })] })) : (_jsxs(_Fragment, { children: [_jsx("span", { className: "
|
|
369
|
+
} })] })) : (_jsxs(_Fragment, { children: [_jsx("span", { className: "blog_mono blog_ed-permalink-value", children: permalink }), _jsx("button", { type: "button", className: "blog_btn blog_btn-tiny", onClick: () => setSlugEditing(true), children: "Edit" })] })), _jsxs("span", { className: "blog_ed-bar-meta", style: { marginLeft: "auto" }, children: [wordCount, " words"] })] }), _jsx(RichTextEditor, { initialHtml: existing?.contentHtml ?? contentHtml, onChange: onContentChange, onPickImage: onPickImage, searchTargets: searchTargets }), _jsxs("div", { className: "blog_box", children: [_jsxs("div", { className: "blog_box-head", children: [_jsx("h3", { children: "Excerpt" }), _jsxs("span", { className: "blog_box-hint", children: [excerpt.length, "/300"] })] }), _jsx("div", { className: "blog_box-body", children: _jsx("textarea", { className: "blog_fld", rows: 3, maxLength: 300, value: excerpt, placeholder: "One or two sentences. Used on index pages and as the meta description fallback.", onChange: (e) => setExcerpt(e.target.value) }) })] }), _jsx(YoastSeoMetabox, { title: title, slug: slug, excerpt: excerpt, visibility: visibility, featuredImage: featuredImage, seo: seo, seoInput: seoInput, siteName: brand?.name ?? "My Site", categoryName: categoryName, contentText: contentText, contentHtml: contentHtml, publishedAt: publishedAt, scheduledFor: scheduledFor, fieldErrors: fieldErrors, onSeoChange: onSeoChange, onSlugChange: setSlug, onSlugBlur: () => setSlug(slugify(slug || title)), onPickOgImage: () => setPickerFor("og"), onClearFieldError: (field) => setFieldErrors((current) => {
|
|
370
370
|
const next = { ...current };
|
|
371
371
|
delete next[field];
|
|
372
372
|
return next;
|
|
373
|
-
}), onSetFieldError: (field, message) => setFieldErrors((current) => ({ ...current, [field]: message })) })] }), _jsxs("aside", { className: "
|
|
373
|
+
}), onSetFieldError: (field, message) => setFieldErrors((current) => ({ ...current, [field]: message })) })] }), _jsxs("aside", { className: "blog_ed-side", "aria-label": "Post settings", children: [_jsxs("div", { className: "blog_box blog_box-publish", children: [_jsx("div", { className: "blog_box-head", children: _jsx("h3", { children: "Publish" }) }), _jsxs("div", { className: "blog_box-body blog_stack", children: [_jsxs("div", { className: "blog_btn-row", children: [_jsx("button", { type: "button", className: "blog_btn", disabled: pending, onClick: () => persist(isLive ? "publish" : "draft"), children: pending ? "Saving…" : isLive ? "Update" : "Save Draft" }), _jsx("button", { type: "button", className: "blog_btn", disabled: pending, title: "Preview", children: "Preview" })] }), _jsxs("div", { className: "blog_facts blog_wp-facts", children: [_jsxs("div", { className: "blog_fact-row", children: [_jsx("span", { className: "blog_fact-lbl", children: "Status:" }), _jsxs("span", { className: "blog_fact-val", children: [isLive ? "Published" : "Draft", " ", _jsx("button", { type: "button", className: "blog_linkish", onClick: () => setShowStatus((v) => !v), children: "Edit" })] })] }), showStatus ? (_jsx("div", { className: "blog_fact-edit", children: _jsxs("select", { className: "blog_fld blog_fld-sm", value: isLive ? "published" : "draft", onChange: (e) => {
|
|
374
374
|
if (e.target.value === "published")
|
|
375
375
|
persist("publish");
|
|
376
376
|
else if (isLive)
|
|
377
377
|
persist("unpublish");
|
|
378
|
-
}, children: [_jsx("option", { value: "draft", children: "Draft" }), _jsx("option", { value: "published", children: "Published" })] }) })) : null, _jsxs("div", { className: "
|
|
378
|
+
}, children: [_jsx("option", { value: "draft", children: "Draft" }), _jsx("option", { value: "published", children: "Published" })] }) })) : null, _jsxs("div", { className: "blog_fact-row", children: [_jsx("span", { className: "blog_fact-lbl", children: "Visibility:" }), _jsxs("span", { className: "blog_fact-val", children: [visibility === "private" ? "Private" : "Public", " ", _jsx("button", { type: "button", className: "blog_linkish", onClick: () => setShowVisibility((v) => !v), children: "Edit" })] })] }), showVisibility ? (_jsx("div", { className: "blog_fact-edit", children: _jsxs("select", { className: "blog_fld blog_fld-sm", value: visibility, onChange: (e) => setVisibility(e.target.value), children: [_jsx("option", { value: "public", children: "Public" }), _jsx("option", { value: "private", children: "Private" })] }) })) : null, _jsxs("div", { className: "blog_fact-row", children: [_jsx("span", { className: "blog_fact-lbl", children: scheduledFor && !isLive ? "Schedule:" : "Publish:" }), _jsxs("span", { className: "blog_fact-val", children: [publishWhenLabel, " ", _jsx("button", { type: "button", className: "blog_linkish", onClick: () => setShowSchedule((v) => !v), children: "Edit" })] })] })] }), showSchedule ? (_jsxs("div", { className: "blog_stack", children: [_jsx("input", { type: "datetime-local", className: "blog_fld", value: scheduleWhen, onChange: (e) => setScheduleWhen(e.target.value), "aria-label": "Publish date and time" }), _jsxs("div", { className: "blog_btn-row", children: [_jsx("button", { type: "button", className: "blog_btn blog_btn-primary blog_btn-tiny", disabled: pending, onClick: () => persist("schedule"), children: "OK" }), _jsx("button", { type: "button", className: "blog_btn blog_btn-tiny", onClick: () => setShowSchedule(false), children: "Cancel" }), scheduledFor ? (_jsx("button", { type: "button", className: "blog_btn blog_btn-tiny", disabled: pending, onClick: () => persist("unschedule"), children: "Clear" })) : null] })] })) : null, _jsxs("div", { className: "blog_seo-summary blog_publish-seo-summary blog_wp-publish-scores", children: [_jsxs("div", { className: "blog_fact-row", children: [_jsx("span", { className: "blog_fact-lbl", children: "SEO analysis:" }), _jsxs("span", { className: "blog_fact-val", children: [_jsx(ScoreDot, { tone: seoTone }), " ", toneLabel(seoTone)] })] }), _jsxs("div", { className: "blog_fact-row", children: [_jsx("span", { className: "blog_fact-lbl", children: "Readability:" }), _jsxs("span", { className: "blog_fact-val", children: [_jsx(ScoreDot, { tone: readTone }), " ", toneLabel(readTone)] })] })] }), _jsxs("div", { className: "blog_publish-foot", children: [_jsxs("div", { className: "blog_publish-foot-left", children: [postIdCurrent ? (_jsx("button", { type: "button", className: "blog_linkish", disabled: pending, onClick: copyToDraft, children: "Copy to a new draft" })) : null, postIdCurrent ? (_jsx("button", { type: "button", className: "blog_btn-danger-link", disabled: pending, onClick: moveToTrash, children: "Move to Trash" })) : null] }), !isLive ? (_jsx("button", { type: "button", className: "blog_btn blog_btn-primary", disabled: pending, onClick: () => persist("publish"), children: pending ? "Publishing…" : "Publish" })) : null] })] })] }), _jsxs("div", { className: "blog_box", children: [_jsx("div", { className: "blog_box-head", children: _jsx("h3", { children: "Format" }) }), _jsx("div", { className: "blog_box-body", children: _jsx("div", { className: "blog_pick-list blog_pick-list-auto", children: [
|
|
379
379
|
["standard", "Standard"],
|
|
380
380
|
["video", "Video"],
|
|
381
381
|
["audio", "Audio"],
|
|
@@ -383,23 +383,23 @@ export function PostEditor({ postId, onDone, }) {
|
|
|
383
383
|
["gallery", "Gallery"],
|
|
384
384
|
["image", "Image"],
|
|
385
385
|
["link", "Link"],
|
|
386
|
-
].map(([value, label]) => (_jsxs("label", { className: "
|
|
386
|
+
].map(([value, label]) => (_jsxs("label", { className: "blog_pick-row", children: [_jsx("input", { type: "radio", name: "postFormat", checked: postFormat === value, onChange: () => setPostFormat(value) }), label] }, value))) }) })] }), _jsxs("div", { className: "blog_box", children: [_jsx("div", { className: "blog_box-head", children: _jsx("h3", { children: "Categories" }) }), _jsxs("div", { className: "blog_box-body blog_stack", children: [_jsxs("div", { className: "blog_yoast-preview-modes blog_wp-cat-tabs", role: "tablist", "aria-label": "Category list", children: [_jsx("button", { type: "button", role: "tab", className: `blog_yoast-preview-mode${categoryTab === "all" ? " blog_is-active" : ""}`, "aria-selected": categoryTab === "all", onClick: () => setCategoryTab("all"), children: "All Categories" }), _jsx("button", { type: "button", role: "tab", className: `blog_yoast-preview-mode${categoryTab === "popular" ? " blog_is-active" : ""}`, "aria-selected": categoryTab === "popular", onClick: () => setCategoryTab("popular"), children: "Most Used" })] }), _jsx("div", { className: "blog_pick-list", children: categoryOptions.length === 0 ? (_jsx("p", { className: "blog_note", children: "No categories in this brand yet." })) : (categoryOptions.map((c) => (_jsxs("label", { className: "blog_pick-row", children: [_jsx("input", { type: "checkbox", checked: categoryIds.includes(c.id), onChange: () => setCategoryIds((ids) => ids.includes(c.id) ? ids.filter((x) => x !== c.id) : [...ids, c.id]) }), c.name] }, c.id)))) }), categoryIds.length > 1 ? (_jsxs("p", { className: "blog_note", children: [categoryIds.length, " selected \u00B7 first selected is used for breadcrumbs."] })) : null, _jsx("button", { type: "button", className: "blog_yoast-link-btn", onClick: () => document.getElementById("new-category-input")?.focus(), children: "+ Add New Category" }), _jsxs("div", { className: "blog_btn-row", children: [_jsx("input", { id: "new-category-input", className: "blog_fld", placeholder: "New category", value: newCategory, onChange: (e) => setNewCategory(e.target.value), onKeyDown: (e) => {
|
|
387
387
|
if (e.key === "Enter") {
|
|
388
388
|
e.preventDefault();
|
|
389
389
|
addCategory();
|
|
390
390
|
}
|
|
391
|
-
} }), _jsx("button", { type: "button", className: "
|
|
391
|
+
} }), _jsx("button", { type: "button", className: "blog_btn", onClick: addCategory, disabled: !newCategory.trim(), children: "Add" })] })] })] }), _jsxs("div", { className: "blog_box", children: [_jsx("div", { className: "blog_box-head", children: _jsx("h3", { children: "Tags" }) }), _jsxs("div", { className: "blog_box-body blog_stack", children: [_jsxs("div", { className: "blog_tokens", children: [tagIds.map((id) => {
|
|
392
392
|
const tag = tags.find((t) => t.id === id);
|
|
393
|
-
return (_jsxs("span", { className: "
|
|
394
|
-
}), newTags.map((name) => (_jsxs("span", { className: "
|
|
393
|
+
return (_jsxs("span", { className: "blog_token", children: [tag?.name ?? "tag", _jsx("button", { type: "button", "aria-label": `Remove ${tag?.name ?? "tag"}`, onClick: () => setTagIds((list) => list.filter((t) => t !== id)), children: "\u2715" })] }, id));
|
|
394
|
+
}), newTags.map((name) => (_jsxs("span", { className: "blog_token blog_token-new", children: [name, _jsx("button", { type: "button", "aria-label": `Remove ${name}`, onClick: () => setNewTags((list) => list.filter((t) => t !== name)), children: "\u2715" })] }, name)))] }), _jsxs("div", { className: "blog_btn-row", children: [_jsx("input", { className: "blog_fld", placeholder: "Add a tag and press Enter", value: tagDraft, list: "tag-suggestions", onChange: (e) => setTagDraft(e.target.value), onKeyDown: (e) => {
|
|
395
395
|
if (e.key === "Enter" || e.key === ",") {
|
|
396
396
|
e.preventDefault();
|
|
397
397
|
addTagFromDraft();
|
|
398
398
|
}
|
|
399
|
-
} }), _jsx("datalist", { id: "tag-suggestions", children: tags.map((t) => (_jsx("option", { value: t.name }, t.id))) }), _jsx("button", { type: "button", className: "
|
|
399
|
+
} }), _jsx("datalist", { id: "tag-suggestions", children: tags.map((t) => (_jsx("option", { value: t.name }, t.id))) }), _jsx("button", { type: "button", className: "blog_btn", onClick: addTagFromDraft, disabled: !tagDraft.trim(), children: "Add" })] }), tags.filter((t) => !tagIds.includes(t.id)).length ? (_jsxs("div", { className: "blog_tag-most-used", children: [_jsx("p", { className: "blog_note", children: "Most used" }), _jsx("div", { className: "blog_tag-cloud", children: tags
|
|
400
400
|
.filter((t) => !tagIds.includes(t.id))
|
|
401
401
|
.slice(0, 12)
|
|
402
|
-
.map((t) => (_jsx("button", { type: "button", className: "
|
|
402
|
+
.map((t) => (_jsx("button", { type: "button", className: "blog_tag-cloud-item", onClick: () => setTagIds((ids) => [...ids, t.id]), children: t.name }, t.id))) })] })) : null, _jsx("p", { className: "blog_note", children: "New tags are created when the post is saved." })] })] }), _jsxs("div", { className: "blog_box", children: [_jsxs("div", { className: "blog_box-head", children: [_jsx("h3", { children: "Featured image" }), _jsx("span", { className: "blog_box-hint", children: "optional" })] }), _jsxs("div", { className: `blog_box-body blog_drop-target${featuredDrag ? " blog_is-dragging" : ""}`, onDragOver: (e) => {
|
|
403
403
|
e.preventDefault();
|
|
404
404
|
setFeaturedDrag(true);
|
|
405
405
|
}, onDragLeave: () => setFeaturedDrag(false), onDrop: (e) => {
|
|
@@ -411,18 +411,18 @@ export function PostEditor({ postId, onDone, }) {
|
|
|
411
411
|
if (e.target.files?.length)
|
|
412
412
|
void uploadFeatured(e.target.files);
|
|
413
413
|
e.target.value = "";
|
|
414
|
-
} }), featuredImage ? (_jsxs("div", { className: "
|
|
414
|
+
} }), featuredImage ? (_jsxs("div", { className: "blog_stack", children: [_jsx("img", { className: "blog_feat", src: featuredImage.url, alt: featuredImage.alt || "" }), _jsxs("label", { className: "blog_fld-row blog_feat-alt", children: [_jsxs("span", { className: "blog_fld-lbl", children: ["Alt text ", _jsx("em", { className: "blog_fld-em", children: "recommended for accessibility" })] }), _jsx("textarea", { className: "blog_fld", rows: 2, value: featuredImage.alt, placeholder: "Describe the image for screen readers and SEO\u2026", onChange: (e) => {
|
|
415
415
|
setFeaturedImage((img) => (img ? { ...img, alt: e.target.value } : img));
|
|
416
416
|
setFeaturedAltSaved(false);
|
|
417
417
|
}, onBlur: () => {
|
|
418
418
|
if (featuredImage)
|
|
419
419
|
saveFeaturedAlt();
|
|
420
|
-
}, "aria-describedby": "feat-alt-hint" }), _jsx("span", { id: "feat-alt-hint", className: "
|
|
420
|
+
}, "aria-describedby": "feat-alt-hint" }), _jsx("span", { id: "feat-alt-hint", className: "blog_note blog_feat-alt-foot", children: featuredAltBusy
|
|
421
421
|
? "Saving alt text…"
|
|
422
422
|
: featuredAltSaved
|
|
423
423
|
? "Alt text saved to the media library."
|
|
424
|
-
: "Saved on the image itself — used on the public post and in SEO." })] }), _jsxs("div", { className: "
|
|
424
|
+
: "Saved on the image itself — used on the public post and in SEO." })] }), _jsxs("div", { className: "blog_btn-row", children: [_jsx("button", { type: "button", className: "blog_btn blog_btn-tiny", disabled: featuredAltBusy, onClick: () => saveFeaturedAlt(), children: featuredAltBusy ? "Saving…" : "Save alt text" }), _jsx("button", { type: "button", className: "blog_btn blog_btn-tiny", onClick: () => setPickerFor("featured"), children: "Replace image" }), _jsx("button", { type: "button", className: "blog_btn blog_btn-tiny", disabled: featuredBusy, onClick: () => featuredFileRef.current?.click(), children: featuredBusy ? "Uploading…" : "Upload" }), _jsx("button", { type: "button", className: "blog_btn blog_btn-tiny", onClick: () => {
|
|
425
425
|
setFeaturedImage(null);
|
|
426
426
|
setFeaturedAltSaved(false);
|
|
427
|
-
}, children: "Remove featured image" })] })] })) : (_jsxs("div", { className: "
|
|
427
|
+
}, children: "Remove featured image" })] })] })) : (_jsxs("div", { className: "blog_stack", children: [_jsx("button", { type: "button", className: "blog_btn blog_btn-block", onClick: () => setPickerFor("featured"), children: "Set featured image" }), _jsxs("button", { type: "button", className: "blog_dropzone-cta", disabled: featuredBusy, onClick: () => featuredFileRef.current?.click(), children: [_jsx("span", { className: "blog_dropzone-icon", "aria-hidden": "true", children: "\u2B06" }), _jsx("span", { children: featuredBusy ? "Uploading…" : "Or drop an image here to upload" }), _jsx("span", { className: "blog_note", children: "JPEG, PNG, WebP or AVIF \u00B7 up to 12 MB \u00B7 add alt text after upload" })] })] }))] })] }), _jsxs("div", { className: "blog_box", children: [_jsx("div", { className: "blog_box-head", children: _jsx("h3", { children: "Post Attributes" }) }), _jsx("div", { className: "blog_box-body blog_stack", children: _jsxs("label", { className: "blog_fld-row", children: [_jsx("span", { className: "blog_fld-lbl", children: "Template" }), _jsx("select", { className: "blog_fld", value: template, onChange: (e) => setTemplate(e.target.value), children: _jsx("option", { value: "default", children: "Default template" }) })] }) })] }), _jsxs("div", { className: "blog_box", children: [_jsx("div", { className: "blog_box-head", children: _jsx("h3", { children: "Authors" }) }), _jsxs("div", { className: "blog_box-body blog_stack", children: [_jsx("div", { className: "blog_pick-list", children: authors.length === 0 ? (_jsxs("p", { className: "blog_note", children: ["No authors for ", brand?.name ?? "this brand", " yet \u2014 create them on the Authors screen."] })) : (authors.map((a) => (_jsxs("label", { className: "blog_pick-row", children: [_jsx("input", { type: "checkbox", checked: authorIds.includes(a.id), onChange: (e) => setAuthorIds((ids) => e.target.checked ? [...ids, a.id] : ids.filter((x) => x !== a.id)) }), _jsxs("span", { children: [a.name, a.name === profile.name ? _jsx("span", { className: "blog_box-hint", children: " \u00B7 you" }) : null] })] }, a.id)))) }), _jsx("p", { className: "blog_note", children: "You are selected by default. Add or remove others anytime." }), _jsxs("label", { className: "blog_fld-row", children: [_jsx("span", { className: "blog_fld-lbl", children: "Author Box" }), _jsx("select", { className: "blog_fld", value: authorBox, onChange: (e) => setAuthorBox(e.target.value), children: _jsx("option", { value: "default", children: "Default Author Box" }) })] })] })] }), _jsxs("div", { className: "blog_box", children: [_jsx("div", { className: "blog_box-head", children: _jsx("h3", { children: "Discussion" }) }), _jsxs("div", { className: "blog_box-body blog_stack", children: [_jsxs("label", { className: "blog_pick-row", children: [_jsx("input", { type: "checkbox", checked: allowComments, onChange: (e) => setAllowComments(e.target.checked) }), _jsx("span", { children: "Allow comments on this post" })] }), _jsx("p", { className: "blog_note", children: "When checked, readers can leave comments on the published article. Uncheck to close the discussion without deleting existing comments." })] })] }), _jsxs("div", { className: "blog_box", children: [_jsx("div", { className: "blog_box-head", children: _jsx("h3", { children: "Table of Contents" }) }), _jsxs("div", { className: "blog_box-body blog_stack", children: [_jsxs("label", { className: "blog_pick-row", children: [_jsx("input", { type: "checkbox", checked: showToc, onChange: (e) => setShowToc(e.target.checked) }), _jsx("span", { children: "Show Table of Contents on this post" })] }), _jsx("p", { className: "blog_note", children: "When checked, the published article shows a right-side Table of Contents listing other published posts from this brand. Leave unchecked to hide the card." })] })] })] }), _jsx(MediaPicker, { open: pickerFor !== null, brandId: brandId || null, onClose: () => setPickerFor(null), onSelect: choosePicked, title: pickerFor === "og" ? "Social share image" : "Featured image" })] }));
|
|
428
428
|
}
|
|
@@ -386,7 +386,7 @@ export const RichTextEditor = ({ initialHtml, onChange, onPickImage, searchTarge
|
|
|
386
386
|
}, 220);
|
|
387
387
|
return () => clearTimeout(id);
|
|
388
388
|
}, [linkOpen, linkQuery, searchTargets]);
|
|
389
|
-
const linkPopover = linkOpen && linkPos && mode === 'visual' ? (_jsxs("div", { ref: linkPopoverRef, className: "
|
|
389
|
+
const linkPopover = linkOpen && linkPos && mode === 'visual' ? (_jsxs("div", { ref: linkPopoverRef, className: "blog_ed-link-popover", style: { top: linkPos.top, left: linkPos.left }, role: "dialog", "aria-label": "Insert link", onMouseDown: (e) => e.stopPropagation(), children: [linkSelection ? _jsxs("p", { className: "blog_ed-link-selection", children: ["Linking: \u201C", linkSelection, "\u201D"] }) : null, _jsxs("div", { className: "blog_ed-link-row", children: [_jsx("input", { className: "blog_fld", placeholder: "Paste URL or type to search", value: linkUrl || linkQuery, onChange: (e) => {
|
|
390
390
|
const v = e.target.value;
|
|
391
391
|
if (/^https?:\/\//i.test(v) || v.includes('.')) {
|
|
392
392
|
setLinkUrl(v);
|
|
@@ -404,13 +404,13 @@ export const RichTextEditor = ({ initialHtml, onChange, onPickImage, searchTarge
|
|
|
404
404
|
}
|
|
405
405
|
if (e.key === 'Escape')
|
|
406
406
|
closeLinkDialog();
|
|
407
|
-
}, autoFocus: true }), _jsx("button", { type: "button", className: "
|
|
407
|
+
}, autoFocus: true }), _jsx("button", { type: "button", className: "blog_btn blog_btn-primary blog_btn-tiny", onMouseDown: (e) => e.preventDefault(), onClick: applyExternalLink, disabled: !linkUrl.trim(), children: "Apply" })] }), _jsxs("label", { className: "blog_ed-link-tab", children: [_jsx("input", { type: "checkbox", checked: linkNewTab, onChange: (e) => setLinkNewTab(e.target.checked) }), "Open in new tab"] }), hits.length ? (_jsx("ul", { className: "blog_ed-link-hits", children: hits.map((hit) => (_jsx("li", { children: _jsxs("button", { type: "button", onMouseDown: (e) => e.preventDefault(), onClick: () => applyInternalLink(hit), children: [_jsx("span", { className: "blog_hit-label", children: hit.label }), _jsx("span", { className: "blog_hit-path blog_mono", children: hit.path })] }) }, `${hit.collection}-${hit.id}`))) })) : linkQuery.trim() ? (_jsx("p", { className: "blog_ed-link-note", children: "No posts or categories match." })) : null, _jsxs("div", { className: "blog_ed-link-actions", children: [marks.link ? (_jsx("button", { type: "button", className: "blog_linkish", onMouseDown: (e) => e.preventDefault(), onClick: removeLink, children: "Remove link" })) : null, _jsx("button", { type: "button", className: "blog_linkish", onClick: closeLinkDialog, children: "Cancel" })] })] })) : null;
|
|
408
408
|
const insertImage = async () => {
|
|
409
409
|
const picked = await onPickImage();
|
|
410
410
|
if (!picked)
|
|
411
411
|
return;
|
|
412
412
|
focusBack();
|
|
413
|
-
document.execCommand('insertHTML', false, `<figure class="
|
|
413
|
+
document.execCommand('insertHTML', false, `<figure class="blog_ed-figure" contenteditable="false" data-media-id="${escapeAttr(picked.id)}">` +
|
|
414
414
|
`<img src="${escapeAttr(picked.url)}" alt="${escapeAttr(picked.alt)}">` +
|
|
415
415
|
`<figcaption>${escapeAttr(picked.alt || 'Image')}</figcaption></figure><p><br></p>`);
|
|
416
416
|
emit();
|
|
@@ -447,8 +447,8 @@ export const RichTextEditor = ({ initialHtml, onChange, onPickImage, searchTarge
|
|
|
447
447
|
}
|
|
448
448
|
};
|
|
449
449
|
const [active, setActive] = useState(false);
|
|
450
|
-
const Btn = ({ cmd, label, title, onClick, active: btnActive, }) => (_jsx("button", { type: "button", className: `
|
|
451
|
-
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: `
|
|
450
|
+
const Btn = ({ cmd, label, title, onClick, active: btnActive, }) => (_jsx("button", { type: "button", className: `blog_tb-btn${btnActive ? ' blog_is-on' : ''}`, title: title, "aria-label": title, "aria-pressed": btnActive ? 'true' : 'false', onMouseDown: (e) => e.preventDefault(), onClick: () => (onClick ? onClick() : cmd ? exec(cmd) : undefined), children: label }));
|
|
451
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: `blog_ed${active ? ' blog_is-active' : ''}`, onFocusCapture: () => setActive(true), onBlurCapture: (e) => {
|
|
452
452
|
const root = e.currentTarget;
|
|
453
453
|
const next = e.relatedTarget;
|
|
454
454
|
if (next && root.contains(next))
|
|
@@ -463,13 +463,13 @@ export const RichTextEditor = ({ initialHtml, onChange, onPickImage, searchTarge
|
|
|
463
463
|
return;
|
|
464
464
|
setActive(false);
|
|
465
465
|
}, 0);
|
|
466
|
-
}, children: [_jsxs("div", { className: "
|
|
466
|
+
}, children: [_jsxs("div", { className: "blog_ed-chrome", children: [_jsxs("div", { className: "blog_ed-mode-tabs", role: "tablist", "aria-label": "Editor mode", children: [_jsx("button", { type: "button", role: "tab", className: mode === 'visual' ? 'blog_is-on' : undefined, "aria-selected": mode === 'visual', onClick: () => switchMode('visual'), children: "Visual" }), _jsx("button", { type: "button", role: "tab", className: mode === 'code' ? 'blog_is-on' : undefined, "aria-selected": mode === 'code', onClick: () => switchMode('code'), children: "Code" })] }), mode === 'visual' ? (_jsxs("div", { className: "blog_ed-toolbar", role: "toolbar", "aria-label": "Formatting", children: [_jsx("button", { type: "button", className: "blog_tb-btn blog_tb-media", title: "Add Media", onMouseDown: (e) => e.preventDefault(), onClick: () => void insertImage(), children: "Add Media" }), _jsx("select", { className: "blog_tb-select", value: BLOCKS.some((b) => b.tag === block) ? block : 'p', onChange: (e) => setBlockTag(e.target.value), "aria-label": "Text style", children: BLOCKS.map((b) => (_jsx("option", { value: b.tag, children: b.label }, b.tag))) }), _jsx("span", { className: "blog_tb-sep" }), _jsx(Btn, { cmd: "bold", label: _jsx("b", { children: "B" }), title: "Bold (\u2318B)", active: marks.bold }), _jsx(Btn, { cmd: "italic", label: _jsx("i", { children: "I" }), title: "Italic (\u2318I)", active: marks.italic }), _jsx(Btn, { cmd: "underline", label: _jsx("u", { children: "U" }), title: "Underline (\u2318U)", active: marks.underline }), _jsx("span", { className: "blog_tb-sep" }), _jsx(Btn, { cmd: "insertUnorderedList", label: "\u2022\u2014", title: "Bulleted list", active: marks.insertUnorderedList }), _jsx(Btn, { cmd: "insertOrderedList", label: "1\u2014", title: "Numbered list", active: marks.insertOrderedList }), _jsx(Btn, { label: "\u275D", title: "Quote", onClick: toggleQuote, active: marks.quote }), _jsx("span", { className: "blog_tb-sep" }), _jsx(Btn, { cmd: "justifyLeft", label: "\u2261", title: "Align left" }), _jsx(Btn, { cmd: "justifyCenter", label: "\u2263", title: "Align centre" }), _jsx(Btn, { cmd: "justifyRight", label: "\u2261", title: "Align right" }), _jsx("span", { className: "blog_tb-sep" }), _jsx(Btn, { label: "\uD83D\uDD17", title: "Link (\u2318K)", onClick: () => {
|
|
467
467
|
refreshState();
|
|
468
468
|
openLinkDialog();
|
|
469
|
-
}, active: marks.link }), _jsx(Btn, { label: "\u26D3\uFE0E", title: "Remove link", onClick: () => exec('unlink') }), _jsx(Btn, { label: "\uD83D\uDDBC", title: "Insert image", onClick: insertImage }), _jsx("span", { className: "
|
|
469
|
+
}, active: marks.link }), _jsx(Btn, { label: "\u26D3\uFE0E", title: "Remove link", onClick: () => exec('unlink') }), _jsx(Btn, { label: "\uD83D\uDDBC", title: "Insert image", onClick: insertImage }), _jsx("span", { className: "blog_tb-sep" }), _jsx(Btn, { label: "\u25BE", title: "Toolbar toggle", active: moreTools, onClick: () => setMoreTools((v) => !v) }), _jsxs("span", { className: "blog_tb-count", children: [words, " words"] }), moreTools ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "blog_tb-sep" }), _jsx(Btn, { cmd: "strikeThrough", label: _jsx("s", { children: "S" }), title: "Strikethrough", active: marks.strikeThrough }), _jsx(Btn, { label: "\u2015", title: "Divider", onClick: () => exec('insertHorizontalRule') }), _jsxs("label", { className: "blog_tb-color", title: "Text colour", children: [_jsx("span", { "aria-hidden": "true", children: "A" }), _jsx("input", { type: "color", value: foreColor, onChange: (e) => {
|
|
470
470
|
setForeColor(e.target.value);
|
|
471
471
|
focusBack();
|
|
472
472
|
document.execCommand('foreColor', false, e.target.value);
|
|
473
473
|
emit();
|
|
474
|
-
}, "aria-label": "Text colour" })] }), _jsx(Btn, { label: "\u03A9", title: "Special character", onClick: () => setCharsOpen((v) => !v), active: charsOpen }), _jsx(Btn, { cmd: "outdent", label: "\u21E4", title: "Decrease indent" }), _jsx(Btn, { cmd: "indent", label: "\u21E5", title: "Increase indent" }), _jsx(Btn, { label: _jsx("span", { className: "
|
|
474
|
+
}, "aria-label": "Text colour" })] }), _jsx(Btn, { label: "\u03A9", title: "Special character", onClick: () => setCharsOpen((v) => !v), active: charsOpen }), _jsx(Btn, { cmd: "outdent", label: "\u21E4", title: "Decrease indent" }), _jsx(Btn, { cmd: "indent", label: "\u21E5", title: "Increase indent" }), _jsx(Btn, { label: _jsx("span", { className: "blog_mono", children: '</>' }), title: "Inline code", onClick: toggleCode, active: marks.code }), _jsx(Btn, { cmd: "removeFormat", label: "Tx", title: "Clear formatting" }), _jsx(Btn, { label: "\u21BA", title: "Undo", onClick: () => exec('undo') }), _jsx(Btn, { label: "\u21BB", title: "Redo", onClick: () => exec('redo') })] })) : null] })) : null, charsOpen && mode === 'visual' ? (_jsx("div", { className: "blog_ed-chars", role: "listbox", "aria-label": "Special characters", children: SPECIAL_CHARS.map((ch) => (_jsx("button", { type: "button", onMouseDown: (e) => e.preventDefault(), onClick: () => insertSpecial(ch), children: ch }, ch))) })) : null] }), mode === 'code' ? (_jsx("textarea", { className: "blog_ed-code blog_mono", value: codeHtml, onChange: (e) => setCodeHtml(e.target.value), "aria-label": "HTML source", spellCheck: false })) : !mounted ? (_jsx("div", { className: "blog_ed-surface", ref: surfaceRef, children: _jsx("div", { className: "blog_ed-input blog_prose blog_ed-skeleton", "aria-hidden": "true" }) })) : (_jsxs("div", { className: "blog_ed-surface", ref: surfaceRef, children: [empty ? _jsx("div", { className: "blog_ed-placeholder", children: "Tell the story\u2026" }) : null, _jsx("div", { ref: editorRef, className: "blog_ed-input blog_prose", contentEditable: true, suppressContentEditableWarning: true, role: "textbox", "aria-multiline": "true", "aria-label": "Post content", spellCheck: true, onInput: scheduleEmit, onBlur: emit, onPaste: onPaste, onKeyDown: onKeyDown, onKeyUp: refreshState, onMouseUp: refreshState })] }))] }), mounted && linkPopover ? createPortal(linkPopover, document.body) : null] }));
|
|
475
475
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
const AccChevron = () => (_jsx("span", { className: "
|
|
3
|
+
const AccChevron = () => (_jsx("span", { className: "blog_acc-chevron", "aria-hidden": "true", children: _jsx("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: _jsx("path", { d: "M2.5 4.5L6 8L9.5 4.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }) }));
|
|
4
4
|
export { AccChevron };
|
|
5
|
-
export const YoastCollapsible = ({ title, defaultOpen = false, status, children, className }) => (_jsxs("details", { className: `
|
|
5
|
+
export const YoastCollapsible = ({ title, defaultOpen = false, status, children, className }) => (_jsxs("details", { className: `blog_yoast-collapse${className ? ` ${className}` : ''}`, open: defaultOpen, children: [_jsxs("summary", { className: "blog_yoast-collapse-summary", children: [_jsx(AccChevron, {}), _jsx("span", { className: "blog_yoast-collapse-title", children: title }), status ? _jsx("span", { className: "blog_yoast-collapse-status", children: status }) : null] }), _jsx("div", { className: "blog_yoast-collapse-body", children: children })] }));
|
|
@@ -27,5 +27,5 @@ export const YoastInsightsPanel = ({ contentText }) => {
|
|
|
27
27
|
.sort((a, b) => b[1] - a[1])
|
|
28
28
|
.slice(0, 8);
|
|
29
29
|
}, [words]);
|
|
30
|
-
return (_jsxs("div", { className: "
|
|
30
|
+
return (_jsxs("div", { className: "blog_yoast-accordion-panel", children: [_jsx("p", { className: "blog_yoast-help", children: "See word count and prominent terms used in your post content." }), _jsxs("div", { className: "blog_yoast-link-section", children: [_jsx("h4", { className: "blog_yoast-link-section-title", children: "Word count" }), _jsx("p", { className: "blog_yoast-insight-value", children: wordCount })] }), _jsxs("div", { className: "blog_yoast-link-section", children: [_jsx("h4", { className: "blog_yoast-link-section-title", children: "Prominent words" }), prominent.length === 0 ? (_jsx("p", { className: "blog_yoast-result-empty", children: "Add body content to see prominent words." })) : (_jsx("ul", { className: "blog_yoast-link-list", children: prominent.map(([word, count]) => (_jsxs("li", { children: [_jsx("span", { className: "blog_yoast-link-label", children: word }), _jsxs("span", { className: "blog_yoast-link-href", children: [count, " occurrences"] })] }, word))) }))] })] }));
|
|
31
31
|
};
|
|
@@ -31,5 +31,5 @@ export const YoastInternalLinksPanel = ({ contentHtml }) => {
|
|
|
31
31
|
}
|
|
32
32
|
return { outgoing, external };
|
|
33
33
|
}, [contentHtml]);
|
|
34
|
-
return (_jsxs("div", { className: "
|
|
34
|
+
return (_jsxs("div", { className: "blog_yoast-accordion-panel", children: [_jsx("p", { className: "blog_yoast-help", children: "Internal links help visitors and search engines navigate your site. Outbound links add context." }), _jsxs("div", { className: "blog_yoast-link-section", children: [_jsx("h4", { className: "blog_yoast-link-section-title", children: "Outgoing internal links" }), outgoing.length === 0 ? (_jsx("p", { className: "blog_yoast-result-empty", children: "No internal links in this post yet." })) : (_jsx("ul", { className: "blog_yoast-link-list", children: outgoing.map((l, i) => (_jsxs("li", { children: [_jsx("span", { className: "blog_yoast-link-label", children: l.label }), _jsx("span", { className: "blog_yoast-link-href blog_mono", children: l.href })] }, `${l.href}-${i}`))) }))] }), _jsxs("div", { className: "blog_yoast-link-section", children: [_jsx("h4", { className: "blog_yoast-link-section-title", children: "Outgoing external links" }), external.length === 0 ? (_jsx("p", { className: "blog_yoast-result-empty", children: "No external links in this post yet." })) : (_jsx("ul", { className: "blog_yoast-link-list", children: external.map((l, i) => (_jsxs("li", { children: [_jsx("span", { className: "blog_yoast-link-label", children: l.label }), _jsx("span", { className: "blog_yoast-link-href blog_mono", children: l.href })] }, `${l.href}-${i}`))) }))] })] }));
|
|
35
35
|
};
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { toneLabel } from '../../../lib/seo-analysis';
|
|
4
|
-
const Dot = ({ tone }) => (_jsx("span", { className: `
|
|
4
|
+
const Dot = ({ tone }) => (_jsx("span", { className: `blog_yoast-dot blog_yoast-dot-${tone}`, "aria-hidden": "true" }));
|
|
5
5
|
const CheckList = ({ items }) => {
|
|
6
6
|
if (!items.length) {
|
|
7
|
-
return _jsx("p", { className: "
|
|
7
|
+
return _jsx("p", { className: "blog_yoast-result-empty", children: "No items in this group." });
|
|
8
8
|
}
|
|
9
|
-
return (_jsx("ul", { className: "
|
|
9
|
+
return (_jsx("ul", { className: "blog_yoast-link-list", children: items.map((c) => (_jsxs("li", { children: [_jsxs("span", { className: "blog_yoast-link-label", children: [_jsx(Dot, { tone: c.tone }), " ", c.label] }), _jsx("span", { className: "blog_yoast-link-href", children: c.detail })] }, c.id))) }));
|
|
10
10
|
};
|
|
11
|
-
const AnalysisBlock = ({ title, items, }) => (_jsxs("div", { className: "
|
|
11
|
+
const AnalysisBlock = ({ title, items, }) => (_jsxs("div", { className: "blog_yoast-link-section", children: [_jsxs("h4", { className: "blog_yoast-link-section-title", children: [title, " (", items.length, ")"] }), _jsx(CheckList, { items: items })] }));
|
|
12
12
|
export const YoastSeoAnalysisPanel = ({ checks, tone, hasKeyphrase, }) => {
|
|
13
13
|
const problems = checks.filter((c) => c.tone === 'bad');
|
|
14
14
|
const improvements = checks.filter((c) => c.tone === 'ok');
|
|
15
15
|
const good = checks.filter((c) => c.tone === 'good');
|
|
16
|
-
return (_jsxs("div", { className: "
|
|
16
|
+
return (_jsxs("div", { className: "blog_yoast-accordion-panel", children: [_jsx("p", { className: "blog_yoast-help", children: "Review SEO recommendations for your focus keyphrase, title, meta description, and content." }), !hasKeyphrase ? (_jsx("p", { className: "blog_yoast-result-empty", role: "status", children: "Please enter a focus keyphrase to see your SEO score." })) : (_jsxs("div", { className: "blog_yoast-analysis-results-head", children: [_jsx("h4", { className: "blog_yoast-link-section-title", children: "Overall score" }), _jsxs("span", { className: `blog_seo-pill blog_seo-pill-${tone}`, children: [_jsx(Dot, { tone: tone }), " ", toneLabel(tone)] })] })), _jsx(AnalysisBlock, { title: "Problems", tone: "bad", items: problems }), _jsx(AnalysisBlock, { title: "Improvements", tone: "ok", items: improvements }), _jsx(AnalysisBlock, { title: "Good results", tone: "good", items: good })] }));
|
|
17
17
|
};
|
|
18
18
|
export const YoastReadabilityPanel = ({ checks, tone, }) => {
|
|
19
19
|
const problems = checks.filter((c) => c.tone === 'bad');
|
|
20
20
|
const improvements = checks.filter((c) => c.tone === 'ok');
|
|
21
21
|
const good = checks.filter((c) => c.tone === 'good');
|
|
22
|
-
return (_jsxs("div", { className: "
|
|
22
|
+
return (_jsxs("div", { className: "blog_yoast-accordion-panel", children: [_jsx("p", { className: "blog_yoast-help", children: "Yoast SEO checks sentence length, passive voice, subheading distribution, and more." }), _jsxs("div", { className: "blog_yoast-analysis-results-head", children: [_jsx("h4", { className: "blog_yoast-link-section-title", children: "Overall score" }), _jsxs("span", { className: `blog_seo-pill blog_seo-pill-${tone}`, children: [_jsx(Dot, { tone: tone }), " ", toneLabel(tone)] })] }), _jsx(AnalysisBlock, { title: "Problems", tone: "bad", items: problems }), _jsx(AnalysisBlock, { title: "Improvements", tone: "ok", items: improvements }), _jsx(AnalysisBlock, { title: "Good results", tone: "good", items: good })] }));
|
|
23
23
|
};
|