web-manager 4.0.21 → 4.0.23

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
@@ -379,13 +379,37 @@ When an element is bound, Web Manager automatically adds the `wm-bound` class to
379
379
  }
380
380
  ```
381
381
 
382
+ #### Style Bindings
383
+ Set CSS custom properties (CSS variables) or inline styles dynamically:
384
+
385
+ ```html
386
+ <!-- Set CSS custom property -->
387
+ <div data-wm-bind="@style --rating-percent site.ratings.starWidth"></div>
388
+
389
+ <!-- Set regular style property -->
390
+ <div data-wm-bind="@style width user.profile.width"></div>
391
+
392
+ <!-- Multiple styles -->
393
+ <div data-wm-bind="@style --primary-color theme.primaryColor, @style --secondary-color theme.secondaryColor"></div>
394
+ ```
395
+
396
+ Then use the custom property in your CSS:
397
+
398
+ ```css
399
+ .rating-stars::before {
400
+ width: var(--rating-percent, 0%);
401
+ background: var(--primary-color, #007bff);
402
+ }
403
+ ```
404
+
382
405
  #### Supported Actions
383
406
  - **`@text`** (default): Sets the text content of the element
384
407
  - **`@show`**: Shows the element when condition is true
385
408
  - **`@hide`**: Hides the element when condition is true
386
409
  - **`@attr`**: Sets an attribute value (format: `@attr attributeName expression`)
410
+ - **`@style`**: Sets a CSS custom property or inline style (format: `@style propertyName expression`)
387
411
 
388
- Future actions like `@class` and `@style` can be easily added.
412
+ Future actions like `@class` can be easily added.
389
413
 
390
414
  ### Firebase Authentication
391
415
 
@@ -13,7 +13,10 @@ class Bindings {
13
13
  ...data
14
14
  };
15
15
 
16
- this._updateBindings(this._context);
16
+ // Get the top-level keys that were updated
17
+ const updatedKeys = Object.keys(data);
18
+
19
+ this._updateBindings(this._context, updatedKeys);
17
20
  }
18
21
 
19
22
  // Get current context
@@ -24,11 +27,11 @@ class Bindings {
24
27
  // Clear all context
25
28
  clear() {
26
29
  this._context = {};
27
- this._updateBindings(this._context);
30
+ this._updateBindings(this._context, null); // null = update all bindings
28
31
  }
29
32
 
30
33
  // Main binding update system
31
- _updateBindings(context) {
34
+ _updateBindings(context, updatedKeys = null) {
32
35
  // Find all elements with data-wm-bind attribute
33
36
  const bindElements = document.querySelectorAll('[data-wm-bind]');
34
37
 
@@ -40,13 +43,16 @@ class Bindings {
40
43
 
41
44
  // Execute each action
42
45
  bindings.forEach(({ action, expression }) => {
43
- this._executeAction(element, action, expression, context);
46
+ this._executeAction(element, action, expression, context, updatedKeys);
44
47
  });
45
48
 
46
- // Add bound class to indicate element has been processed
47
- if (!element.classList.contains('wm-bound')) {
48
- element.classList.add('wm-bound');
49
- }
49
+ // Add bound class to trigger fade out
50
+ element.classList.add('wm-bound');
51
+
52
+ // Remove skeleton class after fade completes
53
+ setTimeout(() => {
54
+ element.classList.remove('wm-binding-skeleton');
55
+ }, 300);
50
56
  });
51
57
  }
52
58
 
@@ -82,10 +88,16 @@ class Bindings {
82
88
  }
83
89
 
84
90
  // Execute a single action on an element
85
- _executeAction(element, action, expression, context) {
91
+ _executeAction(element, action, expression, context, updatedKeys = null) {
86
92
  switch (action) {
87
93
  case '@show':
88
94
  // Show element if condition is true (or always if no condition)
95
+
96
+ // Check if this path should be updated
97
+ if (!this._shouldUpdatePath(expression, updatedKeys)) {
98
+ return;
99
+ }
100
+
89
101
  const shouldShow = expression ? this._evaluateCondition(expression, context) : true;
90
102
  if (shouldShow) {
91
103
  element.removeAttribute('hidden');
@@ -96,6 +108,12 @@ class Bindings {
96
108
 
97
109
  case '@hide':
98
110
  // Hide element if condition is true (or always if no condition)
111
+
112
+ // Check if this path should be updated
113
+ if (!this._shouldUpdatePath(expression, updatedKeys)) {
114
+ return;
115
+ }
116
+
99
117
  const shouldHide = expression ? this._evaluateCondition(expression, context) : true;
100
118
  if (shouldHide) {
101
119
  element.setAttribute('hidden', '');
@@ -110,6 +128,12 @@ class Bindings {
110
128
  const attrParts = expression.split(' ');
111
129
  const attrName = attrParts[0];
112
130
  const attrExpression = attrParts.slice(1).join(' ');
131
+
132
+ // Check if this path should be updated
133
+ if (!this._shouldUpdatePath(attrExpression, updatedKeys)) {
134
+ return;
135
+ }
136
+
113
137
  const attrValue = this._resolvePath(context, attrExpression) || '';
114
138
 
115
139
  if (attrValue) {
@@ -119,9 +143,47 @@ class Bindings {
119
143
  }
120
144
  break;
121
145
 
146
+ case '@style':
147
+ // Set CSS custom property or style
148
+ // Format: @style propertyName expression
149
+ const styleParts = expression.split(' ');
150
+ const styleName = styleParts[0];
151
+ const styleExpression = styleParts.slice(1).join(' ');
152
+
153
+ // Check if this path should be updated
154
+ if (!this._shouldUpdatePath(styleExpression, updatedKeys)) {
155
+ return;
156
+ }
157
+
158
+ const styleValue = this._resolvePath(context, styleExpression);
159
+
160
+ if (styleValue !== null && styleValue !== undefined && styleValue !== '') {
161
+ // If it starts with --, it's a CSS custom property
162
+ if (styleName.startsWith('--')) {
163
+ element.style.setProperty(styleName, styleValue);
164
+ } else {
165
+ // Regular style property
166
+ element.style[styleName] = styleValue;
167
+ }
168
+ } else {
169
+ // Remove the style if value is empty
170
+ if (styleName.startsWith('--')) {
171
+ element.style.removeProperty(styleName);
172
+ } else {
173
+ element.style[styleName] = '';
174
+ }
175
+ }
176
+ break;
177
+
122
178
  case '@text':
123
179
  default:
124
180
  // Set text content (default behavior)
181
+
182
+ // Check if this path should be updated
183
+ if (!this._shouldUpdatePath(expression, updatedKeys)) {
184
+ return;
185
+ }
186
+
125
187
  const value = this._resolvePath(context, expression) || '';
126
188
 
127
189
  if (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA') {
@@ -130,11 +192,21 @@ class Bindings {
130
192
  element.textContent = value;
131
193
  }
132
194
  break;
195
+ }
196
+ }
133
197
 
134
- // Future actions can be added here:
135
- // case '@class':
136
- // case '@style':
198
+ // Check if a path should be updated based on updatedKeys
199
+ _shouldUpdatePath(path, updatedKeys) {
200
+ // If no updatedKeys filter, always update
201
+ if (updatedKeys === null || !path) {
202
+ return true;
137
203
  }
204
+
205
+ // Extract the root key from the path
206
+ const rootKey = path.trim().split('.')[0];
207
+
208
+ // Only update if the root key is in updatedKeys
209
+ return updatedKeys.includes(rootKey);
138
210
  }
139
211
 
140
212
  // Resolve nested object path
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "web-manager",
3
- "version": "4.0.21",
3
+ "version": "4.0.23",
4
4
  "description": "Easily access important variables such as the query string, current domain, and current page in a single object.",
5
5
  "main": "dist/index.js",
6
6
  "module": "src/index.js",