@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.
- package/CHANGELOG.md +24 -1
- 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 +50 -33
- 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
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
import { Icon, SelectControl, Tooltip } from '@wordpress/components';
|
|
2
|
+
import { __, sprintf } from '@wordpress/i18n';
|
|
3
|
+
import {
|
|
4
|
+
sidesBottom,
|
|
5
|
+
sidesHorizontal,
|
|
6
|
+
sidesLeft,
|
|
7
|
+
sidesRight,
|
|
8
|
+
sidesTop,
|
|
9
|
+
sidesVertical,
|
|
10
|
+
} from '@wordpress/icons';
|
|
11
|
+
|
|
12
|
+
import { setImmutably } from '../../../_utils';
|
|
13
|
+
import { SelectOption } from '../types';
|
|
14
|
+
import { getEffectiveValue, getInheritedFromBreakpoint } from '../utils';
|
|
15
|
+
import { LinkedValuesControl, LinkMode } from './LinkedValuesControl';
|
|
16
|
+
|
|
17
|
+
interface Side {
|
|
18
|
+
/** Attribute property name, e.g. 'margin-top' */
|
|
19
|
+
name: string;
|
|
20
|
+
label: string;
|
|
21
|
+
values: SelectOption[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface BoxModelControlProps {
|
|
25
|
+
type: 'margin' | 'padding';
|
|
26
|
+
sides: Side[];
|
|
27
|
+
attributes: Record<string, any>;
|
|
28
|
+
setAttributes: (attrs: Record<string, any>) => void;
|
|
29
|
+
/** Attribute group key, e.g. 'spacing' */
|
|
30
|
+
attributeKey: string;
|
|
31
|
+
breakpoint: string;
|
|
32
|
+
/** Ordered breakpoint names for cascade indicators */
|
|
33
|
+
breakpoints?: string[];
|
|
34
|
+
disabled?: boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ── Prefix helpers ──────────────────────────────────────────────────
|
|
38
|
+
// Spacing values are stored as full Tailwind classes (e.g. "mt-4").
|
|
39
|
+
// When linking sides we need to map the scale part to each side's prefix.
|
|
40
|
+
|
|
41
|
+
/** Extract the TW utility prefix from a side's value list (e.g. "mt" from "mt-4"). */
|
|
42
|
+
const getSidePrefix = (side: Side): string | null => {
|
|
43
|
+
const entry = side.values.find((v) => v.value !== '');
|
|
44
|
+
if (!entry) return null;
|
|
45
|
+
const match = entry.value.match(/^([a-z]+)-/);
|
|
46
|
+
return match ? match[1] : null;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/** Extract the scale/key portion from a TW class: "mt-4" → "4", "mt-auto" → "auto". */
|
|
50
|
+
const extractScale = (value: string): string | null => {
|
|
51
|
+
const match = value.match(/^[a-z]+-(.+)$/);
|
|
52
|
+
return match ? match[1] : null;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/** Build the correct TW class for a target side given a scale: ("mr", "4") → "mr-4". */
|
|
56
|
+
const buildValue = (prefix: string, scale: string): string =>
|
|
57
|
+
`${prefix}-${scale}`;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Visual box-model control for margin or padding.
|
|
61
|
+
* Two states: linked (H+V axis selects) or unlinked (4-corner grid).
|
|
62
|
+
*/
|
|
63
|
+
export const BoxModelControl = ({
|
|
64
|
+
type,
|
|
65
|
+
sides,
|
|
66
|
+
attributes,
|
|
67
|
+
setAttributes,
|
|
68
|
+
attributeKey,
|
|
69
|
+
breakpoint,
|
|
70
|
+
breakpoints,
|
|
71
|
+
disabled = false,
|
|
72
|
+
}: BoxModelControlProps) => {
|
|
73
|
+
const linkModeKey = `_${type}LinkMode`;
|
|
74
|
+
const currentLinkMode: LinkMode =
|
|
75
|
+
(attributes[attributeKey]?.[linkModeKey]?.value?.[breakpoint] as
|
|
76
|
+
| LinkMode
|
|
77
|
+
| undefined) ?? 'unlinked';
|
|
78
|
+
|
|
79
|
+
const topSide = sides.find((s) => s.name.includes('top'));
|
|
80
|
+
const bottomSide = sides.find((s) => s.name.includes('bottom'));
|
|
81
|
+
const leftSide = sides.find((s) => s.name.includes('left'));
|
|
82
|
+
const rightSide = sides.find((s) => s.name.includes('right'));
|
|
83
|
+
|
|
84
|
+
const isLinked = currentLinkMode === 'linked';
|
|
85
|
+
|
|
86
|
+
const getValue = (side?: Side): string =>
|
|
87
|
+
side
|
|
88
|
+
? (attributes[attributeKey]?.[side.name]?.value?.[breakpoint] ?? '')
|
|
89
|
+
: '';
|
|
90
|
+
|
|
91
|
+
// ── Mode toggle: sync pairs when switching to linked ──────────
|
|
92
|
+
const setLinkMode = (nextMode: LinkMode) => {
|
|
93
|
+
let newAttrs = setImmutably(
|
|
94
|
+
attributes,
|
|
95
|
+
[attributeKey, linkModeKey, 'value', breakpoint],
|
|
96
|
+
nextMode,
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
// Sync pairs when switching to linked: left→right, top→bottom
|
|
100
|
+
if (nextMode === 'linked') {
|
|
101
|
+
if (leftSide && rightSide) {
|
|
102
|
+
const leftVal = getValue(leftSide);
|
|
103
|
+
if (leftVal) {
|
|
104
|
+
const scale = extractScale(leftVal);
|
|
105
|
+
const rightPrefix = getSidePrefix(rightSide);
|
|
106
|
+
if (scale && rightPrefix) {
|
|
107
|
+
newAttrs = setImmutably(
|
|
108
|
+
newAttrs,
|
|
109
|
+
[attributeKey, rightSide.name, 'value', breakpoint],
|
|
110
|
+
buildValue(rightPrefix, scale),
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (topSide && bottomSide) {
|
|
116
|
+
const topVal = getValue(topSide);
|
|
117
|
+
if (topVal) {
|
|
118
|
+
const scale = extractScale(topVal);
|
|
119
|
+
const bottomPrefix = getSidePrefix(bottomSide);
|
|
120
|
+
if (scale && bottomPrefix) {
|
|
121
|
+
newAttrs = setImmutably(
|
|
122
|
+
newAttrs,
|
|
123
|
+
[attributeKey, bottomSide.name, 'value', breakpoint],
|
|
124
|
+
buildValue(bottomPrefix, scale),
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
setAttributes(newAttrs);
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
// ── Value change with pair propagation in linked mode ─────────
|
|
135
|
+
const handleChange = (sideName: string, value: string) => {
|
|
136
|
+
// Determine which sides to update together
|
|
137
|
+
let targetSides: Side[];
|
|
138
|
+
|
|
139
|
+
if (isLinked) {
|
|
140
|
+
const isHorizontal =
|
|
141
|
+
sideName.includes('left') || sideName.includes('right');
|
|
142
|
+
if (isHorizontal) {
|
|
143
|
+
targetSides = [leftSide, rightSide].filter(Boolean) as Side[];
|
|
144
|
+
} else {
|
|
145
|
+
targetSides = [topSide, bottomSide].filter(Boolean) as Side[];
|
|
146
|
+
}
|
|
147
|
+
} else {
|
|
148
|
+
const side = sides.find((s) => s.name === sideName);
|
|
149
|
+
targetSides = side ? [side] : [];
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
let newAttrs = { ...attributes };
|
|
153
|
+
|
|
154
|
+
if (!value) {
|
|
155
|
+
for (const side of targetSides) {
|
|
156
|
+
newAttrs = setImmutably(
|
|
157
|
+
newAttrs,
|
|
158
|
+
[attributeKey, side.name, 'value', breakpoint],
|
|
159
|
+
'',
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
} else {
|
|
163
|
+
const scale = extractScale(value);
|
|
164
|
+
for (const side of targetSides) {
|
|
165
|
+
const prefix = getSidePrefix(side);
|
|
166
|
+
const mapped = scale && prefix ? buildValue(prefix, scale) : value;
|
|
167
|
+
newAttrs = setImmutably(
|
|
168
|
+
newAttrs,
|
|
169
|
+
[attributeKey, side.name, 'value', breakpoint],
|
|
170
|
+
mapped,
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
setAttributes(newAttrs);
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const resetAll = () => {
|
|
179
|
+
let newAttrs = { ...attributes };
|
|
180
|
+
for (const side of sides) {
|
|
181
|
+
newAttrs = setImmutably(
|
|
182
|
+
newAttrs,
|
|
183
|
+
[attributeKey, side.name, 'value', breakpoint],
|
|
184
|
+
'',
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
setAttributes(newAttrs);
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const hasAnyValue = sides.some(
|
|
191
|
+
(s) => !!attributes[attributeKey]?.[s.name]?.value?.[breakpoint],
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
// ── Select renderer with cascade indicators ─────────────────────
|
|
195
|
+
const renderSelect = (
|
|
196
|
+
side: Side | undefined,
|
|
197
|
+
labelOverride?: string,
|
|
198
|
+
hideLabel?: boolean,
|
|
199
|
+
) => {
|
|
200
|
+
if (!side) return null;
|
|
201
|
+
|
|
202
|
+
const explicitValue = getValue(side);
|
|
203
|
+
let options = side.values;
|
|
204
|
+
let inheritedClassName = '';
|
|
205
|
+
|
|
206
|
+
if (!explicitValue && breakpoints?.length) {
|
|
207
|
+
const inheritedFrom = getInheritedFromBreakpoint(
|
|
208
|
+
attributes,
|
|
209
|
+
attributeKey,
|
|
210
|
+
side.name,
|
|
211
|
+
breakpoint,
|
|
212
|
+
breakpoints,
|
|
213
|
+
);
|
|
214
|
+
if (inheritedFrom) {
|
|
215
|
+
const inheritedValue = getEffectiveValue(
|
|
216
|
+
attributes,
|
|
217
|
+
attributeKey,
|
|
218
|
+
side.name,
|
|
219
|
+
breakpoint,
|
|
220
|
+
breakpoints,
|
|
221
|
+
);
|
|
222
|
+
if (inheritedValue) {
|
|
223
|
+
const label =
|
|
224
|
+
side.values.find((o) => o.value === inheritedValue)?.label ??
|
|
225
|
+
inheritedValue;
|
|
226
|
+
const placeholderText = sprintf(
|
|
227
|
+
__('%s (from %s)', 'webentor'),
|
|
228
|
+
label,
|
|
229
|
+
inheritedFrom,
|
|
230
|
+
);
|
|
231
|
+
options = [
|
|
232
|
+
{ label: placeholderText, value: '' },
|
|
233
|
+
...side.values.filter((o) => o.value !== ''),
|
|
234
|
+
];
|
|
235
|
+
inheritedClassName = 'wbtr-inherited-value';
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return (
|
|
241
|
+
<SelectControl
|
|
242
|
+
label={labelOverride ?? side.label}
|
|
243
|
+
hideLabelFromVision={hideLabel}
|
|
244
|
+
value={explicitValue}
|
|
245
|
+
options={options}
|
|
246
|
+
disabled={disabled}
|
|
247
|
+
onChange={(val) => handleChange(side.name, val)}
|
|
248
|
+
className={inheritedClassName}
|
|
249
|
+
__nextHasNoMarginBottom
|
|
250
|
+
/>
|
|
251
|
+
);
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
return (
|
|
255
|
+
<div className="wbtr:my-2 wbtr:flex wbtr:flex-col wbtr:gap-2 wbtr:border wbtr:border-editor-border wbtr:p-2">
|
|
256
|
+
<div className="wbtr:flex wbtr:items-center wbtr:gap-2">
|
|
257
|
+
<p className="wbtr:mb-0! wbtr:text-12 wbtr:uppercase">
|
|
258
|
+
{type === 'margin'
|
|
259
|
+
? __('Margin', 'webentor')
|
|
260
|
+
: __('Padding', 'webentor')}
|
|
261
|
+
</p>
|
|
262
|
+
|
|
263
|
+
<LinkedValuesControl
|
|
264
|
+
mode={currentLinkMode}
|
|
265
|
+
onModeChange={setLinkMode}
|
|
266
|
+
onReset={resetAll}
|
|
267
|
+
resetDisabled={!hasAnyValue}
|
|
268
|
+
/>
|
|
269
|
+
</div>
|
|
270
|
+
|
|
271
|
+
{isLinked ? (
|
|
272
|
+
<div className="wbtr:flex wbtr:flex-col wbtr:gap-1.5">
|
|
273
|
+
<div className="wbtr:flex wbtr:items-center wbtr:gap-2">
|
|
274
|
+
<Tooltip text={__('Horizontal', 'webentor')}>
|
|
275
|
+
<span className="wbtr:flex">
|
|
276
|
+
<Icon icon={sidesHorizontal} size={20} />
|
|
277
|
+
</span>
|
|
278
|
+
</Tooltip>
|
|
279
|
+
<div className="wbtr:flex-1">
|
|
280
|
+
{renderSelect(leftSide, __('Horizontal', 'webentor'), true)}
|
|
281
|
+
</div>
|
|
282
|
+
</div>
|
|
283
|
+
<div className="wbtr:flex wbtr:items-center wbtr:gap-2">
|
|
284
|
+
<Tooltip text={__('Vertical', 'webentor')}>
|
|
285
|
+
<span className="wbtr:flex">
|
|
286
|
+
<Icon icon={sidesVertical} size={20} />
|
|
287
|
+
</span>
|
|
288
|
+
</Tooltip>
|
|
289
|
+
<div className="wbtr:flex-1">
|
|
290
|
+
{renderSelect(topSide, __('Vertical', 'webentor'), true)}
|
|
291
|
+
</div>
|
|
292
|
+
</div>
|
|
293
|
+
</div>
|
|
294
|
+
) : (
|
|
295
|
+
<div
|
|
296
|
+
style={{
|
|
297
|
+
display: 'grid',
|
|
298
|
+
gridTemplateColumns: '1fr 1fr',
|
|
299
|
+
gap: '4px',
|
|
300
|
+
}}
|
|
301
|
+
>
|
|
302
|
+
<div className="wbtr:flex wbtr:items-center wbtr:gap-1.5">
|
|
303
|
+
<Tooltip text={__('Top', 'webentor')}>
|
|
304
|
+
<span className="wbtr:flex">
|
|
305
|
+
<Icon icon={sidesTop} size={20} />
|
|
306
|
+
</span>
|
|
307
|
+
</Tooltip>
|
|
308
|
+
<div className="wbtr:flex-1">
|
|
309
|
+
{renderSelect(topSide, topSide?.label, true)}
|
|
310
|
+
</div>
|
|
311
|
+
</div>
|
|
312
|
+
<div className="wbtr:flex wbtr:items-center wbtr:gap-1.5">
|
|
313
|
+
<Tooltip text={__('Right', 'webentor')}>
|
|
314
|
+
<span className="wbtr:flex">
|
|
315
|
+
<Icon icon={sidesRight} size={20} />
|
|
316
|
+
</span>
|
|
317
|
+
</Tooltip>
|
|
318
|
+
<div className="wbtr:flex-1">
|
|
319
|
+
{renderSelect(rightSide, rightSide?.label, true)}
|
|
320
|
+
</div>
|
|
321
|
+
</div>
|
|
322
|
+
<div className="wbtr:flex wbtr:items-center wbtr:gap-1.5">
|
|
323
|
+
<Tooltip text={__('Bottom', 'webentor')}>
|
|
324
|
+
<span className="wbtr:flex">
|
|
325
|
+
<Icon icon={sidesBottom} size={20} />
|
|
326
|
+
</span>
|
|
327
|
+
</Tooltip>
|
|
328
|
+
<div className="wbtr:flex-1">
|
|
329
|
+
{renderSelect(bottomSide, bottomSide?.label, true)}
|
|
330
|
+
</div>
|
|
331
|
+
</div>
|
|
332
|
+
<div className="wbtr:flex wbtr:items-center wbtr:gap-1.5">
|
|
333
|
+
<Tooltip text={__('Left', 'webentor')}>
|
|
334
|
+
<span className="wbtr:flex">
|
|
335
|
+
<Icon icon={sidesLeft} size={20} />
|
|
336
|
+
</span>
|
|
337
|
+
</Tooltip>
|
|
338
|
+
<div className="wbtr:flex-1">
|
|
339
|
+
{renderSelect(leftSide, leftSide?.label, true)}
|
|
340
|
+
</div>
|
|
341
|
+
</div>
|
|
342
|
+
</div>
|
|
343
|
+
)}
|
|
344
|
+
</div>
|
|
345
|
+
);
|
|
346
|
+
};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BreakpointResetButton — Clears all setting values for a specific
|
|
3
|
+
* breakpoint within a panel group. Renders as a small button at the
|
|
4
|
+
* top of each breakpoint tab.
|
|
5
|
+
*/
|
|
6
|
+
import { Button } from '@wordpress/components';
|
|
7
|
+
import { __ } from '@wordpress/i18n';
|
|
8
|
+
|
|
9
|
+
import { registry } from '../registry';
|
|
10
|
+
import { PanelGroup } from '../types';
|
|
11
|
+
|
|
12
|
+
interface BreakpointResetButtonProps {
|
|
13
|
+
panelGroup: PanelGroup;
|
|
14
|
+
breakpoint: string;
|
|
15
|
+
attributes: Record<string, any>;
|
|
16
|
+
setAttributes: (attrs: Record<string, any>) => void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const BreakpointResetButton = ({
|
|
20
|
+
panelGroup,
|
|
21
|
+
breakpoint,
|
|
22
|
+
attributes,
|
|
23
|
+
setAttributes,
|
|
24
|
+
}: BreakpointResetButtonProps) => {
|
|
25
|
+
const modules = registry.getByPanelGroup(panelGroup);
|
|
26
|
+
|
|
27
|
+
const hasAnyValues = modules.some((def) => {
|
|
28
|
+
// Check all attribute keys the module owns.
|
|
29
|
+
for (const key of Object.keys(def.attributeSchema)) {
|
|
30
|
+
const attrGroup = attributes?.[key];
|
|
31
|
+
if (!attrGroup || typeof attrGroup !== 'object') continue;
|
|
32
|
+
if (
|
|
33
|
+
Object.values(attrGroup).some(
|
|
34
|
+
(prop: any) => prop?.value?.[breakpoint] !== undefined,
|
|
35
|
+
)
|
|
36
|
+
)
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
return false;
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
if (!hasAnyValues) return null;
|
|
43
|
+
|
|
44
|
+
const handleReset = () => {
|
|
45
|
+
const patch: Record<string, any> = {};
|
|
46
|
+
|
|
47
|
+
for (const def of modules) {
|
|
48
|
+
// Iterate all attribute keys in the module's schema.
|
|
49
|
+
for (const key of Object.keys(def.attributeSchema)) {
|
|
50
|
+
const attrGroup = attributes?.[key];
|
|
51
|
+
if (!attrGroup || typeof attrGroup !== 'object') continue;
|
|
52
|
+
|
|
53
|
+
const updatedGroup = { ...attrGroup };
|
|
54
|
+
let changed = false;
|
|
55
|
+
|
|
56
|
+
for (const [propName, propData] of Object.entries(updatedGroup)) {
|
|
57
|
+
const prop = propData as any;
|
|
58
|
+
if (prop?.value?.[breakpoint] !== undefined) {
|
|
59
|
+
const { [breakpoint]: _, ...rest } = prop.value;
|
|
60
|
+
updatedGroup[propName] = { ...prop, value: rest };
|
|
61
|
+
changed = true;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (changed) {
|
|
66
|
+
patch[key] = updatedGroup;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (Object.keys(patch).length) {
|
|
72
|
+
setAttributes(patch);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<div
|
|
78
|
+
style={{
|
|
79
|
+
display: 'flex',
|
|
80
|
+
justifyContent: 'flex-end',
|
|
81
|
+
marginBottom: '8px',
|
|
82
|
+
}}
|
|
83
|
+
>
|
|
84
|
+
<Button
|
|
85
|
+
variant="tertiary"
|
|
86
|
+
isDestructive
|
|
87
|
+
size="small"
|
|
88
|
+
onClick={handleReset}
|
|
89
|
+
>
|
|
90
|
+
{__('Reset breakpoint', 'webentor')}
|
|
91
|
+
</Button>
|
|
92
|
+
</div>
|
|
93
|
+
);
|
|
94
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DebugPanel — Collapsible inspector panel showing raw responsive
|
|
3
|
+
* setting attributes. Gated behind window.WEBENTOR_WP_DEBUG.
|
|
4
|
+
*/
|
|
5
|
+
import { PanelBody } from '@wordpress/components';
|
|
6
|
+
import { __ } from '@wordpress/i18n';
|
|
7
|
+
|
|
8
|
+
import { BlockPanelProps } from '../types';
|
|
9
|
+
|
|
10
|
+
declare global {
|
|
11
|
+
interface Window {
|
|
12
|
+
WEBENTOR_WP_DEBUG?: boolean;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const ATTRIBUTE_KEYS = [
|
|
17
|
+
'layout',
|
|
18
|
+
'sizing',
|
|
19
|
+
'spacing',
|
|
20
|
+
'flexbox',
|
|
21
|
+
'grid',
|
|
22
|
+
'flexItem',
|
|
23
|
+
'gridItem',
|
|
24
|
+
'border',
|
|
25
|
+
'_preset',
|
|
26
|
+
'_presetClasses',
|
|
27
|
+
'blockLink',
|
|
28
|
+
// v1 keys are not supported anymore, kept here for debugging reference
|
|
29
|
+
'display',
|
|
30
|
+
'flexboxItem',
|
|
31
|
+
] as const;
|
|
32
|
+
|
|
33
|
+
export const DebugPanel = ({ attributes }: BlockPanelProps) => {
|
|
34
|
+
if (!window.WEBENTOR_WP_DEBUG) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const debugData: Record<string, any> = {};
|
|
39
|
+
for (const key of ATTRIBUTE_KEYS) {
|
|
40
|
+
if (attributes?.[key] !== undefined) {
|
|
41
|
+
debugData[key] = attributes[key];
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<PanelBody
|
|
47
|
+
title={__('Debug: Responsive Settings', 'webentor')}
|
|
48
|
+
initialOpen={false}
|
|
49
|
+
>
|
|
50
|
+
<pre
|
|
51
|
+
style={{
|
|
52
|
+
fontSize: '10px',
|
|
53
|
+
lineHeight: '1.4',
|
|
54
|
+
maxHeight: '400px',
|
|
55
|
+
overflow: 'auto',
|
|
56
|
+
background: '#f0f0f0',
|
|
57
|
+
padding: '8px',
|
|
58
|
+
borderRadius: '4px',
|
|
59
|
+
whiteSpace: 'pre-wrap',
|
|
60
|
+
wordBreak: 'break-all',
|
|
61
|
+
}}
|
|
62
|
+
>
|
|
63
|
+
{JSON.stringify(debugData, null, 2)}
|
|
64
|
+
</pre>
|
|
65
|
+
</PanelBody>
|
|
66
|
+
);
|
|
67
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* InheritedIndicator — shows when a setting section is visible due to
|
|
3
|
+
* breakpoint cascading (min-width inheritance) rather than an explicit
|
|
4
|
+
* value at the current breakpoint.
|
|
5
|
+
*
|
|
6
|
+
* Displays e.g. "Inherited from basic" so the user understands why
|
|
7
|
+
* flexbox/grid controls appear even though no display value is explicitly
|
|
8
|
+
* set at the active breakpoint.
|
|
9
|
+
*/
|
|
10
|
+
import { __, sprintf } from '@wordpress/i18n';
|
|
11
|
+
|
|
12
|
+
interface InheritedIndicatorProps {
|
|
13
|
+
/** The breakpoint name the value cascades from (e.g. 'basic', 'sm') */
|
|
14
|
+
fromBreakpoint: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const INDICATOR_STYLE: React.CSSProperties = {
|
|
18
|
+
fontSize: '11px',
|
|
19
|
+
color: '#757575',
|
|
20
|
+
fontStyle: 'italic',
|
|
21
|
+
marginBottom: '8px',
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const InheritedIndicator = ({
|
|
25
|
+
fromBreakpoint,
|
|
26
|
+
}: InheritedIndicatorProps) => {
|
|
27
|
+
return (
|
|
28
|
+
<div style={INDICATOR_STYLE}>
|
|
29
|
+
{sprintf(__('Inherited from %s', 'webentor'), fromBreakpoint)}
|
|
30
|
+
</div>
|
|
31
|
+
);
|
|
32
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Button } from '@wordpress/components';
|
|
2
|
+
import { __ } from '@wordpress/i18n';
|
|
3
|
+
import { link, linkOff } from '@wordpress/icons';
|
|
4
|
+
|
|
5
|
+
export type LinkMode = 'linked' | 'unlinked';
|
|
6
|
+
|
|
7
|
+
interface LinkedValuesControlProps {
|
|
8
|
+
mode: LinkMode;
|
|
9
|
+
onModeChange: (mode: LinkMode) => void;
|
|
10
|
+
onReset?: () => void;
|
|
11
|
+
resetDisabled?: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Toggle button for linking/unlinking spacing sides.
|
|
16
|
+
* Linked = horizontal+vertical pairs synced; Unlinked = all 4 independent.
|
|
17
|
+
*/
|
|
18
|
+
export const LinkedValuesControl = ({
|
|
19
|
+
mode,
|
|
20
|
+
onModeChange,
|
|
21
|
+
onReset,
|
|
22
|
+
resetDisabled = false,
|
|
23
|
+
}: LinkedValuesControlProps) => {
|
|
24
|
+
const isLinked = mode === 'linked';
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<div className="wbtr:flex wbtr:grow-1 wbtr:items-center wbtr:justify-between">
|
|
28
|
+
<Button
|
|
29
|
+
icon={isLinked ? linkOff : link}
|
|
30
|
+
isPressed={isLinked}
|
|
31
|
+
onClick={() => onModeChange(isLinked ? 'unlinked' : 'linked')}
|
|
32
|
+
label={
|
|
33
|
+
isLinked
|
|
34
|
+
? __('Unlink sides', 'webentor')
|
|
35
|
+
: __('Link sides', 'webentor')
|
|
36
|
+
}
|
|
37
|
+
showTooltip
|
|
38
|
+
size="small"
|
|
39
|
+
/>
|
|
40
|
+
|
|
41
|
+
{onReset && (
|
|
42
|
+
<Button
|
|
43
|
+
variant="tertiary"
|
|
44
|
+
onClick={onReset}
|
|
45
|
+
disabled={resetDisabled}
|
|
46
|
+
label={__('Reset to defaults', 'webentor')}
|
|
47
|
+
showTooltip
|
|
48
|
+
size="small"
|
|
49
|
+
>
|
|
50
|
+
{__('Reset', 'webentor')}
|
|
51
|
+
</Button>
|
|
52
|
+
)}
|
|
53
|
+
</div>
|
|
54
|
+
);
|
|
55
|
+
};
|