@webikon/webentor-core 0.9.13 → 0.10.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 (126) hide show
  1. package/CHANGELOG.md +24 -1
  2. package/README.md +41 -0
  3. package/core-js/_alpine.ts +6 -0
  4. package/core-js/_slider.ts +22 -11
  5. package/core-js/blocks-components/button.tsx +50 -33
  6. package/core-js/blocks-components/custom-image-sizes-panel.tsx +3 -1
  7. package/core-js/blocks-components/typography-picker-select.tsx +16 -1
  8. package/core-js/blocks-filters/_filter-core-typography.tsx +11 -1
  9. package/core-js/blocks-filters/_slider-settings.tsx +1 -1
  10. package/core-js/blocks-filters/_wrap-with-container.tsx +104 -0
  11. package/core-js/blocks-filters/responsive-settings/AGENTS.md +255 -0
  12. package/core-js/blocks-filters/responsive-settings/components/AppliedClassesViewer.tsx +189 -0
  13. package/core-js/blocks-filters/responsive-settings/components/BoxModelControl.tsx +346 -0
  14. package/core-js/blocks-filters/responsive-settings/components/BreakpointResetButton.tsx +94 -0
  15. package/core-js/blocks-filters/responsive-settings/components/DebugPanel.tsx +67 -0
  16. package/core-js/blocks-filters/responsive-settings/components/InheritedIndicator.tsx +32 -0
  17. package/core-js/blocks-filters/responsive-settings/components/LinkedValuesControl.tsx +55 -0
  18. package/core-js/blocks-filters/responsive-settings/components/ResponsiveSelectGroup.tsx +185 -0
  19. package/core-js/blocks-filters/responsive-settings/components/ResponsiveTabPanel.tsx +106 -0
  20. package/core-js/blocks-filters/responsive-settings/index.tsx +97 -148
  21. package/core-js/blocks-filters/responsive-settings/migration.ts +86 -0
  22. package/core-js/blocks-filters/responsive-settings/panels/BlockLinkPanel.tsx +38 -0
  23. package/core-js/blocks-filters/responsive-settings/panels/BorderPanel.tsx +61 -0
  24. package/core-js/blocks-filters/responsive-settings/panels/DisplayLayoutPanel.tsx +92 -0
  25. package/core-js/blocks-filters/responsive-settings/panels/SpacingPanel.tsx +63 -0
  26. package/core-js/blocks-filters/responsive-settings/panels/index.ts +4 -0
  27. package/core-js/blocks-filters/responsive-settings/registry.ts +88 -0
  28. package/core-js/blocks-filters/responsive-settings/settings/block-link/index.ts +3 -0
  29. package/core-js/blocks-filters/responsive-settings/settings/block-link/panel.tsx +6 -6
  30. package/core-js/blocks-filters/responsive-settings/settings/block-link/registration.ts +35 -0
  31. package/core-js/blocks-filters/responsive-settings/settings/border/border/properties.ts +1 -2
  32. package/core-js/blocks-filters/responsive-settings/settings/border/border/settings.tsx +21 -3
  33. package/core-js/blocks-filters/responsive-settings/settings/border/border-radius/index.tsx +2 -1
  34. package/core-js/blocks-filters/responsive-settings/settings/border/border-radius/properties.ts +6 -29
  35. package/core-js/blocks-filters/responsive-settings/settings/border/border-radius/settings.tsx +79 -6
  36. package/core-js/blocks-filters/responsive-settings/settings/border/index.ts +5 -1
  37. package/core-js/blocks-filters/responsive-settings/settings/border/panel.tsx +5 -54
  38. package/core-js/blocks-filters/responsive-settings/settings/border/registration.ts +84 -0
  39. package/core-js/blocks-filters/responsive-settings/settings/border/settings.tsx +21 -0
  40. package/core-js/blocks-filters/responsive-settings/settings/flex-item/index.ts +4 -0
  41. package/core-js/blocks-filters/responsive-settings/settings/flex-item/properties.ts +60 -0
  42. package/core-js/blocks-filters/responsive-settings/settings/flex-item/registration.ts +78 -0
  43. package/core-js/blocks-filters/responsive-settings/settings/flex-item/settings.tsx +90 -0
  44. package/core-js/blocks-filters/responsive-settings/settings/flexbox/index.ts +4 -0
  45. package/core-js/blocks-filters/responsive-settings/settings/flexbox/properties.ts +80 -0
  46. package/core-js/blocks-filters/responsive-settings/settings/flexbox/registration.ts +66 -0
  47. package/core-js/blocks-filters/responsive-settings/settings/flexbox/settings.tsx +78 -0
  48. package/core-js/blocks-filters/responsive-settings/settings/grid/index.ts +4 -0
  49. package/core-js/blocks-filters/responsive-settings/settings/grid/properties.ts +72 -0
  50. package/core-js/blocks-filters/responsive-settings/settings/grid/registration.ts +66 -0
  51. package/core-js/blocks-filters/responsive-settings/settings/grid/settings.tsx +78 -0
  52. package/core-js/blocks-filters/responsive-settings/settings/grid-item/index.ts +4 -0
  53. package/core-js/blocks-filters/responsive-settings/settings/grid-item/properties.ts +44 -0
  54. package/core-js/blocks-filters/responsive-settings/settings/grid-item/registration.ts +74 -0
  55. package/core-js/blocks-filters/responsive-settings/settings/grid-item/settings.tsx +87 -0
  56. package/core-js/blocks-filters/responsive-settings/settings/layout/index.ts +4 -0
  57. package/core-js/blocks-filters/responsive-settings/settings/layout/properties.ts +51 -0
  58. package/core-js/blocks-filters/responsive-settings/settings/layout/registration.ts +96 -0
  59. package/core-js/blocks-filters/responsive-settings/settings/layout/settings.tsx +64 -0
  60. package/core-js/blocks-filters/responsive-settings/settings/presets/index.ts +4 -0
  61. package/core-js/blocks-filters/responsive-settings/settings/presets/presets.ts +52 -0
  62. package/core-js/blocks-filters/responsive-settings/settings/presets/registration.ts +53 -0
  63. package/core-js/blocks-filters/responsive-settings/settings/presets/settings.tsx +100 -0
  64. package/core-js/blocks-filters/responsive-settings/settings/shared/gap-values.ts +16 -0
  65. package/core-js/blocks-filters/responsive-settings/settings/shared/layout-values.ts +56 -0
  66. package/core-js/blocks-filters/responsive-settings/settings/shared/tw-values.ts +107 -0
  67. package/core-js/blocks-filters/responsive-settings/settings/sizing/index.ts +4 -0
  68. package/core-js/blocks-filters/responsive-settings/settings/sizing/properties.ts +71 -0
  69. package/core-js/blocks-filters/responsive-settings/settings/sizing/registration.ts +52 -0
  70. package/core-js/blocks-filters/responsive-settings/settings/sizing/settings.tsx +96 -0
  71. package/core-js/blocks-filters/responsive-settings/settings/spacing/index.ts +7 -2
  72. package/core-js/blocks-filters/responsive-settings/settings/spacing/panel.tsx +5 -45
  73. package/core-js/blocks-filters/responsive-settings/settings/spacing/properties.ts +51 -29
  74. package/core-js/blocks-filters/responsive-settings/settings/spacing/registration.ts +53 -0
  75. package/core-js/blocks-filters/responsive-settings/settings/spacing/settings.tsx +26 -55
  76. package/core-js/blocks-filters/responsive-settings/types/index.ts +174 -28
  77. package/core-js/blocks-filters/responsive-settings/utils.ts +247 -216
  78. package/core-js/config/index.ts +6 -0
  79. package/core-js/config/webentor-config.ts +44 -2
  80. package/core-js/index.ts +8 -10
  81. package/core-js/types/index.ts +6 -0
  82. package/package.json +116 -6
  83. package/public/build/assets/_utils-CzK6Vfiv.js +2 -0
  84. package/public/build/assets/{_utils-PDaZ1Dn1.js.map → _utils-CzK6Vfiv.js.map} +1 -1
  85. package/public/build/assets/coreAppStyles-Bvp3emQy.css +1 -0
  86. package/public/build/assets/coreEditorJs-DYd3ZopL.js +366 -0
  87. package/public/build/assets/coreEditorJs-DYd3ZopL.js.map +1 -0
  88. package/public/build/assets/coreEditorStyles-BzlB6eA_.css +1 -0
  89. package/public/build/assets/resources/blocks/e-table/{script-BIchbcPK.js → script-C_Z50hjm.js} +2 -2
  90. package/public/build/assets/resources/blocks/e-table/{script-BIchbcPK.js.map → script-C_Z50hjm.js.map} +1 -1
  91. package/public/build/assets/{sliderJs-Ch69_tVA.js → sliderJs-CyGnrv0Q.js} +3 -3
  92. package/public/build/assets/{sliderJs-Ch69_tVA.js.map → sliderJs-CyGnrv0Q.js.map} +1 -1
  93. package/public/build/manifest.json +10 -10
  94. package/resources/blocks/e-accordion-group/block.json +6 -4
  95. package/resources/blocks/e-gallery/block.json +2 -2
  96. package/resources/blocks/e-gallery/e-gallery.block.tsx +4 -0
  97. package/resources/blocks/e-image/e-image.block.tsx +4 -0
  98. package/resources/blocks/e-slider/block.json +3 -2
  99. package/resources/blocks/e-tab-container/block.json +2 -1
  100. package/resources/blocks/e-tabs/block.json +2 -1
  101. package/resources/blocks/l-flexible-container/block.json +3 -2
  102. package/resources/blocks/l-mobile-nav/block.json +2 -1
  103. package/resources/blocks/l-nav-menu/block.json +2 -1
  104. package/resources/blocks/l-nav-menu/l-nav-menu.block.tsx +2 -0
  105. package/resources/blocks/l-section/block.json +7 -5
  106. package/resources/blocks/l-section/l-section.block.tsx +40 -31
  107. package/resources/scripts/editor.ts +2 -0
  108. package/resources/styles/common/_editor.css +22 -0
  109. package/resources/styles/common/_utilities.css +210 -0
  110. package/core-js/blocks-filters/responsive-settings/constants.ts +0 -11
  111. package/core-js/blocks-filters/responsive-settings/settings/container/display/index.ts +0 -2
  112. package/core-js/blocks-filters/responsive-settings/settings/container/display/properties.ts +0 -167
  113. package/core-js/blocks-filters/responsive-settings/settings/container/display/settings.tsx +0 -73
  114. package/core-js/blocks-filters/responsive-settings/settings/container/flexbox/index.ts +0 -2
  115. package/core-js/blocks-filters/responsive-settings/settings/container/flexbox/properties.ts +0 -187
  116. package/core-js/blocks-filters/responsive-settings/settings/container/flexbox/settings.tsx +0 -131
  117. package/core-js/blocks-filters/responsive-settings/settings/container/grid/index.ts +0 -2
  118. package/core-js/blocks-filters/responsive-settings/settings/container/grid/properties.ts +0 -187
  119. package/core-js/blocks-filters/responsive-settings/settings/container/grid/settings.tsx +0 -132
  120. package/core-js/blocks-filters/responsive-settings/settings/container/index.ts +0 -4
  121. package/core-js/blocks-filters/responsive-settings/settings/container/panel.tsx +0 -92
  122. package/public/build/assets/_utils-PDaZ1Dn1.js +0 -2
  123. package/public/build/assets/coreAppStyles-Dp0WYk4N.css +0 -1
  124. package/public/build/assets/coreEditorJs-Cyc87wTo.js +0 -366
  125. package/public/build/assets/coreEditorJs-Cyc87wTo.js.map +0 -1
  126. package/public/build/assets/coreEditorStyles-D8-nNpQG.css +0 -1
