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
package/README.md
ADDED
|
@@ -0,0 +1,1333 @@
|
|
|
1
|
+
# rw-elements-tools
|
|
2
|
+
|
|
3
|
+
**The official build toolkit for creating RapidWeaver Elements**
|
|
4
|
+
|
|
5
|
+
Build powerful, reusable web components for RapidWeaver without the complexity. This toolkit handles the heavy lifting so you can focus on what matters: creating great elements.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## What is rw-elements-tools?
|
|
10
|
+
|
|
11
|
+
rw-elements-tools is a development toolkit that simplifies the process of creating custom elements for [RapidWeaver](https://www.realmacsoftware.com/rapidweaver/), the popular Mac website builder. It provides:
|
|
12
|
+
|
|
13
|
+
- **A powerful CLI** for building and watching your element files
|
|
14
|
+
- **Ready-to-use controls** for common UI patterns (colors, spacing, typography, and more)
|
|
15
|
+
- **Shared utilities** for generating Tailwind CSS classes
|
|
16
|
+
- **Smart optimization** that automatically removes unused code from your builds
|
|
17
|
+
|
|
18
|
+
## Who is this for?
|
|
19
|
+
|
|
20
|
+
- **Theme developers** who want to create custom RapidWeaver elements
|
|
21
|
+
- **Agencies** building bespoke elements for client projects
|
|
22
|
+
- **Developers** looking to extend RapidWeaver's capabilities
|
|
23
|
+
- **Anyone** who wants to contribute elements to the RapidWeaver ecosystem
|
|
24
|
+
|
|
25
|
+
## Why use rw-elements-tools?
|
|
26
|
+
|
|
27
|
+
| Without rw-elements-tools | With rw-elements-tools |
|
|
28
|
+
|--------------------------|----------------------|
|
|
29
|
+
| Manually write complex JSON config files | Use intuitive JavaScript configuration |
|
|
30
|
+
| Copy/paste utility code between elements | Import from a shared library |
|
|
31
|
+
| Bloated output with unused code | Automatic dead code elimination |
|
|
32
|
+
| Manual rebuilds on every change | Watch mode for instant updates |
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm install --save-dev rw-elements-tools
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Quick Start
|
|
43
|
+
|
|
44
|
+
### Step 1: Set Up Your Project
|
|
45
|
+
|
|
46
|
+
Create a new directory for your element pack project and initialize it:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
mkdir my-element-pack
|
|
50
|
+
cd my-element-pack
|
|
51
|
+
npm init -y
|
|
52
|
+
npm install --save-dev rw-elements-tools
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Step 2: Create the Required Directory Structure
|
|
56
|
+
|
|
57
|
+
Your project needs a `packs` folder containing your element pack(s). Each pack follows this structure:
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
my-element-pack/
|
|
61
|
+
├── package.json
|
|
62
|
+
├── packs/ # Default packs directory
|
|
63
|
+
│ └── MyPack.elementsdevpack/ # Your pack (must end in .elementsdevpack)
|
|
64
|
+
│ └── components/
|
|
65
|
+
│ └── com.yourcompany.elementname/ # Component folder (must start with com.)
|
|
66
|
+
│ ├── properties.config.json # Source config (you edit this)
|
|
67
|
+
│ ├── properties.json # Generated output (don't edit)
|
|
68
|
+
│ ├── hooks.source.js # Source hooks (you edit this)
|
|
69
|
+
│ └── hooks.js # Generated output (don't edit)
|
|
70
|
+
└── node_modules/
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Key naming conventions:**
|
|
74
|
+
- Pack folders must end with `.elementsdevpack`
|
|
75
|
+
- Component folders must start with `com.` (e.g., `com.mycompany.button`)
|
|
76
|
+
- Source files: `properties.config.json` and `hooks.source.js`
|
|
77
|
+
- Generated files: `properties.json` and `hooks.js`
|
|
78
|
+
|
|
79
|
+
### Step 3: Create Your First Element
|
|
80
|
+
|
|
81
|
+
Create the folder structure for your first element:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
mkdir -p packs/MyPack.elementsdevpack/components/com.mycompany.button
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Create a minimal `properties.config.json`:
|
|
88
|
+
|
|
89
|
+
```json
|
|
90
|
+
{
|
|
91
|
+
"groups": [
|
|
92
|
+
{
|
|
93
|
+
"title": "Content",
|
|
94
|
+
"icon": "text.alignleft",
|
|
95
|
+
"properties": [
|
|
96
|
+
{
|
|
97
|
+
"title": "Button Text",
|
|
98
|
+
"id": "buttonText",
|
|
99
|
+
"text": {
|
|
100
|
+
"default": "Click Me"
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
]
|
|
104
|
+
}
|
|
105
|
+
]
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Create a `hooks.source.js` that uses the shared hook utilities:
|
|
110
|
+
|
|
111
|
+
```javascript
|
|
112
|
+
function transformHook(rw) {
|
|
113
|
+
// Props are accessed via rw.props - property IDs from properties.config.json
|
|
114
|
+
const { buttonText } = rw.props;
|
|
115
|
+
|
|
116
|
+
// Build CSS classes using the shared classnames utility
|
|
117
|
+
const classes = classnames()
|
|
118
|
+
.add(globalSpacing(rw))
|
|
119
|
+
.add(globalBgColor(rw))
|
|
120
|
+
.toString();
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
classes,
|
|
124
|
+
buttonText
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
exports.transformHook = transformHook;
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
> **Note:** The `buttonText` prop corresponds to the `"id": "buttonText"` defined in `properties.config.json`. All property IDs become available on `rw.props`. Functions like `classnames()`, `globalSpacing()`, and `globalBgColor()` come from the shared hooks library—no imports needed.
|
|
132
|
+
|
|
133
|
+
### Step 4: Add Build Scripts
|
|
134
|
+
|
|
135
|
+
Add these scripts to your `package.json`:
|
|
136
|
+
|
|
137
|
+
```json
|
|
138
|
+
{
|
|
139
|
+
"scripts": {
|
|
140
|
+
"build": "rw-build all",
|
|
141
|
+
"build:properties": "rw-build properties",
|
|
142
|
+
"build:hooks": "rw-build hooks",
|
|
143
|
+
"dev": "rw-build all --watch"
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Step 5: Build Your Elements
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
# One-time build
|
|
152
|
+
npm run build
|
|
153
|
+
|
|
154
|
+
# Or watch for changes during development
|
|
155
|
+
npm run dev
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
That's it! The build tool will generate `properties.json` and `hooks.js` files in each component folder.
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
### Build Commands Reference
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
# Build all properties and hooks
|
|
166
|
+
npx rw-build all
|
|
167
|
+
|
|
168
|
+
# Build properties only
|
|
169
|
+
npx rw-build properties
|
|
170
|
+
|
|
171
|
+
# Build hooks only
|
|
172
|
+
npx rw-build hooks
|
|
173
|
+
|
|
174
|
+
# Watch for changes
|
|
175
|
+
npx rw-build all --watch # Watch both properties and hooks
|
|
176
|
+
npx rw-build properties --watch # Watch properties only
|
|
177
|
+
npx rw-build hooks --watch # Watch hooks only
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Configuration
|
|
181
|
+
|
|
182
|
+
The packs directory can be configured via multiple methods (in priority order):
|
|
183
|
+
|
|
184
|
+
### 1. CLI Argument (highest priority)
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
rw-build all --packs ./my-elements
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### 2. Environment Variable
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
RW_PACKS_DIR=./my-elements npm run build
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### 3. package.json
|
|
197
|
+
|
|
198
|
+
```json
|
|
199
|
+
{
|
|
200
|
+
"rw-elements-tools": {
|
|
201
|
+
"packsDir": "./my-elements"
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### 4. Config File
|
|
207
|
+
|
|
208
|
+
```javascript
|
|
209
|
+
// rw-elements-tools.config.js
|
|
210
|
+
export default {
|
|
211
|
+
packsDir: './my-elements'
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### 5. Default
|
|
216
|
+
|
|
217
|
+
If no configuration is provided, looks for `./packs` in the project root.
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Table of Contents
|
|
222
|
+
|
|
223
|
+
1. [Overview](#overview)
|
|
224
|
+
2. [Directory Structure](#directory-structure)
|
|
225
|
+
3. [Properties Build System](#properties-build-system)
|
|
226
|
+
4. [Shared Hooks Build System](#shared-hooks-build-system)
|
|
227
|
+
5. [Controls](#controls)
|
|
228
|
+
6. [Properties](#properties)
|
|
229
|
+
7. [Configuration File Format](#configuration-file-format)
|
|
230
|
+
8. [Adding New Controls](#adding-new-controls)
|
|
231
|
+
9. [Adding New Properties](#adding-new-properties)
|
|
232
|
+
10. [Adding Shared Hooks](#adding-shared-hooks)
|
|
233
|
+
11. [Advanced Features](#advanced-features)
|
|
234
|
+
12. [Build Commands](#build-commands)
|
|
235
|
+
13. [Programmatic Usage](#programmatic-usage)
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## Overview
|
|
240
|
+
|
|
241
|
+
The build system processes `properties.config.json` files located in element component directories and generates expanded `properties.json` files. This allows developers to:
|
|
242
|
+
|
|
243
|
+
- **Reuse common UI controls** across multiple elements via `globalControl` references
|
|
244
|
+
- **Share property definitions** (like spacing values, font weights) via `use` references
|
|
245
|
+
- **Override defaults** per-element while maintaining a single source of truth
|
|
246
|
+
- **Apply theme defaults** for consistent theming across elements
|
|
247
|
+
|
|
248
|
+
### Data Flow
|
|
249
|
+
|
|
250
|
+
```
|
|
251
|
+
properties.config.json Controls (controls/)
|
|
252
|
+
│ │
|
|
253
|
+
▼ ▼
|
|
254
|
+
┌─────────────────────────────────────┐
|
|
255
|
+
│ build-properties.js │
|
|
256
|
+
│ • Expands globalControl references │
|
|
257
|
+
│ • Resolves 'use' property refs │
|
|
258
|
+
│ • Applies overrides & defaults │
|
|
259
|
+
│ • Injects Advanced group controls │
|
|
260
|
+
└─────────────────────────────────────┘
|
|
261
|
+
│
|
|
262
|
+
▼
|
|
263
|
+
properties.json
|
|
264
|
+
(consumed by RapidWeaver)
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## Directory Structure
|
|
270
|
+
|
|
271
|
+
```
|
|
272
|
+
rw-elements-tools/
|
|
273
|
+
├── bin/
|
|
274
|
+
│ └── cli.js # CLI entry point (rw-build command)
|
|
275
|
+
├── build-properties.js # Properties build script
|
|
276
|
+
├── build-shared-hooks.js # Shared hooks build script
|
|
277
|
+
├── config.js # Configuration resolver
|
|
278
|
+
├── index.js # Package entry point
|
|
279
|
+
├── package.json # npm package config
|
|
280
|
+
├── README.md # This documentation
|
|
281
|
+
├── controls/ # Reusable UI control definitions
|
|
282
|
+
│ ├── index.js # Exports all controls
|
|
283
|
+
│ ├── alignment/ # Flexbox/Grid alignment controls
|
|
284
|
+
│ ├── Animations/ # Animation and scroll animation controls
|
|
285
|
+
│ ├── Background/ # Background color, image, gradient, video
|
|
286
|
+
│ ├── Borders/ # Border and outline controls
|
|
287
|
+
│ ├── core/ # Essential controls (ID, CSSClasses, etc.)
|
|
288
|
+
│ ├── Effects/ # Box shadow, opacity, filters, blur
|
|
289
|
+
│ ├── grid-flex/ # Grid and flexbox item controls
|
|
290
|
+
│ ├── interactive/ # Button, input, link, filter controls
|
|
291
|
+
│ ├── Layout/ # Position, overflow, visibility, z-index
|
|
292
|
+
│ ├── Overlay/ # Overlay color, gradient, image
|
|
293
|
+
│ ├── Sizing/ # Width, height, min/max sizing
|
|
294
|
+
│ ├── Spacing/ # Margin and padding controls
|
|
295
|
+
│ ├── Transforms/ # Rotate, scale, skew, translate
|
|
296
|
+
│ ├── Transitions/ # Transition timing and properties
|
|
297
|
+
│ └── typography/ # Text color, decoration, styles
|
|
298
|
+
├── properties/ # Reusable property value definitions
|
|
299
|
+
│ ├── index.js # Exports all properties
|
|
300
|
+
│ ├── Slider.js # Slider value ranges
|
|
301
|
+
│ ├── FontWeight.js # Font weight options
|
|
302
|
+
│ └── ... # Other property definitions
|
|
303
|
+
└── shared-hooks/ # Shared JavaScript hook functions
|
|
304
|
+
├── animations/ # Animation and reveal functions
|
|
305
|
+
├── background/ # Background processing functions
|
|
306
|
+
├── borders/ # Border and outline functions
|
|
307
|
+
├── core/ # Essential utilities (classnames, etc.)
|
|
308
|
+
├── effects/ # Visual effects (opacity, filters)
|
|
309
|
+
├── interactive/ # Link and filter functions
|
|
310
|
+
├── layout/ # Layout and positioning
|
|
311
|
+
├── navigation/ # Navigation component styles
|
|
312
|
+
├── sizing/ # Dimensions and aspect ratios
|
|
313
|
+
├── spacing/ # Margin and padding functions
|
|
314
|
+
├── transforms/ # CSS transform functions
|
|
315
|
+
├── transitions/ # CSS and Alpine transitions
|
|
316
|
+
└── typography/ # Text and font style functions
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
---
|
|
320
|
+
|
|
321
|
+
## Properties Build System
|
|
322
|
+
|
|
323
|
+
### Entry Point
|
|
324
|
+
|
|
325
|
+
The properties build script is executed via:
|
|
326
|
+
|
|
327
|
+
```bash
|
|
328
|
+
cd src
|
|
329
|
+
npm run build
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
This runs `node build.js` which:
|
|
333
|
+
|
|
334
|
+
1. **Finds all config files** matching the glob pattern:
|
|
335
|
+
```
|
|
336
|
+
../**/*.elementsdevpack/components/com.**/**/properties.config.json
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
2. **Processes each config file** through the following pipeline:
|
|
340
|
+
- Parse the JSON configuration
|
|
341
|
+
- Set up the Advanced group with injected controls (CSSClasses, ID)
|
|
342
|
+
- Process each property group
|
|
343
|
+
- Expand `globalControl` references
|
|
344
|
+
- Resolve `use` property references
|
|
345
|
+
- Apply overrides and theme defaults
|
|
346
|
+
- Write the output to `properties.json`
|
|
347
|
+
|
|
348
|
+
### Processing Pipeline
|
|
349
|
+
|
|
350
|
+
For each property in a config file:
|
|
351
|
+
|
|
352
|
+
```
|
|
353
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
354
|
+
│ 1. Check for globalControl │
|
|
355
|
+
│ └─ If present: Load control from Controls registry │
|
|
356
|
+
│ └─ If absent: Pass through with 'use' resolution only │
|
|
357
|
+
├─────────────────────────────────────────────────────────────┤
|
|
358
|
+
│ 2. Deep clone the control (avoid mutations) │
|
|
359
|
+
├─────────────────────────────────────────────────────────────┤
|
|
360
|
+
│ 3. Apply property overrides │
|
|
361
|
+
│ └─ String values: Replace directly │
|
|
362
|
+
│ └─ Object values: Shallow merge │
|
|
363
|
+
├─────────────────────────────────────────────────────────────┤
|
|
364
|
+
│ 4. Apply default values │
|
|
365
|
+
│ └─ Primitive defaults: Set control.default │
|
|
366
|
+
│ └─ Object defaults: Merge into theme properties │
|
|
367
|
+
├─────────────────────────────────────────────────────────────┤
|
|
368
|
+
│ 5. Apply theme defaults │
|
|
369
|
+
│ └─ themeColor, themeFont, themeBorderRadius, etc. │
|
|
370
|
+
├─────────────────────────────────────────────────────────────┤
|
|
371
|
+
│ 6. Transform IDs using {{value}} template │
|
|
372
|
+
│ └─ "prefix{{value}}Suffix" → "prefixControlIdSuffix" │
|
|
373
|
+
├─────────────────────────────────────────────────────────────┤
|
|
374
|
+
│ 7. Process nested globalControls recursively │
|
|
375
|
+
├─────────────────────────────────────────────────────────────┤
|
|
376
|
+
│ 8. Resolve 'use' references to Properties │
|
|
377
|
+
└─────────────────────────────────────────────────────────────┘
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
---
|
|
381
|
+
|
|
382
|
+
## Shared Hooks Build System
|
|
383
|
+
|
|
384
|
+
The shared hooks build system combines reusable JavaScript utility functions with component-specific hook code, then applies dead code elimination to produce optimized output.
|
|
385
|
+
|
|
386
|
+
### Overview
|
|
387
|
+
|
|
388
|
+
```
|
|
389
|
+
shared-hooks/**/*.js Component hooks.source.js
|
|
390
|
+
│ │
|
|
391
|
+
▼ ▼
|
|
392
|
+
┌─────────────────────────────────────────┐
|
|
393
|
+
│ build-shared-hooks.js │
|
|
394
|
+
│ • Concatenates shared + component code │
|
|
395
|
+
│ • Applies esbuild dead code elimination │
|
|
396
|
+
│ • Keeps only code reachable from │
|
|
397
|
+
│ transformHook function │
|
|
398
|
+
└─────────────────────────────────────────┘
|
|
399
|
+
│
|
|
400
|
+
▼
|
|
401
|
+
hooks.js (optimized)
|
|
402
|
+
(consumed by RapidWeaver)
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
### How It Works
|
|
406
|
+
|
|
407
|
+
1. **Find all source files**: Scans `packs/` for `hooks.source.js` files
|
|
408
|
+
2. **Read shared hooks**: Loads all `.js` files from `shared-hooks/` and its subfolders
|
|
409
|
+
3. **Concatenate**: Combines shared code + component code
|
|
410
|
+
4. **Dead code elimination**: Uses esbuild to remove unused functions
|
|
411
|
+
5. **Output**: Writes optimized `hooks.js` to each component
|
|
412
|
+
|
|
413
|
+
### Shared Hook Organization
|
|
414
|
+
|
|
415
|
+
Shared hooks are organized into category subfolders:
|
|
416
|
+
|
|
417
|
+
| Folder | Purpose | Example Functions |
|
|
418
|
+
|--------|---------|-------------------|
|
|
419
|
+
| `core/` | Essential utilities | `classnames`, `getHoverPrefix`, `globalHTMLTag` |
|
|
420
|
+
| `layout/` | Layout and positioning | `globalLayout`, `globalActAsGridOrFlexItem` |
|
|
421
|
+
| `sizing/` | Dimensions and aspect ratios | `globalSizing`, `aspectRatioClasses` |
|
|
422
|
+
| `spacing/` | Margin and padding | `globalSpacing`, `globalSpacingMargin` |
|
|
423
|
+
| `background/` | Background styles | `globalBackground`, `globalBgImageFetchPriority` |
|
|
424
|
+
| `borders/` | Borders and outlines | `globalBorders`, `globalOutline` |
|
|
425
|
+
| `effects/` | Visual effects | `globalEffects`, `globalFilters`, `globalOverlay` |
|
|
426
|
+
| `typography/` | Text and font styles | `globalTextFontsAndTextStyles`, `globalHeadingColor` |
|
|
427
|
+
| `transforms/` | CSS transforms | `globalTransforms` |
|
|
428
|
+
| `transitions/` | CSS/Alpine transitions | `globalTransitions`, `getAlpineTransitionAttributesMobile` |
|
|
429
|
+
| `animations/` | Animations and reveals | `globalAnimations`, `globalReveal` |
|
|
430
|
+
| `navigation/` | Navigation styles | `globalNavItems`, `globalMenuItem` |
|
|
431
|
+
| `interactive/` | Links and filters | `globalLink`, `globalFilter` |
|
|
432
|
+
|
|
433
|
+
### Component Hook Files
|
|
434
|
+
|
|
435
|
+
Each component that needs hooks creates a `hooks.source.js` file:
|
|
436
|
+
|
|
437
|
+
```javascript
|
|
438
|
+
// packs/Core.elementsdevpack/components/com.realmacsoftware.button/hooks.source.js
|
|
439
|
+
|
|
440
|
+
function transformHook(rw) {
|
|
441
|
+
// Use any shared hook functions here
|
|
442
|
+
const classes = classnames(rw.props.customClasses)
|
|
443
|
+
.add(globalSpacing(rw))
|
|
444
|
+
.toString();
|
|
445
|
+
|
|
446
|
+
return {
|
|
447
|
+
classes
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
exports.transformHook = transformHook;
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
### Key Points
|
|
455
|
+
|
|
456
|
+
- **Entry point**: The `transformHook` function is the only exported function
|
|
457
|
+
- **Dead code elimination**: Only code reachable from `transformHook` is kept
|
|
458
|
+
- **No manual imports**: Shared code is concatenated, not imported
|
|
459
|
+
- **Auto-generated**: Output `hooks.js` files are marked "do not edit"
|
|
460
|
+
|
|
461
|
+
### Build Commands
|
|
462
|
+
|
|
463
|
+
```bash
|
|
464
|
+
# Build all hooks once
|
|
465
|
+
npm run build:hooks
|
|
466
|
+
|
|
467
|
+
# Watch for changes
|
|
468
|
+
npm run build:hooks:watch
|
|
469
|
+
|
|
470
|
+
# Using npm scripts
|
|
471
|
+
npm run build:hooks
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
---
|
|
475
|
+
|
|
476
|
+
## Controls
|
|
477
|
+
|
|
478
|
+
Controls are reusable UI component definitions that map to RapidWeaver's property inspector UI elements.
|
|
479
|
+
|
|
480
|
+
### Control Structure
|
|
481
|
+
|
|
482
|
+
A control is a JavaScript object (or array of objects) that defines:
|
|
483
|
+
|
|
484
|
+
```javascript
|
|
485
|
+
// Simple control (single object)
|
|
486
|
+
const FlexDirection = {
|
|
487
|
+
title: "Direction",
|
|
488
|
+
id: "flexDirection",
|
|
489
|
+
select: {
|
|
490
|
+
default: "flex-col",
|
|
491
|
+
items: [
|
|
492
|
+
{ value: "flex-col", title: "Column" },
|
|
493
|
+
{ value: "flex-row", title: "Row" },
|
|
494
|
+
],
|
|
495
|
+
},
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
export default FlexDirection;
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
```javascript
|
|
502
|
+
// Compound control (array of objects)
|
|
503
|
+
const Link = [
|
|
504
|
+
{
|
|
505
|
+
title: "Link",
|
|
506
|
+
heading: {}
|
|
507
|
+
},
|
|
508
|
+
{
|
|
509
|
+
title: "To",
|
|
510
|
+
id: "globalLink",
|
|
511
|
+
link: {}
|
|
512
|
+
}
|
|
513
|
+
];
|
|
514
|
+
|
|
515
|
+
export default Link;
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
### UI Element Types
|
|
519
|
+
|
|
520
|
+
Controls can use these UI element types:
|
|
521
|
+
|
|
522
|
+
| Type | Description | Example |
|
|
523
|
+
|------|-------------|---------|
|
|
524
|
+
| `select` | Dropdown menu | `{ select: { default: "value", items: [...] } }` |
|
|
525
|
+
| `segmented` | Segmented button control | `{ segmented: { default: "a", items: [...] } }` |
|
|
526
|
+
| `switch` | Boolean toggle | `{ switch: { default: false } }` |
|
|
527
|
+
| `slider` | Numeric slider | `{ slider: { default: 50, min: 0, max: 100 } }` |
|
|
528
|
+
| `number` | Numeric input | `{ number: { default: 0 } }` |
|
|
529
|
+
| `text` | Text input | `{ text: { default: "" } }` |
|
|
530
|
+
| `textArea` | Multi-line text | `{ textArea: { default: "" } }` |
|
|
531
|
+
| `link` | Link picker | `{ link: {} }` |
|
|
532
|
+
| `resource` | Resource/image picker | `{ resource: {} }` |
|
|
533
|
+
| `heading` | Section heading (no value) | `{ heading: {} }` |
|
|
534
|
+
| `divider` | Visual separator | `{ divider: {} }` |
|
|
535
|
+
| `information` | Info text | `{ information: {} }` |
|
|
536
|
+
| `themeColor` | Theme color picker | `{ themeColor: { default: {...} } }` |
|
|
537
|
+
| `themeSpacing` | Theme spacing picker | `{ themeSpacing: { mode: "single" } }` |
|
|
538
|
+
| `themeBorderRadius` | Border radius picker | `{ themeBorderRadius: {...} }` |
|
|
539
|
+
| `themeBorderWidth` | Border width picker | `{ themeBorderWidth: {...} }` |
|
|
540
|
+
| `themeShadow` | Shadow picker | `{ themeShadow: {...} }` |
|
|
541
|
+
|
|
542
|
+
### Control Properties
|
|
543
|
+
|
|
544
|
+
| Property | Type | Description |
|
|
545
|
+
|----------|------|-------------|
|
|
546
|
+
| `title` | string | Display label in the UI |
|
|
547
|
+
| `id` | string | Property identifier (used in templates) |
|
|
548
|
+
| `format` | string | CSS class format, e.g., `"gap-x-{{value}}"` |
|
|
549
|
+
| `visible` | string | Visibility condition, e.g., `"otherProp == 'value'"` |
|
|
550
|
+
| `responsive` | boolean | Whether control is responsive (default: true) |
|
|
551
|
+
| `globalControl` | string | Reference to another control (nested) |
|
|
552
|
+
|
|
553
|
+
### Nested globalControls
|
|
554
|
+
|
|
555
|
+
Controls can reference other controls for composition:
|
|
556
|
+
|
|
557
|
+
```javascript
|
|
558
|
+
const Borders = [
|
|
559
|
+
{
|
|
560
|
+
globalControl: "ControlType",
|
|
561
|
+
id: "{{value}}Borders",
|
|
562
|
+
},
|
|
563
|
+
{
|
|
564
|
+
globalControl: "BorderStyle",
|
|
565
|
+
visible: "globalControlTypeBorders == 'static'",
|
|
566
|
+
},
|
|
567
|
+
{
|
|
568
|
+
globalControl: "BorderColor",
|
|
569
|
+
visible: "globalControlTypeBorders == 'static'",
|
|
570
|
+
},
|
|
571
|
+
];
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
---
|
|
575
|
+
|
|
576
|
+
## Properties
|
|
577
|
+
|
|
578
|
+
Properties are reusable value definitions (like enums or option lists) that can be referenced using the `use` key.
|
|
579
|
+
|
|
580
|
+
### Property Structure
|
|
581
|
+
|
|
582
|
+
```javascript
|
|
583
|
+
// properties/FontWeight.js
|
|
584
|
+
const FontWeight = {
|
|
585
|
+
default: "normal",
|
|
586
|
+
items: [
|
|
587
|
+
{ value: "thin", title: "Thin" },
|
|
588
|
+
{ value: "light", title: "Light" },
|
|
589
|
+
{ value: "normal", title: "Normal" },
|
|
590
|
+
{ value: "medium", title: "Medium" },
|
|
591
|
+
{ value: "semibold", title: "Semibold" },
|
|
592
|
+
{ value: "bold", title: "Bold" },
|
|
593
|
+
],
|
|
594
|
+
};
|
|
595
|
+
|
|
596
|
+
export default FontWeight;
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
### Using Properties in Controls
|
|
600
|
+
|
|
601
|
+
Reference a property using the `use` key:
|
|
602
|
+
|
|
603
|
+
```javascript
|
|
604
|
+
const TextWeight = {
|
|
605
|
+
title: "Weight",
|
|
606
|
+
id: "textWeight",
|
|
607
|
+
select: {
|
|
608
|
+
use: "FontWeight" // Merges FontWeight's items and default
|
|
609
|
+
}
|
|
610
|
+
};
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
The build system will merge the referenced property, with local values taking precedence:
|
|
614
|
+
|
|
615
|
+
```javascript
|
|
616
|
+
// Output
|
|
617
|
+
{
|
|
618
|
+
title: "Weight",
|
|
619
|
+
id: "textWeight",
|
|
620
|
+
select: {
|
|
621
|
+
default: "normal",
|
|
622
|
+
items: [
|
|
623
|
+
{ value: "thin", title: "Thin" },
|
|
624
|
+
// ... etc
|
|
625
|
+
]
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
---
|
|
631
|
+
|
|
632
|
+
## Configuration File Format
|
|
633
|
+
|
|
634
|
+
### properties.config.json Structure
|
|
635
|
+
|
|
636
|
+
```json
|
|
637
|
+
{
|
|
638
|
+
"groups": [
|
|
639
|
+
{
|
|
640
|
+
"title": "Group Title",
|
|
641
|
+
"icon": "sf-symbol-name",
|
|
642
|
+
"properties": [
|
|
643
|
+
// Property definitions
|
|
644
|
+
]
|
|
645
|
+
}
|
|
646
|
+
]
|
|
647
|
+
}
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
### Using globalControl
|
|
651
|
+
|
|
652
|
+
Reference a control by name:
|
|
653
|
+
|
|
654
|
+
```json
|
|
655
|
+
{
|
|
656
|
+
"globalControl": "Spacing"
|
|
657
|
+
}
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
### Overriding Control Properties
|
|
661
|
+
|
|
662
|
+
Add properties alongside `globalControl` to override:
|
|
663
|
+
|
|
664
|
+
```json
|
|
665
|
+
{
|
|
666
|
+
"globalControl": "BorderRadius",
|
|
667
|
+
"title": "Corner Radius",
|
|
668
|
+
"default": {
|
|
669
|
+
"base": {
|
|
670
|
+
"topLeft": "lg",
|
|
671
|
+
"topRight": "lg",
|
|
672
|
+
"bottomLeft": "none",
|
|
673
|
+
"bottomRight": "none"
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
```
|
|
678
|
+
|
|
679
|
+
### ID Templates with {{value}}
|
|
680
|
+
|
|
681
|
+
Transform the control's ID using a template:
|
|
682
|
+
|
|
683
|
+
```json
|
|
684
|
+
{
|
|
685
|
+
"globalControl": "Spacing",
|
|
686
|
+
"id": "card{{value}}"
|
|
687
|
+
}
|
|
688
|
+
```
|
|
689
|
+
|
|
690
|
+
If the Spacing control has `id: "globalPadding"`, the output becomes `id: "cardGlobalPadding"`.
|
|
691
|
+
|
|
692
|
+
### Theme Defaults
|
|
693
|
+
|
|
694
|
+
Override theme-related properties:
|
|
695
|
+
|
|
696
|
+
```json
|
|
697
|
+
{
|
|
698
|
+
"globalControl": "Background_Color",
|
|
699
|
+
"themeColor": {
|
|
700
|
+
"default": {
|
|
701
|
+
"name": "brand",
|
|
702
|
+
"brightness": 500
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
```
|
|
707
|
+
|
|
708
|
+
Supported theme properties:
|
|
709
|
+
- `themeColor`
|
|
710
|
+
- `themeFont`
|
|
711
|
+
- `themeBorderRadius`
|
|
712
|
+
- `themeBorderWidth`
|
|
713
|
+
- `themeSpacing`
|
|
714
|
+
- `themeShadow`
|
|
715
|
+
- `themeTextStyle`
|
|
716
|
+
|
|
717
|
+
### Inline Properties
|
|
718
|
+
|
|
719
|
+
Properties without `globalControl` are passed through with `use` resolution:
|
|
720
|
+
|
|
721
|
+
```json
|
|
722
|
+
{
|
|
723
|
+
"title": "Custom Width",
|
|
724
|
+
"id": "customWidth",
|
|
725
|
+
"slider": {
|
|
726
|
+
"use": "Slider",
|
|
727
|
+
"default": 100,
|
|
728
|
+
"min": 0,
|
|
729
|
+
"max": 500
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
```
|
|
733
|
+
|
|
734
|
+
---
|
|
735
|
+
|
|
736
|
+
## Adding New Controls
|
|
737
|
+
|
|
738
|
+
### Step 1: Create the Control File
|
|
739
|
+
|
|
740
|
+
Create a new file in the appropriate `controls/` subdirectory:
|
|
741
|
+
|
|
742
|
+
```javascript
|
|
743
|
+
// controls/Effects/NewEffect.js
|
|
744
|
+
|
|
745
|
+
const NewEffect = {
|
|
746
|
+
title: "Effect Intensity",
|
|
747
|
+
id: "effectIntensity",
|
|
748
|
+
format: "effect-[{{value}}%]",
|
|
749
|
+
slider: {
|
|
750
|
+
default: 50,
|
|
751
|
+
min: 0,
|
|
752
|
+
max: 100,
|
|
753
|
+
round: true,
|
|
754
|
+
units: "%"
|
|
755
|
+
}
|
|
756
|
+
};
|
|
757
|
+
|
|
758
|
+
export default NewEffect;
|
|
759
|
+
```
|
|
760
|
+
|
|
761
|
+
### Step 2: Export from index.js
|
|
762
|
+
|
|
763
|
+
Add the export to `controls/index.js`:
|
|
764
|
+
|
|
765
|
+
```javascript
|
|
766
|
+
// In the appropriate section
|
|
767
|
+
export { default as NewEffect } from "./Effects/NewEffect.js";
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
### Step 3: Use in Config Files
|
|
771
|
+
|
|
772
|
+
Reference in any `properties.config.json`:
|
|
773
|
+
|
|
774
|
+
```json
|
|
775
|
+
{
|
|
776
|
+
"globalControl": "NewEffect"
|
|
777
|
+
}
|
|
778
|
+
```
|
|
779
|
+
|
|
780
|
+
### Step 4: Rebuild
|
|
781
|
+
|
|
782
|
+
```bash
|
|
783
|
+
cd src
|
|
784
|
+
npm run build
|
|
785
|
+
```
|
|
786
|
+
|
|
787
|
+
### Creating Compound Controls
|
|
788
|
+
|
|
789
|
+
For controls with multiple UI elements:
|
|
790
|
+
|
|
791
|
+
```javascript
|
|
792
|
+
// controls/interactive/CustomButton.js
|
|
793
|
+
|
|
794
|
+
const CustomButton = [
|
|
795
|
+
{
|
|
796
|
+
title: "Button Settings",
|
|
797
|
+
heading: {}
|
|
798
|
+
},
|
|
799
|
+
{
|
|
800
|
+
title: "Style",
|
|
801
|
+
id: "buttonStyle",
|
|
802
|
+
segmented: {
|
|
803
|
+
default: "solid",
|
|
804
|
+
items: [
|
|
805
|
+
{ value: "solid", title: "Solid" },
|
|
806
|
+
{ value: "outline", title: "Outline" },
|
|
807
|
+
{ value: "ghost", title: "Ghost" }
|
|
808
|
+
]
|
|
809
|
+
}
|
|
810
|
+
},
|
|
811
|
+
{
|
|
812
|
+
title: "Size",
|
|
813
|
+
id: "buttonSize",
|
|
814
|
+
select: {
|
|
815
|
+
use: "ButtonSize"
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
];
|
|
819
|
+
|
|
820
|
+
export default CustomButton;
|
|
821
|
+
```
|
|
822
|
+
|
|
823
|
+
### Creating Controls with Nested globalControls
|
|
824
|
+
|
|
825
|
+
```javascript
|
|
826
|
+
// controls/Layout/CustomLayout.js
|
|
827
|
+
|
|
828
|
+
const CustomLayout = [
|
|
829
|
+
{
|
|
830
|
+
globalControl: "ControlType",
|
|
831
|
+
id: "{{value}}CustomLayout"
|
|
832
|
+
},
|
|
833
|
+
{
|
|
834
|
+
visible: "globalControlTypeCustomLayout != 'none'",
|
|
835
|
+
divider: {}
|
|
836
|
+
},
|
|
837
|
+
{
|
|
838
|
+
visible: "globalControlTypeCustomLayout != 'none'",
|
|
839
|
+
globalControl: "Position"
|
|
840
|
+
},
|
|
841
|
+
{
|
|
842
|
+
visible: "globalControlTypeCustomLayout != 'none'",
|
|
843
|
+
globalControl: "ZIndex"
|
|
844
|
+
}
|
|
845
|
+
];
|
|
846
|
+
|
|
847
|
+
export default CustomLayout;
|
|
848
|
+
```
|
|
849
|
+
|
|
850
|
+
---
|
|
851
|
+
|
|
852
|
+
## Adding New Properties
|
|
853
|
+
|
|
854
|
+
### Step 1: Create the Property File
|
|
855
|
+
|
|
856
|
+
```javascript
|
|
857
|
+
// properties/CustomSizes.js
|
|
858
|
+
|
|
859
|
+
const CustomSizes = {
|
|
860
|
+
default: "md",
|
|
861
|
+
items: [
|
|
862
|
+
{ value: "xs", title: "Extra Small" },
|
|
863
|
+
{ value: "sm", title: "Small" },
|
|
864
|
+
{ value: "md", title: "Medium" },
|
|
865
|
+
{ value: "lg", title: "Large" },
|
|
866
|
+
{ value: "xl", title: "Extra Large" },
|
|
867
|
+
]
|
|
868
|
+
};
|
|
869
|
+
|
|
870
|
+
export default CustomSizes;
|
|
871
|
+
```
|
|
872
|
+
|
|
873
|
+
### Step 2: Export from index.js
|
|
874
|
+
|
|
875
|
+
```javascript
|
|
876
|
+
// properties/index.js
|
|
877
|
+
export { default as CustomSizes } from "./CustomSizes.js";
|
|
878
|
+
```
|
|
879
|
+
|
|
880
|
+
### Step 3: Use in Controls
|
|
881
|
+
|
|
882
|
+
```javascript
|
|
883
|
+
const SizeSelector = {
|
|
884
|
+
title: "Size",
|
|
885
|
+
id: "elementSize",
|
|
886
|
+
select: {
|
|
887
|
+
use: "CustomSizes"
|
|
888
|
+
}
|
|
889
|
+
};
|
|
890
|
+
```
|
|
891
|
+
|
|
892
|
+
Or use directly in config files:
|
|
893
|
+
|
|
894
|
+
```json
|
|
895
|
+
{
|
|
896
|
+
"title": "Size",
|
|
897
|
+
"id": "mySize",
|
|
898
|
+
"select": {
|
|
899
|
+
"use": "CustomSizes"
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
```
|
|
903
|
+
|
|
904
|
+
---
|
|
905
|
+
|
|
906
|
+
## Adding Shared Hooks
|
|
907
|
+
|
|
908
|
+
### Step 1: Create the Shared Hook File
|
|
909
|
+
|
|
910
|
+
Create a new file in the appropriate `shared-hooks/` subfolder:
|
|
911
|
+
|
|
912
|
+
```javascript
|
|
913
|
+
// shared-hooks/effects/customEffect.js
|
|
914
|
+
|
|
915
|
+
/**
|
|
916
|
+
* Generate custom effect classes based on element properties
|
|
917
|
+
* @param {Object} rw - The RapidWeaver element object
|
|
918
|
+
* @returns {string} CSS class string
|
|
919
|
+
*/
|
|
920
|
+
function customEffect(rw) {
|
|
921
|
+
const { customEnabled, customIntensity } = rw.props;
|
|
922
|
+
|
|
923
|
+
if (!customEnabled) return '';
|
|
924
|
+
|
|
925
|
+
return classnames([
|
|
926
|
+
'custom-effect',
|
|
927
|
+
customIntensity && `intensity-${customIntensity}`
|
|
928
|
+
]).toString();
|
|
929
|
+
}
|
|
930
|
+
```
|
|
931
|
+
|
|
932
|
+
### Step 2: Use in Component Hooks
|
|
933
|
+
|
|
934
|
+
Reference the function in any `hooks.source.js`:
|
|
935
|
+
|
|
936
|
+
```javascript
|
|
937
|
+
// packs/MyPack.elementsdevpack/components/com.example.mycomponent/hooks.source.js
|
|
938
|
+
|
|
939
|
+
function transformHook(rw) {
|
|
940
|
+
// customEffect is available from shared hooks (no import needed)
|
|
941
|
+
const effectClasses = customEffect(rw);
|
|
942
|
+
|
|
943
|
+
return {
|
|
944
|
+
effectClasses
|
|
945
|
+
};
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
exports.transformHook = transformHook;
|
|
949
|
+
```
|
|
950
|
+
|
|
951
|
+
### Step 3: Build
|
|
952
|
+
|
|
953
|
+
```bash
|
|
954
|
+
npm run build:hooks
|
|
955
|
+
```
|
|
956
|
+
|
|
957
|
+
### Naming Conventions
|
|
958
|
+
|
|
959
|
+
- **Folder organization**: Place files in the appropriate category folder
|
|
960
|
+
- **Prefix with `global`**: For element property processing functions (e.g., `globalSpacing`)
|
|
961
|
+
- **Use descriptive names**: Match the function name to the file name
|
|
962
|
+
|
|
963
|
+
| Category | Folder | Example |
|
|
964
|
+
|----------|--------|---------|
|
|
965
|
+
| Core utilities | `core/` | `classnames.js`, `getHoverPrefix.js` |
|
|
966
|
+
| Layout functions | `layout/` | `globalLayout.js` |
|
|
967
|
+
| Visual effects | `effects/` | `globalEffects.js` |
|
|
968
|
+
| Typography | `typography/` | `globalHeadingColor.js` |
|
|
969
|
+
|
|
970
|
+
### Dead Code Elimination
|
|
971
|
+
|
|
972
|
+
The build system automatically removes unused code. If you add a function to shared hooks but no component uses it, it won't appear in any output `hooks.js` file. This keeps the output lean.
|
|
973
|
+
|
|
974
|
+
### Example: Complex Shared Hook
|
|
975
|
+
|
|
976
|
+
```javascript
|
|
977
|
+
// shared-hooks/layout/globalLayout.js
|
|
978
|
+
|
|
979
|
+
/**
|
|
980
|
+
* Generate layout-related CSS classes
|
|
981
|
+
*/
|
|
982
|
+
const globalLayout = (app, args = {}) => {
|
|
983
|
+
const {
|
|
984
|
+
globalLayoutPosition: position,
|
|
985
|
+
globalLayoutZIndex: zIndex,
|
|
986
|
+
globalLayoutOverflow: overflow,
|
|
987
|
+
} = app.props;
|
|
988
|
+
|
|
989
|
+
return classnames([
|
|
990
|
+
position,
|
|
991
|
+
zIndex,
|
|
992
|
+
overflow,
|
|
993
|
+
]).toString();
|
|
994
|
+
};
|
|
995
|
+
```
|
|
996
|
+
|
|
997
|
+
---
|
|
998
|
+
|
|
999
|
+
## Advanced Features
|
|
1000
|
+
|
|
1001
|
+
### Conditional Visibility
|
|
1002
|
+
|
|
1003
|
+
Show/hide controls based on other property values:
|
|
1004
|
+
|
|
1005
|
+
```json
|
|
1006
|
+
{
|
|
1007
|
+
"title": "Custom Value",
|
|
1008
|
+
"id": "customValue",
|
|
1009
|
+
"visible": "sizeType == 'custom'",
|
|
1010
|
+
"number": {
|
|
1011
|
+
"default": 100
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
```
|
|
1015
|
+
|
|
1016
|
+
Complex conditions:
|
|
1017
|
+
|
|
1018
|
+
```json
|
|
1019
|
+
{
|
|
1020
|
+
"visible": "enableFeature == true && mode == 'advanced'"
|
|
1021
|
+
}
|
|
1022
|
+
```
|
|
1023
|
+
|
|
1024
|
+
### Format Strings
|
|
1025
|
+
|
|
1026
|
+
Generate CSS class names from values:
|
|
1027
|
+
|
|
1028
|
+
```json
|
|
1029
|
+
{
|
|
1030
|
+
"id": "gapX",
|
|
1031
|
+
"format": "gap-x-{{value}}",
|
|
1032
|
+
"themeSpacing": {
|
|
1033
|
+
"default": { "base": { "value": "4" } }
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
```
|
|
1037
|
+
|
|
1038
|
+
Output when value is "8": `gap-x-8`
|
|
1039
|
+
|
|
1040
|
+
### Responsive Controls
|
|
1041
|
+
|
|
1042
|
+
By default, controls are responsive. Disable with:
|
|
1043
|
+
|
|
1044
|
+
```json
|
|
1045
|
+
{
|
|
1046
|
+
"id": "staticValue",
|
|
1047
|
+
"responsive": false,
|
|
1048
|
+
"text": { "default": "" }
|
|
1049
|
+
}
|
|
1050
|
+
```
|
|
1051
|
+
|
|
1052
|
+
### The Advanced Group
|
|
1053
|
+
|
|
1054
|
+
The build system automatically:
|
|
1055
|
+
1. Injects `CSSClasses` and `ID` controls at the start of the Advanced group
|
|
1056
|
+
2. Creates an Advanced group if one doesn't exist
|
|
1057
|
+
3. Moves any existing Advanced group to the end
|
|
1058
|
+
4. Sets the icon to "gearshape"
|
|
1059
|
+
|
|
1060
|
+
To add controls to the Advanced group:
|
|
1061
|
+
|
|
1062
|
+
```json
|
|
1063
|
+
{
|
|
1064
|
+
"groups": [
|
|
1065
|
+
{
|
|
1066
|
+
"title": "Advanced",
|
|
1067
|
+
"properties": [
|
|
1068
|
+
{ "divider": {} },
|
|
1069
|
+
{ "globalControl": "HTMLTag" }
|
|
1070
|
+
]
|
|
1071
|
+
}
|
|
1072
|
+
]
|
|
1073
|
+
}
|
|
1074
|
+
```
|
|
1075
|
+
|
|
1076
|
+
---
|
|
1077
|
+
|
|
1078
|
+
## Build Commands
|
|
1079
|
+
|
|
1080
|
+
### Using the CLI (recommended)
|
|
1081
|
+
|
|
1082
|
+
```bash
|
|
1083
|
+
# Build everything (properties + hooks)
|
|
1084
|
+
rw-build all
|
|
1085
|
+
|
|
1086
|
+
# Build properties only
|
|
1087
|
+
rw-build properties
|
|
1088
|
+
|
|
1089
|
+
# Build hooks only
|
|
1090
|
+
rw-build hooks
|
|
1091
|
+
|
|
1092
|
+
# Watch for changes
|
|
1093
|
+
rw-build all --watch # Watch both properties and hooks
|
|
1094
|
+
rw-build properties --watch # Watch properties only
|
|
1095
|
+
rw-build hooks --watch # Watch hooks only
|
|
1096
|
+
|
|
1097
|
+
# Build with custom packs directory
|
|
1098
|
+
rw-build all --packs ./my-elements
|
|
1099
|
+
|
|
1100
|
+
# Show help
|
|
1101
|
+
rw-build --help
|
|
1102
|
+
```
|
|
1103
|
+
|
|
1104
|
+
### Using npm scripts
|
|
1105
|
+
|
|
1106
|
+
Add these to your `package.json`:
|
|
1107
|
+
|
|
1108
|
+
```json
|
|
1109
|
+
{
|
|
1110
|
+
"scripts": {
|
|
1111
|
+
"build": "rw-build all",
|
|
1112
|
+
"build:properties": "rw-build properties",
|
|
1113
|
+
"build:hooks": "rw-build hooks",
|
|
1114
|
+
"dev": "rw-build all --watch"
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
```
|
|
1118
|
+
|
|
1119
|
+
Then run:
|
|
1120
|
+
|
|
1121
|
+
```bash
|
|
1122
|
+
npm run build
|
|
1123
|
+
npm run dev
|
|
1124
|
+
```
|
|
1125
|
+
|
|
1126
|
+
### Development Mode Details
|
|
1127
|
+
|
|
1128
|
+
The `--watch` flag monitors for changes and automatically rebuilds:
|
|
1129
|
+
|
|
1130
|
+
| Command | Watches |
|
|
1131
|
+
|---------|---------|
|
|
1132
|
+
| `rw-build all --watch` | Both properties and hooks (runs watchers concurrently) |
|
|
1133
|
+
| `rw-build properties --watch` | `properties.config.json` files in `packs/` |
|
|
1134
|
+
| `rw-build hooks --watch` | `hooks.source.js` files in `packs/` and `shared-hooks/*.js` |
|
|
1135
|
+
|
|
1136
|
+
### Troubleshooting
|
|
1137
|
+
|
|
1138
|
+
**"Global control 'X' not found"**
|
|
1139
|
+
- Check the control is exported in `controls/index.js`
|
|
1140
|
+
- Verify the spelling matches exactly (case-sensitive)
|
|
1141
|
+
|
|
1142
|
+
**"Property 'X' not found in Properties"**
|
|
1143
|
+
- Check the property is exported in `properties/index.js`
|
|
1144
|
+
- Verify the `use` key spelling
|
|
1145
|
+
|
|
1146
|
+
**Build produces unexpected output**
|
|
1147
|
+
- Check for circular `globalControl` references
|
|
1148
|
+
- Verify JSON syntax in config files
|
|
1149
|
+
- Run with `--trace-warnings` flag for more details
|
|
1150
|
+
|
|
1151
|
+
---
|
|
1152
|
+
|
|
1153
|
+
## Example: Complete Element Config
|
|
1154
|
+
|
|
1155
|
+
```json
|
|
1156
|
+
{
|
|
1157
|
+
"groups": [
|
|
1158
|
+
{
|
|
1159
|
+
"title": "Content",
|
|
1160
|
+
"icon": "text.alignleft",
|
|
1161
|
+
"properties": [
|
|
1162
|
+
{
|
|
1163
|
+
"title": "Heading",
|
|
1164
|
+
"id": "headingText",
|
|
1165
|
+
"text": {
|
|
1166
|
+
"default": "Welcome"
|
|
1167
|
+
}
|
|
1168
|
+
},
|
|
1169
|
+
{
|
|
1170
|
+
"globalControl": "HeadingColor"
|
|
1171
|
+
}
|
|
1172
|
+
]
|
|
1173
|
+
},
|
|
1174
|
+
{
|
|
1175
|
+
"title": "Layout",
|
|
1176
|
+
"icon": "square.split.bottomrightquarter",
|
|
1177
|
+
"properties": [
|
|
1178
|
+
{
|
|
1179
|
+
"globalControl": "Layout"
|
|
1180
|
+
}
|
|
1181
|
+
]
|
|
1182
|
+
},
|
|
1183
|
+
{
|
|
1184
|
+
"title": "Spacing",
|
|
1185
|
+
"icon": "squareshape.squareshape.dotted",
|
|
1186
|
+
"properties": [
|
|
1187
|
+
{
|
|
1188
|
+
"globalControl": "Spacing"
|
|
1189
|
+
}
|
|
1190
|
+
]
|
|
1191
|
+
},
|
|
1192
|
+
{
|
|
1193
|
+
"title": "Background",
|
|
1194
|
+
"icon": "paintbrush.fill",
|
|
1195
|
+
"properties": [
|
|
1196
|
+
{
|
|
1197
|
+
"globalControl": "BackgroundTransparent"
|
|
1198
|
+
}
|
|
1199
|
+
]
|
|
1200
|
+
},
|
|
1201
|
+
{
|
|
1202
|
+
"title": "Borders",
|
|
1203
|
+
"icon": "square.dashed",
|
|
1204
|
+
"properties": [
|
|
1205
|
+
{
|
|
1206
|
+
"globalControl": "Borders"
|
|
1207
|
+
}
|
|
1208
|
+
]
|
|
1209
|
+
},
|
|
1210
|
+
{
|
|
1211
|
+
"title": "Advanced",
|
|
1212
|
+
"properties": [
|
|
1213
|
+
{
|
|
1214
|
+
"divider": {}
|
|
1215
|
+
},
|
|
1216
|
+
{
|
|
1217
|
+
"globalControl": "HTMLTag"
|
|
1218
|
+
}
|
|
1219
|
+
]
|
|
1220
|
+
}
|
|
1221
|
+
]
|
|
1222
|
+
}
|
|
1223
|
+
```
|
|
1224
|
+
|
|
1225
|
+
---
|
|
1226
|
+
|
|
1227
|
+
## Programmatic Usage
|
|
1228
|
+
|
|
1229
|
+
You can also use rw-elements-tools programmatically in your own build scripts:
|
|
1230
|
+
|
|
1231
|
+
```javascript
|
|
1232
|
+
import {
|
|
1233
|
+
buildProperties,
|
|
1234
|
+
buildHooks,
|
|
1235
|
+
watchProperties,
|
|
1236
|
+
watchHooks,
|
|
1237
|
+
resolveConfig,
|
|
1238
|
+
Controls,
|
|
1239
|
+
Properties
|
|
1240
|
+
} from 'rw-elements-tools';
|
|
1241
|
+
|
|
1242
|
+
// Resolve configuration from all sources
|
|
1243
|
+
const config = await resolveConfig({
|
|
1244
|
+
packs: './my-elements' // Optional CLI override
|
|
1245
|
+
});
|
|
1246
|
+
|
|
1247
|
+
// Build properties
|
|
1248
|
+
await buildProperties(config);
|
|
1249
|
+
|
|
1250
|
+
// Build hooks
|
|
1251
|
+
await buildHooks(config);
|
|
1252
|
+
|
|
1253
|
+
// Watch for changes
|
|
1254
|
+
await watchProperties(config); // Watch properties only
|
|
1255
|
+
await watchHooks(config); // Watch hooks only
|
|
1256
|
+
|
|
1257
|
+
// Or watch both concurrently
|
|
1258
|
+
await Promise.all([
|
|
1259
|
+
watchProperties(config),
|
|
1260
|
+
watchHooks(config)
|
|
1261
|
+
]);
|
|
1262
|
+
```
|
|
1263
|
+
|
|
1264
|
+
### Accessing Controls and Properties
|
|
1265
|
+
|
|
1266
|
+
```javascript
|
|
1267
|
+
import { Controls, Properties } from 'rw-elements-tools';
|
|
1268
|
+
|
|
1269
|
+
// Use a control definition
|
|
1270
|
+
console.log(Controls.Spacing);
|
|
1271
|
+
console.log(Controls.BorderRadius);
|
|
1272
|
+
|
|
1273
|
+
// Use a property definition
|
|
1274
|
+
console.log(Properties.FontWeight);
|
|
1275
|
+
console.log(Properties.Slider);
|
|
1276
|
+
```
|
|
1277
|
+
|
|
1278
|
+
### Custom Build Pipeline
|
|
1279
|
+
|
|
1280
|
+
```javascript
|
|
1281
|
+
import { resolveConfig, buildProperties, buildHooks } from 'rw-elements-tools';
|
|
1282
|
+
|
|
1283
|
+
async function customBuild() {
|
|
1284
|
+
const config = await resolveConfig();
|
|
1285
|
+
|
|
1286
|
+
console.log('Building for:', config.packsDir);
|
|
1287
|
+
|
|
1288
|
+
// Build properties first
|
|
1289
|
+
await buildProperties(config);
|
|
1290
|
+
|
|
1291
|
+
// Then build hooks
|
|
1292
|
+
await buildHooks(config);
|
|
1293
|
+
|
|
1294
|
+
console.log('Build complete!');
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
customBuild();
|
|
1298
|
+
```
|
|
1299
|
+
|
|
1300
|
+
---
|
|
1301
|
+
|
|
1302
|
+
## Publishing
|
|
1303
|
+
|
|
1304
|
+
To publish the package to npm:
|
|
1305
|
+
|
|
1306
|
+
```bash
|
|
1307
|
+
cd src
|
|
1308
|
+
npm login
|
|
1309
|
+
npm publish
|
|
1310
|
+
```
|
|
1311
|
+
|
|
1312
|
+
For scoped packages:
|
|
1313
|
+
|
|
1314
|
+
```bash
|
|
1315
|
+
npm publish --access public
|
|
1316
|
+
```
|
|
1317
|
+
|
|
1318
|
+
---
|
|
1319
|
+
|
|
1320
|
+
## Contributing
|
|
1321
|
+
|
|
1322
|
+
When adding new controls or properties:
|
|
1323
|
+
|
|
1324
|
+
1. **Follow naming conventions**: PascalCase for exports, descriptive names
|
|
1325
|
+
2. **Place in correct directory**: Use the categorical structure
|
|
1326
|
+
3. **Add exports**: Update the relevant `index.js`
|
|
1327
|
+
4. **Test the build**: Run `npm run build` and verify output
|
|
1328
|
+
5. **Document**: Add comments for complex controls
|
|
1329
|
+
|
|
1330
|
+
---
|
|
1331
|
+
|
|
1332
|
+
*Last updated: January 2026*
|
|
1333
|
+
|