hyperclayjs 1.8.0 → 1.10.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.
@@ -1,38 +1,61 @@
1
- function insertStyleTag(href) {
2
- // First check if there's already a link with this exact href
3
- if (document.querySelector(`link[href="${href}"]`)) {
4
- return;
1
+ /**
2
+ * Insert styles into the document (inline CSS or external stylesheet).
3
+ *
4
+ * With a persistent DOM (i.e. hyperclay), we need a way to update styles.
5
+ * This function always inserts the new styles first, then removes any
6
+ * duplicates. This ensures:
7
+ * - No flickering: new styles are applied before old ones are removed
8
+ * - Always upgrades: we default to the new styles/approach
9
+ */
10
+ function insertStyles(nameOrHref, css) {
11
+ if (css !== undefined) {
12
+ // Inline style: insertStyles('my-styles', '.foo { ... }')
13
+ const name = nameOrHref;
14
+ const oldStyles = document.querySelectorAll(`style[data-name="${name}"]`);
15
+
16
+ const style = document.createElement('style');
17
+ style.dataset.name = name;
18
+ style.textContent = css;
19
+ document.head.appendChild(style);
20
+
21
+ oldStyles.forEach(el => el.remove());
22
+ return style;
5
23
  }
6
-
7
- // Extract a more reliable identifier from the URL
24
+
25
+ // External stylesheet: insertStyles('/path/to/file.css')
26
+ const href = nameOrHref;
27
+
8
28
  let identifier;
9
29
  try {
10
30
  const url = new URL(href, window.location.href);
11
- // Get the filename from the path
12
31
  identifier = url.pathname.split('/').pop();
13
32
  } catch (e) {
14
- // Fallback to using the href itself if URL parsing fails
15
33
  identifier = href;
16
34
  }
17
-
18
- // Look for any link that contains this identifier
19
- if (identifier && document.querySelector(`link[href*="${identifier}"]`)) {
20
- return;
21
- }
22
-
23
- // If no duplicate found, add the stylesheet
35
+
36
+ const oldLinks = document.querySelectorAll(
37
+ `link[href="${href}"], link[href*="${identifier}"]`
38
+ );
39
+
24
40
  const link = document.createElement('link');
25
41
  link.rel = 'stylesheet';
26
42
  link.href = href;
27
43
  document.head.appendChild(link);
44
+
45
+ oldLinks.forEach(el => el.remove());
46
+ return link;
28
47
  }
29
48
 
30
49
  // Auto-export to window unless suppressed by loader
31
50
  if (!window.__hyperclayNoAutoExport) {
32
- window.insertStyleTag = insertStyleTag;
51
+ window.insertStyles = insertStyles;
52
+ window.insertStyleTag = insertStyles; // backwards-compat alias
33
53
  window.hyperclay = window.hyperclay || {};
34
- window.hyperclay.insertStyleTag = insertStyleTag;
54
+ window.hyperclay.insertStyles = insertStyles;
55
+ window.hyperclay.insertStyleTag = insertStyles; // backwards-compat alias
35
56
  window.h = window.hyperclay;
36
57
  }
37
58
 
38
- export default insertStyleTag;
59
+ export { insertStyles };
60
+ export { insertStyles as insertStyleTag }; // backwards-compat alias
61
+ export default insertStyles;
package/src/hyperclay.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * HyperclayJS v1.8.0 - Minimal Browser-Native Loader
2
+ * HyperclayJS v1.10.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.
@@ -28,9 +28,11 @@ const MODULE_PATHS = {
28
28
  "save-core": "./core/savePageCore.js",
29
29
  "save-system": "./core/savePage.js",
30
30
  "autosave": "./core/autosave.js",
31
+ "unsaved-warning": "./core/unsavedWarning.js",
31
32
  "save-toast": "./core/saveToast.js",
32
33
  "edit-mode-helpers": "./core/adminSystem.js",
33
34
  "persist": "./core/enablePersistentFormInputValues.js",
35
+ "snapshot": "./core/snapshot.js",
34
36
  "option-visibility": "./core/optionVisibility.js",
35
37
  "edit-mode": "./core/editmodeSystem.js",
36
38
  "event-attrs": "./custom-attributes/events.js",
@@ -48,6 +50,7 @@ const MODULE_PATHS = {
48
50
  "cookie": "./utilities/cookie.js",
49
51
  "throttle": "./utilities/throttle.js",
50
52
  "debounce": "./utilities/debounce.js",
53
+ "cache-bust": "./utilities/cacheBust.js",
51
54
  "dom-ready": "./dom-utilities/onDomReady.js",
52
55
  "window-load": "./dom-utilities/onLoad.js",
53
56
  "all-js": "./dom-utilities/All.js",
@@ -60,6 +63,7 @@ const MODULE_PATHS = {
60
63
  "behavior-collector": "./communication/behaviorCollector.js",
61
64
  "send-message": "./communication/sendMessage.js",
62
65
  "file-upload": "./communication/uploadFile.js",
66
+ "live-sync": "./communication/live-sync.js",
63
67
  "export-to-window": "./core/exportToWindow.js"
64
68
  };
65
69
  const PRESETS = {
@@ -98,9 +102,11 @@ const PRESETS = {
98
102
  "save-core",
99
103
  "save-system",
100
104
  "autosave",
105
+ "unsaved-warning",
101
106
  "save-toast",
102
107
  "edit-mode-helpers",
103
108
  "persist",
109
+ "snapshot",
104
110
  "option-visibility",
105
111
  "edit-mode",
106
112
  "event-attrs",
@@ -118,6 +124,7 @@ const PRESETS = {
118
124
  "cookie",
119
125
  "throttle",
120
126
  "debounce",
127
+ "cache-bust",
121
128
  "dom-ready",
122
129
  "window-load",
123
130
  "all-js",
@@ -130,6 +137,7 @@ const PRESETS = {
130
137
  "behavior-collector",
131
138
  "send-message",
132
139
  "file-upload",
140
+ "live-sync",
133
141
  "export-to-window"
134
142
  ]
135
143
  }
@@ -205,6 +213,12 @@ export const savePage = window.hyperclayModules['save-core']?.savePage ?? window
205
213
  export const beforeSave = window.hyperclayModules['save-system']?.beforeSave ?? window.hyperclayModules['save-system']?.default;
206
214
  export const savePageThrottled = window.hyperclayModules['save-system']?.savePageThrottled ?? window.hyperclayModules['save-system']?.default;
207
215
  export const replacePageWith = window.hyperclayModules['save-system']?.replacePageWith ?? window.hyperclayModules['save-system']?.default;
216
+ export const captureSnapshot = window.hyperclayModules['snapshot']?.captureSnapshot ?? window.hyperclayModules['snapshot']?.default;
217
+ export const captureForSave = window.hyperclayModules['snapshot']?.captureForSave ?? window.hyperclayModules['snapshot']?.default;
218
+ export const captureBodyForSync = window.hyperclayModules['snapshot']?.captureBodyForSync ?? window.hyperclayModules['snapshot']?.default;
219
+ export const onSnapshot = window.hyperclayModules['snapshot']?.onSnapshot ?? window.hyperclayModules['snapshot']?.default;
220
+ export const onPrepareForSave = window.hyperclayModules['snapshot']?.onPrepareForSave ?? window.hyperclayModules['snapshot']?.default;
221
+ export const getPageContents = window.hyperclayModules['snapshot']?.getPageContents ?? window.hyperclayModules['snapshot']?.default;
208
222
  export const toggleEditMode = window.hyperclayModules['edit-mode']?.toggleEditMode ?? window.hyperclayModules['edit-mode']?.default;
209
223
  export const isEditMode = window.hyperclayModules['edit-mode']?.isEditMode ?? window.hyperclayModules['edit-mode']?.default;
210
224
  export const isOwner = window.hyperclayModules['edit-mode']?.isOwner ?? window.hyperclayModules['edit-mode']?.default;
@@ -220,9 +234,11 @@ export const nearest = window.hyperclayModules['nearest']?.nearest ?? window.hyp
220
234
  export const cookie = window.hyperclayModules['cookie']?.cookie ?? window.hyperclayModules['cookie']?.default;
221
235
  export const throttle = window.hyperclayModules['throttle']?.throttle ?? window.hyperclayModules['throttle']?.default;
222
236
  export const debounce = window.hyperclayModules['debounce']?.debounce ?? window.hyperclayModules['debounce']?.default;
237
+ export const cacheBust = window.hyperclayModules['cache-bust']?.cacheBust ?? window.hyperclayModules['cache-bust']?.default;
223
238
  export const onDomReady = window.hyperclayModules['dom-ready']?.onDomReady ?? window.hyperclayModules['dom-ready']?.default;
224
239
  export const onLoad = window.hyperclayModules['window-load']?.onLoad ?? window.hyperclayModules['window-load']?.default;
225
240
  export const All = window.hyperclayModules['all-js']?.All ?? window.hyperclayModules['all-js']?.default;
241
+ export const insertStyles = window.hyperclayModules['style-injection']?.insertStyles ?? window.hyperclayModules['style-injection']?.default;
226
242
  export const insertStyleTag = window.hyperclayModules['style-injection']?.insertStyleTag ?? window.hyperclayModules['style-injection']?.default;
227
243
  export const getDataFromForm = window.hyperclayModules['form-data']?.getDataFromForm ?? window.hyperclayModules['form-data']?.default;
228
244
  export const Idiomorph = window.hyperclayModules['idiomorph']?.Idiomorph ?? window.hyperclayModules['idiomorph']?.default;
@@ -234,3 +250,4 @@ export const sendMessage = window.hyperclayModules['send-message']?.sendMessage
234
250
  export const uploadFile = window.hyperclayModules['file-upload']?.uploadFile ?? window.hyperclayModules['file-upload']?.default;
235
251
  export const createFile = window.hyperclayModules['file-upload']?.createFile ?? window.hyperclayModules['file-upload']?.default;
236
252
  export const uploadFileBasic = window.hyperclayModules['file-upload']?.uploadFileBasic ?? window.hyperclayModules['file-upload']?.default;
253
+ export const liveSync = window.hyperclayModules['live-sync']?.liveSync ?? window.hyperclayModules['live-sync']?.default;
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "rawDependencies": {
3
3
  "communication/behaviorCollector.js": [],
4
+ "communication/live-sync.js": [],
4
5
  "communication/sendMessage.js": [
5
6
  "communication/behaviorCollector.js",
6
7
  "dom-utilities/getDataFromForm.js",
@@ -51,7 +52,7 @@
51
52
  "core/setPageTypeOnDocumentElement.js"
52
53
  ],
53
54
  "core/enablePersistentFormInputValues.js": [
54
- "core/savePage.js"
55
+ "core/snapshot.js"
55
56
  ],
56
57
  "core/exportToWindow.js": [],
57
58
  "core/isAdminOfCurrentResource.js": [
@@ -59,6 +60,7 @@
59
60
  "utilities/cookie.js"
60
61
  ],
61
62
  "core/optionVisibility.js": [
63
+ "dom-utilities/insertStyleTag.js",
62
64
  "utilities/mutation.js"
63
65
  ],
64
66
  "core/savePage.js": [
@@ -68,6 +70,7 @@
68
70
  ],
69
71
  "core/savePageCore.js": [
70
72
  "core/isAdminOfCurrentResource.js",
73
+ "core/snapshot.js",
71
74
  "utilities/cookie.js"
72
75
  ],
73
76
  "core/saveToast.js": [
@@ -79,6 +82,11 @@
79
82
  "core/savePage.js",
80
83
  "dom-utilities/onDomReady.js"
81
84
  ],
85
+ "core/snapshot.js": [],
86
+ "core/unsavedWarning.js": [
87
+ "core/isAdminOfCurrentResource.js",
88
+ "core/savePage.js"
89
+ ],
82
90
  "custom-attributes/ajaxElements.js": [
83
91
  "dom-utilities/getDataFromForm.js"
84
92
  ],
@@ -137,6 +145,7 @@
137
145
  "ui/toast.js"
138
146
  ],
139
147
  "ui/toast.js": [],
148
+ "utilities/cacheBust.js": [],
140
149
  "utilities/cookie.js": [],
141
150
  "utilities/debounce.js": [],
142
151
  "utilities/loadVendorScript.js": [],
@@ -151,7 +160,7 @@
151
160
  "save-core": {
152
161
  "name": "save-core",
153
162
  "category": "core",
154
- "size": 6.5,
163
+ "size": 6.8,
155
164
  "files": [
156
165
  "core/savePageCore.js"
157
166
  ],
@@ -169,7 +178,7 @@
169
178
  "files": [
170
179
  "core/savePage.js"
171
180
  ],
172
- "description": "Manual save: keyboard shortcut (CMD+S), save button, change tracking",
181
+ "description": "CMD+S, [trigger-save] button, savestatus attribute",
173
182
  "exports": {
174
183
  "beforeSave": [
175
184
  "hyperclay"
@@ -188,11 +197,21 @@
188
197
  "autosave": {
189
198
  "name": "autosave",
190
199
  "category": "core",
191
- "size": 1.1,
200
+ "size": 0.9,
192
201
  "files": [
193
202
  "core/autosave.js"
194
203
  ],
195
- "description": "Auto-save on DOM changes, unsaved changes warning",
204
+ "description": "Auto-save on DOM changes",
205
+ "exports": {}
206
+ },
207
+ "unsaved-warning": {
208
+ "name": "unsaved-warning",
209
+ "category": "core",
210
+ "size": 0.8,
211
+ "files": [
212
+ "core/unsavedWarning.js"
213
+ ],
214
+ "description": "Warn before leaving page with unsaved changes",
196
215
  "exports": {}
197
216
  },
198
217
  "save-toast": {
@@ -222,17 +241,49 @@
222
241
  "persist": {
223
242
  "name": "persist",
224
243
  "category": "core",
225
- "size": 2.5,
244
+ "size": 2.4,
226
245
  "files": [
227
246
  "core/enablePersistentFormInputValues.js"
228
247
  ],
229
248
  "description": "Persist input/select/textarea values to the DOM with [persist] attribute",
230
249
  "exports": {}
231
250
  },
251
+ "snapshot": {
252
+ "name": "snapshot",
253
+ "category": "core",
254
+ "size": 7.5,
255
+ "files": [
256
+ "core/snapshot.js"
257
+ ],
258
+ "description": "Source of truth for page state - captures DOM snapshots for save and sync",
259
+ "exports": {
260
+ "captureSnapshot": [
261
+ "hyperclay"
262
+ ],
263
+ "captureForSave": [
264
+ "hyperclay"
265
+ ],
266
+ "captureBodyForSync": [
267
+ "hyperclay"
268
+ ],
269
+ "onSnapshot": [
270
+ "hyperclay"
271
+ ],
272
+ "onPrepareForSave": [
273
+ "hyperclay"
274
+ ],
275
+ "beforeSave": [
276
+ "hyperclay"
277
+ ],
278
+ "getPageContents": [
279
+ "hyperclay"
280
+ ]
281
+ }
282
+ },
232
283
  "option-visibility": {
233
284
  "name": "option-visibility",
234
285
  "category": "core",
235
- "size": 5.9,
286
+ "size": 5.3,
236
287
  "files": [
237
288
  "core/optionVisibility.js"
238
289
  ],
@@ -278,7 +329,7 @@
278
329
  "ajax-elements": {
279
330
  "name": "ajax-elements",
280
331
  "category": "custom-attributes",
281
- "size": 2.8,
332
+ "size": 2.6,
282
333
  "files": [
283
334
  "custom-attributes/ajaxElements.js"
284
335
  ],
@@ -320,7 +371,7 @@
320
371
  "onaftersave": {
321
372
  "name": "onaftersave",
322
373
  "category": "custom-attributes",
323
- "size": 1.2,
374
+ "size": 1,
324
375
  "files": [
325
376
  "custom-attributes/onaftersave.js"
326
377
  ],
@@ -349,6 +400,7 @@
349
400
  "hyperclay"
350
401
  ],
351
402
  "snippet": [
403
+ "window",
352
404
  "hyperclay"
353
405
  ]
354
406
  }
@@ -471,6 +523,21 @@
471
523
  ]
472
524
  }
473
525
  },
526
+ "cache-bust": {
527
+ "name": "cache-bust",
528
+ "category": "utilities",
529
+ "size": 0.6,
530
+ "files": [
531
+ "utilities/cacheBust.js"
532
+ ],
533
+ "description": "Cache-bust href/src attributes",
534
+ "exports": {
535
+ "cacheBust": [
536
+ "window",
537
+ "hyperclay"
538
+ ]
539
+ }
540
+ },
474
541
  "dom-ready": {
475
542
  "name": "dom-ready",
476
543
  "category": "dom-utilities",
@@ -517,12 +584,16 @@
517
584
  "style-injection": {
518
585
  "name": "style-injection",
519
586
  "category": "dom-utilities",
520
- "size": 1.1,
587
+ "size": 1.9,
521
588
  "files": [
522
589
  "dom-utilities/insertStyleTag.js"
523
590
  ],
524
591
  "description": "Dynamic stylesheet injection",
525
592
  "exports": {
593
+ "insertStyles": [
594
+ "window",
595
+ "hyperclay"
596
+ ],
526
597
  "insertStyleTag": [
527
598
  "window",
528
599
  "hyperclay"
@@ -547,7 +618,7 @@
547
618
  "idiomorph": {
548
619
  "name": "idiomorph",
549
620
  "category": "vendor",
550
- "size": 8.2,
621
+ "size": 8.3,
551
622
  "files": [
552
623
  "vendor/idiomorph.min.js"
553
624
  ],
@@ -619,7 +690,7 @@
619
690
  "send-message": {
620
691
  "name": "send-message",
621
692
  "category": "communication",
622
- "size": 1.4,
693
+ "size": 1.3,
623
694
  "files": [
624
695
  "communication/sendMessage.js"
625
696
  ],
@@ -650,6 +721,20 @@
650
721
  ]
651
722
  }
652
723
  },
724
+ "live-sync": {
725
+ "name": "live-sync",
726
+ "category": "communication",
727
+ "size": 12.7,
728
+ "files": [
729
+ "communication/live-sync.js"
730
+ ],
731
+ "description": "Real-time DOM sync across browsers",
732
+ "exports": {
733
+ "liveSync": [
734
+ "hyperclay"
735
+ ]
736
+ }
737
+ },
653
738
  "export-to-window": {
654
739
  "name": "export-to-window",
655
740
  "category": "core",
@@ -665,9 +750,11 @@
665
750
  "save-core": "./core/savePageCore.js",
666
751
  "save-system": "./core/savePage.js",
667
752
  "autosave": "./core/autosave.js",
753
+ "unsaved-warning": "./core/unsavedWarning.js",
668
754
  "save-toast": "./core/saveToast.js",
669
755
  "edit-mode-helpers": "./core/adminSystem.js",
670
756
  "persist": "./core/enablePersistentFormInputValues.js",
757
+ "snapshot": "./core/snapshot.js",
671
758
  "option-visibility": "./core/optionVisibility.js",
672
759
  "edit-mode": "./core/editmodeSystem.js",
673
760
  "event-attrs": "./custom-attributes/events.js",
@@ -685,6 +772,7 @@
685
772
  "cookie": "./utilities/cookie.js",
686
773
  "throttle": "./utilities/throttle.js",
687
774
  "debounce": "./utilities/debounce.js",
775
+ "cache-bust": "./utilities/cacheBust.js",
688
776
  "dom-ready": "./dom-utilities/onDomReady.js",
689
777
  "window-load": "./dom-utilities/onLoad.js",
690
778
  "all-js": "./dom-utilities/All.js",
@@ -697,6 +785,7 @@
697
785
  "behavior-collector": "./communication/behaviorCollector.js",
698
786
  "send-message": "./communication/sendMessage.js",
699
787
  "file-upload": "./communication/uploadFile.js",
788
+ "live-sync": "./communication/live-sync.js",
700
789
  "export-to-window": "./core/exportToWindow.js"
701
790
  },
702
791
  "categories": {
@@ -707,9 +796,11 @@
707
796
  "save-core",
708
797
  "save-system",
709
798
  "autosave",
799
+ "unsaved-warning",
710
800
  "save-toast",
711
801
  "edit-mode-helpers",
712
802
  "persist",
803
+ "snapshot",
713
804
  "option-visibility",
714
805
  "edit-mode"
715
806
  ]
@@ -743,7 +834,8 @@
743
834
  "nearest",
744
835
  "cookie",
745
836
  "throttle",
746
- "debounce"
837
+ "debounce",
838
+ "cache-bust"
747
839
  ]
748
840
  },
749
841
  "dom-utilities": {
@@ -770,7 +862,8 @@
770
862
  "description": "File handling and messaging",
771
863
  "modules": [
772
864
  "send-message",
773
- "file-upload"
865
+ "file-upload",
866
+ "live-sync"
774
867
  ]
775
868
  },
776
869
  "vendor": {
@@ -817,9 +910,11 @@
817
910
  "save-core",
818
911
  "save-system",
819
912
  "autosave",
913
+ "unsaved-warning",
820
914
  "save-toast",
821
915
  "edit-mode-helpers",
822
916
  "persist",
917
+ "snapshot",
823
918
  "option-visibility",
824
919
  "edit-mode",
825
920
  "event-attrs",
@@ -837,6 +932,7 @@
837
932
  "cookie",
838
933
  "throttle",
839
934
  "debounce",
935
+ "cache-bust",
840
936
  "dom-ready",
841
937
  "window-load",
842
938
  "all-js",
@@ -849,6 +945,7 @@
849
945
  "behavior-collector",
850
946
  "send-message",
851
947
  "file-upload",
948
+ "live-sync",
852
949
  "export-to-window"
853
950
  ]
854
951
  }
@@ -726,6 +726,8 @@ const themodal = (() => {
726
726
 
727
727
  this.isShowing = true;
728
728
 
729
+ onOpen.forEach(cb => cb());
730
+
729
731
  if (!disableFocus) {
730
732
  let firstInput = modalOverlayElem.querySelector(".micromodal__content :is(input,textarea,button):not(.micromodal__hide), .micromodal__buttons :is(input,textarea,button):not(.micromodal__hide)");
731
733
  firstInput?.focus();
@@ -0,0 +1,19 @@
1
+ // cacheBust.js
2
+ // Cache-bust an element's href or src attribute by adding/updating a version query param
3
+
4
+ function cacheBust(el) {
5
+ const attr = el.href !== undefined ? 'href' : 'src';
6
+ const url = new URL(el[attr], location.href);
7
+ url.searchParams.set('v', Date.now());
8
+ el[attr] = url.href;
9
+ }
10
+
11
+ // Auto-export to window unless suppressed by loader
12
+ if (!window.__hyperclayNoAutoExport) {
13
+ window.cacheBust = cacheBust;
14
+ window.hyperclay = window.hyperclay || {};
15
+ window.hyperclay.cacheBust = cacheBust;
16
+ window.h = window.hyperclay;
17
+ }
18
+
19
+ export default cacheBust;
@@ -12,6 +12,7 @@ if (!window.__hyperclayNoAutoExport) {
12
12
  window.hyperclay = window.hyperclay || {};
13
13
  window.hyperclay.Idiomorph = Idiomorph;
14
14
  window.hyperclay.morph = morph;
15
+ window.morph = morph;
15
16
  window.h = window.hyperclay;
16
17
  }
17
18