@webikon/webentor-core 0.9.13 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/CHANGELOG.md +24 -1
  2. package/README.md +41 -0
  3. package/core-js/_alpine.ts +6 -0
  4. package/core-js/_slider.ts +22 -11
  5. package/core-js/blocks-components/button.tsx +50 -33
  6. package/core-js/blocks-components/custom-image-sizes-panel.tsx +3 -1
  7. package/core-js/blocks-components/typography-picker-select.tsx +16 -1
  8. package/core-js/blocks-filters/_filter-core-typography.tsx +11 -1
  9. package/core-js/blocks-filters/_slider-settings.tsx +1 -1
  10. package/core-js/blocks-filters/_wrap-with-container.tsx +104 -0
  11. package/core-js/blocks-filters/responsive-settings/AGENTS.md +255 -0
  12. package/core-js/blocks-filters/responsive-settings/components/AppliedClassesViewer.tsx +189 -0
  13. package/core-js/blocks-filters/responsive-settings/components/BoxModelControl.tsx +346 -0
  14. package/core-js/blocks-filters/responsive-settings/components/BreakpointResetButton.tsx +94 -0
  15. package/core-js/blocks-filters/responsive-settings/components/DebugPanel.tsx +67 -0
  16. package/core-js/blocks-filters/responsive-settings/components/InheritedIndicator.tsx +32 -0
  17. package/core-js/blocks-filters/responsive-settings/components/LinkedValuesControl.tsx +55 -0
  18. package/core-js/blocks-filters/responsive-settings/components/ResponsiveSelectGroup.tsx +185 -0
  19. package/core-js/blocks-filters/responsive-settings/components/ResponsiveTabPanel.tsx +106 -0
  20. package/core-js/blocks-filters/responsive-settings/index.tsx +97 -148
  21. package/core-js/blocks-filters/responsive-settings/migration.ts +86 -0
  22. package/core-js/blocks-filters/responsive-settings/panels/BlockLinkPanel.tsx +38 -0
  23. package/core-js/blocks-filters/responsive-settings/panels/BorderPanel.tsx +61 -0
  24. package/core-js/blocks-filters/responsive-settings/panels/DisplayLayoutPanel.tsx +92 -0
  25. package/core-js/blocks-filters/responsive-settings/panels/SpacingPanel.tsx +63 -0
  26. package/core-js/blocks-filters/responsive-settings/panels/index.ts +4 -0
  27. package/core-js/blocks-filters/responsive-settings/registry.ts +88 -0
  28. package/core-js/blocks-filters/responsive-settings/settings/block-link/index.ts +3 -0
  29. package/core-js/blocks-filters/responsive-settings/settings/block-link/panel.tsx +6 -6
  30. package/core-js/blocks-filters/responsive-settings/settings/block-link/registration.ts +35 -0
  31. package/core-js/blocks-filters/responsive-settings/settings/border/border/properties.ts +1 -2
  32. package/core-js/blocks-filters/responsive-settings/settings/border/border/settings.tsx +21 -3
  33. package/core-js/blocks-filters/responsive-settings/settings/border/border-radius/index.tsx +2 -1
  34. package/core-js/blocks-filters/responsive-settings/settings/border/border-radius/properties.ts +6 -29
  35. package/core-js/blocks-filters/responsive-settings/settings/border/border-radius/settings.tsx +79 -6
  36. package/core-js/blocks-filters/responsive-settings/settings/border/index.ts +5 -1
  37. package/core-js/blocks-filters/responsive-settings/settings/border/panel.tsx +5 -54
  38. package/core-js/blocks-filters/responsive-settings/settings/border/registration.ts +84 -0
  39. package/core-js/blocks-filters/responsive-settings/settings/border/settings.tsx +21 -0
  40. package/core-js/blocks-filters/responsive-settings/settings/flex-item/index.ts +4 -0
  41. package/core-js/blocks-filters/responsive-settings/settings/flex-item/properties.ts +60 -0
  42. package/core-js/blocks-filters/responsive-settings/settings/flex-item/registration.ts +78 -0
  43. package/core-js/blocks-filters/responsive-settings/settings/flex-item/settings.tsx +90 -0
  44. package/core-js/blocks-filters/responsive-settings/settings/flexbox/index.ts +4 -0
  45. package/core-js/blocks-filters/responsive-settings/settings/flexbox/properties.ts +80 -0
  46. package/core-js/blocks-filters/responsive-settings/settings/flexbox/registration.ts +66 -0
  47. package/core-js/blocks-filters/responsive-settings/settings/flexbox/settings.tsx +78 -0
  48. package/core-js/blocks-filters/responsive-settings/settings/grid/index.ts +4 -0
  49. package/core-js/blocks-filters/responsive-settings/settings/grid/properties.ts +72 -0
  50. package/core-js/blocks-filters/responsive-settings/settings/grid/registration.ts +66 -0
  51. package/core-js/blocks-filters/responsive-settings/settings/grid/settings.tsx +78 -0
  52. package/core-js/blocks-filters/responsive-settings/settings/grid-item/index.ts +4 -0
  53. package/core-js/blocks-filters/responsive-settings/settings/grid-item/properties.ts +44 -0
  54. package/core-js/blocks-filters/responsive-settings/settings/grid-item/registration.ts +74 -0
  55. package/core-js/blocks-filters/responsive-settings/settings/grid-item/settings.tsx +87 -0
  56. package/core-js/blocks-filters/responsive-settings/settings/layout/index.ts +4 -0
  57. package/core-js/blocks-filters/responsive-settings/settings/layout/properties.ts +51 -0
  58. package/core-js/blocks-filters/responsive-settings/settings/layout/registration.ts +96 -0
  59. package/core-js/blocks-filters/responsive-settings/settings/layout/settings.tsx +64 -0
  60. package/core-js/blocks-filters/responsive-settings/settings/presets/index.ts +4 -0
  61. package/core-js/blocks-filters/responsive-settings/settings/presets/presets.ts +52 -0
  62. package/core-js/blocks-filters/responsive-settings/settings/presets/registration.ts +53 -0
  63. package/core-js/blocks-filters/responsive-settings/settings/presets/settings.tsx +100 -0
  64. package/core-js/blocks-filters/responsive-settings/settings/shared/gap-values.ts +16 -0
  65. package/core-js/blocks-filters/responsive-settings/settings/shared/layout-values.ts +56 -0
  66. package/core-js/blocks-filters/responsive-settings/settings/shared/tw-values.ts +107 -0
  67. package/core-js/blocks-filters/responsive-settings/settings/sizing/index.ts +4 -0
  68. package/core-js/blocks-filters/responsive-settings/settings/sizing/properties.ts +71 -0
  69. package/core-js/blocks-filters/responsive-settings/settings/sizing/registration.ts +52 -0
  70. package/core-js/blocks-filters/responsive-settings/settings/sizing/settings.tsx +96 -0
  71. package/core-js/blocks-filters/responsive-settings/settings/spacing/index.ts +7 -2
  72. package/core-js/blocks-filters/responsive-settings/settings/spacing/panel.tsx +5 -45
  73. package/core-js/blocks-filters/responsive-settings/settings/spacing/properties.ts +51 -29
  74. package/core-js/blocks-filters/responsive-settings/settings/spacing/registration.ts +53 -0
  75. package/core-js/blocks-filters/responsive-settings/settings/spacing/settings.tsx +26 -55
  76. package/core-js/blocks-filters/responsive-settings/types/index.ts +174 -28
  77. package/core-js/blocks-filters/responsive-settings/utils.ts +247 -216
  78. package/core-js/config/index.ts +6 -0
  79. package/core-js/config/webentor-config.ts +44 -2
  80. package/core-js/index.ts +8 -10
  81. package/core-js/types/index.ts +6 -0
  82. package/package.json +116 -6
  83. package/public/build/assets/_utils-CzK6Vfiv.js +2 -0
  84. package/public/build/assets/{_utils-PDaZ1Dn1.js.map → _utils-CzK6Vfiv.js.map} +1 -1
  85. package/public/build/assets/coreAppStyles-Bvp3emQy.css +1 -0
  86. package/public/build/assets/coreEditorJs-DYd3ZopL.js +366 -0
  87. package/public/build/assets/coreEditorJs-DYd3ZopL.js.map +1 -0
  88. package/public/build/assets/coreEditorStyles-BzlB6eA_.css +1 -0
  89. package/public/build/assets/resources/blocks/e-table/{script-BIchbcPK.js → script-C_Z50hjm.js} +2 -2
  90. package/public/build/assets/resources/blocks/e-table/{script-BIchbcPK.js.map → script-C_Z50hjm.js.map} +1 -1
  91. package/public/build/assets/{sliderJs-Ch69_tVA.js → sliderJs-CyGnrv0Q.js} +3 -3
  92. package/public/build/assets/{sliderJs-Ch69_tVA.js.map → sliderJs-CyGnrv0Q.js.map} +1 -1
  93. package/public/build/manifest.json +10 -10
  94. package/resources/blocks/e-accordion-group/block.json +6 -4
  95. package/resources/blocks/e-gallery/block.json +2 -2
  96. package/resources/blocks/e-gallery/e-gallery.block.tsx +4 -0
  97. package/resources/blocks/e-image/e-image.block.tsx +4 -0
  98. package/resources/blocks/e-slider/block.json +3 -2
  99. package/resources/blocks/e-tab-container/block.json +2 -1
  100. package/resources/blocks/e-tabs/block.json +2 -1
  101. package/resources/blocks/l-flexible-container/block.json +3 -2
  102. package/resources/blocks/l-mobile-nav/block.json +2 -1
  103. package/resources/blocks/l-nav-menu/block.json +2 -1
  104. package/resources/blocks/l-nav-menu/l-nav-menu.block.tsx +2 -0
  105. package/resources/blocks/l-section/block.json +7 -5
  106. package/resources/blocks/l-section/l-section.block.tsx +40 -31
  107. package/resources/scripts/editor.ts +2 -0
  108. package/resources/styles/common/_editor.css +22 -0
  109. package/resources/styles/common/_utilities.css +210 -0
  110. package/core-js/blocks-filters/responsive-settings/constants.ts +0 -11
  111. package/core-js/blocks-filters/responsive-settings/settings/container/display/index.ts +0 -2
  112. package/core-js/blocks-filters/responsive-settings/settings/container/display/properties.ts +0 -167
  113. package/core-js/blocks-filters/responsive-settings/settings/container/display/settings.tsx +0 -73
  114. package/core-js/blocks-filters/responsive-settings/settings/container/flexbox/index.ts +0 -2
  115. package/core-js/blocks-filters/responsive-settings/settings/container/flexbox/properties.ts +0 -187
  116. package/core-js/blocks-filters/responsive-settings/settings/container/flexbox/settings.tsx +0 -131
  117. package/core-js/blocks-filters/responsive-settings/settings/container/grid/index.ts +0 -2
  118. package/core-js/blocks-filters/responsive-settings/settings/container/grid/properties.ts +0 -187
  119. package/core-js/blocks-filters/responsive-settings/settings/container/grid/settings.tsx +0 -132
  120. package/core-js/blocks-filters/responsive-settings/settings/container/index.ts +0 -4
  121. package/core-js/blocks-filters/responsive-settings/settings/container/panel.tsx +0 -92
  122. package/public/build/assets/_utils-PDaZ1Dn1.js +0 -2
  123. package/public/build/assets/coreAppStyles-Dp0WYk4N.css +0 -1
  124. package/public/build/assets/coreEditorJs-Cyc87wTo.js +0 -366
  125. package/public/build/assets/coreEditorJs-Cyc87wTo.js.map +0 -1
  126. package/public/build/assets/coreEditorStyles-D8-nNpQG.css +0 -1
