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