hyperclayjs 1.0.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 +21 -0
- package/README.md +360 -0
- package/README.template.md +276 -0
- package/communication/behaviorCollector.js +230 -0
- package/communication/sendMessage.js +48 -0
- package/communication/uploadFile.js +348 -0
- package/core/adminContenteditable.js +36 -0
- package/core/adminInputs.js +58 -0
- package/core/adminOnClick.js +31 -0
- package/core/adminResources.js +33 -0
- package/core/adminSystem.js +15 -0
- package/core/editmode.js +8 -0
- package/core/editmodeSystem.js +18 -0
- package/core/enablePersistentFormInputValues.js +62 -0
- package/core/isAdminOfCurrentResource.js +13 -0
- package/core/optionVisibilityRuleGenerator.js +160 -0
- package/core/savePage.js +196 -0
- package/core/savePageCore.js +236 -0
- package/core/setPageTypeOnDocumentElement.js +23 -0
- package/custom-attributes/ajaxElements.js +94 -0
- package/custom-attributes/autosize.js +17 -0
- package/custom-attributes/domHelpers.js +175 -0
- package/custom-attributes/events.js +15 -0
- package/custom-attributes/inputHelpers.js +11 -0
- package/custom-attributes/onclickaway.js +27 -0
- package/custom-attributes/onclone.js +35 -0
- package/custom-attributes/onpagemutation.js +20 -0
- package/custom-attributes/onrender.js +30 -0
- package/custom-attributes/preventEnter.js +13 -0
- package/custom-attributes/sortable.js +76 -0
- package/dom-utilities/All.js +412 -0
- package/dom-utilities/getDataFromForm.js +60 -0
- package/dom-utilities/insertStyleTag.js +28 -0
- package/dom-utilities/onDomReady.js +7 -0
- package/dom-utilities/onLoad.js +7 -0
- package/hyperclay.js +465 -0
- package/module-dependency-graph.json +612 -0
- package/package.json +95 -0
- package/string-utilities/copy-to-clipboard.js +35 -0
- package/string-utilities/emmet-html.js +54 -0
- package/string-utilities/query.js +1 -0
- package/string-utilities/slugify.js +21 -0
- package/ui/info.js +39 -0
- package/ui/prompts.js +179 -0
- package/ui/theModal.js +677 -0
- package/ui/toast.js +273 -0
- package/utilities/cookie.js +45 -0
- package/utilities/debounce.js +12 -0
- package/utilities/mutation.js +403 -0
- package/utilities/nearest.js +97 -0
- package/utilities/pipe.js +1 -0
- package/utilities/throttle.js +21 -0
- package/vendor/Sortable.js +3351 -0
- package/vendor/idiomorph.min.js +8 -0
- package/vendor/tailwind-base.css +1471 -0
- package/vendor/tailwind-play.js +169 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// [prevent-enter]: Prevents Enter key from creating newlines
|
|
2
|
+
function init () {
|
|
3
|
+
document.addEventListener('keydown', function(event) {
|
|
4
|
+
if (event.key === 'Enter' || event.keyCode === 13) {
|
|
5
|
+
const preventEnterElement = event.target.closest('[prevent-enter]');
|
|
6
|
+
if (preventEnterElement) {
|
|
7
|
+
event.preventDefault();
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
export { init };
|
|
13
|
+
export default init;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/*
|
|
2
|
+
|
|
3
|
+
Make elements drag-and-drop sortable
|
|
4
|
+
|
|
5
|
+
How to use:
|
|
6
|
+
- add `sortable` attribute to an element to make children sortable
|
|
7
|
+
- e.g. <div sortable></div>
|
|
8
|
+
- add `onsorted` attribute to execute code when items are sorted
|
|
9
|
+
- e.g. <ul sortable onsorted="console.log('Items reordered!')"></ul>
|
|
10
|
+
|
|
11
|
+
*/
|
|
12
|
+
import { isEditMode, isOwner } from "../core/isAdminOfCurrentResource.js";
|
|
13
|
+
import Mutation from "../utilities/mutation.js";
|
|
14
|
+
import Sortable from '../vendor/Sortable.js';
|
|
15
|
+
|
|
16
|
+
function makeSortable (sortableElem) {
|
|
17
|
+
let options = {};
|
|
18
|
+
|
|
19
|
+
// Check if Sortable instance already exists
|
|
20
|
+
if (Sortable.get(sortableElem)) return;
|
|
21
|
+
|
|
22
|
+
const groupName = sortableElem.getAttribute('sortable');
|
|
23
|
+
if (groupName) options.group = groupName;
|
|
24
|
+
|
|
25
|
+
// Check for handles, but exclude those inside nested sortable elements
|
|
26
|
+
const handles = sortableElem.querySelectorAll('[sortable-handle]');
|
|
27
|
+
const nestedSortables = sortableElem.querySelectorAll('[sortable]');
|
|
28
|
+
|
|
29
|
+
// Check if any handle is NOT inside a nested sortable
|
|
30
|
+
const hasDirectHandle = Array.from(handles).some(handle => {
|
|
31
|
+
return !Array.from(nestedSortables).some(nested => nested.contains(handle));
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
if (hasDirectHandle) {
|
|
35
|
+
options.handle = '[sortable-handle]';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Add onsorted callback if attribute exists
|
|
39
|
+
const onsortedCode = sortableElem.getAttribute('onsorted');
|
|
40
|
+
if (onsortedCode) {
|
|
41
|
+
options.onEnd = function(evt) {
|
|
42
|
+
try {
|
|
43
|
+
const asyncFn = new Function(`return (async function(evt) { ${onsortedCode} })`)();
|
|
44
|
+
asyncFn.call(sortableElem, evt);
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.error('Error in onsorted execution:', error);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
Sortable.create(sortableElem, options);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function init () {
|
|
55
|
+
if (!isEditMode) return;
|
|
56
|
+
|
|
57
|
+
// Set up sortable on page load
|
|
58
|
+
document.querySelectorAll('[sortable]').forEach(makeSortable);
|
|
59
|
+
|
|
60
|
+
// Set up listener for dynamically added elements
|
|
61
|
+
Mutation.onAddElement({
|
|
62
|
+
selectorFilter: "[sortable]",
|
|
63
|
+
debounce: 200
|
|
64
|
+
}, (changes) => {
|
|
65
|
+
changes.forEach(({ element }) => {
|
|
66
|
+
makeSortable(element);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// ❗️re-initializing sortable on parent elements isn't necessary
|
|
71
|
+
// sortable.js handles this automatically
|
|
72
|
+
// ❌ onElementAdded(newElem => makeSortable(newElem.closest('[sortable]')))
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export { init };
|
|
76
|
+
export default init;
|
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
// Helper to check if an array contains DOM nodes
|
|
2
|
+
const isElementArray = (arr) => {
|
|
3
|
+
if (!Array.isArray(arr)) return false;
|
|
4
|
+
// Empty arrays from element operations should stay wrapped for chainability
|
|
5
|
+
if (arr.length === 0) return true;
|
|
6
|
+
return arr.every(item => item instanceof Element || item instanceof Node || item instanceof Document);
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const createMethodHandler = (elements, plugins, methods) => ({
|
|
10
|
+
get(target, prop) {
|
|
11
|
+
// Handle array-like numeric access and length
|
|
12
|
+
if (prop === 'length') return elements.length;
|
|
13
|
+
|
|
14
|
+
// Handle Symbol.iterator BEFORE number check
|
|
15
|
+
if (prop === Symbol.iterator) {
|
|
16
|
+
return elements[Symbol.iterator].bind(elements);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Handle other Symbols (toStringTag, etc.) - return undefined to avoid Symbol-to-number conversion
|
|
20
|
+
if (typeof prop === 'symbol') {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Handle numeric indices
|
|
25
|
+
if (!isNaN(prop)) return elements[prop];
|
|
26
|
+
|
|
27
|
+
// Handle array methods
|
|
28
|
+
if (prop in Array.prototype) {
|
|
29
|
+
const arrayMethod = Array.prototype[prop];
|
|
30
|
+
return (...args) => {
|
|
31
|
+
const result = arrayMethod.apply(elements, args);
|
|
32
|
+
// For methods that return arrays, only wrap if they contain DOM nodes
|
|
33
|
+
// Otherwise return plain array (jQuery-like behavior for primitives)
|
|
34
|
+
if (Array.isArray(result)) {
|
|
35
|
+
return isElementArray(result)
|
|
36
|
+
? createElementProxy(result, plugins, methods)
|
|
37
|
+
: result; // Return plain array for primitives
|
|
38
|
+
}
|
|
39
|
+
// For single element returns (like find), return raw value
|
|
40
|
+
if (result instanceof Element) {
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
43
|
+
// For boolean/primitive returns (some, every, etc), return raw value
|
|
44
|
+
return result;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Handle event listeners and support event delegation
|
|
49
|
+
if (prop.startsWith('on')) {
|
|
50
|
+
return (selectorOrHandlerOrObject, optionalHandler) => {
|
|
51
|
+
const eventName = prop.slice(2);
|
|
52
|
+
|
|
53
|
+
// Handle object of bindings
|
|
54
|
+
if (selectorOrHandlerOrObject && typeof selectorOrHandlerOrObject === 'object') {
|
|
55
|
+
Object.entries(selectorOrHandlerOrObject).forEach(([selector, handler]) => {
|
|
56
|
+
elements.forEach(el => {
|
|
57
|
+
el.addEventListener(eventName, (e) => {
|
|
58
|
+
const closest = e.target.closest(selector);
|
|
59
|
+
if (closest && el.contains(closest)) {
|
|
60
|
+
handler.call(closest, e);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
return createElementProxy(elements, plugins, methods);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Handle single binding (existing delegation code)
|
|
69
|
+
if (optionalHandler) {
|
|
70
|
+
const selector = selectorOrHandlerOrObject;
|
|
71
|
+
const handler = optionalHandler;
|
|
72
|
+
|
|
73
|
+
elements.forEach(el => {
|
|
74
|
+
el.addEventListener(eventName, (e) => {
|
|
75
|
+
const closest = e.target.closest(selector);
|
|
76
|
+
if (closest && el.contains(closest)) {
|
|
77
|
+
handler.call(closest, e);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
} else {
|
|
82
|
+
const handler = selectorOrHandlerOrObject;
|
|
83
|
+
elements.forEach(el => el.addEventListener(eventName, handler));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return createElementProxy(elements, plugins, methods);
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Check plugins first
|
|
91
|
+
if (plugins[prop]) {
|
|
92
|
+
const plugin = plugins[prop];
|
|
93
|
+
if (typeof plugin === 'function') {
|
|
94
|
+
return createElementProxy(plugin(elements), plugins, methods);
|
|
95
|
+
}
|
|
96
|
+
if (typeof plugin === 'object') {
|
|
97
|
+
if (plugin.value) {
|
|
98
|
+
return createElementProxy(plugin.value(elements), plugins, methods);
|
|
99
|
+
}
|
|
100
|
+
if (plugin.properties) {
|
|
101
|
+
return createNestedPluginProxy(elements, plugin.properties, plugins, methods);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return plugin;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (methods[prop]) {
|
|
108
|
+
const method = methods[prop];
|
|
109
|
+
return (...args) => {
|
|
110
|
+
const result = method.apply(createElementProxy(elements, plugins, methods), args);
|
|
111
|
+
// Only wrap arrays that contain DOM nodes, return plain arrays for primitives
|
|
112
|
+
return (result instanceof Array && isElementArray(result))
|
|
113
|
+
? createElementProxy(result, plugins, methods)
|
|
114
|
+
: result;
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Check if we have any elements to work with
|
|
119
|
+
// Example: All('.missing') returns [] so firstEl is null
|
|
120
|
+
const firstEl = elements[0];
|
|
121
|
+
if (!firstEl) {
|
|
122
|
+
// No elements found - need to handle property access gracefully
|
|
123
|
+
// so chains like All.missing.classList.add('hidden') don't break
|
|
124
|
+
|
|
125
|
+
// These properties return objects with their own methods (classList.add, style.color, etc)
|
|
126
|
+
// We know these are safe to proxy even when empty
|
|
127
|
+
if (['style', 'classList', 'dataset'].includes(prop)) {
|
|
128
|
+
return createIntermediateProxy([], prop, plugins, methods);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// For unknown properties, we need to check what they are on the DOM
|
|
132
|
+
// BUT we can't do Element.prototype.classList - that throws "Illegal invocation"
|
|
133
|
+
// Instead we use getOwnPropertyDescriptor which safely tells us ABOUT the property
|
|
134
|
+
const descriptor = Object.getOwnPropertyDescriptor(Element.prototype, prop) ||
|
|
135
|
+
Object.getOwnPropertyDescriptor(Node.prototype, prop);
|
|
136
|
+
|
|
137
|
+
if (descriptor?.get) {
|
|
138
|
+
// Properties like 'children', 'parentElement' have getters
|
|
139
|
+
// Treat them like classList - return a proxy that won't break chains
|
|
140
|
+
return createIntermediateProxy([], prop, plugins, methods);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (typeof descriptor?.value === 'function') {
|
|
144
|
+
// Regular methods like 'appendChild', 'remove'
|
|
145
|
+
// Return a no-op function: All.missing.remove() does nothing, returns proxy
|
|
146
|
+
return (...args) => createElementProxy([], plugins, methods);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Property doesn't exist on DOM elements (like All.missing.foobar)
|
|
150
|
+
return undefined;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const value = firstEl[prop];
|
|
154
|
+
|
|
155
|
+
// The library is designed to handle chaining correctly
|
|
156
|
+
// - When a method returns an Element (like cloneNode), it wraps it in a proxy
|
|
157
|
+
// - When a method returns undefined (like removeAttribute), it chains on the original elements
|
|
158
|
+
// - For other return values, like strings, it returns the results in an array
|
|
159
|
+
// We also handle passing in an all-wrapped proxy object as an argument and loop over all elements in it
|
|
160
|
+
// In the method handler's get function, replace the function call handling with:
|
|
161
|
+
if (typeof value === 'function') {
|
|
162
|
+
return (...args) => {
|
|
163
|
+
// Unwrap any proxy arguments
|
|
164
|
+
const unwrappedArgs = args.map(arg => {
|
|
165
|
+
if (arg && arg.constructor === Proxy) {
|
|
166
|
+
return Array.from(arg);
|
|
167
|
+
}
|
|
168
|
+
return arg;
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
const results = elements.map(el => {
|
|
172
|
+
// Check if any of the unwrapped arguments are arrays (from proxies)
|
|
173
|
+
const hasProxyArgs = unwrappedArgs.some(Array.isArray);
|
|
174
|
+
|
|
175
|
+
if (hasProxyArgs) {
|
|
176
|
+
// Handle proxy arguments case
|
|
177
|
+
const elementResults = unwrappedArgs.map(arg => {
|
|
178
|
+
if (Array.isArray(arg)) {
|
|
179
|
+
return arg.map(proxyEl => el[prop](proxyEl));
|
|
180
|
+
}
|
|
181
|
+
return [el[prop](...args)];
|
|
182
|
+
}).flat();
|
|
183
|
+
return elementResults[elementResults.length - 1];
|
|
184
|
+
} else {
|
|
185
|
+
// Simple case - just call the method once with original arguments
|
|
186
|
+
return el[prop](...args);
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
if (results[0] instanceof Element) {
|
|
191
|
+
return createElementProxy(results.filter(Boolean), plugins, methods);
|
|
192
|
+
}
|
|
193
|
+
if (results[0] === undefined) {
|
|
194
|
+
return createElementProxy(elements, plugins, methods);
|
|
195
|
+
}
|
|
196
|
+
return results;
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Handle intermediate objects (style, classList, dataset)
|
|
201
|
+
if (['style', 'classList', 'dataset'].includes(prop)) {
|
|
202
|
+
return createIntermediateProxy(elements, prop, plugins, methods);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Return array of values for leaf properties
|
|
206
|
+
return elements.map(el => el[prop]);
|
|
207
|
+
},
|
|
208
|
+
|
|
209
|
+
set(target, prop, value) {
|
|
210
|
+
elements.forEach(el => {
|
|
211
|
+
el[prop] = value;
|
|
212
|
+
});
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
const createNestedPluginProxy = (elements, properties, plugins, methods) => {
|
|
218
|
+
return new Proxy({}, {
|
|
219
|
+
get(target, prop) {
|
|
220
|
+
if (properties[prop]) {
|
|
221
|
+
const plugin = properties[prop];
|
|
222
|
+
if (typeof plugin === 'function') {
|
|
223
|
+
return createElementProxy(plugin(elements), plugins, methods);
|
|
224
|
+
}
|
|
225
|
+
return plugin;
|
|
226
|
+
}
|
|
227
|
+
return undefined;
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
const createIntermediateProxy = (elements, propName, plugins, methods) => {
|
|
233
|
+
return new Proxy({}, {
|
|
234
|
+
get(target, prop) {
|
|
235
|
+
// Handle function properties (like classList.add)
|
|
236
|
+
const firstEl = elements[0];
|
|
237
|
+
if (!firstEl) {
|
|
238
|
+
// Return no-op function for any property access on empty collection
|
|
239
|
+
// This ensures chaining continues to work even with no elements
|
|
240
|
+
return (...args) => createElementProxy(elements, plugins, methods);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const value = firstEl[propName][prop];
|
|
244
|
+
if (typeof value === 'function') {
|
|
245
|
+
return (...args) => {
|
|
246
|
+
elements.forEach(el => el[propName][prop](...args));
|
|
247
|
+
return createElementProxy(elements, plugins, methods);
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Return array of values for leaf properties
|
|
252
|
+
return elements.map(el => el[propName][prop]);
|
|
253
|
+
},
|
|
254
|
+
|
|
255
|
+
set(target, prop, value) {
|
|
256
|
+
elements.forEach(el => {
|
|
257
|
+
el[propName][prop] = value;
|
|
258
|
+
});
|
|
259
|
+
return true;
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
const sharedState = {
|
|
265
|
+
plugins: {},
|
|
266
|
+
methods: {}
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
const createElementProxy = (elements, plugins = sharedState.plugins, methods = sharedState.methods) => {
|
|
270
|
+
return new Proxy(elements, createMethodHandler(elements, plugins, methods));
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
const toElementArray = (input) => {
|
|
274
|
+
if (input instanceof Element || input instanceof Document) {
|
|
275
|
+
return [input];
|
|
276
|
+
}
|
|
277
|
+
if (Array.isArray(input)) {
|
|
278
|
+
if (input.every(el => el instanceof Element || el instanceof Document)) {
|
|
279
|
+
return input;
|
|
280
|
+
}
|
|
281
|
+
throw new TypeError('All array elements must be DOM Elements or Document');
|
|
282
|
+
}
|
|
283
|
+
if (typeof input === 'string') {
|
|
284
|
+
return Array.from(document.querySelectorAll(input));
|
|
285
|
+
}
|
|
286
|
+
throw new TypeError('Input must be a selector string, Element, Document, or Array of Elements');
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
// Default plugins
|
|
290
|
+
const defaultPlugins = {
|
|
291
|
+
methods: {
|
|
292
|
+
eq(index) {
|
|
293
|
+
if (typeof index !== 'number') {
|
|
294
|
+
throw new TypeError('eq() requires a number as an argument');
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Handle negative indices (counting from the end)
|
|
298
|
+
const normalizedIndex = index < 0 ? this.length + index : index;
|
|
299
|
+
|
|
300
|
+
// Return array with single element at index, or empty array if index is invalid
|
|
301
|
+
// ❗️ Allows chaining to continue
|
|
302
|
+
return this[normalizedIndex] ? [this[normalizedIndex]] : [];
|
|
303
|
+
},
|
|
304
|
+
|
|
305
|
+
at(index) {
|
|
306
|
+
if (typeof index !== 'number') {
|
|
307
|
+
throw new TypeError('at() requires a number as an argument');
|
|
308
|
+
}
|
|
309
|
+
const normalizedIndex = index < 0 ? this.length + index : index;
|
|
310
|
+
return this[normalizedIndex];
|
|
311
|
+
},
|
|
312
|
+
|
|
313
|
+
prop(properties) {
|
|
314
|
+
if (typeof properties !== 'object' || properties === null) {
|
|
315
|
+
throw new TypeError('prop() requires an object of properties');
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
Object.entries(properties).forEach(([key, value]) => {
|
|
319
|
+
this.forEach(el => {
|
|
320
|
+
el[key] = value;
|
|
321
|
+
});
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
return this;
|
|
325
|
+
},
|
|
326
|
+
|
|
327
|
+
css(styles) {
|
|
328
|
+
if (typeof styles !== 'object' || styles === null) {
|
|
329
|
+
throw new TypeError('css() requires an object of styles');
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
Object.entries(styles).forEach(([property, value]) => {
|
|
333
|
+
this.forEach(el => {
|
|
334
|
+
el.style[property] = value;
|
|
335
|
+
});
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
return this;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
const All = new Proxy(function (selectorOrElements, contextSelector) {
|
|
344
|
+
// If there's a context selector, search within that context
|
|
345
|
+
if (arguments.length === 2) {
|
|
346
|
+
if (typeof contextSelector !== 'string') {
|
|
347
|
+
throw new TypeError('Context selector must be a string');
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Get the context element(s)
|
|
351
|
+
const contextElements = Array.from(document.querySelectorAll(contextSelector));
|
|
352
|
+
|
|
353
|
+
// If first arg is a string selector, search within each context
|
|
354
|
+
if (typeof selectorOrElements === 'string') {
|
|
355
|
+
const elements = contextElements.flatMap(context => {
|
|
356
|
+
// Include context itself if it matches the selector
|
|
357
|
+
const matches = context.matches(selectorOrElements) ? [context] : [];
|
|
358
|
+
// Plus all descendants that match
|
|
359
|
+
const descendants = Array.from(context.querySelectorAll(selectorOrElements));
|
|
360
|
+
return [...matches, ...descendants];
|
|
361
|
+
});
|
|
362
|
+
return createElementProxy(elements);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// If first arg is elements, filter to those within context
|
|
366
|
+
const searchElements = toElementArray(selectorOrElements);
|
|
367
|
+
const elements = searchElements.filter(el =>
|
|
368
|
+
contextElements.some(context => context.contains(el))
|
|
369
|
+
);
|
|
370
|
+
return createElementProxy(elements);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Single argument - normalize and return
|
|
374
|
+
const elements = toElementArray(selectorOrElements);
|
|
375
|
+
return createElementProxy(elements);
|
|
376
|
+
}, {
|
|
377
|
+
get(target, prop) {
|
|
378
|
+
if (prop === 'use') {
|
|
379
|
+
return function (plugin) {
|
|
380
|
+
if (!plugin || typeof plugin !== 'object') {
|
|
381
|
+
throw new TypeError('Plugin must be an object with "properties" or "methods"');
|
|
382
|
+
}
|
|
383
|
+
if (plugin.properties) {
|
|
384
|
+
Object.assign(sharedState.plugins, plugin.properties);
|
|
385
|
+
}
|
|
386
|
+
if (plugin.methods) {
|
|
387
|
+
Object.assign(sharedState.methods, plugin.methods);
|
|
388
|
+
}
|
|
389
|
+
return this;
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
if (prop === Symbol.iterator) return undefined;
|
|
394
|
+
if (prop in target) return target[prop];
|
|
395
|
+
|
|
396
|
+
const elements = Array.from(document.querySelectorAll(`[${prop}], .${prop}`));
|
|
397
|
+
return createElementProxy(elements);
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
// Install default plugins
|
|
402
|
+
All.use(defaultPlugins);
|
|
403
|
+
|
|
404
|
+
// Export to window for global access
|
|
405
|
+
export function exportToWindow() {
|
|
406
|
+
if (!window.hyperclay) {
|
|
407
|
+
window.hyperclay = {};
|
|
408
|
+
}
|
|
409
|
+
window.hyperclay.All = All;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
export default All;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// easily convert a form into a JS object
|
|
2
|
+
export default function getDataFromForm(container) {
|
|
3
|
+
const formData = {};
|
|
4
|
+
|
|
5
|
+
// Helper function to process a single element
|
|
6
|
+
const processElement = (elem) => {
|
|
7
|
+
const name = elem.getAttribute('name');
|
|
8
|
+
const value = elem.getAttribute('value') || elem.value;
|
|
9
|
+
|
|
10
|
+
// Skip elements without a name or with a disabled attribute
|
|
11
|
+
if (!name || elem.disabled) return;
|
|
12
|
+
|
|
13
|
+
// Handle different element types
|
|
14
|
+
switch (elem.type) {
|
|
15
|
+
case 'checkbox':
|
|
16
|
+
if (!formData[name]) {
|
|
17
|
+
formData[name] = [];
|
|
18
|
+
}
|
|
19
|
+
if (elem.checked) {
|
|
20
|
+
formData[name].push(value);
|
|
21
|
+
}
|
|
22
|
+
break;
|
|
23
|
+
case 'radio':
|
|
24
|
+
if (elem.checked) {
|
|
25
|
+
formData[name] = value;
|
|
26
|
+
}
|
|
27
|
+
break;
|
|
28
|
+
case 'select-multiple':
|
|
29
|
+
formData[name] = Array.from(elem.selectedOptions, option => option.value);
|
|
30
|
+
break;
|
|
31
|
+
case 'button':
|
|
32
|
+
case 'submit':
|
|
33
|
+
case 'reset':
|
|
34
|
+
// Only include buttons if they have both name and value attributes
|
|
35
|
+
if (name && value) {
|
|
36
|
+
formData[name] = value;
|
|
37
|
+
}
|
|
38
|
+
break;
|
|
39
|
+
default:
|
|
40
|
+
formData[name] = value;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// If container is a form, use elements property
|
|
45
|
+
if (container instanceof HTMLFormElement) {
|
|
46
|
+
Array.from(container.elements).forEach(processElement);
|
|
47
|
+
}
|
|
48
|
+
// Otherwise, process container itself and then query for elements with name attribute
|
|
49
|
+
else {
|
|
50
|
+
// Process container element if it has a name attribute
|
|
51
|
+
if (container.hasAttribute('name')) {
|
|
52
|
+
processElement(container);
|
|
53
|
+
}
|
|
54
|
+
// Process all child elements with name attributes
|
|
55
|
+
const elements = container.querySelectorAll('[name]');
|
|
56
|
+
elements.forEach(processElement);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return formData;
|
|
60
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export default 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;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
// Extract a more reliable identifier from the URL
|
|
8
|
+
let identifier;
|
|
9
|
+
try {
|
|
10
|
+
const url = new URL(href, window.location.href);
|
|
11
|
+
// Get the filename from the path
|
|
12
|
+
identifier = url.pathname.split('/').pop();
|
|
13
|
+
} catch (e) {
|
|
14
|
+
// Fallback to using the href itself if URL parsing fails
|
|
15
|
+
identifier = href;
|
|
16
|
+
}
|
|
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
|
|
24
|
+
const link = document.createElement('link');
|
|
25
|
+
link.rel = 'stylesheet';
|
|
26
|
+
link.href = href;
|
|
27
|
+
document.head.appendChild(link);
|
|
28
|
+
}
|