@standhigher/puck-page-builder 0.11.0 → 0.12.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/README.md +1 -1
- package/dist/{chunk-PFCWXLWN.js → chunk-YXBTNYPR.js} +50 -0
- package/dist/extensions.d.ts +18 -4
- package/dist/extensions.js +7 -1
- package/dist/index.d.ts +38 -11
- package/dist/index.js +60 -31
- package/dist/{registry-eNtkEC6o.d.ts → registry-CXJdOya3.d.ts} +1 -1
- package/dist/renderer.d.ts +1 -1
- package/dist/runtime.d.ts +2 -2
- package/dist/runtime.js +7 -1
- package/package.json +1 -1
- package/src/styles.css +22 -11
package/README.md
CHANGED
|
@@ -65,7 +65,7 @@ const notice = {
|
|
|
65
65
|
/>
|
|
66
66
|
```
|
|
67
67
|
|
|
68
|
-
Built-in field controls are `text` (single line), `textarea` (multi-line), `url`, and `
|
|
68
|
+
Built-in field controls are `text` (single line), `textarea` (multi-line), `url`, `color`, and `products`. `products` only renders the selected snapshot and a host picker button; Shopify search stays in the host `productPicker` adapter. Their validation runs in the editor before save or publish; custom fields may continue using a block's `validate` function. `validateFieldValue` is exported from `/extensions` when a host needs the same validation outside the editor.
|
|
69
69
|
|
|
70
70
|
Theme values remain deliberately token-based: template theme → page theme → variant theme → block style. A block resets an inherited token by setting its local token explicitly (for example `{ radius: "0" }`), so no incompatible persisted style-reset shape is needed.
|
|
71
71
|
|
|
@@ -1,3 +1,46 @@
|
|
|
1
|
+
// src/core/extensions/product-reference.ts
|
|
2
|
+
function isHttpUrl(value) {
|
|
3
|
+
try {
|
|
4
|
+
const url = new URL(value);
|
|
5
|
+
return (url.protocol === "https:" || url.protocol === "http:") && !url.username && !url.password;
|
|
6
|
+
} catch {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
function isProductReference(value) {
|
|
11
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
12
|
+
const candidate = value;
|
|
13
|
+
if (typeof candidate.id !== "string" || !candidate.id.trim()) return false;
|
|
14
|
+
if (candidate.title !== void 0 && typeof candidate.title !== "string") return false;
|
|
15
|
+
if (candidate.handle !== void 0 && typeof candidate.handle !== "string") return false;
|
|
16
|
+
if (candidate.imageUrl !== void 0 && (typeof candidate.imageUrl !== "string" || !isHttpUrl(candidate.imageUrl))) return false;
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
function parseProductReferences(value) {
|
|
20
|
+
if (!Array.isArray(value)) return [];
|
|
21
|
+
const products = [];
|
|
22
|
+
for (const item of value) {
|
|
23
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) continue;
|
|
24
|
+
const candidate = item;
|
|
25
|
+
if (typeof candidate.id !== "string" || !candidate.id.trim()) continue;
|
|
26
|
+
products.push({
|
|
27
|
+
id: candidate.id.trim(),
|
|
28
|
+
...typeof candidate.title === "string" && candidate.title.trim() ? { title: candidate.title.trim() } : {},
|
|
29
|
+
...typeof candidate.handle === "string" && candidate.handle.trim() ? { handle: candidate.handle.trim() } : {},
|
|
30
|
+
...typeof candidate.imageUrl === "string" && isHttpUrl(candidate.imageUrl) ? { imageUrl: candidate.imageUrl } : {}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
return products;
|
|
34
|
+
}
|
|
35
|
+
function toProductReferenceJson(product) {
|
|
36
|
+
return {
|
|
37
|
+
id: product.id,
|
|
38
|
+
...product.title ? { title: product.title } : {},
|
|
39
|
+
...product.handle ? { handle: product.handle } : {},
|
|
40
|
+
...product.imageUrl ? { imageUrl: product.imageUrl } : {}
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
1
44
|
// src/core/extensions/validation.ts
|
|
2
45
|
var hexColor = /^#(?:[\da-f]{3}|[\da-f]{4}|[\da-f]{6}|[\da-f]{8})$/i;
|
|
3
46
|
function stringLength(value) {
|
|
@@ -19,6 +62,10 @@ function validateFieldValue(config, value) {
|
|
|
19
62
|
const control = config.control;
|
|
20
63
|
if (!control && !config.required && !config.validation) return [];
|
|
21
64
|
if (value === void 0 && !config.required) return [];
|
|
65
|
+
if (control === "products") {
|
|
66
|
+
if (!Array.isArray(value)) return [{ path: "", message: "\u5FC5\u987B\u662F\u5546\u54C1\u5217\u8868\u3002" }];
|
|
67
|
+
return value.flatMap((item, index) => isProductReference(item) ? [] : [{ path: String(index), message: "\u5FC5\u987B\u5305\u542B\u7A33\u5B9A\u7684\u5546\u54C1 ID\u3002" }]);
|
|
68
|
+
}
|
|
22
69
|
if (typeof value !== "string") return [{ path: "", message: "\u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u3002" }];
|
|
23
70
|
const issues = [];
|
|
24
71
|
if (config.required && value.trim().length === 0) issues.push({ path: "", message: "\u6B64\u5B57\u6BB5\u4E3A\u5FC5\u586B\u9879\u3002" });
|
|
@@ -314,6 +361,9 @@ function validatePageDocumentWithRegistry(document, registry) {
|
|
|
314
361
|
}
|
|
315
362
|
|
|
316
363
|
export {
|
|
364
|
+
isProductReference,
|
|
365
|
+
parseProductReferences,
|
|
366
|
+
toProductReferenceJson,
|
|
317
367
|
validateFieldValue,
|
|
318
368
|
validateBlockPolicy,
|
|
319
369
|
mergeBlockPolicies,
|
package/dist/extensions.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { h as ExtensionRegistry, V as ValidationIssue, c as BlockPolicy, F as FieldConfig } from './registry-
|
|
2
|
-
export { B as BlockDefinition, a as BlockEditorProps, b as BlockOperation, d as BlockVariantDefinition, D as DataSourceDefinition, E as EditorAction, e as EditorActionPosition, f as EditorOperationPolicy, g as ExtensionActionContext, i as ExtensionRegistryError, j as ExtensionRegistryErrorCode, k as ExtensionRegistryOptions, l as ExtensionTarget, m as FieldDefinition, n as FieldProps, o as FieldValidation, L as LifecycleHooks, P as PageBuilderExtension, R as RendererDefinition, T as TemplateDefinition, p as TemplateRegistry, q as TemplateSource, U as UISlotContribution, r as UISlotName, s as createExtensionRegistry, t as createTemplateRegistry } from './registry-
|
|
3
|
-
import { PageDocument } from './schema.js';
|
|
1
|
+
import { h as ExtensionRegistry, V as ValidationIssue, c as BlockPolicy, F as FieldConfig } from './registry-CXJdOya3.js';
|
|
2
|
+
export { B as BlockDefinition, a as BlockEditorProps, b as BlockOperation, d as BlockVariantDefinition, D as DataSourceDefinition, E as EditorAction, e as EditorActionPosition, f as EditorOperationPolicy, g as ExtensionActionContext, i as ExtensionRegistryError, j as ExtensionRegistryErrorCode, k as ExtensionRegistryOptions, l as ExtensionTarget, m as FieldDefinition, n as FieldProps, o as FieldValidation, L as LifecycleHooks, P as PageBuilderExtension, R as RendererDefinition, T as TemplateDefinition, p as TemplateRegistry, q as TemplateSource, U as UISlotContribution, r as UISlotName, s as createExtensionRegistry, t as createTemplateRegistry } from './registry-CXJdOya3.js';
|
|
3
|
+
import { PageDocument, JsonValue } from './schema.js';
|
|
4
4
|
import 'react';
|
|
5
5
|
import './theme.js';
|
|
6
6
|
|
|
@@ -12,6 +12,20 @@ import './theme.js';
|
|
|
12
12
|
*/
|
|
13
13
|
declare function validatePageDocumentWithRegistry(document: PageDocument, registry: ExtensionRegistry): ValidationIssue[];
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* JSON-only product snapshot persisted in PageDocument props.
|
|
17
|
+
* Hosts may add a stable id plus display fields; never store tokens or prices as secrets.
|
|
18
|
+
*/
|
|
19
|
+
type ProductReference = {
|
|
20
|
+
id: string;
|
|
21
|
+
title?: string;
|
|
22
|
+
imageUrl?: string;
|
|
23
|
+
handle?: string;
|
|
24
|
+
};
|
|
25
|
+
declare function isProductReference(value: unknown): value is ProductReference;
|
|
26
|
+
declare function parseProductReferences(value: unknown): ProductReference[];
|
|
27
|
+
declare function toProductReferenceJson(product: ProductReference): JsonValue;
|
|
28
|
+
|
|
15
29
|
/** Validates the portable built-in controls without relying on browser-only APIs. */
|
|
16
30
|
declare function validateFieldValue(config: FieldConfig, value: unknown): ValidationIssue[];
|
|
17
31
|
/** Returns a diagnostic for an invalid declaration, or `null` when it is usable. */
|
|
@@ -20,4 +34,4 @@ declare function mergeBlockPolicies(...policies: Array<BlockPolicy | undefined>)
|
|
|
20
34
|
declare function minimumBlockInstances(policy: BlockPolicy | undefined): number;
|
|
21
35
|
declare function maximumBlockInstances(policy: BlockPolicy | undefined): number;
|
|
22
36
|
|
|
23
|
-
export { BlockPolicy, ExtensionRegistry, FieldConfig, ValidationIssue, maximumBlockInstances, mergeBlockPolicies, minimumBlockInstances, validateBlockPolicy, validateFieldValue, validatePageDocumentWithRegistry };
|
|
37
|
+
export { BlockPolicy, ExtensionRegistry, FieldConfig, type ProductReference, ValidationIssue, isProductReference, maximumBlockInstances, mergeBlockPolicies, minimumBlockInstances, parseProductReferences, toProductReferenceJson, validateBlockPolicy, validateFieldValue, validatePageDocumentWithRegistry };
|
package/dist/extensions.js
CHANGED
|
@@ -4,22 +4,28 @@ import {
|
|
|
4
4
|
TemplateRegistry,
|
|
5
5
|
createExtensionRegistry,
|
|
6
6
|
createTemplateRegistry,
|
|
7
|
+
isProductReference,
|
|
7
8
|
maximumBlockInstances,
|
|
8
9
|
mergeBlockPolicies,
|
|
9
10
|
minimumBlockInstances,
|
|
11
|
+
parseProductReferences,
|
|
12
|
+
toProductReferenceJson,
|
|
10
13
|
validateBlockPolicy,
|
|
11
14
|
validateFieldValue,
|
|
12
15
|
validatePageDocumentWithRegistry
|
|
13
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-YXBTNYPR.js";
|
|
14
17
|
export {
|
|
15
18
|
ExtensionRegistry,
|
|
16
19
|
ExtensionRegistryError,
|
|
17
20
|
TemplateRegistry,
|
|
18
21
|
createExtensionRegistry,
|
|
19
22
|
createTemplateRegistry,
|
|
23
|
+
isProductReference,
|
|
20
24
|
maximumBlockInstances,
|
|
21
25
|
mergeBlockPolicies,
|
|
22
26
|
minimumBlockInstances,
|
|
27
|
+
parseProductReferences,
|
|
28
|
+
toProductReferenceJson,
|
|
23
29
|
validateBlockPolicy,
|
|
24
30
|
validateFieldValue,
|
|
25
31
|
validatePageDocumentWithRegistry
|
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
|
-
import { f as EditorOperationPolicy, c as BlockPolicy, V as ValidationIssue, h as ExtensionRegistry } from './registry-
|
|
4
|
-
export { B as BlockDefinition, a as BlockEditorProps, b as BlockOperation, d as BlockVariantDefinition, D as DataSourceDefinition, E as EditorAction, e as EditorActionPosition, g as ExtensionActionContext, i as ExtensionRegistryError, j as ExtensionRegistryErrorCode, k as ExtensionRegistryOptions, l as ExtensionTarget, F as FieldConfig, m as FieldDefinition, n as FieldProps, o as FieldValidation, L as LifecycleHooks, P as PageBuilderExtension, R as RendererDefinition, T as TemplateDefinition, p as TemplateRegistry, q as TemplateSource, U as UISlotContribution, r as UISlotName, s as createExtensionRegistry, t as createTemplateRegistry } from './registry-
|
|
3
|
+
import { f as EditorOperationPolicy, c as BlockPolicy, V as ValidationIssue, h as ExtensionRegistry } from './registry-CXJdOya3.js';
|
|
4
|
+
export { B as BlockDefinition, a as BlockEditorProps, b as BlockOperation, d as BlockVariantDefinition, D as DataSourceDefinition, E as EditorAction, e as EditorActionPosition, g as ExtensionActionContext, i as ExtensionRegistryError, j as ExtensionRegistryErrorCode, k as ExtensionRegistryOptions, l as ExtensionTarget, F as FieldConfig, m as FieldDefinition, n as FieldProps, o as FieldValidation, L as LifecycleHooks, P as PageBuilderExtension, R as RendererDefinition, T as TemplateDefinition, p as TemplateRegistry, q as TemplateSource, U as UISlotContribution, r as UISlotName, s as createExtensionRegistry, t as createTemplateRegistry } from './registry-CXJdOya3.js';
|
|
5
5
|
import { PageDocument, BlockNode, JsonValue } from './schema.js';
|
|
6
6
|
export { BlockStyleOverrides, DataBinding, PageDocumentIssue, PageDocumentMigration, PageDocumentSchemaVersion, PageSettings, RenderTarget, createPageDocument, migratePageDocument, validatePageDocument } from './schema.js';
|
|
7
|
+
import { ProductReference } from './extensions.js';
|
|
8
|
+
export { isProductReference, maximumBlockInstances, mergeBlockPolicies, minimumBlockInstances, parseProductReferences, toProductReferenceJson, validateBlockPolicy, validateFieldValue, validatePageDocumentWithRegistry } from './extensions.js';
|
|
7
9
|
export { ThemeTokenName, ThemeTokens, mergeThemeTokens, normalizeThemeTokens, systemThemeTokens, toThemeStyle } from './theme.js';
|
|
8
10
|
import { Data } from '@puckeditor/core';
|
|
9
11
|
export { WebRenderer, WebRendererProps } from './renderer.js';
|
|
10
|
-
export { maximumBlockInstances, mergeBlockPolicies, minimumBlockInstances, validateBlockPolicy, validateFieldValue, validatePageDocumentWithRegistry } from './extensions.js';
|
|
11
12
|
|
|
12
13
|
type BlockView = "blocks" | "outline";
|
|
13
14
|
type Device = "desktop" | "tablet" | "mobile" | "full";
|
|
@@ -129,6 +130,19 @@ type AssetPickerAdapter = {
|
|
|
129
130
|
current?: Partial<AssetReference>;
|
|
130
131
|
}): Promise<AssetReference | null>;
|
|
131
132
|
};
|
|
133
|
+
/**
|
|
134
|
+
* Opens a host-owned product picker. Search, Shopify App Bridge, and
|
|
135
|
+
* authorization stay in the host; this package only displays the snapshot
|
|
136
|
+
* and forwards the button click.
|
|
137
|
+
*/
|
|
138
|
+
type ProductPickerAdapter = {
|
|
139
|
+
selectProducts(input: {
|
|
140
|
+
pageId: string;
|
|
141
|
+
blockId: string;
|
|
142
|
+
current: readonly ProductReference[];
|
|
143
|
+
multiple?: boolean;
|
|
144
|
+
}): Promise<readonly ProductReference[] | null>;
|
|
145
|
+
};
|
|
132
146
|
type PagePublicationStatus = "unpublished" | "published";
|
|
133
147
|
/** Presentational data for the reusable page state card. */
|
|
134
148
|
type PageStatus = {
|
|
@@ -233,9 +247,16 @@ type PageDocumentEditorShellProps = {
|
|
|
233
247
|
publishAction?: PublishAction;
|
|
234
248
|
/** Host-owned picker/uploader for shop-scoped assets. */
|
|
235
249
|
assetPicker?: AssetPickerAdapter;
|
|
250
|
+
/** Host-owned product picker. The shell only shows the selected snapshot and a button. */
|
|
251
|
+
productPicker?: ProductPickerAdapter;
|
|
236
252
|
/** Optional reusable page lifecycle summary. */
|
|
237
253
|
pageStatus?: PageStatus;
|
|
238
254
|
onBack?: () => void;
|
|
255
|
+
onHistory?: (input: {
|
|
256
|
+
document: PageDocument;
|
|
257
|
+
draftRevision?: number;
|
|
258
|
+
session?: EditorSession;
|
|
259
|
+
}) => void;
|
|
239
260
|
onPreview?: (input: {
|
|
240
261
|
document: PageDocument;
|
|
241
262
|
draftRevision?: number;
|
|
@@ -286,6 +307,7 @@ declare const messages: {
|
|
|
286
307
|
readonly unsaved: "未保存变更";
|
|
287
308
|
readonly saved: "所有更改已保留在当前会话中";
|
|
288
309
|
readonly save: "保存草稿";
|
|
310
|
+
readonly history: "历史记录";
|
|
289
311
|
readonly saving: "正在保存…";
|
|
290
312
|
readonly publish: "发布";
|
|
291
313
|
readonly publishing: "正在发布…";
|
|
@@ -314,9 +336,11 @@ declare const messages: {
|
|
|
314
336
|
readonly collapseRight: "收起属性面板";
|
|
315
337
|
readonly expandLeft: "打开区块面板";
|
|
316
338
|
readonly expandRight: "打开属性面板";
|
|
317
|
-
readonly
|
|
318
|
-
readonly
|
|
319
|
-
readonly
|
|
339
|
+
readonly selectProducts: "从 Shopify 选择商品";
|
|
340
|
+
readonly selectingProducts: "正在选择商品…";
|
|
341
|
+
readonly selectProductsFailed: "无法选择商品,请重试。";
|
|
342
|
+
readonly emptyProducts: "未选择商品";
|
|
343
|
+
readonly productCountSuffix: "件商品";
|
|
320
344
|
};
|
|
321
345
|
readonly en: {
|
|
322
346
|
readonly blocks: "Blocks";
|
|
@@ -341,6 +365,7 @@ declare const messages: {
|
|
|
341
365
|
readonly unsaved: "Unsaved changes";
|
|
342
366
|
readonly saved: "Changes are kept in this session";
|
|
343
367
|
readonly save: "Save draft";
|
|
368
|
+
readonly history: "History";
|
|
344
369
|
readonly saving: "Saving…";
|
|
345
370
|
readonly publish: "Publish";
|
|
346
371
|
readonly publishing: "Publishing…";
|
|
@@ -369,19 +394,21 @@ declare const messages: {
|
|
|
369
394
|
readonly collapseRight: "Collapse properties panel";
|
|
370
395
|
readonly expandLeft: "Open blocks panel";
|
|
371
396
|
readonly expandRight: "Open properties panel";
|
|
372
|
-
readonly
|
|
373
|
-
readonly
|
|
374
|
-
readonly
|
|
397
|
+
readonly selectProducts: "Select from Shopify";
|
|
398
|
+
readonly selectingProducts: "Selecting products…";
|
|
399
|
+
readonly selectProductsFailed: "Could not select products. Try again.";
|
|
400
|
+
readonly emptyProducts: "No products selected";
|
|
401
|
+
readonly productCountSuffix: "products";
|
|
375
402
|
};
|
|
376
403
|
};
|
|
377
404
|
type AdminMessageKey = keyof typeof messages["zh-CN"];
|
|
378
405
|
/** Minimal, dependency-free Admin copy catalog. Product locale remains PageDocument.settings.locale. */
|
|
379
406
|
declare function createAdminI18n(locale?: string): {
|
|
380
407
|
locale: AdminLocale;
|
|
381
|
-
t: (key: AdminMessageKey) => "Desktop" | "Tablet" | "Mobile" | "未保存变更" | "Outline" | "Preview" | "缩放" | "区块" | "结构" | "撤销" | "恢复" | "收起左侧面板" | "收起属性面板" | "Delete" | "Duplicate" | "编辑锁已失效" | "属性" | "添加区块" | "复制" | "删除" | "确认删除区块?" | "删除后可使用撤销恢复。" | "取消" | "确认删除" | "上移" | "下移" | "正在加载编辑器…" | "页面还没有区块" | "编辑器无法加载" | "编辑器当前不可编辑" | "操作已完成" | "所有更改已保留在当前会话中" | "保存草稿" | "正在保存…" | "发布" | "正在发布…" | "保存草稿失败" | "重试保存" | "当前离线,恢复网络后将自动保存" | "发布失败" | "已发布" | "返回" | "离开编辑器?" | "离开" | "自动缩放" | "正式预览" | "添加到店铺" | "正在获取编辑锁…" | "该页面正在被编辑" | "页面为只读状态" | "你有未保存的更改。确定要离开吗?" | "选择一个区块以编辑。" | "桌面" | "平板" | "手机" | "打开区块面板" | "打开属性面板" | "
|
|
408
|
+
t: (key: AdminMessageKey) => "Desktop" | "Tablet" | "Mobile" | "未保存变更" | "Outline" | "Preview" | "缩放" | "区块" | "结构" | "撤销" | "恢复" | "收起左侧面板" | "收起属性面板" | "Delete" | "Duplicate" | "products" | "编辑锁已失效" | "属性" | "添加区块" | "复制" | "删除" | "确认删除区块?" | "删除后可使用撤销恢复。" | "取消" | "确认删除" | "上移" | "下移" | "正在加载编辑器…" | "页面还没有区块" | "编辑器无法加载" | "编辑器当前不可编辑" | "操作已完成" | "所有更改已保留在当前会话中" | "保存草稿" | "历史记录" | "正在保存…" | "发布" | "正在发布…" | "保存草稿失败" | "重试保存" | "当前离线,恢复网络后将自动保存" | "发布失败" | "已发布" | "返回" | "离开编辑器?" | "离开" | "自动缩放" | "正式预览" | "添加到店铺" | "正在获取编辑锁…" | "该页面正在被编辑" | "页面为只读状态" | "你有未保存的更改。确定要离开吗?" | "选择一个区块以编辑。" | "桌面" | "平板" | "手机" | "打开区块面板" | "打开属性面板" | "从 Shopify 选择商品" | "正在选择商品…" | "无法选择商品,请重试。" | "未选择商品" | "件商品" | "Blocks" | "Properties" | "Add block" | "Undo" | "Redo" | "Delete this block?" | "You can restore it with Undo after deleting." | "Cancel" | "Delete block" | "Move up" | "Move down" | "Loading editor…" | "This page has no blocks" | "The editor could not load" | "The editor is currently read-only" | "Action completed" | "Unsaved changes" | "Changes are kept in this session" | "Save draft" | "History" | "Saving…" | "Publish" | "Publishing…" | "Could not save draft" | "Retry save" | "Offline — changes will save when connection returns" | "Could not publish" | "Published" | "Back" | "Leave the editor?" | "Leave" | "Zoom" | "Auto zoom" | "Add to store" | "Acquiring edit lock…" | "This page is being edited" | "The edit lock has expired" | "This page is read-only" | "You have unsaved changes. Are you sure you want to leave?" | "Select a block to edit it." | "Collapse blocks panel" | "Collapse properties panel" | "Open blocks panel" | "Open properties panel" | "Select from Shopify" | "Selecting products…" | "Could not select products. Try again." | "No products selected";
|
|
382
409
|
};
|
|
383
410
|
|
|
384
411
|
declare function toEngineData(document: PageDocument, registry?: ExtensionRegistry): Data;
|
|
385
412
|
declare function fromEngineData(data: Data, base: PageDocument, registry?: ExtensionRegistry): PageDocument;
|
|
386
413
|
|
|
387
|
-
export { type AdminLocale, type AdminMessageKey, type AssetPickerAdapter, type AssetReference, type AssetStatus, BlockNode, BlockPolicy, type DeleteConfirmationConfig, type DemoBlock, type Device, type DraftPersistenceAdapter, type DraftSaveInput, type DraftSaveResult, type EditorActionState, type EditorLoadState, EditorOperationPolicy, EditorProvider, type EditorSession, type EditorSessionAcquireResult, type EditorSessionAdapter, type EditorSessionState, EditorShell, type EditorShellProps, type EditorState, ExtensionRegistry, JsonValue, PageDocument, type PageDocumentEditorPolicy, PageDocumentEditorShell, type PageDocumentEditorShellProps, type PagePublicationStatus, type PageStatus, PageStatusCard, type PageStatusCardProps, type PreviewMode, type PublishAction, type PublishActionInput, type PublishActionResult, type PublishState, type SaveState, ValidationIssue, type Zoom, createAdminI18n, fromEngineData, toEngineData, useEditorContext };
|
|
414
|
+
export { type AdminLocale, type AdminMessageKey, type AssetPickerAdapter, type AssetReference, type AssetStatus, BlockNode, BlockPolicy, type DeleteConfirmationConfig, type DemoBlock, type Device, type DraftPersistenceAdapter, type DraftSaveInput, type DraftSaveResult, type EditorActionState, type EditorLoadState, EditorOperationPolicy, EditorProvider, type EditorSession, type EditorSessionAcquireResult, type EditorSessionAdapter, type EditorSessionState, EditorShell, type EditorShellProps, type EditorState, ExtensionRegistry, JsonValue, PageDocument, type PageDocumentEditorPolicy, PageDocumentEditorShell, type PageDocumentEditorShellProps, type PagePublicationStatus, type PageStatus, PageStatusCard, type PageStatusCardProps, type PreviewMode, type ProductPickerAdapter, ProductReference, type PublishAction, type PublishActionInput, type PublishActionResult, type PublishState, type SaveState, ValidationIssue, type Zoom, createAdminI18n, fromEngineData, toEngineData, useEditorContext };
|
package/dist/index.js
CHANGED
|
@@ -4,13 +4,16 @@ import {
|
|
|
4
4
|
TemplateRegistry,
|
|
5
5
|
createExtensionRegistry,
|
|
6
6
|
createTemplateRegistry,
|
|
7
|
+
isProductReference,
|
|
7
8
|
maximumBlockInstances,
|
|
8
9
|
mergeBlockPolicies,
|
|
9
10
|
minimumBlockInstances,
|
|
11
|
+
parseProductReferences,
|
|
12
|
+
toProductReferenceJson,
|
|
10
13
|
validateBlockPolicy,
|
|
11
14
|
validateFieldValue,
|
|
12
15
|
validatePageDocumentWithRegistry
|
|
13
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-YXBTNYPR.js";
|
|
14
17
|
import {
|
|
15
18
|
WebRenderer
|
|
16
19
|
} from "./chunk-KYXIXIWK.js";
|
|
@@ -347,9 +350,9 @@ function PropertyPanel({
|
|
|
347
350
|
}
|
|
348
351
|
|
|
349
352
|
// src/editor/shell/PageDocumentEditorShell.tsx
|
|
350
|
-
import {
|
|
353
|
+
import { Puck as Puck2, usePuck } from "@puckeditor/core";
|
|
351
354
|
import { Badge as Badge3, Banner, Button as Button2, ButtonGroup as ButtonGroup2, InlineStack as InlineStack3, Select as Select2, Text as Text3, TextField } from "@shopify/polaris";
|
|
352
|
-
import { DragHandleIcon as DragHandleIcon2, LayoutSectionIcon as LayoutSectionIcon2, MenuIcon as MenuIcon2, RedoIcon as RedoIcon2, UndoIcon as UndoIcon2, XIcon as XIcon2 } from "@shopify/polaris-icons";
|
|
355
|
+
import { DragHandleIcon as DragHandleIcon2, LayoutSectionIcon as LayoutSectionIcon2, MenuIcon as MenuIcon2, ProductIcon, RedoIcon as RedoIcon2, UndoIcon as UndoIcon2, XIcon as XIcon2 } from "@shopify/polaris-icons";
|
|
353
356
|
import { useCallback as useCallback2, useEffect as useEffect3, useMemo as useMemo2, useRef as useRef4, useState as useState3 } from "react";
|
|
354
357
|
|
|
355
358
|
// src/adapters/puck/canvas-extension-block.tsx
|
|
@@ -786,6 +789,7 @@ var messages = {
|
|
|
786
789
|
unsaved: "\u672A\u4FDD\u5B58\u53D8\u66F4",
|
|
787
790
|
saved: "\u6240\u6709\u66F4\u6539\u5DF2\u4FDD\u7559\u5728\u5F53\u524D\u4F1A\u8BDD\u4E2D",
|
|
788
791
|
save: "\u4FDD\u5B58\u8349\u7A3F",
|
|
792
|
+
history: "\u5386\u53F2\u8BB0\u5F55",
|
|
789
793
|
saving: "\u6B63\u5728\u4FDD\u5B58\u2026",
|
|
790
794
|
publish: "\u53D1\u5E03",
|
|
791
795
|
publishing: "\u6B63\u5728\u53D1\u5E03\u2026",
|
|
@@ -814,9 +818,11 @@ var messages = {
|
|
|
814
818
|
collapseRight: "\u6536\u8D77\u5C5E\u6027\u9762\u677F",
|
|
815
819
|
expandLeft: "\u6253\u5F00\u533A\u5757\u9762\u677F",
|
|
816
820
|
expandRight: "\u6253\u5F00\u5C5E\u6027\u9762\u677F",
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
821
|
+
selectProducts: "\u4ECE Shopify \u9009\u62E9\u5546\u54C1",
|
|
822
|
+
selectingProducts: "\u6B63\u5728\u9009\u62E9\u5546\u54C1\u2026",
|
|
823
|
+
selectProductsFailed: "\u65E0\u6CD5\u9009\u62E9\u5546\u54C1\uFF0C\u8BF7\u91CD\u8BD5\u3002",
|
|
824
|
+
emptyProducts: "\u672A\u9009\u62E9\u5546\u54C1",
|
|
825
|
+
productCountSuffix: "\u4EF6\u5546\u54C1"
|
|
820
826
|
},
|
|
821
827
|
en: {
|
|
822
828
|
blocks: "Blocks",
|
|
@@ -841,6 +847,7 @@ var messages = {
|
|
|
841
847
|
unsaved: "Unsaved changes",
|
|
842
848
|
saved: "Changes are kept in this session",
|
|
843
849
|
save: "Save draft",
|
|
850
|
+
history: "History",
|
|
844
851
|
saving: "Saving\u2026",
|
|
845
852
|
publish: "Publish",
|
|
846
853
|
publishing: "Publishing\u2026",
|
|
@@ -869,9 +876,11 @@ var messages = {
|
|
|
869
876
|
collapseRight: "Collapse properties panel",
|
|
870
877
|
expandLeft: "Open blocks panel",
|
|
871
878
|
expandRight: "Open properties panel",
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
879
|
+
selectProducts: "Select from Shopify",
|
|
880
|
+
selectingProducts: "Selecting products\u2026",
|
|
881
|
+
selectProductsFailed: "Could not select products. Try again.",
|
|
882
|
+
emptyProducts: "No products selected",
|
|
883
|
+
productCountSuffix: "products"
|
|
875
884
|
}
|
|
876
885
|
};
|
|
877
886
|
function createAdminI18n(locale) {
|
|
@@ -899,12 +908,6 @@ function blockTypeLabel(type, registry) {
|
|
|
899
908
|
if (type === "core.image") return "\u56FE\u7247";
|
|
900
909
|
return registry?.getBlock(type)?.label ?? type;
|
|
901
910
|
}
|
|
902
|
-
function PuckPanelIcon({ side }) {
|
|
903
|
-
return /* @__PURE__ */ jsxs5("svg", { xmlns: "http://www.w3.org/2000/svg", width: "28", height: "28", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", focusable: "false", children: [
|
|
904
|
-
/* @__PURE__ */ jsx7("rect", { width: "18", height: "18", x: "3", y: "3", rx: "2" }),
|
|
905
|
-
/* @__PURE__ */ jsx7("path", { d: side === "left" ? "M9 3v18" : "M15 3v18" })
|
|
906
|
-
] });
|
|
907
|
-
}
|
|
908
911
|
function validateDocumentBlocks(document, registry) {
|
|
909
912
|
return registry ? validatePageDocumentWithRegistry(document, registry) : [];
|
|
910
913
|
}
|
|
@@ -957,7 +960,7 @@ function PageDocumentEditorShell(props) {
|
|
|
957
960
|
}, [managedSession.state, onSessionStateChange]);
|
|
958
961
|
return /* @__PURE__ */ jsx7(EditorProvider, { initialDocument: props.initialDocument, registry: props.registry, policy: props.policy, loadState: props.loadState, sessionState: managedSession.state, session: managedSession.session, sessionMessage: managedSession.message, leaveWarning: createAdminI18n(props.adminLocale).t("leaveWarning"), onDocumentChange: props.onDocumentChange, children: /* @__PURE__ */ jsx7(PageDocumentEditor, { ...props }) });
|
|
959
962
|
}
|
|
960
|
-
function PageDocumentEditor({ iframe = false, registry, adminLocale, onSave, onPublish, draftPersistence, draftRevision: initialDraftRevision, autoSave = true, autoSaveDelayMs = 800, publishAction, assetPicker, pageStatus, onBack, onPreview, onAddToStore, deleteConfirmation, availableBlockTypes, appearanceControls = false }) {
|
|
963
|
+
function PageDocumentEditor({ iframe = false, registry, adminLocale, onSave, onPublish, draftPersistence, draftRevision: initialDraftRevision, autoSave = true, autoSaveDelayMs = 800, publishAction, assetPicker, productPicker, pageStatus, onBack, onHistory, onPreview, onAddToStore, deleteConfirmation, availableBlockTypes, appearanceControls = false }) {
|
|
961
964
|
const editor = useEditorContext();
|
|
962
965
|
const [blockView, setBlockView] = useState3("blocks");
|
|
963
966
|
const [leftRailOpen, setLeftRailOpen] = useState3(true);
|
|
@@ -1104,18 +1107,15 @@ function PageDocumentEditor({ iframe = false, registry, adminLocale, onSave, onP
|
|
|
1104
1107
|
editor.document.target
|
|
1105
1108
|
] }),
|
|
1106
1109
|
pageStatus ? /* @__PURE__ */ jsx7(PageStatusCard, { status: pageStatus, sessionState: editor.sessionState }) : null
|
|
1107
|
-
] }),
|
|
1108
|
-
/* @__PURE__ */ jsxs5("div", { className: "pb-header-sidebar-toggles", role: "group", "aria-label": i18n.t("toggleSidebars"), "data-testid": "sidebar-toggles", children: [
|
|
1109
|
-
/* @__PURE__ */ jsx7(IconButton2, { type: "button", title: i18n.t("toggleLeftSidebar"), onClick: () => setLeftRailOpen((open) => !open), children: /* @__PURE__ */ jsx7(PuckPanelIcon, { side: "left" }) }),
|
|
1110
|
-
/* @__PURE__ */ jsx7(IconButton2, { type: "button", title: i18n.t("toggleRightSidebar"), onClick: () => setRightPanelOpen((open) => !open), children: /* @__PURE__ */ jsx7(PuckPanelIcon, { side: "right" }) })
|
|
1111
1110
|
] })
|
|
1112
1111
|
] }),
|
|
1113
|
-
/* @__PURE__ */ jsxs5("div", { className: "pb-header-device-toolbar", children: [
|
|
1112
|
+
/* @__PURE__ */ jsx7("div", { className: "pb-header-controls", children: /* @__PURE__ */ jsxs5("div", { className: "pb-header-device-toolbar", children: [
|
|
1114
1113
|
/* @__PURE__ */ jsx7(ButtonGroup2, { variant: "segmented", children: Object.keys(deviceLabels2).map((device) => /* @__PURE__ */ jsx7(Button2, { pressed: editor.device === device, onClick: () => editor.setDevice(device), children: i18n.t(deviceLabels2[device]) }, device)) }),
|
|
1115
1114
|
/* @__PURE__ */ jsx7("div", { className: "pb-zoom-control", children: /* @__PURE__ */ jsx7(Select2, { label: i18n.t("zoom"), labelHidden: true, options: [{ label: i18n.t("zoomAuto"), value: "auto" }, { label: "50%", value: "50" }, { label: "70%", value: "70" }, { label: "100%", value: "100" }], value: zoom, onChange: (value) => setZoom(value) }) })
|
|
1116
|
-
] }),
|
|
1117
|
-
/* @__PURE__ */ jsx7("div", { className: "pb-header-actions", children: /* @__PURE__ */ jsxs5(InlineStack3, { gap: "150", blockAlign: "center", wrap:
|
|
1115
|
+
] }) }),
|
|
1116
|
+
/* @__PURE__ */ jsx7("div", { className: "pb-header-actions", children: /* @__PURE__ */ jsxs5(InlineStack3, { gap: "150", blockAlign: "center", wrap: true, children: [
|
|
1118
1117
|
/* @__PURE__ */ jsx7(Badge3, { tone: saveBadgeTone, children: saveBadgeLabel }),
|
|
1118
|
+
onHistory ? /* @__PURE__ */ jsx7("span", { "data-editor-history": "", children: /* @__PURE__ */ jsx7(Button2, { disabled: request !== "idle", onClick: () => onHistory({ document: editor.document, draftRevision, session: editor.session }), children: i18n.t("history") }) }) : null,
|
|
1119
1119
|
/* @__PURE__ */ jsx7(Button2, { disabled: !canPersistDraft || !editor.isDirty || request !== "idle" || validationIssues.length > 0 || !isOnline, onClick: () => void save(), children: saveButtonLabel }),
|
|
1120
1120
|
onPreview ? /* @__PURE__ */ jsx7(Button2, { disabled: request !== "idle" || !isOnline, onClick: () => void onPreview({ document: editor.document, draftRevision, session: editor.session }), children: i18n.t("preview") }) : null,
|
|
1121
1121
|
onAddToStore ? /* @__PURE__ */ jsx7(Button2, { disabled: request !== "idle" || pageStatus?.publicationStatus === "unpublished", onClick: () => void onAddToStore({ document: editor.document, session: editor.session }), children: i18n.t("addToStore") }) : null,
|
|
@@ -1189,7 +1189,7 @@ function PageDocumentEditor({ iframe = false, registry, adminLocale, onSave, onP
|
|
|
1189
1189
|
/* @__PURE__ */ jsx7(Button2, { accessibilityLabel: i18n.t("collapseRight"), icon: XIcon2, variant: "tertiary", onClick: () => setRightPanelOpen(false) })
|
|
1190
1190
|
] }),
|
|
1191
1191
|
editor.selectedBlock ? /* @__PURE__ */ jsxs5(Fragment2, { children: [
|
|
1192
|
-
/* @__PURE__ */ jsx7(DocumentInspector, { pageId: editor.document.pageId, assetPicker, block: editor.selectedBlock, registry, disabled: !editor.actionState.canEdit, appearanceControls, onChange: (props) => editor.updateBlockProps(editor.selectedBlock.id, props), onPresentationChange: (presentation) => updateBlockPresentation(editor.selectedBlock.id, presentation) }),
|
|
1192
|
+
/* @__PURE__ */ jsx7(DocumentInspector, { pageId: editor.document.pageId, assetPicker, productPicker, i18n, block: editor.selectedBlock, registry, disabled: !editor.actionState.canEdit, appearanceControls, onChange: (props) => editor.updateBlockProps(editor.selectedBlock.id, props), onPresentationChange: (presentation) => updateBlockPresentation(editor.selectedBlock.id, presentation) }),
|
|
1193
1193
|
/* @__PURE__ */ jsx7(InspectorActions, { block: editor.selectedBlock, canDuplicate: editor.actionState.canDuplicate, canDelete: editor.actionState.canDelete, canMove: editor.actionState.canReorder, canMoveUp: editor.document.blocks[0]?.id !== editor.selectedBlock.id, canMoveDown: editor.document.blocks.at(-1)?.id !== editor.selectedBlock.id, i18n, onDuplicate: () => editor.duplicateBlock(editor.selectedBlock.id), onDelete: () => editor.requestDeleteBlock(editor.selectedBlock.id), onMove: (direction) => editor.moveBlock(editor.selectedBlock.id, direction) })
|
|
1194
1194
|
] }) : /* @__PURE__ */ jsx7(Text3, { as: "p", tone: "subdued", children: i18n.t("selectBlock") })
|
|
1195
1195
|
] })
|
|
@@ -1260,7 +1260,33 @@ function InspectorTextControl({ label, value, control, disabled, onChange }) {
|
|
|
1260
1260
|
if (control === "color") return /* @__PURE__ */ jsx7("input", { "aria-label": label, type: "color", value: /^#[\da-f]{6}$/i.test(stringValue) ? stringValue : "#000000", disabled, onChange: (event) => onChange(event.currentTarget.value) });
|
|
1261
1261
|
return /* @__PURE__ */ jsx7(TextField, { label, labelHidden: true, value: stringValue, onChange, autoComplete: "off", disabled, multiline: control === "textarea" ? 4 : false, type: control === "url" ? "url" : "text" });
|
|
1262
1262
|
}
|
|
1263
|
-
function
|
|
1263
|
+
function InspectorProductsControl({ value, disabled, picker, pageId, blockId, i18n, onChange }) {
|
|
1264
|
+
const products = parseProductReferences(value);
|
|
1265
|
+
const [busy, setBusy] = useState3(false);
|
|
1266
|
+
const [error, setError] = useState3(null);
|
|
1267
|
+
const preview = products.slice(0, 4);
|
|
1268
|
+
return /* @__PURE__ */ jsxs5("div", { className: "pb-product-picker", "data-testid": "product-picker", children: [
|
|
1269
|
+
/* @__PURE__ */ jsxs5("div", { className: "pb-product-picker__summary", children: [
|
|
1270
|
+
preview.length ? /* @__PURE__ */ jsx7("span", { className: "pb-product-picker__thumbs", "aria-hidden": "true", children: preview.map((product, index) => /* @__PURE__ */ jsx7("span", { className: "pb-product-picker__thumb", style: { zIndex: preview.length - index }, children: product.imageUrl ? /* @__PURE__ */ jsx7("img", { src: product.imageUrl, alt: "" }) : /* @__PURE__ */ jsx7("span", { className: "pb-product-picker__thumb-fallback" }) }, product.id)) }) : null,
|
|
1271
|
+
/* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodySm", children: products.length ? `${products.length} ${i18n.t("productCountSuffix")}` : i18n.t("emptyProducts") })
|
|
1272
|
+
] }),
|
|
1273
|
+
/* @__PURE__ */ jsx7(Button2, { fullWidth: true, icon: ProductIcon, variant: "secondary", disabled: disabled || busy || !picker, onClick: () => void (async () => {
|
|
1274
|
+
if (!picker) return;
|
|
1275
|
+
setBusy(true);
|
|
1276
|
+
setError(null);
|
|
1277
|
+
try {
|
|
1278
|
+
const next = await picker.selectProducts({ pageId, blockId, current: products, multiple: true });
|
|
1279
|
+
if (next) onChange(parseProductReferences(next).map(toProductReferenceJson));
|
|
1280
|
+
} catch {
|
|
1281
|
+
setError(i18n.t("selectProductsFailed"));
|
|
1282
|
+
} finally {
|
|
1283
|
+
setBusy(false);
|
|
1284
|
+
}
|
|
1285
|
+
})(), children: busy ? i18n.t("selectingProducts") : i18n.t("selectProducts") }),
|
|
1286
|
+
error ? /* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodySm", tone: "critical", children: error }) : null
|
|
1287
|
+
] });
|
|
1288
|
+
}
|
|
1289
|
+
function InspectorField({ name, field, value, registry, disabled, productPicker, pageId, blockId, i18n, onChange }) {
|
|
1264
1290
|
const label = field.label ?? name;
|
|
1265
1291
|
const issues = validateFieldValue(field, value);
|
|
1266
1292
|
const Field = registry?.getField(field.field)?.component;
|
|
@@ -1269,7 +1295,7 @@ function InspectorField({ name, field, value, registry, disabled, onChange }) {
|
|
|
1269
1295
|
/* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodySm", fontWeight: "semibold", children: label }),
|
|
1270
1296
|
field.description ? /* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodySm", tone: "subdued", children: field.description }) : null
|
|
1271
1297
|
] }),
|
|
1272
|
-
field.control ? /* @__PURE__ */ jsx7(InspectorTextControl, { label, value, control: field.control, disabled, onChange: (next) => onChange(next) }) : Field ? /* @__PURE__ */ jsx7(Field, { value, onChange }) : null,
|
|
1298
|
+
field.control === "products" ? /* @__PURE__ */ jsx7(InspectorProductsControl, { value, disabled, picker: productPicker, pageId, blockId, i18n, onChange }) : field.control ? /* @__PURE__ */ jsx7(InspectorTextControl, { label, value, control: field.control, disabled, onChange: (next) => onChange(next) }) : Field ? /* @__PURE__ */ jsx7(Field, { value, onChange }) : null,
|
|
1273
1299
|
issues.map((issue) => /* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodySm", tone: "critical", children: issue.message }, issue.message))
|
|
1274
1300
|
] });
|
|
1275
1301
|
}
|
|
@@ -1308,7 +1334,7 @@ function inspectorFieldConfig(name, field) {
|
|
|
1308
1334
|
return { ...field, group: field.group ?? group, description };
|
|
1309
1335
|
}
|
|
1310
1336
|
var appearanceTokens = [["color.primary", "\u4E3B\u8272"], ["color.surface", "\u8868\u9762\u8272"], ["radius", "\u5706\u89D2"], ["spacing", "\u95F4\u8DDD"]];
|
|
1311
|
-
function DocumentInspector({ pageId, assetPicker, block, registry, disabled, appearanceControls, onChange, onPresentationChange }) {
|
|
1337
|
+
function DocumentInspector({ pageId, assetPicker, productPicker, i18n, block, registry, disabled, appearanceControls, onChange, onPresentationChange }) {
|
|
1312
1338
|
const [assetError, setAssetError] = useState3(null);
|
|
1313
1339
|
const [selectingAsset, setSelectingAsset] = useState3(false);
|
|
1314
1340
|
const definition = registry?.getBlock(block.type);
|
|
@@ -1327,10 +1353,10 @@ function DocumentInspector({ pageId, assetPicker, block, registry, disabled, app
|
|
|
1327
1353
|
/* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodySm", tone: "subdued", children: definition?.category ?? "Core block" })
|
|
1328
1354
|
] })
|
|
1329
1355
|
] }),
|
|
1330
|
-
block.type === "core.text" ? /* @__PURE__ */ jsx7(InspectorSection, { title: "Content", children: /* @__PURE__ */ jsx7(InspectorField, { name: "content", field: { field: "", label: "\u6587\u672C\u5185\u5BB9", control: "textarea", description: "\u652F\u6301\u8F83\u957F\u7684\u6B63\u6587\u5185\u5BB9\u3002" }, value: block.props.content, registry, disabled, onChange: (content) => onChange({ content }) }) }) : null,
|
|
1356
|
+
block.type === "core.text" ? /* @__PURE__ */ jsx7(InspectorSection, { title: "Content", children: /* @__PURE__ */ jsx7(InspectorField, { name: "content", field: { field: "", label: "\u6587\u672C\u5185\u5BB9", control: "textarea", description: "\u652F\u6301\u8F83\u957F\u7684\u6B63\u6587\u5185\u5BB9\u3002" }, value: block.props.content, registry, disabled, productPicker, pageId, blockId: block.id, i18n, onChange: (content) => onChange({ content }) }) }) : null,
|
|
1331
1357
|
block.type === "core.image" ? /* @__PURE__ */ jsxs5(InspectorSection, { title: "Image", children: [
|
|
1332
|
-
/* @__PURE__ */ jsx7(InspectorField, { name: "src", field: { field: "", label: "\u56FE\u7247 URL", control: "url", description: "\u4F7F\u7528 HTTPS \u56FE\u7247\u5730\u5740\u3002" }, value: block.props.src, registry, disabled, onChange: (src) => onChange({ src }) }),
|
|
1333
|
-
/* @__PURE__ */ jsx7(InspectorField, { name: "alt", field: { field: "", label: "\u66FF\u4EE3\u6587\u672C", control: "text", description: "\u7528\u4E8E\u65E0\u969C\u788D\u9605\u8BFB\u548C\u56FE\u7247\u52A0\u8F7D\u5931\u8D25\u573A\u666F\u3002" }, value: block.props.alt, registry, disabled, onChange: (alt) => onChange({ alt }) }),
|
|
1358
|
+
/* @__PURE__ */ jsx7(InspectorField, { name: "src", field: { field: "", label: "\u56FE\u7247 URL", control: "url", description: "\u4F7F\u7528 HTTPS \u56FE\u7247\u5730\u5740\u3002" }, value: block.props.src, registry, disabled, productPicker, pageId, blockId: block.id, i18n, onChange: (src) => onChange({ src }) }),
|
|
1359
|
+
/* @__PURE__ */ jsx7(InspectorField, { name: "alt", field: { field: "", label: "\u66FF\u4EE3\u6587\u672C", control: "text", description: "\u7528\u4E8E\u65E0\u969C\u788D\u9605\u8BFB\u548C\u56FE\u7247\u52A0\u8F7D\u5931\u8D25\u573A\u666F\u3002" }, value: block.props.alt, registry, disabled, productPicker, pageId, blockId: block.id, i18n, onChange: (alt) => onChange({ alt }) }),
|
|
1334
1360
|
assetPicker ? /* @__PURE__ */ jsxs5(Fragment2, { children: [
|
|
1335
1361
|
/* @__PURE__ */ jsx7(Button2, { disabled: disabled || selectingAsset, onClick: () => void (async () => {
|
|
1336
1362
|
setSelectingAsset(true);
|
|
@@ -1347,7 +1373,7 @@ function DocumentInspector({ pageId, assetPicker, block, registry, disabled, app
|
|
|
1347
1373
|
assetError ? /* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodySm", tone: "critical", children: assetError }) : null
|
|
1348
1374
|
] }) : null
|
|
1349
1375
|
] }) : null,
|
|
1350
|
-
Object.entries(groupedFields).map(([group, fields]) => /* @__PURE__ */ jsx7(InspectorSection, { title: group, defaultOpen: group !== "Advanced", children: fields.map(([name, field]) => /* @__PURE__ */ jsx7(InspectorField, { name, field, value: block.props[name], registry, disabled, onChange: (value) => onChange({ ...block.props, [name]: value }) }, name)) }, group)),
|
|
1376
|
+
Object.entries(groupedFields).map(([group, fields]) => /* @__PURE__ */ jsx7(InspectorSection, { title: group, defaultOpen: group !== "Advanced", children: fields.map(([name, field]) => /* @__PURE__ */ jsx7(InspectorField, { name, field, value: block.props[name], registry, disabled, productPicker, pageId, blockId: block.id, i18n, onChange: (value) => onChange({ ...block.props, [name]: value }) }, name)) }, group)),
|
|
1351
1377
|
appearanceControls && definition?.variants?.length ? /* @__PURE__ */ jsx7(InspectorSection, { title: "\u5916\u89C2", children: /* @__PURE__ */ jsx7(Select2, { label: "\u6837\u5F0F\u53D8\u4F53", options: definition.variants.map((variant) => ({ label: variant.label, value: variant.id })), value: block.variant, disabled, onChange: (variant) => onPresentationChange({ variant, style: block.style }) }) }) : null,
|
|
1352
1378
|
appearanceControls && definition ? /* @__PURE__ */ jsx7(InspectorSection, { title: "\u6837\u5F0F\u8986\u76D6", defaultOpen: false, children: appearanceTokens.map(([token, label]) => /* @__PURE__ */ jsx7(TextField, { label, value: block.style[token] ?? "", placeholder: "\u7EE7\u627F\u9875\u9762\u6216\u6A21\u677F\u8BBE\u7F6E", autoComplete: "off", disabled, onChange: (value) => {
|
|
1353
1379
|
const style = { ...block.style };
|
|
@@ -1371,14 +1397,17 @@ export {
|
|
|
1371
1397
|
createPageDocument,
|
|
1372
1398
|
createTemplateRegistry,
|
|
1373
1399
|
fromEngineData,
|
|
1400
|
+
isProductReference,
|
|
1374
1401
|
maximumBlockInstances,
|
|
1375
1402
|
mergeBlockPolicies,
|
|
1376
1403
|
mergeThemeTokens,
|
|
1377
1404
|
migratePageDocument,
|
|
1378
1405
|
minimumBlockInstances,
|
|
1379
1406
|
normalizeThemeTokens,
|
|
1407
|
+
parseProductReferences,
|
|
1380
1408
|
systemThemeTokens,
|
|
1381
1409
|
toEngineData,
|
|
1410
|
+
toProductReferenceJson,
|
|
1382
1411
|
toThemeStyle,
|
|
1383
1412
|
useEditorContext,
|
|
1384
1413
|
validateBlockPolicy,
|
|
@@ -42,7 +42,7 @@ type FieldConfig = {
|
|
|
42
42
|
field: string;
|
|
43
43
|
label?: string;
|
|
44
44
|
/** Product-facing editor metadata. Custom field components remain supported. */
|
|
45
|
-
control?: "text" | "textarea" | "url" | "color";
|
|
45
|
+
control?: "text" | "textarea" | "url" | "color" | "products";
|
|
46
46
|
description?: string;
|
|
47
47
|
group?: string;
|
|
48
48
|
/** Kept for compatibility; it participates in the built-in validation. */
|
package/dist/renderer.d.ts
CHANGED
package/dist/runtime.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { WebRenderer, WebRendererProps } from './renderer.js';
|
|
2
|
-
export { B as BlockDefinition, a as BlockEditorProps, b as BlockOperation, c as BlockPolicy, d as BlockVariantDefinition, D as DataSourceDefinition, E as EditorAction, e as EditorActionPosition, f as EditorOperationPolicy, g as ExtensionActionContext, h as ExtensionRegistry, i as ExtensionRegistryError, j as ExtensionRegistryErrorCode, k as ExtensionRegistryOptions, l as ExtensionTarget, F as FieldConfig, m as FieldDefinition, n as FieldProps, o as FieldValidation, L as LifecycleHooks, P as PageBuilderExtension, R as RendererDefinition, T as TemplateDefinition, p as TemplateRegistry, q as TemplateSource, U as UISlotContribution, r as UISlotName, V as ValidationIssue, s as createExtensionRegistry, t as createTemplateRegistry } from './registry-
|
|
3
|
-
export { maximumBlockInstances, mergeBlockPolicies, minimumBlockInstances, validateBlockPolicy, validateFieldValue, validatePageDocumentWithRegistry } from './extensions.js';
|
|
2
|
+
export { B as BlockDefinition, a as BlockEditorProps, b as BlockOperation, c as BlockPolicy, d as BlockVariantDefinition, D as DataSourceDefinition, E as EditorAction, e as EditorActionPosition, f as EditorOperationPolicy, g as ExtensionActionContext, h as ExtensionRegistry, i as ExtensionRegistryError, j as ExtensionRegistryErrorCode, k as ExtensionRegistryOptions, l as ExtensionTarget, F as FieldConfig, m as FieldDefinition, n as FieldProps, o as FieldValidation, L as LifecycleHooks, P as PageBuilderExtension, R as RendererDefinition, T as TemplateDefinition, p as TemplateRegistry, q as TemplateSource, U as UISlotContribution, r as UISlotName, V as ValidationIssue, s as createExtensionRegistry, t as createTemplateRegistry } from './registry-CXJdOya3.js';
|
|
3
|
+
export { ProductReference, isProductReference, maximumBlockInstances, mergeBlockPolicies, minimumBlockInstances, parseProductReferences, toProductReferenceJson, validateBlockPolicy, validateFieldValue, validatePageDocumentWithRegistry } from './extensions.js';
|
|
4
4
|
export { BlockNode, BlockStyleOverrides, DataBinding, JsonPrimitive, JsonValue, PageDocument, PageDocumentIssue, PageDocumentMigration, PageDocumentSchemaVersion, PageDocumentValidation, PageSettings, RenderTarget, createPageDocument, migratePageDocument, validatePageDocument } from './schema.js';
|
|
5
5
|
export { ThemeTokenName, ThemeTokenValidation, ThemeTokens, mergeThemeTokens, normalizeThemeTokens, systemThemeTokens, toThemeStyle } from './theme.js';
|
|
6
6
|
import 'react';
|
package/dist/runtime.js
CHANGED
|
@@ -4,13 +4,16 @@ import {
|
|
|
4
4
|
TemplateRegistry,
|
|
5
5
|
createExtensionRegistry,
|
|
6
6
|
createTemplateRegistry,
|
|
7
|
+
isProductReference,
|
|
7
8
|
maximumBlockInstances,
|
|
8
9
|
mergeBlockPolicies,
|
|
9
10
|
minimumBlockInstances,
|
|
11
|
+
parseProductReferences,
|
|
12
|
+
toProductReferenceJson,
|
|
10
13
|
validateBlockPolicy,
|
|
11
14
|
validateFieldValue,
|
|
12
15
|
validatePageDocumentWithRegistry
|
|
13
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-YXBTNYPR.js";
|
|
14
17
|
import {
|
|
15
18
|
WebRenderer
|
|
16
19
|
} from "./chunk-KYXIXIWK.js";
|
|
@@ -33,13 +36,16 @@ export {
|
|
|
33
36
|
createExtensionRegistry,
|
|
34
37
|
createPageDocument,
|
|
35
38
|
createTemplateRegistry,
|
|
39
|
+
isProductReference,
|
|
36
40
|
maximumBlockInstances,
|
|
37
41
|
mergeBlockPolicies,
|
|
38
42
|
mergeThemeTokens,
|
|
39
43
|
migratePageDocument,
|
|
40
44
|
minimumBlockInstances,
|
|
41
45
|
normalizeThemeTokens,
|
|
46
|
+
parseProductReferences,
|
|
42
47
|
systemThemeTokens,
|
|
48
|
+
toProductReferenceJson,
|
|
43
49
|
toThemeStyle,
|
|
44
50
|
validateBlockPolicy,
|
|
45
51
|
validateFieldValue,
|
package/package.json
CHANGED
package/src/styles.css
CHANGED
|
@@ -7,16 +7,18 @@ button:focus-visible, [role="button"]:focus-visible { outline: 2px solid var(--b
|
|
|
7
7
|
.pb-shell { min-height: 100vh; color: var(--builder-text); background: var(--builder-canvas-bg); }
|
|
8
8
|
.pb-shell [class*="PuckLayout-inner"] { height: auto !important; min-height: 100%; }
|
|
9
9
|
.pb-header { position: sticky; top: 0; z-index: 20; min-height: 56px; padding: var(--builder-space-2) var(--builder-space-4); border-bottom: 1px solid var(--builder-border); background: var(--builder-surface); }
|
|
10
|
-
.pb-header-content { display:
|
|
11
|
-
.pb-header-title-group { display: flex; align-items: center; gap: var(--builder-space-2); min-width: 0; }
|
|
10
|
+
.pb-header-content { display: flex; flex-wrap: wrap; align-items: center; gap: var(--builder-space-2) var(--builder-space-3); }
|
|
11
|
+
.pb-header-title-group { display: flex; flex: 1 1 0; align-items: center; gap: var(--builder-space-2); min-width: 0; }
|
|
12
|
+
.pb-header-sidebar-toggles, [class*="PuckHeader-toggle"] { display: none !important; }
|
|
12
13
|
.pb-page-title { display: grid; gap: 0; min-width: 0; }
|
|
13
|
-
.pb-
|
|
14
|
-
.pb-header-sidebar-toggles [class*="IconButton"] { min-width: 28px; min-height: 28px; padding: 0; color: var(--builder-text); }
|
|
15
|
-
.pb-header-sidebar-toggles svg { display: block; width: 28px; height: 28px; }
|
|
14
|
+
.pb-page-title h1, .pb-page-title p { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
16
15
|
.pb-page-title p { margin: 0; }
|
|
17
|
-
.pb-header-
|
|
18
|
-
.pb-header-
|
|
19
|
-
.pb-
|
|
16
|
+
.pb-header-controls { display: flex; flex: 0 0 auto; align-items: center; justify-content: center; }
|
|
17
|
+
.pb-header-device-toolbar { display: flex; align-items: center; gap: var(--builder-space-2); }
|
|
18
|
+
.pb-header-actions { display: flex; flex: 1 1 0; justify-content: flex-end; min-width: 0; }
|
|
19
|
+
.pb-header-actions .Polaris-InlineStack { flex-wrap: wrap; justify-content: flex-end; }
|
|
20
|
+
.pb-header-actions .Polaris-Badge { max-width: 14rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
21
|
+
.pb-zoom-control { width: 112px; flex: 0 0 auto; }
|
|
20
22
|
.pb-page-status-card { display: grid; gap: 2px; margin-top: var(--builder-space-1); }
|
|
21
23
|
.pb-page-status-card p { margin: 0; }
|
|
22
24
|
.pb-workspace { display: grid; grid-template-columns: 64px minmax(260px, 300px) minmax(0, 1fr) minmax(300px, 320px); min-height: calc(100vh - 56px); }
|
|
@@ -50,6 +52,14 @@ button:focus-visible, [role="button"]:focus-visible { outline: 2px solid var(--b
|
|
|
50
52
|
.pb-inspector-field input:hover, .pb-inspector-field textarea:hover { border-color: #94a3b8; }
|
|
51
53
|
.pb-inspector-field input:focus, .pb-inspector-field textarea:focus { border-color: #2563eb; box-shadow: 0 0 0 3px rgb(37 99 235 / 12%); }
|
|
52
54
|
.pb-inspector-field input:disabled, .pb-inspector-field textarea:disabled { color: #94a3b8; background: #f8fafc; cursor: not-allowed; }
|
|
55
|
+
.pb-product-picker { display: grid; gap: 8px; }
|
|
56
|
+
.pb-product-picker__summary { display: flex; align-items: center; gap: 10px; min-height: 44px; padding: 10px 12px; border: 1px solid #e2e8f0; border-radius: 10px; background: #fff; }
|
|
57
|
+
.pb-product-picker__summary p { margin: 0; color: #334155; }
|
|
58
|
+
.pb-product-picker__thumbs { display: flex; align-items: center; }
|
|
59
|
+
.pb-product-picker__thumb { display: inline-flex; width: 32px; height: 32px; margin-left: -8px; overflow: hidden; border: 2px solid #fff; border-radius: 8px; background: #e2e8f0; box-shadow: 0 0 0 1px #e2e8f0; }
|
|
60
|
+
.pb-product-picker__thumb:first-child { margin-left: 0; }
|
|
61
|
+
.pb-product-picker__thumb img, .pb-product-picker__thumb-fallback { display: block; width: 100%; height: 100%; object-fit: cover; }
|
|
62
|
+
.pb-product-picker__thumb-fallback { background: #cbd5e1; }
|
|
53
63
|
.pb-inspector__hint { margin: 0; padding: 0 var(--builder-space-2); color: #64748b; font-size: 12px; line-height: 1.5; }
|
|
54
64
|
.pb-inspector-actions { padding-top: var(--builder-space-2); border-top: 1px solid #e2e8f0; }
|
|
55
65
|
.pb-delete-confirmation-backdrop { position: fixed; z-index: 1000; inset: 0; display: grid; place-items: center; padding: var(--builder-space-4); background: rgb(15 23 42 / 42%); }
|
|
@@ -98,7 +108,8 @@ button:focus-visible, [role="button"]:focus-visible { outline: 2px solid var(--b
|
|
|
98
108
|
.pb-library-block-drag-hint svg { width: 16px; height: 16px; }
|
|
99
109
|
.pb-document-block-row:hover { background: var(--builder-surface-subtle); }
|
|
100
110
|
.pb-document-block-row--selected { border-color: var(--builder-accent); background: var(--builder-accent-subtle); }
|
|
101
|
-
.pb-editor-status { display: grid; place-items: center; min-height:
|
|
111
|
+
.pb-editor-status { display: grid; place-items: center; box-sizing: border-box; width: 100%; min-height: 100dvh; padding: var(--builder-space-6); }
|
|
112
|
+
.pb-editor-status > * { width: min(100%, 28rem); justify-self: center; }
|
|
102
113
|
.pb-shell--v04 > .Polaris-Banner { margin: var(--builder-space-3) var(--builder-space-4) 0; }
|
|
103
114
|
.pb-document-canvas__text, .pb-document-canvas__image { max-width: 760px; margin: 0 auto; padding: 48px; color: #1d2939; background: #fff; cursor: pointer; }
|
|
104
115
|
.pb-document-canvas__text { font-size: clamp(32px, 4vw, 52px); font-weight: 650; line-height: 1.1; letter-spacing: -.03em; }
|
|
@@ -131,5 +142,5 @@ button:focus-visible, [role="button"]:focus-visible { outline: 2px solid var(--b
|
|
|
131
142
|
.bt-storefront__form input { flex: 1; min-width: 0; padding: 13px 14px; border: 1px solid #b8c3cc; border-radius: 8px; font: inherit; }
|
|
132
143
|
.bt-storefront__form button { padding: 13px 18px; border: 0; border-radius: 8px; color: white; background: #203e54; font: inherit; font-weight: 600; }
|
|
133
144
|
@media (max-width: 1180px) { .pb-workspace { grid-template-columns: 64px minmax(260px, 300px) minmax(0, 1fr); } .pb-workspace--left-closed { grid-template-columns: 64px minmax(0, 1fr); } .pb-right-panel { grid-column: 2 / -1; border-top: 1px solid var(--builder-border); border-left: 0; } }
|
|
134
|
-
@media (max-width: 1023px) { .pb-header { position: static;
|
|
135
|
-
@media (max-width: 640px) { .pb-header, .pb-canvas-area { padding: var(--builder-space-2); } .pb-header-content {
|
|
145
|
+
@media (max-width: 1023px) { .pb-header { position: static; } .pb-workspace { grid-template-columns: 64px minmax(0, 1fr); } .pb-left-panel, .pb-right-panel { grid-column: 2; border-left: 0; border-right: 0; border-bottom: 1px solid var(--builder-border); } .pb-canvas-area { grid-column: 2; } }
|
|
146
|
+
@media (max-width: 640px) { .pb-header, .pb-canvas-area { padding: var(--builder-space-2); } .pb-header-title-group, .pb-header-controls, .pb-header-actions { flex: 1 1 100%; } .pb-header-controls, .pb-header-actions { justify-content: flex-start; } .pb-header-actions .Polaris-InlineStack { justify-content: flex-start; } .pb-canvas-stage { padding: 0; } .pb-canvas-frame--tablet, .pb-canvas-frame--mobile { width: 100%; } .bt-storefront { padding: 28px 20px; } .bt-storefront__form { flex-direction: column; } }
|