@standhigher/puck-page-builder 0.11.0 → 0.12.1
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-RLKWR5JM.js} +53 -2
- package/dist/extensions.d.ts +18 -4
- package/dist/extensions.js +7 -1
- package/dist/index.d.ts +55 -11
- package/dist/index.js +116 -41
- package/dist/{registry-eNtkEC6o.d.ts → registry-BkqtMP-A.d.ts} +3 -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 +24 -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,13 +62,17 @@ 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" });
|
|
25
72
|
if (control === "text" && /[\r\n]/.test(value)) issues.push({ path: "", message: "\u5FC5\u987B\u4E3A\u5355\u884C\u6587\u672C\u3002" });
|
|
26
73
|
if (config.validation?.minLength !== void 0 && stringLength(value) < config.validation.minLength) issues.push({ path: "", message: `\u81F3\u5C11\u9700\u8981 ${config.validation.minLength} \u4E2A\u5B57\u7B26\u3002` });
|
|
27
74
|
if (config.validation?.maxLength !== void 0 && stringLength(value) > config.validation.maxLength) issues.push({ path: "", message: `\u6700\u591A\u5141\u8BB8 ${config.validation.maxLength} \u4E2A\u5B57\u7B26\u3002` });
|
|
28
|
-
if (value && control === "url" && !isSafeUrl(value, config)) issues.push({ path: "", message: "\u5FC5\u987B\u662F\u7AD9\u5185\u76F8\u5BF9\u8DEF\u5F84\u6216\u5141\u8BB8\u7684 HTTP(S) URL\u3002" });
|
|
75
|
+
if (value && (control === "url" || control === "asset") && !isSafeUrl(value, config)) issues.push({ path: "", message: "\u5FC5\u987B\u662F\u7AD9\u5185\u76F8\u5BF9\u8DEF\u5F84\u6216\u5141\u8BB8\u7684 HTTP(S) URL\u3002" });
|
|
29
76
|
if (value && control === "color" && !hexColor.test(value)) issues.push({ path: "", message: "\u5FC5\u987B\u662F\u5341\u516D\u8FDB\u5236\u989C\u8272\uFF0C\u4F8B\u5982 #005BD3\u3002" });
|
|
30
77
|
return issues;
|
|
31
78
|
}
|
|
@@ -280,11 +327,12 @@ function validateBlock(document, index, registry) {
|
|
|
280
327
|
if (block.version !== definition.version) issues.push({ path: `${path}.version`, message: `\u533A\u5757\u7248\u672C\u5FC5\u987B\u4E3A ${definition.version}\u3002` });
|
|
281
328
|
if (!supportsTarget(definition, document.target)) issues.push({ path, message: `\u533A\u5757\u4E0D\u652F\u6301 ${document.target} target\u3002` });
|
|
282
329
|
if (definition.variants?.length && !definition.variants.some((variant) => variant.id === block.variant)) issues.push({ path: `${path}.variant`, message: `\u4E0D\u652F\u6301\u7684 variant\uFF1A${block.variant}\u3002` });
|
|
283
|
-
const allowedProps = /* @__PURE__ */ new Set([...Object.keys(definition.defaultProps), ...Object.
|
|
330
|
+
const allowedProps = /* @__PURE__ */ new Set([...Object.keys(definition.defaultProps), ...Object.entries(definition.fields).filter(([, field]) => field.persist !== false).map(([name]) => name)]);
|
|
284
331
|
for (const key of Object.keys(block.props)) {
|
|
285
332
|
if (!allowedProps.has(key)) issues.push({ path: `${path}.props.${key}`, message: "\u4E0D\u5141\u8BB8\u4FDD\u5B58\u672A\u77E5\u5B57\u6BB5\u3002" });
|
|
286
333
|
}
|
|
287
334
|
for (const [name, field] of Object.entries(definition.fields)) {
|
|
335
|
+
if (field.persist === false) continue;
|
|
288
336
|
for (const issue of validateFieldValue(field, block.props[name])) {
|
|
289
337
|
issues.push({ ...issue, path: issue.path ? `${path}.props.${name}.${issue.path}` : `${path}.props.${name}` });
|
|
290
338
|
}
|
|
@@ -314,6 +362,9 @@ function validatePageDocumentWithRegistry(document, registry) {
|
|
|
314
362
|
}
|
|
315
363
|
|
|
316
364
|
export {
|
|
365
|
+
isProductReference,
|
|
366
|
+
parseProductReferences,
|
|
367
|
+
toProductReferenceJson,
|
|
317
368
|
validateFieldValue,
|
|
318
369
|
validateBlockPolicy,
|
|
319
370
|
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-BkqtMP-A.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-BkqtMP-A.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-RLKWR5JM.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-BkqtMP-A.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-BkqtMP-A.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,23 @@ type PageDocumentEditorShellProps = {
|
|
|
233
247
|
publishAction?: PublishAction;
|
|
234
248
|
/** Host-owned picker/uploader for shop-scoped assets. */
|
|
235
249
|
assetPicker?: AssetPickerAdapter;
|
|
250
|
+
/** External settings, keyed by block id (or block type for shared settings), outside document history and persistence. */
|
|
251
|
+
inspectorSettings?: Record<string, {
|
|
252
|
+
values: Record<string, JsonValue>;
|
|
253
|
+
disabled?: boolean;
|
|
254
|
+
onChange: (fieldName: string, value: JsonValue) => void;
|
|
255
|
+
footer?: ReactNode;
|
|
256
|
+
}>;
|
|
257
|
+
/** Host-owned product picker. The shell only shows the selected snapshot and a button. */
|
|
258
|
+
productPicker?: ProductPickerAdapter;
|
|
236
259
|
/** Optional reusable page lifecycle summary. */
|
|
237
260
|
pageStatus?: PageStatus;
|
|
238
261
|
onBack?: () => void;
|
|
262
|
+
onHistory?: (input: {
|
|
263
|
+
document: PageDocument;
|
|
264
|
+
draftRevision?: number;
|
|
265
|
+
session?: EditorSession;
|
|
266
|
+
}) => void;
|
|
239
267
|
onPreview?: (input: {
|
|
240
268
|
document: PageDocument;
|
|
241
269
|
draftRevision?: number;
|
|
@@ -286,6 +314,7 @@ declare const messages: {
|
|
|
286
314
|
readonly unsaved: "未保存变更";
|
|
287
315
|
readonly saved: "所有更改已保留在当前会话中";
|
|
288
316
|
readonly save: "保存草稿";
|
|
317
|
+
readonly history: "历史记录";
|
|
289
318
|
readonly saving: "正在保存…";
|
|
290
319
|
readonly publish: "发布";
|
|
291
320
|
readonly publishing: "正在发布…";
|
|
@@ -314,9 +343,16 @@ declare const messages: {
|
|
|
314
343
|
readonly collapseRight: "收起属性面板";
|
|
315
344
|
readonly expandLeft: "打开区块面板";
|
|
316
345
|
readonly expandRight: "打开属性面板";
|
|
317
|
-
readonly
|
|
318
|
-
readonly
|
|
319
|
-
readonly
|
|
346
|
+
readonly selectProducts: "从 Shopify 选择商品";
|
|
347
|
+
readonly selectingProducts: "正在选择商品…";
|
|
348
|
+
readonly selectProductsFailed: "无法选择商品,请重试。";
|
|
349
|
+
readonly emptyProducts: "未选择商品";
|
|
350
|
+
readonly productCountSuffix: "件商品";
|
|
351
|
+
readonly selectAsset: "选择图片";
|
|
352
|
+
readonly selectingAsset: "正在上传图片…";
|
|
353
|
+
readonly selectAssetFailed: "无法上传图片,请重试。";
|
|
354
|
+
readonly emptyAsset: "未设置图片";
|
|
355
|
+
readonly removeAsset: "移除图片";
|
|
320
356
|
};
|
|
321
357
|
readonly en: {
|
|
322
358
|
readonly blocks: "Blocks";
|
|
@@ -341,6 +377,7 @@ declare const messages: {
|
|
|
341
377
|
readonly unsaved: "Unsaved changes";
|
|
342
378
|
readonly saved: "Changes are kept in this session";
|
|
343
379
|
readonly save: "Save draft";
|
|
380
|
+
readonly history: "History";
|
|
344
381
|
readonly saving: "Saving…";
|
|
345
382
|
readonly publish: "Publish";
|
|
346
383
|
readonly publishing: "Publishing…";
|
|
@@ -369,19 +406,26 @@ declare const messages: {
|
|
|
369
406
|
readonly collapseRight: "Collapse properties panel";
|
|
370
407
|
readonly expandLeft: "Open blocks panel";
|
|
371
408
|
readonly expandRight: "Open properties panel";
|
|
372
|
-
readonly
|
|
373
|
-
readonly
|
|
374
|
-
readonly
|
|
409
|
+
readonly selectProducts: "Select from Shopify";
|
|
410
|
+
readonly selectingProducts: "Selecting products…";
|
|
411
|
+
readonly selectProductsFailed: "Could not select products. Try again.";
|
|
412
|
+
readonly emptyProducts: "No products selected";
|
|
413
|
+
readonly productCountSuffix: "products";
|
|
414
|
+
readonly selectAsset: "Select image";
|
|
415
|
+
readonly selectingAsset: "Uploading image…";
|
|
416
|
+
readonly selectAssetFailed: "Could not upload the image. Try again.";
|
|
417
|
+
readonly emptyAsset: "No image selected";
|
|
418
|
+
readonly removeAsset: "Remove image";
|
|
375
419
|
};
|
|
376
420
|
};
|
|
377
421
|
type AdminMessageKey = keyof typeof messages["zh-CN"];
|
|
378
422
|
/** Minimal, dependency-free Admin copy catalog. Product locale remains PageDocument.settings.locale. */
|
|
379
423
|
declare function createAdminI18n(locale?: string): {
|
|
380
424
|
locale: AdminLocale;
|
|
381
|
-
t: (key: AdminMessageKey) => "Desktop" | "Tablet" | "Mobile" | "未保存变更" | "Outline" | "Preview" | "缩放" | "区块" | "结构" | "撤销" | "恢复" | "收起左侧面板" | "收起属性面板" | "Delete" | "Duplicate" | "编辑锁已失效" | "属性" | "添加区块" | "复制" | "删除" | "确认删除区块?" | "删除后可使用撤销恢复。" | "取消" | "确认删除" | "上移" | "下移" | "正在加载编辑器…" | "页面还没有区块" | "编辑器无法加载" | "编辑器当前不可编辑" | "操作已完成" | "所有更改已保留在当前会话中" | "保存草稿" | "正在保存…" | "发布" | "正在发布…" | "保存草稿失败" | "重试保存" | "当前离线,恢复网络后将自动保存" | "发布失败" | "已发布" | "返回" | "离开编辑器?" | "离开" | "自动缩放" | "正式预览" | "添加到店铺" | "正在获取编辑锁…" | "该页面正在被编辑" | "页面为只读状态" | "你有未保存的更改。确定要离开吗?" | "选择一个区块以编辑。" | "桌面" | "平板" | "手机" | "打开区块面板" | "打开属性面板" | "
|
|
425
|
+
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" | "Select image" | "Uploading image…" | "Could not upload the image. Try again." | "No image selected" | "Remove image";
|
|
382
426
|
};
|
|
383
427
|
|
|
384
428
|
declare function toEngineData(document: PageDocument, registry?: ExtensionRegistry): Data;
|
|
385
429
|
declare function fromEngineData(data: Data, base: PageDocument, registry?: ExtensionRegistry): PageDocument;
|
|
386
430
|
|
|
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 };
|
|
431
|
+
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-RLKWR5JM.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,16 @@ 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",
|
|
826
|
+
selectAsset: "\u9009\u62E9\u56FE\u7247",
|
|
827
|
+
selectingAsset: "\u6B63\u5728\u4E0A\u4F20\u56FE\u7247\u2026",
|
|
828
|
+
selectAssetFailed: "\u65E0\u6CD5\u4E0A\u4F20\u56FE\u7247\uFF0C\u8BF7\u91CD\u8BD5\u3002",
|
|
829
|
+
emptyAsset: "\u672A\u8BBE\u7F6E\u56FE\u7247",
|
|
830
|
+
removeAsset: "\u79FB\u9664\u56FE\u7247"
|
|
820
831
|
},
|
|
821
832
|
en: {
|
|
822
833
|
blocks: "Blocks",
|
|
@@ -841,6 +852,7 @@ var messages = {
|
|
|
841
852
|
unsaved: "Unsaved changes",
|
|
842
853
|
saved: "Changes are kept in this session",
|
|
843
854
|
save: "Save draft",
|
|
855
|
+
history: "History",
|
|
844
856
|
saving: "Saving\u2026",
|
|
845
857
|
publish: "Publish",
|
|
846
858
|
publishing: "Publishing\u2026",
|
|
@@ -869,9 +881,16 @@ var messages = {
|
|
|
869
881
|
collapseRight: "Collapse properties panel",
|
|
870
882
|
expandLeft: "Open blocks panel",
|
|
871
883
|
expandRight: "Open properties panel",
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
884
|
+
selectProducts: "Select from Shopify",
|
|
885
|
+
selectingProducts: "Selecting products\u2026",
|
|
886
|
+
selectProductsFailed: "Could not select products. Try again.",
|
|
887
|
+
emptyProducts: "No products selected",
|
|
888
|
+
productCountSuffix: "products",
|
|
889
|
+
selectAsset: "Select image",
|
|
890
|
+
selectingAsset: "Uploading image\u2026",
|
|
891
|
+
selectAssetFailed: "Could not upload the image. Try again.",
|
|
892
|
+
emptyAsset: "No image selected",
|
|
893
|
+
removeAsset: "Remove image"
|
|
875
894
|
}
|
|
876
895
|
};
|
|
877
896
|
function createAdminI18n(locale) {
|
|
@@ -899,12 +918,6 @@ function blockTypeLabel(type, registry) {
|
|
|
899
918
|
if (type === "core.image") return "\u56FE\u7247";
|
|
900
919
|
return registry?.getBlock(type)?.label ?? type;
|
|
901
920
|
}
|
|
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
921
|
function validateDocumentBlocks(document, registry) {
|
|
909
922
|
return registry ? validatePageDocumentWithRegistry(document, registry) : [];
|
|
910
923
|
}
|
|
@@ -957,7 +970,7 @@ function PageDocumentEditorShell(props) {
|
|
|
957
970
|
}, [managedSession.state, onSessionStateChange]);
|
|
958
971
|
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
972
|
}
|
|
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 }) {
|
|
973
|
+
function PageDocumentEditor({ iframe = false, registry, adminLocale, onSave, onPublish, draftPersistence, draftRevision: initialDraftRevision, autoSave = true, autoSaveDelayMs = 800, publishAction, assetPicker, inspectorSettings, productPicker, pageStatus, onBack, onHistory, onPreview, onAddToStore, deleteConfirmation, availableBlockTypes, appearanceControls = false }) {
|
|
961
974
|
const editor = useEditorContext();
|
|
962
975
|
const [blockView, setBlockView] = useState3("blocks");
|
|
963
976
|
const [leftRailOpen, setLeftRailOpen] = useState3(true);
|
|
@@ -1104,25 +1117,24 @@ function PageDocumentEditor({ iframe = false, registry, adminLocale, onSave, onP
|
|
|
1104
1117
|
editor.document.target
|
|
1105
1118
|
] }),
|
|
1106
1119
|
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
1120
|
] })
|
|
1112
1121
|
] }),
|
|
1113
|
-
/* @__PURE__ */ jsxs5("div", { className: "pb-header-
|
|
1114
|
-
/* @__PURE__ */
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
/* @__PURE__ */ jsx7(
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1122
|
+
/* @__PURE__ */ jsxs5("div", { className: "pb-header-controls", children: [
|
|
1123
|
+
/* @__PURE__ */ jsxs5("div", { className: "pb-header-device-toolbar", children: [
|
|
1124
|
+
/* @__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)) }),
|
|
1125
|
+
/* @__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) }) })
|
|
1126
|
+
] }),
|
|
1127
|
+
/* @__PURE__ */ jsx7("div", { className: "pb-header-actions", children: /* @__PURE__ */ jsxs5(InlineStack3, { gap: "150", blockAlign: "center", wrap: true, children: [
|
|
1128
|
+
/* @__PURE__ */ jsx7(Badge3, { tone: saveBadgeTone, children: saveBadgeLabel }),
|
|
1129
|
+
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,
|
|
1130
|
+
/* @__PURE__ */ jsx7(Button2, { disabled: !canPersistDraft || !editor.isDirty || request !== "idle" || validationIssues.length > 0 || !isOnline, onClick: () => void save(), children: saveButtonLabel }),
|
|
1131
|
+
onPreview ? /* @__PURE__ */ jsx7(Button2, { disabled: request !== "idle" || !isOnline, onClick: () => void onPreview({ document: editor.document, draftRevision, session: editor.session }), children: i18n.t("preview") }) : null,
|
|
1132
|
+
onAddToStore ? /* @__PURE__ */ jsx7(Button2, { disabled: request !== "idle" || pageStatus?.publicationStatus === "unpublished", onClick: () => void onAddToStore({ document: editor.document, session: editor.session }), children: i18n.t("addToStore") }) : null,
|
|
1133
|
+
/* @__PURE__ */ jsx7(Button2, { variant: "primary", disabled: !publishAction && !onPublish || request !== "idle" || validationIssues.length > 0 || !isOnline, onClick: () => void publish(), children: request === "publishing" ? i18n.t("publishing") : i18n.t("publish") }),
|
|
1134
|
+
/* @__PURE__ */ jsx7(Button2, { accessibilityLabel: i18n.t("undo"), icon: UndoIcon2, variant: "tertiary", disabled: !editor.actionState.canUndo, onClick: editor.undo }),
|
|
1135
|
+
/* @__PURE__ */ jsx7(Button2, { accessibilityLabel: i18n.t("redo"), icon: RedoIcon2, variant: "tertiary", disabled: !editor.actionState.canRedo, onClick: editor.redo })
|
|
1136
|
+
] }) })
|
|
1137
|
+
] })
|
|
1126
1138
|
] }) }),
|
|
1127
1139
|
editor.loadState === "success" ? /* @__PURE__ */ jsx7(Banner, { tone: "success", children: i18n.t("success") }) : null,
|
|
1128
1140
|
notice ? /* @__PURE__ */ jsx7(Banner, { tone: notice === "published" ? "success" : "critical", children: i18n.t(notice) }) : null,
|
|
@@ -1189,7 +1201,7 @@ function PageDocumentEditor({ iframe = false, registry, adminLocale, onSave, onP
|
|
|
1189
1201
|
/* @__PURE__ */ jsx7(Button2, { accessibilityLabel: i18n.t("collapseRight"), icon: XIcon2, variant: "tertiary", onClick: () => setRightPanelOpen(false) })
|
|
1190
1202
|
] }),
|
|
1191
1203
|
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) }),
|
|
1204
|
+
/* @__PURE__ */ jsx7(DocumentInspector, { settings: inspectorSettings?.[editor.selectedBlock.id] ?? inspectorSettings?.[editor.selectedBlock.type], 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) }, editor.selectedBlock.id),
|
|
1193
1205
|
/* @__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
1206
|
] }) : /* @__PURE__ */ jsx7(Text3, { as: "p", tone: "subdued", children: i18n.t("selectBlock") })
|
|
1195
1207
|
] })
|
|
@@ -1260,7 +1272,66 @@ function InspectorTextControl({ label, value, control, disabled, onChange }) {
|
|
|
1260
1272
|
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
1273
|
return /* @__PURE__ */ jsx7(TextField, { label, labelHidden: true, value: stringValue, onChange, autoComplete: "off", disabled, multiline: control === "textarea" ? 4 : false, type: control === "url" ? "url" : "text" });
|
|
1262
1274
|
}
|
|
1263
|
-
function
|
|
1275
|
+
function InspectorAssetControl({ value, disabled, picker, pageId, blockId, i18n, onChange }) {
|
|
1276
|
+
const [busy, setBusy] = useState3(false);
|
|
1277
|
+
const [error, setError] = useState3(null);
|
|
1278
|
+
const active = useRef4(true);
|
|
1279
|
+
useEffect3(() => {
|
|
1280
|
+
active.current = !disabled;
|
|
1281
|
+
return () => {
|
|
1282
|
+
active.current = false;
|
|
1283
|
+
};
|
|
1284
|
+
}, [disabled]);
|
|
1285
|
+
const url = typeof value === "string" && validateFieldValue({ field: "", control: "asset" }, value).length === 0 ? value : "";
|
|
1286
|
+
return /* @__PURE__ */ jsxs5("div", { className: "pb-asset-picker", "data-testid": "asset-picker", children: [
|
|
1287
|
+
url ? /* @__PURE__ */ jsx7("img", { src: url, alt: "", className: "pb-asset-picker__preview" }) : /* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodySm", tone: "subdued", children: i18n.t("emptyAsset") }),
|
|
1288
|
+
url ? /* @__PURE__ */ jsx7(Button2, { fullWidth: true, variant: "tertiary", disabled: disabled || busy, onClick: () => onChange(""), children: i18n.t("removeAsset") }) : null,
|
|
1289
|
+
/* @__PURE__ */ jsx7(Button2, { fullWidth: true, disabled: disabled || busy || !picker, onClick: () => void (async () => {
|
|
1290
|
+
if (!picker) return;
|
|
1291
|
+
setBusy(true);
|
|
1292
|
+
setError(null);
|
|
1293
|
+
try {
|
|
1294
|
+
const asset = await picker.selectAsset({ pageId, blockId, current: { url } });
|
|
1295
|
+
if (asset && active.current) {
|
|
1296
|
+
if (validateFieldValue({ field: "", control: "asset", required: true }, asset.url).length) throw new Error("Invalid image URL");
|
|
1297
|
+
onChange(asset.url);
|
|
1298
|
+
}
|
|
1299
|
+
} catch {
|
|
1300
|
+
setError(i18n.t("selectAssetFailed"));
|
|
1301
|
+
} finally {
|
|
1302
|
+
setBusy(false);
|
|
1303
|
+
}
|
|
1304
|
+
})(), children: busy ? i18n.t("selectingAsset") : i18n.t("selectAsset") }),
|
|
1305
|
+
error ? /* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodySm", tone: "critical", children: error }) : null
|
|
1306
|
+
] });
|
|
1307
|
+
}
|
|
1308
|
+
function InspectorProductsControl({ value, disabled, picker, pageId, blockId, i18n, onChange }) {
|
|
1309
|
+
const products = parseProductReferences(value);
|
|
1310
|
+
const [busy, setBusy] = useState3(false);
|
|
1311
|
+
const [error, setError] = useState3(null);
|
|
1312
|
+
const preview = products.slice(0, 4);
|
|
1313
|
+
return /* @__PURE__ */ jsxs5("div", { className: "pb-product-picker", "data-testid": "product-picker", children: [
|
|
1314
|
+
/* @__PURE__ */ jsxs5("div", { className: "pb-product-picker__summary", children: [
|
|
1315
|
+
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,
|
|
1316
|
+
/* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodySm", children: products.length ? `${products.length} ${i18n.t("productCountSuffix")}` : i18n.t("emptyProducts") })
|
|
1317
|
+
] }),
|
|
1318
|
+
/* @__PURE__ */ jsx7(Button2, { fullWidth: true, icon: ProductIcon, variant: "secondary", disabled: disabled || busy || !picker, onClick: () => void (async () => {
|
|
1319
|
+
if (!picker) return;
|
|
1320
|
+
setBusy(true);
|
|
1321
|
+
setError(null);
|
|
1322
|
+
try {
|
|
1323
|
+
const next = await picker.selectProducts({ pageId, blockId, current: products, multiple: true });
|
|
1324
|
+
if (next) onChange(parseProductReferences(next).map(toProductReferenceJson));
|
|
1325
|
+
} catch {
|
|
1326
|
+
setError(i18n.t("selectProductsFailed"));
|
|
1327
|
+
} finally {
|
|
1328
|
+
setBusy(false);
|
|
1329
|
+
}
|
|
1330
|
+
})(), children: busy ? i18n.t("selectingProducts") : i18n.t("selectProducts") }),
|
|
1331
|
+
error ? /* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodySm", tone: "critical", children: error }) : null
|
|
1332
|
+
] });
|
|
1333
|
+
}
|
|
1334
|
+
function InspectorField({ name, field, value, registry, disabled, assetPicker, productPicker, pageId, blockId, i18n, onChange }) {
|
|
1264
1335
|
const label = field.label ?? name;
|
|
1265
1336
|
const issues = validateFieldValue(field, value);
|
|
1266
1337
|
const Field = registry?.getField(field.field)?.component;
|
|
@@ -1269,7 +1340,7 @@ function InspectorField({ name, field, value, registry, disabled, onChange }) {
|
|
|
1269
1340
|
/* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodySm", fontWeight: "semibold", children: label }),
|
|
1270
1341
|
field.description ? /* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodySm", tone: "subdued", children: field.description }) : null
|
|
1271
1342
|
] }),
|
|
1272
|
-
field.control ? /* @__PURE__ */ jsx7(InspectorTextControl, { label, value, control: field.control, disabled, onChange: (next) => onChange(next) }) : Field ? /* @__PURE__ */ jsx7(Field, { value, onChange }) : null,
|
|
1343
|
+
field.control === "products" ? /* @__PURE__ */ jsx7(InspectorProductsControl, { value, disabled, picker: productPicker, pageId, blockId, i18n, onChange }) : field.control === "asset" ? /* @__PURE__ */ jsx7(InspectorAssetControl, { value, disabled, picker: assetPicker, 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
1344
|
issues.map((issue) => /* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodySm", tone: "critical", children: issue.message }, issue.message))
|
|
1274
1345
|
] });
|
|
1275
1346
|
}
|
|
@@ -1308,7 +1379,7 @@ function inspectorFieldConfig(name, field) {
|
|
|
1308
1379
|
return { ...field, group: field.group ?? group, description };
|
|
1309
1380
|
}
|
|
1310
1381
|
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 }) {
|
|
1382
|
+
function DocumentInspector({ settings, pageId, assetPicker, productPicker, i18n, block, registry, disabled, appearanceControls, onChange, onPresentationChange }) {
|
|
1312
1383
|
const [assetError, setAssetError] = useState3(null);
|
|
1313
1384
|
const [selectingAsset, setSelectingAsset] = useState3(false);
|
|
1314
1385
|
const definition = registry?.getBlock(block.type);
|
|
@@ -1327,10 +1398,10 @@ function DocumentInspector({ pageId, assetPicker, block, registry, disabled, app
|
|
|
1327
1398
|
/* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodySm", tone: "subdued", children: definition?.category ?? "Core block" })
|
|
1328
1399
|
] })
|
|
1329
1400
|
] }),
|
|
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,
|
|
1401
|
+
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
1402
|
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 }) }),
|
|
1403
|
+
/* @__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 }) }),
|
|
1404
|
+
/* @__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
1405
|
assetPicker ? /* @__PURE__ */ jsxs5(Fragment2, { children: [
|
|
1335
1406
|
/* @__PURE__ */ jsx7(Button2, { disabled: disabled || selectingAsset, onClick: () => void (async () => {
|
|
1336
1407
|
setSelectingAsset(true);
|
|
@@ -1347,7 +1418,8 @@ function DocumentInspector({ pageId, assetPicker, block, registry, disabled, app
|
|
|
1347
1418
|
assetError ? /* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodySm", tone: "critical", children: assetError }) : null
|
|
1348
1419
|
] }) : null
|
|
1349
1420
|
] }) : 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({
|
|
1421
|
+
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: field.persist === false ? settings?.values[name] : block.props[name], registry, disabled: disabled || field.persist === false && (!settings || Boolean(settings.disabled)), assetPicker, productPicker, pageId, blockId: block.id, i18n, onChange: (value) => field.persist === false ? settings?.onChange(name, value) : onChange({ [name]: value }) }, name)) }, group)),
|
|
1422
|
+
Object.values(definition?.fields ?? {}).some((field) => field.persist === false) ? settings?.footer : null,
|
|
1351
1423
|
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
1424
|
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
1425
|
const style = { ...block.style };
|
|
@@ -1371,14 +1443,17 @@ export {
|
|
|
1371
1443
|
createPageDocument,
|
|
1372
1444
|
createTemplateRegistry,
|
|
1373
1445
|
fromEngineData,
|
|
1446
|
+
isProductReference,
|
|
1374
1447
|
maximumBlockInstances,
|
|
1375
1448
|
mergeBlockPolicies,
|
|
1376
1449
|
mergeThemeTokens,
|
|
1377
1450
|
migratePageDocument,
|
|
1378
1451
|
minimumBlockInstances,
|
|
1379
1452
|
normalizeThemeTokens,
|
|
1453
|
+
parseProductReferences,
|
|
1380
1454
|
systemThemeTokens,
|
|
1381
1455
|
toEngineData,
|
|
1456
|
+
toProductReferenceJson,
|
|
1382
1457
|
toThemeStyle,
|
|
1383
1458
|
useEditorContext,
|
|
1384
1459
|
validateBlockPolicy,
|
|
@@ -42,9 +42,11 @@ 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" | "asset";
|
|
46
46
|
description?: string;
|
|
47
47
|
group?: string;
|
|
48
|
+
/** Host-owned setting; never written to PageDocument props. */
|
|
49
|
+
persist?: boolean;
|
|
48
50
|
/** Kept for compatibility; it participates in the built-in validation. */
|
|
49
51
|
required?: boolean;
|
|
50
52
|
validation?: FieldValidation;
|
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-BkqtMP-A.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-RLKWR5JM.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 12rem; 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: 2 1 28rem; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: var(--builder-space-2) var(--builder-space-3); min-width: 0; }
|
|
17
|
+
.pb-header-device-toolbar { display: flex; flex: 0 1 auto; align-items: center; gap: var(--builder-space-2); min-width: 0; }
|
|
18
|
+
.pb-header-actions { display: flex; flex: 1 1 16rem; 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,16 @@ 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; }
|
|
63
|
+
.pb-asset-picker { display: grid; gap: 8px; }
|
|
64
|
+
.pb-asset-picker__preview { display: block; width: 100%; max-height: 180px; object-fit: cover; border: 1px solid #e2e8f0; border-radius: 10px; background: #f8fafc; }
|
|
53
65
|
.pb-inspector__hint { margin: 0; padding: 0 var(--builder-space-2); color: #64748b; font-size: 12px; line-height: 1.5; }
|
|
54
66
|
.pb-inspector-actions { padding-top: var(--builder-space-2); border-top: 1px solid #e2e8f0; }
|
|
55
67
|
.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 +110,8 @@ button:focus-visible, [role="button"]:focus-visible { outline: 2px solid var(--b
|
|
|
98
110
|
.pb-library-block-drag-hint svg { width: 16px; height: 16px; }
|
|
99
111
|
.pb-document-block-row:hover { background: var(--builder-surface-subtle); }
|
|
100
112
|
.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:
|
|
113
|
+
.pb-editor-status { display: grid; place-items: center; box-sizing: border-box; width: 100%; min-height: 100dvh; padding: var(--builder-space-6); }
|
|
114
|
+
.pb-editor-status > * { width: min(100%, 28rem); justify-self: center; }
|
|
102
115
|
.pb-shell--v04 > .Polaris-Banner { margin: var(--builder-space-3) var(--builder-space-4) 0; }
|
|
103
116
|
.pb-document-canvas__text, .pb-document-canvas__image { max-width: 760px; margin: 0 auto; padding: 48px; color: #1d2939; background: #fff; cursor: pointer; }
|
|
104
117
|
.pb-document-canvas__text { font-size: clamp(32px, 4vw, 52px); font-weight: 650; line-height: 1.1; letter-spacing: -.03em; }
|
|
@@ -131,5 +144,5 @@ button:focus-visible, [role="button"]:focus-visible { outline: 2px solid var(--b
|
|
|
131
144
|
.bt-storefront__form input { flex: 1; min-width: 0; padding: 13px 14px; border: 1px solid #b8c3cc; border-radius: 8px; font: inherit; }
|
|
132
145
|
.bt-storefront__form button { padding: 13px 18px; border: 0; border-radius: 8px; color: white; background: #203e54; font: inherit; font-weight: 600; }
|
|
133
146
|
@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 {
|
|
147
|
+
@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; } }
|
|
148
|
+
@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-basis: 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; } }
|