@plaudit/gutenberg-api-extensions 2.80.1 → 2.81.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.
@@ -0,0 +1,1100 @@
1
+ # Simple Native Properties (SNP)
2
+
3
+ SNP allows you to define Inspector (sidebar) controls in `block.json` purely with data, avoiding custom React code for standard use cases.
4
+
5
+ ## 📑 Table of Contents
6
+
7
+ - [Quick Start Example](#-quick-start-example)
8
+ - [Control Reference](#-control-reference)
9
+ - [Common Properties](#common-properties-available-on-all-controls)
10
+ - [Control Types](#️-control-types)
11
+ - [Basic Input Controls](#basic-input-controls)
12
+ - [`text`](#text)
13
+ - [`textarea`](#textarea)
14
+ - [`number`](#number)
15
+ - [`range`](#range)
16
+ - [Toggle & Choice Controls](#toggle--choice-controls)
17
+ - [`toggle`](#toggle)
18
+ - [`toggleGroup`](#togglegroup)
19
+ - [`select`](#select)
20
+ - [`radio`](#radio)
21
+ - [`colorPalette`](#colorpalette)
22
+ - [Media & Relational Controls](#media--relational-controls)
23
+ - [`image`](#image)
24
+ - [`link`](#link)
25
+ - [`post`](#post)
26
+ - [`taxonomy`](#taxonomy)
27
+ - [`term`](#term)
28
+ - [`user`](#user)
29
+ - [Structure & Collection Controls](#structure--collection-controls)
30
+ - [`group`](#group)
31
+ - [`list`](#list)
32
+ - [`date`](#date)
33
+ - [`constant`](#constant)
34
+ - [Organization & Logic](#️-organization--logic)
35
+ - [1. Structure (Panels & Tabs)](#1-structure-panels--tabs)
36
+ - [2. Layout (Rows)](#2-layout-rows)
37
+ - [3. Conditions (Show/Hide Logic)](#3-conditions-showhide-logic)
38
+ - [4. Option Sources (Dynamic Options)](#4-option-sources-dynamic-options)
39
+ - [Advanced Features](#-advanced-features)
40
+ - [Style Properties](#style-properties)
41
+ - [Validators & Transformers](#validators--transformers)
42
+ - [Tips & Best Practices](#tips--best-practices)
43
+
44
+ ---
45
+
46
+ ## ⚡ Quick Start Example
47
+
48
+ Copy and adapt this block to cover 90% of your use cases. It demonstrates **Panels**, **Layouts**, **Conditions**, and **Common Controls**.
49
+
50
+ ```json
51
+ "plaudit": {
52
+ "properties": [
53
+ {
54
+ "title": "Main Content",
55
+ "initialOpen": true,
56
+ "properties": [
57
+ { "name": "headline", "label": "Headline", "control": "text", "placeholder": "Enter headline..." },
58
+ {
59
+ "name": "subhead", "label": "Subhead", "control": "textarea",
60
+ "condition": [{ "op": "!empty", "property": "headline" }]
61
+ },
62
+ [
63
+ { "name": "amount", "label": "Count", "control": "number", "min": 1, "max": 10 },
64
+ { "name": "showCount", "label": "Display", "control": "toggle", "switch": "yesNo" }
65
+ ]
66
+ ]
67
+ },
68
+ {
69
+ "title": "Media & Links",
70
+ "group": "settings",
71
+ "properties": [
72
+ { "name": "media", "label": "Hero Image", "control": "image", "includeFocalPointPicker": true },
73
+ { "name": "cta", "label": "Call to Action", "control": "link" },
74
+ {
75
+ "name": "featuredPost", "label": "Featured Article", "control": "post",
76
+ "postTypes": ["post"], "multiple": false
77
+ }
78
+ ]
79
+ },
80
+ {
81
+ "title": "Design Settings",
82
+ "group": "styles",
83
+ "properties": [
84
+ { "name": "bgColor", "label": "Background", "control": "colorPalette", "options": "select:theme:colors" },
85
+ { "name": "opacity", "label": "Opacity", "control": "range", "min": 0, "max": 100, "step": 10 },
86
+ {
87
+ "name": "align", "label": "Alignment", "control": "select",
88
+ "options": [
89
+ ["left", "Left"],
90
+ ["center", "Center"],
91
+ ["right", "Right"]
92
+ ]
93
+ }
94
+ ]
95
+ }
96
+ ]
97
+ }
98
+ ```
99
+
100
+ ---
101
+
102
+ ## 📋 Control Reference
103
+
104
+ All controls require three properties: `"control"`, `"name"`, and `"label"`.
105
+
106
+ ### Common Properties (Available on All Controls)
107
+
108
+ | Property | Type | Description |
109
+ |:----------------|:---------|:-----------------------------------------------------------------------------------------------|
110
+ | `name` | string | **Required.** The attribute key where the value is stored. |
111
+ | `label` | string | **Required.** Display label shown in the sidebar. |
112
+ | `control` | string | **Required.** The type of control (e.g., `"text"`, `"toggle"`). |
113
+ | `default` | any | Default value when the attribute is not set. |
114
+ | `help` | string | Help text displayed below the control. |
115
+ | `required` | boolean | Whether the field is required. Default: `false`. |
116
+ | `alwaysStore` | boolean | Whether to always store the value even if undefined. Default: `false`. |
117
+ | `condition` | array | Show/hide logic based on other properties (see Conditions section). |
118
+ | `component` | object | Additional props passed to the underlying React component. |
119
+ | `styleProperty` | string | CSS property name to bind this value to (for controls with `string` and `number` value types). |
120
+ | `validator` | function | Custom validation function (TypeScript/React only). |
121
+ | `transformer` | object | Custom value transformation (TypeScript/React only). |
122
+
123
+ ---
124
+
125
+ ## 🎛️ Control Types
126
+
127
+ ### Basic Input Controls
128
+
129
+ #### `text`
130
+ Single-line text input.
131
+
132
+ **Value Type:** `string`
133
+
134
+ ##### Additional Properties
135
+ | Property | Type | Description | Default |
136
+ |:--------------|:---------|:-----------------------------------|:--------|
137
+ | `placeholder` | `string` | Placeholder text shown when empty. | `''` |
138
+
139
+ **Example:**
140
+
141
+ ```json
142
+ {
143
+ "name": "headline",
144
+ "label": "Headline",
145
+ "control": "text",
146
+ "placeholder": "Enter your headline...",
147
+ "default": ""
148
+ }
149
+ ```
150
+
151
+ ---
152
+
153
+ #### `textarea`
154
+ Multi-line text input.
155
+
156
+ **Value Type:** `string`
157
+
158
+ ##### Additional Properties
159
+ | Property | Type | Description | Default |
160
+ |:-----------------|:---------|:----------------------------|:--------|
161
+ | `component.rows` | `number` | Number of visible text rows | `4` |
162
+
163
+ **Example:**
164
+
165
+ ```json
166
+ {
167
+ "name": "description",
168
+ "label": "Description",
169
+ "control": "textarea",
170
+ "component": { "rows": 6 },
171
+ "help": "Enter a detailed description"
172
+ }
173
+ ```
174
+
175
+ ---
176
+
177
+ #### `number`
178
+ Numeric input with optional constraints.
179
+
180
+ **Value Type:** `number`
181
+
182
+ ##### Additional Properties
183
+ | Property | Type | Description | Default |
184
+ |:--------------|:------------------|:------------------------------------------------------|:--------|
185
+ | `min` | `number` | Minimum allowed value | `unset` |
186
+ | `max` | `number` | Maximum allowed value | `unset` |
187
+ | `step` | `number \| 'any'` | Increment step. Use `"any"` to switch to decimal mode | `unset` |
188
+ | `float` | `boolean` | Force decimal mode | `false` |
189
+ | `placeholder` | `string` | Placeholder text | `''` |
190
+
191
+ **Example:**
192
+
193
+ ```json
194
+ {
195
+ "name": "quantity",
196
+ "label": "Quantity",
197
+ "control": "number",
198
+ "min": 1,
199
+ "max": 100,
200
+ "step": 1,
201
+ "default": 10
202
+ }
203
+ ```
204
+
205
+ ---
206
+
207
+ #### `range`
208
+ Slider control for selecting numeric values.
209
+
210
+ **Value Type:** `number`
211
+
212
+ ##### Additional Properties
213
+ | Property | Type | Description | Default |
214
+ |:---------|:---------|:------------------------------------|:--------|
215
+ | `min` | `number` | **Required.** Minimum allowed value | `unset` |
216
+ | `max` | `number` | **Required.** Maximum allowed value | `unset` |
217
+ | `step` | `number` | Increment step | `1` |
218
+
219
+ **Example:**
220
+
221
+ ```json
222
+ {
223
+ "name": "opacity",
224
+ "label": "Opacity",
225
+ "control": "range",
226
+ "min": 0,
227
+ "max": 100,
228
+ "step": 5,
229
+ "default": 100
230
+ }
231
+ ```
232
+
233
+ ---
234
+
235
+ ### Toggle & Choice Controls
236
+
237
+ #### `toggle`
238
+ Boolean on/off switch.
239
+
240
+ **Value Type:** `boolean`
241
+
242
+ ##### Additional Properties
243
+ - `switch` (`string | object`): Switch style configuration.
244
+ - String values: `"yesNo"`, `"showHide"`
245
+ - Object with properties:
246
+ - `type` (`string`): `"yesNo"` or `"showHide"`
247
+ - `small` (`boolean`): Use small toggle style. Default: `false`.
248
+ - `narrow` (`boolean`): Use narrow layout. Default: `false`.
249
+ - `onLabel` (`string`): Custom label for "on" state.
250
+ - `offLabel` (`string`): Custom label for "off" state.
251
+
252
+ **Example:**
253
+
254
+ ```json
255
+ {
256
+ "name": "showBreadcrumbs",
257
+ "label": "Show Breadcrumbs",
258
+ "control": "toggle",
259
+ "switch": "yesNo",
260
+ "default": true
261
+ }
262
+ ```
263
+
264
+ **Custom Labels:**
265
+
266
+ ```json
267
+ {
268
+ "name": "enableFeature",
269
+ "label": "Enable Feature",
270
+ "control": "toggle",
271
+ "switch": {
272
+ "type": "yesNo",
273
+ "onLabel": "Enabled",
274
+ "offLabel": "Disabled"
275
+ }
276
+ }
277
+ ```
278
+
279
+ **Small Toggle:**
280
+
281
+ ```json
282
+ {
283
+ "name": "compactMode",
284
+ "label": "Compact Mode",
285
+ "control": "toggle",
286
+ "switch": {
287
+ "small": true
288
+ }
289
+ }
290
+ ```
291
+
292
+ ---
293
+
294
+ #### `toggleGroup`
295
+ Button group for selecting one value from multiple options.
296
+
297
+ **Value Type:** `string` or `number`
298
+
299
+ ##### Additional Properties
300
+ | Property | Type | Description | Default |
301
+ |:------------|:-----------------------|:---------------------------------------------------------------------------------------------------------------------------------|:-----------|
302
+ | `options` | `array \| string` | **Required.** Array of `[value, label]` tuples or option objects or a [dynamic source string](#4-option-sources-dynamic-options) | `unset` |
303
+ | `clearable` | `boolean` | Allow deselecting all options | `false` |
304
+ | `type` | `"string" \| "number"` | The value type returned by the toggle group | `"string"` |
305
+
306
+ **Example:**
307
+
308
+ ```json
309
+ {
310
+ "name": "alignment",
311
+ "label": "Text Alignment",
312
+ "control": "toggleGroup",
313
+ "options": [
314
+ ["left", "Left"],
315
+ ["center", "Center"],
316
+ ["right", "Right"]
317
+ ],
318
+ "default": "left"
319
+ }
320
+ ```
321
+
322
+ **With Icons:**
323
+
324
+ ```json
325
+ {
326
+ "name": "layout",
327
+ "label": "Layout",
328
+ "control": "toggleGroup",
329
+ "options": [
330
+ ["list", { "text": "List", "icon": "list" }],
331
+ ["grid", { "text": "Grid", "icon": "grid" }]
332
+ ]
333
+ }
334
+ ```
335
+
336
+ ---
337
+
338
+ #### `select`
339
+ Dropdown selection control.
340
+
341
+ **Value Type:** `string` (single) or `string[]` (multiple)
342
+
343
+ ##### Additional Properties
344
+ | Property | Type | Description | Default |
345
+ |:----------------|:------------------|:---------------------------------------------------------------------------------------------------------------------------------|:--------|
346
+ | `options` | `array \| string` | **Required.** Array of `[value, label]` tuples or option objects or a [dynamic source string](#4-option-sources-dynamic-options) | `unset` |
347
+ | `multiple` | `boolean` | Allow selecting multiple values | `false` |
348
+ | `clearable` | `boolean` | Allow clearing the selection (single select only) | `false` |
349
+ | `expandOnFocus` | `boolean` | Auto-expand dropdown on focus (multi-select only) | `false` |
350
+ | `maxLength` | `number` | Maximum number of selections (multi-select only) | `unset` |
351
+
352
+ **Example (Single Select):**
353
+
354
+ ```json
355
+ {
356
+ "name": "viewType",
357
+ "label": "View Type",
358
+ "control": "select",
359
+ "options": [
360
+ ["list", "List"],
361
+ ["gallery", "Gallery"],
362
+ ["grid", "Grid"]
363
+ ],
364
+ "default": "grid"
365
+ }
366
+ ```
367
+
368
+ **Example (Multiple Select):**
369
+
370
+ ```json
371
+ {
372
+ "name": "categories",
373
+ "label": "Categories",
374
+ "control": "select",
375
+ "options": [
376
+ ["tech", "Technology"],
377
+ ["design", "Design"],
378
+ ["business", "Business"]
379
+ ],
380
+ "multiple": true,
381
+ "maxLength": 3
382
+ }
383
+ ```
384
+
385
+ ---
386
+
387
+ #### `radio`
388
+ Radio button group for selecting one option.
389
+
390
+ **Value Type:** `string`
391
+
392
+ ##### Additional Properties
393
+ | Property | Type | Description | Default |
394
+ |:--------------|:------------------|:---------------------------------------------------------------------------------------------------------------------------------|:--------|
395
+ | `options` | `array \| string` | **Required.** Array of `[value, label]` tuples or option objects or a [dynamic source string](#4-option-sources-dynamic-options) | `unset` |
396
+ | `allowCustom` | `boolean` | Allow entering a custom value | `false` |
397
+
398
+ **Example:**
399
+
400
+ ```json
401
+ {
402
+ "name": "dateFormat",
403
+ "label": "Date Format",
404
+ "control": "radio",
405
+ "options": [
406
+ ["none", "None"],
407
+ ["F j, Y", "January 30, 1987"],
408
+ ["m/d/Y", "01/30/1987"]
409
+ ],
410
+ "allowCustom": true,
411
+ "default": "none"
412
+ }
413
+ ```
414
+
415
+ ---
416
+
417
+ #### `colorPalette`
418
+ Color picker with theme palette integration.
419
+
420
+ **Value Type:** `string`
421
+
422
+ ##### Additional Properties
423
+ | Property | Type | Description | Default |
424
+ |:--------------|:------------------|:----------------------------------------------------------------------------------------------------------------------------------------|:--------|
425
+ | `options` | `array \| string` | **Required.** Array of `[color string, label]` tuples or option objects or a [dynamic source string](#4-option-sources-dynamic-options) | `unset` |
426
+ | `allowCustom` | `boolean` | Allow custom color input | `false` |
427
+ | `clearable` | `boolean` | Allow clearing the selection | `false` |
428
+
429
+ **Example:**
430
+
431
+ ```json
432
+ {
433
+ "name": "backgroundColor",
434
+ "label": "Background Color",
435
+ "control": "colorPalette",
436
+ "options": "select:theme:colors",
437
+ "clearable": true
438
+ }
439
+ ```
440
+
441
+ ---
442
+
443
+ ### Media & Relational Controls
444
+
445
+ #### `image`
446
+ Image selector with media library integration.
447
+
448
+ **Value Type:** `object` (ImageData)
449
+
450
+ **Additional Properties:**
451
+ - `includeFocalPointPicker` (boolean): Enable focal point selection. Default: `false`.
452
+ - `storage` (object): Storage configuration for the image control.
453
+
454
+ **Example:**
455
+
456
+ ```json
457
+ {
458
+ "name": "heroImage",
459
+ "label": "Hero Image",
460
+ "control": "image",
461
+ "includeFocalPointPicker": true
462
+ }
463
+ ```
464
+
465
+ ---
466
+
467
+ #### `link`
468
+ Link picker for URLs, pages, posts, etc.
469
+
470
+ **Value Type:** `object`
471
+
472
+ **Additional Properties:**
473
+ - `settings` (boolean | array): Enable link settings (opens in new tab, etc.). Default: `false`.
474
+
475
+ **Example:**
476
+
477
+ ```json
478
+ {
479
+ "name": "ctaLink",
480
+ "label": "Call to Action Link",
481
+ "control": "link",
482
+ "settings": true
483
+ }
484
+ ```
485
+
486
+ ---
487
+
488
+ #### `post`
489
+ Post/page selector with search functionality.
490
+
491
+ **Value Type:** `number` (single) or `number[]` (multiple)
492
+
493
+ **Additional Properties:**
494
+ - `postTypes` (array): Array of post type slugs to include. Default: `["post", "page"]`.
495
+ - `multiple` (boolean): Allow selecting multiple posts. Default: `false`.
496
+ - `placeholder` (string): Placeholder text.
497
+ - `taxonomyQuery` (object): Filter posts by taxonomy terms.
498
+
499
+ **Example (Single Post):**
500
+
501
+ ```json
502
+ {
503
+ "name": "featuredPost",
504
+ "label": "Featured Article",
505
+ "control": "post",
506
+ "postTypes": ["post"],
507
+ "multiple": false
508
+ }
509
+ ```
510
+
511
+ **Example (Multiple Products):**
512
+
513
+ ```json
514
+ {
515
+ "name": "relatedProducts",
516
+ "label": "Related Products",
517
+ "control": "post",
518
+ "postTypes": ["product"],
519
+ "multiple": true,
520
+ "placeholder": "Search for products..."
521
+ }
522
+ ```
523
+
524
+ ---
525
+
526
+ #### `taxonomy`
527
+ Taxonomy selector (categories, tags, custom taxonomies).
528
+
529
+ **Value Type:** `string` (single) or `string[]` (multiple)
530
+
531
+ **Additional Properties:**
532
+ - `multiple` (boolean): Allow selecting multiple taxonomies. Default: `true`.
533
+ - `placeholder` (string): Placeholder text.
534
+ - `visibility` (object): Control taxonomy visibility filters.
535
+ - Properties match WordPress taxonomy visibility settings (e.g., `publicly_queryable`, `show_ui`).
536
+
537
+ **Example:**
538
+
539
+ ```json
540
+ {
541
+ "name": "targetTaxonomies",
542
+ "label": "Target Taxonomies",
543
+ "control": "taxonomy",
544
+ "multiple": true,
545
+ "visibility": {
546
+ "publicly_queryable": true
547
+ }
548
+ }
549
+ ```
550
+
551
+ ---
552
+
553
+ #### `term`
554
+ Taxonomy term selector (specific category/tag picker).
555
+
556
+ **Value Type:** `string` (single) or `string[]` (multiple)
557
+
558
+ **Additional Properties:**
559
+ - `taxonomy` (string): **Required.** The taxonomy slug (e.g., `"category"`, `"post_tag"`).
560
+ - `multiple` (boolean): Allow selecting multiple terms. Default: `true`.
561
+ - `placeholder` (string): Placeholder text.
562
+
563
+ **Example:**
564
+
565
+ ```json
566
+ {
567
+ "name": "productCategories",
568
+ "label": "Product Categories",
569
+ "control": "term",
570
+ "taxonomy": "product_cat",
571
+ "multiple": true
572
+ }
573
+ ```
574
+
575
+ ---
576
+
577
+ #### `user`
578
+ User selector with role filtering.
579
+
580
+ **Value Type:** `number` (single) or `number[]` (multiple)
581
+
582
+ **Additional Properties:**
583
+ - `userRoles` (array): Array of role slugs to filter by (e.g., `["editor", "author"]`).
584
+ - `multiple` (boolean): Allow selecting multiple users. Default: `false`.
585
+ - `placeholder` (string): Placeholder text.
586
+
587
+ **Example (Single User):**
588
+
589
+ ```json
590
+ {
591
+ "name": "author",
592
+ "label": "Content Author",
593
+ "control": "user",
594
+ "userRoles": ["author", "editor"],
595
+ "multiple": false
596
+ }
597
+ ```
598
+
599
+ **Example (Multiple Users):**
600
+
601
+ ```json
602
+ {
603
+ "name": "teamMembers",
604
+ "label": "Team Members",
605
+ "control": "user",
606
+ "multiple": true,
607
+ "placeholder": "Search for users..."
608
+ }
609
+ ```
610
+
611
+ ---
612
+
613
+ ### Structure & Collection Controls
614
+
615
+ #### `group`
616
+ Groups related controls together visually.
617
+
618
+ **Value Type:** `object`
619
+
620
+ **Additional Properties:**
621
+ - `fields` (`array`): **Required.** Array of control definitions.
622
+ - `interface` (`string`): UI style: `"default"` or `"toolsPanel"`. Default: `"default"`.
623
+
624
+ **Example:**
625
+
626
+ ```json
627
+ {
628
+ "name": "spacing",
629
+ "label": "Spacing Settings",
630
+ "control": "group",
631
+ "fields": [
632
+ { "name": "top", "label": "Top", "control": "number", "min": 0 },
633
+ { "name": "bottom", "label": "Bottom", "control": "number", "min": 0 }
634
+ ]
635
+ }
636
+ ```
637
+
638
+ **Tools Panel Example:**
639
+
640
+ ```json
641
+ {
642
+ "name": "dimensions",
643
+ "label": "Dimensions",
644
+ "control": "group",
645
+ "interface": "toolsPanel",
646
+ "fields": [
647
+ { "name": "width", "label": "Width", "control": "number" },
648
+ { "name": "height", "label": "Height", "control": "number" }
649
+ ]
650
+ }
651
+ ```
652
+
653
+ ---
654
+
655
+ #### `list`
656
+ Repeatable list of items (simple strings/numbers or complex objects).
657
+
658
+ **Value Type:** `array`
659
+
660
+ **Additional Properties:**
661
+ - `itemType` (string | array): **Required.**
662
+ - `"string"` for simple string lists
663
+ - `"int"`, `"integer"`, or `"float"` for number lists
664
+ - Array of controls for complex object lists
665
+ - Object with type definitions for flexible items (different item types)
666
+ - `min` (number): Minimum number of items.
667
+ - `max` (number): Maximum number of items.
668
+ - `emptyValue` (any): Value for new items.
669
+ - `sharedProperties` (array): Controls that apply to all flexible items.
670
+ - `listComponent` (object): Props for the list item input component.
671
+
672
+ **Example (Simple String List):**
673
+
674
+ ```json
675
+ {
676
+ "name": "tags",
677
+ "label": "Tags",
678
+ "control": "list",
679
+ "itemType": "string",
680
+ "max": 5
681
+ }
682
+ ```
683
+
684
+ **Example (Number List):**
685
+
686
+ ```json
687
+ {
688
+ "name": "scores",
689
+ "label": "Scores",
690
+ "control": "list",
691
+ "itemType": "float",
692
+ "min": 1,
693
+ "max": 10
694
+ }
695
+ ```
696
+
697
+ **Example (Complex Object List):**
698
+
699
+ ```json
700
+ {
701
+ "name": "teamMembers",
702
+ "label": "Team Members",
703
+ "control": "list",
704
+ "itemType": [
705
+ { "name": "name", "label": "Name", "control": "text" },
706
+ { "name": "role", "label": "Role", "control": "text" },
707
+ { "name": "photo", "label": "Photo", "control": "image" }
708
+ ],
709
+ "min": 1,
710
+ "max": 10
711
+ }
712
+ ```
713
+
714
+ **Example (Flexible Items - Different Types):**
715
+
716
+ ```json
717
+ {
718
+ "name": "contentBlocks",
719
+ "label": "Content Blocks",
720
+ "control": "list",
721
+ "itemType": {
722
+ "text": {
723
+ "label": "Text Block",
724
+ "properties": [
725
+ { "name": "content", "label": "Content", "control": "textarea" }
726
+ ]
727
+ },
728
+ "image": {
729
+ "label": "Image Block",
730
+ "properties": [
731
+ { "name": "image", "label": "Image", "control": "image" },
732
+ { "name": "caption", "label": "Caption", "control": "text" }
733
+ ]
734
+ }
735
+ },
736
+ "sharedProperties": [
737
+ { "name": "title", "label": "Block Title", "control": "text" }
738
+ ]
739
+ }
740
+ ```
741
+
742
+ ---
743
+
744
+ #### `date`
745
+ Date and optional time picker.
746
+
747
+ **Value Type:** `number` (Unix timestamp in milliseconds)
748
+
749
+ **Additional Properties:**
750
+ - `time` (boolean): Include time selection. Default: `false`.
751
+
752
+ **Example:**
753
+
754
+ ```json
755
+ {
756
+ "name": "eventDate",
757
+ "label": "Event Date",
758
+ "control": "date",
759
+ "time": true
760
+ }
761
+ ```
762
+
763
+ ---
764
+
765
+ #### `constant`
766
+ Hidden field with a constant value (not editable by users).
767
+
768
+ **Value Type:** Depends on `type` property
769
+
770
+ **Additional Properties:**
771
+ - `type` (`string`): **Required.** Value type: `"string"`, `"number"`, `"boolean"`, `"array"`, `"object"`.
772
+ - `default` (depends): **Required.** The constant value, must be of the specified type
773
+
774
+ **Example:**
775
+
776
+ ```json
777
+ {
778
+ "name": "blockVersion",
779
+ "label": "Block Version",
780
+ "control": "constant",
781
+ "type": "string",
782
+ "default": "2.0"
783
+ }
784
+ ```
785
+
786
+ ---
787
+
788
+ ## 🏗️ Organization & Logic
789
+
790
+ ### 1. Structure (Panels & Tabs)
791
+
792
+ Wrap your controls in **Panels** to create collapsible sections in the sidebar.
793
+
794
+ **Panel Properties:**
795
+ - `title` (string): **Required.** Panel heading.
796
+ - `properties` (array): **Required.** Array of controls or control rows.
797
+ - `group` (string): Sidebar tab where panel appears. Options:
798
+ - `"settings"` (default) - Settings tab
799
+ - `"styles"` - Styles tab
800
+ - `"advanced"` - Advanced tab
801
+ - `"layered-styles"` - Layered Styles tab
802
+ - Other WordPress panel groups: `"post"`, `"background"`, `"border"`, `"color"`, `"dimensions"`, `"effects"`, `"filter"`, `"list"`, `"position"`, `"typography"`
803
+ - `initialOpen` (boolean): Whether panel starts expanded. Default: `false`.
804
+ - `condition` (array): Show/hide the entire panel conditionally.
805
+ - `position` (number): Panel display order.
806
+ - `raw` (boolean): Render panel without wrapper (advanced).
807
+ - `identifier` (any): Custom identifier for the panel.
808
+
809
+ **Example:**
810
+
811
+ ```json
812
+ {
813
+ "title": "Advanced Options",
814
+ "group": "advanced",
815
+ "initialOpen": false,
816
+ "properties": [
817
+ { "name": "customCSS", "label": "Custom CSS", "control": "textarea" }
818
+ ]
819
+ }
820
+ ```
821
+
822
+ **Tab Structure:**
823
+
824
+ Tabs are differentiated from panels by using `items` instead of `properties`
825
+
826
+ You can also organize panels into tabs:
827
+
828
+ ```json
829
+ {
830
+ "title": "Content Settings",
831
+ "group": "settings",
832
+ "items": [
833
+ {
834
+ "title": "Text",
835
+ "properties": [
836
+ { "name": "heading", "label": "Heading", "control": "text" }
837
+ ]
838
+ },
839
+ {
840
+ "title": "Media",
841
+ "properties": [
842
+ { "name": "image", "label": "Image", "control": "image" }
843
+ ]
844
+ }
845
+ ]
846
+ }
847
+ ```
848
+
849
+ ---
850
+
851
+ ### 2. Layout (Rows)
852
+
853
+ - **Single Control:** One control object per row.
854
+ - **Multiple Controls:** Wrap controls in an array to display them side-by-side.
855
+
856
+ **Example:**
857
+
858
+ ```json
859
+ [
860
+ { "name": "width", "label": "Width", "control": "number" },
861
+ { "name": "height", "label": "Height", "control": "number" }
862
+ ]
863
+ ```
864
+
865
+ ---
866
+
867
+ ### 3. Conditions (Show/Hide Logic)
868
+
869
+ Add a `condition` array to any control or panel to show/hide it based on other property values.
870
+
871
+ **Condition Structure:**
872
+
873
+ ```json
874
+ "condition": [
875
+ { "op": "==", "property": "fieldName", "value": "someValue" }
876
+ ]
877
+ ```
878
+
879
+ **Available Operators:**
880
+ - `==` and `===` - Equals (coercive and non-coercive comparison respectively)
881
+ - `!=` and `!==` - Not equals (coercive and non-coercive comparison respectively)
882
+ - `>`, `<`, `>=`, `<=` - Comparison operators
883
+ - `empty` - Property is empty/undefined/falsy
884
+ - `!empty` - Property has a truthy value
885
+ - `in` - Value is in array or string
886
+ - `!in` - Value is not in array or string
887
+ - `contains` - Array/string contains value
888
+ - `!contains` - Array/string does not contain value
889
+
890
+ **Combiners:**
891
+ - `and` - All conditions must be true (default when multiple conditions in array)
892
+ - `or` - At least one condition must be true
893
+ - `not` - Inverts the result
894
+ - `nand` - Not all conditions are true
895
+ - `nor` - None of the conditions are true
896
+
897
+ **Property Path Syntax:**
898
+ - Simple property: `"propertyName"`
899
+ - Nested property: `"parent.child"`
900
+ - Array index: `"items.0.name"`
901
+ - Relative to parent: `"@parent.siblingProperty"`
902
+ - From root: `"@root.topLevelProperty"`
903
+ - Block attributes: `"@block.attributes.anchor"`, `"@block.name"`, `"@block.className"`
904
+ - Parent block: `"@block.name(1)"` (1 = immediate parent, 2 = grandparent, etc.)
905
+
906
+ **Examples:**
907
+
908
+ ```json
909
+ // Show only if another field is not empty
910
+ "condition": [{ "op": "!empty", "property": "headline" }]
911
+
912
+ // Show only if layout is "grid"
913
+ "condition": [{ "op": "==", "property": "layout", "value": "grid" }]
914
+
915
+ // Multiple conditions (AND logic - all must be true)
916
+ "condition": [
917
+ { "op": "==", "property": "showImage", "value": true },
918
+ { "op": "!empty", "property": "imageUrl" }
919
+ ]
920
+
921
+ // OR logic (at least one must be true)
922
+ "condition": [
923
+ { "op": "or", "conditions": [
924
+ { "op": "==", "property": "type", "value": "featured" },
925
+ { "op": "==", "property": "type", "value": "highlighted" }
926
+ ]}
927
+ ]
928
+
929
+ // Complex nested conditions
930
+ "condition": [
931
+ { "op": "and", "conditions": [
932
+ { "op": "==", "property": "enableFeature", "value": true },
933
+ { "op": "or", "conditions": [
934
+ { "op": "==", "property": "mode", "value": "advanced" },
935
+ { "op": "==", "property": "userRole", "value": "admin" }
936
+ ]}
937
+ ]}
938
+ ]
939
+
940
+ // Check parent block type
941
+ "condition": [
942
+ { "op": "==", "property": "@block.name(1)", "value": "core/group" }
943
+ ]
944
+ ```
945
+
946
+ ---
947
+
948
+ ### 4. Option Sources (Dynamic Options)
949
+
950
+ For `select`, `radio`, `colorPalette`, and `toggleGroup` controls, you can use shorthand strings to load dynamic options:
951
+
952
+ #### Source Syntax
953
+ - The source syntax is based off of the URI syntax and uses custom protocols. The two built-in protocols are `select` and `settings`; however, new ones can be registered via filters.
954
+
955
+ ##### The `select` Protocol
956
+ **Notes:**
957
+ - This protocol is used to extract values from a Redux data store that has a `get` or `resolve` function that takes a `string` and `Record<string, any>` as its parameters.
958
+ - At present, that means using the `plaudit/simple-gutenberg-apis` store.
959
+ - The `plaudit/simple-gutenberg-apis` store provides access to js-defined endpoints that can be sent to the server.
960
+ - While new endpoint definitions can only be registered via JavaScript, they can be referenced from JSON.
961
+ - To reference an endpoint from JSON, use the following syntax: `select://endpoint@store` or, if the endpoint expects the block's attributes, `select://endpoint@store?attributes`
962
+ - To get a list of registered endpoints, open the block editor in your browser and run `wp.data.select('plaudit/simple-gutenberg-apis').registeredEndpoints()` in the browser console
963
+ - To test one of the registered endpoints, run `(await wp.data.select('plaudit/simple-gutenberg-apis').resolve('endpoint.name.here'))` in the same context
964
+
965
+
966
+ ##### The `settings` Protocol
967
+ **Notes:**
968
+ - This protocol is used to get values from the theme.json file
969
+ - The returned values are effected by the current block (this is to allow for block-specific values, most-commonly font sizes and colors)
970
+ - The overall structure is: `settings://dot.separated.path.to.value?<optional parameters>`
971
+ - Example: `settings://plaudit.markerColor`
972
+ - The `value-pattern` parameter can be used to transform the value returned from the theme.json file
973
+ - The pattern MUST contain `$slug` (`$slug` is used to mark where the value returned from theme.json should be inserted)
974
+ - For example, `?value-pattern=var(--wp-preset-color-$slug)` can be used to transform a list of preset colors into valid CSS values
975
+ - If desired, a CSS inherit option can be added to the list by adding `with-inherit` to the parameters list (this will only show up if the value source has been defined as supporting it)
976
+
977
+ **Examples:**
978
+
979
+ ```json
980
+ {
981
+ "name": "textColor",
982
+ "label": "Text Color",
983
+ "control": "colorPalette",
984
+ "options": "settings://color.palette"
985
+ }
986
+ ```
987
+
988
+ ```json
989
+ {
990
+ "name": "dimensions",
991
+ "label": "Aspect Ratio",
992
+ "control": "select",
993
+ "options": "settings://dimensions.aspectRatios"
994
+ }
995
+ ```
996
+
997
+ ---
998
+
999
+ ## 🔧 Advanced Features
1000
+
1001
+ ### Style Properties
1002
+
1003
+ This feature is intended for setting CSS custom variables based on a block's properties.
1004
+
1005
+ You can bind string or number properties directly to CSS properties using the `styleProperty` field. This automatically applies the value as an inline style on the block wrapper.
1006
+
1007
+ **Notes:**
1008
+ - Only works for root-level properties (not those nested in arrays or objects)
1009
+ - The value is not validated for compatibility with the CSS property or CSS syntax in general, so care must be taken to avoid issues
1010
+ - A CSS class `has-{kebab-case-property-name}` is automatically added
1011
+
1012
+ **Example:**
1013
+
1014
+ ```json
1015
+ {
1016
+ "name": "customPadding",
1017
+ "label": "Custom Padding",
1018
+ "control": "text",
1019
+ "styleProperty": "--custom-padding",
1020
+ "placeholder": "e.g., 20px 40px"
1021
+ }
1022
+ ```
1023
+
1024
+ When the user enters `"20px 40px"`, the block wrapper will receive:
1025
+ - `style="--custom-padding: 20px 40px"`
1026
+ - `class="has-custom-padding"`
1027
+
1028
+ ---
1029
+
1030
+ ### Validators & Transformers
1031
+
1032
+ **Note:** These features are only available when using TypeScript/React to define properties programmatically.
1033
+
1034
+ **Validator Function:**
1035
+
1036
+ ```typescript
1037
+ {
1038
+ name: "email",
1039
+ label: "Email Address",
1040
+ control: "text",
1041
+ validator: (value: string | undefined) => {
1042
+ if (!value) return null;
1043
+ return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)
1044
+ ? null
1045
+ : "Please enter a valid email address";
1046
+ }
1047
+ }
1048
+ ```
1049
+
1050
+ **Transformer Object:**
1051
+
1052
+ ```typescript
1053
+ {
1054
+ name: "price",
1055
+ label: "Price",
1056
+ control: "number",
1057
+ transformer: {
1058
+ to: (input: unknown) => {
1059
+ // Convert stored value to display value
1060
+ return typeof input === 'number' ? input / 100 : undefined;
1061
+ },
1062
+ from: (value: number | undefined) => {
1063
+ // Convert display value to stored value (cents)
1064
+ return value !== undefined ? Math.round(value * 100) : undefined;
1065
+ }
1066
+ }
1067
+ }
1068
+ ```
1069
+
1070
+ ---
1071
+
1072
+ ## Tips & Best Practices
1073
+
1074
+ 1. **Add Help Text:** Use the `help` property to guide users, especially for complex controls.
1075
+
1076
+ 2. **Group Related Controls:** Use panels to organize controls logically.
1077
+
1078
+ 3. **Leverage Conditions:** Hide irrelevant controls to keep the UI clean and focused.
1079
+
1080
+ 4. **Validate Inputs:** Use `min`, `max`, `required`, and `step` to prevent invalid data.
1081
+
1082
+ 5. **Use Appropriate Control Types:** Choose the control that best matches your data:
1083
+ - Use `toggle` for boolean flags
1084
+ - Use `toggleGroup` for 2-5 mutually exclusive options
1085
+ - Use `select` for 6+ options or when searchability is needed
1086
+ - Use `radio` when you need to show all options at once
1087
+
1088
+ 6. **Default Values:** Always provide sensible defaults to improve the user experience.
1089
+
1090
+ 7. **Required Fields:** Mark critical fields as `required: true` to ensure data completeness.
1091
+
1092
+ 8. **Placeholder Text:** Use placeholders to show example values or formatting hints.
1093
+
1094
+ 9. **Nested Properties:** Use `group` controls to create logical hierarchies in your data.
1095
+
1096
+ 10. **Performance:** For lists with many items, consider setting `max` limits to prevent performance issues.
1097
+
1098
+ 11. **Accessibility:** Labels are required for all controls to ensure screen reader compatibility.
1099
+
1100
+ 12. **Testing:** Always test your controls with various data states (empty, partial, complete, invalid).