@@ -0,0 +1,185 @@
1
+ import { SelectControl } from '@wordpress/components';
2
+ import { Fragment } from '@wordpress/element';
3
+ import { __, sprintf } from '@wordpress/i18n';
4
+
5
+ import { setImmutably } from '../../../_utils';
6
+ import { PropertyDefinition } from '../registry';
7
+ import { SelectOptionGroup } from '../types';
8
+ import { getEffectiveValue, getInheritedFromBreakpoint } from '../utils';
9
+
10
+ interface ResponsiveSelectGroupProps {
11
+ attributeKey: string;
12
+ properties: PropertyDefinition[];
13
+ attributes: Record<string, any>;
14
+ setAttributes: (attrs: Record<string, any>) => void;
15
+ breakpoint: string;
16
+ /** Ordered breakpoint names for cascade indicators */
17
+ breakpoints?: string[];
18
+ disabled?: boolean;
19
+ /** Per-property visibility filter (e.g. display supports check) */
20
+ isPropertyVisible?: (property: PropertyDefinition) => boolean;
21
+ /** Map of property name → grouped options for optgroup rendering */
22
+ valueGroups?: Record<string, SelectOptionGroup[]>;
23
+ }
24
+
25
+ /**
26
+ * Resolve the inherited placeholder text for a property.
27
+ * Returns the human-readable label of the cascaded value + source breakpoint,
28
+ * or the default "None selected" if nothing is inherited.
29
+ */
30
+ const getInheritedPlaceholder = (
31
+ attributes: Record<string, any>,
32
+ attributeKey: string,
33
+ property: PropertyDefinition,
34
+ breakpoint: string,
35
+ breakpoints: string[] | undefined,
36
+ valueGroups?: Record<string, SelectOptionGroup[]>,
37
+ ): { text: string; isInherited: boolean } => {
38
+ const defaultText = __('None selected', 'webentor');
39
+ if (!breakpoints?.length) return { text: defaultText, isInherited: false };
40
+
41
+ const inheritedFrom = getInheritedFromBreakpoint(
42
+ attributes,
43
+ attributeKey,
44
+ property.name,
45
+ breakpoint,
46
+ breakpoints,
47
+ );
48
+ if (!inheritedFrom) return { text: defaultText, isInherited: false };
49
+
50
+ const inheritedValue = getEffectiveValue(
51
+ attributes,
52
+ attributeKey,
53
+ property.name,
54
+ breakpoint,
55
+ breakpoints,
56
+ );
57
+ if (!inheritedValue) return { text: defaultText, isInherited: false };
58
+
59
+ // Resolve human-readable label from flat options or grouped options
60
+ let label: string | undefined;
61
+ const groups = property.groupedValues
62
+ ? valueGroups?.[property.name]
63
+ : undefined;
64
+ if (groups) {
65
+ for (const group of groups) {
66
+ const match = group.options.find((o) => o.value === inheritedValue);
67
+ if (match) {
68
+ label = match.label;
69
+ break;
70
+ }
71
+ }
72
+ }
73
+ if (!label) {
74
+ label =
75
+ property.values.find((o) => o.value === inheritedValue)?.label ??
76
+ inheritedValue;
77
+ }
78
+
79
+ return {
80
+ text: sprintf(__('%s (from %s)', 'webentor'), label, inheritedFrom),
81
+ isInherited: true,
82
+ };
83
+ };
84
+
85
+ /**
86
+ * Generic SelectControl list renderer for responsive settings.
87
+ *
88
+ * Shows cascade indicators: when a property has no explicit value at the
89
+ * current breakpoint but inherits one from a lower breakpoint, the empty
90
+ * option shows the inherited value and source breakpoint.
91
+ *
92
+ * When a property has groupedValues + matching valueGroups entry,
93
+ * passes <optgroup> children to SelectControl instead of the options prop.
94
+ * WP's SelectControl renders children directly inside the native <select>.
95
+ */
96
+ export const ResponsiveSelectGroup = ({
97
+ attributeKey,
98
+ properties,
99
+ attributes,
100
+ setAttributes,
101
+ breakpoint,
102
+ breakpoints,
103
+ disabled = false,
104
+ isPropertyVisible,
105
+ valueGroups,
106
+ }: ResponsiveSelectGroupProps) => {
107
+ return (
108
+ <>
109
+ {properties.map((property) => {
110
+ if (isPropertyVisible && !isPropertyVisible(property)) return null;
111
+
112
+ const groups = property.groupedValues
113
+ ? valueGroups?.[property.name]
114
+ : undefined;
115
+ const handleChange = (selected: string) =>
116
+ setAttributes(
117
+ setImmutably(
118
+ attributes,
119
+ [attributeKey, property.name, 'value', breakpoint],
120
+ selected,
121
+ ),
122
+ );
123
+
124
+ const explicitValue =
125
+ attributes[attributeKey]?.[property.name]?.value?.[breakpoint] ?? '';
126
+ const { text: placeholderText, isInherited } = getInheritedPlaceholder(
127
+ attributes,
128
+ attributeKey,
129
+ property,
130
+ breakpoint,
131
+ breakpoints,
132
+ valueGroups,
133
+ );
134
+
135
+ const inheritedClassName =
136
+ !explicitValue && isInherited ? 'wbtr-inherited-value' : '';
137
+
138
+ return (
139
+ <Fragment key={property.name + breakpoint}>
140
+ {groups ? (
141
+ <SelectControl
142
+ label={property.label}
143
+ value={explicitValue}
144
+ help={property?.help}
145
+ disabled={disabled}
146
+ __nextHasNoMarginBottom
147
+ onChange={handleChange}
148
+ className={inheritedClassName}
149
+ >
150
+ <option value="">{placeholderText}</option>
151
+ {groups.map((group) => (
152
+ <optgroup key={group.label} label={group.label}>
153
+ {group.options.map((opt) => (
154
+ <option key={opt.value} value={opt.value}>
155
+ {opt.label}
156
+ </option>
157
+ ))}
158
+ </optgroup>
159
+ ))}
160
+ </SelectControl>
161
+ ) : (
162
+ <SelectControl
163
+ label={property.label}
164
+ value={explicitValue}
165
+ help={property?.help}
166
+ disabled={disabled}
167
+ __nextHasNoMarginBottom
168
+ options={
169
+ isInherited
170
+ ? [
171
+ { label: placeholderText, value: '' },
172
+ ...property.values.filter((o) => o.value !== ''),
173
+ ]
174
+ : property.values
175
+ }
176
+ onChange={handleChange}
177
+ className={inheritedClassName}
178
+ />
179
+ )}
180
+ </Fragment>
181
+ );
182
+ })}
183
+ </>
184
+ );
185
+ };
@@ -0,0 +1,106 @@
1
+ import { TabPanel } from '@wordpress/components';
2
+ import { __ } from '@wordpress/i18n';
3
+
4
+ interface ResponsiveTabPanelProps {
5
+ breakpoints: string[];
6
+ /** Returns true when the breakpoint has at least one active value (renders `*` indicator) */
7
+ hasActiveSettings: (breakpoint: string) => boolean;
8
+ children: (breakpoint: string) => React.ReactNode;
9
+ className?: string;
10
+ /** Resolved twTheme.screens — used for breakpoint tooltip text */
11
+ screens?: Record<string, any>;
12
+ }
13
+
14
+ /**
15
+ * Extracts a human-readable min-width from a Tailwind screen value.
16
+ * Handles plain strings ('768px') and object configs ({ min: '768px' }).
17
+ */
18
+ function resolveScreenValue(
19
+ screen: string | Record<string, string> | Record<string, string>[],
20
+ ): string | null {
21
+ if (typeof screen === 'string') return screen;
22
+ if (Array.isArray(screen)) {
23
+ const entry = screen.find((s) => s.min);
24
+ return entry?.min ?? null;
25
+ }
26
+ if (typeof screen === 'object' && screen !== null) {
27
+ return screen.min ?? screen.raw ?? null;
28
+ }
29
+ return null;
30
+ }
31
+
32
+ function getBreakpointTooltip(
33
+ bp: string,
34
+ screens?: Record<string, any>,
35
+ ): string {
36
+ if (bp === 'basic') {
37
+ return __('min-width: 0px', 'webentor');
38
+ }
39
+ const raw = screens?.[bp];
40
+ if (!raw) return bp;
41
+ const value = resolveScreenValue(raw);
42
+ return value ? `min-width: ${value}` : bp;
43
+ }
44
+
45
+ /**
46
+ * Inline SVG that renders a breakpoint label as text.
47
+ * Used as TabPanel tab `icon` so WP's built-in tooltip activates
48
+ * (when icon is set, TabPanel shows `title` as the tooltip).
49
+ */
50
+ function BreakpointIcon({ label }: { label: string }) {
51
+ const charWidth = 7.5;
52
+ const width = Math.max(20, label.length * charWidth + 6);
53
+
54
+ return (
55
+ <svg
56
+ xmlns="http://www.w3.org/2000/svg"
57
+ viewBox={`0 0 ${width} 20`}
58
+ width={width}
59
+ height={20}
60
+ >
61
+ <text
62
+ x={width / 2}
63
+ y={15}
64
+ textAnchor="middle"
65
+ fontSize="13"
66
+ fontWeight="inherit"
67
+ fill="currentColor"
68
+ >
69
+ {label}
70
+ </text>
71
+ </svg>
72
+ );
73
+ }
74
+
75
+ /**
76
+ * Shared responsive breakpoint tab wrapper.
77
+ * Uses icon-based tabs so TabPanel's built-in tooltip shows each
78
+ * breakpoint's min-width value on hover.
79
+ */
80
+ export const ResponsiveTabPanel = ({
81
+ breakpoints,
82
+ hasActiveSettings,
83
+ children,
84
+ screens,
85
+ className = 'w-responsive-settings-tabs',
86
+ }: ResponsiveTabPanelProps) => {
87
+ return (
88
+ <TabPanel
89
+ activeClass="is-active"
90
+ className={className}
91
+ initialTabName={breakpoints[0]}
92
+ tabs={breakpoints.map((bp) => {
93
+ const active = hasActiveSettings(bp);
94
+ const label = `${bp}${active ? '*' : ''}`;
95
+
96
+ return {
97
+ name: bp,
98
+ title: getBreakpointTooltip(bp, screens),
99
+ icon: <BreakpointIcon label={label} />,
100
+ };
101
+ })}
102
+ >
103
+ {(tab) => <>{children(tab.name)}</>}
104
+ </TabPanel>
105
+ );
106
+ };
@@ -1,163 +1,90 @@
1
+ /**
2
+ * Responsive Settings — Entry point.
3
+ *
4
+ * This module provides per-breakpoint block controls for spacing, layout,
5
+ * sizing, flexbox, grid, flex/grid item, border, and presets.
6
+ *
7
+ * Architecture:
8
+ * - Each setting module self-registers with the SettingsRegistry via side-effect imports
9
+ * - Panel components (SpacingPanel, DisplayLayoutPanel, BorderPanel) query the
10
+ * registry for their panelGroup and render each module's SettingsComponent
11
+ * - BlockEdit renders the panels in InspectorControls
12
+ *
13
+ * @see ./registry.ts — SettingsRegistry singleton
14
+ * @see ./migration.ts — display value helpers
15
+ */
1
16
  import { registerBlockExtension } from '@10up/block-components';
