@ecomcx/ecomweb-theme-sdk 1.25.0 → 1.27.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/content.js +5 -3
- package/dist/dynamic.js +2 -2
- package/dist/rich.d.ts +8 -0
- package/dist/rich.js +16 -2
- package/dist/template-blocks.js +1 -1
- package/dist/types.d.ts +2 -0
- package/package.json +1 -1
package/dist/content.js
CHANGED
|
@@ -19,7 +19,7 @@ export async function PostContent({ post, content, className }) {
|
|
|
19
19
|
}
|
|
20
20
|
if (!doc?.blocks?.length)
|
|
21
21
|
return null;
|
|
22
|
-
const rendered = await renderContent(doc, await contentContext(site, post), (blocks) => applyFilters("post.blocks", blocks, { site, post }, site));
|
|
22
|
+
const rendered = await renderContent(doc, { ...(await contentContext(site, post)), source: post ? `post:${post.id}` : undefined }, (blocks) => applyFilters("post.blocks", blocks, { site, post }, site));
|
|
23
23
|
return _jsx("div", { className: cx(className ?? "entry-content", "ecx-content"), children: rendered });
|
|
24
24
|
}
|
|
25
25
|
/**
|
|
@@ -31,7 +31,7 @@ export async function EditableRegion({ name, data, className, children }) {
|
|
|
31
31
|
const doc = (await getTemplates().catch(() => null))?.parts[name];
|
|
32
32
|
const site = await getSite();
|
|
33
33
|
const own = doc?.blocks?.length
|
|
34
|
-
? _jsx("div", { className: cx(`ecx-region ecx-region-${name}`, "ecx-content", className), children: await renderContent(doc, await contentContext(site, data?.post, data)) })
|
|
34
|
+
? _jsx("div", { className: cx(`ecx-region ecx-region-${name}`, "ecx-content", className), children: await renderContent(doc, { ...(await contentContext(site, data?.post, data)), source: `part:${name}` }) })
|
|
35
35
|
: _jsx(_Fragment, { children: children });
|
|
36
36
|
return _jsx(_Fragment, { children: await regionOverride(name, own, site, data) });
|
|
37
37
|
}
|
|
@@ -45,9 +45,11 @@ async function regionOverride(name, fallback, site, data) {
|
|
|
45
45
|
/** Ngữ cảnh vẽ nội dung dùng chung cho bài viết và template khối. */
|
|
46
46
|
export async function contentContext(site, post, data) {
|
|
47
47
|
const t = await getT();
|
|
48
|
+
// Bản nháp (xem trực tiếp / Trình dựng) không vào cache công khai → được gắn vị trí khối cho khung soạn
|
|
49
|
+
const markBlocks = await import("next/headers").then(async ({ draftMode }) => (await draftMode()).isEnabled).catch(() => false);
|
|
48
50
|
const blocks = (await getActiveExtensions(site)).flatMap((d) => d.blockTypes ?? []);
|
|
49
51
|
return {
|
|
50
|
-
site, post, data, blocks,
|
|
52
|
+
site, post, data, blocks, markBlocks,
|
|
51
53
|
resolve: { shortcode: (name) => findShortcode(name, site), block: (type) => findBlock(type, site) },
|
|
52
54
|
labels: { download: t("file.download"), toc: t("toc.title") },
|
|
53
55
|
boundary: (name, key, el) => _jsx(ExtensionBoundary, { name: name, children: el }, key),
|
package/dist/dynamic.js
CHANGED
|
@@ -112,14 +112,14 @@ export async function renderDynamic(node, key, ctx, children) {
|
|
|
112
112
|
const doc = await ctx.pattern(a.ref);
|
|
113
113
|
if (!doc)
|
|
114
114
|
return null;
|
|
115
|
-
return _jsx(Fragment, { children: await renderContent(doc, { ...ctx, partDepth: (ctx.partDepth ?? 0) + 1 }) }, key);
|
|
115
|
+
return _jsx(Fragment, { children: await renderContent(doc, { ...ctx, partDepth: (ctx.partDepth ?? 0) + 1, source: `pattern:${a.ref}` }) }, key);
|
|
116
116
|
}
|
|
117
117
|
case "templatePart": {
|
|
118
118
|
if (!a.slug || !ctx.parts || (ctx.partDepth ?? 0) >= 4)
|
|
119
119
|
return null;
|
|
120
120
|
const doc = await ctx.parts(String(a.slug));
|
|
121
121
|
const Tag = (["header", "footer", "aside", "section"].includes(a.tagName) ? a.tagName : "div");
|
|
122
|
-
const part = doc ? _jsx(Tag, { className: cx("ecx-template-part", `ecx-template-part-${a.slug}`), children: await renderContent(doc, { ...ctx, partDepth: (ctx.partDepth ?? 0) + 1 }) }) : null;
|
|
122
|
+
const part = doc ? _jsx(Tag, { className: cx("ecx-template-part", `ecx-template-part-${a.slug}`), children: await renderContent(doc, { ...ctx, partDepth: (ctx.partDepth ?? 0) + 1, source: `part:${a.slug}` }) }) : null;
|
|
123
123
|
const el = ctx.region ? await ctx.region(String(a.slug), part) : part;
|
|
124
124
|
return el == null ? null : _jsx(Fragment, { children: el }, key);
|
|
125
125
|
}
|
package/dist/rich.d.ts
CHANGED
|
@@ -48,6 +48,14 @@ export interface RenderContext {
|
|
|
48
48
|
id: string;
|
|
49
49
|
text: string;
|
|
50
50
|
}>;
|
|
51
|
+
/**
|
|
52
|
+
* Đang xem bản nháp (Trình dựng / xem trực tiếp): gắn data-ecx-doc + data-ecx-path cho từng khối để khung soạn ở admin
|
|
53
|
+
* biết người dùng bấm vào khối nào. `source` = tài liệu đang vẽ ("template:single", "part:header", "post:12", "pattern:3").
|
|
54
|
+
*/
|
|
55
|
+
markBlocks?: boolean;
|
|
56
|
+
source?: string;
|
|
57
|
+
/** Khoá của khối richtext đang vẽ — đường dẫn khối = phần khoá sau nó (do renderBlock đặt). */
|
|
58
|
+
pathRoot?: string;
|
|
51
59
|
}
|
|
52
60
|
type Node = Omit<RichNode, "attrs" | "content" | "marks"> & {
|
|
53
61
|
attrs?: Record<string, any>;
|
package/dist/rich.js
CHANGED
|
@@ -152,7 +152,21 @@ function decorate(el, a, key, ctx) {
|
|
|
152
152
|
async function renderNode(node, key, ctx) {
|
|
153
153
|
const el = await renderNodeInner(node, key, ctx);
|
|
154
154
|
const a = node.attrs;
|
|
155
|
-
|
|
155
|
+
const out = a && (a.style || a.blockStyle || a.className || a.anchor) ? decorate(el, a, key, ctx) : el;
|
|
156
|
+
return ctx.markBlocks && ctx.source && ctx.pathRoot != null && key.startsWith(ctx.pathRoot + "-") && !UNMARKED.has(node.type)
|
|
157
|
+
? markBlock(out, ctx.source, key.slice(ctx.pathRoot.length + 1), key)
|
|
158
|
+
: out;
|
|
159
|
+
}
|
|
160
|
+
/** Nút không chọn riêng được trong khung soạn (hoặc bọc thêm thẻ sẽ sai cấu trúc HTML: li trong ul, ô trong bảng). */
|
|
161
|
+
const UNMARKED = new Set(["text", "hardBreak", "listItem", "taskItem", "tableRow", "tableCell", "tableHeader"]);
|
|
162
|
+
/** Gắn vị trí khối: thẻ HTML thì thêm thuộc tính, component thì bọc thẻ display:contents (không đổi bố cục). */
|
|
163
|
+
function markBlock(el, source, path, key) {
|
|
164
|
+
if (el == null || el === false)
|
|
165
|
+
return el;
|
|
166
|
+
const attrs = { "data-ecx-doc": source, "data-ecx-path": path };
|
|
167
|
+
if (isValidElement(el) && typeof el.type === "string")
|
|
168
|
+
return cloneElement(el, attrs);
|
|
169
|
+
return _jsx("div", { style: { display: "contents" }, ...attrs, children: el }, key);
|
|
156
170
|
}
|
|
157
171
|
async function renderNodeInner(node, key, ctx) {
|
|
158
172
|
const children = async () => Promise.all((node.content ?? []).map((c, i) => renderNode(c, `${key}-${i}`, ctx)));
|
|
@@ -359,7 +373,7 @@ async function renderHtml(html, key, ctx) {
|
|
|
359
373
|
}
|
|
360
374
|
async function renderBlock(block, key, ctx) {
|
|
361
375
|
switch (block.type) {
|
|
362
|
-
case "richtext": return block.data ? await renderNode(block.data, key, ctx) : null;
|
|
376
|
+
case "richtext": return block.data ? await renderNode(block.data, key, ctx.markBlocks ? { ...ctx, pathRoot: key } : ctx) : null;
|
|
363
377
|
case "html": return await renderHtml(String(block.data?.html ?? ""), key, ctx);
|
|
364
378
|
default: {
|
|
365
379
|
const Comp = await ctx.resolve.block(block.type);
|
package/dist/template-blocks.js
CHANGED
|
@@ -7,5 +7,5 @@ export default async function BlockTemplate({ data, site, template }) {
|
|
|
7
7
|
const doc = (await getTemplates()).templates[template];
|
|
8
8
|
if (!doc)
|
|
9
9
|
return null;
|
|
10
|
-
return _jsx("div", { className: `ecx-template ecx-template-${template} ecx-content`, children: await renderContent(doc, await contentContext(site, data.post, data)) });
|
|
10
|
+
return _jsx("div", { className: `ecx-template ecx-template-${template} ecx-content`, children: await renderContent(doc, { ...(await contentContext(site, data.post, data)), source: `template:${template}` }) });
|
|
11
11
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -277,6 +277,8 @@ export interface ThemeManifest {
|
|
|
277
277
|
template?: string;
|
|
278
278
|
locked?: boolean;
|
|
279
279
|
}>;
|
|
280
|
+
/** Nguồn ngoài website theme cần nạp (CSP): { "style-src": ["https://fonts.googleapis.com"], "font-src": ["https://fonts.gstatic.com"] }. */
|
|
281
|
+
csp?: Partial<Record<"script-src" | "style-src" | "img-src" | "font-src" | "connect-src" | "frame-src" | "media-src" | "worker-src" | "form-action", string[]>>;
|
|
280
282
|
[key: string]: unknown;
|
|
281
283
|
}
|
|
282
284
|
type Loader<T> = () => Promise<{
|