@webikon/webentor-core 0.9.14 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/CHANGELOG.md +20 -0
  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 +17 -2
  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,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
+ };
@@ -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
  );