@toototech/webbuilder-plugins 0.1.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.
Files changed (193) hide show
  1. package/LICENSE +1 -0
  2. package/README.md +3 -0
  3. package/dist/basic/cssScope.d.ts +3 -0
  4. package/dist/basic/index.d.ts +81 -0
  5. package/dist/basic/injectStyle.d.ts +20 -0
  6. package/dist/basic/plugin.d.ts +19 -0
  7. package/dist/basic/publisher.d.ts +4 -0
  8. package/dist/basic/registries/interactive/accordion/index.d.ts +3 -0
  9. package/dist/basic/registries/interactive/backButton/index.d.ts +3 -0
  10. package/dist/basic/registries/interactive/button/index.d.ts +10 -0
  11. package/dist/basic/registries/interactive/countUp/index.d.ts +9 -0
  12. package/dist/basic/registries/interactive/customCode/index.d.ts +3 -0
  13. package/dist/basic/registries/interactive/index.d.ts +2 -0
  14. package/dist/basic/registries/interactive/inquiryForm/index.d.ts +3 -0
  15. package/dist/basic/registries/interactive/klaviyoSubscribe/index.d.ts +3 -0
  16. package/dist/basic/registries/interactive/popup/index.d.ts +5 -0
  17. package/dist/basic/registries/interactive/salesmartlyChatButton/index.d.ts +10 -0
  18. package/dist/basic/registries/interactive/search/index.d.ts +10 -0
  19. package/dist/basic/registries/interactive/socialShare/index.d.ts +33 -0
  20. package/dist/basic/registries/interactive/tabs/index.d.ts +3 -0
  21. package/dist/basic/registries/layout/container/index.d.ts +19 -0
  22. package/dist/basic/registries/layout/divider/index.d.ts +7 -0
  23. package/dist/basic/registries/layout/grid/index.d.ts +11 -0
  24. package/dist/basic/registries/layout/index.d.ts +2 -0
  25. package/dist/basic/registries/layout/layoutBase/index.d.ts +10 -0
  26. package/dist/basic/registries/layout/section/index.d.ts +3 -0
  27. package/dist/basic/registries/layout/sectionGridBlock/index.d.ts +3 -0
  28. package/dist/basic/registries/layout/spacer/index.d.ts +7 -0
  29. package/dist/basic/registries/media/banner/index.d.ts +11 -0
  30. package/dist/basic/registries/media/carousel/index.d.ts +6 -0
  31. package/dist/basic/registries/media/flipbook/index.d.ts +5 -0
  32. package/dist/basic/registries/media/icon/index.d.ts +3 -0
  33. package/dist/basic/registries/media/image/index.d.ts +3 -0
  34. package/dist/basic/registries/media/index.d.ts +2 -0
  35. package/dist/basic/registries/media/industryTabs/index.d.ts +3 -0
  36. package/dist/basic/registries/media/marquee/index.d.ts +4 -0
  37. package/dist/basic/registries/media/ourSolutions/helpers.d.ts +289 -0
  38. package/dist/basic/registries/media/ourSolutions/index.d.ts +4 -0
  39. package/dist/basic/registries/media/ourSolutions/script.d.ts +1 -0
  40. package/dist/basic/registries/media/ourSolutions/style.d.ts +1 -0
  41. package/dist/basic/registries/media/pdfViewer/index.d.ts +3 -0
  42. package/dist/basic/registries/media/productCategories/index.d.ts +4 -0
  43. package/dist/basic/registries/media/tabMediaGallery/index.d.ts +3 -0
  44. package/dist/basic/registries/media/video/index.d.ts +12 -0
  45. package/dist/basic/registries/navigation/footer/index.d.ts +10 -0
  46. package/dist/basic/registries/navigation/index.d.ts +2 -0
  47. package/dist/basic/registries/navigation/languageSwitcher/index.d.ts +6 -0
  48. package/dist/basic/registries/navigation/logo/index.d.ts +9 -0
  49. package/dist/basic/registries/navigation/navbar/constants.d.ts +38 -0
  50. package/dist/basic/registries/navigation/navbar/factories.d.ts +848 -0
  51. package/dist/basic/registries/navigation/navbar/helpers.d.ts +12 -0
  52. package/dist/basic/registries/navigation/navbar/index.d.ts +4 -0
  53. package/dist/basic/registries/navigation/navbar/registerMegaTypes.d.ts +2 -0
  54. package/dist/basic/registries/navigation/navbar/registerMenuTypes.d.ts +2 -0
  55. package/dist/basic/registries/navigation/navbar/registerRootTypes.d.ts +2 -0
  56. package/dist/basic/registries/navigation/navbar/script.d.ts +1 -0
  57. package/dist/basic/registries/navigation/navbar/style.d.ts +1 -0
  58. package/dist/basic/registries/navigation/socialLinks/index.d.ts +3 -0
  59. package/dist/basic/registries/section/allApplications/index.d.ts +3 -0
  60. package/dist/basic/registries/section/cardCarouselSection/index.d.ts +3 -0
  61. package/dist/basic/registries/section/caseSpotlight/index.d.ts +3 -0
  62. package/dist/basic/registries/section/companyScale/index.d.ts +3 -0
  63. package/dist/basic/registries/section/customizationGrid/index.d.ts +3 -0
  64. package/dist/basic/registries/section/factoryMap/index.d.ts +3 -0
  65. package/dist/basic/registries/section/focaHistoryTimeline/index.d.ts +3 -0
  66. package/dist/basic/registries/section/historyTimeline/index.d.ts +3 -0
  67. package/dist/basic/registries/section/homeBannerCarousel/index.d.ts +4 -0
  68. package/dist/basic/registries/section/hotspotShowcase/index.d.ts +30 -0
  69. package/dist/basic/registries/section/index.d.ts +2 -0
  70. package/dist/basic/registries/section/map/index.d.ts +4 -0
  71. package/dist/basic/registries/section/milestoneCardStrip/index.d.ts +3 -0
  72. package/dist/basic/registries/section/moreCardCarousel/index.d.ts +3 -0
  73. package/dist/basic/registries/section/ourAdvantages/index.d.ts +4 -0
  74. package/dist/basic/registries/section/overviewSplit/index.d.ts +3 -0
  75. package/dist/basic/registries/section/processTimeline/index.d.ts +3 -0
  76. package/dist/basic/registries/section/productCardStrip/index.d.ts +3 -0
  77. package/dist/basic/registries/section/resourceSection/index.d.ts +6 -0
  78. package/dist/basic/registries/section/responsiveHeroCarousel/index.d.ts +4 -0
  79. package/dist/basic/registries/section/serviceIconGrid/index.d.ts +4 -0
  80. package/dist/basic/registries/section/servicesCarousel/index.d.ts +4 -0
  81. package/dist/basic/registries/section/servicesShowcase/index.d.ts +4 -0
  82. package/dist/basic/registries/section/servicesThb/index.d.ts +4 -0
  83. package/dist/basic/registries/section/solutionList/index.d.ts +4 -0
  84. package/dist/basic/registries/section/staticPinMap/index.d.ts +4 -0
  85. package/dist/basic/registries/section/statsCards/index.d.ts +3 -0
  86. package/dist/basic/registries/section/swiperRuntime.d.ts +3 -0
  87. package/dist/basic/registries/shared/sharedTraits.d.ts +37 -0
  88. package/dist/basic/registries/types.d.ts +5 -0
  89. package/dist/basic/registries/typography/heading/index.d.ts +8 -0
  90. package/dist/basic/registries/typography/index.d.ts +2 -0
  91. package/dist/basic/registries/typography/textEditor/index.d.ts +6 -0
  92. package/dist/basic/registryManifest.d.ts +9 -0
  93. package/dist/basic/styleHelpers.d.ts +29 -0
  94. package/dist/basic/svgIcon.d.ts +11 -0
  95. package/dist/basic/traitBridge.d.ts +20 -0
  96. package/dist/basic/traitFactory.d.ts +106 -0
  97. package/dist/basic.js +1146 -0
  98. package/dist/cms/cmsFactory.d.ts +164 -0
  99. package/dist/cms/dynamicRenderPipeline.d.ts +12 -0
  100. package/dist/cms/index.d.ts +31 -0
  101. package/dist/cms/plugin.d.ts +12 -0
  102. package/dist/cms/publisher.d.ts +7 -0
  103. package/dist/cms/registries/dynamic/cms/constants.d.ts +16 -0
  104. package/dist/cms/registries/dynamic/cms/dynamicField/bindings.d.ts +78 -0
  105. package/dist/cms/registries/dynamic/cms/dynamicField/blocks/breadcrumbBlock.d.ts +1 -0
  106. package/dist/cms/registries/dynamic/cms/dynamicField/blocks/conditionalBlock.d.ts +1 -0
  107. package/dist/cms/registries/dynamic/cms/dynamicField/blocks/datetimeBlock.d.ts +1 -0
  108. package/dist/cms/registries/dynamic/cms/dynamicField/blocks/htmlBlock.d.ts +1 -0
  109. package/dist/cms/registries/dynamic/cms/dynamicField/blocks/imageBlock.d.ts +1 -0
  110. package/dist/cms/registries/dynamic/cms/dynamicField/blocks/linkBlock.d.ts +1 -0
  111. package/dist/cms/registries/dynamic/cms/dynamicField/blocks/seoBlock.d.ts +1 -0
  112. package/dist/cms/registries/dynamic/cms/dynamicField/blocks/textBlock.d.ts +1 -0
  113. package/dist/cms/registries/dynamic/cms/dynamicField/blocks/tocBlock.d.ts +1 -0
  114. package/dist/cms/registries/dynamic/cms/dynamicField/constants.d.ts +19 -0
  115. package/dist/cms/registries/dynamic/cms/dynamicField/helpers.d.ts +52 -0
  116. package/dist/cms/registries/dynamic/cms/dynamicField/registerBlock.d.ts +57 -0
  117. package/dist/cms/registries/dynamic/cms/dynamicField/styles.d.ts +7 -0
  118. package/dist/cms/registries/dynamic/cms/media/previewMediaTrait.d.ts +7 -0
  119. package/dist/cms/registries/dynamic/cms/menu/siteMenuAttrs.d.ts +11 -0
  120. package/dist/cms/registries/dynamic/cms/menuTree/menuTreeAttrs.d.ts +8 -0
  121. package/dist/cms/registries/dynamic/cms/post/styles.d.ts +6 -0
  122. package/dist/cms/registries/dynamic/cms/product/detail.styles.d.ts +1 -0
  123. package/dist/cms/registries/dynamic/cms/product/detailV2.styles.d.ts +1 -0
  124. package/dist/cms/registries/dynamic/cms/product/previewProductTrait.d.ts +6 -0
  125. package/dist/cms/registries/dynamic/cms/product/styles.d.ts +6 -0
  126. package/dist/cms/registries/dynamic/dataProvider.d.ts +45 -0
  127. package/dist/cms/registries/dynamic/loopGrid/paginationStyles.d.ts +5 -0
  128. package/dist/cms/registries/dynamic/loopGrid/preview.d.ts +16 -0
  129. package/dist/cms/registries/dynamic/loopGrid/publisher.d.ts +3 -0
  130. package/dist/cms/registries/dynamic/loopGrid/types.d.ts +97 -0
  131. package/dist/cms/registries/navigation/navbarThb/index.d.ts +3 -0
  132. package/dist/cms/registries/navigation/navbarThb/script.d.ts +1 -0
  133. package/dist/cms/registries/navigation/navbarThb/style.d.ts +1 -0
  134. package/dist/cms.js +4535 -0
  135. package/dist/global-settings/components/FontFamilySelect.vue.d.ts +29 -0
  136. package/dist/global-settings/components/FontManagerPanel.vue.d.ts +37 -0
  137. package/dist/global-settings/index.d.ts +8 -0
  138. package/dist/global-settings/plugin.d.ts +3 -0
  139. package/dist/global-settings/publisher.d.ts +15 -0
  140. package/dist/global-settings/runtime/canvasInjection.d.ts +13 -0
  141. package/dist/global-settings/runtime/panelDraftSave.d.ts +17 -0
  142. package/dist/global-settings/runtime/settingsSource.d.ts +4 -0
  143. package/dist/global-settings/useFontManager.d.ts +38 -0
  144. package/dist/global-settings/useGoogleFonts.d.ts +20 -0
  145. package/dist/global-settings/vue.d.ts +1 -0
  146. package/dist/global-settings.js +66 -0
  147. package/dist/i18n/I18nPanel.vue.d.ts +23 -0
  148. package/dist/i18n/i18n.d.ts +25 -0
  149. package/dist/i18n/index.d.ts +7 -0
  150. package/dist/i18n/languageOrder.d.ts +9 -0
  151. package/dist/i18n/plugin.d.ts +21 -0
  152. package/dist/i18n/types.d.ts +101 -0
  153. package/dist/i18n/useWebBuilderI18n.d.ts +164 -0
  154. package/dist/i18n/vue.d.ts +1 -0
  155. package/dist/i18n-BYR3l48y.js +959 -0
  156. package/dist/i18n.js +929 -0
  157. package/dist/index-CxJlLwvG.js +35378 -0
  158. package/dist/index-DWfJ4PBm.js +5724 -0
  159. package/dist/index.d.ts +9 -0
  160. package/dist/index.js +12 -0
  161. package/dist/layout-template/components/LayoutPanel.vue.d.ts +37 -0
  162. package/dist/layout-template/components/TemplateRulesPanel.vue.d.ts +41 -0
  163. package/dist/layout-template/config/layoutSharedResources.d.ts +9 -0
  164. package/dist/layout-template/config/templateSharedResources.d.ts +28 -0
  165. package/dist/layout-template/index.d.ts +9 -0
  166. package/dist/layout-template/plugin.d.ts +13 -0
  167. package/dist/layout-template/runtime/storageAdapter.d.ts +49 -0
  168. package/dist/layout-template/utils/layoutProjectData.d.ts +15 -0
  169. package/dist/layout-template/utils/layoutRulePages.d.ts +19 -0
  170. package/dist/layout-template/utils/layoutSettings.d.ts +45 -0
  171. package/dist/layout-template/utils/templateRules.d.ts +52 -0
  172. package/dist/layout-template/vue.d.ts +2 -0
  173. package/dist/layout-template.js +435 -0
  174. package/dist/layoutSettings-D4SYUMri.js +252 -0
  175. package/dist/plugin-BPA8qlaC.js +40 -0
  176. package/dist/plugin-C0PrxrIe.js +228 -0
  177. package/dist/plugin-DQshk1sY.js +361 -0
  178. package/dist/plugin-DebyCjXx.js +191 -0
  179. package/dist/plugin-Dr6TOtyH.js +73 -0
  180. package/dist/publisher/index.d.ts +5 -0
  181. package/dist/publisher/publisherAssets.d.ts +9 -0
  182. package/dist/publisher/publisherComponents.d.ts +7 -0
  183. package/dist/publisher/publisherPlugins.d.ts +12 -0
  184. package/dist/publisher-C6VWXq8u.js +25 -0
  185. package/dist/publisher.js +1711 -0
  186. package/dist/solar-BsElUqfQ.js +29843 -0
  187. package/dist/style.css +1181 -0
  188. package/dist/templateSharedResources-D1u7eFIs.js +89 -0
  189. package/dist/types-DNbok59z.js +2359 -0
  190. package/dist/useFontManager-CdrLq1eG.js +336 -0
  191. package/dist/vue.d.ts +3 -0
  192. package/dist/vue.js +2171 -0
  193. package/package.json +77 -0