package/CHANGELOG.md CHANGED
@@ -1,6 +1,29 @@
1
1
  # Webentor Core Changelog
2
2
 
3
- ## DEV
3
+ ## 0.10.0
4
+
5
+ - Refactor responsive settings and improve UX
6
+ - **BREAKING**: Run keys migrator from `Settings -> Webentor Migrator`
7
+ - **BREAKING**: Attributes keys changed, so supports in `block.json` and other usage in theme needs to be checked and renamed:
8
+ - `flexboxItem` -> `flexItem`
9
+ - `display` -> `layout` (for `display`) and `sizing` (for `width`, `height`, `minWidth`, `minHeight`, `maxWidth`, `maxHeight`)
10
+ - Added reset buttons for breakpoints
11
+ - Better values grouping
12
+ - Better cascade breakpoints conditioning
13
+ - Spacing linking
14
+ - Add `webentor/block_wrapper_class_properties` filter to exclude generated class groups such as `backgroundColor` and `textColor` from filtered `classes_by_property` and wrapper output, while keeping the raw generated map available for `block_custom_classes`
15
+ - Add Wrap with Flexible container
16
+ - Add applied classes viewer to see which responsive classes are applied
17
+ - Add Quick layout presets, customizable with filter `webentor.core.responsiveSettings.layoutPresets`
18
+ - Fix PHP path in `init.php`
19
+ - Add package module exports, you can now use `@webikon/webentor-core` instead of `@webentorCore` when importing in the theme
20
+ - Add `webentor.core.l-section.output` filter to customize the section editor preview markup
21
+
22
+
23
+ ## 0.9.14
24
+
25
+ - Fix Button link display
26
+ - Disable auto sizes styles for images
4
27
 
