rw-elements-tools 1.2.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.
- package/LICENSE +22 -0
- package/README.md +1333 -0
- package/bin/cli.js +213 -0
- package/build-properties.js +654 -0
- package/build-shared-hooks.js +253 -0
- package/config.js +148 -0
- package/controls/Animations/AnimationEffects.js +111 -0
- package/controls/Animations/AnimationSettings.js +437 -0
- package/controls/Animations/Reveals.js +168 -0
- package/controls/Animations/ScrollAnimation_Opacity.js +15 -0
- package/controls/Animations/ScrollAnimation_Rotate.js +17 -0
- package/controls/Animations/ScrollAnimation_Scale.js +18 -0
- package/controls/Animations/ScrollAnimation_Translate.js +31 -0
- package/controls/Background/Background.js +66 -0
- package/controls/Background/BackgroundButton.js +69 -0
- package/controls/Background/BackgroundColor.js +28 -0
- package/controls/Background/BackgroundContainer.js +73 -0
- package/controls/Background/BackgroundGradient.js +149 -0
- package/controls/Background/BackgroundImage.js +108 -0
- package/controls/Background/BackgroundOnlyColor.js +53 -0
- package/controls/Background/BackgroundTransparent.js +66 -0
- package/controls/Background/BackgroundVideo.js +9 -0
- package/controls/Background/Color.js +52 -0
- package/controls/Background/Gradient.js +263 -0
- package/controls/Background/GradientContainer.js +263 -0
- package/controls/Background/Image.js +269 -0
- package/controls/Background/Image_CMS.js +305 -0
- package/controls/Background/SVG.js +235 -0
- package/controls/Background/Video.js +29 -0
- package/controls/Borders/Border.js +114 -0
- package/controls/Borders/BorderColor.js +25 -0
- package/controls/Borders/BorderRadius.js +19 -0
- package/controls/Borders/BorderStyle.js +26 -0
- package/controls/Borders/BorderWidth.js +20 -0
- package/controls/Borders/Borders.js +69 -0
- package/controls/Borders/BordersContainer.js +90 -0
- package/controls/Borders/BordersInput.js +107 -0
- package/controls/Borders/Outline.js +100 -0
- package/controls/Borders/OutlineColor.js +25 -0
- package/controls/Borders/OutlineOffset.js +13 -0
- package/controls/Borders/OutlineStyle.js +26 -0
- package/controls/Borders/OutlineWidth.js +13 -0
- package/controls/Effects/BackdropBlur.js +11 -0
- package/controls/Effects/Blur.js +11 -0
- package/controls/Effects/BoxShadow.js +15 -0
- package/controls/Effects/Brightness.js +11 -0
- package/controls/Effects/DropShadow.js +14 -0
- package/controls/Effects/Effects.js +71 -0
- package/controls/Effects/Filters.js +114 -0
- package/controls/Effects/Opacity.js +14 -0
- package/controls/Effects/Saturate.js +11 -0
- package/controls/Layout/AspectRatio.js +53 -0
- package/controls/Layout/Container.js +24 -0
- package/controls/Layout/Hidden.js +9 -0
- package/controls/Layout/Inset.js +15 -0
- package/controls/Layout/Isolation.js +25 -0
- package/controls/Layout/Layout.js +38 -0
- package/controls/Layout/Overflow.js +33 -0
- package/controls/Layout/Position.js +37 -0
- package/controls/Layout/TopRightBottomLeft.js +90 -0
- package/controls/Layout/Visibility.js +25 -0
- package/controls/Layout/ZIndex.js +36 -0
- package/controls/Overlay/Color.js +52 -0
- package/controls/Overlay/Gradient.js +298 -0
- package/controls/Overlay/Image.js +226 -0
- package/controls/Overlay/Overlay.js +66 -0
- package/controls/Sizing/Height.js +18 -0
- package/controls/Sizing/MaxHeight.js +17 -0
- package/controls/Sizing/MaxWidth.js +17 -0
- package/controls/Sizing/MinHeight.js +18 -0
- package/controls/Sizing/MinWidth.js +18 -0
- package/controls/Sizing/Sizing.js +66 -0
- package/controls/Sizing/SizingContainer.js +122 -0
- package/controls/Sizing/SizingImage.js +75 -0
- package/controls/Sizing/SizingInput.js +71 -0
- package/controls/Sizing/SizingSVG.js +74 -0
- package/controls/Sizing/Width.js +18 -0
- package/controls/Spacing/Margin.js +17 -0
- package/controls/Spacing/Padding.js +17 -0
- package/controls/Spacing/Spacing.js +23 -0
- package/controls/Spacing/SpacingButton.js +42 -0
- package/controls/Spacing/SpacingContainer.js +32 -0
- package/controls/Spacing/SpacingInput.js +42 -0
- package/controls/Transforms/Rotate.js +13 -0
- package/controls/Transforms/Scale.js +13 -0
- package/controls/Transforms/Skew.js +25 -0
- package/controls/Transforms/TransformOrigin.js +12 -0
- package/controls/Transforms/Transforms.js +98 -0
- package/controls/Transforms/Translate.js +26 -0
- package/controls/Transitions/Delay.js +13 -0
- package/controls/Transitions/Duration.js +13 -0
- package/controls/Transitions/Property.js +42 -0
- package/controls/Transitions/TimingFunction.js +44 -0
- package/controls/Transitions/Transitions.js +20 -0
- package/controls/alignment/AlignContent.js +48 -0
- package/controls/alignment/AlignItems.js +64 -0
- package/controls/alignment/AlignSelf.js +34 -0
- package/controls/alignment/JustifyContent.js +44 -0
- package/controls/alignment/JustifyItems.js +32 -0
- package/controls/alignment/JustifySelf.js +34 -0
- package/controls/core/CSSClasses.js +11 -0
- package/controls/core/ControlType.js +25 -0
- package/controls/core/HTMLTag.js +80 -0
- package/controls/core/HoverGroup.js +38 -0
- package/controls/core/ID.js +12 -0
- package/controls/core/Image.js +95 -0
- package/controls/core/MenuItem.js +187 -0
- package/controls/core/ObjectFit.js +32 -0
- package/controls/core/ObjectPosition.js +65 -0
- package/controls/grid-flex/ActAsGridOrFlexItem.js +54 -0
- package/controls/grid-flex/ColEnd.js +28 -0
- package/controls/grid-flex/ColStart.js +27 -0
- package/controls/grid-flex/Columns.js +38 -0
- package/controls/grid-flex/FlexDirection.js +27 -0
- package/controls/grid-flex/FlexItem.js +106 -0
- package/controls/grid-flex/GridItem.js +41 -0
- package/controls/grid-flex/Order.js +45 -0
- package/controls/grid-flex/RowEnd.js +28 -0
- package/controls/grid-flex/RowStart.js +27 -0
- package/controls/grid-flex/Rows.js +38 -0
- package/controls/index.js +187 -0
- package/controls/interactive/ButtonFontAndTextStyles.js +208 -0
- package/controls/interactive/Filter.js +54 -0
- package/controls/interactive/InputFontAndTextStyles.js +156 -0
- package/controls/interactive/Link.js +13 -0
- package/controls/typography/HeadingColor.js +112 -0
- package/controls/typography/TextColor.js +51 -0
- package/controls/typography/TextDecoration.js +99 -0
- package/controls/typography/TextFontsAndTextStyles.js +243 -0
- package/controls/typography/TextSimple.js +55 -0
- package/controls/typography/TextStyles.js +55 -0
- package/controls/typography/Typography.js +13 -0
- package/index.js +19 -0
- package/package.json +55 -0
- package/properties/BackgroundType.js +18 -0
- package/properties/ButtonSize.js +19 -0
- package/properties/ContainerHeights.js +23 -0
- package/properties/ContainerWidths.js +27 -0
- package/properties/FontWeight.js +16 -0
- package/properties/GradientDirection.js +39 -0
- package/properties/LetterSpacing.js +13 -0
- package/properties/LineHeight.js +13 -0
- package/properties/RevealAnimations.js +12 -0
- package/properties/Slider.js +10 -0
- package/properties/TextAlign.js +23 -0
- package/properties/TransformOrigins.js +43 -0
- package/properties/TransitionNames.js +20 -0
- package/properties/index.js +13 -0
- package/shared-hooks/animations/globalAnimations.js +141 -0
- package/shared-hooks/animations/globalReveal.js +48 -0
- package/shared-hooks/background/globalBackground.js +306 -0
- package/shared-hooks/background/globalBgImageFetchPriority.js +34 -0
- package/shared-hooks/borders/globalBorders.js +85 -0
- package/shared-hooks/borders/globalOutline.js +39 -0
- package/shared-hooks/core/addPrefixToTailwindClasses.js +24 -0
- package/shared-hooks/core/advancedClasses.js +5 -0
- package/shared-hooks/core/classnames.js +92 -0
- package/shared-hooks/core/getHoverPrefix.js +21 -0
- package/shared-hooks/core/globalHTMLTag.js +17 -0
- package/shared-hooks/core/injectPrefixOnDarkModeColors.js +6 -0
- package/shared-hooks/effects/globalEffects.js +45 -0
- package/shared-hooks/effects/globalFilters.js +80 -0
- package/shared-hooks/effects/globalOverlay.js +166 -0
- package/shared-hooks/interactive/globalFilter.js +24 -0
- package/shared-hooks/interactive/globalLink.js +23 -0
- package/shared-hooks/layout/globalActAsGridOrFlexItem.js +66 -0
- package/shared-hooks/layout/globalLayout.js +50 -0
- package/shared-hooks/navigation/globalMenuItem.js +35 -0
- package/shared-hooks/navigation/globalNavItems.js +60 -0
- package/shared-hooks/navigation/globalNavTitle.js +23 -0
- package/shared-hooks/sizing/aspectRatioClasses.js +20 -0
- package/shared-hooks/sizing/globalSizing.js +19 -0
- package/shared-hooks/sizing/globalSizingContainer.js +40 -0
- package/shared-hooks/sizing/objectClasses.js +9 -0
- package/shared-hooks/spacing/globalSpacing.js +13 -0
- package/shared-hooks/spacing/globalSpacingMargin.js +11 -0
- package/shared-hooks/spacing/globalSpacingPadding.js +11 -0
- package/shared-hooks/transforms/globalTransforms.js +78 -0
- package/shared-hooks/transitions/getAlpineTransitionAttributesDesktop.js +111 -0
- package/shared-hooks/transitions/getAlpineTransitionAttributesMobile.js +110 -0
- package/shared-hooks/transitions/globalTransitions.js +48 -0
- package/shared-hooks/typography/globalButtonFontAndTextStyles.js +65 -0
- package/shared-hooks/typography/globalHeadingColor.js +69 -0
- package/shared-hooks/typography/globalInputFontAndTextStyles.js +40 -0
- package/shared-hooks/typography/globalTextFontsAndTextStyles.js +47 -0
|
@@ -0,0 +1,654 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Properties Build Script
|
|
3
|
+
*
|
|
4
|
+
* Processes properties.config.json files and generates properties.json files
|
|
5
|
+
* for RapidWeaver element components.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { sync as globSync } from "glob";
|
|
9
|
+
import fs from "fs/promises";
|
|
10
|
+
import path from "path";
|
|
11
|
+
import { fileURLToPath } from "url";
|
|
12
|
+
import * as Controls from "./controls/index.js";
|
|
13
|
+
import * as Properties from "./properties/index.js";
|
|
14
|
+
import pkg from "lodash";
|
|
15
|
+
const { cloneDeep } = pkg;
|
|
16
|
+
|
|
17
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
18
|
+
const __dirname = path.dirname(__filename);
|
|
19
|
+
|
|
20
|
+
// =============================================================================
|
|
21
|
+
// CONSTANTS
|
|
22
|
+
// =============================================================================
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* List of theme-related property keys that can be overridden in config files.
|
|
26
|
+
* These are extracted from the property config and applied to the control.
|
|
27
|
+
*/
|
|
28
|
+
const THEME_PROPERTY_KEYS = [
|
|
29
|
+
"themeColor",
|
|
30
|
+
"themeFont",
|
|
31
|
+
"themeBorderRadius",
|
|
32
|
+
"themeBorderWidth",
|
|
33
|
+
"themeSpacing",
|
|
34
|
+
"themeShadow",
|
|
35
|
+
"themeTextStyle",
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Controls automatically injected into the Advanced group.
|
|
40
|
+
*/
|
|
41
|
+
const ADVANCED_GROUP_CONTROLS = [Controls.CSSClasses, Controls.ID];
|
|
42
|
+
|
|
43
|
+
// =============================================================================
|
|
44
|
+
// UTILITY FUNCTIONS
|
|
45
|
+
// =============================================================================
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Capitalizes the first character of a string.
|
|
49
|
+
* @param {string} str - The string to capitalize.
|
|
50
|
+
* @returns {string} The string with first character capitalized.
|
|
51
|
+
*/
|
|
52
|
+
function capitalize(str) {
|
|
53
|
+
if (!str) return str;
|
|
54
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Lowercases the first character of a string.
|
|
59
|
+
* @param {string} str - The string to lowercase.
|
|
60
|
+
* @returns {string} The string with first character lowercased.
|
|
61
|
+
*/
|
|
62
|
+
function lowercaseFirst(str) {
|
|
63
|
+
if (!str) return str;
|
|
64
|
+
return str.charAt(0).toLowerCase() + str.slice(1);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Normalizes a control to always be an array.
|
|
69
|
+
* @param {Object|Array} control - A control object or array of controls.
|
|
70
|
+
* @returns {Array} Array of control objects.
|
|
71
|
+
*/
|
|
72
|
+
function normalizeToArray(control) {
|
|
73
|
+
return Array.isArray(control) ? control : [control];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Filters an object to only include entries with non-null values.
|
|
78
|
+
* @param {Object} obj - The object to filter.
|
|
79
|
+
* @returns {Object} Object with only non-null values.
|
|
80
|
+
*/
|
|
81
|
+
function filterNullValues(obj) {
|
|
82
|
+
return Object.fromEntries(
|
|
83
|
+
Object.entries(obj).filter(([_, value]) => value != null)
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// =============================================================================
|
|
88
|
+
// ID TRANSFORMATION
|
|
89
|
+
// =============================================================================
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Transforms an ID template by replacing {{value}} with the control's ID.
|
|
93
|
+
* Handles camelCase conventions:
|
|
94
|
+
* - If {{value}} is at the start, keeps original casing
|
|
95
|
+
* - If {{value}} is in the middle, capitalizes the control ID
|
|
96
|
+
* - Parts after {{value}} are capitalized
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* // "prefix{{value}}Suffix" with controlId "margin" => "prefixMarginSuffix"
|
|
100
|
+
* // "{{value}}Suffix" with controlId "margin" => "marginSuffix"
|
|
101
|
+
*
|
|
102
|
+
* @param {string} template - The ID template containing {{value}}.
|
|
103
|
+
* @param {string} controlId - The control's original ID.
|
|
104
|
+
* @returns {string} The transformed ID.
|
|
105
|
+
*/
|
|
106
|
+
function transformIdTemplate(template, controlId) {
|
|
107
|
+
if (!template?.includes("{{value}}") || !controlId) {
|
|
108
|
+
return controlId;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const placeholder = "{{value}}";
|
|
112
|
+
const valueIndex = template.indexOf(placeholder);
|
|
113
|
+
const beforeValue = template.slice(0, valueIndex);
|
|
114
|
+
const afterValue = template.slice(valueIndex + placeholder.length);
|
|
115
|
+
|
|
116
|
+
// Capitalize controlId only if placeholder is not at the start
|
|
117
|
+
const transformedId = valueIndex === 0 ? controlId : capitalize(controlId);
|
|
118
|
+
|
|
119
|
+
// Build the new ID
|
|
120
|
+
let newId = beforeValue + transformedId + afterValue;
|
|
121
|
+
|
|
122
|
+
// Ensure proper camelCase: lowercase first char if there's a prefix
|
|
123
|
+
if (beforeValue) {
|
|
124
|
+
newId = lowercaseFirst(newId);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Capitalize the part after {{value}} if it exists
|
|
128
|
+
if (afterValue) {
|
|
129
|
+
const insertPosition = beforeValue.length + transformedId.length;
|
|
130
|
+
newId =
|
|
131
|
+
newId.slice(0, insertPosition) +
|
|
132
|
+
capitalize(newId.slice(insertPosition));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return newId;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Creates a transformer function for applying ID patterns to nested controls.
|
|
140
|
+
* @param {string} pattern - The ID pattern (may contain {{value}}).
|
|
141
|
+
* @returns {Function} A function that transforms ID strings.
|
|
142
|
+
*/
|
|
143
|
+
function createIdTransformer(pattern) {
|
|
144
|
+
return (originalId) => {
|
|
145
|
+
if (pattern?.includes("{{value}}")) {
|
|
146
|
+
return pattern.replace("{{value}}", originalId);
|
|
147
|
+
}
|
|
148
|
+
return pattern || originalId;
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Recursively transforms all 'id' keys in a control structure.
|
|
154
|
+
* @param {*} control - The control object or array to transform.
|
|
155
|
+
* @param {Function} transformer - The ID transformer function.
|
|
156
|
+
* @returns {*} The control with transformed IDs.
|
|
157
|
+
*/
|
|
158
|
+
function transformIdsRecursively(control, transformer) {
|
|
159
|
+
if (Array.isArray(control)) {
|
|
160
|
+
return control.map((item) => transformIdsRecursively(item, transformer));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (typeof control === "object" && control !== null) {
|
|
164
|
+
const result = {};
|
|
165
|
+
for (const [key, value] of Object.entries(control)) {
|
|
166
|
+
if (key === "id" && typeof value === "string") {
|
|
167
|
+
result[key] = transformer(value);
|
|
168
|
+
} else {
|
|
169
|
+
result[key] = transformIdsRecursively(value, transformer);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return result;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return control;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// =============================================================================
|
|
179
|
+
// OVERRIDE & DEFAULT APPLICATION
|
|
180
|
+
// =============================================================================
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Merges override properties into a control.
|
|
184
|
+
* For existing keys: strings replace, objects are shallow merged.
|
|
185
|
+
* New keys are added directly.
|
|
186
|
+
*
|
|
187
|
+
* @param {Object} control - The control to merge into.
|
|
188
|
+
* @param {Object} overrides - The override properties.
|
|
189
|
+
* @returns {Object} The control with overrides applied.
|
|
190
|
+
*/
|
|
191
|
+
function mergeOverrides(control, overrides) {
|
|
192
|
+
const result = { ...control };
|
|
193
|
+
const remainingOverrides = { ...overrides };
|
|
194
|
+
|
|
195
|
+
for (const key of Object.keys(overrides)) {
|
|
196
|
+
if (key in result) {
|
|
197
|
+
if (typeof overrides[key] === "string") {
|
|
198
|
+
result[key] = overrides[key];
|
|
199
|
+
} else {
|
|
200
|
+
result[key] = { ...result[key], ...overrides[key] };
|
|
201
|
+
}
|
|
202
|
+
delete remainingOverrides[key];
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Add any remaining overrides as new properties
|
|
207
|
+
return { ...result, ...remainingOverrides };
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Applies theme defaults to a control.
|
|
212
|
+
* Theme defaults override the corresponding theme property on the control.
|
|
213
|
+
*
|
|
214
|
+
* @param {Object} control - The control to apply theme defaults to.
|
|
215
|
+
* @param {Object} themeDefaults - Object containing theme property overrides.
|
|
216
|
+
* @returns {Object} The control with theme defaults applied.
|
|
217
|
+
*/
|
|
218
|
+
function applyThemeDefaults(control, themeDefaults) {
|
|
219
|
+
if (!themeDefaults || Object.keys(themeDefaults).length === 0) {
|
|
220
|
+
return control;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const result = { ...control };
|
|
224
|
+
for (const [key, value] of Object.entries(themeDefaults)) {
|
|
225
|
+
if (key in result) {
|
|
226
|
+
result[key] = value;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return result;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Applies a default value to a control.
|
|
234
|
+
*
|
|
235
|
+
* For primitive defaults:
|
|
236
|
+
* - Sets control.default if it exists
|
|
237
|
+
* - Transforms item IDs if control has an items array
|
|
238
|
+
*
|
|
239
|
+
* For object defaults:
|
|
240
|
+
* - Merges into theme properties (themeColor, etc.)
|
|
241
|
+
* - Merges into control.default if it exists
|
|
242
|
+
*
|
|
243
|
+
* @param {Object} control - The control to apply defaults to.
|
|
244
|
+
* @param {*} defaultVal - The default value (primitive or object).
|
|
245
|
+
* @returns {Object} The control with defaults applied.
|
|
246
|
+
*/
|
|
247
|
+
function applyDefaultValue(control, defaultVal) {
|
|
248
|
+
if (defaultVal === undefined) {
|
|
249
|
+
return control;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const result = { ...control };
|
|
253
|
+
|
|
254
|
+
if (typeof defaultVal !== "object") {
|
|
255
|
+
// Primitive default: set directly and transform item IDs
|
|
256
|
+
if (result.default !== undefined) {
|
|
257
|
+
result.default = defaultVal;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Transform item IDs using the default value as a pattern
|
|
261
|
+
if (result.items && Array.isArray(result.items)) {
|
|
262
|
+
const transformer = createIdTransformer(defaultVal);
|
|
263
|
+
result.items = result.items.map((item) => {
|
|
264
|
+
if (item.id && typeof item.id === "string") {
|
|
265
|
+
return { ...item, id: transformer(item.id) };
|
|
266
|
+
}
|
|
267
|
+
return item;
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
} else {
|
|
271
|
+
// Object default: apply to theme properties and control.default
|
|
272
|
+
for (const key of Object.keys(result)) {
|
|
273
|
+
if (key.startsWith("theme") && typeof result[key] === "object") {
|
|
274
|
+
result[key] = { ...result[key], default: defaultVal };
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (result.default !== undefined) {
|
|
279
|
+
result.default = { ...result.default, ...defaultVal };
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return result;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// =============================================================================
|
|
287
|
+
// GLOBAL CONTROL PROCESSING
|
|
288
|
+
// =============================================================================
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Recursively processes nested globalControl references within a control.
|
|
292
|
+
* @param {*} control - The control or array to process.
|
|
293
|
+
* @returns {*} The control with all globalControls expanded.
|
|
294
|
+
*/
|
|
295
|
+
function processNestedGlobalControls(control) {
|
|
296
|
+
if (Array.isArray(control)) {
|
|
297
|
+
return control.map(processNestedGlobalControls);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (typeof control === "object" && control !== null && control.globalControl) {
|
|
301
|
+
return processProperty(control);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return control;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Processes 'use' key references in a property, replacing them with
|
|
309
|
+
* the referenced Property definition from src/properties/.
|
|
310
|
+
*
|
|
311
|
+
* @param {Object|Array} property - The property object or array to process.
|
|
312
|
+
* @returns {Object|Array} The property with 'use' keys resolved.
|
|
313
|
+
*/
|
|
314
|
+
function resolveUseReferences(property) {
|
|
315
|
+
// Handle arrays by recursively processing each element
|
|
316
|
+
if (Array.isArray(property)) {
|
|
317
|
+
return property.map(resolveUseReferences);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const result = { ...property };
|
|
321
|
+
|
|
322
|
+
for (const [key, value] of Object.entries(result)) {
|
|
323
|
+
if (value && typeof value === "object" && value.use) {
|
|
324
|
+
const referencedProperty = Properties[value.use];
|
|
325
|
+
|
|
326
|
+
if (!referencedProperty) {
|
|
327
|
+
console.warn(`Property '${value.use}' not found in Properties.`);
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Merge: referenced property as base, then override with local values
|
|
332
|
+
const { use: _, ...localOverrides } = value;
|
|
333
|
+
result[key] = { ...referencedProperty, ...localOverrides };
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return result;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Processes a single control with all transformations:
|
|
342
|
+
* 1. Deep clone to avoid mutations
|
|
343
|
+
* 2. Apply overrides
|
|
344
|
+
* 3. Apply default values
|
|
345
|
+
* 4. Apply theme defaults
|
|
346
|
+
* 5. Transform ID using template
|
|
347
|
+
* 6. Process nested globalControls
|
|
348
|
+
* 7. Apply ID transformation to nested IDs
|
|
349
|
+
* 8. Resolve 'use' references
|
|
350
|
+
*
|
|
351
|
+
* @param {Object} control - The base control definition.
|
|
352
|
+
* @param {string} idTemplate - ID template with {{value}} placeholder.
|
|
353
|
+
* @param {Object} overrides - Property overrides to apply.
|
|
354
|
+
* @param {*} defaultVal - Default value to apply.
|
|
355
|
+
* @param {Object} themeDefaults - Theme property overrides.
|
|
356
|
+
* @returns {Object|Array} The fully processed control(s).
|
|
357
|
+
*/
|
|
358
|
+
function processControl(control, idTemplate, overrides, defaultVal, themeDefaults) {
|
|
359
|
+
// 1. Deep clone to avoid mutating the original
|
|
360
|
+
let result = cloneDeep(control);
|
|
361
|
+
|
|
362
|
+
// 2. Apply property overrides
|
|
363
|
+
result = mergeOverrides(result, overrides);
|
|
364
|
+
|
|
365
|
+
// 3. Apply default value
|
|
366
|
+
result = applyDefaultValue(result, defaultVal);
|
|
367
|
+
|
|
368
|
+
// 4. Apply theme defaults
|
|
369
|
+
result = applyThemeDefaults(result, themeDefaults);
|
|
370
|
+
|
|
371
|
+
// 5. Transform the control's ID using the template
|
|
372
|
+
if (idTemplate?.includes("{{value}}") && result.id) {
|
|
373
|
+
result.id = transformIdTemplate(idTemplate, result.id);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// 6. Process any nested globalControls
|
|
377
|
+
result = processNestedGlobalControls(result);
|
|
378
|
+
|
|
379
|
+
// 7. Apply ID transformation to all nested IDs
|
|
380
|
+
const idTransformer = createIdTransformer(result.id || "");
|
|
381
|
+
result = transformIdsRecursively(result, idTransformer);
|
|
382
|
+
|
|
383
|
+
// 8. Resolve 'use' references
|
|
384
|
+
return resolveUseReferences(result);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Extracts theme defaults from a property configuration.
|
|
389
|
+
* @param {Object} property - The property config object.
|
|
390
|
+
* @returns {Object} Object containing only defined theme properties.
|
|
391
|
+
*/
|
|
392
|
+
function extractThemeDefaults(property) {
|
|
393
|
+
const themeProps = {};
|
|
394
|
+
for (const key of THEME_PROPERTY_KEYS) {
|
|
395
|
+
if (property[key] != null) {
|
|
396
|
+
themeProps[key] = property[key];
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
return themeProps;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Processes a property from the config file.
|
|
404
|
+
* If the property has a globalControl, expands it with overrides.
|
|
405
|
+
* Otherwise, just resolves 'use' references.
|
|
406
|
+
*
|
|
407
|
+
* @param {Object} property - The property configuration object.
|
|
408
|
+
* @returns {Array} Array of processed property objects.
|
|
409
|
+
*/
|
|
410
|
+
function processProperty(property) {
|
|
411
|
+
// No globalControl: just resolve 'use' references
|
|
412
|
+
if (!property.globalControl) {
|
|
413
|
+
return [resolveUseReferences(property)];
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const globalControlName = property.globalControl;
|
|
417
|
+
const globalControl = Controls[globalControlName];
|
|
418
|
+
|
|
419
|
+
if (!globalControl) {
|
|
420
|
+
console.warn(`Global control '${globalControlName}' not found.`);
|
|
421
|
+
return [];
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Extract special properties from the config
|
|
425
|
+
const {
|
|
426
|
+
globalControl: _,
|
|
427
|
+
default: defaultVal,
|
|
428
|
+
id: idTemplate,
|
|
429
|
+
...rest
|
|
430
|
+
} = property;
|
|
431
|
+
|
|
432
|
+
// Separate theme defaults from other overrides
|
|
433
|
+
const themeDefaults = extractThemeDefaults(rest);
|
|
434
|
+
const overrides = {};
|
|
435
|
+
|
|
436
|
+
for (const [key, value] of Object.entries(rest)) {
|
|
437
|
+
if (!THEME_PROPERTY_KEYS.includes(key)) {
|
|
438
|
+
overrides[key] = value;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// Process each control in the globalControl (may be array)
|
|
443
|
+
const controls = normalizeToArray(globalControl);
|
|
444
|
+
const processed = controls.map((ctrl) =>
|
|
445
|
+
processControl(ctrl, idTemplate, overrides, defaultVal, themeDefaults)
|
|
446
|
+
);
|
|
447
|
+
|
|
448
|
+
// Flatten in case processControl returns arrays (from nested globalControls)
|
|
449
|
+
return processed.flat();
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// =============================================================================
|
|
453
|
+
// ADVANCED GROUP HANDLING
|
|
454
|
+
// =============================================================================
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Ensures the config has a properly structured Advanced group at the end.
|
|
458
|
+
* Injects standard controls (CSSClasses, ID) at the beginning of the group.
|
|
459
|
+
*
|
|
460
|
+
* @param {Object} config - The configuration object with groups array.
|
|
461
|
+
* @returns {Object} The config with Advanced group properly set up.
|
|
462
|
+
*/
|
|
463
|
+
function setupAdvancedGroup(config) {
|
|
464
|
+
const advancedControls = ADVANCED_GROUP_CONTROLS.flatMap(normalizeToArray);
|
|
465
|
+
|
|
466
|
+
const existingIndex = config.groups.findIndex(
|
|
467
|
+
(group) => group.title === "Advanced"
|
|
468
|
+
);
|
|
469
|
+
|
|
470
|
+
if (existingIndex !== -1) {
|
|
471
|
+
// Move existing Advanced group to end with injected controls
|
|
472
|
+
const [advancedGroup] = config.groups.splice(existingIndex, 1);
|
|
473
|
+
config.groups.push({
|
|
474
|
+
...advancedGroup,
|
|
475
|
+
icon: "gearshape",
|
|
476
|
+
properties: [...advancedControls, ...advancedGroup.properties],
|
|
477
|
+
});
|
|
478
|
+
} else {
|
|
479
|
+
// Create new Advanced group
|
|
480
|
+
config.groups.push({
|
|
481
|
+
title: "Advanced",
|
|
482
|
+
icon: "gearshape",
|
|
483
|
+
properties: advancedControls,
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
return config;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// =============================================================================
|
|
491
|
+
// FILE PROCESSING
|
|
492
|
+
// =============================================================================
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Reads and processes a properties.config.json file, generating properties.json.
|
|
496
|
+
*
|
|
497
|
+
* @param {string} configPath - Path to the properties.config.json file.
|
|
498
|
+
*/
|
|
499
|
+
async function processConfigFile(configPath) {
|
|
500
|
+
try {
|
|
501
|
+
const fileDir = path.dirname(configPath);
|
|
502
|
+
const configContent = await fs.readFile(configPath, "utf8");
|
|
503
|
+
let config = JSON.parse(configContent);
|
|
504
|
+
|
|
505
|
+
// Set up the Advanced group with standard controls
|
|
506
|
+
config = setupAdvancedGroup(config);
|
|
507
|
+
|
|
508
|
+
// Process all properties in each group
|
|
509
|
+
config.groups = config.groups.map((group) => ({
|
|
510
|
+
...group,
|
|
511
|
+
properties: group.properties.flatMap(processProperty),
|
|
512
|
+
}));
|
|
513
|
+
|
|
514
|
+
// Write the processed config
|
|
515
|
+
const outputPath = path.join(fileDir, "properties.json");
|
|
516
|
+
await fs.writeFile(outputPath, JSON.stringify(config, null, 2));
|
|
517
|
+
} catch (error) {
|
|
518
|
+
console.error(`Error processing file ${configPath}:`, error);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* Checks if a properties.config.json exists in a directory and processes it.
|
|
524
|
+
*
|
|
525
|
+
* @param {string} dirPath - The directory path to check.
|
|
526
|
+
*/
|
|
527
|
+
async function processDirectoryIfConfigExists(dirPath) {
|
|
528
|
+
try {
|
|
529
|
+
const configPath = path.join(dirPath, "properties.config.json");
|
|
530
|
+
|
|
531
|
+
try {
|
|
532
|
+
await fs.access(configPath);
|
|
533
|
+
await processConfigFile(configPath);
|
|
534
|
+
} catch {
|
|
535
|
+
// Config file doesn't exist, skip silently
|
|
536
|
+
}
|
|
537
|
+
} catch (error) {
|
|
538
|
+
console.error(`Error processing directory ${dirPath}:`, error);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* Finds all directories matching a glob pattern and processes their configs.
|
|
544
|
+
*
|
|
545
|
+
* @param {string} packsDir - The packs directory to search.
|
|
546
|
+
*/
|
|
547
|
+
async function processAllConfigs(packsDir) {
|
|
548
|
+
try {
|
|
549
|
+
const pattern = path.join(packsDir, "**/*.elementsdevpack/components/com.**/**");
|
|
550
|
+
const directories = globSync(pattern, { absolute: true });
|
|
551
|
+
|
|
552
|
+
console.log(`[properties] Found ${directories.length} component directories`);
|
|
553
|
+
|
|
554
|
+
let processed = 0;
|
|
555
|
+
for (const dir of directories) {
|
|
556
|
+
const configPath = path.join(dir, "properties.config.json");
|
|
557
|
+
try {
|
|
558
|
+
await fs.access(configPath);
|
|
559
|
+
await processConfigFile(configPath);
|
|
560
|
+
processed++;
|
|
561
|
+
} catch {
|
|
562
|
+
// Config file doesn't exist, skip silently
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
console.log(`[properties] Processed ${processed} properties.config.json files`);
|
|
567
|
+
} catch (error) {
|
|
568
|
+
console.error("[properties] Error reading directories:", error);
|
|
569
|
+
throw error;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// =============================================================================
|
|
574
|
+
// EXPORTS
|
|
575
|
+
// =============================================================================
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* Main entry point for building properties.
|
|
579
|
+
*
|
|
580
|
+
* @param {Object} config - Configuration object from resolveConfig()
|
|
581
|
+
* @param {string} config.packsDir - Absolute path to the packs directory
|
|
582
|
+
*/
|
|
583
|
+
export async function buildProperties(config) {
|
|
584
|
+
console.log(`[properties] Building properties...`);
|
|
585
|
+
await processAllConfigs(config.packsDir);
|
|
586
|
+
console.log(`[properties] Build complete`);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/**
|
|
590
|
+
* Starts watch mode for continuous building of properties
|
|
591
|
+
* @param {Object} config - Configuration object
|
|
592
|
+
* @param {string} config.packsDir - Absolute path to the packs directory
|
|
593
|
+
*/
|
|
594
|
+
export async function startWatch(config) {
|
|
595
|
+
const packsDir = config.packsDir;
|
|
596
|
+
|
|
597
|
+
console.log('[properties] Watch mode enabled. Listening for changes...');
|
|
598
|
+
|
|
599
|
+
let building = false;
|
|
600
|
+
const rebuild = async () => {
|
|
601
|
+
if (building) return;
|
|
602
|
+
building = true;
|
|
603
|
+
try {
|
|
604
|
+
await buildProperties(config);
|
|
605
|
+
} catch (err) {
|
|
606
|
+
console.error('[properties] Build error:', err.message || err);
|
|
607
|
+
} finally {
|
|
608
|
+
building = false;
|
|
609
|
+
}
|
|
610
|
+
};
|
|
611
|
+
|
|
612
|
+
// Watch the packs directory for properties.config.json changes
|
|
613
|
+
try {
|
|
614
|
+
const { default: fsModule } = await import('fs');
|
|
615
|
+
const watcher = fsModule.watch(packsDir, { recursive: true }, (eventType, filename) => {
|
|
616
|
+
if (!filename) return;
|
|
617
|
+
const lower = filename.toLowerCase();
|
|
618
|
+
if (lower.endsWith('properties.config.json')) {
|
|
619
|
+
console.log(`[properties] Change detected: ${filename} (${eventType})`);
|
|
620
|
+
rebuild();
|
|
621
|
+
}
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
watcher.on('error', (err) => {
|
|
625
|
+
console.error('[properties] Watcher error:', err);
|
|
626
|
+
});
|
|
627
|
+
} catch (err) {
|
|
628
|
+
console.warn(`[properties] Could not watch ${packsDir}: ${err.message}`);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
await rebuild();
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// Allow direct execution for backwards compatibility
|
|
635
|
+
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
636
|
+
// Direct execution: use default path
|
|
637
|
+
const WATCH = process.argv.includes('--watch') || process.argv.includes('-w');
|
|
638
|
+
const defaultPacksDir = path.resolve(__dirname, "..", "packs");
|
|
639
|
+
const config = { packsDir: defaultPacksDir };
|
|
640
|
+
|
|
641
|
+
(async () => {
|
|
642
|
+
try {
|
|
643
|
+
if (WATCH) {
|
|
644
|
+
await startWatch(config);
|
|
645
|
+
} else {
|
|
646
|
+
await buildProperties(config);
|
|
647
|
+
}
|
|
648
|
+
} catch (err) {
|
|
649
|
+
console.error("[properties] Build failed:", err);
|
|
650
|
+
process.exit(1);
|
|
651
|
+
}
|
|
652
|
+
})();
|
|
653
|
+
}
|
|
654
|
+
|