hyperclayjs 1.5.0 → 1.7.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 +0 -5
- package/README.md +7 -10
- package/core/optionVisibility.js +216 -0
- package/core/savePage.js +2 -0
- package/core/savePageCore.js +1 -1
- package/hyperclay.js +2 -4
- package/module-dependency-graph.json +8 -28
- package/package.json +1 -1
- package/core/optionVisibilityRuleGenerator.js +0 -171
- package/vendor/tailwind-base.css +0 -1471
- package/vendor/tailwind-play.js +0 -23
- package/vendor/tailwind-play.vendor.js +0 -169
package/LICENSE
CHANGED
|
@@ -31,11 +31,6 @@ This project includes the following third-party libraries:
|
|
|
31
31
|
- License: 0BSD (Zero-Clause BSD)
|
|
32
32
|
- https://github.com/bigskysoftware/idiomorph
|
|
33
33
|
|
|
34
|
-
### Tailwind CSS
|
|
35
|
-
- Copyright (c) Tailwind Labs, Inc.
|
|
36
|
-
- License: MIT
|
|
37
|
-
- https://github.com/tailwindlabs/tailwindcss
|
|
38
|
-
|
|
39
34
|
### Sortable.js
|
|
40
35
|
- Copyright (c) All contributors to Sortable
|
|
41
36
|
- License: MIT
|
package/README.md
CHANGED
|
@@ -60,11 +60,11 @@ import 'hyperclayjs/presets/standard.js';
|
|
|
60
60
|
| autosave | 1.1KB | Auto-save on DOM changes, unsaved changes warning |
|
|
61
61
|
| edit-mode | 1.8KB | Toggle edit mode on hyperclay on/off |
|
|
62
62
|
| edit-mode-helpers | 5.4KB | Admin-only functionality: [edit-mode-input], [edit-mode-resource], [edit-mode-onclick] |
|
|
63
|
-
| option-visibility |
|
|
63
|
+
| option-visibility | 5.9KB | Dynamic show/hide based on ancestor state with option:attribute="value" |
|
|
64
64
|
| persist | 2.5KB | Persist input/select/textarea values to the DOM with [persist] attribute |
|
|
65
65
|
| save-core | 6.3KB | Basic save function only - hyperclay.savePage() |
|
|
66
|
-
| save-system |
|
|
67
|
-
| save-toast | 0.9KB | Toast notifications for save events
|
|
66
|
+
| save-system | 7.1KB | Manual save: keyboard shortcut (CMD+S), save button, change tracking |
|
|
67
|
+
| save-toast | 0.9KB | Toast notifications for save events |
|
|
68
68
|
|
|
69
69
|
### Custom Attributes (HTML enhancements)
|
|
70
70
|
|
|
@@ -82,7 +82,6 @@ import 'hyperclayjs/presets/standard.js';
|
|
|
82
82
|
| Module | Size | Description |
|
|
83
83
|
|--------|------|-------------|
|
|
84
84
|
| dialogs | 8.4KB | ask(), consent(), tell(), snippet() dialog functions |
|
|
85
|
-
| tailwind-play | 0.7KB | Live Tailwind CSS editing, lazy-loads ~370KB script in edit mode only |
|
|
86
85
|
| the-modal | 19.8KB | Full modal window creation system - window.theModal |
|
|
87
86
|
| toast | 7.7KB | Success/error message notifications, toast(msg, msgType) |
|
|
88
87
|
|
|
@@ -128,17 +127,17 @@ import 'hyperclayjs/presets/standard.js';
|
|
|
128
127
|
|
|
129
128
|
## Presets
|
|
130
129
|
|
|
131
|
-
### Minimal (~27.
|
|
130
|
+
### Minimal (~27.8KB)
|
|
132
131
|
Essential features for basic editing
|
|
133
132
|
|
|
134
133
|
**Modules:** `save-core`, `save-system`, `edit-mode-helpers`, `toast`, `save-toast`, `export-to-window`
|
|
135
134
|
|
|
136
|
-
### Standard (~
|
|
135
|
+
### Standard (~45.5KB)
|
|
137
136
|
Standard feature set for most use cases
|
|
138
137
|
|
|
139
138
|
**Modules:** `save-core`, `save-system`, `edit-mode-helpers`, `persist`, `option-visibility`, `event-attrs`, `dom-helpers`, `toast`, `save-toast`, `export-to-window`
|
|
140
139
|
|
|
141
|
-
### Everything (~
|
|
140
|
+
### Everything (~150.2KB)
|
|
142
141
|
All available features
|
|
143
142
|
|
|
144
143
|
Includes all available modules across all categories.
|
|
@@ -150,7 +149,6 @@ Some modules with large vendor dependencies are **lazy-loaded** to optimize page
|
|
|
150
149
|
| Module | Wrapper Size | Vendor Size | Loaded When |
|
|
151
150
|
|--------|-------------|-------------|-------------|
|
|
152
151
|
| `sortable` | ~3KB | ~118KB | Edit mode only |
|
|
153
|
-
| `tailwind-play` | ~1KB | ~370KB | Edit mode only |
|
|
154
152
|
|
|
155
153
|
**How it works:**
|
|
156
154
|
- The wrapper module checks if the page is in edit mode (`isEditMode`)
|
|
@@ -160,7 +158,7 @@ Some modules with large vendor dependencies are **lazy-loaded** to optimize page
|
|
|
160
158
|
|
|
161
159
|
This means:
|
|
162
160
|
- **Editors** get full functionality when needed
|
|
163
|
-
- **Viewers** never download
|
|
161
|
+
- **Viewers** never download the heavy vendor scripts
|
|
164
162
|
- **Saved pages** stay clean with no leftover script tags
|
|
165
163
|
|
|
166
164
|
## Visual Configurator
|
|
@@ -369,7 +367,6 @@ MIT © Hyperclay
|
|
|
369
367
|
This project includes the following open-source libraries:
|
|
370
368
|
|
|
371
369
|
- **[Idiomorph](https://github.com/bigskysoftware/idiomorph)** - DOM morphing library by Big Sky Software (0BSD)
|
|
372
|
-
- **[Tailwind CSS](https://github.com/tailwindlabs/tailwindcss)** - Utility-first CSS framework by Tailwind Labs (MIT)
|
|
373
370
|
- **[Sortable.js](https://github.com/SortableJS/Sortable)** - Drag-and-drop library (MIT)
|
|
374
371
|
|
|
375
372
|
## Links
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Option Visibility (CSS Layers Implementation)
|
|
3
|
+
*
|
|
4
|
+
* Shows/hides elements based on `option:` attributes and ancestor matches.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* <div editmode="true">
|
|
8
|
+
* <button option:editmode="true">Visible</button>
|
|
9
|
+
* <button option:editmode="false">Hidden</button>
|
|
10
|
+
* </div>
|
|
11
|
+
*
|
|
12
|
+
* An element with `option:name="value"` is hidden by default.
|
|
13
|
+
* It becomes visible when ANY ancestor has `name="value"`.
|
|
14
|
+
*
|
|
15
|
+
* ---
|
|
16
|
+
*
|
|
17
|
+
* HOW IT WORKS:
|
|
18
|
+
* 1. Uses `display: none !important` to forcefully hide elements
|
|
19
|
+
* 2. Uses `display: revert-layer !important` to un-hide when ancestor matches
|
|
20
|
+
* `revert-layer` tells the browser: "Ignore rules in this layer, fall back to author styles"
|
|
21
|
+
* 3. This preserves the user's original `display` (flex, grid, block) without us knowing what it is
|
|
22
|
+
*
|
|
23
|
+
* BROWSER SUPPORT:
|
|
24
|
+
* Requires `@layer` and `revert-layer` support (~92% of browsers, 2022+).
|
|
25
|
+
* Falls back gracefully - elements remain visible if unsupported.
|
|
26
|
+
*
|
|
27
|
+
* TRADEOFFS:
|
|
28
|
+
* - Pro: Pure CSS after generation, zero JS overhead for toggling
|
|
29
|
+
* - Pro: Simple code, similar to original approach
|
|
30
|
+
* - Con: Loses to user `!important` rules (layered !important < unlayered !important)
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import Mutation from "../utilities/mutation.js";
|
|
34
|
+
|
|
35
|
+
const optionVisibility = {
|
|
36
|
+
debug: false,
|
|
37
|
+
_started: false,
|
|
38
|
+
_styleElement: null,
|
|
39
|
+
_unsubscribe: null,
|
|
40
|
+
|
|
41
|
+
LAYER_NAME: 'option-visibility',
|
|
42
|
+
STYLE_CLASS: 'option-visibility-layer-styles',
|
|
43
|
+
|
|
44
|
+
log(...args) {
|
|
45
|
+
if (this.debug) console.log('[OptionVisibility:Layer]', ...args);
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Check if browser supports the layer approach
|
|
50
|
+
*/
|
|
51
|
+
isSupported() {
|
|
52
|
+
return typeof CSS !== 'undefined'
|
|
53
|
+
&& typeof CSS.supports === 'function'
|
|
54
|
+
&& CSS.supports('display', 'revert-layer');
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Find all unique option:name="value" patterns using XPath (faster than regex on HTML)
|
|
59
|
+
*/
|
|
60
|
+
findOptionAttributes() {
|
|
61
|
+
const patterns = new Map();
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
const snapshot = document.evaluate(
|
|
65
|
+
'//*[@*[starts-with(name(), "option:")]]',
|
|
66
|
+
document.documentElement,
|
|
67
|
+
null,
|
|
68
|
+
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
|
|
69
|
+
null
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
for (let i = 0; i < snapshot.snapshotLength; i++) {
|
|
73
|
+
const el = snapshot.snapshotItem(i);
|
|
74
|
+
for (const attr of el.attributes) {
|
|
75
|
+
if (attr.name.startsWith('option:')) {
|
|
76
|
+
const name = attr.name.slice(7);
|
|
77
|
+
const value = attr.value;
|
|
78
|
+
const key = `${name}=${value}`;
|
|
79
|
+
if (!patterns.has(key)) {
|
|
80
|
+
patterns.set(key, { name, value });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
} catch (error) {
|
|
86
|
+
this.log('XPath error, falling back to empty', error);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return [...patterns.values()];
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Generate CSS rules wrapped in @layer
|
|
94
|
+
*/
|
|
95
|
+
generateCSS(attributes) {
|
|
96
|
+
if (!attributes.length) return '';
|
|
97
|
+
|
|
98
|
+
const rules = attributes.map(({ name, value }) => {
|
|
99
|
+
const safeName = CSS.escape(name);
|
|
100
|
+
const safeValue = CSS.escape(value);
|
|
101
|
+
|
|
102
|
+
// Hidden by default, visible when ancestor matches
|
|
103
|
+
// Both rules need !important for consistency within the layer
|
|
104
|
+
return `[option\\:${safeName}="${safeValue}"]{display:none!important}[${safeName}="${safeValue}"] [option\\:${safeName}="${safeValue}"]{display:revert-layer!important}`;
|
|
105
|
+
}).join('');
|
|
106
|
+
|
|
107
|
+
return `@layer ${this.LAYER_NAME}{${rules}}`;
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Update the style element with current rules
|
|
112
|
+
*/
|
|
113
|
+
update() {
|
|
114
|
+
if (!this.isSupported()) {
|
|
115
|
+
this.log('Browser lacks revert-layer support, skipping');
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
const attributes = this.findOptionAttributes();
|
|
121
|
+
const css = this.generateCSS(attributes);
|
|
122
|
+
|
|
123
|
+
// Remove style element if no attributes
|
|
124
|
+
if (!css) {
|
|
125
|
+
if (this._styleElement) {
|
|
126
|
+
this._styleElement.remove();
|
|
127
|
+
this._styleElement = null;
|
|
128
|
+
this.log('Removed empty style element');
|
|
129
|
+
}
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Skip if unchanged
|
|
134
|
+
if (this._styleElement?.textContent === css) {
|
|
135
|
+
this.log('Styles unchanged');
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Create or update
|
|
140
|
+
if (!this._styleElement) {
|
|
141
|
+
this._styleElement = document.createElement('style');
|
|
142
|
+
this._styleElement.className = this.STYLE_CLASS;
|
|
143
|
+
document.head.appendChild(this._styleElement);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
this._styleElement.textContent = css;
|
|
147
|
+
this.log(`Generated ${attributes.length} rules`);
|
|
148
|
+
|
|
149
|
+
} catch (error) {
|
|
150
|
+
console.error('[OptionVisibility:Layer] Error generating rules:', error);
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
start() {
|
|
155
|
+
if (this._started) return;
|
|
156
|
+
|
|
157
|
+
if (document.readyState === 'loading') {
|
|
158
|
+
document.addEventListener('DOMContentLoaded', () => this.start(), { once: true });
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
this._started = true;
|
|
163
|
+
|
|
164
|
+
if (!this.isSupported()) {
|
|
165
|
+
console.warn('[OptionVisibility:Layer] Browser lacks revert-layer support. Elements will remain visible.');
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
this.update();
|
|
170
|
+
|
|
171
|
+
// selectorFilter only triggers on option:* attribute changes (new patterns).
|
|
172
|
+
// Ancestor attribute changes (e.g., editmode="true" -> "false") are handled
|
|
173
|
+
// automatically by the browser - CSS rules re-evaluate when attributes change.
|
|
174
|
+
this._unsubscribe = Mutation.onAnyChange({
|
|
175
|
+
debounce: 200,
|
|
176
|
+
selectorFilter: el => [...el.attributes].some(attr => attr.name.startsWith('option:')),
|
|
177
|
+
omitChangeDetails: true
|
|
178
|
+
}, () => this.update());
|
|
179
|
+
|
|
180
|
+
this.log('Started');
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
stop() {
|
|
184
|
+
if (!this._started) return;
|
|
185
|
+
|
|
186
|
+
this._started = false;
|
|
187
|
+
|
|
188
|
+
if (this._unsubscribe) {
|
|
189
|
+
this._unsubscribe();
|
|
190
|
+
this._unsubscribe = null;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (this._styleElement) {
|
|
194
|
+
this._styleElement.remove();
|
|
195
|
+
this._styleElement = null;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
this.log('Stopped');
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
// Auto-export
|
|
203
|
+
if (!window.__hyperclayNoAutoExport) {
|
|
204
|
+
window.optionVisibility = optionVisibility;
|
|
205
|
+
window.hyperclay = window.hyperclay || {};
|
|
206
|
+
window.hyperclay.optionVisibility = optionVisibility;
|
|
207
|
+
window.h = window.hyperclay;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export default optionVisibility;
|
|
211
|
+
|
|
212
|
+
export function init() {
|
|
213
|
+
optionVisibility.start();
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
init();
|
package/core/savePage.js
CHANGED
|
@@ -100,6 +100,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
100
100
|
if (isEditMode) {
|
|
101
101
|
// Capture initial state immediately for comparison
|
|
102
102
|
lastSavedContents = getPageContents();
|
|
103
|
+
// Set initial save status to 'saved'
|
|
104
|
+
document.documentElement.setAttribute('savestatus', 'saved');
|
|
103
105
|
}
|
|
104
106
|
});
|
|
105
107
|
|
package/core/savePageCore.js
CHANGED
|
@@ -121,7 +121,7 @@ export function savePage(callback = () => {}) {
|
|
|
121
121
|
|
|
122
122
|
const msg = err.name === 'AbortError'
|
|
123
123
|
? 'Server not responding'
|
|
124
|
-
:
|
|
124
|
+
: 'Save failed';
|
|
125
125
|
|
|
126
126
|
if (typeof callback === 'function') {
|
|
127
127
|
callback({msg, msgType: "error"});
|
package/hyperclay.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* HyperclayJS v1.
|
|
2
|
+
* HyperclayJS v1.7.0 - Minimal Browser-Native Loader
|
|
3
3
|
*
|
|
4
4
|
* Modules auto-init when imported (no separate init call needed).
|
|
5
5
|
* Include `export-to-window` feature to export to window.hyperclay.
|
|
@@ -31,7 +31,7 @@ const MODULE_PATHS = {
|
|
|
31
31
|
"save-toast": "./core/saveToast.js",
|
|
32
32
|
"edit-mode-helpers": "./core/adminSystem.js",
|
|
33
33
|
"persist": "./core/enablePersistentFormInputValues.js",
|
|
34
|
-
"option-visibility": "./core/
|
|
34
|
+
"option-visibility": "./core/optionVisibility.js",
|
|
35
35
|
"edit-mode": "./core/editmodeSystem.js",
|
|
36
36
|
"event-attrs": "./custom-attributes/events.js",
|
|
37
37
|
"ajax-elements": "./custom-attributes/ajaxElements.js",
|
|
@@ -43,7 +43,6 @@ const MODULE_PATHS = {
|
|
|
43
43
|
"toast": "./ui/toast.js",
|
|
44
44
|
"toast-hyperclay": "./ui/toast-hyperclay.js",
|
|
45
45
|
"the-modal": "./ui/theModal.js",
|
|
46
|
-
"tailwind-play": "./vendor/tailwind-play.js",
|
|
47
46
|
"mutation": "./utilities/mutation.js",
|
|
48
47
|
"nearest": "./utilities/nearest.js",
|
|
49
48
|
"cookie": "./utilities/cookie.js",
|
|
@@ -114,7 +113,6 @@ const PRESETS = {
|
|
|
114
113
|
"toast",
|
|
115
114
|
"toast-hyperclay",
|
|
116
115
|
"the-modal",
|
|
117
|
-
"tailwind-play",
|
|
118
116
|
"mutation",
|
|
119
117
|
"nearest",
|
|
120
118
|
"cookie",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"string-utilities/query.js",
|
|
69
69
|
"utilities/cookie.js"
|
|
70
70
|
],
|
|
71
|
-
"core/
|
|
71
|
+
"core/optionVisibility.js": [
|
|
72
72
|
"utilities/mutation.js"
|
|
73
73
|
],
|
|
74
74
|
"core/savePage.js": [
|
|
@@ -155,14 +155,7 @@
|
|
|
155
155
|
"utilities/pipe.js": [],
|
|
156
156
|
"utilities/throttle.js": [],
|
|
157
157
|
"vendor/Sortable.vendor.js": [],
|
|
158
|
-
"vendor/idiomorph.min.js": []
|
|
159
|
-
"vendor/tailwind-play.js": [
|
|
160
|
-
"core/isAdminOfCurrentResource.js",
|
|
161
|
-
"utilities/loadVendorScript.js"
|
|
162
|
-
],
|
|
163
|
-
"vendor/tailwind-play.vendor.js": [
|
|
164
|
-
"dom-utilities/insertStyleTag.js"
|
|
165
|
-
]
|
|
158
|
+
"vendor/idiomorph.min.js": []
|
|
166
159
|
},
|
|
167
160
|
"modules": {
|
|
168
161
|
"save-core": {
|
|
@@ -182,7 +175,7 @@
|
|
|
182
175
|
"save-system": {
|
|
183
176
|
"name": "save-system",
|
|
184
177
|
"category": "core",
|
|
185
|
-
"size": 7,
|
|
178
|
+
"size": 7.1,
|
|
186
179
|
"files": [
|
|
187
180
|
"core/savePage.js"
|
|
188
181
|
],
|
|
@@ -219,7 +212,7 @@
|
|
|
219
212
|
"files": [
|
|
220
213
|
"core/saveToast.js"
|
|
221
214
|
],
|
|
222
|
-
"description": "Toast notifications for save events
|
|
215
|
+
"description": "Toast notifications for save events",
|
|
223
216
|
"exports": {}
|
|
224
217
|
},
|
|
225
218
|
"edit-mode-helpers": {
|
|
@@ -249,9 +242,9 @@
|
|
|
249
242
|
"option-visibility": {
|
|
250
243
|
"name": "option-visibility",
|
|
251
244
|
"category": "core",
|
|
252
|
-
"size":
|
|
245
|
+
"size": 5.9,
|
|
253
246
|
"files": [
|
|
254
|
-
"core/
|
|
247
|
+
"core/optionVisibility.js"
|
|
255
248
|
],
|
|
256
249
|
"description": "Dynamic show/hide based on ancestor state with option:attribute=\"value\"",
|
|
257
250
|
"exports": {}
|
|
@@ -415,16 +408,6 @@
|
|
|
415
408
|
]
|
|
416
409
|
}
|
|
417
410
|
},
|
|
418
|
-
"tailwind-play": {
|
|
419
|
-
"name": "tailwind-play",
|
|
420
|
-
"category": "ui",
|
|
421
|
-
"size": 0.7,
|
|
422
|
-
"files": [
|
|
423
|
-
"vendor/tailwind-play.js"
|
|
424
|
-
],
|
|
425
|
-
"description": "Live Tailwind CSS editing, lazy-loads ~370KB script in edit mode only",
|
|
426
|
-
"exports": {}
|
|
427
|
-
},
|
|
428
411
|
"mutation": {
|
|
429
412
|
"name": "mutation",
|
|
430
413
|
"category": "utilities",
|
|
@@ -695,7 +678,7 @@
|
|
|
695
678
|
"save-toast": "./core/saveToast.js",
|
|
696
679
|
"edit-mode-helpers": "./core/adminSystem.js",
|
|
697
680
|
"persist": "./core/enablePersistentFormInputValues.js",
|
|
698
|
-
"option-visibility": "./core/
|
|
681
|
+
"option-visibility": "./core/optionVisibility.js",
|
|
699
682
|
"edit-mode": "./core/editmodeSystem.js",
|
|
700
683
|
"event-attrs": "./custom-attributes/events.js",
|
|
701
684
|
"ajax-elements": "./custom-attributes/ajaxElements.js",
|
|
@@ -707,7 +690,6 @@
|
|
|
707
690
|
"toast": "./ui/toast.js",
|
|
708
691
|
"toast-hyperclay": "./ui/toast-hyperclay.js",
|
|
709
692
|
"the-modal": "./ui/theModal.js",
|
|
710
|
-
"tailwind-play": "./vendor/tailwind-play.js",
|
|
711
693
|
"mutation": "./utilities/mutation.js",
|
|
712
694
|
"nearest": "./utilities/nearest.js",
|
|
713
695
|
"cookie": "./utilities/cookie.js",
|
|
@@ -760,8 +742,7 @@
|
|
|
760
742
|
"modules": [
|
|
761
743
|
"dialogs",
|
|
762
744
|
"toast",
|
|
763
|
-
"the-modal"
|
|
764
|
-
"tailwind-play"
|
|
745
|
+
"the-modal"
|
|
765
746
|
]
|
|
766
747
|
},
|
|
767
748
|
"utilities": {
|
|
@@ -861,7 +842,6 @@
|
|
|
861
842
|
"toast",
|
|
862
843
|
"toast-hyperclay",
|
|
863
844
|
"the-modal",
|
|
864
|
-
"tailwind-play",
|
|
865
845
|
"mutation",
|
|
866
846
|
"nearest",
|
|
867
847
|
"cookie",
|
package/package.json
CHANGED
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
* Automatically show/hide elements with "option" attributes based on ancestors' attributes.
|
|
4
|
-
*
|
|
5
|
-
* # Usage:
|
|
6
|
-
* optionVisibilityRuleGenerator.debug = true;
|
|
7
|
-
* optionVisibilityRuleGenerator.start();
|
|
8
|
-
*
|
|
9
|
-
* # HTML Example:
|
|
10
|
-
* <div editmode="true"> <!-- Parent element with matching attribute -->
|
|
11
|
-
* <div option:editmode="true"></div> <!-- This will be visible -->
|
|
12
|
-
* <div option:editmode="false"></div> <!-- This will be hidden -->
|
|
13
|
-
* </div>
|
|
14
|
-
*
|
|
15
|
-
* Elements with `option:` attributes will be:
|
|
16
|
-
* - Visible if any ancestor has matching attribute
|
|
17
|
-
* - Hidden if no ancestor has matching attribute
|
|
18
|
-
*
|
|
19
|
-
*/
|
|
20
|
-
import Mutation from "../utilities/mutation.js";
|
|
21
|
-
|
|
22
|
-
const optionVisibilityRuleGenerator = {
|
|
23
|
-
debug: false,
|
|
24
|
-
styleElement: null,
|
|
25
|
-
|
|
26
|
-
HIDDEN_STYLES: `
|
|
27
|
-
visibility: hidden;
|
|
28
|
-
pointer-events: none;
|
|
29
|
-
width: 0;
|
|
30
|
-
height: 0;
|
|
31
|
-
overflow: hidden;
|
|
32
|
-
`,
|
|
33
|
-
VISIBLE_STYLES: `
|
|
34
|
-
visibility: visible;
|
|
35
|
-
pointer-events: auto;
|
|
36
|
-
width: auto;
|
|
37
|
-
height: auto;
|
|
38
|
-
overflow: visible;
|
|
39
|
-
`,
|
|
40
|
-
|
|
41
|
-
STYLE_CLASS: 'option-visibility-styles',
|
|
42
|
-
|
|
43
|
-
log(message, ...args) {
|
|
44
|
-
if (this.debug) {
|
|
45
|
-
console.log(`[OptionVisibilityRuleGenerator] ${message}`, ...args);
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
|
|
49
|
-
findOptionAttributes() {
|
|
50
|
-
const html = document.documentElement.outerHTML;
|
|
51
|
-
const optionAttributes = new Set(); // Using Set for unique combinations
|
|
52
|
-
const optionRegex = /option:([^\s"']+)=["']([^"']+)["']/g; // regex: "option:" + (anything but space and quote) + equal + quote + (anything but quote) + quote
|
|
53
|
-
|
|
54
|
-
let match;
|
|
55
|
-
while ((match = optionRegex.exec(html)) !== null) {
|
|
56
|
-
// Create a unique key for each name-value pair
|
|
57
|
-
const key = JSON.stringify({name: match[1], value: match[2]});
|
|
58
|
-
optionAttributes.add(key);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Convert back to objects
|
|
62
|
-
return Array.from(optionAttributes).map(key => JSON.parse(key));
|
|
63
|
-
},
|
|
64
|
-
|
|
65
|
-
minifyCSS(css) {
|
|
66
|
-
return css
|
|
67
|
-
.replace(/\s+/g, ' ')
|
|
68
|
-
.replace(/{\s+/g, '{')
|
|
69
|
-
.replace(/\s+}/g, '}')
|
|
70
|
-
.replace(/;\s+/g, ';')
|
|
71
|
-
.replace(/:\s+/g, ':')
|
|
72
|
-
.trim();
|
|
73
|
-
},
|
|
74
|
-
|
|
75
|
-
generateCSSRules(optionAttributes) {
|
|
76
|
-
const rules = [];
|
|
77
|
-
|
|
78
|
-
optionAttributes.forEach(({name, value}) => {
|
|
79
|
-
const escapedValue = value.replace(/["\\]/g, '\\$&');
|
|
80
|
-
|
|
81
|
-
rules.push(`
|
|
82
|
-
[option\\:${name}="${escapedValue}"] {
|
|
83
|
-
${this.HIDDEN_STYLES}
|
|
84
|
-
}
|
|
85
|
-
`);
|
|
86
|
-
|
|
87
|
-
rules.push(`
|
|
88
|
-
[${name}="${escapedValue}"] [option\\:${name}="${escapedValue}"] {
|
|
89
|
-
${this.VISIBLE_STYLES}
|
|
90
|
-
}
|
|
91
|
-
`);
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
return this.minifyCSS(rules.join('\n'));
|
|
95
|
-
},
|
|
96
|
-
|
|
97
|
-
generateRules() {
|
|
98
|
-
try {
|
|
99
|
-
this.log('Starting rule generation');
|
|
100
|
-
|
|
101
|
-
const optionAttributes = this.findOptionAttributes();
|
|
102
|
-
this.log('Found option attributes:', optionAttributes);
|
|
103
|
-
|
|
104
|
-
// Early return if no option attributes found
|
|
105
|
-
if (optionAttributes.length === 0) {
|
|
106
|
-
this.log('No option attributes found, skipping style creation');
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const cssRules = this.generateCSSRules(optionAttributes);
|
|
111
|
-
this.log('Generated CSS rules:', cssRules);
|
|
112
|
-
|
|
113
|
-
// Check if we already have these exact rules
|
|
114
|
-
const existingStyleElement = document.head.querySelector(`.${this.STYLE_CLASS}`);
|
|
115
|
-
if (existingStyleElement && existingStyleElement.textContent.trim() === cssRules) {
|
|
116
|
-
this.log('Rules unchanged, skipping update');
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Create new style element
|
|
121
|
-
const newStyleElement = document.createElement('style');
|
|
122
|
-
newStyleElement.className = this.STYLE_CLASS;
|
|
123
|
-
newStyleElement.textContent = cssRules;
|
|
124
|
-
document.head.appendChild(newStyleElement);
|
|
125
|
-
|
|
126
|
-
// Remove all previous style elements
|
|
127
|
-
document.head
|
|
128
|
-
.querySelectorAll(`.${this.STYLE_CLASS}`)
|
|
129
|
-
.forEach(el => {
|
|
130
|
-
if (el !== newStyleElement) {
|
|
131
|
-
el.remove();
|
|
132
|
-
}
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
this.styleElement = newStyleElement;
|
|
136
|
-
|
|
137
|
-
this.log('Rule generation complete');
|
|
138
|
-
} catch (error) {
|
|
139
|
-
console.error('Error generating visibility rules:', error);
|
|
140
|
-
}
|
|
141
|
-
},
|
|
142
|
-
|
|
143
|
-
start() {
|
|
144
|
-
Mutation.onAnyChange({
|
|
145
|
-
selectorFilter: el => [...el.attributes].some(attr => attr.name.startsWith('option:')),
|
|
146
|
-
debounce: 200
|
|
147
|
-
}, () => {
|
|
148
|
-
this.generateRules();
|
|
149
|
-
});
|
|
150
|
-
this.generateRules();
|
|
151
|
-
this.log('Started observing DOM mutations');
|
|
152
|
-
},
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
// Auto-export to window unless suppressed by loader
|
|
156
|
-
if (!window.__hyperclayNoAutoExport) {
|
|
157
|
-
window.optionVisibilityRuleGenerator = optionVisibilityRuleGenerator;
|
|
158
|
-
window.hyperclay = window.hyperclay || {};
|
|
159
|
-
window.hyperclay.optionVisibilityRuleGenerator = optionVisibilityRuleGenerator;
|
|
160
|
-
window.h = window.hyperclay;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
export default optionVisibilityRuleGenerator;
|
|
164
|
-
|
|
165
|
-
// Auto-initialize
|
|
166
|
-
export function init() {
|
|
167
|
-
optionVisibilityRuleGenerator.start();
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Auto-init when module is imported
|
|
171
|
-
init();
|