@dotcms/uve 1.5.1-next.1964 → 1.5.1-next.1965

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -32,18 +32,7 @@ With `@dotcms/uve`, framework SDKs are able to:
32
32
  - [`sendMessageToUVE()`](#sendmessagetouvemessage)
33
33
  - [Style Editor](#style-editor)
34
34
  - [What is the Style Editor?](#what-is-the-style-editor)
35
- - [Quick Start](#quick-start)
36
- - [`defineStyleEditorSchema()`](#definestyleeditorschemaform)
37
- - [Field Types](#field-types)
38
- - [`styleEditorField.input()`](#styleeditorfieldinputconfig)
39
- - [`styleEditorField.dropdown()`](#styleeditorfielddropdownconfig)
40
- - [`styleEditorField.radio()`](#styleditorfieldradioconfig)
41
- - [`styleEditorField.checkboxGroup()`](#styleeditorfieldcheckboxgroupconfig)
42
- - [`registerStyleEditorSchemas()`](#registerstyleeditorschemasschemas)
43
- - [`useStyleEditorSchemas()` (React Hook)](#usestyleeditorschemasschemas-react-hook)
44
35
  - [Accessing Style Values](#accessing-style-values)
45
- - [Best Practices](#best-practices)
46
- - [Complete Example](#complete-example)
47
36
  - [Current Capabilities and Limitations](#current-capabilities-and-limitations)
48
37
  - [Troubleshooting](#troubleshooting)
49
38
  - [Common Issues & Solutions](#common-issues--solutions)
@@ -492,15 +481,15 @@ sendMessageToUVE({
492
481
 
493
482
  ### What is the Style Editor?
494
483
 
495
- The Style Editor is a powerful feature that enables content authors and developers to define dynamic, real-time editable properties for contentlets within the Universal Visual Editor (UVE). This allows for live customization of component appearance, layout, typography, colors, and any other configurable aspects without requiring code changes or page reloads.
484
+ The Style Editor is a powerful feature that enables content authors to customize component appearance, layout, typography, colors, and other configurable aspects in real time within the Universal Visual Editor (UVE), without requiring code changes or page reloads.
485
+
486
+ Style editor schemas are configured in the DotCMS admin UI under **Content Types** (in the content type metadata). The SDK fetches schemas automatically — no schema definition code is required in your application.
496
487
 
497
488
  **Key Benefits:**
498
489
 
499
490
  - **Real-Time Visual Editing**: Modify component styles and see changes instantly in the editor
500
491
  - **Content-Specific Customization**: Different content types can have unique style schemas, and the same contentlet could have different styles depending on if it is located in a different container or page
501
- - **Developer-Controlled**: Developers define which properties are editable and how they're presented
502
- - **Flexible Configuration**: Support for text inputs, dropdowns, radio buttons, and checkbox groups
503
- - **Type-Safe**: Full TypeScript support with type inference for option values
492
+ - **Admin-Managed**: Style schemas are defined in the DotCMS admin UI under Content Types and fetched automatically by the SDK
504
493
 
505
494
  **Use Cases:**
506
495
 
@@ -511,568 +500,6 @@ The Style Editor is a powerful feature that enables content authors and develope
511
500
  - Control responsive behavior
512
501
  - Modify animation settings
513
502
 
514
- ### Quick Start
515
-
516
- **1. Install the required packages:**
517
-
518
- ```bash
519
- npm install @dotcms/uve@latest
520
- npm install @dotcms/types@latest --save-dev
521
- ```
522
-
523
- **2. Define a style editor schema:**
524
-
525
- ```typescript
526
- import { defineStyleEditorSchema, styleEditorField } from '@dotcms/uve';
527
-
528
- const mySchema = defineStyleEditorSchema({
529
- contentType: 'BlogPost',
530
- sections: [
531
- {
532
- title: 'Typography',
533
- fields: [
534
- styleEditorField.dropdown({
535
- id: 'font-size',
536
- label: 'Font Size',
537
- options: [
538
- { label: 'Small (14px)', value: '14px' },
539
- { label: 'Medium (16px)', value: '16px' },
540
- { label: 'Large (18px)', value: '18px' }
541
- ]
542
- })
543
- ]
544
- }
545
- ]
546
- });
547
- ```
548
-
549
- **3. Register the schema:**
550
-
551
- **Using React:**
552
-
553
- ```typescript
554
- import { useStyleEditorSchemas } from '@dotcms/react';
555
-
556
- function MyComponent() {
557
- useStyleEditorSchemas([mySchema]);
558
-
559
- return <div>Your component content</div>;
560
- }
561
- ```
562
-
563
- **Using vanilla JavaScript:**
564
-
565
- ```typescript
566
- import { registerStyleEditorSchemas } from '@dotcms/uve';
567
-
568
- registerStyleEditorSchemas([mySchema]);
569
- ```
570
-
571
- ### `defineStyleEditorSchema(form)`
572
-
573
- `defineStyleEditorSchema` creates a normalized style editor schema that UVE can process. It validates your form definition and converts it into the format expected by the Universal Visual Editor.
574
-
575
- | Input | Type | Required | Description |
576
- | ------ | ------------------ | -------- | ------------------------------------------------------ |
577
- | `form` | `StyleEditorForm` | ✅ | The form definition with content type, sections, and fields |
578
-
579
- **Returns:** `StyleEditorFormSchema` - A normalized schema ready for registration with UVE
580
-
581
- #### StyleEditorForm Structure
582
-
583
- ```typescript
584
- interface StyleEditorForm {
585
- contentType: string; // The content type identifier
586
- sections: StyleEditorSection[]; // Array of form sections
587
- }
588
-
589
- interface StyleEditorSection {
590
- title: string; // Section heading displayed in the editor
591
- fields: StyleEditorField[]; // Array of field definitions
592
- }
593
- ```
594
-
595
- #### Usage
596
-
597
- ```typescript
598
- import { defineStyleEditorSchema, styleEditorField } from '@dotcms/uve';
599
-
600
- const schema = defineStyleEditorSchema({
601
- contentType: 'Activity',
602
- sections: [
603
- {
604
- title: 'Typography',
605
- fields: [
606
- styleEditorField.input({
607
- id: 'heading-size',
608
- label: 'Heading Size',
609
- inputType: 'number',
610
- placeholder: '24'
611
- }),
612
- styleEditorField.dropdown({
613
- id: 'font-family',
614
- label: 'Font Family',
615
- options: ['Arial', 'Helvetica', 'Georgia']
616
- })
617
- ]
618
- },
619
- {
620
- title: 'Layout',
621
- fields: [
622
- styleEditorField.radio({
623
- id: 'alignment',
624
- label: 'Text Alignment',
625
- options: ['Left', 'Center', 'Right']
626
- })
627
- ]
628
- }
629
- ]
630
- });
631
- ```
632
-
633
- **⚠️ Important Notes:**
634
-
635
- - Each field must have a unique `id` within the schema
636
- - The `contentType` must match the content type in your dotCMS instance
637
- - Schemas are only processed when UVE is in EDIT mode
638
-
639
- ### Field Types
640
-
641
- The Style Editor supports four field types, each designed for specific use cases. Use the `styleEditorField` factory functions to create type-safe field definitions.
642
-
643
- #### `styleEditorField.input(config)`
644
-
645
- Creates a text or number input field for free-form entry.
646
-
647
- **Configuration:**
648
-
649
- ```typescript
650
- interface StyleEditorInputFieldConfig {
651
- id: string; // Unique identifier
652
- label: string; // Display label
653
- inputType: StyleEditorFieldInputType; // Input type
654
- placeholder?: string; // Optional placeholder text
655
- }
656
- ```
657
-
658
- **Use Cases:**
659
-
660
- - Custom values (e.g., font sizes, margins, colors)
661
- - Numeric settings (e.g., animation duration, opacity)
662
- - Text values (e.g., CSS class names, custom IDs)
663
-
664
- **Examples:**
665
-
666
- ```typescript
667
- // Number input for pixel values
668
- styleEditorField.input({
669
- id: 'padding-top',
670
- label: 'Top Padding (px)',
671
- inputType: 'number',
672
- placeholder: '16'
673
- });
674
-
675
- // Text input for custom CSS
676
- styleEditorField.input({
677
- id: 'custom-class',
678
- label: 'Custom CSS Class',
679
- inputType: 'text',
680
- placeholder: 'my-custom-style'
681
- });
682
-
683
- // Number input with decimal values
684
- styleEditorField.input({
685
- id: 'opacity',
686
- label: 'Opacity',
687
- inputType: 'number',
688
- placeholder: '1.0'
689
- });
690
- ```
691
-
692
- #### `styleEditorField.dropdown(config)`
693
-
694
- Creates a dropdown (select) field with predefined options. Users can select one value from the list.
695
-
696
- **Configuration:**
697
-
698
- ```typescript
699
- interface StyleEditorDropdownField {
700
- id: string; // Unique identifier
701
- label: string; // Display label
702
- options: StyleEditorOption[]; // Array of options
703
- }
704
-
705
- type StyleEditorOption = { label: string; value: string };
706
- ```
707
-
708
- **Use Cases:**
709
-
710
- - Predefined sizes (e.g., small, medium, large)
711
- - Font families or style presets
712
- - Color themes
713
- - Any single-choice selection from a list
714
-
715
- **Examples:**
716
-
717
- ```typescript
718
- // Font size options
719
- const FONT_SIZES = [
720
- { label: 'Extra Small (12px)', value: '12px' },
721
- { label: 'Small (14px)', value: '14px' },
722
- { label: 'Medium (16px)', value: '16px' },
723
- { label: 'Large (18px)', value: '18px' },
724
- { label: 'Extra Large (24px)', value: '24px' }
725
- ];
726
-
727
- styleEditorField.dropdown({
728
- id: 'font-size',
729
- label: 'Font Size',
730
- options: FONT_SIZES
731
- });
732
-
733
- // Theme selection
734
- styleEditorField.dropdown({
735
- id: 'theme',
736
- label: 'Color Theme',
737
- options: [
738
- { label: 'Light Theme', value: 'light' },
739
- { label: 'Dark Theme', value: 'dark' },
740
- { label: 'High Contrast', value: 'high-contrast' }
741
- ]
742
- });
743
- ```
744
-
745
-
746
- #### `styleEditorField.radio(config)`
747
-
748
- Creates a radio button group for single-choice selection. Optionally supports images for visual selection.
749
-
750
- **Configuration:**
751
-
752
- ```typescript
753
- interface StyleEditorRadioField {
754
- id: string; // Unique identifier
755
- label: string; // Display label
756
- options: StyleEditorRadioOption[]; // Array of options
757
- columns?: 1 | 2; // Layout: 1 or 2 columns (default: 1)
758
- }
759
-
760
- type StyleEditorRadioOption = {
761
- label: string;
762
- value: string;
763
- imageURL?: string; // Optional preview image
764
- };
765
- ```
766
-
767
- **Use Cases:**
768
-
769
- - Layout selection with visual previews
770
- - Alignment options (left, center, right)
771
- - Style variants with images
772
- - Any single-choice where visual feedback is helpful
773
-
774
- **Examples:**
775
-
776
- ```typescript
777
- // Simple text options
778
- styleEditorField.radio({
779
- id: 'text-align',
780
- label: 'Text Alignment',
781
- options: [
782
- { label: 'Left', value: 'left' },
783
- { label: 'Center', value: 'center' },
784
- { label: 'Right', value: 'right' },
785
- { label: 'Justify', value: 'justify' }
786
- ]
787
- });
788
-
789
- // Two-column layout with images
790
- const LAYOUT_OPTIONS = [
791
- {
792
- label: 'Left Sidebar',
793
- value: 'left',
794
- imageURL: 'https://example.com/layouts/left-sidebar.png'
795
- },
796
- {
797
- label: 'Right Sidebar',
798
- value: 'right',
799
- imageURL: 'https://example.com/layouts/right-sidebar.png'
800
- },
801
- {
802
- label: 'Full Width',
803
- value: 'full',
804
- imageURL: 'https://example.com/layouts/full-width.png'
805
- },
806
- {
807
- label: 'Split View',
808
- value: 'split',
809
- imageURL: 'https://example.com/layouts/split-view.png'
810
- }
811
- ];
812
-
813
- styleEditorField.radio({
814
- id: 'page-layout',
815
- label: 'Page Layout',
816
- columns: 2, // Display in 2-column grid
817
- options: LAYOUT_OPTIONS
818
- });
819
-
820
- // Font weight selection
821
- styleEditorField.radio({
822
- id: 'font-weight',
823
- label: 'Font Weight',
824
- options: [
825
- { label: 'Normal', value: '400' },
826
- { label: 'Medium', value: '500' },
827
- { label: 'Semi-Bold', value: '600' },
828
- { label: 'Bold', value: '700' }
829
- ]
830
- });
831
- ```
832
-
833
- **💡 Image Guidelines:**
834
-
835
- - Use clear, recognizable preview images
836
- - Recommended size: 200x150px or similar aspect ratio
837
- - Use consistent image dimensions within a radio group
838
- - Images should clearly differentiate between options
839
-
840
- #### `styleEditorField.checkboxGroup(config)`
841
-
842
- Creates a group of checkboxes for multi-selection. Each checkbox returns a boolean value (checked/unchecked).
843
-
844
- **Configuration:**
845
-
846
- ```typescript
847
- interface StyleEditorCheckboxGroupField {
848
- id: string; // Unique identifier for the group
849
- label: string; // Display label for the group
850
- options: StyleEditorCheckboxOption[]; // Array of checkbox options
851
- }
852
-
853
- interface StyleEditorCheckboxOption {
854
- label: string; // Display text for the checkbox
855
- key: string; // Unique identifier (NOT 'value')
856
- }
857
- ```
858
-
859
- **⚠️ Important:** Checkbox options use `key` instead of `value` because the actual value is boolean (true/false).
860
-
861
- **Use Cases:**
862
-
863
- - Text decorations (bold, italic, underline)
864
- - Feature toggles (enable shadows, borders, animations)
865
- - Multiple style attributes
866
- - Any multi-select boolean options
867
-
868
- **Examples:**
869
-
870
- ```typescript
871
- // Typography settings
872
- styleEditorField.checkboxGroup({
873
- id: 'text-style',
874
- label: 'Text Style',
875
- options: [
876
- { label: 'Bold', key: 'bold' },
877
- { label: 'Italic', key: 'italic' },
878
- { label: 'Underline', key: 'underline' },
879
- { label: 'Strikethrough', key: 'strikethrough' }
880
- ]
881
- });
882
-
883
- // Component features
884
- styleEditorField.checkboxGroup({
885
- id: 'component-features',
886
- label: 'Component Features',
887
- options: [
888
- { label: 'Show Shadow', key: 'shadow' },
889
- { label: 'Show Border', key: 'border' },
890
- { label: 'Enable Animation', key: 'animate' },
891
- { label: 'Rounded Corners', key: 'rounded' }
892
- ]
893
- });
894
-
895
- // Responsive behavior
896
- styleEditorField.checkboxGroup({
897
- id: 'responsive',
898
- label: 'Responsive Options',
899
- options: [
900
- { label: 'Hide on Mobile', key: 'hide-mobile' },
901
- { label: 'Hide on Tablet', key: 'hide-tablet' },
902
- { label: 'Full Width on Mobile', key: 'full-width-mobile' }
903
- ]
904
- });
905
- ```
906
-
907
- **Return Value Structure:**
908
-
909
- ```typescript
910
- // Example return value when checkboxes are checked
911
- {
912
- "bold": true,
913
- "italic": false,
914
- "underline": true,
915
- "strikethrough": false
916
- }
917
- ```
918
-
919
- ### `registerStyleEditorSchemas(schemas)`
920
-
921
- `registerStyleEditorSchemas` registers one or more style editor schemas with UVE. This function should be called during your component initialization to make the schemas available in the editor.
922
-
923
- | Input | Type | Required | Description |
924
- | --------- | --------------------------- | -------- | ------------------------------------------------ |
925
- | `schemas` | `StyleEditorFormSchema[]` | ✅ | Array of normalized schemas from `defineStyleEditorSchema` |
926
-
927
- **Returns:** `void`
928
-
929
- **Behavior:**
930
-
931
- - Only registers schemas when UVE is in **EDIT** mode
932
- - Silently returns if UVE is not in EDIT mode
933
- - Validates that each schema has a `contentType` property
934
- - Logs a warning and skips schemas without `contentType`
935
- - Sends validated schemas to UVE via internal messaging
936
-
937
- #### Usage
938
-
939
- ```typescript
940
- import { defineStyleEditorSchema, styleEditorField, registerStyleEditorSchemas } from '@dotcms/uve';
941
-
942
- // Create schemas
943
- const blogSchema = defineStyleEditorSchema({
944
- contentType: 'BlogPost',
945
- sections: [
946
- {
947
- title: 'Typography',
948
- fields: [
949
- styleEditorField.dropdown({
950
- id: 'font-size',
951
- label: 'Font Size',
952
- options: ['14px', '16px', '18px']
953
- })
954
- ]
955
- }
956
- ]
957
- });
958
-
959
- const activitySchema = defineStyleEditorSchema({
960
- contentType: 'Activity',
961
- sections: [
962
- {
963
- title: 'Layout',
964
- fields: [
965
- styleEditorField.radio({
966
- id: 'layout',
967
- label: 'Layout',
968
- options: ['Left', 'Right', 'Center']
969
- })
970
- ]
971
- }
972
- ]
973
- });
974
-
975
- // Register multiple schemas at once
976
- registerStyleEditorSchemas([blogSchema, activitySchema]);
977
- ```
978
-
979
- **⚠️ Important Notes:**
980
-
981
- - Call this function after UVE initialization (`initUVE`)
982
- - Schemas are only processed in EDIT mode
983
- - Missing `contentType` will cause the schema to be skipped
984
- - You can register multiple schemas for different content types
985
-
986
- ### `useStyleEditorSchemas(schemas)` (React Hook)
987
-
988
- **Available in:** `@dotcms/react` package
989
-
990
- `useStyleEditorSchemas` is a React hook that simplifies schema registration by automatically handling the component lifecycle. It registers schemas when the component mounts and re-registers if the schemas array reference changes.
991
-
992
- | Input | Type | Required | Description |
993
- | -------- | --------------------------- | -------- | ------------------------------- |
994
- | `schemas` | `StyleEditorFormSchema[]` | ✅ | Array of normalized form schemas |
995
-
996
- **Returns:** `void`
997
-
998
- **Behavior:**
999
-
1000
- - Registers schemas on component mount
1001
- - Re-registers when the `schemas` array reference changes
1002
- - Internally calls `registerStyleEditorSchemas()`
1003
- - Safe to call in multiple components
1004
-
1005
- #### Usage
1006
-
1007
- ```typescript
1008
- import { useStyleEditorSchemas } from '@dotcms/react';
1009
- import { defineStyleEditorSchema, styleEditorField } from '@dotcms/uve';
1010
-
1011
- function BlogPostEditor() {
1012
- // Define schemas
1013
- const schemas = [
1014
- defineStyleEditorSchema({
1015
- contentType: 'BlogPost',
1016
- sections: [
1017
- {
1018
- title: 'Typography',
1019
- fields: [
1020
- styleEditorField.dropdown({
1021
- id: 'font-size',
1022
- label: 'Font Size',
1023
- options: [
1024
- { label: '14px', value: '14px' },
1025
- { label: '16px', value: '16px' },
1026
- { label: '18px', value: '18px' },
1027
- { label: '24px', value: '24px' }
1028
- ]
1029
- }),
1030
- styleEditorField.radio({
1031
- id: 'font-weight',
1032
- label: 'Font Weight',
1033
- options: [
1034
- { label: 'Normal', value: 'normal' },
1035
- { label: 'Bold', value: 'bold' }
1036
- ]
1037
- })
1038
- ]
1039
- }
1040
- ]
1041
- })
1042
- ];
1043
-
1044
- // Register schemas automatically
1045
- useStyleEditorSchemas(schemas);
1046
-
1047
- return (
1048
- <div>
1049
- <h1>Blog Post Editor</h1>
1050
- {/* Your component content */}
1051
- </div>
1052
- );
1053
- }
1054
- ```
1055
-
1056
- **💡 Performance Tip:** For better performance in components that re-render frequently, you can optionally use `useMemo` to prevent re-creating the schema on every render:
1057
-
1058
- ```typescript
1059
- import { useMemo } from 'react';
1060
-
1061
- function BlogPostEditor() {
1062
- const schemas = useMemo(
1063
- () => [
1064
- defineStyleEditorSchema({
1065
- /* schema definition */
1066
- })
1067
- ],
1068
- [] // Empty deps = create once
1069
- );
1070
-
1071
- useStyleEditorSchemas(schemas);
1072
- return <div>Content</div>;
1073
- }
1074
- ```
1075
-
1076
503
  ### Accessing Style Values
1077
504
 
1078
505
  Style Editor values are managed internally by UVE and passed to your components through the `dotStyleProperties` attribute. This attribute is available in your contentlet component props.
@@ -1183,439 +610,7 @@ function BlogPost(props) {
1183
610
  }
1184
611
  ```
1185
612
 
1186
- **💡 Note:** The `dotStyleProperties` prop is automatically passed to your contentlet components by the framework SDK when UVE is active and style schemas are registered.
1187
-
1188
- ### Best Practices
1189
-
1190
- #### 1. Use Meaningful IDs and Labels
1191
-
1192
- ```typescript
1193
- // ✅ Good: Clear, descriptive IDs and labels
1194
- styleEditorField.dropdown({
1195
- id: 'heading-font-size',
1196
- label: 'Heading Font Size',
1197
- options: [
1198
- { label: 'Small (18px)', value: '18px' },
1199
- { label: 'Medium (24px)', value: '24px' },
1200
- { label: 'Large (32px)', value: '32px' }
1201
- ]
1202
- });
1203
-
1204
- // ❌ Bad: Vague IDs and labels
1205
- styleEditorField.dropdown({
1206
- id: 'size',
1207
- label: 'Size',
1208
- options: ['18px', '24px', '32px']
1209
- });
1210
- ```
1211
-
1212
- #### 2. Group Related Fields in Sections
1213
-
1214
- ```typescript
1215
- // ✅ Good: Logical grouping by functionality
1216
- defineStyleEditorSchema({
1217
- contentType: 'BlogPost',
1218
- sections: [
1219
- {
1220
- title: 'Typography',
1221
- fields: [
1222
- /* font-related fields */
1223
- ]
1224
- },
1225
- {
1226
- title: 'Layout',
1227
- fields: [
1228
- /* layout-related fields */
1229
- ]
1230
- },
1231
- {
1232
- title: 'Colors',
1233
- fields: [
1234
- /* color-related fields */
1235
- ]
1236
- }
1237
- ]
1238
- });
1239
-
1240
- // ❌ Bad: All fields in one section
1241
- defineStyleEditorSchema({
1242
- contentType: 'BlogPost',
1243
- sections: [
1244
- {
1245
- title: 'Settings',
1246
- fields: [
1247
- /* all fields mixed together */
1248
- ]
1249
- }
1250
- ]
1251
- });
1252
- ```
1253
-
1254
- #### 3. Provide Clear Option Labels
1255
-
1256
- ```typescript
1257
- // ✅ Good: Descriptive labels with context
1258
- styleEditorField.dropdown({
1259
- id: 'font-size',
1260
- label: 'Font Size',
1261
- options: [
1262
- { label: 'Extra Small (12px)', value: '12px' },
1263
- { label: 'Small (14px)', value: '14px' },
1264
- { label: 'Medium (16px)', value: '16px' },
1265
- { label: 'Large (18px)', value: '18px' }
1266
- ]
1267
- });
1268
-
1269
- // ❌ Bad: Unclear labels
1270
- styleEditorField.dropdown({
1271
- id: 'font-size',
1272
- label: 'Font Size',
1273
- options: ['XS', 'S', 'M', 'L']
1274
- });
1275
- ```
1276
-
1277
- #### 4. Use Appropriate Field Types
1278
-
1279
- ```typescript
1280
- // ✅ Good: Radio with images for visual layouts
1281
- styleEditorField.radio({
1282
- id: 'page-layout',
1283
- label: 'Layout',
1284
- columns: 2,
1285
- options: [
1286
- { label: 'Left', value: 'left', imageURL: '...' },
1287
- { label: 'Right', value: 'right', imageURL: '...' }
1288
- ]
1289
- });
1290
-
1291
- // ✅ Good: Dropdown for text-only options
1292
- styleEditorField.dropdown({
1293
- id: 'font-family',
1294
- label: 'Font',
1295
- options: ['Arial', 'Georgia', 'Verdana']
1296
- });
1297
-
1298
- // ✅ Good: Checkbox group for boolean flags
1299
- styleEditorField.checkboxGroup({
1300
- id: 'text-decorations',
1301
- label: 'Text Decorations',
1302
- options: [
1303
- { label: 'Bold', key: 'bold' },
1304
- { label: 'Italic', key: 'italic' }
1305
- ]
1306
- });
1307
- ```
1308
-
1309
- #### 5. Validate Content Type Matching
1310
-
1311
- ```typescript
1312
- // ✅ Good: Content type matches your dotCMS content type
1313
- defineStyleEditorSchema({
1314
- contentType: 'BlogPost', // Matches content type in dotCMS
1315
- sections: [
1316
- /* ... */
1317
- ]
1318
- });
1319
-
1320
- // ❌ Bad: Typo or mismatch
1321
- defineStyleEditorSchema({
1322
- contentType: 'blog-post', // Won't match 'BlogPost' in dotCMS
1323
- sections: [
1324
- /* ... */
1325
- ]
1326
- });
1327
- ```
1328
-
1329
- #### 6. Provide Sensible Defaults
1330
-
1331
- When using style properties, always provide fallback defaults:
1332
-
1333
- ```typescript
1334
- // ✅ Good: Fallback values prevent errors
1335
- const fontSize = dotStyleProperties?.['font-size'] || '16px';
1336
- const layout = dotStyleProperties?.layout || 'default';
1337
- const textStyles = dotStyleProperties?.['text-style'] || {};
1338
-
1339
- // ❌ Bad: No fallbacks (could cause errors)
1340
- const fontSize = dotStyleProperties?.['font-size'];
1341
- const layout = dotStyleProperties?.layout;
1342
- ```
1343
-
1344
- ### Complete Example
1345
-
1346
- Here's a comprehensive example demonstrating all Style Editor features:
1347
-
1348
- ```typescript
1349
- import { useStyleEditorSchemas } from '@dotcms/react';
1350
- import { defineStyleEditorSchema, styleEditorField } from '@dotcms/uve';
1351
-
1352
- export function BlogPostStyleEditor() {
1353
- // Define option constants
1354
- const FONT_SIZES = [
1355
- { label: 'Extra Small (12px)', value: '12px' },
1356
- { label: 'Small (14px)', value: '14px' },
1357
- { label: 'Medium (16px)', value: '16px' },
1358
- { label: 'Large (18px)', value: '18px' },
1359
- { label: 'Extra Large (24px)', value: '24px' },
1360
- { label: 'Huge (32px)', value: '32px' }
1361
- ];
1362
-
1363
- const FONT_FAMILIES = [
1364
- { label: 'Arial', value: 'arial' },
1365
- { label: 'Georgia', value: 'georgia' },
1366
- { label: 'Helvetica', value: 'helvetica' },
1367
- { label: 'Times New Roman', value: 'times' },
1368
- { label: 'Verdana', value: 'verdana' },
1369
- { label: 'Courier New', value: 'courier' }
1370
- ];
1371
-
1372
- const LAYOUT_OPTIONS = [
1373
- {
1374
- label: 'Left Sidebar',
1375
- value: 'sidebar-left',
1376
- imageURL: 'https://example.com/layouts/sidebar-left.png'
1377
- },
1378
- {
1379
- label: 'Right Sidebar',
1380
- value: 'sidebar-right',
1381
- imageURL: 'https://example.com/layouts/sidebar-right.png'
1382
- },
1383
- {
1384
- label: 'Full Width',
1385
- value: 'full-width',
1386
- imageURL: 'https://example.com/layouts/full-width.png'
1387
- },
1388
- {
1389
- label: 'Centered',
1390
- value: 'centered',
1391
- imageURL: 'https://example.com/layouts/centered.png'
1392
- }
1393
- ];
1394
-
1395
- const COLOR_THEMES = [
1396
- { label: 'Light Theme', value: 'light' },
1397
- { label: 'Dark Theme', value: 'dark' },
1398
- { label: 'High Contrast', value: 'high-contrast' },
1399
- { label: 'Sepia', value: 'sepia' }
1400
- ];
1401
-
1402
- // Define schema (optionally use useMemo to prevent re-creation on every render)
1403
- const schemas = [
1404
- defineStyleEditorSchema({
1405
- contentType: 'BlogPost',
1406
- sections: [
1407
- {
1408
- title: 'Typography',
1409
- fields: [
1410
- styleEditorField.dropdown({
1411
- id: 'heading-font-size',
1412
- label: 'Heading Font Size',
1413
- options: FONT_SIZES
1414
- }),
1415
- styleEditorField.dropdown({
1416
- id: 'body-font-size',
1417
- label: 'Body Font Size',
1418
- options: FONT_SIZES.slice(0, 4) // Only smaller sizes
1419
- }),
1420
- styleEditorField.dropdown({
1421
- id: 'font-family',
1422
- label: 'Font Family',
1423
- options: FONT_FAMILIES
1424
- }),
1425
- styleEditorField.input({
1426
- id: 'line-height',
1427
- label: 'Line Height',
1428
- inputType: 'number',
1429
- placeholder: '1.5'
1430
- }),
1431
- styleEditorField.checkboxGroup({
1432
- id: 'text-style',
1433
- label: 'Text Style',
1434
- options: [
1435
- { label: 'Bold Headings', key: 'bold-headings' },
1436
- { label: 'Italic Quotes', key: 'italic-quotes' },
1437
- { label: 'Underline Links', key: 'underline-links' }
1438
- ]
1439
- })
1440
- ]
1441
- },
1442
- {
1443
- title: 'Layout',
1444
- fields: [
1445
- styleEditorField.radio({
1446
- id: 'page-layout',
1447
- label: 'Page Layout',
1448
- columns: 2,
1449
- options: LAYOUT_OPTIONS
1450
- }),
1451
- styleEditorField.radio({
1452
- id: 'content-width',
1453
- label: 'Content Width',
1454
- options: [
1455
- { label: 'Narrow (800px)', value: '800px' },
1456
- { label: 'Medium (1000px)', value: '1000px' },
1457
- { label: 'Wide (1200px)', value: '1200px' },
1458
- { label: 'Extra Wide (1400px)', value: '1400px' }
1459
- ]
1460
- }),
1461
- styleEditorField.input({
1462
- id: 'section-spacing',
1463
- label: 'Section Spacing (px)',
1464
- inputType: 'number',
1465
- placeholder: '40'
1466
- })
1467
- ]
1468
- },
1469
- {
1470
- title: 'Colors & Theme',
1471
- fields: [
1472
- styleEditorField.dropdown({
1473
- id: 'color-theme',
1474
- label: 'Color Theme',
1475
- options: COLOR_THEMES
1476
- }),
1477
- styleEditorField.input({
1478
- id: 'primary-color',
1479
- label: 'Primary Color',
1480
- inputType: 'text',
1481
- placeholder: '#007bff'
1482
- }),
1483
- styleEditorField.input({
1484
- id: 'secondary-color',
1485
- label: 'Secondary Color',
1486
- inputType: 'text',
1487
- placeholder: '#6c757d'
1488
- }),
1489
- styleEditorField.input({
1490
- id: 'background-color',
1491
- label: 'Background Color',
1492
- inputType: 'text',
1493
- placeholder: '#ffffff'
1494
- })
1495
- ]
1496
- },
1497
- {
1498
- title: 'Component Features',
1499
- fields: [
1500
- styleEditorField.checkboxGroup({
1501
- id: 'features',
1502
- label: 'Enable Features',
1503
- options: [
1504
- { label: 'Drop Shadow', key: 'shadow' },
1505
- { label: 'Border', key: 'border' },
1506
- { label: 'Rounded Corners', key: 'rounded' },
1507
- { label: 'Smooth Animations', key: 'animate' },
1508
- { label: 'Hover Effects', key: 'hover-effects' }
1509
- ]
1510
- }),
1511
- styleEditorField.checkboxGroup({
1512
- id: 'responsive',
1513
- label: 'Responsive Options',
1514
- options: [
1515
- { label: 'Hide on Mobile', key: 'hide-mobile' },
1516
- { label: 'Stack on Tablet', key: 'stack-tablet' },
1517
- {
1518
- label: 'Full Width on Mobile',
1519
- key: 'full-width-mobile'
1520
- }
1521
- ]
1522
- })
1523
- ]
1524
- }
1525
- ]
1526
- })
1527
- ];
1528
-
1529
- // Register schemas with UVE
1530
- useStyleEditorSchemas(schemas);
1531
-
1532
- return (
1533
- <div>
1534
- <h1>Blog Post Style Editor</h1>
1535
- <p>Style editor schema is registered and available in UVE edit mode.</p>
1536
- </div>
1537
- );
1538
- }
1539
-
1540
- // Example: Using style properties in a component
1541
- export function BlogPostRenderer(props) {
1542
- const { title, body, dotStyleProperties } = props;
1543
-
1544
- // Extract style values with defaults
1545
- const headingSize = dotStyleProperties?.['heading-font-size'] || '24px';
1546
- const bodySize = dotStyleProperties?.['body-font-size'] || '16px';
1547
- const fontFamily = dotStyleProperties?.['font-family'] || 'arial';
1548
- const lineHeight = dotStyleProperties?.['line-height'] || '1.5';
1549
- const layout = dotStyleProperties?.['page-layout'] || 'full-width';
1550
- const contentWidth = dotStyleProperties?.['content-width'] || '1000px';
1551
- const sectionSpacing = dotStyleProperties?.['section-spacing'] || 40;
1552
- const theme = dotStyleProperties?.['color-theme'] || 'light';
1553
- const primaryColor = dotStyleProperties?.['primary-color'] || '#007bff';
1554
- const backgroundColor = dotStyleProperties?.['background-color'] || '#ffffff';
1555
-
1556
- // Extract checkbox group values
1557
- const textStyle = dotStyleProperties?.['text-style'] || {};
1558
- const features = dotStyleProperties?.features || {};
1559
- const responsive = dotStyleProperties?.responsive || {};
1560
-
1561
- // Build CSS classes based on values
1562
- const containerClasses = [
1563
- `layout-${layout}`,
1564
- `theme-${theme}`,
1565
- features.shadow ? 'has-shadow' : '',
1566
- features.border ? 'has-border' : '',
1567
- features.rounded ? 'has-rounded' : '',
1568
- features.animate ? 'has-animations' : '',
1569
- responsive['hide-mobile'] ? 'hide-mobile' : '',
1570
- responsive['stack-tablet'] ? 'stack-tablet' : ''
1571
- ]
1572
- .filter(Boolean)
1573
- .join(' ');
1574
-
1575
- return (
1576
- <div
1577
- className={containerClasses}
1578
- style={{
1579
- fontFamily,
1580
- lineHeight,
1581
- backgroundColor,
1582
- maxWidth: contentWidth,
1583
- paddingTop: `${sectionSpacing}px`,
1584
- paddingBottom: `${sectionSpacing}px`
1585
- }}
1586
- >
1587
- <h1
1588
- style={{
1589
- fontSize: headingSize,
1590
- fontWeight: textStyle['bold-headings'] ? 'bold' : 'normal',
1591
- color: primaryColor
1592
- }}
1593
- >
1594
- {title}
1595
- </h1>
1596
-
1597
- <div
1598
- style={{
1599
- fontSize: bodySize
1600
- }}
1601
- >
1602
- {body}
1603
- </div>
1604
- </div>
1605
- );
1606
- }
1607
- ```
1608
-
1609
- **This example demonstrates:**
1610
-
1611
- - ✅ Organized option constants
1612
- - ✅ Logical section grouping (Typography, Layout, Colors, Features)
1613
- - ✅ All four field types (input, dropdown, radio, checkboxGroup)
1614
- - ✅ Visual layout selection with images
1615
- - ✅ Checkbox groups for boolean flags
1616
- - ✅ Clear, descriptive labels
1617
- - ✅ Safe value extraction with defaults using `dotStyleProperties`
1618
- - ✅ Dynamic styling based on style values
613
+ **💡 Note:** The `dotStyleProperties` prop is automatically passed to your contentlet components by the framework SDK when UVE is active.
1619
614
 
1620
615
  ### Current Capabilities and Limitations:
1621
616