5
28
  ## 0.9.13
6
29
 
package/README.md CHANGED
@@ -21,6 +21,47 @@ pnpm install
21
21
  pnpm build
22
22
  ```
23
23
 
24
+ ## JS Package Exports
25
+
26
+ This package exports raw TypeScript source so the consumer's Vite pipeline
27
+ compiles it together with the rest of the project. Consumer projects using
28
+ Vite 7+ must enable the TypeScript-aware config loader:
29
+
30
+ ```jsonc
31
+ // package.json scripts
32
+ "dev": "vite --configLoader runner",
33
+ "build": "vite build --configLoader runner"
34
+ ```
35
+
36
+ Available subpath imports:
37
+
38
+ ```ts
39
+ import { debounce } from '@webikon/webentor-core';
40
+ import { Alpine } from '@webikon/webentor-core/_alpine';
41
+ import { SliderComponent } from '@webikon/webentor-core/_slider';
42
+ import { WebentorButton } from '@webikon/webentor-core/blocks-components';
43
+ import { initResponsiveSettings } from '@webikon/webentor-core/blocks-filters';
44
+ import { buildSafelist, webentorDefaultConfig } from '@webikon/webentor-core/config';
45
+ import type { WebentorConfig } from '@webikon/webentor-core/types';
46
+ ```
47
+
48
+ ## WP-CLI
49
+
50
+ Use the responsive attributes migration command on an existing project when you
51
+ want to scan or migrate block content without opening the WordPress admin.
52
+
53
+ ```bash
54
+ wp webentor migrate-responsive-attributes
55
+ wp webentor migrate-responsive-attributes --migrate
56
+ wp webentor migrate-responsive-attributes --cleanup
57
+ wp webentor migrate-responsive-attributes --site-id=7 --cleanup
58
+ ```
59
+
60
+ By default the command only scans and reports how many posts and blocks still
61
+ need migration or cleanup. `--migrate` writes v2 attributes while keeping legacy
62
+ keys, and `--cleanup` removes the legacy keys after backfilling the v2 shape.
63
+ On multisite, `--site-id=<id>` runs the command against a specific site.
64
+
24
65
  ## Linting
25
66
 
26
67
  Core consumes shared presets from `@webikon/webentor-configs` to keep lint behavior aligned across repositories.
@@ -1,6 +1,12 @@
1
1
  import collapse from '@alpinejs/collapse';
2
2
  import Alpine from 'alpinejs';
3
3
 
4
+ declare global {
5
+ interface Window {
6
+ Alpine?: typeof Alpine;
7
+ }
8
+ }
9
+
4
10
  // Initialize Alpine if it's not already initialized
5
11
  const AlpineInstance = window.Alpine || Alpine;
6
12
  if (!window.Alpine) {
@@ -3,18 +3,21 @@ import Swiper from 'swiper/bundle';
3
3
 
4
4
  import { debounce, throttle } from './_utils';
5
5
 
6
- type BreakpointConfig = Swiper & {
6
+ type SliderParams = Record<string, any> & {
7
+ autoplayControl: boolean;
8
+ breakpoints: Record<string, BreakpointConfig>;
9
+ on?: Record<string, (...args: any[]) => void>;
10
+ };
11
+
12
+ type BreakpointConfig = Record<string, any> & {
7
13
  enabled: boolean;
8
14
  };
9
15
 
10
16
  class SliderComponent {
11
17
  private element: HTMLElement;
12
- private params: Swiper & {
13
- breakpoints: Record<string, BreakpointConfig>;
14
- autoplayControl: boolean;
15
- };
18
+ private params: SliderParams;
16
19
  private swiper: Swiper | null;
17
- private container: HTMLElement;
20
+ private container: HTMLElement | null;
18
21
  private breakpoints: Record<string, BreakpointConfig> | null;
19
22
  private autoplayButton: HTMLElement | null;
20
23
  private timerSeconds: HTMLElement | null;
@@ -108,10 +111,14 @@ class SliderComponent {
108
111
  }
109
112
  },
110
113
  autoplayTimeLeft: (swiper, time, progress) => {
111
- if (this.autoplayControlEnabled) {
114
+ if (
115
+ this.autoplayControlEnabled &&
116
+ this.timerCircle &&
117
+ this.timerSeconds
118
+ ) {
112
119
  this.timerCircle.style.setProperty(
113
120
  '--slider-timer-progress',
114
- 1 - progress,
121
+ String(1 - progress),
115
122
  );
116
123
  this.timerSeconds.textContent = `${Math.ceil(time / 1000)}s`;
117
124
  }
@@ -125,8 +132,12 @@ class SliderComponent {
125
132
  ...this.params.on,
126
133
  },
127
134
  navigation: {
128
- nextEl: this.element.querySelector('.js-slider-button-next'),
129
- prevEl: this.element.querySelector('.js-slider-button-prev'),
135
+ nextEl: this.element.querySelector(
136
+ '.js-slider-button-next',
137
+ ) as HTMLElement | null,
138
+ prevEl: this.element.querySelector(
139
+ '.js-slider-button-prev',
140
+ ) as HTMLElement | null,
130
141
  },
131
142
  });
132
143
 
@@ -174,7 +185,7 @@ class SliderComponent {
174
185
 
175
186
  if (!wrapper) return;
176
187
 
177
- const slides = Array.from(wrapper.children);
188
+ const slides = Array.from(wrapper.children) as HTMLElement[];
178
189
 
179
190
  if (slides.length === 0) return;
180
191
 
@@ -87,7 +87,20 @@ import { __ } from '@wordpress/i18n';
87
87
  }
88
88
  */
89
89
 
90
- export const WebentorButton = (props) => {
90
+ type WebentorButtonProps = {
91
+ attributeName: string;
92
+ attributes: Record<string, any>;
93
+ buttonClassName?: string;
94
+ className?: string;
95
+ hideLink?: boolean;
96
+ hideSize?: boolean;
97
+ hideVariant?: boolean;
98
+ innerClassName?: string;
99
+ placement?: string;
100
+ setAttributes: (attributes: Record<string, any>) => void;
101
+ };
102
+
103
+ export const WebentorButton = (props: WebentorButtonProps) => {
91
104
  const {
92
105
  placement,
93
106
  className,
@@ -199,7 +212,7 @@ export const WebentorButton = (props) => {
199
212
  </button>,
200
213
  props,
201
214
  buttonClassName,
202
- );
215
+ ) as React.ReactNode;
203
216
 
204
217
  return (
205
218
  <span className={`${className ?? ''} wbtr:relative wbtr:inline-block`}>
@@ -300,6 +313,7 @@ export const WebentorButton = (props) => {
300
313
  <SelectControl
301
314
  label={__('Button HTML Element', 'webentor')}
302
315
  value={attributes[attributeName]?.htmlElement}
316
+ __nextHasNoMarginBottom
303
317
  options={[
304
318
  { label: __('Link (<a>)', 'webentor'), value: 'a' },
305
319
  { label: __('Button (<button>)', 'webentor'), value: 'button' },
@@ -309,38 +323,40 @@ export const WebentorButton = (props) => {
309
323
  }
310
324
  />
311
325
 
312
- {!hideLink && attributes[attributeName]?.htmlElement === 'a' && (
313
- <>
314
- <div className="wbtr:mt-0 wbtr:mb-2 wbtr:text-[11px] wbtr:uppercase">
315
- {__('Button URL', 'webentor')}
316
- </div>
317
- <URLInput
318
- // label={__('Button URL', 'webentor')}
319
- value={
320
- attributes[attributeName] && attributes[attributeName]?.url
321
- ? attributes[attributeName]?.url
322
- : ''
323
- }
324
- onChange={(value) =>
325
- updateObjectAttribute(attributeName, 'url', value)
326
- }
327
- className="wbtr:mb-2"
328
- />
326
+ {!hideLink &&
327
+ attributes[attributeName]?.htmlElement != 'button' && (
328
+ <>
329
+ <div className="wbtr:mt-0 wbtr:mb-2 wbtr:text-[11px] wbtr:uppercase">
330
+ {__('Button URL', 'webentor')}
331
+ </div>
332
+ <URLInput
333
+ // label={__('Button URL', 'webentor')}
334
+ value={
335
+ attributes[attributeName] &&
336
+ attributes[attributeName]?.url
337
+ ? attributes[attributeName]?.url
338
+ : ''
339
+ }
340
+ onChange={(value) =>
341
+ updateObjectAttribute(attributeName, 'url', value)
342
+ }
343
+ className="wbtr:mb-2"
344
+ />
329
345
 
330
- <ToggleControl
331
- label={__('Open in new tab', 'webentor')}
332
- checked={
333
- attributes[attributeName] &&
334
- attributes[attributeName]?.newTab
335
- ? attributes[attributeName]?.newTab
336
- : false
337
- }
338
- onChange={(value) =>
339
- updateObjectAttribute(attributeName, 'newTab', value)
340
- }
341
- />
342
- </>
343
- )}
346
+ <ToggleControl
347
+ label={__('Open in new tab', 'webentor')}
348
+ checked={
349
+ attributes[attributeName] &&
350
+ attributes[attributeName]?.newTab
351
+ ? attributes[attributeName]?.newTab
352
+ : false
353
+ }
354
+ onChange={(value) =>
355
+ updateObjectAttribute(attributeName, 'newTab', value)
356
+ }
357
+ />
358
+ </>
359
+ )}
344
360
 
345
361
  <div className="wbtr:border wbtr:border-editor-border wbtr:p-2">
346
362
  <div className="wbtr:my-2">
@@ -376,6 +392,7 @@ export const WebentorButton = (props) => {
376
392
  <SelectControl
377
393
  label="Icon Position"
378
394
  value={attributes[attributeName]?.iconPosition}
395
+ __nextHasNoMarginBottom
379
396
  options={iconPositions}
380
397
  onChange={(value) =>
381
398
  updateObjectAttribute(
@@ -43,7 +43,9 @@ export const CustomImageSizesPanel: React.FC<Props> = (props: Props) => {
43
43
  noticeAfter,
44
44
  } = props;
45
45
 
46
- const breakpoints = applyFilters('webentor.core.twBreakpoints', ['basic']);
46
+ const breakpoints = applyFilters('webentor.core.twBreakpoints', [
47
+ 'basic',
48
+ ]) as string[];
47
49
 
48
50
  const hasSizeSettingsForBreakpoint = (attributes, breakpoint) => {
49
51
  return (
@@ -1,7 +1,22 @@
1
1
  import { CustomSelectControl } from '@wordpress/components';
2
2
  import { __, sprintf } from '@wordpress/i18n';
3
3
 
4
- export const WebentorTypographyPickerSelect = (props) => {
4
+ type TypographyOption = {
5
+ key: string;
6
+ name: string;
7
+ value: string;
8
+ };
9
+
10
+ type WebentorTypographyPickerSelectProps = {
11
+ __next40pxDefaultSize?: boolean;
12
+ onChange: (value: string) => void;
13
+ options: TypographyOption[];
14
+ value?: string;
15
+ };
16
+
17
+ export const WebentorTypographyPickerSelect = (
18
+ props: WebentorTypographyPickerSelectProps,
19
+ ) => {
5
20
  const { __next40pxDefaultSize, value, onChange, options } = props;
6
21
 
7
22
  const selectedOption = value
@@ -13,6 +13,13 @@ const BLOCKS = [
13
13
  'core/post-title',
14
14
  ];
15
15
 
16
+ type TypographyOption = {
17
+ __experimentalHint?: string;
18
+ key: string;
19
+ name: string;
20
+ value: string;
21
+ };
22
+
16
23
  /**
17
24
  * BlockEdit
18
25
  *
@@ -27,7 +34,10 @@ const BLOCKS = [
27
34
  function BlockEdit(props) {
28
35
  const { customTypography } = props.attributes;
29
36
 
30
- const options = applyFilters('webentor.core.customTypographyKeys', []);
37
+ const options = applyFilters(
38
+ 'webentor.core.customTypographyKeys',
39
+ [],
40
+ ) as TypographyOption[];
31
41
 
32
42
  return (
33
43
  <InspectorControls group="styles">
@@ -23,7 +23,7 @@ const initSliderSettings = () => {
23
23
 
24
24
  const breakpoints = applyFilters('webentor.core.twBreakpoints', [
25
25
  'basic',
26
- ]);
26
+ ]) as string[];
27
27
 
28
28
  const { attributes, setAttributes } = props;
29
29
 
@@ -0,0 +1,104 @@
1
+ import {
2
+ BlockControls,
3
+ store as blockEditorStore,
4
+ } from '@wordpress/block-editor';
5
+ import { cloneBlock, createBlock } from '@wordpress/blocks';
6
+ import { ToolbarButton, ToolbarGroup } from '@wordpress/components';
7
+ import { createHigherOrderComponent } from '@wordpress/compose';
8
+ import { select as dataSelect, useDispatch, useSelect } from '@wordpress/data';
9
+ import { Fragment } from '@wordpress/element';
10
+ import { addFilter, applyFilters } from '@wordpress/hooks';
11
+ import { __ } from '@wordpress/i18n';
12
+ import { group as groupIcon } from '@wordpress/icons';
13
+
14
+ const withWrapContainerButton = createHigherOrderComponent((BlockEdit) => {
15
+ return (props) => {
16
+ const { clientId, name } = props;
17
+
18
+ // Allow themes to exclude specific blocks via filter
19
+ const excludedBlocks: string[] = applyFilters(
20
+ 'webentor.core.wrapWithContainer.excludedBlocks',
21
+ ['webentor/l-section'],
22
+ );
23
+
24
+ if (excludedBlocks.includes(name)) {
25
+ return <BlockEdit {...props} />;
26
+ }
27
+
28
+ const { replaceBlocks } = useDispatch(blockEditorStore);
29
+
30
+ const { isMultiSelected, isFirstInMultiSelection } = useSelect(
31
+ (select) => {
32
+ const store = select(blockEditorStore);
33
+ const multiIds = store.getMultiSelectedBlockClientIds();
34
+ const isMulti = multiIds.length > 0;
35
+
36
+ return {
37
+ isMultiSelected: isMulti,
38
+ isFirstInMultiSelection: isMulti && multiIds[0] === clientId,
39
+ };
40
+ },
41
+ [clientId],
42
+ );
43
+
44
+ // During multi-selection, only render the button on the first selected block
45
+ if (isMultiSelected && !isFirstInMultiSelection) {
46
+ return <BlockEdit {...props} />;
47
+ }
48
+
49
+ const handleWrap = () => {
50
+ const store = dataSelect(blockEditorStore);
51
+ const allSelectedClientIds = store.getMultiSelectedBlockClientIds();
52
+
53
+ // Build the selected block list on demand so useSelect only returns stable values.
54
+ const allSelectedBlocks = allSelectedClientIds
55
+ .map((selectedClientId) => store.getBlock(selectedClientId))
56
+ .filter(Boolean);
57
+
58
+ if (allSelectedBlocks.length > 0) {
59
+ const clonedBlocks = allSelectedBlocks.map((b) => cloneBlock(b));
60
+ const containerBlock = createBlock(
61
+ 'webentor/l-flexible-container',
62
+ {},
63
+ clonedBlocks,
64
+ );
65
+ replaceBlocks(allSelectedClientIds, [containerBlock]);
66
+ } else {
67
+ const currentBlock = store.getBlock(clientId);
68
+
69
+ if (!currentBlock) return;
70
+
71
+ const clonedBlock = cloneBlock(currentBlock);
72
+ const containerBlock = createBlock(
73
+ 'webentor/l-flexible-container',
74
+ {},
75
+ [clonedBlock],
76
+ );
77
+ replaceBlocks(clientId, [containerBlock]);
78
+ }
79
+ };
80
+
81
+ return (
82
+ <Fragment>
83
+ <BlockControls group="other">
84
+ <ToolbarGroup>
85
+ <ToolbarButton
86
+ icon={groupIcon}
87
+ label={__('Wrap with Flexible Container', 'webentor')}
88
+ onClick={handleWrap}
89
+ />
90
+ </ToolbarGroup>
91
+ </BlockControls>
92
+
93
+ <BlockEdit {...props} />
94
+ </Fragment>
95
+ );
96
+ };
97
+ }, 'withWrapContainerButton');
98
+
99
+ // Self-register -- imported from core editor.ts, no manual init needed
100
+ addFilter(
101
+ 'editor.BlockEdit',
102
+ 'webentor/blockEdit/wrapWithContainer',
103
+ withWrapContainerButton,
104
+ );