@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,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Preset definitions - one-click layout configurations.
|
|
3
|
+
*
|
|
4
|
+
* Core intentionally ships no built-in presets because layout shortcuts are
|
|
5
|
+
* project-specific. Themes/plugins are expected to provide their own preset
|
|
6
|
+
* catalog through `webentor.core.responsiveSettings.layoutPresets` in the
|
|
7
|
+
* editor bootstrap where they already register `twTheme` and breakpoints.
|
|
8
|
+
*
|
|
9
|
+
* Each preset specifies which attribute values to set and optionally includes
|
|
10
|
+
* custom CSS classes for layouts that cannot be expressed with pure Tailwind
|
|
11
|
+
* utilities (for example: flex-wrap + gap + equal columns).
|
|
12
|
+
*
|
|
13
|
+
* Presets are decomposable: selecting one fills in the individual settings
|
|
14
|
+
* below, which remain editable. The `_preset` marker only tracks which preset
|
|
15
|
+
* was last applied.
|
|
16
|
+
*/
|
|
17
|
+
import { applyFilters } from '@wordpress/hooks';
|
|
18
|
+
|
|
19
|
+
import { WebentorConfig } from '../../../../types/_webentor-config';
|
|
20
|
+
import { LayoutPreset } from '../../types';
|
|
21
|
+
|
|
22
|
+
// Intentionally empty: themes/projects own the preset catalog.
|
|
23
|
+
export const layoutPresets: LayoutPreset[] = [];
|
|
24
|
+
|
|
25
|
+
const clonePreset = (preset: LayoutPreset): LayoutPreset => ({
|
|
26
|
+
...preset,
|
|
27
|
+
applies: Object.fromEntries(
|
|
28
|
+
Object.entries(preset.applies).map(([attributeKey, propertyMap]) => [
|
|
29
|
+
attributeKey,
|
|
30
|
+
Object.fromEntries(
|
|
31
|
+
Object.entries(propertyMap).map(([propertyName, propertyValue]) => [
|
|
32
|
+
propertyName,
|
|
33
|
+
{
|
|
34
|
+
value: { ...propertyValue.value },
|
|
35
|
+
},
|
|
36
|
+
]),
|
|
37
|
+
),
|
|
38
|
+
]),
|
|
39
|
+
),
|
|
40
|
+
customClasses: preset.customClasses ? [...preset.customClasses] : undefined,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
export const getLayoutPresets = (
|
|
44
|
+
blockName: string,
|
|
45
|
+
twTheme: WebentorConfig['theme'],
|
|
46
|
+
): LayoutPreset[] =>
|
|
47
|
+
applyFilters(
|
|
48
|
+
'webentor.core.responsiveSettings.layoutPresets',
|
|
49
|
+
layoutPresets.map(clonePreset),
|
|
50
|
+
blockName,
|
|
51
|
+
twTheme,
|
|
52
|
+
) as LayoutPreset[];
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Presets module registration.
|
|
3
|
+
*
|
|
4
|
+
* panelGroup: displayLayout, order: 0 (renders first, above layout/sizing)
|
|
5
|
+
* Provides one-click layout configurations that populate the individual
|
|
6
|
+
* settings below.
|
|
7
|
+
*
|
|
8
|
+
* Support keys: dedicated 'presets' opt-in plus 'layout' so existing
|
|
9
|
+
* blocks keep the current preset UI behavior during the v2 support-key migration.
|
|
10
|
+
*/
|
|
11
|
+
import { registry } from '../../registry';
|
|
12
|
+
import { ClassGenContext } from '../../types';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Preset custom classes are stored in _presetClasses and output directly.
|
|
16
|
+
* These are non-decomposable classes needed for edge cases like
|
|
17
|
+
* flex-wrap + gap + equal columns.
|
|
18
|
+
*/
|
|
19
|
+
const generatePresetClasses = (
|
|
20
|
+
attributes: Record<string, any>,
|
|
21
|
+
breakpoint: string,
|
|
22
|
+
_context: ClassGenContext,
|
|
23
|
+
): string[] => {
|
|
24
|
+
if (breakpoint !== 'basic') return [];
|
|
25
|
+
|
|
26
|
+
const presetClasses = attributes?._presetClasses;
|
|
27
|
+
if (!Array.isArray(presetClasses)) return [];
|
|
28
|
+
|
|
29
|
+
return presetClasses;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Presets are registered for attribute schema + class generation only.
|
|
34
|
+
* The UI (PresetSettings) is rendered directly by DisplayLayoutPanel
|
|
35
|
+
* above the breakpoint tabs, since presets apply globally.
|
|
36
|
+
*/
|
|
37
|
+
registry.register({
|
|
38
|
+
name: 'presets',
|
|
39
|
+
panelGroup: 'displayLayout',
|
|
40
|
+
order: 0,
|
|
41
|
+
attributeKey: '_preset',
|
|
42
|
+
supportKey: ['presets', 'layout'],
|
|
43
|
+
attributeSchema: {
|
|
44
|
+
_preset: { type: 'string', default: '' },
|
|
45
|
+
_presetClasses: { type: 'array', default: [] },
|
|
46
|
+
},
|
|
47
|
+
SettingsComponent: () => null,
|
|
48
|
+
generateClasses: generatePresetClasses,
|
|
49
|
+
hasActiveSettings: () => {
|
|
50
|
+
// Presets are rendered above the tabs (non-responsive) — never flag a tab
|
|
51
|
+
return false;
|
|
52
|
+
},
|
|
53
|
+
});
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PresetSettings — Quick layout preset buttons.
|
|
3
|
+
*
|
|
4
|
+
* Rendered at the top of the DisplayLayoutPanel (order: 0).
|
|
5
|
+
* Selecting a preset populates the underlying layout/flexbox/grid
|
|
6
|
+
* settings and marks the _preset attribute for tracking.
|
|
7
|
+
*/
|
|
8
|
+
import { Button } from '@wordpress/components';
|
|
9
|
+
import { __ } from '@wordpress/i18n';
|
|
10
|
+
|
|
11
|
+
import { setImmutably } from '../../../../_utils';
|
|
12
|
+
import { BlockPanelProps, LayoutPreset } from '../../types';
|
|
13
|
+
import { getLayoutPresets } from './presets';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Apply a preset's attribute values to the current block.
|
|
17
|
+
* Merges preset values into existing attributes rather than replacing,
|
|
18
|
+
* so non-preset settings are preserved.
|
|
19
|
+
*/
|
|
20
|
+
const applyPreset = (
|
|
21
|
+
preset: LayoutPreset,
|
|
22
|
+
attributes: Record<string, any>,
|
|
23
|
+
setAttributes: (attrs: Record<string, any>) => void,
|
|
24
|
+
) => {
|
|
25
|
+
let newAttrs = { ...attributes };
|
|
26
|
+
|
|
27
|
+
// Apply each module's preset values
|
|
28
|
+
for (const [attrKey, presetValues] of Object.entries(preset.applies)) {
|
|
29
|
+
for (const [propName, propValue] of Object.entries(presetValues)) {
|
|
30
|
+
const val = propValue as any;
|
|
31
|
+
if (val?.value) {
|
|
32
|
+
for (const [bp, bpValue] of Object.entries(val.value)) {
|
|
33
|
+
newAttrs = setImmutably(
|
|
34
|
+
newAttrs,
|
|
35
|
+
[attrKey, propName, 'value', bp],
|
|
36
|
+
bpValue,
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Store preset marker and custom classes
|
|
44
|
+
newAttrs._preset = preset.id;
|
|
45
|
+
if (preset.customClasses?.length) {
|
|
46
|
+
newAttrs._presetClasses = preset.customClasses;
|
|
47
|
+
} else {
|
|
48
|
+
newAttrs._presetClasses = undefined;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
setAttributes(newAttrs);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const PresetSettings = ({
|
|
55
|
+
attributes,
|
|
56
|
+
setAttributes,
|
|
57
|
+
name,
|
|
58
|
+
twTheme,
|
|
59
|
+
}: BlockPanelProps) => {
|
|
60
|
+
const activePreset = attributes?._preset;
|
|
61
|
+
const presets = getLayoutPresets(name, twTheme);
|
|
62
|
+
|
|
63
|
+
if (!presets.length) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<div style={{ marginTop: '8px', marginBottom: '8px' }}>
|
|
69
|
+
<p
|
|
70
|
+
style={{
|
|
71
|
+
fontSize: '11px',
|
|
72
|
+
textTransform: 'uppercase',
|
|
73
|
+
marginBottom: '8px',
|
|
74
|
+
color: '#757575',
|
|
75
|
+
}}
|
|
76
|
+
>
|
|
77
|
+
{__('Quick Layout Presets', 'webentor')}
|
|
78
|
+
</p>
|
|
79
|
+
<div
|
|
80
|
+
style={{
|
|
81
|
+
display: 'flex',
|
|
82
|
+
flexWrap: 'wrap',
|
|
83
|
+
gap: '4px',
|
|
84
|
+
}}
|
|
85
|
+
>
|
|
86
|
+
{presets.map((preset) => (
|
|
87
|
+
<Button
|
|
88
|
+
key={preset.id}
|
|
89
|
+
variant={activePreset === preset.id ? 'primary' : 'secondary'}
|
|
90
|
+
size="small"
|
|
91
|
+
onClick={() => applyPreset(preset, attributes, setAttributes)}
|
|
92
|
+
title={preset.description}
|
|
93
|
+
>
|
|
94
|
+
{preset.label}
|
|
95
|
+
</Button>
|
|
96
|
+
))}
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
);
|
|
100
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { WebentorConfig } from '../../../../types/_webentor-config';
|
|
2
|
+
import { SelectOption } from '../../types';
|
|
3
|
+
import { createTwThemeValues, spacingLabel } from './tw-values';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Shared gap value generator used by both grid and flexbox properties.
|
|
7
|
+
*/
|
|
8
|
+
export const getGapValues = (
|
|
9
|
+
axis: '' | 'x' | 'y' = '',
|
|
10
|
+
twTheme: WebentorConfig['theme'],
|
|
11
|
+
): SelectOption[] => {
|
|
12
|
+
const prefix = axis ? `gap-${axis}` : 'gap';
|
|
13
|
+
return createTwThemeValues(twTheme, 'gap', prefix, {
|
|
14
|
+
labelFormatter: spacingLabel,
|
|
15
|
+
});
|
|
16
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { __ } from '@wordpress/i18n';
|
|
2
|
+
|
|
3
|
+
import { SelectOption } from '../../types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Shared layout value lists used by both flexbox and grid properties.
|
|
7
|
+
* Eliminates the 5 identical function pairs that were copy-pasted between
|
|
8
|
+
* flexbox/properties.ts and grid/properties.ts.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export const getJustifyContentValues = (): SelectOption[] => [
|
|
12
|
+
{ label: __('None selected', 'webentor'), value: '' },
|
|
13
|
+
{ label: __('Normal', 'webentor'), value: 'justify-normal' },
|
|
14
|
+
{ label: __('Flex Start', 'webentor'), value: 'justify-start' },
|
|
15
|
+
{ label: __('Flex End', 'webentor'), value: 'justify-end' },
|
|
16
|
+
{ label: __('Center', 'webentor'), value: 'justify-center' },
|
|
17
|
+
{ label: __('Space Between', 'webentor'), value: 'justify-between' },
|
|
18
|
+
{ label: __('Space Around', 'webentor'), value: 'justify-around' },
|
|
19
|
+
{ label: __('Space Evenly', 'webentor'), value: 'justify-evenly' },
|
|
20
|
+
{ label: __('Stretch', 'webentor'), value: 'justify-stretch' },
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
export const getAlignItemsValues = (): SelectOption[] => [
|
|
24
|
+
{ label: __('None selected', 'webentor'), value: '' },
|
|
25
|
+
{ label: __('Flex Start', 'webentor'), value: 'items-start' },
|
|
26
|
+
{ label: __('Flex End', 'webentor'), value: 'items-end' },
|
|
27
|
+
{ label: __('Center', 'webentor'), value: 'items-center' },
|
|
28
|
+
{ label: __('Baseline', 'webentor'), value: 'items-baseline' },
|
|
29
|
+
{ label: __('Stretch', 'webentor'), value: 'items-stretch' },
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
export const getAlignContentValues = (): SelectOption[] => [
|
|
33
|
+
{ label: __('None selected', 'webentor'), value: '' },
|
|
34
|
+
{ label: __('Normal', 'webentor'), value: 'content-normal' },
|
|
35
|
+
{ label: __('Flex Start', 'webentor'), value: 'content-start' },
|
|
36
|
+
{ label: __('Flex End', 'webentor'), value: 'content-end' },
|
|
37
|
+
{ label: __('Center', 'webentor'), value: 'content-center' },
|
|
38
|
+
{ label: __('Space Between', 'webentor'), value: 'content-between' },
|
|
39
|
+
{ label: __('Space Around', 'webentor'), value: 'content-around' },
|
|
40
|
+
{ label: __('Space Evenly', 'webentor'), value: 'content-evenly' },
|
|
41
|
+
{ label: __('Baseline', 'webentor'), value: 'content-baseline' },
|
|
42
|
+
{ label: __('Stretch', 'webentor'), value: 'content-stretch' },
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
export const getOrderValues = (): SelectOption[] => [
|
|
46
|
+
{ label: __('None selected', 'webentor'), value: '' },
|
|
47
|
+
{ label: __('Order First', 'webentor'), value: 'order-first' },
|
|
48
|
+
{ label: __('Order Last', 'webentor'), value: 'order-last' },
|
|
49
|
+
{ label: __('Order None', 'webentor'), value: 'order-none' },
|
|
50
|
+
{ label: __('Order 0', 'webentor'), value: 'order-0' },
|
|
51
|
+
{ label: __('Order 1', 'webentor'), value: 'order-1' },
|
|
52
|
+
{ label: __('Order 2', 'webentor'), value: 'order-2' },
|
|
53
|
+
{ label: __('Order 3', 'webentor'), value: 'order-3' },
|
|
54
|
+
{ label: __('Order 4', 'webentor'), value: 'order-4' },
|
|
55
|
+
{ label: __('Order 5', 'webentor'), value: 'order-5' },
|
|
56
|
+
];
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { __ } from '@wordpress/i18n';
|
|
2
|
+
|
|
3
|
+
import { WebentorConfig } from '../../../../types/_webentor-config';
|
|
4
|
+
import { SelectOption, SelectOptionGroup } from '../../types';
|
|
5
|
+
import { getPixelFromRemValue } from '../../utils';
|
|
6
|
+
|
|
7
|
+
export interface TwThemeValueOptions {
|
|
8
|
+
sort?: boolean;
|
|
9
|
+
labelFormatter?: (key: string, rawValue: string) => string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const defaultLabelFormatter = (key: string, rawValue: string) =>
|
|
13
|
+
`${key} (${getPixelFromRemValue(rawValue)})`;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Creates a SelectOption[] from a Tailwind theme config section.
|
|
17
|
+
* Replaces the 13+ near-identical getXValues() functions scattered
|
|
18
|
+
* across display, flexbox, grid, spacing, and border property files.
|
|
19
|
+
*
|
|
20
|
+
* @param twTheme Tailwind resolved theme
|
|
21
|
+
* @param themeKey Key inside twTheme (e.g. 'height', 'spacing', 'gap')
|
|
22
|
+
* @param prefix CSS utility prefix (e.g. 'h', 'w', 'mt', 'gap-x')
|
|
23
|
+
* @param opts Sorting and label formatting overrides
|
|
24
|
+
*/
|
|
25
|
+
export const createTwThemeValues = (
|
|
26
|
+
twTheme: WebentorConfig['theme'],
|
|
27
|
+
themeKey: string,
|
|
28
|
+
prefix: string,
|
|
29
|
+
opts?: TwThemeValueOptions,
|
|
30
|
+
): SelectOption[] => {
|
|
31
|
+
const themeObj = twTheme?.[themeKey];
|
|
32
|
+
if (!themeObj) return [{ label: __('None selected', 'webentor'), value: '' }];
|
|
33
|
+
|
|
34
|
+
const keys = Object.keys(themeObj);
|
|
35
|
+
if (opts?.sort !== false) {
|
|
36
|
+
// Sort by the resolved CSS value (e.g. 0.25rem → 4, 1rem → 16)
|
|
37
|
+
keys.sort(
|
|
38
|
+
(a, b) => (parseFloat(themeObj[a]) || 0) - (parseFloat(themeObj[b]) || 0),
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const formatter = opts?.labelFormatter ?? defaultLabelFormatter;
|
|
43
|
+
|
|
44
|
+
const values = keys.map((key) => ({
|
|
45
|
+
label: formatter(key, themeObj[key]),
|
|
46
|
+
value: prefix ? `${prefix}-${key}` : key,
|
|
47
|
+
}));
|
|
48
|
+
|
|
49
|
+
values.unshift({
|
|
50
|
+
label: __('None selected', 'webentor'),
|
|
51
|
+
value: '',
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
return values;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/** Matches pure integers (96) and floats (3.5) — rejects units like 3rem, 100% */
|
|
58
|
+
const PURE_NUMBER = /^\d+(\.\d+)?$/;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Creates grouped SelectOptionGroup[] splitting numeric vs string values.
|
|
62
|
+
* Numeric = theme key is a pure integer or float (e.g. "96", "3.5").
|
|
63
|
+
* String = everything else (e.g. "auto", "full", "screen", "px").
|
|
64
|
+
*/
|
|
65
|
+
export const createGroupedTwThemeValues = (
|
|
66
|
+
twTheme: WebentorConfig['theme'],
|
|
67
|
+
themeKey: string,
|
|
68
|
+
prefix: string,
|
|
69
|
+
opts?: TwThemeValueOptions,
|
|
70
|
+
): SelectOptionGroup[] => {
|
|
71
|
+
const themeObj = twTheme?.[themeKey];
|
|
72
|
+
if (!themeObj) return [];
|
|
73
|
+
|
|
74
|
+
const keys = Object.keys(themeObj);
|
|
75
|
+
if (opts?.sort !== false) {
|
|
76
|
+
keys.sort(
|
|
77
|
+
(a, b) => (parseFloat(themeObj[a]) || 0) - (parseFloat(themeObj[b]) || 0),
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const formatter = opts?.labelFormatter ?? defaultLabelFormatter;
|
|
82
|
+
const numeric: SelectOption[] = [];
|
|
83
|
+
const keyword: SelectOption[] = [];
|
|
84
|
+
|
|
85
|
+
for (const key of keys) {
|
|
86
|
+
const rawValue = themeObj[key];
|
|
87
|
+
const option: SelectOption = {
|
|
88
|
+
label: formatter(key, rawValue),
|
|
89
|
+
value: prefix ? `${prefix}-${key}` : key,
|
|
90
|
+
};
|
|
91
|
+
if (PURE_NUMBER.test(key)) {
|
|
92
|
+
numeric.push(option);
|
|
93
|
+
} else {
|
|
94
|
+
keyword.push(option);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const groups: SelectOptionGroup[] = [];
|
|
99
|
+
if (numeric.length)
|
|
100
|
+
groups.push({ label: __('Numeric', 'webentor'), options: numeric });
|
|
101
|
+
if (keyword.length)
|
|
102
|
+
groups.push({ label: __('Keyword', 'webentor'), options: keyword });
|
|
103
|
+
return groups;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/** Label formatter for spacing-scale keys (key × 4 = px) */
|
|
107
|
+
export const spacingLabel = (key: string): string => `${Number(key) * 4}px`;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sizing properties — width, height, min/max dimensions.
|
|
3
|
+
*
|
|
4
|
+
* These were previously lumped under the 'display' attribute alongside
|
|
5
|
+
* the CSS display property. Now they live in their own 'sizing' attribute
|
|
6
|
+
* with a dedicated support key.
|
|
7
|
+
*/
|
|
8
|
+
import { __ } from '@wordpress/i18n';
|
|
9
|
+
|
|
10
|
+
import { WebentorConfig } from '../../../../types/_webentor-config';
|
|
11
|
+
import { PropertyDefinition } from '../../types';
|
|
12
|
+
import { createTwThemeValues } from '../shared/tw-values';
|
|
13
|
+
|
|
14
|
+
export const getSizingProperties = (
|
|
15
|
+
twTheme: WebentorConfig['theme'],
|
|
16
|
+
): PropertyDefinition[] => [
|
|
17
|
+
{
|
|
18
|
+
label: __('Height', 'webentor'),
|
|
19
|
+
name: 'height',
|
|
20
|
+
values: createTwThemeValues(twTheme, 'height', 'h'),
|
|
21
|
+
groupedValues: true,
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
label: __('Min Height', 'webentor'),
|
|
25
|
+
name: 'min-height',
|
|
26
|
+
values: createTwThemeValues(twTheme, 'minHeight', 'min-h'),
|
|
27
|
+
groupedValues: true,
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
label: __('Max Height', 'webentor'),
|
|
31
|
+
name: 'max-height',
|
|
32
|
+
values: createTwThemeValues(twTheme, 'maxHeight', 'max-h'),
|
|
33
|
+
groupedValues: true,
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
label: __('Width', 'webentor'),
|
|
37
|
+
name: 'width',
|
|
38
|
+
values: createTwThemeValues(twTheme, 'width', 'w'),
|
|
39
|
+
groupedValues: true,
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
label: __('Min Width', 'webentor'),
|
|
43
|
+
name: 'min-width',
|
|
44
|
+
values: createTwThemeValues(twTheme, 'minWidth', 'min-w'),
|
|
45
|
+
groupedValues: true,
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
label: __('Max Width', 'webentor'),
|
|
49
|
+
name: 'max-width',
|
|
50
|
+
values: createTwThemeValues(twTheme, 'maxWidth', 'max-w'),
|
|
51
|
+
groupedValues: true,
|
|
52
|
+
},
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
export const SIZING_PROPERTY_NAMES = [
|
|
56
|
+
'height',
|
|
57
|
+
'min-height',
|
|
58
|
+
'max-height',
|
|
59
|
+
'width',
|
|
60
|
+
'min-width',
|
|
61
|
+
'max-width',
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
export const hasSizingSettingsForBreakpoint = (
|
|
65
|
+
attributes: Record<string, any>,
|
|
66
|
+
breakpoint: string,
|
|
67
|
+
): boolean => {
|
|
68
|
+
return SIZING_PROPERTY_NAMES.some(
|
|
69
|
+
(prop) => !!attributes?.sizing?.[prop]?.value?.[breakpoint],
|
|
70
|
+
);
|
|
71
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sizing module registration.
|
|
3
|
+
*
|
|
4
|
+
* panelGroup: displayLayout, order: 20
|
|
5
|
+
* Handles width, height, min/max dimensions.
|
|
6
|
+
* Support key: 'sizing'
|
|
7
|
+
*/
|
|
8
|
+
import { registry } from '../../registry';
|
|
9
|
+
import { ClassGenContext } from '../../types';
|
|
10
|
+
import {
|
|
11
|
+
hasSizingSettingsForBreakpoint,
|
|
12
|
+
SIZING_PROPERTY_NAMES,
|
|
13
|
+
} from './properties';
|
|
14
|
+
import { SizingSettings } from './settings';
|
|
15
|
+
|
|
16
|
+
const generateSizingClasses = (
|
|
17
|
+
attributes: Record<string, any>,
|
|
18
|
+
breakpoint: string,
|
|
19
|
+
_context: ClassGenContext,
|
|
20
|
+
): string[] => {
|
|
21
|
+
const classes: string[] = [];
|
|
22
|
+
|
|
23
|
+
const sizingAttr = attributes?.sizing || {};
|
|
24
|
+
|
|
25
|
+
for (const propName of SIZING_PROPERTY_NAMES) {
|
|
26
|
+
const propData = sizingAttr[propName];
|
|
27
|
+
if (!propData?.value) continue;
|
|
28
|
+
const bpValue = propData.value[breakpoint];
|
|
29
|
+
if (!bpValue) continue;
|
|
30
|
+
|
|
31
|
+
if (attributes?.slider?.enabled?.value?.[breakpoint]) continue;
|
|
32
|
+
|
|
33
|
+
const twBreakpoint = breakpoint === 'basic' ? '' : `${breakpoint}:`;
|
|
34
|
+
classes.push(`${twBreakpoint}${bpValue}`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return classes;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
registry.register({
|
|
41
|
+
name: 'sizing',
|
|
42
|
+
panelGroup: 'displayLayout',
|
|
43
|
+
order: 20,
|
|
44
|
+
attributeKey: 'sizing',
|
|
45
|
+
supportKey: 'sizing',
|
|
46
|
+
attributeSchema: {
|
|
47
|
+
sizing: { type: 'object', default: {} },
|
|
48
|
+
},
|
|
49
|
+
SettingsComponent: SizingSettings,
|
|
50
|
+
generateClasses: generateSizingClasses,
|
|
51
|
+
hasActiveSettings: hasSizingSettingsForBreakpoint,
|
|
52
|
+
});
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SizingSettings — width, height, min/max dimension controls.
|
|
3
|
+
*
|
|
4
|
+
* Rendered inside the DisplayLayoutPanel at order: 20.
|
|
5
|
+
*/
|
|
6
|
+
import { getBlockSupport } from '@wordpress/blocks';
|
|
7
|
+
import { Fragment, useMemo } from '@wordpress/element';
|
|
8
|
+
|
|
9
|
+
import { camelize } from '../../../../_utils';
|
|
10
|
+
import { DisabledSliderInfo } from '../../components/DisabledSliderInfo';
|
|
11
|
+
import { ResponsiveSelectGroup } from '../../components/ResponsiveSelectGroup';
|
|
12
|
+
import {
|
|
13
|
+
PropertyDefinition,
|
|
14
|
+
SelectOptionGroup,
|
|
15
|
+
SettingsComponentProps,
|
|
16
|
+
} from '../../types';
|
|
17
|
+
import { isSliderEnabledForBreakpoint } from '../../utils';
|
|
18
|
+
import { createGroupedTwThemeValues } from '../shared/tw-values';
|
|
19
|
+
import { getSizingProperties } from './properties';
|
|
20
|
+
|
|
21
|
+
export const SizingSettings = ({
|
|
22
|
+
attributes,
|
|
23
|
+
setAttributes,
|
|
24
|
+
name,
|
|
25
|
+
breakpoint,
|
|
26
|
+
breakpoints,
|
|
27
|
+
twTheme,
|
|
28
|
+
}: SettingsComponentProps) => {
|
|
29
|
+
if (!attributes?.sizing) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const isSliderEnabled = isSliderEnabledForBreakpoint(
|
|
34
|
+
name,
|
|
35
|
+
attributes,
|
|
36
|
+
breakpoint,
|
|
37
|
+
);
|
|
38
|
+
const sizingProperties = getSizingProperties(twTheme);
|
|
39
|
+
|
|
40
|
+
/** Property name → [themeKey, prefix] for grouped value generation */
|
|
41
|
+
const themeKeyMap: Record<string, [string, string]> = {
|
|
42
|
+
height: ['height', 'h'],
|
|
43
|
+
'min-height': ['minHeight', 'min-h'],
|
|
44
|
+
'max-height': ['maxHeight', 'max-h'],
|
|
45
|
+
width: ['width', 'w'],
|
|
46
|
+
'min-width': ['minWidth', 'min-w'],
|
|
47
|
+
'max-width': ['maxWidth', 'max-w'],
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const valueGroups = useMemo(() => {
|
|
51
|
+
const groups: Record<string, SelectOptionGroup[]> = {};
|
|
52
|
+
for (const prop of sizingProperties) {
|
|
53
|
+
if (!prop.groupedValues) continue;
|
|
54
|
+
const mapping = themeKeyMap[prop.name];
|
|
55
|
+
if (mapping) {
|
|
56
|
+
groups[prop.name] = createGroupedTwThemeValues(
|
|
57
|
+
twTheme,
|
|
58
|
+
mapping[0],
|
|
59
|
+
mapping[1],
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return groups;
|
|
64
|
+
}, [twTheme]);
|
|
65
|
+
|
|
66
|
+
const webentorSupports = getBlockSupport(name, 'webentor') as
|
|
67
|
+
| Record<string, any>
|
|
68
|
+
| undefined;
|
|
69
|
+
const supports = webentorSupports?.sizing;
|
|
70
|
+
|
|
71
|
+
const isPropertyVisible = (property: PropertyDefinition) => {
|
|
72
|
+
if (supports === true) return true;
|
|
73
|
+
return supports?.[camelize(property.name)] === true;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const attributeKey = 'sizing';
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<Fragment>
|
|
80
|
+
<div style={{ marginTop: '16px' }}>
|
|
81
|
+
{isSliderEnabled && <DisabledSliderInfo />}
|
|
82
|
+
<ResponsiveSelectGroup
|
|
83
|
+
attributeKey={attributeKey}
|
|
84
|
+
properties={sizingProperties}
|
|
85
|
+
attributes={attributes}
|
|
86
|
+
setAttributes={setAttributes}
|
|
87
|
+
breakpoint={breakpoint}
|
|
88
|
+
breakpoints={breakpoints}
|
|
89
|
+
disabled={isSliderEnabled}
|
|
90
|
+
isPropertyVisible={isPropertyVisible}
|
|
91
|
+
valueGroups={valueGroups}
|
|
92
|
+
/>
|
|
93
|
+
</div>
|
|
94
|
+
</Fragment>
|
|
95
|
+
);
|
|
96
|
+
};
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
import './registration';
|
|
2
|
+
|
|
2
3
|
export { SpacingSettings } from './settings';
|
|
3
|
-
export {
|
|
4
|
+
export {
|
|
5
|
+
getSpacingProperties,
|
|
6
|
+
getMarginSides,
|
|
7
|
+
getPaddingSides,
|
|
8
|
+
} from './properties';
|
|
@@ -1,45 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import { SpacingSettings } from './settings';
|
|
7
|
-
|
|
8
|
-
export const SpacingPanel = (props: BlockPanelProps) => {
|
|
9
|
-
const { attributes, breakpoints, twTheme } = props;
|
|
10
|
-
|
|
11
|
-
if (!attributes?.spacing) {
|
|
12
|
-
return null;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const hasSpacingSettingsForBreakpoint = (breakpoint: string): boolean => {
|
|
16
|
-
return !!(
|
|
17
|
-
attributes?.spacing?.['margin-top']?.value?.[breakpoint] ||
|
|
18
|
-
attributes?.spacing?.['margin-bottom']?.value?.[breakpoint] ||
|
|
19
|
-
attributes?.spacing?.['margin-left']?.value?.[breakpoint] ||
|
|
20
|
-
attributes?.spacing?.['margin-right']?.value?.[breakpoint] ||
|
|
21
|
-
attributes?.spacing?.['padding-top']?.value?.[breakpoint] ||
|
|
22
|
-
attributes?.spacing?.['padding-bottom']?.value?.[breakpoint] ||
|
|
23
|
-
attributes?.spacing?.['padding-left']?.value?.[breakpoint] ||
|
|
24
|
-
attributes?.spacing?.['padding-right']?.value?.[breakpoint]
|
|
25
|
-
);
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
return (
|
|
29
|
-
<PanelBody title={__('Spacing Settings', 'webentor')} initialOpen={false}>
|
|
30
|
-
<TabPanel
|
|
31
|
-
activeClass="is-active"
|
|
32
|
-
className="w-responsive-settings-tabs"
|
|
33
|
-
initialTabName={breakpoints[0]}
|
|
34
|
-
tabs={breakpoints.map((breakpoint) => ({
|
|
35
|
-
name: breakpoint,
|
|
36
|
-
title: `${breakpoint}${hasSpacingSettingsForBreakpoint(breakpoint) ? '*' : ''}`, // Add * if spacing is set on this breakpoint
|
|
37
|
-
}))}
|
|
38
|
-
>
|
|
39
|
-
{(tab) => (
|
|
40
|
-
<SpacingSettings {...props} breakpoint={tab.name} twTheme={twTheme} />
|
|
41
|
-
)}
|
|
42
|
-
</TabPanel>
|
|
43
|
-
</PanelBody>
|
|
44
|
-
);
|
|
45
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* @deprecated — SpacingPanel is now provided by panels/SpacingPanel.tsx.
|
|
3
|
+
* This file is kept for backward compat with any external imports.
|
|
4
|
+
*/
|
|
5
|
+
export { SpacingPanel } from '../../panels/SpacingPanel';
|