@@ -0,0 +1,2359 @@
1
+ import { h as getLoopItemType } from "./templateSharedResources-D1u7eFIs.js";
2
+ const LOOP_GRID_PAGINATION_CSS = `
3
+ .wb-loop-grid-pagination{grid-column:1/-1;display:flex;gap:8px;justify-content:center;align-items:center;flex-wrap:wrap;padding:28px 0 4px;}
4
+ .wb-loop-grid-pagination>a,.wb-loop-grid-pagination>button,.wb-loop-grid-pagination>span{display:inline-flex;align-items:center;justify-content:center;width:22px;min-width:22px;height:22px;padding:0;border:0;border-radius:4px;background:transparent;color:#1b2a33;font-size:14px;font-weight:400;line-height:1;text-decoration:none;box-sizing:border-box;box-shadow:none;}
5
+ .wb-loop-grid-pagination svg{display:block;width:18px;height:18px;}
6
+ .wb-loop-grid-pagination>a,.wb-loop-grid-pagination>button{cursor:pointer;}
7
+ .wb-loop-grid-pagination>a:hover,.wb-loop-grid-pagination>button:hover{color:#2847f3;background:transparent;}
8
+ .wb-loop-grid-pagination>a.active,.wb-loop-grid-pagination>button.active,.wb-loop-grid-pagination>span.active,.wb-loop-grid-pagination>a.is-active,.wb-loop-grid-pagination>button.is-active,.wb-loop-grid-pagination>span.is-active{background:#2847f3;color:#fff;font-weight:500;}
9
+ `;
10
+ const LOOP_GRID_PAGINATION_STYLE = `
11
+ <style data-wb-loop-grid-pagination-style>
12
+ ${LOOP_GRID_PAGINATION_CSS}
13
+ </style>
14
+ `;
15
+ const LOOP_GRID_PREV_ICON = '<svg aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none"><path d="M15 18L9 12L15 6" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"/></svg>';
16
+ const LOOP_GRID_NEXT_ICON = '<svg aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none"><path d="M9 6L15 12L9 18" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"/></svg>';
17
+ const LOOP_GRID_PAGINATION_SCRIPT = `
18
+ <script data-wb-loop-grid-pagination-script>
19
+ (function(){
20
+ var prevIcon = ${JSON.stringify(LOOP_GRID_PREV_ICON)};
21
+ var nextIcon = ${JSON.stringify(LOOP_GRID_NEXT_ICON)};
22
+ function normalizePager(root){
23
+ var pagers = (root || document).querySelectorAll('.wb-loop-grid-pagination');
24
+ Array.prototype.forEach.call(pagers, function(pager){
25
+ Array.prototype.forEach.call(pager.children, function(item){
26
+ var text = (item.textContent || '').replace(/\\s+/g, ' ').trim().toLowerCase();
27
+ if (text === 'prev' || text === 'previous' || text === '上一页' || text === '‹' || text === '«' || text.indexOf('prev') !== -1 || text.indexOf('previous') !== -1 || text.indexOf('上一页') !== -1 || text.indexOf('‹') !== -1 || text.indexOf('«') !== -1) {
28
+ item.innerHTML = prevIcon;
29
+ item.setAttribute('aria-label', '上一页');
30
+ } else if (text === 'next' || text === 'next »' || text === 'next >' || text === '下一页' || text === '下一页 »' || text === '›' || text === '»' || text.indexOf('next') !== -1 || text.indexOf('下一页') !== -1 || text.indexOf('›') !== -1 || text.indexOf('»') !== -1) {
31
+ item.innerHTML = nextIcon;
32
+ item.setAttribute('aria-label', '下一页');
33
+ }
34
+ });
35
+ });
36
+ }
37
+ if (document.readyState === 'loading') {
38
+ document.addEventListener('DOMContentLoaded', function(){ normalizePager(document); });
39
+ } else {
40
+ normalizePager(document);
41
+ }
42
+ if (window.MutationObserver) {
43
+ new MutationObserver(function(mutations){
44
+ mutations.forEach(function(mutation){
45
+ Array.prototype.forEach.call(mutation.addedNodes || [], function(node){
46
+ if (node && node.nodeType === 1) normalizePager(node);
47
+ });
48
+ });
49
+ }).observe(document.documentElement, { childList: true, subtree: true });
50
+ }
51
+ })();
52
+ <\/script>
53
+ `;
54
+ const WB_CMS_POST_LATEST_TYPE = "wb-cms-post-latest";
55
+ const WB_CMS_POST_DETAIL_TYPE = "wb-cms-post-detail";
56
+ const WB_CMS_MEDIA_LIST_TYPE = "wb-cms-media-list";
57
+ const WB_CMS_TECHNICAL_SERVICE_LIST_TYPE = "wb-cms-technical-service-list";
58
+ const WB_CMS_TECHNICAL_DOWNLOAD_LIST_TYPE = "wb-cms-technical-download-list";
59
+ const WB_CMS_TECHNICAL_SUPPORT_DETAIL_TYPE = "wb-cms-technical-support-detail";
60
+ const WB_CMS_MEDIA_LATEST_TYPE = "wb-cms-media-latest";
61
+ const WB_CMS_MEDIA_DETAIL_TYPE = "wb-cms-media-detail";
62
+ const WB_CMS_PRODUCT_LATEST_TYPE = "wb-cms-product-latest";
63
+ const WB_CMS_PRODUCT_FEATURED_TYPE = "wb-cms-product-featured";
64
+ const WB_CMS_PRODUCT_RELATED_TYPE = "wb-cms-product-related";
65
+ const WB_CMS_FAQ_SECTION_TYPE = "wb-cms-faq-section";
66
+ const WB_CMS_PRODUCT_CATEGORY_FAQ_TYPE = "wb-cms-product-category-faq";
67
+ const WB_CMS_SEARCH_TYPE = "wb-cms-search";
68
+ const WB_CMS_SITE_MENU_TYPE = "wb-cms-site-menu";
69
+ const WB_CMS_MENU_TREE_TYPE = "wb-cms-menu-tree";
70
+ const DEFAULT_MENU_TREE_CODE = "main-menu";
71
+ const normalizeClassName = (rawValue) => {
72
+ const classes = rawValue.split(/\s+/).map((item) => item.trim()).filter(Boolean);
73
+ if (!classes.includes("wb-menu-tree")) {
74
+ classes.unshift("wb-menu-tree");
75
+ }
76
+ if (!classes.includes("notranslate")) {
77
+ classes.push("notranslate");
78
+ }
79
+ return classes.join(" ");
80
+ };
81
+ const normalizeDataKey = (rawValue) => {
82
+ const normalized = rawValue.trim().replace(/[^A-Za-z0-9_]/g, "_");
83
+ if (!normalized)
84
+ return "";
85
+ return /^\d/.test(normalized) ? `_${normalized}` : normalized;
86
+ };
87
+ function resolveMenuTreeAttrs(model) {
88
+ var _a, _b, _c;
89
+ const attrs = ((_a = model == null ? void 0 : model.getAttributes) == null ? void 0 : _a.call(model)) || {};
90
+ const menuCode = String(
91
+ ((_b = model == null ? void 0 : model.get) == null ? void 0 : _b.call(model, "menuCode")) ?? attrs["data-menu-code"] ?? DEFAULT_MENU_TREE_CODE
92
+ ).trim() || DEFAULT_MENU_TREE_CODE;
93
+ const menuDataKey = normalizeDataKey(String(
94
+ ((_c = model == null ? void 0 : model.get) == null ? void 0 : _c.call(model, "menuDataKey")) ?? attrs["data-menu-data-key"] ?? ""
95
+ ));
96
+ return {
97
+ class: normalizeClassName(String(attrs.class ?? "")),
98
+ "data-menu-code": menuCode,
99
+ "data-menu-data-key": menuDataKey,
100
+ "data-wb-i18n-skip": "true",
101
+ translate: "no"
102
+ };
103
+ }
104
+ const WB_CMS_DYN_TEXT_TYPE = "wb-cms-dynamic-text";
105
+ const WB_CMS_DYN_HTML_TYPE = "wb-cms-dynamic-html";
106
+ const WB_CMS_DYN_IMAGE_TYPE = "wb-cms-dynamic-image";
107
+ const WB_CMS_DYN_LINK_TYPE = "wb-cms-dynamic-link";
108
+ const WB_CMS_DYN_DATETIME_TYPE = "wb-cms-dynamic-datetime";
109
+ const WB_CMS_DYN_IF_TYPE = "wb-cms-dynamic-if";
110
+ const WB_CMS_DYN_REPEAT_TYPE = "wb-cms-dynamic-repeat";
111
+ const WB_CMS_DYN_REPEAT_ITEM_TYPE = "wb-cms-dynamic-repeat-item";
112
+ const WB_CMS_DYN_SEO_TYPE = "wb-cms-dynamic-seo";
113
+ const WB_CMS_DYN_TOC_TYPE = "wb-cms-dynamic-toc";
114
+ const WB_CMS_DYN_BREADCRUMB_TYPE = "wb-cms-dynamic-breadcrumb";
115
+ const WB_CMS_DYNAMIC_FIELD_TYPES = [
116
+ WB_CMS_DYN_TEXT_TYPE,
117
+ WB_CMS_DYN_HTML_TYPE,
118
+ WB_CMS_DYN_IMAGE_TYPE,
119
+ WB_CMS_DYN_LINK_TYPE,
120
+ WB_CMS_DYN_DATETIME_TYPE,
121
+ WB_CMS_DYN_IF_TYPE,
122
+ WB_CMS_DYN_REPEAT_TYPE,
123
+ WB_CMS_DYN_SEO_TYPE,
124
+ WB_CMS_DYN_TOC_TYPE,
125
+ WB_CMS_DYN_BREADCRUMB_TYPE
126
+ ];
127
+ const WB_DYN_MARK_ATTR = "data-wb-dynamic";
128
+ const WB_TEMPLATE_CONTEXT_KEY = "wbTemplateContext";
129
+ const DYNAMIC_FIELD_STYLES = `
130
+ [data-wb-dynamic] {
131
+ position: relative;
132
+ }
133
+ [data-wb-dynamic][data-wb-dyn-empty="true"] {
134
+ outline: 1px dashed #cbd5e1;
135
+ outline-offset: 2px;
136
+ min-height: 18px;
137
+ background: rgba(148, 163, 184, 0.08);
138
+ }
139
+ [data-wb-dynamic="image"] {
140
+ display: inline-block;
141
+ }
142
+ [data-wb-dynamic="text"] {
143
+ white-space: pre-line;
144
+ }
145
+ img[data-wb-dynamic="image"][data-wb-dyn-empty="true"] {
146
+ min-width: 120px;
147
+ min-height: 80px;
148
+ }
149
+ [data-wb-dynamic="if"] {
150
+ display: block;
151
+ padding: 4px;
152
+ border: 1px dashed #fca5a5;
153
+ background: rgba(252, 165, 165, 0.08);
154
+ border-radius: 4px;
155
+ }
156
+ [data-wb-dynamic="if"]::before {
157
+ content: "";
158
+ position: absolute;
159
+ top: -10px;
160
+ left: 8px;
161
+ font-size: 10px;
162
+ color: #b91c1c;
163
+ background: #fff;
164
+ padding: 0 4px;
165
+ line-height: 1;
166
+ }
167
+ [data-wb-dynamic="seo-meta"] {
168
+ display: none;
169
+ }
170
+
171
+ /* ───────── 面包屑导航 ───────── */
172
+ .wb-cms-dynamic-breadcrumb {
173
+ display: block;
174
+ box-sizing: border-box;
175
+ color: var(--wb-breadcrumb-color, #202020);
176
+ font-size: var(--wb-breadcrumb-font-size, 12px);
177
+ font-weight: var(--wb-breadcrumb-font-weight, 400);
178
+ line-height: var(--wb-breadcrumb-line-height, 1.4);
179
+ letter-spacing: var(--wb-breadcrumb-letter-spacing, 0);
180
+ }
181
+ .wb-cms-dynamic-breadcrumb__list {
182
+ display: flex;
183
+ align-items: center;
184
+ flex-wrap: wrap;
185
+ gap: 0;
186
+ margin: 0;
187
+ padding: 0;
188
+ list-style: none;
189
+ }
190
+ .wb-cms-dynamic-breadcrumb__item {
191
+ display: inline-flex;
192
+ align-items: center;
193
+ min-width: 0;
194
+ }
195
+ .wb-cms-dynamic-breadcrumb__link {
196
+ color: inherit;
197
+ text-decoration: none;
198
+ max-width: 28ch;
199
+ overflow: hidden;
200
+ text-overflow: ellipsis;
201
+ white-space: nowrap;
202
+ }
203
+ .wb-cms-dynamic-breadcrumb__link:hover {
204
+ color: var(--wb-breadcrumb-hover-color, #111111);
205
+ }
206
+ .wb-cms-dynamic-breadcrumb__separator {
207
+ color: var(--wb-breadcrumb-separator-color, #777777);
208
+ margin: 0 var(--wb-breadcrumb-separator-space, 14px);
209
+ }
210
+ .wb-cms-dynamic-breadcrumb__item:last-child .wb-cms-dynamic-breadcrumb__link {
211
+ color: var(--wb-breadcrumb-current-color, #202020);
212
+ pointer-events: none;
213
+ }
214
+ .wb-cms-dynamic-breadcrumb__item.is-current .wb-cms-dynamic-breadcrumb__link {
215
+ color: var(--wb-breadcrumb-current-color, #202020);
216
+ pointer-events: none;
217
+ }
218
+ .wb-cms-dynamic-breadcrumb__item:last-child .wb-cms-dynamic-breadcrumb__separator {
219
+ display: none;
220
+ }
221
+ .wb-cms-dynamic-breadcrumb__item.is-current .wb-cms-dynamic-breadcrumb__separator {
222
+ display: none;
223
+ }
224
+ @media (max-width: 640px) {
225
+ .wb-cms-dynamic-breadcrumb {
226
+ font-size: var(--wb-breadcrumb-mobile-font-size, 12px);
227
+ }
228
+ .wb-cms-dynamic-breadcrumb__separator {
229
+ margin: 0 var(--wb-breadcrumb-mobile-separator-space, 8px);
230
+ }
231
+ }
232
+
233
+ /* ───────── 动态 HTML:保留后台富文本原生样式 ─────────
234
+ * 富文本里的 h1–h6 / p / ul / ol / blockquote 等元素默认会被外层模板
235
+ * (如 .wb-cms-detail-content h2 {...} / 全局 reset)覆盖,导致字号、
236
+ * 粗细、列表缩进、段落 margin 等丢失。这里把这些标签 \`all: revert\`
237
+ * 到浏览器 UA 默认值,等效于"什么都不加样式",从而展示后台富文本
238
+ * 自己带的 inline style / 原生观感;用户自定义样式(更高权重或
239
+ * inline style)仍可覆盖。
240
+ */
241
+ [data-wb-dynamic="html"] {
242
+ word-wrap: break-word;
243
+ overflow-wrap: break-word;
244
+ }
245
+ [data-wb-dynamic="html"] h1,
246
+ [data-wb-dynamic="html"] h2,
247
+ [data-wb-dynamic="html"] h3,
248
+ [data-wb-dynamic="html"] h4,
249
+ [data-wb-dynamic="html"] h5,
250
+ [data-wb-dynamic="html"] h6,
251
+ [data-wb-dynamic="html"] p,
252
+ [data-wb-dynamic="html"] ul,
253
+ [data-wb-dynamic="html"] ol,
254
+ [data-wb-dynamic="html"] li,
255
+ [data-wb-dynamic="html"] dl,
256
+ [data-wb-dynamic="html"] dt,
257
+ [data-wb-dynamic="html"] dd,
258
+ [data-wb-dynamic="html"] blockquote,
259
+ [data-wb-dynamic="html"] pre,
260
+ [data-wb-dynamic="html"] code,
261
+ [data-wb-dynamic="html"] hr,
262
+ [data-wb-dynamic="html"] table,
263
+ [data-wb-dynamic="html"] thead,
264
+ [data-wb-dynamic="html"] tbody,
265
+ [data-wb-dynamic="html"] tr,
266
+ [data-wb-dynamic="html"] th,
267
+ [data-wb-dynamic="html"] td,
268
+ [data-wb-dynamic="html"] figure,
269
+ [data-wb-dynamic="html"] figcaption,
270
+ [data-wb-dynamic="html"] strong,
271
+ [data-wb-dynamic="html"] b,
272
+ [data-wb-dynamic="html"] em,
273
+ [data-wb-dynamic="html"] i,
274
+ [data-wb-dynamic="html"] small,
275
+ [data-wb-dynamic="html"] sub,
276
+ [data-wb-dynamic="html"] sup,
277
+ [data-wb-dynamic="html"] mark,
278
+ [data-wb-dynamic="html"] a {
279
+ all: revert;
280
+ }
281
+ /* 媒体元素额外保证响应式,不破坏排版 */
282
+ [data-wb-dynamic="html"] img,
283
+ [data-wb-dynamic="html"] video,
284
+ [data-wb-dynamic="html"] iframe {
285
+ max-width: 100%;
286
+ height: auto;
287
+ }
288
+ /* 代码块保留换行 + 横向滚动,避免撑开容器 */
289
+ [data-wb-dynamic="html"] pre {
290
+ white-space: pre-wrap;
291
+ overflow-x: auto;
292
+ }
293
+ /* 表格占满容器宽度,保持富文本内表格可读 */
294
+ [data-wb-dynamic="html"] table {
295
+ width: 100%;
296
+ border-collapse: collapse;
297
+ }
298
+
299
+ /* ───────── 文章目录(TOC) ───────── */
300
+ .wb-cms-dynamic-toc {
301
+ display: block;
302
+ box-sizing: border-box;
303
+ color: #111827;
304
+ }
305
+ .wb-cms-dynamic-toc__list {
306
+ display: block;
307
+ margin: 0;
308
+ padding-left: 1.2em;
309
+ list-style: disc;
310
+ }
311
+ .wb-cms-dynamic-toc__item {
312
+ margin: 0;
313
+ }
314
+ .wb-cms-dynamic-toc__link {
315
+ color: inherit;
316
+ font-size: 14px;
317
+ line-height: 2em;
318
+ text-decoration: none;
319
+ transition: color 0.15s;
320
+ word-break: break-word;
321
+ }
322
+ .wb-cms-dynamic-toc__link:hover {
323
+ color: #3C53E8;
324
+ }
325
+ .wb-cms-dynamic-toc__item[aria-current="true"] .wb-cms-dynamic-toc__link,
326
+ .wb-cms-dynamic-toc__item.is-active .wb-cms-dynamic-toc__link {
327
+ color: #3C53E8;
328
+ font-weight: 500;
329
+ }
330
+ `;
331
+ const DYNAMIC_FIELD_STYLE_KEY = "data-wb-dynamic";
332
+ const RESOURCE_TYPE_TO_DYNAMIC_CONTEXT = {
333
+ TEMP_POST_DETAIL: "post-detail",
334
+ TEMP_POST_CATEGORY_LIST: "post-category-item",
335
+ TEMP_MEDIA_DETAIL: "media-detail",
336
+ TEMP_MEDIA_CATEGORY_LIST: "media-category-item",
337
+ TEMP_PRODUCT_DETAIL: "product-detail",
338
+ TEMP_PRODUCT_CATEGORY_LIST: "product-category-item"
339
+ };
340
+ const resolveDynamicContext = (resourceType, extJson) => {
341
+ const normalized = `${resourceType ?? ""}`.trim();
342
+ if (normalized === "TEMP_LOOP_ITEM") {
343
+ switch (getLoopItemType(extJson)) {
344
+ case "post":
345
+ return "post-loop-item";
346
+ case "media":
347
+ return "media-loop-item";
348
+ case "product":
349
+ return "product-loop-item";
350
+ case "postCategory":
351
+ return "post-category-item";
352
+ case "mediaCategory":
353
+ return "media-category-item";
354
+ case "productCategory":
355
+ return "product-category-item";
356
+ case "productCategoryFaq":
357
+ return "product-category-faq-loop-item";
358
+ default:
359
+ return "product-loop-item";
360
+ }
361
+ }
362
+ return RESOURCE_TYPE_TO_DYNAMIC_CONTEXT[normalized] || null;
363
+ };
364
+ const BREADCRUMB_REPEAT_SOURCE = {
365
+ value: "breadcrumb@breadcrumbs",
366
+ label: "面包屑导航",
367
+ itemAlias: "breadcrumb",
368
+ collection: "breadcrumbs",
369
+ itemFields: [
370
+ { value: "breadcrumb.label", label: "面包屑-标题", kind: "text" },
371
+ { value: "breadcrumb.url", label: "面包屑-URL", kind: "url" },
372
+ { value: "breadcrumb.position", label: "面包屑-序号", kind: "number" },
373
+ { value: "breadcrumb.isCurrent", label: "面包屑-当前项", kind: "bool" },
374
+ { value: "breadcrumb.currentClass", label: "面包屑-当前项 class", kind: "text" },
375
+ { value: "breadcrumb.ariaCurrent", label: "面包屑-aria-current", kind: "text" }
376
+ ]
377
+ };
378
+ const CONTEXT_DETAIL_CMS_COMPONENT = {
379
+ "post-detail": "post-detail",
380
+ "media-detail": "media-detail",
381
+ "product-detail": "product-detail",
382
+ "post-loop-item": "post-list",
383
+ "media-loop-item": "media-list",
384
+ "product-loop-item": "product-list",
385
+ "post-category-item": "post-list",
386
+ "media-category-item": "media-list",
387
+ "product-category-item": "product-list",
388
+ "product-category-faq-loop-item": "context-loop"
389
+ };
390
+ const POST_DETAIL_FIELDS = [
391
+ { value: "post.id", label: "文章 ID", kind: "number", group: "文章" },
392
+ { value: "post.name", label: "文章标题", kind: "text", group: "文章" },
393
+ { value: "post.slug", label: "文章 Slug", kind: "text", group: "文章" },
394
+ { value: "post.excerpt", label: "摘要", kind: "text", group: "文章" },
395
+ { value: "post.content", label: "正文 HTML", kind: "html", group: "文章" },
396
+ {
397
+ value: "post.publishTime",
398
+ label: "发布时间",
399
+ kind: "datetime",
400
+ defaultFormat: "yyyy-MM-dd",
401
+ group: "文章"
402
+ },
403
+ { value: "post.image", label: "封面图", kind: "image", group: "文章" },
404
+ { value: "post.imageAlt", label: "封面图 alt", kind: "text", group: "文章" },
405
+ { value: "post.coverWidth", label: "封面图宽度", kind: "number", group: "文章" },
406
+ { value: "post.coverHeight", label: "封面图高度", kind: "number", group: "文章" },
407
+ { value: "post.url", label: "文章 URL", kind: "url", group: "文章" },
408
+ { value: "post.typeCode", label: "文章类型 Code", kind: "text", group: "文章" },
409
+ { value: "post.typeName", label: "文章类型名称", kind: "text", group: "文章" },
410
+ { value: "post.categoryIds", label: "分类 ID 列表", kind: "list", group: "文章" },
411
+ { value: "post.categoryNames", label: "分类名称列表", kind: "list", group: "文章" },
412
+ { value: "post.views", label: "浏览量", kind: "number", group: "文章" },
413
+ { value: "post.author", label: "作者", kind: "text", group: "文章" },
414
+ { value: "post.tagNames", label: "标签名称列表", kind: "list", group: "文章" },
415
+ { value: "post.metaKeywords", label: "SEO keywords", kind: "text", group: "SEO" },
416
+ { value: "post.metaDescription", label: "SEO description", kind: "text", group: "SEO" },
417
+ { value: "prevPost.name", label: "上一篇标题", kind: "text", group: "上下篇" },
418
+ { value: "prevPost.url", label: "上一篇 URL", kind: "url", group: "上下篇" },
419
+ { value: "nextPost.name", label: "下一篇标题", kind: "text", group: "上下篇" },
420
+ { value: "nextPost.url", label: "下一篇 URL", kind: "url", group: "上下篇" }
421
+ ];
422
+ const POST_DETAIL_REPEATS = [
423
+ BREADCRUMB_REPEAT_SOURCE,
424
+ {
425
+ value: "tag@post.tags",
426
+ label: "文章标签",
427
+ itemAlias: "tag",
428
+ collection: "post.tags",
429
+ itemFields: [
430
+ { value: "tag.id", label: "标签-ID", kind: "number" },
431
+ { value: "tag.name", label: "标签-名称", kind: "text" }
432
+ ]
433
+ },
434
+ {
435
+ value: "tocItem@tocItems",
436
+ label: "文章目录(TOC)",
437
+ itemAlias: "tocItem",
438
+ collection: "tocItems",
439
+ itemFields: [
440
+ { value: "tocItem.text", label: "章节标题", kind: "text" },
441
+ { value: "tocItem.href", label: "章节锚点 URL", kind: "url" },
442
+ { value: "tocItem.level", label: "章节层级", kind: "number" }
443
+ ]
444
+ },
445
+ {
446
+ value: "relatedPost@relatedPosts",
447
+ label: "相关文章",
448
+ itemAlias: "relatedPost",
449
+ collection: "relatedPosts",
450
+ itemFields: [
451
+ { value: "relatedPost.name", label: "相关-标题", kind: "text" },
452
+ { value: "relatedPost.excerpt", label: "相关-摘要", kind: "text" },
453
+ {
454
+ value: "relatedPost.publishTime",
455
+ label: "相关-发布时间",
456
+ kind: "datetime",
457
+ defaultFormat: "yyyy-MM-dd"
458
+ },
459
+ { value: "relatedPost.image", label: "相关-封面图", kind: "image" },
460
+ { value: "relatedPost.imageAlt", label: "相关-封面 alt", kind: "text" },
461
+ { value: "relatedPost.url", label: "相关-URL", kind: "url" }
462
+ ]
463
+ }
464
+ ];
465
+ const MEDIA_DETAIL_FIELDS = [
466
+ { value: "media.id", label: "媒体 ID", kind: "number", group: "媒体" },
467
+ { value: "media.title", label: "媒体标题", kind: "text", group: "媒体" },
468
+ { value: "media.description", label: "媒体描述", kind: "text", group: "媒体" },
469
+ { value: "media.slug", label: "媒体 Slug", kind: "text", group: "媒体" },
470
+ { value: "media.type", label: "媒体类型", kind: "text", group: "媒体" },
471
+ { value: "media.url", label: "原始资源 URL", kind: "url", group: "媒体" },
472
+ { value: "media.size", label: "媒体大小", kind: "text", group: "媒体" },
473
+ { value: "media.coverUrl", label: "封面图", kind: "image", group: "媒体" },
474
+ { value: "media.altText", label: "封面图 alt", kind: "text", group: "媒体" },
475
+ { value: "media.categoryCode", label: "媒体分类 Code", kind: "text", group: "媒体" },
476
+ { value: "media.detailUrl", label: "媒体详情 URL", kind: "url", group: "媒体" },
477
+ {
478
+ value: "media.publishTime",
479
+ label: "发布时间",
480
+ kind: "datetime",
481
+ defaultFormat: "yyyy-MM-dd",
482
+ group: "媒体"
483
+ },
484
+ { value: "media.seoTitle", label: "SEO 标题", kind: "text", group: "SEO" },
485
+ { value: "media.seoDescription", label: "SEO description", kind: "text", group: "SEO" },
486
+ { value: "media.seoKeywords", label: "SEO keywords", kind: "text", group: "SEO" }
487
+ ];
488
+ const MEDIA_DETAIL_REPEATS = [
489
+ BREADCRUMB_REPEAT_SOURCE,
490
+ {
491
+ value: "item@media.items",
492
+ label: "媒体附件项",
493
+ itemAlias: "item",
494
+ collection: "media.items",
495
+ itemFields: [{ value: "item.url", label: "附件图片 URL", kind: "image" }]
496
+ }
497
+ ];
498
+ const PRODUCT_DETAIL_FIELDS = [
499
+ { value: "product.id", label: "产品 ID", kind: "number", group: "产品" },
500
+ { value: "product.name", label: "产品名称", kind: "text", group: "产品" },
501
+ { value: "product.slug", label: "产品 Slug", kind: "text", group: "产品" },
502
+ { value: "product.brandName", label: "品牌", kind: "text", group: "产品" },
503
+ { value: "product.categoryName", label: "产品分类名称", kind: "text", group: "产品" },
504
+ { value: "product.introduction", label: "简介(纯文本)", kind: "text", group: "产品" },
505
+ { value: "product.featuresText", label: "Features / 产品特性", kind: "text", group: "产品" },
506
+ { value: "product.keyword", label: "产品关键字", kind: "text", group: "产品" },
507
+ { value: "product.description", label: "详细描述(HTML)", kind: "html", group: "产品" },
508
+ { value: "product.picUrl", label: "主图", kind: "image", group: "产品" },
509
+ { value: "product.price", label: "价格(分)", kind: "number", group: "产品" },
510
+ { value: "product.marketPrice", label: "市场价(分)", kind: "number", group: "产品" },
511
+ { value: "product.priceFormatted", label: "格式化价格", kind: "text", group: "产品" },
512
+ { value: "product.stock", label: "库存", kind: "number", group: "产品" },
513
+ { value: "product.salesCount", label: "销量", kind: "number", group: "产品" },
514
+ { value: "product.url", label: "产品详情 URL", kind: "url", group: "产品" },
515
+ { value: "product.buyNowUrl", label: "立即购买 URL", kind: "url", group: "产品" },
516
+ { value: "product.buyNowTarget", label: "立即购买 target", kind: "text", group: "产品" },
517
+ { value: "product.datasheetDesignation", label: "Datasheet 型号", kind: "text", group: "产品" }
518
+ ];
519
+ const PRODUCT_DETAIL_REPEATS = [
520
+ BREADCRUMB_REPEAT_SOURCE,
521
+ {
522
+ value: "pic@product.sliderPicUrls",
523
+ label: "产品轮播图",
524
+ itemAlias: "pic",
525
+ collection: "product.sliderPicUrls",
526
+ itemFields: [
527
+ // 裸字符串数组,条目本身就是 URL
528
+ { value: "pic", label: "轮播图片 URL", kind: "image" }
529
+ ]
530
+ },
531
+ {
532
+ value: "sku@product.skus",
533
+ label: "SKU 列表",
534
+ itemAlias: "sku",
535
+ collection: "product.skus",
536
+ itemFields: [
537
+ { value: "sku.name", label: "SKU-名称", kind: "text" },
538
+ { value: "sku.price", label: "SKU-格式化价格", kind: "text" }
539
+ ]
540
+ },
541
+ {
542
+ value: "doc@product.documents",
543
+ label: "产品文档",
544
+ itemAlias: "doc",
545
+ collection: "product.documents",
546
+ itemFields: [
547
+ { value: "doc.name", label: "文档-文件名", kind: "text" },
548
+ { value: "doc.url", label: "文档-下载 URL", kind: "url" }
549
+ ]
550
+ },
551
+ {
552
+ value: "feature@product.features",
553
+ label: "产品特性",
554
+ itemAlias: "feature",
555
+ collection: "product.features",
556
+ itemFields: [{ value: "feature.text", label: "特性-文本", kind: "text" }]
557
+ },
558
+ {
559
+ value: "spec@product.specifications",
560
+ label: "产品规格",
561
+ itemAlias: "spec",
562
+ collection: "product.specifications",
563
+ itemFields: [
564
+ { value: "spec.code", label: "规格-Code", kind: "text" },
565
+ { value: "spec.name", label: "规格-名称", kind: "text" },
566
+ { value: "spec.label", label: "规格-标签", kind: "text" },
567
+ { value: "spec.value", label: "规格-值", kind: "text" },
568
+ { value: "spec.valueHtml", label: "规格-值(HTML)", kind: "html" },
569
+ { value: "spec.groupName", label: "规格-分组", kind: "text" },
570
+ { value: "spec.groupCode", label: "规格分组-Code", kind: "text" },
571
+ { value: "spec.unit", label: "规格-单位", kind: "text" },
572
+ { value: "spec.valueType", label: "规格-值类型", kind: "text" }
573
+ ]
574
+ },
575
+ {
576
+ value: "specGroup@product.specGroups",
577
+ label: "产品规格分组",
578
+ itemAlias: "specGroup",
579
+ collection: "product.specGroups",
580
+ itemFields: [
581
+ { value: "specGroup.name", label: "规格分组-名称", kind: "text" },
582
+ { value: "specGroup.code", label: "规格分组-Code", kind: "text" },
583
+ { value: "specGroup.sort", label: "规格分组-排序", kind: "number" }
584
+ ]
585
+ },
586
+ {
587
+ value: "spec@specGroup.specifications",
588
+ label: "当前规格分组内规格",
589
+ parentSource: "specGroup@product.specGroups",
590
+ itemAlias: "spec",
591
+ collection: "specGroup.specifications",
592
+ itemFields: [
593
+ { value: "spec.code", label: "规格-Code", kind: "text" },
594
+ { value: "spec.name", label: "规格-名称", kind: "text" },
595
+ { value: "spec.label", label: "规格-标签", kind: "text" },
596
+ { value: "spec.value", label: "规格-值", kind: "text" },
597
+ { value: "spec.valueHtml", label: "规格-值(HTML)", kind: "html" },
598
+ { value: "spec.groupName", label: "规格-分组", kind: "text" },
599
+ { value: "spec.groupCode", label: "规格分组-Code", kind: "text" },
600
+ { value: "spec.unit", label: "规格-单位", kind: "text" },
601
+ { value: "spec.valueType", label: "规格-值类型", kind: "text" }
602
+ ]
603
+ },
604
+ {
605
+ value: "faq@product.faqs",
606
+ label: "常见问答",
607
+ itemAlias: "faq",
608
+ collection: "product.faqs",
609
+ itemFields: [
610
+ { value: "faq.question", label: "问答-问题", kind: "text" },
611
+ { value: "faq.answerHtml", label: "问答-答案(HTML)", kind: "html" }
612
+ ]
613
+ },
614
+ {
615
+ value: "prop@product.propertyOptions",
616
+ label: "属性选项",
617
+ itemAlias: "prop",
618
+ collection: "product.propertyOptions",
619
+ itemFields: [
620
+ { value: "prop.propertyName", label: "属性-名称", kind: "text" },
621
+ { value: "prop.displayType", label: "属性-展示类型", kind: "text" }
622
+ ]
623
+ },
624
+ {
625
+ value: "value@prop.values",
626
+ label: "属性选项值(嵌套在属性选项内)",
627
+ parentSource: "prop@product.propertyOptions",
628
+ itemAlias: "value",
629
+ collection: "prop.values",
630
+ itemFields: [
631
+ { value: "value.valueName", label: "选项-名称", kind: "text" },
632
+ { value: "value.imageUrl", label: "选项-图片 URL", kind: "image" },
633
+ { value: "value.colorStyle", label: "选项-颜色 style", kind: "text" },
634
+ { value: "value.colorCode", label: "选项-颜色值", kind: "text" }
635
+ ]
636
+ },
637
+ {
638
+ value: "related@product.relatedProducts",
639
+ label: "相关产品",
640
+ itemAlias: "related",
641
+ collection: "product.relatedProducts",
642
+ itemFields: [
643
+ { value: "related.name", label: "相关-产品名", kind: "text" },
644
+ { value: "related.introduction", label: "相关-简介", kind: "text" },
645
+ { value: "related.picUrl", label: "相关-主图", kind: "image" },
646
+ { value: "related.url", label: "相关-URL", kind: "url" }
647
+ ]
648
+ },
649
+ {
650
+ value: "popular@product.popularModels",
651
+ label: "Popular Models",
652
+ itemAlias: "popular",
653
+ collection: "product.popularModels",
654
+ itemFields: [
655
+ { value: "popular.name", label: "Popular-产品名", kind: "text" },
656
+ { value: "popular.introduction", label: "Popular-简介", kind: "text" },
657
+ { value: "popular.picUrl", label: "Popular-主图", kind: "image" },
658
+ { value: "popular.url", label: "Popular-URL", kind: "url" }
659
+ ]
660
+ }
661
+ ];
662
+ const POST_LOOP_ITEM_FIELDS = [
663
+ { value: "post.id", label: "文章 ID", kind: "number", group: "文章" },
664
+ { value: "post.name", label: "文章标题", kind: "text", group: "文章" },
665
+ { value: "post.slug", label: "文章 Slug", kind: "text", group: "文章" },
666
+ { value: "post.excerpt", label: "摘要", kind: "text", group: "文章" },
667
+ { value: "post.content", label: "正文 HTML", kind: "html", group: "文章" },
668
+ {
669
+ value: "post.publishTime",
670
+ label: "发布时间",
671
+ kind: "datetime",
672
+ defaultFormat: "yyyy-MM-dd",
673
+ group: "文章"
674
+ },
675
+ { value: "post.image", label: "封面图", kind: "image", group: "文章" },
676
+ { value: "post.imageAlt", label: "封面图 alt", kind: "text", group: "文章" },
677
+ { value: "post.coverWidth", label: "封面图宽度", kind: "number", group: "文章" },
678
+ { value: "post.coverHeight", label: "封面图高度", kind: "number", group: "文章" },
679
+ { value: "post.url", label: "文章 URL", kind: "url", group: "文章" },
680
+ { value: "post.typeCode", label: "文章类型 Code", kind: "text", group: "文章" },
681
+ { value: "post.typeName", label: "文章类型名称", kind: "text", group: "文章" },
682
+ { value: "post.categoryIds", label: "分类 ID 列表", kind: "list", group: "文章" },
683
+ { value: "post.categoryNames", label: "分类名称列表", kind: "list", group: "文章" },
684
+ { value: "post.tagIds", label: "标签 ID 列表", kind: "list", group: "文章" },
685
+ { value: "post.tagNames", label: "标签名称列表", kind: "list", group: "文章" },
686
+ { value: "post.views", label: "浏览量", kind: "number", group: "文章" },
687
+ { value: "post.author", label: "作者", kind: "text", group: "文章" },
688
+ { value: "post.metaKeywords", label: "SEO keywords", kind: "text", group: "SEO" },
689
+ { value: "post.metaDescription", label: "SEO description", kind: "text", group: "SEO" }
690
+ ];
691
+ const MEDIA_LOOP_ITEM_FIELDS = [
692
+ { value: "media.id", label: "媒体 ID", kind: "number", group: "媒体" },
693
+ { value: "media.title", label: "媒体标题", kind: "text", group: "媒体" },
694
+ { value: "media.description", label: "媒体描述", kind: "text", group: "媒体" },
695
+ { value: "media.slug", label: "媒体 Slug", kind: "text", group: "媒体" },
696
+ { value: "media.type", label: "媒体类型", kind: "text", group: "媒体" },
697
+ { value: "media.url", label: "原始资源 URL", kind: "url", group: "媒体" },
698
+ { value: "media.size", label: "媒体大小", kind: "text", group: "媒体" },
699
+ { value: "media.coverUrl", label: "封面图", kind: "image", group: "媒体" },
700
+ { value: "media.altText", label: "封面图 alt", kind: "text", group: "媒体" },
701
+ { value: "media.categoryCode", label: "媒体分类 Code", kind: "text", group: "媒体" },
702
+ { value: "media.detailUrl", label: "媒体详情 URL", kind: "url", group: "媒体" },
703
+ {
704
+ value: "media.publishTime",
705
+ label: "发布时间",
706
+ kind: "datetime",
707
+ defaultFormat: "yyyy-MM-dd",
708
+ group: "媒体"
709
+ },
710
+ { value: "media.seoTitle", label: "SEO 标题", kind: "text", group: "SEO" },
711
+ { value: "media.seoDescription", label: "SEO description", kind: "text", group: "SEO" },
712
+ { value: "media.seoKeywords", label: "SEO keywords", kind: "text", group: "SEO" }
713
+ ];
714
+ const PRODUCT_LOOP_ITEM_FIELDS = [
715
+ { value: "product.id", label: "产品 ID", kind: "number", group: "产品" },
716
+ { value: "product.name", label: "产品名称", kind: "text", group: "产品" },
717
+ { value: "product.slug", label: "产品 Slug", kind: "text", group: "产品" },
718
+ { value: "product.brandName", label: "品牌", kind: "text", group: "产品" },
719
+ { value: "product.categoryName", label: "产品分类名称", kind: "text", group: "产品" },
720
+ { value: "product.introduction", label: "简介(纯文本)", kind: "text", group: "产品" },
721
+ { value: "product.keyword", label: "产品关键字", kind: "text", group: "产品" },
722
+ { value: "product.description", label: "详细描述(HTML)", kind: "html", group: "产品" },
723
+ { value: "product.picUrl", label: "主图", kind: "image", group: "产品" },
724
+ { value: "product.price", label: "价格(分)", kind: "number", group: "产品" },
725
+ { value: "product.marketPrice", label: "市场价(分)", kind: "number", group: "产品" },
726
+ { value: "product.priceFormatted", label: "格式化价格", kind: "text", group: "产品" },
727
+ { value: "product.stock", label: "库存", kind: "number", group: "产品" },
728
+ { value: "product.salesCount", label: "销量", kind: "number", group: "产品" },
729
+ { value: "product.url", label: "产品详情 URL", kind: "url", group: "产品" },
730
+ { value: "product.buyNowUrl", label: "立即购买 URL", kind: "url", group: "产品" },
731
+ { value: "product.buyNowTarget", label: "立即购买 target", kind: "text", group: "产品" },
732
+ { value: "product.datasheetDesignation", label: "Datasheet 型号", kind: "text", group: "产品" }
733
+ ];
734
+ const POST_CATEGORY_FIELDS = [
735
+ { value: "postCategory.id", label: "文章分类 ID", kind: "number", group: "文章分类" },
736
+ { value: "postCategory.parentId", label: "父级分类 ID", kind: "number", group: "文章分类" },
737
+ { value: "postCategory.code", label: "文章分类 Code", kind: "text", group: "文章分类" },
738
+ { value: "postCategory.name", label: "文章分类名称", kind: "text", group: "文章分类" },
739
+ { value: "postCategory.description", label: "文章分类描述", kind: "text", group: "文章分类" },
740
+ { value: "postCategory.url", label: "文章分类 URL", kind: "url", group: "文章分类" },
741
+ { value: "postCategory.clickTarget", label: "点击目标", kind: "text", group: "文章分类" }
742
+ ];
743
+ const MEDIA_CATEGORY_FIELDS = [
744
+ { value: "mediaCategory.id", label: "媒体分类 ID", kind: "number", group: "媒体分类" },
745
+ { value: "mediaCategory.code", label: "媒体分类 Code", kind: "text", group: "媒体分类" },
746
+ { value: "mediaCategory.name", label: "媒体分类名称", kind: "text", group: "媒体分类" },
747
+ { value: "mediaCategory.description", label: "媒体分类描述", kind: "text", group: "媒体分类" },
748
+ { value: "mediaCategory.url", label: "媒体分类 URL", kind: "url", group: "媒体分类" },
749
+ { value: "mediaCategory.clickTarget", label: "点击目标", kind: "text", group: "媒体分类" }
750
+ ];
751
+ const PRODUCT_CATEGORY_FIELDS = [
752
+ { value: "productCategory.id", label: "产品分类 ID", kind: "number", group: "产品分类" },
753
+ { value: "productCategory.parentId", label: "父级分类 ID", kind: "number", group: "产品分类" },
754
+ { value: "productCategory.code", label: "产品分类 Code", kind: "text", group: "产品分类" },
755
+ { value: "productCategory.name", label: "产品分类名称", kind: "text", group: "产品分类" },
756
+ { value: "productCategory.description", label: "产品分类描述", kind: "text", group: "产品分类" },
757
+ { value: "productCategory.features", label: "产品分类特性(旧文本字段)", kind: "text", group: "产品分类", hidden: true },
758
+ { value: "productCategory.featuresHtml", label: "产品分类特性(换行)", kind: "html", group: "产品分类" },
759
+ { value: "productCategory.image", label: "产品分类图片", kind: "image", group: "产品分类" },
760
+ { value: "productCategory.picUrl", label: "产品分类图片 URL", kind: "image", group: "产品分类" },
761
+ { value: "productCategory.url", label: "产品分类 URL", kind: "url", group: "产品分类" },
762
+ { value: "productCategory.productCount", label: "产品数量", kind: "number", group: "产品分类" },
763
+ {
764
+ value: "productCategory.faqCategoryId",
765
+ label: "FAQ 分类 ID",
766
+ kind: "number",
767
+ group: "产品分类"
768
+ },
769
+ { value: "productCategory.clickTarget", label: "点击目标", kind: "text", group: "产品分类" }
770
+ ];
771
+ const createCategoryPostFields = (alias, labelPrefix) => [
772
+ { value: `${alias}.id`, label: `${labelPrefix}-文章 ID`, kind: "number" },
773
+ { value: `${alias}.name`, label: `${labelPrefix}-标题`, kind: "text" },
774
+ { value: `${alias}.slug`, label: `${labelPrefix}-Slug`, kind: "text" },
775
+ { value: `${alias}.excerpt`, label: `${labelPrefix}-摘要`, kind: "text" },
776
+ {
777
+ value: `${alias}.publishTime`,
778
+ label: `${labelPrefix}-发布时间`,
779
+ kind: "datetime",
780
+ defaultFormat: "yyyy-MM-dd"
781
+ },
782
+ { value: `${alias}.image`, label: `${labelPrefix}-封面图`, kind: "image" },
783
+ { value: `${alias}.imageAlt`, label: `${labelPrefix}-封面 alt`, kind: "text" },
784
+ { value: `${alias}.url`, label: `${labelPrefix}-URL`, kind: "url" },
785
+ { value: `${alias}.typeCode`, label: `${labelPrefix}-类型 Code`, kind: "text" },
786
+ { value: `${alias}.typeName`, label: `${labelPrefix}-类型名称`, kind: "text" }
787
+ ];
788
+ const PRODUCT_CATEGORY_FAQ_FIELDS = [
789
+ { value: "faq.id", label: "FAQ-ID", kind: "number" },
790
+ { value: "faq.question", label: "FAQ-问题", kind: "text" },
791
+ { value: "faq.answer", label: "FAQ-答案", kind: "text" },
792
+ { value: "faq.answerHtml", label: "FAQ-答案(HTML)", kind: "html" }
793
+ ];
794
+ const PRODUCT_CATEGORY_APPLICATION_POST_FIELDS = createCategoryPostFields(
795
+ "applicationPost",
796
+ "Application"
797
+ );
798
+ const PRODUCT_CATEGORY_ENGINEERING_POST_FIELDS = createCategoryPostFields(
799
+ "engineeringPost",
800
+ "Engineering"
801
+ );
802
+ const PRODUCT_CATEGORY_CHALLENGE_POST_FIELDS = createCategoryPostFields(
803
+ "challengePost",
804
+ "Challenges"
805
+ );
806
+ const PRODUCT_CATEGORY_REPEATS = [
807
+ BREADCRUMB_REPEAT_SOURCE,
808
+ {
809
+ value: "field@datasheetFields",
810
+ label: "Datasheet 字段表头",
811
+ itemAlias: "field",
812
+ collection: "datasheetFields",
813
+ itemFields: [
814
+ { value: "field.code", label: "字段-Code", kind: "text" },
815
+ { value: "field.label", label: "字段名", kind: "text" },
816
+ { value: "field.unit", label: "单位", kind: "text" },
817
+ { value: "field.valueType", label: "字段类型", kind: "text" }
818
+ ]
819
+ },
820
+ {
821
+ value: "product@products",
822
+ label: "分类产品列表",
823
+ itemAlias: "product",
824
+ collection: "products",
825
+ itemFields: PRODUCT_LOOP_ITEM_FIELDS
826
+ },
827
+ {
828
+ value: "cell@product.datasheetCells",
829
+ label: "产品 Datasheet 单元格",
830
+ parentSource: "product@products",
831
+ itemAlias: "cell",
832
+ collection: "product.datasheetCells",
833
+ itemFields: [
834
+ { value: "cell.code", label: "单元格-Code", kind: "text" },
835
+ { value: "cell.label", label: "单元格-字段名", kind: "text" },
836
+ { value: "cell.value", label: "单元格-值", kind: "text" },
837
+ { value: "cell.valueHtml", label: "单元格-值(HTML)", kind: "html" },
838
+ { value: "cell.unit", label: "单元格-单位", kind: "text" },
839
+ { value: "cell.valueType", label: "单元格-类型", kind: "text" }
840
+ ]
841
+ },
842
+ {
843
+ value: "faq@productCategory.faqs",
844
+ label: "分类 FAQ",
845
+ itemAlias: "faq",
846
+ collection: "productCategory.faqs",
847
+ itemFields: PRODUCT_CATEGORY_FAQ_FIELDS
848
+ },
849
+ {
850
+ value: "applicationPost@productCategory.applicationPosts",
851
+ label: "Application 文章",
852
+ itemAlias: "applicationPost",
853
+ collection: "productCategory.applicationPosts",
854
+ itemFields: PRODUCT_CATEGORY_APPLICATION_POST_FIELDS
855
+ },
856
+ {
857
+ value: "engineeringPost@productCategory.engineeringPosts",
858
+ label: "Engineering 文章",
859
+ itemAlias: "engineeringPost",
860
+ collection: "productCategory.engineeringPosts",
861
+ itemFields: PRODUCT_CATEGORY_ENGINEERING_POST_FIELDS
862
+ },
863
+ {
864
+ value: "challengePost@productCategory.challengePosts",
865
+ label: "Challenges 文章",
866
+ itemAlias: "challengePost",
867
+ collection: "productCategory.challengePosts",
868
+ itemFields: PRODUCT_CATEGORY_CHALLENGE_POST_FIELDS
869
+ },
870
+ {
871
+ value: "popular@productCategory.popularModels",
872
+ label: "Popular Models",
873
+ itemAlias: "popular",
874
+ collection: "productCategory.popularModels",
875
+ itemFields: [
876
+ { value: "popular.name", label: "Popular-产品名", kind: "text" },
877
+ { value: "popular.introduction", label: "Popular-简介", kind: "text" },
878
+ { value: "popular.picUrl", label: "Popular-主图", kind: "image" },
879
+ { value: "popular.url", label: "Popular-URL", kind: "url" }
880
+ ]
881
+ }
882
+ ];
883
+ const DYNAMIC_FIELD_MAP = {
884
+ "post-detail": POST_DETAIL_FIELDS,
885
+ "media-detail": MEDIA_DETAIL_FIELDS,
886
+ "product-detail": PRODUCT_DETAIL_FIELDS,
887
+ "post-loop-item": POST_LOOP_ITEM_FIELDS,
888
+ "media-loop-item": MEDIA_LOOP_ITEM_FIELDS,
889
+ "product-loop-item": PRODUCT_LOOP_ITEM_FIELDS,
890
+ "post-category-item": POST_CATEGORY_FIELDS,
891
+ "media-category-item": MEDIA_CATEGORY_FIELDS,
892
+ "product-category-item": PRODUCT_CATEGORY_FIELDS,
893
+ "product-category-faq-loop-item": PRODUCT_CATEGORY_FAQ_FIELDS
894
+ };
895
+ const DYNAMIC_REPEAT_MAP = {
896
+ "post-detail": POST_DETAIL_REPEATS,
897
+ "media-detail": MEDIA_DETAIL_REPEATS,
898
+ "product-detail": PRODUCT_DETAIL_REPEATS,
899
+ "post-loop-item": [],
900
+ "media-loop-item": [],
901
+ "product-loop-item": [],
902
+ "post-category-item": [BREADCRUMB_REPEAT_SOURCE],
903
+ "media-category-item": [BREADCRUMB_REPEAT_SOURCE],
904
+ "product-category-item": PRODUCT_CATEGORY_REPEATS,
905
+ "product-category-faq-loop-item": []
906
+ };
907
+ const DEFAULT_DYNAMIC_CONTEXT = "post-detail";
908
+ const ALL_FIELDS = Object.values(DYNAMIC_FIELD_MAP).flat();
909
+ const ALL_REPEAT_ITEM_FIELDS = Object.values(DYNAMIC_REPEAT_MAP).flat().flatMap((source) => source.itemFields);
910
+ const findFieldMeta = (fieldValue) => {
911
+ const normalized = `${fieldValue ?? ""}`.trim();
912
+ if (!normalized)
913
+ return null;
914
+ return ALL_FIELDS.find((f) => f.value === normalized) || ALL_REPEAT_ITEM_FIELDS.find((f) => f.value === normalized) || null;
915
+ };
916
+ const findRepeatSource = (context, sourceValue) => {
917
+ const normalized = `${sourceValue ?? ""}`.trim();
918
+ if (!normalized)
919
+ return null;
920
+ return (DYNAMIC_REPEAT_MAP[context] || []).find((s) => s.value === normalized) || null;
921
+ };
922
+ const getPageLevelFields = (context) => {
923
+ return DYNAMIC_FIELD_MAP[context] || [];
924
+ };
925
+ const getAllRepeatItemFields = (context) => {
926
+ return (DYNAMIC_REPEAT_MAP[context] || []).flatMap((source) => source.itemFields);
927
+ };
928
+ const buildFieldSelectOptions = (fields, options) => {
929
+ const result = [];
930
+ if (options == null ? void 0 : options.includeEmpty) {
931
+ result.push({ value: options.emptyValue ?? "", label: options.emptyLabel ?? "—(不绑定)" });
932
+ }
933
+ fields.forEach((field) => {
934
+ if (field.hidden)
935
+ return;
936
+ const prefix = field.group ? `[${field.group}] ` : "";
937
+ result.push({ value: field.value, label: `${prefix}${field.label}` });
938
+ });
939
+ return result;
940
+ };
941
+ const filterFieldsByKind = (fields, kinds) => {
942
+ if (!kinds || kinds.length === 0)
943
+ return fields;
944
+ const set = new Set(kinds);
945
+ return fields.filter((field) => set.has(field.kind));
946
+ };
947
+ const normalizeContext = (value) => {
948
+ const s = `${value ?? ""}`.trim();
949
+ if (s === "post-detail" || s === "media-detail" || s === "product-detail" || s === "post-loop-item" || s === "media-loop-item" || s === "product-loop-item" || s === "post-category-item" || s === "media-category-item" || s === "product-category-item" || s === "product-category-faq-loop-item")
950
+ return s;
951
+ return null;
952
+ };
953
+ const getDynamicContextFromComponent = (component, editor) => {
954
+ var _a;
955
+ try {
956
+ const page = findPageForComponent(component, editor);
957
+ if (page) {
958
+ const custom = ((_a = page.get) == null ? void 0 : _a.call(page, "custom")) ?? page.custom ?? {};
959
+ const direct = normalizeContext(custom == null ? void 0 : custom[WB_TEMPLATE_CONTEXT_KEY]);
960
+ if (direct)
961
+ return direct;
962
+ const resourceType = custom == null ? void 0 : custom.resourceType;
963
+ const fromResource = resolveDynamicContext(resourceType);
964
+ if (fromResource)
965
+ return fromResource;
966
+ }
967
+ } catch {
968
+ }
969
+ return DEFAULT_DYNAMIC_CONTEXT;
970
+ };
971
+ const findPageForComponent = (component, editor) => {
972
+ var _a, _b, _c, _d, _e, _f, _g, _h;
973
+ const ed = editor || ((_b = (_a = component == null ? void 0 : component.em) == null ? void 0 : _a.get) == null ? void 0 : _b.call(_a, "Editor")) || (component == null ? void 0 : component.em);
974
+ if (!(ed == null ? void 0 : ed.Pages))
975
+ return null;
976
+ const selected = (_d = (_c = ed.Pages).getSelected) == null ? void 0 : _d.call(_c);
977
+ if (selected) {
978
+ const main = (_e = selected.getMainComponent) == null ? void 0 : _e.call(selected);
979
+ if (main && isDescendantOf(component, main))
980
+ return selected;
981
+ }
982
+ const pages = ((_g = (_f = ed.Pages).getAll) == null ? void 0 : _g.call(_f)) || [];
983
+ for (const page of pages) {
984
+ const main = (_h = page.getMainComponent) == null ? void 0 : _h.call(page);
985
+ if (main && isDescendantOf(component, main))
986
+ return page;
987
+ }
988
+ return selected || null;
989
+ };
990
+ const isDescendantOf = (component, maybeAncestor) => {
991
+ var _a;
992
+ if (!component || !maybeAncestor)
993
+ return false;
994
+ let cur = component;
995
+ const seen = /* @__PURE__ */ new Set();
996
+ while (cur && !seen.has(cur)) {
997
+ seen.add(cur);
998
+ if (cur === maybeAncestor)
999
+ return true;
1000
+ cur = (_a = cur.parent) == null ? void 0 : _a.call(cur);
1001
+ }
1002
+ return false;
1003
+ };
1004
+ const findAncestorRepeat = (component) => {
1005
+ return findAncestorRepeats(component)[0] || null;
1006
+ };
1007
+ const findAncestorRepeats = (component) => {
1008
+ var _a, _b, _c;
1009
+ const repeats = [];
1010
+ if (!component)
1011
+ return repeats;
1012
+ let cur = (_a = component.parent) == null ? void 0 : _a.call(component);
1013
+ let steps = 0;
1014
+ while (cur && steps < 64) {
1015
+ if (((_b = cur.get) == null ? void 0 : _b.call(cur, "type")) === WB_CMS_DYN_REPEAT_TYPE)
1016
+ repeats.push(cur);
1017
+ cur = (_c = cur.parent) == null ? void 0 : _c.call(cur);
1018
+ steps += 1;
1019
+ }
1020
+ return repeats;
1021
+ };
1022
+ const resolveAvailableFields = (component, options) => {
1023
+ var _a;
1024
+ const context = getDynamicContextFromComponent(component, options == null ? void 0 : options.editor);
1025
+ const repeatAncestors = findAncestorRepeats(component);
1026
+ const repeatAncestor = repeatAncestors[0] || null;
1027
+ let fields = [];
1028
+ if (repeatAncestor) {
1029
+ const source = findRepeatSource(context, (_a = repeatAncestor.get) == null ? void 0 : _a.call(repeatAncestor, "dynSource"));
1030
+ if (source) {
1031
+ fields = [...source.itemFields];
1032
+ } else {
1033
+ fields = getAllRepeatItemFields(context);
1034
+ }
1035
+ if ((options == null ? void 0 : options.includeContextFields) !== false) {
1036
+ const outerFields = repeatAncestors.slice(1).map((ancestor) => {
1037
+ var _a2;
1038
+ return findRepeatSource(context, (_a2 = ancestor.get) == null ? void 0 : _a2.call(ancestor, "dynSource"));
1039
+ }).filter(Boolean).flatMap((outerSource) => outerSource.itemFields);
1040
+ fields = [...fields, ...outerFields, ...getPageLevelFields(context)];
1041
+ }
1042
+ } else {
1043
+ fields = getPageLevelFields(context);
1044
+ }
1045
+ if ((options == null ? void 0 : options.kinds) && options.kinds.length > 0) {
1046
+ fields = filterFieldsByKind(fields, options.kinds);
1047
+ }
1048
+ return {
1049
+ context,
1050
+ inRepeat: !!repeatAncestor,
1051
+ fields
1052
+ };
1053
+ };
1054
+ const buildTraitOptions = (fields, options) => {
1055
+ return buildFieldSelectOptions(fields, options).map((opt) => ({
1056
+ id: opt.value || "__empty__",
1057
+ value: opt.value,
1058
+ label: opt.label
1059
+ }));
1060
+ };
1061
+ const getRepeatSourceOptions = (component, editor) => {
1062
+ var _a, _b;
1063
+ const context = getDynamicContextFromComponent(component, editor);
1064
+ const parentSource = (_b = (_a = findAncestorRepeat(component)) == null ? void 0 : _a.get) == null ? void 0 : _b.call(_a, "dynSource");
1065
+ const allSources = DYNAMIC_REPEAT_MAP[context] || [];
1066
+ const list = parentSource ? allSources.filter((source) => source.parentSource === parentSource) : allSources.filter((source) => !source.parentSource);
1067
+ return [
1068
+ { id: "__empty__", value: "", label: "—(请选择数据源)" },
1069
+ ...list.map((source) => ({ id: source.value, value: source.value, label: source.label }))
1070
+ ];
1071
+ };
1072
+ const refreshTraitOptions = (component, traitName, options) => {
1073
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
1074
+ const trait = (_a = component == null ? void 0 : component.getTrait) == null ? void 0 : _a.call(component, traitName);
1075
+ if (!trait)
1076
+ return;
1077
+ const current = `${((_b = component == null ? void 0 : component.get) == null ? void 0 : _b.call(component, traitName)) ?? ""}`.trim();
1078
+ const currentMeta = current ? findFieldMeta(current) : null;
1079
+ const normalizedOptions = [...options];
1080
+ if (current && !normalizedOptions.some((option) => option.value === current)) {
1081
+ normalizedOptions.push({
1082
+ id: current,
1083
+ value: current,
1084
+ label: currentMeta ? `当前:${currentMeta.label}` : `当前:${current}`
1085
+ });
1086
+ }
1087
+ try {
1088
+ (_c = trait.set) == null ? void 0 : _c.call(trait, "options", normalizedOptions);
1089
+ } catch {
1090
+ }
1091
+ try {
1092
+ (_f = (_e = (_d = component == null ? void 0 : component.get) == null ? void 0 : _d.call(component, "traits")) == null ? void 0 : _e.trigger) == null ? void 0 : _f.call(_e, "change");
1093
+ (_g = component == null ? void 0 : component.trigger) == null ? void 0 : _g.call(component, "change:traits", component);
1094
+ } catch {
1095
+ }
1096
+ try {
1097
+ (_i = (_h = component == null ? void 0 : component.em) == null ? void 0 : _h.trigger) == null ? void 0 : _i.call(_h, "component:update:traits", component);
1098
+ } catch {
1099
+ }
1100
+ };
1101
+ const clearTraitValueIfUnavailable = (component, propName, fields) => {
1102
+ var _a, _b;
1103
+ const current = `${((_a = component == null ? void 0 : component.get) == null ? void 0 : _a.call(component, propName)) ?? ""}`.trim();
1104
+ if (!current)
1105
+ return;
1106
+ if (fields.some((field) => field.value === current))
1107
+ return;
1108
+ if (findFieldMeta(current))
1109
+ return;
1110
+ (_b = component == null ? void 0 : component.set) == null ? void 0 : _b.call(component, propName, "");
1111
+ };
1112
+ const applyBindAttribute = (model, attrName, value) => {
1113
+ var _a, _b;
1114
+ const attrs = { ...((_a = model.getAttributes) == null ? void 0 : _a.call(model)) || {} };
1115
+ const normalized = `${value ?? ""}`.trim();
1116
+ if (normalized) {
1117
+ attrs[attrName] = normalized;
1118
+ } else {
1119
+ delete attrs[attrName];
1120
+ }
1121
+ (_b = model.setAttributes) == null ? void 0 : _b.call(model, attrs);
1122
+ };
1123
+ const buildSparseAttrs = (entries) => {
1124
+ const out = {};
1125
+ entries.forEach(([key, value]) => {
1126
+ const normalized = `${value ?? ""}`.trim();
1127
+ if (normalized)
1128
+ out[key] = normalized;
1129
+ });
1130
+ return out;
1131
+ };
1132
+ const FIRST_ADD_FLAG = "__wbDynFirstAddHandled";
1133
+ const DYNAMIC_KEYS_WITH_PUBLISH_STYLES = /* @__PURE__ */ new Set([
1134
+ "text",
1135
+ "html",
1136
+ "image",
1137
+ "if",
1138
+ "repeat",
1139
+ "seo-meta",
1140
+ "breadcrumb",
1141
+ "toc"
1142
+ ]);
1143
+ const runSyncAttrs = (model, registration) => {
1144
+ var _a, _b, _c;
1145
+ const base = registration.syncAttrs(model) || {};
1146
+ const next = {
1147
+ [WB_DYN_MARK_ATTR]: registration.dynamicKey
1148
+ };
1149
+ Object.entries(base).forEach(([key, value]) => {
1150
+ const normalized = `${value ?? ""}`.trim();
1151
+ if (normalized) {
1152
+ next[key] = normalized;
1153
+ }
1154
+ });
1155
+ const current = ((_a = model.getAttributes) == null ? void 0 : _a.call(model)) || {};
1156
+ const toRemove = [];
1157
+ Object.keys(current).forEach((key) => {
1158
+ if (key.startsWith("data-cms-bind") || key === "data-cms-if" || key === "data-cms-repeat" || key === "data-cms-repeat-container" || key.startsWith("data-cms-repeat-filter-") || key === "data-cms-html" || key === "data-cms-format" || key === "data-wb-item-alias") {
1159
+ if (!(key in next))
1160
+ toRemove.push(key);
1161
+ }
1162
+ });
1163
+ if (toRemove.length) {
1164
+ const cleaned = { ...current };
1165
+ toRemove.forEach((k) => delete cleaned[k]);
1166
+ (_b = model.setAttributes) == null ? void 0 : _b.call(model, { ...cleaned, ...next });
1167
+ } else {
1168
+ (_c = model.addAttributes) == null ? void 0 : _c.call(model, next);
1169
+ }
1170
+ };
1171
+ const registerDynamicFieldBlock = (editor, registration) => {
1172
+ const domComponents = editor == null ? void 0 : editor.DomComponents;
1173
+ if (!domComponents)
1174
+ return;
1175
+ if (domComponents.getType(registration.type))
1176
+ return;
1177
+ const traitsValue = typeof registration.traits === "function" ? registration.traits(editor) : registration.traits;
1178
+ const {
1179
+ tagName,
1180
+ name,
1181
+ content,
1182
+ components,
1183
+ attributes,
1184
+ defaultProps,
1185
+ draggable,
1186
+ droppable,
1187
+ editable,
1188
+ removable,
1189
+ copyable,
1190
+ stylable,
1191
+ styles
1192
+ } = registration.defaults;
1193
+ const componentStyles = styles ?? (DYNAMIC_KEYS_WITH_PUBLISH_STYLES.has(registration.dynamicKey) ? DYNAMIC_FIELD_STYLES : void 0);
1194
+ domComponents.addType(registration.type, {
1195
+ isComponent: (el) => {
1196
+ var _a;
1197
+ return ((_a = el == null ? void 0 : el.getAttribute) == null ? void 0 : _a.call(el, WB_DYN_MARK_ATTR)) === registration.dynamicKey ? { type: registration.type } : false;
1198
+ },
1199
+ model: {
1200
+ defaults: {
1201
+ name,
1202
+ tagName,
1203
+ draggable: draggable ?? true,
1204
+ droppable: droppable ?? false,
1205
+ selectable: true,
1206
+ hoverable: true,
1207
+ editable: editable ?? false,
1208
+ removable: removable ?? true,
1209
+ copyable: copyable ?? true,
1210
+ stylable: stylable ?? true,
1211
+ content,
1212
+ components,
1213
+ attributes: {
1214
+ [WB_DYN_MARK_ATTR]: registration.dynamicKey,
1215
+ ...attributes || {}
1216
+ },
1217
+ ...componentStyles ? { styles: componentStyles } : {},
1218
+ ...defaultProps || {},
1219
+ traits: traitsValue
1220
+ },
1221
+ init() {
1222
+ var _a, _b, _c, _d, _e;
1223
+ try {
1224
+ (_a = registration.hydrateProps) == null ? void 0 : _a.call(registration, this, editor);
1225
+ } catch {
1226
+ }
1227
+ const watch = ((_b = registration.watchProps) == null ? void 0 : _b.length) ? registration.watchProps.map((p) => `change:${p}`).join(" ") : "";
1228
+ if (watch) {
1229
+ this.on(watch, () => {
1230
+ var _a2;
1231
+ runSyncAttrs(this, registration);
1232
+ try {
1233
+ (_a2 = registration.refreshTraits) == null ? void 0 : _a2.call(registration, this, editor);
1234
+ } catch {
1235
+ }
1236
+ });
1237
+ }
1238
+ runSyncAttrs(this, registration);
1239
+ try {
1240
+ if (!this.get(FIRST_ADD_FLAG)) {
1241
+ this.set(FIRST_ADD_FLAG, true, { silent: true });
1242
+ (_c = registration.onFirstAdd) == null ? void 0 : _c.call(registration, this, editor);
1243
+ runSyncAttrs(this, registration);
1244
+ }
1245
+ } catch {
1246
+ }
1247
+ const refresh = () => {
1248
+ var _a2;
1249
+ try {
1250
+ (_a2 = registration.refreshTraits) == null ? void 0 : _a2.call(registration, this, editor);
1251
+ } catch {
1252
+ }
1253
+ };
1254
+ this.on("component:mount component:selected component:update:parent", refresh);
1255
+ try {
1256
+ (_d = editor == null ? void 0 : editor.on) == null ? void 0 : _d.call(editor, "component:selected", (selected) => {
1257
+ if (selected === this)
1258
+ refresh();
1259
+ });
1260
+ } catch {
1261
+ }
1262
+ refresh();
1263
+ (_e = registration.onModelInit) == null ? void 0 : _e.call(registration, this, editor);
1264
+ }
1265
+ }
1266
+ });
1267
+ };
1268
+ const LOCKED$1 = {
1269
+ selectable: false,
1270
+ hoverable: false,
1271
+ draggable: false,
1272
+ droppable: false,
1273
+ removable: false,
1274
+ copyable: false,
1275
+ editable: false,
1276
+ highlightable: false,
1277
+ layerable: false
1278
+ };
1279
+ const registerDynamicBreadcrumbBlock = (editor) => {
1280
+ registerDynamicFieldBlock(editor, {
1281
+ type: WB_CMS_DYN_BREADCRUMB_TYPE,
1282
+ dynamicKey: "breadcrumb",
1283
+ defaults: {
1284
+ tagName: "nav",
1285
+ name: "面包屑导航",
1286
+ attributes: {
1287
+ class: "wb-cms-dynamic-breadcrumb",
1288
+ "aria-label": "Breadcrumb"
1289
+ },
1290
+ droppable: false,
1291
+ editable: false,
1292
+ components: [
1293
+ {
1294
+ tagName: "ol",
1295
+ attributes: {
1296
+ class: "wb-cms-dynamic-breadcrumb__list"
1297
+ },
1298
+ ...LOCKED$1,
1299
+ components: [
1300
+ {
1301
+ tagName: "li",
1302
+ attributes: {
1303
+ class: "wb-cms-dynamic-breadcrumb__item",
1304
+ "data-wb-dynamic": "repeat-item",
1305
+ "data-cms-repeat": "breadcrumb@breadcrumbs",
1306
+ "data-cms-bind-classappend": "breadcrumb.currentClass"
1307
+ },
1308
+ ...LOCKED$1,
1309
+ components: [
1310
+ {
1311
+ tagName: "a",
1312
+ attributes: {
1313
+ class: "wb-cms-dynamic-breadcrumb__link",
1314
+ "data-cms-bind": "breadcrumb.label",
1315
+ "data-cms-bind-href": "breadcrumb.url",
1316
+ "data-cms-bind-title": "breadcrumb.label",
1317
+ "data-cms-bind-aria-current": "breadcrumb.ariaCurrent",
1318
+ href: "#"
1319
+ },
1320
+ content: "Breadcrumb",
1321
+ ...LOCKED$1
1322
+ },
1323
+ {
1324
+ tagName: "span",
1325
+ attributes: {
1326
+ class: "wb-cms-dynamic-breadcrumb__separator",
1327
+ "aria-hidden": "true"
1328
+ },
1329
+ content: "/",
1330
+ ...LOCKED$1
1331
+ }
1332
+ ]
1333
+ }
1334
+ ]
1335
+ }
1336
+ ]
1337
+ },
1338
+ watchProps: [],
1339
+ syncAttrs: () => ({}),
1340
+ traits: []
1341
+ });
1342
+ };
1343
+ const MODE_OPTIONS = [
1344
+ { id: "truthy", value: "truthy", label: "字段有值时显示" },
1345
+ { id: "falsy", value: "falsy", label: "字段为空时显示" }
1346
+ ];
1347
+ const registerDynamicConditionalBlock = (editor) => {
1348
+ registerDynamicFieldBlock(editor, {
1349
+ type: WB_CMS_DYN_IF_TYPE,
1350
+ dynamicKey: "if",
1351
+ defaults: {
1352
+ tagName: "div",
1353
+ name: "条件显示",
1354
+ attributes: {
1355
+ class: "wb-cms-dynamic-if"
1356
+ },
1357
+ defaultProps: {
1358
+ dynField: "",
1359
+ dynMode: "truthy"
1360
+ },
1361
+ droppable: true
1362
+ },
1363
+ watchProps: ["dynField", "dynMode"],
1364
+ syncAttrs: (model) => {
1365
+ const field = String(model.get("dynField") ?? "").trim();
1366
+ const mode = String(model.get("dynMode") ?? "truthy").trim();
1367
+ return {
1368
+ "data-cms-if": field,
1369
+ "data-cms-if-mode": field ? mode : ""
1370
+ };
1371
+ },
1372
+ traits: [
1373
+ {
1374
+ type: "select",
1375
+ label: "依据字段",
1376
+ name: "dynField",
1377
+ changeProp: true,
1378
+ options: [{ value: "", label: "—(请选择字段)" }]
1379
+ },
1380
+ {
1381
+ type: "select",
1382
+ label: "触发条件",
1383
+ name: "dynMode",
1384
+ changeProp: true,
1385
+ options: MODE_OPTIONS
1386
+ }
1387
+ ],
1388
+ refreshTraits: (model) => {
1389
+ const { fields } = resolveAvailableFields(model);
1390
+ clearTraitValueIfUnavailable(model, "dynField", fields);
1391
+ refreshTraitOptions(
1392
+ model,
1393
+ "dynField",
1394
+ buildTraitOptions(fields, { includeEmpty: true, emptyLabel: "—(请选择字段)" })
1395
+ );
1396
+ }
1397
+ });
1398
+ };
1399
+ const DEFAULT_FORMAT = "yyyy-MM-dd";
1400
+ const registerDynamicDatetimeBlock = (editor) => {
1401
+ registerDynamicFieldBlock(editor, {
1402
+ type: WB_CMS_DYN_DATETIME_TYPE,
1403
+ dynamicKey: "datetime",
1404
+ defaults: {
1405
+ tagName: "time",
1406
+ name: "动态日期",
1407
+ content: "2024-01-01",
1408
+ defaultProps: {
1409
+ dynField: "",
1410
+ dynFormat: DEFAULT_FORMAT
1411
+ },
1412
+ editable: false,
1413
+ droppable: false
1414
+ },
1415
+ watchProps: ["dynField", "dynFormat"],
1416
+ syncAttrs: (model) => {
1417
+ const field = String(model.get("dynField") ?? "").trim();
1418
+ const format = String(model.get("dynFormat") ?? "").trim() || DEFAULT_FORMAT;
1419
+ return {
1420
+ "data-cms-bind": field,
1421
+ "data-cms-bind-datetime": field,
1422
+ "data-cms-format": field ? format : ""
1423
+ };
1424
+ },
1425
+ hydrateProps: (model) => {
1426
+ var _a;
1427
+ const attrs = ((_a = model.getAttributes) == null ? void 0 : _a.call(model)) || {};
1428
+ const field = String(attrs["data-cms-bind"] ?? attrs["data-cms-bind-datetime"] ?? "").trim();
1429
+ const format = String(attrs["data-cms-format"] ?? "").trim();
1430
+ if (field && !model.get("dynField"))
1431
+ model.set("dynField", field, { silent: true });
1432
+ if (format && !model.get("dynFormat"))
1433
+ model.set("dynFormat", format, { silent: true });
1434
+ },
1435
+ traits: [
1436
+ {
1437
+ type: "select",
1438
+ label: "日期字段",
1439
+ name: "dynField",
1440
+ changeProp: true,
1441
+ options: [{ value: "", label: "—(请选择字段)" }]
1442
+ },
1443
+ {
1444
+ type: "text",
1445
+ label: "格式",
1446
+ name: "dynFormat",
1447
+ changeProp: true,
1448
+ placeholder: DEFAULT_FORMAT
1449
+ }
1450
+ ],
1451
+ refreshTraits: (model) => {
1452
+ const { fields } = resolveAvailableFields(model, { kinds: ["datetime"] });
1453
+ clearTraitValueIfUnavailable(model, "dynField", fields);
1454
+ refreshTraitOptions(
1455
+ model,
1456
+ "dynField",
1457
+ buildTraitOptions(fields, { includeEmpty: true, emptyLabel: "—(请选择字段)" })
1458
+ );
1459
+ },
1460
+ onFirstAdd: (model) => {
1461
+ const { fields } = resolveAvailableFields(model, { kinds: ["datetime"] });
1462
+ if (fields[0] && !model.get("dynField")) {
1463
+ model.set("dynField", fields[0].value);
1464
+ const meta = findFieldMeta(fields[0].value);
1465
+ if ((meta == null ? void 0 : meta.defaultFormat) && !model.get("dynFormat"))
1466
+ model.set("dynFormat", meta.defaultFormat);
1467
+ }
1468
+ }
1469
+ });
1470
+ };
1471
+ const DEFAULT_PLACEHOLDER_HTML = `
1472
+ <h2>示例标题(H2)</h2>
1473
+ <p>动态正文 HTML 将在发布时由 CMS 数据替换。这里保留后台富文本自身的字号 / 粗细 / 段落 margin / 列表缩进等样式。</p>
1474
+ <h3>次级标题(H3)</h3>
1475
+ <p>支持富文本内容:<strong>加粗</strong>、<em>斜体</em>、<a href="#">链接</a>、列表、引用、代码块等。</p>
1476
+ <ul>
1477
+ <li>列表项 A</li>
1478
+ <li>列表项 B</li>
1479
+ </ul>
1480
+ <blockquote>引用段落的默认样式也会保留。</blockquote>
1481
+ `.trim();
1482
+ const HTML_FIELD_KINDS = ["html", "text", "number", "url", "list"];
1483
+ const registerDynamicHtmlBlock = (editor) => {
1484
+ registerDynamicFieldBlock(editor, {
1485
+ type: WB_CMS_DYN_HTML_TYPE,
1486
+ dynamicKey: "html",
1487
+ defaults: {
1488
+ tagName: "div",
1489
+ name: "动态 HTML",
1490
+ components: [{ tagName: "div", content: DEFAULT_PLACEHOLDER_HTML, selectable: false, hoverable: false }],
1491
+ attributes: { class: "wb-cms-dynamic-html" },
1492
+ defaultProps: {
1493
+ dynField: ""
1494
+ },
1495
+ droppable: false,
1496
+ editable: false
1497
+ },
1498
+ watchProps: ["dynField"],
1499
+ syncAttrs: (model) => {
1500
+ const field = String(model.get("dynField") ?? "").trim();
1501
+ return { "data-cms-html": field };
1502
+ },
1503
+ hydrateProps: (model) => {
1504
+ var _a;
1505
+ const attrs = ((_a = model.getAttributes) == null ? void 0 : _a.call(model)) || {};
1506
+ const field = String(attrs["data-cms-html"] ?? "").trim();
1507
+ if (field && !model.get("dynField"))
1508
+ model.set("dynField", field, { silent: true });
1509
+ },
1510
+ traits: [
1511
+ {
1512
+ type: "select",
1513
+ label: "字段",
1514
+ name: "dynField",
1515
+ changeProp: true,
1516
+ options: [{ value: "", label: "—(请选择字段)" }]
1517
+ }
1518
+ ],
1519
+ refreshTraits: (model) => {
1520
+ const { fields } = resolveAvailableFields(model, { kinds: [...HTML_FIELD_KINDS] });
1521
+ clearTraitValueIfUnavailable(model, "dynField", fields);
1522
+ refreshTraitOptions(
1523
+ model,
1524
+ "dynField",
1525
+ buildTraitOptions(fields, { includeEmpty: true, emptyLabel: "—(请选择字段)" })
1526
+ );
1527
+ },
1528
+ onFirstAdd: (model) => {
1529
+ const { fields } = resolveAvailableFields(model, { kinds: [...HTML_FIELD_KINDS] });
1530
+ if (fields[0] && !model.get("dynField"))
1531
+ model.set("dynField", fields[0].value);
1532
+ },
1533
+ onModelInit: (model) => {
1534
+ const fieldMeta = findFieldMeta(model.get("dynField"));
1535
+ if (fieldMeta && !HTML_FIELD_KINDS.includes(fieldMeta.kind)) {
1536
+ model.set("dynField", "");
1537
+ }
1538
+ }
1539
+ });
1540
+ };
1541
+ const DEFAULT_PLACEHOLDER_SVG = '<svg xmlns="http://www.w3.org/2000/svg" width="320" height="200"><rect width="100%" height="100%" fill="#e5e7eb"/><text x="50%" y="50%" dominant-baseline="central" text-anchor="middle" fill="#6b7280" font-family="sans-serif" font-size="14">动态图片</text></svg>';
1542
+ const DEFAULT_PLACEHOLDER = `data:image/svg+xml;charset=UTF-8,${encodeURIComponent(DEFAULT_PLACEHOLDER_SVG)}`;
1543
+ const OBJECT_FIT_OPTIONS = ["cover", "contain", "fill", "none", "scale-down"].map((v) => ({
1544
+ id: v,
1545
+ value: v,
1546
+ label: v
1547
+ }));
1548
+ const registerDynamicImageBlock = (editor) => {
1549
+ registerDynamicFieldBlock(editor, {
1550
+ type: WB_CMS_DYN_IMAGE_TYPE,
1551
+ dynamicKey: "image",
1552
+ defaults: {
1553
+ tagName: "img",
1554
+ name: "动态图片",
1555
+ attributes: {
1556
+ src: DEFAULT_PLACEHOLDER,
1557
+ alt: "动态图片",
1558
+ style: "max-width:100%;height:auto;display:block;object-fit:cover;"
1559
+ },
1560
+ defaultProps: {
1561
+ dynSrcField: "",
1562
+ dynAltField: "",
1563
+ dynPlaceholder: DEFAULT_PLACEHOLDER,
1564
+ dynObjectFit: "cover"
1565
+ },
1566
+ droppable: false,
1567
+ editable: false
1568
+ },
1569
+ watchProps: ["dynSrcField", "dynAltField", "dynPlaceholder", "dynObjectFit"],
1570
+ syncAttrs: (model) => {
1571
+ var _a, _b;
1572
+ const placeholder = String(model.get("dynPlaceholder") ?? "").trim() || DEFAULT_PLACEHOLDER;
1573
+ const fit = String(model.get("dynObjectFit") ?? "cover").trim() || "cover";
1574
+ const style = `max-width:100%;height:auto;display:block;object-fit:${fit};`;
1575
+ const existing = ((_a = model.getAttributes) == null ? void 0 : _a.call(model)) || {};
1576
+ (_b = model.setAttributes) == null ? void 0 : _b.call(model, {
1577
+ ...existing,
1578
+ src: placeholder,
1579
+ style
1580
+ });
1581
+ return {
1582
+ "data-cms-bind-src": String(model.get("dynSrcField") ?? "").trim(),
1583
+ "data-cms-bind-alt": String(model.get("dynAltField") ?? "").trim()
1584
+ };
1585
+ },
1586
+ hydrateProps: (model) => {
1587
+ var _a, _b, _c;
1588
+ const attrs = ((_a = model.getAttributes) == null ? void 0 : _a.call(model)) || {};
1589
+ const srcField = String(attrs["data-cms-bind-src"] ?? "").trim();
1590
+ const altField = String(attrs["data-cms-bind-alt"] ?? "").trim();
1591
+ const src = String(attrs.src ?? "").trim();
1592
+ const style = String(attrs.style ?? "").trim();
1593
+ const fit = (_c = (_b = style.match(/object-fit\s*:\s*([^;]+)/i)) == null ? void 0 : _b[1]) == null ? void 0 : _c.trim();
1594
+ if (srcField && !model.get("dynSrcField")) {
1595
+ model.set("dynSrcField", srcField, { silent: true });
1596
+ }
1597
+ if (altField && !model.get("dynAltField")) {
1598
+ model.set("dynAltField", altField, { silent: true });
1599
+ }
1600
+ if (src && !model.get("dynPlaceholder"))
1601
+ model.set("dynPlaceholder", src, { silent: true });
1602
+ if (fit && !model.get("dynObjectFit"))
1603
+ model.set("dynObjectFit", fit, { silent: true });
1604
+ },
1605
+ traits: [
1606
+ {
1607
+ type: "select",
1608
+ label: "图片字段",
1609
+ name: "dynSrcField",
1610
+ changeProp: true,
1611
+ options: [{ value: "", label: "—(请选择字段)" }]
1612
+ },
1613
+ {
1614
+ type: "select",
1615
+ label: "Alt 字段",
1616
+ name: "dynAltField",
1617
+ changeProp: true,
1618
+ options: [{ value: "", label: "—(不绑定)" }]
1619
+ },
1620
+ {
1621
+ type: "text",
1622
+ label: "占位图 URL",
1623
+ name: "dynPlaceholder",
1624
+ changeProp: true,
1625
+ placeholder: "编辑器预览使用"
1626
+ },
1627
+ {
1628
+ type: "select",
1629
+ label: "填充方式",
1630
+ name: "dynObjectFit",
1631
+ changeProp: true,
1632
+ options: OBJECT_FIT_OPTIONS
1633
+ }
1634
+ ],
1635
+ refreshTraits: (model) => {
1636
+ const { fields: imageFields } = resolveAvailableFields(model, { kinds: ["image"] });
1637
+ const { fields: textFields } = resolveAvailableFields(model, { kinds: ["text"] });
1638
+ clearTraitValueIfUnavailable(model, "dynSrcField", imageFields);
1639
+ clearTraitValueIfUnavailable(model, "dynAltField", textFields);
1640
+ refreshTraitOptions(
1641
+ model,
1642
+ "dynSrcField",
1643
+ buildTraitOptions(imageFields, { includeEmpty: true, emptyLabel: "—(请选择字段)" })
1644
+ );
1645
+ refreshTraitOptions(
1646
+ model,
1647
+ "dynAltField",
1648
+ buildTraitOptions(textFields, { includeEmpty: true, emptyLabel: "—(不绑定)" })
1649
+ );
1650
+ },
1651
+ onFirstAdd: (model) => {
1652
+ const { fields: imageFields } = resolveAvailableFields(model, { kinds: ["image"] });
1653
+ const { fields: textFields } = resolveAvailableFields(model, { kinds: ["text"] });
1654
+ if (imageFields[0] && !model.get("dynSrcField"))
1655
+ model.set("dynSrcField", imageFields[0].value);
1656
+ const srcField = String(model.get("dynSrcField") ?? "");
1657
+ const guessAltFromSrc = srcField.replace(/\.image$/i, ".imageAlt");
1658
+ const matchedAlt = textFields.find((f) => f.value === guessAltFromSrc) || textFields.find((f) => /alt$/i.test(f.value));
1659
+ if (matchedAlt && !model.get("dynAltField"))
1660
+ model.set("dynAltField", matchedAlt.value);
1661
+ }
1662
+ });
1663
+ };
1664
+ const TARGET_OPTIONS = [
1665
+ { id: "_self", value: "_self", label: "当前窗口" },
1666
+ { id: "_blank", value: "_blank", label: "新窗口" }
1667
+ ];
1668
+ const REL_OPTIONS = [
1669
+ { id: "none", value: "", label: "—(不设置)" },
1670
+ { id: "nofollow", value: "nofollow", label: "nofollow" },
1671
+ { id: "noopener", value: "noopener", label: "noopener" },
1672
+ { id: "noopener-noreferrer", value: "noopener noreferrer", label: "noopener noreferrer" }
1673
+ ];
1674
+ const registerDynamicLinkBlock = (editor) => {
1675
+ registerDynamicFieldBlock(editor, {
1676
+ type: WB_CMS_DYN_LINK_TYPE,
1677
+ dynamicKey: "link",
1678
+ defaults: {
1679
+ tagName: "a",
1680
+ name: "动态链接",
1681
+ content: "动态链接",
1682
+ attributes: { href: "#" },
1683
+ defaultProps: {
1684
+ dynHrefField: "",
1685
+ dynHrefTemplate: "",
1686
+ dynTextField: "",
1687
+ dynTarget: "_self",
1688
+ dynRel: ""
1689
+ },
1690
+ droppable: true
1691
+ },
1692
+ watchProps: ["dynHrefField", "dynHrefTemplate", "dynTextField", "dynTarget", "dynRel"],
1693
+ syncAttrs: (model) => {
1694
+ var _a, _b;
1695
+ const target = String(model.get("dynTarget") ?? "").trim();
1696
+ const rel = String(model.get("dynRel") ?? "").trim();
1697
+ const existing = { ...((_a = model.getAttributes) == null ? void 0 : _a.call(model)) || {} };
1698
+ existing.href = "#";
1699
+ if (target)
1700
+ existing.target = target;
1701
+ else
1702
+ delete existing.target;
1703
+ if (rel)
1704
+ existing.rel = rel;
1705
+ else
1706
+ delete existing.rel;
1707
+ (_b = model.setAttributes) == null ? void 0 : _b.call(model, existing);
1708
+ const textField = String(model.get("dynTextField") ?? "").trim();
1709
+ if (textField) {
1710
+ const textMeta = resolveAvailableFields(model, {
1711
+ kinds: ["text", "number", "url"]
1712
+ }).fields.find((f) => f.value === textField);
1713
+ if (textMeta)
1714
+ model.set("content", textMeta.label);
1715
+ model.set("editable", false, { silent: true });
1716
+ } else {
1717
+ model.set("editable", true, { silent: true });
1718
+ }
1719
+ return {
1720
+ "data-cms-bind-href": String(model.get("dynHrefField") ?? "").trim(),
1721
+ "data-cms-bind-href-template": String(model.get("dynHrefTemplate") ?? "").trim(),
1722
+ "data-cms-bind": textField
1723
+ };
1724
+ },
1725
+ hydrateProps: (model) => {
1726
+ var _a;
1727
+ const attrs = ((_a = model.getAttributes) == null ? void 0 : _a.call(model)) || {};
1728
+ const hrefField = String(attrs["data-cms-bind-href"] ?? "").trim();
1729
+ const hrefTemplate = String(attrs["data-cms-bind-href-template"] ?? "").trim();
1730
+ const textField = String(attrs["data-cms-bind"] ?? "").trim();
1731
+ const target = String(attrs.target ?? "").trim();
1732
+ const rel = String(attrs.rel ?? "").trim();
1733
+ if (hrefField && !model.get("dynHrefField")) {
1734
+ model.set("dynHrefField", hrefField, { silent: true });
1735
+ }
1736
+ if (hrefTemplate && !model.get("dynHrefTemplate")) {
1737
+ model.set("dynHrefTemplate", hrefTemplate, { silent: true });
1738
+ }
1739
+ if (textField && !model.get("dynTextField")) {
1740
+ model.set("dynTextField", textField, { silent: true });
1741
+ }
1742
+ if (target && !model.get("dynTarget"))
1743
+ model.set("dynTarget", target, { silent: true });
1744
+ if (rel && !model.get("dynRel"))
1745
+ model.set("dynRel", rel, { silent: true });
1746
+ },
1747
+ traits: [
1748
+ {
1749
+ type: "select",
1750
+ label: "链接字段",
1751
+ name: "dynHrefField",
1752
+ changeProp: true,
1753
+ options: [{ value: "", label: "—(请选择字段)" }]
1754
+ },
1755
+ {
1756
+ type: "text",
1757
+ label: "链接拼接模板",
1758
+ name: "dynHrefTemplate",
1759
+ changeProp: true,
1760
+ placeholder: "/pdf?url={{value}}"
1761
+ },
1762
+ {
1763
+ type: "select",
1764
+ label: "文字字段",
1765
+ name: "dynTextField",
1766
+ changeProp: true,
1767
+ options: [{ value: "", label: "—(手动编辑或子组件)" }]
1768
+ },
1769
+ {
1770
+ type: "select",
1771
+ label: "打开方式",
1772
+ name: "dynTarget",
1773
+ changeProp: true,
1774
+ options: TARGET_OPTIONS
1775
+ },
1776
+ {
1777
+ type: "select",
1778
+ label: "rel",
1779
+ name: "dynRel",
1780
+ changeProp: true,
1781
+ options: REL_OPTIONS
1782
+ }
1783
+ ],
1784
+ refreshTraits: (model) => {
1785
+ const { fields: urlFields } = resolveAvailableFields(model, { kinds: ["url"] });
1786
+ const { fields: textFields } = resolveAvailableFields(model, {
1787
+ kinds: ["text", "number", "url"]
1788
+ });
1789
+ clearTraitValueIfUnavailable(model, "dynHrefField", urlFields);
1790
+ clearTraitValueIfUnavailable(model, "dynTextField", textFields);
1791
+ refreshTraitOptions(
1792
+ model,
1793
+ "dynHrefField",
1794
+ buildTraitOptions(urlFields, { includeEmpty: true, emptyLabel: "—(请选择字段)" })
1795
+ );
1796
+ refreshTraitOptions(
1797
+ model,
1798
+ "dynTextField",
1799
+ buildTraitOptions(textFields, { includeEmpty: true, emptyLabel: "—(手动编辑或子组件)" })
1800
+ );
1801
+ },
1802
+ onFirstAdd: (model) => {
1803
+ const { fields } = resolveAvailableFields(model, { kinds: ["url"] });
1804
+ if (fields[0] && !model.get("dynHrefField"))
1805
+ model.set("dynHrefField", fields[0].value);
1806
+ },
1807
+ onModelInit: (model) => {
1808
+ var _a, _b;
1809
+ if (((_a = model == null ? void 0 : model.get) == null ? void 0 : _a.call(model, "tagName")) !== "a") {
1810
+ (_b = model.set) == null ? void 0 : _b.call(model, "tagName", "a");
1811
+ }
1812
+ }
1813
+ });
1814
+ };
1815
+ const CHILD_DEFAULTS = { selectable: false, hoverable: false, removable: false, copyable: false };
1816
+ const buildChildren = () => [
1817
+ {
1818
+ tagName: "title",
1819
+ content: "页面标题",
1820
+ attributes: {},
1821
+ ...CHILD_DEFAULTS
1822
+ },
1823
+ {
1824
+ tagName: "meta",
1825
+ attributes: { name: "keywords", content: "" },
1826
+ ...CHILD_DEFAULTS
1827
+ },
1828
+ {
1829
+ tagName: "meta",
1830
+ attributes: { name: "description", content: "" },
1831
+ ...CHILD_DEFAULTS
1832
+ }
1833
+ ];
1834
+ const registerDynamicSeoBlock = (editor) => {
1835
+ registerDynamicFieldBlock(editor, {
1836
+ type: WB_CMS_DYN_SEO_TYPE,
1837
+ dynamicKey: "seo-meta",
1838
+ defaults: {
1839
+ tagName: "div",
1840
+ name: "SEO Meta",
1841
+ attributes: {
1842
+ style: "display:none"
1843
+ },
1844
+ components: buildChildren(),
1845
+ defaultProps: {
1846
+ dynTitleField: "",
1847
+ dynKeywordsField: "",
1848
+ dynDescriptionField: ""
1849
+ },
1850
+ droppable: false
1851
+ },
1852
+ watchProps: ["dynTitleField", "dynKeywordsField", "dynDescriptionField"],
1853
+ syncAttrs: (model) => {
1854
+ var _a, _b;
1855
+ const title = String(model.get("dynTitleField") ?? "").trim();
1856
+ const kw = String(model.get("dynKeywordsField") ?? "").trim();
1857
+ const desc = String(model.get("dynDescriptionField") ?? "").trim();
1858
+ const children = ((_b = (_a = model.components) == null ? void 0 : _a.call(model)) == null ? void 0 : _b.models) || [];
1859
+ children.forEach((child) => {
1860
+ var _a2, _b2;
1861
+ const tag = String(((_a2 = child.get) == null ? void 0 : _a2.call(child, "tagName")) || "").toLowerCase();
1862
+ if (tag === "title")
1863
+ applyOn(child, "data-cms-bind", title);
1864
+ if (tag === "meta") {
1865
+ const attrs = ((_b2 = child.getAttributes) == null ? void 0 : _b2.call(child)) || {};
1866
+ if (attrs.name === "keywords")
1867
+ applyOn(child, "data-cms-bind-content", kw);
1868
+ if (attrs.name === "description")
1869
+ applyOn(child, "data-cms-bind-content", desc);
1870
+ }
1871
+ });
1872
+ return {};
1873
+ },
1874
+ traits: [
1875
+ {
1876
+ type: "select",
1877
+ label: "Title 字段",
1878
+ name: "dynTitleField",
1879
+ changeProp: true,
1880
+ options: [{ value: "", label: "—(请选择字段)" }]
1881
+ },
1882
+ {
1883
+ type: "select",
1884
+ label: "Keywords 字段",
1885
+ name: "dynKeywordsField",
1886
+ changeProp: true,
1887
+ options: [{ value: "", label: "—(不设置)" }]
1888
+ },
1889
+ {
1890
+ type: "select",
1891
+ label: "Description 字段",
1892
+ name: "dynDescriptionField",
1893
+ changeProp: true,
1894
+ options: [{ value: "", label: "—(不设置)" }]
1895
+ }
1896
+ ],
1897
+ refreshTraits: (model) => {
1898
+ const { fields } = resolveAvailableFields(model, { kinds: ["text", "number", "url"] });
1899
+ clearTraitValueIfUnavailable(model, "dynTitleField", fields);
1900
+ clearTraitValueIfUnavailable(model, "dynKeywordsField", fields);
1901
+ clearTraitValueIfUnavailable(model, "dynDescriptionField", fields);
1902
+ const options = buildTraitOptions(fields, { includeEmpty: true });
1903
+ refreshTraitOptions(model, "dynTitleField", options);
1904
+ refreshTraitOptions(model, "dynKeywordsField", options);
1905
+ refreshTraitOptions(model, "dynDescriptionField", options);
1906
+ },
1907
+ onFirstAdd: (model) => {
1908
+ var _a, _b, _c;
1909
+ const { fields } = resolveAvailableFields(model, { kinds: ["text", "number", "url"] });
1910
+ const byName = (suffix) => fields.find((f) => new RegExp(`${suffix}$`, "i").test(f.value));
1911
+ if (!model.get("dynTitleField"))
1912
+ model.set("dynTitleField", ((_a = byName("\\.name")) == null ? void 0 : _a.value) || "");
1913
+ if (!model.get("dynKeywordsField"))
1914
+ model.set("dynKeywordsField", ((_b = byName("metaKeywords")) == null ? void 0 : _b.value) || "");
1915
+ if (!model.get("dynDescriptionField"))
1916
+ model.set("dynDescriptionField", ((_c = byName("metaDescription")) == null ? void 0 : _c.value) || "");
1917
+ }
1918
+ });
1919
+ };
1920
+ const applyOn = (child, attrName, value) => {
1921
+ var _a, _b;
1922
+ const current = { ...((_a = child.getAttributes) == null ? void 0 : _a.call(child)) || {} };
1923
+ const normalized = `${value ?? ""}`.trim();
1924
+ if (normalized) {
1925
+ current[attrName] = normalized;
1926
+ } else {
1927
+ delete current[attrName];
1928
+ }
1929
+ (_b = child.setAttributes) == null ? void 0 : _b.call(child, current);
1930
+ };
1931
+ const TAG_OPTIONS = ["h1", "h2", "h3", "h4", "h5", "h6", "p", "span", "div"].map((tag) => ({
1932
+ id: tag,
1933
+ value: tag,
1934
+ label: tag.toUpperCase()
1935
+ }));
1936
+ const LIST_FORMAT_OPTIONS = [
1937
+ { id: "bracket", value: "bracket", label: "[item] [item](方括号)" },
1938
+ { id: "comma", value: "comma", label: "item, item(英文逗号)" },
1939
+ { id: "space", value: "space", label: "item item(空格)" },
1940
+ { id: "pipe", value: "pipe", label: "item | item(竖线)" },
1941
+ { id: "hashtag", value: "hashtag", label: "#item #item(话题标签)" }
1942
+ ];
1943
+ const LIST_KINDS = ["text", "number", "url", "list"];
1944
+ const previewListText = (format, sample = ["ai", "design"]) => {
1945
+ switch (format) {
1946
+ case "bracket":
1947
+ return sample.map((x) => `[${x}]`).join(" ");
1948
+ case "comma":
1949
+ return sample.join(", ");
1950
+ case "space":
1951
+ return sample.join(" ");
1952
+ case "pipe":
1953
+ return sample.join(" | ");
1954
+ case "hashtag":
1955
+ return sample.map((x) => `#${x}`).join(" ");
1956
+ default:
1957
+ return sample.join(" ");
1958
+ }
1959
+ };
1960
+ const registerDynamicTextBlock = (editor) => {
1961
+ registerDynamicFieldBlock(editor, {
1962
+ type: WB_CMS_DYN_TEXT_TYPE,
1963
+ dynamicKey: "text",
1964
+ defaults: {
1965
+ tagName: "span",
1966
+ name: "动态文本",
1967
+ content: "动态文本",
1968
+ attributes: {},
1969
+ defaultProps: {
1970
+ dynField: "",
1971
+ dynTag: "span",
1972
+ dynFallback: "动态文本",
1973
+ dynListFormat: "bracket"
1974
+ },
1975
+ editable: false,
1976
+ droppable: false
1977
+ },
1978
+ watchProps: ["dynField", "dynTag", "dynFallback", "dynListFormat"],
1979
+ syncAttrs: (model) => {
1980
+ const nextTag = model.get("dynTag") || "span";
1981
+ if (model.get("tagName") !== nextTag) {
1982
+ model.set("tagName", nextTag);
1983
+ }
1984
+ const field = String(model.get("dynField") ?? "").trim();
1985
+ const meta = findFieldMeta(field);
1986
+ const isList = (meta == null ? void 0 : meta.kind) === "list";
1987
+ const listFormat = isList ? String(model.get("dynListFormat") ?? "bracket").trim() || "bracket" : "";
1988
+ const fallback = String(model.get("dynFallback") ?? "").trim();
1989
+ if (isList) {
1990
+ model.set("content", previewListText(listFormat), { avoidStore: true });
1991
+ } else if (fallback) {
1992
+ model.set("content", fallback, { avoidStore: false });
1993
+ }
1994
+ return {
1995
+ "data-cms-bind": field,
1996
+ "data-cms-list-format": listFormat
1997
+ };
1998
+ },
1999
+ hydrateProps: (model) => {
2000
+ var _a;
2001
+ const attrs = ((_a = model.getAttributes) == null ? void 0 : _a.call(model)) || {};
2002
+ const bind = String(attrs["data-cms-bind"] ?? "").trim();
2003
+ const listFormat = String(attrs["data-cms-list-format"] ?? "").trim();
2004
+ if (bind && !model.get("dynField"))
2005
+ model.set("dynField", bind, { silent: true });
2006
+ if (listFormat && !model.get("dynListFormat")) {
2007
+ model.set("dynListFormat", listFormat, { silent: true });
2008
+ }
2009
+ const tagName = String(model.get("tagName") ?? "").trim();
2010
+ if (tagName && !model.get("dynTag"))
2011
+ model.set("dynTag", tagName, { silent: true });
2012
+ const content = String(model.get("content") ?? "").trim();
2013
+ if (content && !model.get("dynFallback"))
2014
+ model.set("dynFallback", content, { silent: true });
2015
+ },
2016
+ traits: [
2017
+ {
2018
+ type: "select",
2019
+ label: "字段",
2020
+ name: "dynField",
2021
+ changeProp: true,
2022
+ options: [{ value: "", label: "—(请选择字段)" }]
2023
+ },
2024
+ {
2025
+ type: "select",
2026
+ label: "HTML 标签",
2027
+ name: "dynTag",
2028
+ changeProp: true,
2029
+ options: TAG_OPTIONS
2030
+ },
2031
+ {
2032
+ type: "select",
2033
+ label: "列表拼接格式",
2034
+ name: "dynListFormat",
2035
+ changeProp: true,
2036
+ options: LIST_FORMAT_OPTIONS
2037
+ },
2038
+ {
2039
+ type: "text",
2040
+ label: "占位文案",
2041
+ name: "dynFallback",
2042
+ changeProp: true,
2043
+ placeholder: "字段为空时显示(list 字段不生效)"
2044
+ }
2045
+ ],
2046
+ refreshTraits: (model) => {
2047
+ const { fields } = resolveAvailableFields(model, {
2048
+ kinds: [...LIST_KINDS]
2049
+ });
2050
+ clearTraitValueIfUnavailable(model, "dynField", fields);
2051
+ refreshTraitOptions(
2052
+ model,
2053
+ "dynField",
2054
+ buildTraitOptions(fields, { includeEmpty: true, emptyLabel: "—(请选择字段)" })
2055
+ );
2056
+ },
2057
+ onFirstAdd: (model) => {
2058
+ const { fields } = resolveAvailableFields(model, { kinds: [...LIST_KINDS] });
2059
+ const prefer = fields.find((f) => f.kind !== "list") || fields[0];
2060
+ const first = prefer;
2061
+ if (first && !model.get("dynField")) {
2062
+ model.set("dynField", first.value);
2063
+ model.set("dynFallback", first.label, { avoidStore: true });
2064
+ model.set("content", first.kind === "list" ? previewListText("bracket") : first.label);
2065
+ }
2066
+ },
2067
+ onModelInit: (model) => {
2068
+ const fieldMeta = findFieldMeta(model.get("dynField"));
2069
+ if (fieldMeta && !LIST_KINDS.includes(fieldMeta.kind)) {
2070
+ model.set("dynField", "");
2071
+ }
2072
+ }
2073
+ });
2074
+ };
2075
+ const DEFAULT_SOURCE_FIELD = "post.content";
2076
+ const DEFAULT_LEVELS = "2";
2077
+ const TOC_CLASS = "wb-cms-dynamic-toc";
2078
+ const TOC_LIST_CLASS = `${TOC_CLASS}__list`;
2079
+ const TOC_ITEM_CLASS = `${TOC_CLASS}__item`;
2080
+ const TOC_LINK_CLASS = `${TOC_CLASS}__link`;
2081
+ const LOCKED = {
2082
+ selectable: false,
2083
+ hoverable: false,
2084
+ draggable: false,
2085
+ droppable: false,
2086
+ removable: false,
2087
+ copyable: false,
2088
+ editable: false,
2089
+ highlightable: false,
2090
+ layerable: false
2091
+ };
2092
+ const resolveRootClass = (model) => {
2093
+ var _a, _b;
2094
+ const current = String(((_b = (_a = model.getAttributes) == null ? void 0 : _a.call(model)) == null ? void 0 : _b.class) ?? "").split(/\s+/).filter(Boolean).filter((className) => className !== TOC_CLASS && !className.startsWith(`${TOC_CLASS}--`));
2095
+ return [TOC_CLASS, ...current].join(" ");
2096
+ };
2097
+ const replaceClass = (component, from, to) => {
2098
+ var _a, _b;
2099
+ const attrs = (_a = component == null ? void 0 : component.getAttributes) == null ? void 0 : _a.call(component);
2100
+ if (!(attrs == null ? void 0 : attrs.class))
2101
+ return;
2102
+ const classes = String(attrs.class).split(/\s+/).filter(Boolean).map((className) => className === from ? to : className);
2103
+ (_b = component.setAttributes) == null ? void 0 : _b.call(component, { ...attrs, class: Array.from(new Set(classes)).join(" ") });
2104
+ };
2105
+ const migrateLegacyClasses = (model) => {
2106
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
2107
+ const list = (_c = (_b = (_a = model.components) == null ? void 0 : _a.call(model)) == null ? void 0 : _b.at) == null ? void 0 : _c.call(_b, 0);
2108
+ const item = (_f = (_e = (_d = list == null ? void 0 : list.components) == null ? void 0 : _d.call(list)) == null ? void 0 : _e.at) == null ? void 0 : _f.call(_e, 0);
2109
+ const link = (_i = (_h = (_g = item == null ? void 0 : item.components) == null ? void 0 : _g.call(item)) == null ? void 0 : _h.at) == null ? void 0 : _i.call(_h, 0);
2110
+ replaceClass(list, "wb-cms-dynamic-toc-list", TOC_LIST_CLASS);
2111
+ replaceClass(item, "wb-cms-dynamic-toc-item", TOC_ITEM_CLASS);
2112
+ replaceClass(link, "wb-cms-dynamic-toc-link", TOC_LINK_CLASS);
2113
+ };
2114
+ const registerDynamicTocBlock = (editor) => {
2115
+ registerDynamicFieldBlock(editor, {
2116
+ type: WB_CMS_DYN_TOC_TYPE,
2117
+ dynamicKey: "toc",
2118
+ defaults: {
2119
+ tagName: "nav",
2120
+ name: "文章目录",
2121
+ attributes: { class: "wb-cms-dynamic-toc" },
2122
+ defaultProps: {
2123
+ dynSourceField: DEFAULT_SOURCE_FIELD
2124
+ },
2125
+ droppable: false,
2126
+ editable: false,
2127
+ components: [
2128
+ {
2129
+ tagName: "ul",
2130
+ attributes: {
2131
+ class: TOC_LIST_CLASS,
2132
+ "data-cms-repeat": "tocItem@tocItems",
2133
+ "data-cms-repeat-container": "true"
2134
+ },
2135
+ ...LOCKED,
2136
+ components: [
2137
+ {
2138
+ tagName: "li",
2139
+ attributes: {
2140
+ class: TOC_ITEM_CLASS,
2141
+ "data-wb-dynamic": "repeat-item"
2142
+ },
2143
+ ...LOCKED,
2144
+ components: [
2145
+ {
2146
+ tagName: "a",
2147
+ attributes: {
2148
+ class: TOC_LINK_CLASS,
2149
+ "data-cms-bind-href": "tocItem.href",
2150
+ "data-cms-bind": "tocItem.text",
2151
+ href: "#"
2152
+ },
2153
+ content: "示例章节(由目录数据填充)",
2154
+ ...LOCKED
2155
+ }
2156
+ ]
2157
+ }
2158
+ ]
2159
+ }
2160
+ ]
2161
+ },
2162
+ watchProps: ["dynSourceField"],
2163
+ syncAttrs: (model) => {
2164
+ const source = String(model.get("dynSourceField") ?? "").trim() || DEFAULT_SOURCE_FIELD;
2165
+ return {
2166
+ class: resolveRootClass(model),
2167
+ "data-cms-toc-source": source,
2168
+ "data-cms-toc-levels": DEFAULT_LEVELS,
2169
+ "data-cms-toc-style": "bulleted"
2170
+ };
2171
+ },
2172
+ traits: [
2173
+ {
2174
+ type: "select",
2175
+ label: "内容来源字段",
2176
+ name: "dynSourceField",
2177
+ changeProp: true,
2178
+ options: [{ value: DEFAULT_SOURCE_FIELD, label: "正文 HTML(post.content)" }]
2179
+ }
2180
+ ],
2181
+ refreshTraits: (model) => {
2182
+ const { fields } = resolveAvailableFields(model, {
2183
+ kinds: ["html"],
2184
+ includeContextFields: false
2185
+ });
2186
+ const options = fields.length ? buildTraitOptions(fields, { includeEmpty: false }) : [
2187
+ {
2188
+ id: DEFAULT_SOURCE_FIELD,
2189
+ value: DEFAULT_SOURCE_FIELD,
2190
+ label: "正文 HTML(post.content)"
2191
+ }
2192
+ ];
2193
+ if (fields.length) {
2194
+ clearTraitValueIfUnavailable(model, "dynSourceField", fields);
2195
+ }
2196
+ refreshTraitOptions(model, "dynSourceField", options);
2197
+ },
2198
+ onFirstAdd: (model) => {
2199
+ var _a;
2200
+ if (!model.get("dynSourceField")) {
2201
+ const { fields } = resolveAvailableFields(model, { kinds: ["html"] });
2202
+ model.set("dynSourceField", ((_a = fields[0]) == null ? void 0 : _a.value) || DEFAULT_SOURCE_FIELD);
2203
+ }
2204
+ },
2205
+ onModelInit: (model) => {
2206
+ migrateLegacyClasses(model);
2207
+ }
2208
+ });
2209
+ };
2210
+ const LOOP_GRID_SCHEMA_VERSION = 1;
2211
+ const DEFAULT_LOOP_GRID_SCHEMA = {
2212
+ type: "loop-grid",
2213
+ version: 1,
2214
+ gridId: "loop-grid-1",
2215
+ filterKey: "",
2216
+ itemTemplateId: "post-card",
2217
+ loopItemType: "post",
2218
+ loopItemTemplateResourceId: "",
2219
+ emptyTemplateId: "empty-default",
2220
+ providerKey: "mock",
2221
+ query: {
2222
+ sourceType: "posts",
2223
+ queryMode: "manual",
2224
+ category: [],
2225
+ contextCollection: "",
2226
+ orderBy: "date",
2227
+ order: "desc"
2228
+ },
2229
+ layout: {
2230
+ columns: 3,
2231
+ itemsPerPage: 6,
2232
+ columnGap: 24,
2233
+ rowGap: 24
2234
+ },
2235
+ pagination: {
2236
+ mode: "numbers",
2237
+ pageLimit: 8
2238
+ },
2239
+ emptyState: {
2240
+ nothingFoundText: "Nothing found."
2241
+ },
2242
+ filterState: {
2243
+ taxonomy: [],
2244
+ tag: [],
2245
+ category: [],
2246
+ author: [],
2247
+ search: "",
2248
+ currentPage: 1,
2249
+ extras: {}
2250
+ },
2251
+ advanced: {
2252
+ hostRenderMode: "ssr"
2253
+ }
2254
+ };
2255
+ function parseCsvList(value) {
2256
+ return String(value ?? "").split(",").map((item) => item.trim()).filter(Boolean);
2257
+ }
2258
+ function serializeCsvList(items) {
2259
+ return items.map((item) => item.trim()).filter(Boolean).join(", ");
2260
+ }
2261
+ function parseNumber(value, fallback, options) {
2262
+ const numeric = Number(value);
2263
+ if (!Number.isFinite(numeric))
2264
+ return fallback;
2265
+ let next = numeric;
2266
+ if ((options == null ? void 0 : options.min) !== void 0 && next < options.min)
2267
+ next = options.min;
2268
+ if ((options == null ? void 0 : options.max) !== void 0 && next > options.max)
2269
+ next = options.max;
2270
+ return next;
2271
+ }
2272
+ function encodeLoopGridSchema(schema) {
2273
+ return encodeURIComponent(JSON.stringify(schema));
2274
+ }
2275
+ function createLoopGridId(seed) {
2276
+ if (seed)
2277
+ return seed;
2278
+ return `loop-grid-${Math.random().toString(36).slice(2, 8)}`;
2279
+ }
2280
+ export {
2281
+ WB_CMS_TECHNICAL_SERVICE_LIST_TYPE as $,
2282
+ WB_CMS_DYN_HTML_TYPE as A,
2283
+ WB_CMS_DYN_IF_TYPE as B,
2284
+ CONTEXT_DETAIL_CMS_COMPONENT as C,
2285
+ DEFAULT_LOOP_GRID_SCHEMA as D,
2286
+ WB_CMS_DYN_IMAGE_TYPE as E,
2287
+ WB_CMS_DYN_LINK_TYPE as F,
2288
+ WB_CMS_DYN_REPEAT_ITEM_TYPE as G,
2289
+ WB_CMS_DYN_REPEAT_TYPE as H,
2290
+ WB_CMS_DYN_SEO_TYPE as I,
2291
+ WB_CMS_DYN_TEXT_TYPE as J,
2292
+ WB_CMS_DYN_TOC_TYPE as K,
2293
+ LOOP_GRID_PAGINATION_STYLE as L,
2294
+ WB_CMS_FAQ_SECTION_TYPE as M,
2295
+ WB_CMS_MEDIA_DETAIL_TYPE as N,
2296
+ WB_CMS_MEDIA_LATEST_TYPE as O,
2297
+ WB_CMS_MEDIA_LIST_TYPE as P,
2298
+ WB_CMS_POST_DETAIL_TYPE as Q,
2299
+ RESOURCE_TYPE_TO_DYNAMIC_CONTEXT as R,
2300
+ WB_CMS_POST_LATEST_TYPE as S,
2301
+ WB_CMS_PRODUCT_CATEGORY_FAQ_TYPE as T,
2302
+ WB_CMS_PRODUCT_FEATURED_TYPE as U,
2303
+ WB_CMS_PRODUCT_LATEST_TYPE as V,
2304
+ WB_CMS_MENU_TREE_TYPE as W,
2305
+ WB_CMS_PRODUCT_RELATED_TYPE as X,
2306
+ WB_CMS_SEARCH_TYPE as Y,
2307
+ WB_CMS_SITE_MENU_TYPE as Z,
2308
+ WB_CMS_TECHNICAL_DOWNLOAD_LIST_TYPE as _,
2309
+ LOOP_GRID_PREV_ICON as a,
2310
+ WB_CMS_TECHNICAL_SUPPORT_DETAIL_TYPE as a0,
2311
+ WB_DYN_MARK_ATTR as a1,
2312
+ WB_TEMPLATE_CONTEXT_KEY as a2,
2313
+ applyBindAttribute as a3,
2314
+ buildFieldSelectOptions as a4,
2315
+ buildSparseAttrs as a5,
2316
+ buildTraitOptions as a6,
2317
+ clearTraitValueIfUnavailable as a7,
2318
+ createLoopGridId as a8,
2319
+ filterFieldsByKind as a9,
2320
+ findAncestorRepeat as aa,
2321
+ findAncestorRepeats as ab,
2322
+ findFieldMeta as ac,
2323
+ findRepeatSource as ad,
2324
+ getAllRepeatItemFields as ae,
2325
+ getDynamicContextFromComponent as af,
2326
+ getPageLevelFields as ag,
2327
+ getRepeatSourceOptions as ah,
2328
+ refreshTraitOptions as ai,
2329
+ registerDynamicFieldBlock as aj,
2330
+ resolveAvailableFields as ak,
2331
+ resolveDynamicContext as al,
2332
+ resolveMenuTreeAttrs as am,
2333
+ serializeCsvList as an,
2334
+ LOOP_GRID_NEXT_ICON as b,
2335
+ LOOP_GRID_PAGINATION_SCRIPT as c,
2336
+ parseCsvList as d,
2337
+ encodeLoopGridSchema as e,
2338
+ DEFAULT_MENU_TREE_CODE as f,
2339
+ registerDynamicHtmlBlock as g,
2340
+ registerDynamicImageBlock as h,
2341
+ registerDynamicLinkBlock as i,
2342
+ registerDynamicDatetimeBlock as j,
2343
+ registerDynamicConditionalBlock as k,
2344
+ registerDynamicBreadcrumbBlock as l,
2345
+ registerDynamicTocBlock as m,
2346
+ registerDynamicSeoBlock as n,
2347
+ DEFAULT_DYNAMIC_CONTEXT as o,
2348
+ parseNumber as p,
2349
+ DYNAMIC_FIELD_MAP as q,
2350
+ registerDynamicTextBlock as r,
2351
+ DYNAMIC_FIELD_STYLES as s,
2352
+ DYNAMIC_FIELD_STYLE_KEY as t,
2353
+ DYNAMIC_REPEAT_MAP as u,
2354
+ LOOP_GRID_PAGINATION_CSS as v,
2355
+ LOOP_GRID_SCHEMA_VERSION as w,
2356
+ WB_CMS_DYNAMIC_FIELD_TYPES as x,
2357
+ WB_CMS_DYN_BREADCRUMB_TYPE as y,
2358
+ WB_CMS_DYN_DATETIME_TYPE as z
2359
+ };