@webikon/webentor-core 0.9.14 → 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.
- package/CHANGELOG.md +20 -0
- package/README.md +41 -0
- package/core-js/_alpine.ts +6 -0
- package/core-js/_slider.ts +22 -11
- package/core-js/blocks-components/button.tsx +17 -2
- package/core-js/blocks-components/custom-image-sizes-panel.tsx +3 -1
- package/core-js/blocks-components/typography-picker-select.tsx +16 -1
- package/core-js/blocks-filters/_filter-core-typography.tsx +11 -1
- package/core-js/blocks-filters/_slider-settings.tsx +1 -1
- package/core-js/blocks-filters/_wrap-with-container.tsx +104 -0
- package/core-js/blocks-filters/responsive-settings/AGENTS.md +255 -0
- package/core-js/blocks-filters/responsive-settings/components/AppliedClassesViewer.tsx +189 -0
- package/core-js/blocks-filters/responsive-settings/components/BoxModelControl.tsx +346 -0
- package/core-js/blocks-filters/responsive-settings/components/BreakpointResetButton.tsx +94 -0
- package/core-js/blocks-filters/responsive-settings/components/DebugPanel.tsx +67 -0
- package/core-js/blocks-filters/responsive-settings/components/InheritedIndicator.tsx +32 -0
- package/core-js/blocks-filters/responsive-settings/components/LinkedValuesControl.tsx +55 -0
- package/core-js/blocks-filters/responsive-settings/components/ResponsiveSelectGroup.tsx +185 -0
- package/core-js/blocks-filters/responsive-settings/components/ResponsiveTabPanel.tsx +106 -0
- package/core-js/blocks-filters/responsive-settings/index.tsx +97 -148
- package/core-js/blocks-filters/responsive-settings/migration.ts +86 -0
- package/core-js/blocks-filters/responsive-settings/panels/BlockLinkPanel.tsx +38 -0
- package/core-js/blocks-filters/responsive-settings/panels/BorderPanel.tsx +61 -0
- package/core-js/blocks-filters/responsive-settings/panels/DisplayLayoutPanel.tsx +92 -0
- package/core-js/blocks-filters/responsive-settings/panels/SpacingPanel.tsx +63 -0
- package/core-js/blocks-filters/responsive-settings/panels/index.ts +4 -0
- package/core-js/blocks-filters/responsive-settings/registry.ts +88 -0
- package/core-js/blocks-filters/responsive-settings/settings/block-link/index.ts +3 -0
- package/core-js/blocks-filters/responsive-settings/settings/block-link/panel.tsx +6 -6
- package/core-js/blocks-filters/responsive-settings/settings/block-link/registration.ts +35 -0
- package/core-js/blocks-filters/responsive-settings/settings/border/border/properties.ts +1 -2
- package/core-js/blocks-filters/responsive-settings/settings/border/border/settings.tsx +21 -3
- package/core-js/blocks-filters/responsive-settings/settings/border/border-radius/index.tsx +2 -1
- package/core-js/blocks-filters/responsive-settings/settings/border/border-radius/properties.ts +6 -29
- package/core-js/blocks-filters/responsive-settings/settings/border/border-radius/settings.tsx +79 -6
- package/core-js/blocks-filters/responsive-settings/settings/border/index.ts +5 -1
- package/core-js/blocks-filters/responsive-settings/settings/border/panel.tsx +5 -54
- package/core-js/blocks-filters/responsive-settings/settings/border/registration.ts +84 -0
- package/core-js/blocks-filters/responsive-settings/settings/border/settings.tsx +21 -0
- package/core-js/blocks-filters/responsive-settings/settings/flex-item/index.ts +4 -0
- package/core-js/blocks-filters/responsive-settings/settings/flex-item/properties.ts +60 -0
- package/core-js/blocks-filters/responsive-settings/settings/flex-item/registration.ts +78 -0
- package/core-js/blocks-filters/responsive-settings/settings/flex-item/settings.tsx +90 -0
- package/core-js/blocks-filters/responsive-settings/settings/flexbox/index.ts +4 -0
- package/core-js/blocks-filters/responsive-settings/settings/flexbox/properties.ts +80 -0
- package/core-js/blocks-filters/responsive-settings/settings/flexbox/registration.ts +66 -0
- package/core-js/blocks-filters/responsive-settings/settings/flexbox/settings.tsx +78 -0
- package/core-js/blocks-filters/responsive-settings/settings/grid/index.ts +4 -0
- package/core-js/blocks-filters/responsive-settings/settings/grid/properties.ts +72 -0
- package/core-js/blocks-filters/responsive-settings/settings/grid/registration.ts +66 -0
- package/core-js/blocks-filters/responsive-settings/settings/grid/settings.tsx +78 -0
- package/core-js/blocks-filters/responsive-settings/settings/grid-item/index.ts +4 -0
- package/core-js/blocks-filters/responsive-settings/settings/grid-item/properties.ts +44 -0
- package/core-js/blocks-filters/responsive-settings/settings/grid-item/registration.ts +74 -0
- package/core-js/blocks-filters/responsive-settings/settings/grid-item/settings.tsx +87 -0
- package/core-js/blocks-filters/responsive-settings/settings/layout/index.ts +4 -0
- package/core-js/blocks-filters/responsive-settings/settings/layout/properties.ts +51 -0
- package/core-js/blocks-filters/responsive-settings/settings/layout/registration.ts +96 -0
- package/core-js/blocks-filters/responsive-settings/settings/layout/settings.tsx +64 -0
- package/core-js/blocks-filters/responsive-settings/settings/presets/index.ts +4 -0
- package/core-js/blocks-filters/responsive-settings/settings/presets/presets.ts +52 -0
- package/core-js/blocks-filters/responsive-settings/settings/presets/registration.ts +53 -0
- package/core-js/blocks-filters/responsive-settings/settings/presets/settings.tsx +100 -0
- package/core-js/blocks-filters/responsive-settings/settings/shared/gap-values.ts +16 -0
- package/core-js/blocks-filters/responsive-settings/settings/shared/layout-values.ts +56 -0
- package/core-js/blocks-filters/responsive-settings/settings/shared/tw-values.ts +107 -0
- package/core-js/blocks-filters/responsive-settings/settings/sizing/index.ts +4 -0
- package/core-js/blocks-filters/responsive-settings/settings/sizing/properties.ts +71 -0
- package/core-js/blocks-filters/responsive-settings/settings/sizing/registration.ts +52 -0
- package/core-js/blocks-filters/responsive-settings/settings/sizing/settings.tsx +96 -0
- package/core-js/blocks-filters/responsive-settings/settings/spacing/index.ts +7 -2
- package/core-js/blocks-filters/responsive-settings/settings/spacing/panel.tsx +5 -45
- package/core-js/blocks-filters/responsive-settings/settings/spacing/properties.ts +51 -29
- package/core-js/blocks-filters/responsive-settings/settings/spacing/registration.ts +53 -0
- package/core-js/blocks-filters/responsive-settings/settings/spacing/settings.tsx +26 -55
- package/core-js/blocks-filters/responsive-settings/types/index.ts +174 -28
- package/core-js/blocks-filters/responsive-settings/utils.ts +247 -216
- package/core-js/config/index.ts +6 -0
- package/core-js/config/webentor-config.ts +44 -2
- package/core-js/index.ts +8 -10
- package/core-js/types/index.ts +6 -0
- package/package.json +116 -6
- package/public/build/assets/_utils-CzK6Vfiv.js +2 -0
- package/public/build/assets/{_utils-PDaZ1Dn1.js.map → _utils-CzK6Vfiv.js.map} +1 -1
- package/public/build/assets/coreAppStyles-Bvp3emQy.css +1 -0
- package/public/build/assets/coreEditorJs-DYd3ZopL.js +366 -0
- package/public/build/assets/coreEditorJs-DYd3ZopL.js.map +1 -0
- package/public/build/assets/coreEditorStyles-BzlB6eA_.css +1 -0
- package/public/build/assets/resources/blocks/e-table/{script-BIchbcPK.js → script-C_Z50hjm.js} +2 -2
- package/public/build/assets/resources/blocks/e-table/{script-BIchbcPK.js.map → script-C_Z50hjm.js.map} +1 -1
- package/public/build/assets/{sliderJs-Ch69_tVA.js → sliderJs-CyGnrv0Q.js} +3 -3
- package/public/build/assets/{sliderJs-Ch69_tVA.js.map → sliderJs-CyGnrv0Q.js.map} +1 -1
- package/public/build/manifest.json +10 -10
- package/resources/blocks/e-accordion-group/block.json +6 -4
- package/resources/blocks/e-gallery/block.json +2 -2
- package/resources/blocks/e-gallery/e-gallery.block.tsx +4 -0
- package/resources/blocks/e-image/e-image.block.tsx +4 -0
- package/resources/blocks/e-slider/block.json +3 -2
- package/resources/blocks/e-tab-container/block.json +2 -1
- package/resources/blocks/e-tabs/block.json +2 -1
- package/resources/blocks/l-flexible-container/block.json +3 -2
- package/resources/blocks/l-mobile-nav/block.json +2 -1
- package/resources/blocks/l-nav-menu/block.json +2 -1
- package/resources/blocks/l-nav-menu/l-nav-menu.block.tsx +2 -0
- package/resources/blocks/l-section/block.json +7 -5
- package/resources/blocks/l-section/l-section.block.tsx +40 -31
- package/resources/scripts/editor.ts +2 -0
- package/resources/styles/common/_editor.css +22 -0
- package/resources/styles/common/_utilities.css +210 -0
- package/core-js/blocks-filters/responsive-settings/constants.ts +0 -11
- package/core-js/blocks-filters/responsive-settings/settings/container/display/index.ts +0 -2
- package/core-js/blocks-filters/responsive-settings/settings/container/display/properties.ts +0 -167
- package/core-js/blocks-filters/responsive-settings/settings/container/display/settings.tsx +0 -73
- package/core-js/blocks-filters/responsive-settings/settings/container/flexbox/index.ts +0 -2
- package/core-js/blocks-filters/responsive-settings/settings/container/flexbox/properties.ts +0 -187
- package/core-js/blocks-filters/responsive-settings/settings/container/flexbox/settings.tsx +0 -131
- package/core-js/blocks-filters/responsive-settings/settings/container/grid/index.ts +0 -2
- package/core-js/blocks-filters/responsive-settings/settings/container/grid/properties.ts +0 -187
- package/core-js/blocks-filters/responsive-settings/settings/container/grid/settings.tsx +0 -132
- package/core-js/blocks-filters/responsive-settings/settings/container/index.ts +0 -4
- package/core-js/blocks-filters/responsive-settings/settings/container/panel.tsx +0 -92
- package/public/build/assets/_utils-PDaZ1Dn1.js +0 -2
- package/public/build/assets/coreAppStyles-Dp0WYk4N.css +0 -1
- package/public/build/assets/coreEditorJs-Cyc87wTo.js +0 -366
- package/public/build/assets/coreEditorJs-Cyc87wTo.js.map +0 -1
- package/public/build/assets/coreEditorStyles-D8-nNpQG.css +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Webentor Core Changelog
|
|
2
2
|
|
|
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
|
+
|
|
3
23
|
## 0.9.14
|
|
4
24
|
|
|
5
25
|
- Fix Button link display
|
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.
|
package/core-js/_alpine.ts
CHANGED
|
@@ -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) {
|
package/core-js/_slider.ts
CHANGED
|
@@ -3,18 +3,21 @@ import Swiper from 'swiper/bundle';
|
|
|
3
3
|
|
|
4
4
|
import { debounce, throttle } from './_utils';
|
|
5
5
|
|
|
6
|
-
type
|
|
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:
|
|
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 (
|
|
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(
|
|
129
|
-
|
|
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
|
-
|
|
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' },
|
|
@@ -378,6 +392,7 @@ export const WebentorButton = (props) => {
|
|
|
378
392
|
<SelectControl
|
|
379
393
|
label="Icon Position"
|
|
380
394
|
value={attributes[attributeName]?.iconPosition}
|
|
395
|
+
__nextHasNoMarginBottom
|
|
381
396
|
options={iconPositions}
|
|
382
397
|
onChange={(value) =>
|
|
383
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', [
|
|
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
|
-
|
|
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(
|
|
37
|
+
const options = applyFilters(
|
|
38
|
+
'webentor.core.customTypographyKeys',
|
|
39
|
+
[],
|
|
40
|
+
) as TypographyOption[];
|
|
31
41
|
|
|
32
42
|
return (
|
|
33
43
|
<InspectorControls group="styles">
|
|
@@ -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
|
+
);
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
# Responsive Settings — AI Guide
|
|
2
|
+
|
|
3
|
+
This file documents the architecture, data flow, and conventions of the
|
|
4
|
+
responsive settings system so AI agents can work on it without re-exploring.
|
|
5
|
+
|
|
6
|
+
## Purpose
|
|
7
|
+
|
|
8
|
+
Provides per-breakpoint (responsive) block controls in the WordPress editor
|
|
9
|
+
sidebar. Users can configure spacing, display mode, sizing, flexbox, grid,
|
|
10
|
+
flex/grid item behaviour, borders, and presets — all with breakpoint tabs
|
|
11
|
+
(`basic`, `sm`, `md`, `lg`, `xl`, `2xl`). Values are stored as Tailwind
|
|
12
|
+
class names and output as CSS classes on the block wrapper.
|
|
13
|
+
|
|
14
|
+
## File Structure
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
responsive-settings/
|
|
18
|
+
index.tsx — Entry point: attribute filter, registerBlockExtension, BlockEdit
|
|
19
|
+
registry.ts — SettingsRegistry singleton (Map-based, panelGroup queries)
|
|
20
|
+
migration.ts — Display value helpers + display-specific cascade helpers
|
|
21
|
+
utils.ts — Class generation orchestrator + border preview helpers + generic breakpoint cascade utilities
|
|
22
|
+
constants.ts — Legacy includedBlocks map (currently empty, kept for fallback)
|
|
23
|
+
types/index.ts — All TypeScript interfaces (SettingDefinition, PanelGroup, BlockAttributes, etc.)
|
|
24
|
+
|
|
25
|
+
panels/ — Thin PanelBody wrappers, one per UI panel
|
|
26
|
+
SpacingPanel.tsx — panelGroup='spacing'
|
|
27
|
+
DisplayLayoutPanel.tsx — panelGroup='displayLayout' (presets rendered above tabs)
|
|
28
|
+
BorderPanel.tsx — panelGroup='border'
|
|
29
|
+
BlockLinkPanel.tsx — panelGroup='blockLink' (standalone, no breakpoint tabs)
|
|
30
|
+
index.ts
|
|
31
|
+
|
|
32
|
+
components/ — Shared UI components
|
|
33
|
+
ResponsiveTabPanel.tsx — Breakpoint tab wrapper with active-settings indicator
|
|
34
|
+
ResponsiveSelectGroup.tsx — Generic SelectControl list renderer (+ optgroup support)
|
|
35
|
+
BreakpointResetButton.tsx — Per-breakpoint "Reset" button inside tabs
|
|
36
|
+
DebugPanel.tsx — JSON attribute inspector (gated by window flag)
|
|
37
|
+
BoxModelControl.tsx — Margin/padding box-model layout with link modes
|
|
38
|
+
LinkedValuesControl.tsx — Link/unlink toggle + reset button
|
|
39
|
+
DisabledSliderInfo.tsx — Info message when slider overrides settings
|
|
40
|
+
InheritedIndicator.tsx — "Inherited from {breakpoint}" label for cascaded settings
|
|
41
|
+
|
|
42
|
+
settings/
|
|
43
|
+
presets/ — Quick layout presets (panelGroup: displayLayout, order: 0)
|
|
44
|
+
layout/ — Display mode: flex/grid/block/hidden (panelGroup: displayLayout, order: 10)
|
|
45
|
+
sizing/ — Width/height/min/max dimensions (panelGroup: displayLayout, order: 20)
|
|
46
|
+
flexbox/ — Flexbox container controls (panelGroup: displayLayout, order: 30)
|
|
47
|
+
grid/ — Grid container controls (panelGroup: displayLayout, order: 40)
|
|
48
|
+
flex-item/ — Flex child controls (panelGroup: displayLayout, order: 50)
|
|
49
|
+
grid-item/ — Grid child controls (panelGroup: displayLayout, order: 60)
|
|
50
|
+
spacing/ — Margin/padding (panelGroup: spacing, order: 10)
|
|
51
|
+
border/ — Border + border-radius (panelGroup: border, order: 10)
|
|
52
|
+
block-link/ — Block link (panelGroup: blockLink, order: 100)
|
|
53
|
+
shared/ — Shared value generators (gap-values, layout-values, tw-values)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Architecture
|
|
57
|
+
|
|
58
|
+
### Two-Layer Pattern: Panel Groups + Setting Modules
|
|
59
|
+
|
|
60
|
+
The UI has **4 collapsible panels** (SpacingPanel, DisplayLayoutPanel, BorderPanel, BlockLinkPanel).
|
|
61
|
+
Internally, the code is modular: each setting module is self-contained and
|
|
62
|
+
declares which `panelGroup` it belongs to.
|
|
63
|
+
|
|
64
|
+
Panel components are thin wrappers: they render a `PanelBody` with
|
|
65
|
+
`ResponsiveTabPanel` tabs, query the registry for all modules in their
|
|
66
|
+
`panelGroup` (sorted by `order`), and render each module's `SettingsComponent`.
|
|
67
|
+
|
|
68
|
+
### SettingsRegistry (`registry.ts`)
|
|
69
|
+
|
|
70
|
+
Singleton `Map<string, SettingDefinition>`. Modules self-register via
|
|
71
|
+
side-effect imports in their `registration.ts` files.
|
|
72
|
+
|
|
73
|
+
Key methods:
|
|
74
|
+
- `register(def)` — add a setting module
|
|
75
|
+
- `getAll()` — all modules sorted by order
|
|
76
|
+
- `getByPanelGroup(group)` — modules for a specific panel
|
|
77
|
+
- `isSupported(supports, def)` — check if a block supports a setting
|
|
78
|
+
|
|
79
|
+
### SettingDefinition Interface
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
{
|
|
83
|
+
name: string; // e.g. 'layout', 'sizing', 'flexbox'
|
|
84
|
+
panelGroup: PanelGroup; // 'spacing' | 'displayLayout' | 'border'
|
|
85
|
+
order: number; // render order within panel (lower = higher)
|
|
86
|
+
attributeKey: string; // WP attribute key (e.g. 'layout', 'spacing')
|
|
87
|
+
supportKey: string | string[]; // webentor.* support flag(s)
|
|
88
|
+
attributeSchema: object; // WP attribute schema
|
|
89
|
+
initAttributes?: Function; // custom attribute defaults (e.g. flex default)
|
|
90
|
+
SettingsComponent: React.FC; // renders inline within the panel
|
|
91
|
+
generateClasses: Function; // Tailwind class array per breakpoint
|
|
92
|
+
hasActiveSettings: Function; // tab indicator (breakpoint has values?)
|
|
93
|
+
migrateFromV1?: Function; // optional per-module migration
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Data Flow
|
|
98
|
+
|
|
99
|
+
1. **Attribute injection** (`blocks.registerBlockType` filter in `index.tsx`):
|
|
100
|
+
- Iterates all registered modules
|
|
101
|
+
- Checks `supports.webentor.*` against each module's `supportKey`
|
|
102
|
+
- Merges attribute schemas into the block
|
|
103
|
+
- Runs `initAttributes` for defaults (e.g. `layout.display = 'flex'`)
|
|
104
|
+
|
|
105
|
+
2. **Editor rendering** (`BlockEdit` in `index.tsx`):
|
|
106
|
+
- Renders SpacingPanel, DisplayLayoutPanel, BorderPanel, BlockLinkPanel
|
|
107
|
+
- Each panel queries registry and renders SettingsComponents
|
|
108
|
+
|
|
109
|
+
3. **Class generation** (`generateClassNames` in `utils.ts`):
|
|
110
|
+
- Called by `registerBlockExtension` classNameGenerator hook
|
|
111
|
+
- Collects breakpoints from all attribute values
|
|
112
|
+
- Calls each module's `generateClasses(attributes, breakpoint, context)` per breakpoint
|
|
113
|
+
- Concatenates results
|
|
114
|
+
|
|
115
|
+
4. **PHP class generation** (`blocks-settings.php`):
|
|
116
|
+
- `SettingsRegistry::generateClasses()` iterates registered handlers
|
|
117
|
+
- Each handler reads attribute values and generates Tailwind classes
|
|
118
|
+
- `prepareBlockClassesFromSettings()` orchestrates all handlers
|
|
119
|
+
|
|
120
|
+
## Attribute Shape
|
|
121
|
+
|
|
122
|
+
All responsive values follow the same pattern:
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
attributes.{attributeKey}.{propertyName}.value.{breakpoint} = "tailwind-class"
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Example:
|
|
129
|
+
```json
|
|
130
|
+
{
|
|
131
|
+
"layout": { "display": { "value": { "basic": "flex", "md": "grid" } } },
|
|
132
|
+
"spacing": { "margin-top": { "value": { "basic": "mt-4", "lg": "mt-8" } } },
|
|
133
|
+
"border": { "border": { "value": { "basic": { "top": { "width": "1", "style": "solid", "color": "black" }, "linked": true } } } }
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Migration
|
|
138
|
+
|
|
139
|
+
- Runtime code only reads canonical v2 keys
|
|
140
|
+
- PHP migration lives in `app/blocks-migration.php`
|
|
141
|
+
- `getDisplayValue()` / `getParentDisplayValue()` are the canonical display readers for `layout.display`
|
|
142
|
+
|
|
143
|
+
## PHP Side (`app/blocks-settings.php`)
|
|
144
|
+
|
|
145
|
+
- `SettingsRegistry` class mirrors the JS pattern
|
|
146
|
+
- `get_display_value_for_breakpoint()` helper for explicit display reads
|
|
147
|
+
- `get_effective_display_value_for_breakpoint()` cascaded display (min-width inheritance)
|
|
148
|
+
- `get_effective_parent_display_value_for_breakpoint()` cascaded parent display
|
|
149
|
+
- Handlers: `prepareLayoutBlockClassesFromSettings`, `prepareSizingBlockClassesFromSettings`,
|
|
150
|
+
`prepareFlexItemBlockClassesFromSettings` (new), plus unchanged handlers for spacing,
|
|
151
|
+
grid, gridItem, flexbox, border
|
|
152
|
+
- `prepareBlockClassesFromSettings()` also outputs `_presetClasses` directly
|
|
153
|
+
|
|
154
|
+
## Presets
|
|
155
|
+
|
|
156
|
+
Defined in `settings/presets/presets.ts` as `LayoutPreset[]`. Each preset
|
|
157
|
+
specifies `applies` (attribute values per module) and optional `customClasses`
|
|
158
|
+
for edge cases that need non-Tailwind CSS (e.g. flex-wrap + gap + equal columns).
|
|
159
|
+
|
|
160
|
+
Selecting a preset fills in the individual settings (which remain editable)
|
|
161
|
+
and stores `_preset` (ID marker) and `_presetClasses` (custom CSS classes).
|
|
162
|
+
|
|
163
|
+
Custom CSS utilities for presets live in `resources/styles/common/_utilities.css`:
|
|
164
|
+
- `.w-flex-cols` — flex container with wrapping
|
|
165
|
+
- `.w-flex-cols-{2-6}` — sets child width via `calc()` accounting for gap
|
|
166
|
+
- `.w-gap-{0-12}` — gap + `--w-col-gap` CSS var
|
|
167
|
+
|
|
168
|
+
## Block.json Support Keys
|
|
169
|
+
|
|
170
|
+
### Supported Keys
|
|
171
|
+
|
|
172
|
+
```json
|
|
173
|
+
"webentor": {
|
|
174
|
+
"spacing": true,
|
|
175
|
+
"layout": true,
|
|
176
|
+
"sizing": true,
|
|
177
|
+
"grid": true,
|
|
178
|
+
"gridItem": true,
|
|
179
|
+
"flexbox": true,
|
|
180
|
+
"flexItem": true,
|
|
181
|
+
"border": true,
|
|
182
|
+
"borderRadius": true,
|
|
183
|
+
"blockLink": true,
|
|
184
|
+
"presets": true
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## JSON Schema
|
|
189
|
+
|
|
190
|
+
`schemas/webentor-block.json` defines the canonical support keys under `supports.webentor`.
|
|
191
|
+
|
|
192
|
+
## Contextual Rendering Rules
|
|
193
|
+
|
|
194
|
+
Display checks use **breakpoint cascading** (min-width inheritance): if `display=flex`
|
|
195
|
+
is set at `basic`, flexbox settings are visible at `sm`, `md`, etc. even without an
|
|
196
|
+
explicit value, because the effective display cascades from `basic`. An
|
|
197
|
+
`InheritedIndicator` label is shown when the value is inherited.
|
|
198
|
+
|
|
199
|
+
- **Flexbox settings**: show when **effective** `display=flex` at the active breakpoint
|
|
200
|
+
- **Grid settings**: show when **effective** `display=grid` at the active breakpoint
|
|
201
|
+
- **Flex-item settings**: show when **parent** block's **effective** `display=flex`
|
|
202
|
+
- **Grid-item settings**: show when **parent** block's **effective** `display=grid`
|
|
203
|
+
- **Slider override**: when `slider.enabled=true` at a breakpoint, display/flexbox/spacing settings are disabled
|
|
204
|
+
|
|
205
|
+
### Generic Cascade Functions (`utils.ts`)
|
|
206
|
+
|
|
207
|
+
| Function | Purpose |
|
|
208
|
+
|----------|---------|
|
|
209
|
+
| `getEffectiveValue(attrs, attrKey, prop, bp, bps)` | Generic cascade for any string attribute property |
|
|
210
|
+
| `getInheritedFromBreakpoint(attrs, attrKey, prop, bp, bps)` | Source breakpoint for inheritance (UI indicator) |
|
|
211
|
+
| `getEffectiveObjectValue(attrs, attrKey, prop, bp, bps)` | Cascade for object-typed values (borders, radius) |
|
|
212
|
+
| `getObjectInheritedFromBreakpoint(attrs, attrKey, prop, bp, bps)` | Source breakpoint for inherited object values |
|
|
213
|
+
|
|
214
|
+
### Display Cascade Functions (`migration.ts`)
|
|
215
|
+
|
|
216
|
+
| Function | Purpose |
|
|
217
|
+
|----------|---------|
|
|
218
|
+
| `getEffectiveDisplayValue(attrs, bp, bps)` | Cascaded display mode |
|
|
219
|
+
| `getEffectiveParentDisplayValue(parentAttrs, bp, bps)` | Cascaded parent display |
|
|
220
|
+
| `getDisplayInheritedFromBreakpoint(attrs, bp, bps)` | Display-specific inheritance source |
|
|
221
|
+
|
|
222
|
+
PHP equivalents in `blocks-settings.php`:
|
|
223
|
+
- `get_effective_display_value_for_breakpoint($attributes, $breakpoint)`
|
|
224
|
+
- `get_effective_parent_display_value_for_breakpoint($parent_block, $breakpoint)`
|
|
225
|
+
|
|
226
|
+
### Per-Property Cascade Indicators
|
|
227
|
+
|
|
228
|
+
Every property control shows inherited values from lower breakpoints:
|
|
229
|
+
|
|
230
|
+
- **`ResponsiveSelectGroup`** — when a select has no explicit value but an inherited
|
|
231
|
+
value exists, the placeholder changes from "None selected" to e.g. "Flex Row (from basic)"
|
|
232
|
+
and the select is styled with `.wbtr-inherited-value` (italic, muted color).
|
|
233
|
+
- **`BoxModelControl`** — same placeholder replacement per side select for spacing.
|
|
234
|
+
- **`BorderSettings` / `BorderRadiusSettings`** — section-level `InheritedIndicator`
|
|
235
|
+
label shown when border/radius objects cascade from a lower breakpoint.
|
|
236
|
+
|
|
237
|
+
The `breakpoints` prop is threaded from `SettingsComponentProps` → `ResponsiveSelectGroup` /
|
|
238
|
+
`BoxModelControl` to enable cascade lookups. CSS class `.wbtr-inherited-value` in
|
|
239
|
+
`resources/styles/common/_editor.css` provides the visual styling.
|
|
240
|
+
|
|
241
|
+
## Adding a New Setting Module
|
|
242
|
+
|
|
243
|
+
1. Create `settings/{name}/` with: `index.ts`, `properties.ts`, `settings.tsx`, `registration.ts`
|
|
244
|
+
2. In `registration.ts`, call `registry.register({ name, panelGroup, order, ... })`
|
|
245
|
+
3. Import `./settings/{name}` in `index.tsx` (side-effect import)
|
|
246
|
+
4. Add a matching PHP handler in `blocks-settings.php` and register it
|
|
247
|
+
5. Update `schemas/webentor-block.json` if adding a new support key
|
|
248
|
+
|
|
249
|
+
## Common Mistakes to Avoid
|
|
250
|
+
|
|
251
|
+
- **Don't read `attributes.layout.display` directly in contextual modules** — use `getDisplayValue()` from `migration.ts`
|
|
252
|
+
- **Don't read parent display directly** — use `getParentDisplayValue()`
|
|
253
|
+
- **Don't create a new PanelBody in a SettingsComponent** — it renders inline within an existing panel
|
|
254
|
+
- **Don't forget both JS and PHP** — class generation runs on both sides
|
|
255
|
+
- **Don't change version numbers here unless the task is an explicit release change** — follow the root `AGENTS.md` release policy for manual versioning and changelog updates
|