2
- import { InspectorControls } from '@wordpress/block-editor';
17
+ import { BlockControls, InspectorControls } from '@wordpress/block-editor';
18
+ import { ToolbarGroup } from '@wordpress/components';
3
19
  import { Fragment } from '@wordpress/element';
4
20
  import { addFilter, applyFilters } from '@wordpress/hooks';
5
21
 
6
- import { WebentorConfig } from '@webentorCore/types/_webentor-config';
7
-
8
- import { includedBlocks } from './constants';
9
- import { BlockLinkPanel } from './settings/block-link';
10
- import { BorderPanel } from './settings/border';
11
- import { ContainerPanel } from './settings/container';
12
- import { SpacingPanel } from './settings/spacing';
22
+ import { WebentorConfig } from '../../types/_webentor-config';
23
+ import { AppliedClassesViewer } from './components/AppliedClassesViewer';
24
+ import { DebugPanel } from './components/DebugPanel';
25
+ import {
26
+ BlockLinkPanel,
27
+ BorderPanel,
28
+ DisplayLayoutPanel,
29
+ SpacingPanel,
30
+ } from './panels';
31
+ import { registry } from './registry';
13
32
  import { generateClassNames, inlineStyleGenerator } from './utils';
14
33
 
34
+ // Side-effect imports: each module self-registers with the SettingsRegistry.
35
+ // Order doesn't matter — each module declares its own panelGroup and order.
36
+ import './settings/block-link';
37
+ import './settings/border';
38
+ import './settings/flex-item';
39
+ import './settings/flexbox';
40
+ import './settings/grid';
41
+ import './settings/grid-item';
42
+ import './settings/layout';
43
+ import './settings/presets';
44
+ import './settings/sizing';
45
+ import './settings/spacing';
46
+
15
47
  const initResponsiveSettings = () => {
16
- // Make sure new attributes are present in blocks so we can conditionally show their settings
48
+ /**
49
+ * Attribute registration filter.
50
+ * Iterates over all registered setting modules and merges their
51
+ * attribute schemas into blocks that declare support for them.
52
+ */
17
53
  addFilter(
18
54
  'blocks.registerBlockType',
19
55
  'webentor/addResponsiveSettingsAttributes',
20
56
  (settings, name) => {
21
- if (
22
- includedBlocks['blockLink'].includes(name) ||
23
- settings?.supports?.webentor?.blockLink
24
- ) {
25
- settings.attributes = {
26
- ...settings.attributes,
27
- ...{
28
- blockLink: {
29
- type: 'object',
30
- default: settings.attributes?.blockLink?.default || {},
31
- },
32
- },
33
- };
34
- }
35
-
36
- if (
37
- includedBlocks['display'].includes(name) ||
38
- settings?.supports?.webentor?.display
39
- ) {
40
- const displaySupport =
41
- settings?.supports?.webentor?.display === true ||
42
- settings?.supports?.webentor?.display?.display === true;
43
-
44
- settings.attributes = {
45
- ...settings.attributes,
46
- ...{
47
- display: {
48
- type: 'object',
49
- default: {
50
- ...settings?.attributes?.display?.default,
51
- display: {
52
- value: {
53
- // Default display is FLEX
54
- ...(displaySupport ? { basic: 'flex' } : {}),
55
- ...settings?.attributes?.display?.default?.display?.value,
56
- },
57
- },
58
- },
59
- },
60
- },
61
- };
62
- }
63
-
64
- if (
65
- includedBlocks['spacing'].includes(name) ||
66
- settings?.supports?.webentor?.spacing
67
- ) {
68
- settings.attributes = {
69
- ...settings.attributes,
70
- ...{
71
- spacing: {
72
- type: 'object',
73
- default: settings.attributes?.spacing?.default || {},
74
- },
75
- },
76
- };
77
- }
78
-
79
- if (
80
- includedBlocks['grid'].includes(name) ||
81
- settings?.supports?.webentor?.grid
82
- ) {
83
- settings.attributes = {
84
- ...settings.attributes,
85
- ...{
86
- grid: {
87
- type: 'object',
88
- default: settings.attributes?.grid?.default || {},
89
- },
90
- },
91
- };
92
- }
93
-
94
- if (
95
- includedBlocks['gridItem'].includes(name) ||
96
- settings?.supports?.webentor?.gridItem
97
- ) {
98
- settings.attributes = {
99
- ...settings.attributes,
100
- ...{
101
- gridItem: {
102
- type: 'object',
103
- default: settings.attributes?.gridItem?.default || {},
104
- },
105
- },
106
- };
107
- }
108
-
109
- if (
110
- includedBlocks['flexbox'].includes(name) ||
111
- settings?.supports?.webentor?.flexbox
112
- ) {
113
- settings.attributes = {
114
- ...settings.attributes,
115
- ...{
116
- flexbox: {
117
- type: 'object',
118
- default: settings.attributes?.flexbox?.default || {},
119
- },
120
- },
121
- };
122
- }
123
-
124
- if (
125
- includedBlocks['flexboxItem'].includes(name) ||
126
- settings?.supports?.webentor?.flexboxItem
127
- ) {
128
- settings.attributes = {
129
- ...settings.attributes,
130
- ...{
131
- flexboxItem: {
132
- type: 'object',
133
- default: settings.attributes?.flexboxItem?.default || {},
57
+ const allDefs = registry.getAll();
58
+
59
+ for (const def of allDefs) {
60
+ const supportedByRegistry = registry.isSupported(
61
+ settings?.supports,
62
+ def,
63
+ );
64
+ if (!supportedByRegistry) continue;
65
+
66
+ // Merge attribute schemas from the module definition
67
+ for (const [attrKey, schema] of Object.entries(def.attributeSchema)) {
68
+ settings.attributes = {
69
+ ...settings.attributes,
70
+ [attrKey]: {
71
+ ...schema,
72
+ default:
73
+ settings.attributes?.[attrKey]?.default || schema.default,
134
74
  },
135
- },
136
- };
137
- }
75
+ };
76
+ }
138
77
 
139
- if (
140
- includedBlocks['border'].includes(name) ||
141
- includedBlocks['borderRadius'].includes(name) ||
142
- settings?.supports?.webentor?.border ||
143
- settings?.supports?.webentor?.borderRadius
144
- ) {
145
- settings.attributes = {
146
- ...settings.attributes,
147
- ...{
148
- border: {
149
- type: 'object',
150
- default: settings.attributes?.border?.default || {},
151
- },
152
- },
153
- };
78
+ // Custom attribute initialiser (e.g. display flex default)
79
+ if (def.initAttributes) {
80
+ settings = def.initAttributes(settings, name);
81
+ }
154
82
  }
155
83
 
156
84
  return settings;
157
85
  },
158
86
  );
159
87
 
160
- // Register block extension for all blocks
161
88
  registerBlockExtension('*', {
162
89
  extensionName: 'webentor.core.addResponsiveSettings',
163
90
  attributes: {},
@@ -168,26 +95,48 @@ const initResponsiveSettings = () => {
168
95
  });
169
96
  };
170
97
 
171
- const BlockEdit = (props) => {
172
- const breakpoints: string[] = applyFilters('webentor.core.twBreakpoints', [
98
+ /**
99
+ * BlockEdit renders InspectorControls panels for responsive settings.
100
+ *
101
+ * Uses panel wrappers (SpacingPanel, DisplayLayoutPanel, BorderPanel, BlockLinkPanel)
102
+ * which internally query the registry for their panelGroup's modules.
103
+ *
104
+ * Responsive settings migration is handled globally in PHP.
105
+ */
106
+ const BlockEdit = (props: any) => {
107
+ const breakpoints = applyFilters('webentor.core.twBreakpoints', [
173
108
  'basic',
174
- ]);
175
- const twTheme: WebentorConfig['theme'] = applyFilters(
109
+ ]) as string[];
110
+ const twTheme = applyFilters(
176
111
  'webentor.core.twTheme',
177
112
  {},
178
- );
113
+ ) as WebentorConfig['theme'];
179
114
 
180
115
  return (
181
116
  <Fragment>
117
+ <BlockControls>
118
+ <ToolbarGroup>
119
+ <AppliedClassesViewer
120
+ {...props}
121
+ breakpoints={breakpoints}
122
+ twTheme={twTheme}
123
+ />
124
+ </ToolbarGroup>
125
+ </BlockControls>
182
126
  <InspectorControls>
183
127
  <SpacingPanel {...props} breakpoints={breakpoints} twTheme={twTheme} />
184
- <ContainerPanel
128
+ <DisplayLayoutPanel
185
129
  {...props}
186
130
  breakpoints={breakpoints}
187
131
  twTheme={twTheme}
188
132
  />
189
133
  <BorderPanel {...props} breakpoints={breakpoints} twTheme={twTheme} />
190
- <BlockLinkPanel {...props} />
134
+ <BlockLinkPanel
135
+ {...props}
136
+ breakpoints={breakpoints}
137
+ twTheme={twTheme}
138
+ />
139
+ <DebugPanel {...props} breakpoints={breakpoints} twTheme={twTheme} />
191
140
  </InspectorControls>
192
141
  </Fragment>
193
142
  );
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Display value helpers.
3
+ *
4
+ * Generic (non-display) cascade utilities live in utils.ts.
5
+ */
6
+
7
+ /**
8
+ * Resolve the "display" value for a given breakpoint (explicit only, no cascade).
9
+ *
10
+ * This is the canonical way to read the display mode for modules that need it
11
+ * (flexbox, grid, flex-item, grid-item).
12
+ */
13
+ export const getDisplayValue = (
14
+ attributes: Record<string, any>,
15
+ breakpoint: string,
16
+ ): string | undefined => {
17
+ return attributes?.layout?.display?.value?.[breakpoint];
18
+ };
19
+
20
+ /**
21
+ * Resolve the parent display value for a given breakpoint (explicit only).
22
+ */
23
+ export const getParentDisplayValue = (
24
+ parentAttributes: Record<string, any> | undefined,
25
+ breakpoint: string,
26
+ ): string | undefined => {
27
+ if (!parentAttributes) return undefined;
28
+ return getDisplayValue(parentAttributes, breakpoint);
29
+ };
30
+
31
+ // ─── Display-specific cascade ───────────────────────────────────────
32
+
33
+ /**
34
+ * Cascaded display value — walks breakpoints and returns the effective
35
+ * display mode at the given breakpoint.
36
+ */
37
+ export const getEffectiveDisplayValue = (
38
+ attributes: Record<string, any>,
39
+ breakpoint: string,
40
+ breakpoints: string[],
41
+ ): string | undefined => {
42
+ const targetIndex = breakpoints.indexOf(breakpoint);
43
+ if (targetIndex === -1) return undefined;
44
+ let effective: string | undefined;
45
+ for (let i = 0; i <= targetIndex; i++) {
46
+ const val = getDisplayValue(attributes, breakpoints[i]);
47
+ if (val !== undefined && val !== '') effective = val;
48
+ }
49
+ return effective;
50
+ };
51
+
52
+ /**
53
+ * Cascaded parent display value — same cascade logic applied to
54
+ * the parent block's attributes.
55
+ */
56
+ export const getEffectiveParentDisplayValue = (
57
+ parentAttributes: Record<string, any> | undefined,
58
+ breakpoint: string,
59
+ breakpoints: string[],
60
+ ): string | undefined => {
61
+ if (!parentAttributes) return undefined;
62
+ return getEffectiveDisplayValue(parentAttributes, breakpoint, breakpoints);
63
+ };
64
+
65
+ /**
66
+ * Display-specific inheritance source.
67
+ * Returns the breakpoint name the display value cascades from,
68
+ * or null if explicitly set at the current breakpoint.
69
+ */
70
+ export const getDisplayInheritedFromBreakpoint = (
71
+ attributes: Record<string, any>,
72
+ breakpoint: string,
73
+ breakpoints: string[],
74
+ ): string | null => {
75
+ const targetIndex = breakpoints.indexOf(breakpoint);
76
+ if (targetIndex === -1) return null;
77
+
78
+ const explicitVal = getDisplayValue(attributes, breakpoint);
79
+ if (explicitVal !== undefined && explicitVal !== '') return null;
80
+
81
+ for (let i = targetIndex - 1; i >= 0; i--) {
82
+ const val = getDisplayValue(attributes, breakpoints[i]);
83
+ if (val !== undefined && val !== '') return breakpoints[i];
84
+ }
85
+ return null;
86
+ };
@@ -0,0 +1,38 @@
1
+ /**
2
+ * BlockLinkPanel — Standalone panel for block link settings.
3
+ *
4
+ * Block link is non-responsive, so this panel renders without
5
+ * breakpoint tabs. Separated from DisplayLayoutPanel to avoid
6
+ * confusion about its scope.
7
+ */
8
+ import { PanelBody } from '@wordpress/components';
9
+ import { __ } from '@wordpress/i18n';
10
+
11
+ import { registry } from '../registry';
12
+ import { BlockPanelProps } from '../types';
13
+
14
+ export const BlockLinkPanel = (props: BlockPanelProps) => {
15
+ const { attributes } = props;
16
+ const modules = registry.getByPanelGroup('blockLink');
17
+
18
+ const hasAnyAttributes = modules.some((def) =>
19
+ Object.keys(def.attributeSchema).some((key) => !!attributes?.[key]),
20
+ );
21
+
22
+ if (!hasAnyAttributes) return null;
23
+
24
+ const hasNonDefaults = modules.some((def) =>
25
+ def.hasActiveSettings(attributes, 'basic'),
26
+ );
27
+
28
+ return (
29
+ <PanelBody
30
+ title={__('Block Link', 'webentor') + (hasNonDefaults ? ' *' : '')}
31
+ initialOpen={false}
32
+ >
33
+ {modules.map((def) => (
34
+ <def.SettingsComponent key={def.name} {...props} breakpoint="basic" />
35
+ ))}
36
+ </PanelBody>
37
+ );
38
+ };