hyperclayjs 1.7.0 → 1.8.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/README.md +13 -13
- package/package.json +17 -25
- package/src/core/adminContenteditable.js +51 -0
- package/{core → src/core}/adminInputs.js +29 -8
- package/src/core/adminOnClick.js +54 -0
- package/{core → src/core}/adminResources.js +25 -5
- package/{core → src/core}/savePage.js +1 -1
- package/{core → src/core}/savePageCore.js +12 -2
- package/{custom-attributes → src/custom-attributes}/domHelpers.js +17 -4
- package/{custom-attributes → src/custom-attributes}/events.js +2 -0
- package/src/custom-attributes/onmutation.js +90 -0
- package/src/custom-attributes/onpagemutation.js +32 -0
- package/{custom-attributes → src/custom-attributes}/sortable.js +16 -1
- package/{dom-utilities → src/dom-utilities}/All.js +22 -0
- package/{hyperclay.js → src/hyperclay.js} +3 -3
- package/{module-dependency-graph.json → src/module-dependency-graph.json} +12 -22
- package/{ui → src/ui}/prompts.js +13 -18
- package/{ui → src/ui}/theModal.js +101 -0
- package/{ui → src/ui}/toast.js +4 -3
- package/core/adminContenteditable.js +0 -36
- package/core/adminOnClick.js +0 -31
- package/custom-attributes/onpagemutation.js +0 -20
- /package/{communication → src/communication}/behaviorCollector.js +0 -0
- /package/{communication → src/communication}/sendMessage.js +0 -0
- /package/{communication → src/communication}/uploadFile.js +0 -0
- /package/{core → src/core}/adminSystem.js +0 -0
- /package/{core → src/core}/autosave.js +0 -0
- /package/{core → src/core}/editmode.js +0 -0
- /package/{core → src/core}/editmodeSystem.js +0 -0
- /package/{core → src/core}/enablePersistentFormInputValues.js +0 -0
- /package/{core → src/core}/exportToWindow.js +0 -0
- /package/{core → src/core}/isAdminOfCurrentResource.js +0 -0
- /package/{core → src/core}/optionVisibility.js +0 -0
- /package/{core → src/core}/saveToast.js +0 -0
- /package/{core → src/core}/setPageTypeOnDocumentElement.js +0 -0
- /package/{custom-attributes → src/custom-attributes}/ajaxElements.js +0 -0
- /package/{custom-attributes → src/custom-attributes}/autosize.js +0 -0
- /package/{custom-attributes → src/custom-attributes}/inputHelpers.js +0 -0
- /package/{custom-attributes → src/custom-attributes}/onaftersave.js +0 -0
- /package/{custom-attributes → src/custom-attributes}/onclickaway.js +0 -0
- /package/{custom-attributes → src/custom-attributes}/onclone.js +0 -0
- /package/{custom-attributes → src/custom-attributes}/onrender.js +0 -0
- /package/{custom-attributes → src/custom-attributes}/preventEnter.js +0 -0
- /package/{dom-utilities → src/dom-utilities}/getDataFromForm.js +0 -0
- /package/{dom-utilities → src/dom-utilities}/insertStyleTag.js +0 -0
- /package/{dom-utilities → src/dom-utilities}/onDomReady.js +0 -0
- /package/{dom-utilities → src/dom-utilities}/onLoad.js +0 -0
- /package/{string-utilities → src/string-utilities}/copy-to-clipboard.js +0 -0
- /package/{string-utilities → src/string-utilities}/query.js +0 -0
- /package/{string-utilities → src/string-utilities}/slugify.js +0 -0
- /package/{ui → src/ui}/toast-hyperclay.js +0 -0
- /package/{utilities → src/utilities}/cookie.js +0 -0
- /package/{utilities → src/utilities}/debounce.js +0 -0
- /package/{utilities → src/utilities}/loadVendorScript.js +0 -0
- /package/{utilities → src/utilities}/mutation.js +0 -0
- /package/{utilities → src/utilities}/nearest.js +0 -0
- /package/{utilities → src/utilities}/pipe.js +0 -0
- /package/{utilities → src/utilities}/throttle.js +0 -0
- /package/{vendor → src/vendor}/Sortable.vendor.js +0 -0
- /package/{vendor → src/vendor}/idiomorph.min.js +0 -0
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ Destructure directly from the import:
|
|
|
21
21
|
|
|
22
22
|
```html
|
|
23
23
|
<script type="module">
|
|
24
|
-
const { toast, savePage } = await import('https://cdn.jsdelivr.net/npm/hyperclayjs@1/hyperclay.js?preset=standard');
|
|
24
|
+
const { toast, savePage } = await import('https://cdn.jsdelivr.net/npm/hyperclayjs@1/src/hyperclay.js?preset=standard');
|
|
25
25
|
toast('Hello!');
|
|
26
26
|
</script>
|
|
27
27
|
```
|
|
@@ -30,7 +30,7 @@ Or with custom features:
|
|
|
30
30
|
|
|
31
31
|
```html
|
|
32
32
|
<script type="module">
|
|
33
|
-
const { toast, ask } = await import('https://cdn.jsdelivr.net/npm/hyperclayjs@1/hyperclay.js?features=toast,dialogs');
|
|
33
|
+
const { toast, ask } = await import('https://cdn.jsdelivr.net/npm/hyperclayjs@1/src/hyperclay.js?features=toast,dialogs');
|
|
34
34
|
</script>
|
|
35
35
|
```
|
|
36
36
|
|
|
@@ -59,10 +59,10 @@ import 'hyperclayjs/presets/standard.js';
|
|
|
59
59
|
|--------|------|-------------|
|
|
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
|
-
| edit-mode-helpers |
|
|
62
|
+
| edit-mode-helpers | 7.5KB | Admin-only functionality: [edit-mode-input], [edit-mode-resource], [edit-mode-onclick] |
|
|
63
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
|
-
| save-core | 6.
|
|
65
|
+
| save-core | 6.5KB | Basic save function only - hyperclay.savePage() |
|
|
66
66
|
| save-system | 7.1KB | Manual save: keyboard shortcut (CMD+S), save button, change tracking |
|
|
67
67
|
| save-toast | 0.9KB | Toast notifications for save events |
|
|
68
68
|
|
|
@@ -71,18 +71,18 @@ import 'hyperclayjs/presets/standard.js';
|
|
|
71
71
|
| Module | Size | Description |
|
|
72
72
|
|--------|------|-------------|
|
|
73
73
|
| ajax-elements | 2.8KB | [ajax-form], [ajax-button] for async form submissions |
|
|
74
|
-
| dom-helpers |
|
|
75
|
-
| event-attrs |
|
|
74
|
+
| dom-helpers | 6.2KB | el.nearest, el.val, el.text, el.exec, el.cycle |
|
|
75
|
+
| event-attrs | 4.1KB | [onclickaway], [onclone], [onpagemutation], [onrender] |
|
|
76
76
|
| input-helpers | 1.2KB | [prevent-enter], [autosize] for textareas |
|
|
77
77
|
| onaftersave | 1.2KB | [onaftersave] attribute - run JS when save status changes |
|
|
78
|
-
| sortable |
|
|
78
|
+
| sortable | 3.4KB | Drag-drop sorting with [sortable], lazy-loads ~118KB Sortable.js in edit mode |
|
|
79
79
|
|
|
80
80
|
### UI Components (User interface elements)
|
|
81
81
|
|
|
82
82
|
| Module | Size | Description |
|
|
83
83
|
|--------|------|-------------|
|
|
84
|
-
| dialogs |
|
|
85
|
-
| the-modal |
|
|
84
|
+
| dialogs | 7.7KB | ask(), consent(), tell(), snippet() dialog functions |
|
|
85
|
+
| the-modal | 21.8KB | Full modal window creation system - window.theModal |
|
|
86
86
|
| toast | 7.7KB | Success/error message notifications, toast(msg, msgType) |
|
|
87
87
|
|
|
88
88
|
### Utilities (Core utilities (often auto-included))
|
|
@@ -99,7 +99,7 @@ import 'hyperclayjs/presets/standard.js';
|
|
|
99
99
|
|
|
100
100
|
| Module | Size | Description |
|
|
101
101
|
|--------|------|-------------|
|
|
102
|
-
| all-js |
|
|
102
|
+
| all-js | 14.4KB | Full DOM manipulation library |
|
|
103
103
|
| dom-ready | 0.4KB | DOM ready callback |
|
|
104
104
|
| form-data | 2KB | Extract form data as an object |
|
|
105
105
|
| style-injection | 1.1KB | Dynamic stylesheet injection |
|
|
@@ -127,17 +127,17 @@ import 'hyperclayjs/presets/standard.js';
|
|
|
127
127
|
|
|
128
128
|
## Presets
|
|
129
129
|
|
|
130
|
-
### Minimal (~
|
|
130
|
+
### Minimal (~30.1KB)
|
|
131
131
|
Essential features for basic editing
|
|
132
132
|
|
|
133
133
|
**Modules:** `save-core`, `save-system`, `edit-mode-helpers`, `toast`, `save-toast`, `export-to-window`
|
|
134
134
|
|
|
135
|
-
### Standard (~
|
|
135
|
+
### Standard (~48.8KB)
|
|
136
136
|
Standard feature set for most use cases
|
|
137
137
|
|
|
138
138
|
**Modules:** `save-core`, `save-system`, `edit-mode-helpers`, `persist`, `option-visibility`, `event-attrs`, `dom-helpers`, `toast`, `save-toast`, `export-to-window`
|
|
139
139
|
|
|
140
|
-
### Everything (~
|
|
140
|
+
### Everything (~155.8KB)
|
|
141
141
|
All available features
|
|
142
142
|
|
|
143
143
|
Includes all available modules across all categories.
|
package/package.json
CHANGED
|
@@ -1,39 +1,31 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hyperclayjs",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"description": "Modular JavaScript library for building interactive HTML applications with Hyperclay",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "hyperclay.js",
|
|
7
|
-
"module": "hyperclay.js",
|
|
6
|
+
"main": "src/hyperclay.js",
|
|
7
|
+
"module": "src/hyperclay.js",
|
|
8
8
|
"exports": {
|
|
9
9
|
".": {
|
|
10
|
-
"import": "./hyperclay.js",
|
|
11
|
-
"default": "./hyperclay.js"
|
|
10
|
+
"import": "./src/hyperclay.js",
|
|
11
|
+
"default": "./src/hyperclay.js"
|
|
12
12
|
},
|
|
13
|
-
"./core/*": "./core/*.js",
|
|
14
|
-
"./custom-attributes/*": "./custom-attributes/*.js",
|
|
15
|
-
"./ui/*": "./ui/*.js",
|
|
16
|
-
"./utilities/*": "./utilities/*.js",
|
|
17
|
-
"./dom-utilities/*": "./dom-utilities/*.js",
|
|
18
|
-
"./string-utilities/*": "./string-utilities/*.js",
|
|
19
|
-
"./communication/*": "./communication/*.js",
|
|
20
|
-
"./vendor/*": "./vendor/*.js"
|
|
13
|
+
"./core/*": "./src/core/*.js",
|
|
14
|
+
"./custom-attributes/*": "./src/custom-attributes/*.js",
|
|
15
|
+
"./ui/*": "./src/ui/*.js",
|
|
16
|
+
"./utilities/*": "./src/utilities/*.js",
|
|
17
|
+
"./dom-utilities/*": "./src/dom-utilities/*.js",
|
|
18
|
+
"./string-utilities/*": "./src/string-utilities/*.js",
|
|
19
|
+
"./communication/*": "./src/communication/*.js",
|
|
20
|
+
"./vendor/*": "./src/vendor/*.js"
|
|
21
21
|
},
|
|
22
22
|
"files": [
|
|
23
|
-
"
|
|
24
|
-
"custom-attributes",
|
|
25
|
-
"ui",
|
|
26
|
-
"utilities",
|
|
27
|
-
"dom-utilities",
|
|
28
|
-
"string-utilities",
|
|
29
|
-
"communication",
|
|
30
|
-
"vendor",
|
|
31
|
-
"hyperclay.js",
|
|
32
|
-
"module-dependency-graph.json"
|
|
23
|
+
"src"
|
|
33
24
|
],
|
|
34
25
|
"scripts": {
|
|
35
|
-
"dev": "npm run build && http-server -p 3535 -c-1 -o /index.html",
|
|
26
|
+
"dev": "npm run build && npm run build:website && http-server website -p 3535 -c-1 -o /index.html",
|
|
36
27
|
"build": "npm run generate:deps && npm run build:loader && npm run build:readme && npm run build:load-jsdelivr && npm run build:index-url",
|
|
28
|
+
"build:website": "node scripts/build-website.js",
|
|
37
29
|
"generate:deps": "node build/generate-dependency-graph.js",
|
|
38
30
|
"build:loader": "node build/build-loader.js",
|
|
39
31
|
"build:readme": "node build/generate-readme.js",
|
|
@@ -44,7 +36,7 @@
|
|
|
44
36
|
"format": "prettier --write .",
|
|
45
37
|
"release": "./scripts/release.sh",
|
|
46
38
|
"prepublishOnly": "npm run build && npm test",
|
|
47
|
-
"postpublish": "test -n \"$SKIP_POSTPUBLISH\" || open http://127.0.0.1:3535/
|
|
39
|
+
"postpublish": "test -n \"$SKIP_POSTPUBLISH\" || open http://127.0.0.1:3535/load-jsdelivr.html"
|
|
48
40
|
},
|
|
49
41
|
"repository": {
|
|
50
42
|
"type": "git",
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { isEditMode, isOwner } from "./isAdminOfCurrentResource.js";
|
|
2
|
+
import onDomReady from "../dom-utilities/onDomReady.js";
|
|
3
|
+
import {beforeSave} from "./savePage.js";
|
|
4
|
+
|
|
5
|
+
export function disableContentEditableBeforeSave () {
|
|
6
|
+
beforeSave(docElem => {
|
|
7
|
+
docElem.querySelectorAll('[edit-mode-contenteditable]').forEach(resource => {
|
|
8
|
+
const originalValue = resource.getAttribute("contenteditable");
|
|
9
|
+
resource.setAttribute("inert-contenteditable", originalValue);
|
|
10
|
+
resource.removeAttribute("contenteditable");
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function enableContentEditableForAdminOnPageLoad () {
|
|
16
|
+
if (!isEditMode) return;
|
|
17
|
+
|
|
18
|
+
onDomReady(() => {
|
|
19
|
+
enableContentEditable();
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Runtime toggle functions
|
|
24
|
+
export function enableContentEditable() {
|
|
25
|
+
document.querySelectorAll('[edit-mode-contenteditable]').forEach(el => {
|
|
26
|
+
let val = el.getAttribute("inert-contenteditable");
|
|
27
|
+
if (!["false", "plaintext-only"].includes(val)) val = "true";
|
|
28
|
+
el.setAttribute("contenteditable", val);
|
|
29
|
+
el.removeAttribute("inert-contenteditable");
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function disableContentEditable() {
|
|
34
|
+
document.querySelectorAll('[edit-mode-contenteditable]').forEach(el => {
|
|
35
|
+
const val = el.getAttribute("contenteditable") || "true";
|
|
36
|
+
el.setAttribute("inert-contenteditable", val);
|
|
37
|
+
el.removeAttribute("contenteditable");
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Auto-initialize
|
|
42
|
+
export function init() {
|
|
43
|
+
disableContentEditableBeforeSave();
|
|
44
|
+
enableContentEditableForAdminOnPageLoad();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Export to window
|
|
48
|
+
window.hyperclay = window.hyperclay || {};
|
|
49
|
+
window.hyperclay.enableContentEditable = enableContentEditable;
|
|
50
|
+
window.hyperclay.disableContentEditable = disableContentEditable;
|
|
51
|
+
window.h = window.hyperclay;
|
|
@@ -18,13 +18,28 @@ export function enableAdminInputsOnPageLoad() {
|
|
|
18
18
|
if (!isEditMode) return;
|
|
19
19
|
|
|
20
20
|
onDomReady(() => {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
enableAdminInputs();
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Runtime toggle functions
|
|
26
|
+
export function enableAdminInputs() {
|
|
27
|
+
document.querySelectorAll('[edit-mode-input]').forEach(input => {
|
|
28
|
+
if (supportsReadonly(input)) {
|
|
29
|
+
input.removeAttribute('readonly');
|
|
30
|
+
} else {
|
|
31
|
+
input.removeAttribute('disabled');
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function disableAdminInputs() {
|
|
37
|
+
document.querySelectorAll('[edit-mode-input]').forEach(input => {
|
|
38
|
+
if (supportsReadonly(input)) {
|
|
39
|
+
input.setAttribute('readonly', '');
|
|
40
|
+
} else {
|
|
41
|
+
input.setAttribute('disabled', '');
|
|
42
|
+
}
|
|
28
43
|
});
|
|
29
44
|
}
|
|
30
45
|
|
|
@@ -55,4 +70,10 @@ function supportsReadonly(element) {
|
|
|
55
70
|
export function init() {
|
|
56
71
|
disableAdminInputsBeforeSave();
|
|
57
72
|
enableAdminInputsOnPageLoad();
|
|
58
|
-
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Export to window
|
|
76
|
+
window.hyperclay = window.hyperclay || {};
|
|
77
|
+
window.hyperclay.enableAdminInputs = enableAdminInputs;
|
|
78
|
+
window.hyperclay.disableAdminInputs = disableAdminInputs;
|
|
79
|
+
window.h = window.hyperclay;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { isEditMode, isOwner } from "./isAdminOfCurrentResource.js";
|
|
2
|
+
import onDomReady from "../dom-utilities/onDomReady.js";
|
|
3
|
+
import {beforeSave} from "./savePage.js";
|
|
4
|
+
|
|
5
|
+
export function disableOnClickBeforeSave () {
|
|
6
|
+
beforeSave(docElem => {
|
|
7
|
+
docElem.querySelectorAll('[edit-mode-onclick]').forEach(resource => {
|
|
8
|
+
const originalValue = resource.getAttribute("onclick");
|
|
9
|
+
resource.setAttribute("inert-onclick", originalValue);
|
|
10
|
+
resource.removeAttribute("onclick");
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function enableOnClickForAdminOnPageLoad () {
|
|
16
|
+
if (!isEditMode) return;
|
|
17
|
+
|
|
18
|
+
onDomReady(() => {
|
|
19
|
+
enableOnClick();
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Runtime toggle functions
|
|
24
|
+
export function enableOnClick() {
|
|
25
|
+
document.querySelectorAll('[edit-mode-onclick]').forEach(el => {
|
|
26
|
+
const val = el.getAttribute("inert-onclick");
|
|
27
|
+
if (val) {
|
|
28
|
+
el.setAttribute("onclick", val);
|
|
29
|
+
el.removeAttribute("inert-onclick");
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function disableOnClick() {
|
|
35
|
+
document.querySelectorAll('[edit-mode-onclick]').forEach(el => {
|
|
36
|
+
const val = el.getAttribute("onclick");
|
|
37
|
+
if (val) {
|
|
38
|
+
el.setAttribute("inert-onclick", val);
|
|
39
|
+
el.removeAttribute("onclick");
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Auto-initialize
|
|
45
|
+
export function init() {
|
|
46
|
+
disableOnClickBeforeSave();
|
|
47
|
+
enableOnClickForAdminOnPageLoad();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Export to window
|
|
51
|
+
window.hyperclay = window.hyperclay || {};
|
|
52
|
+
window.hyperclay.enableOnClick = enableOnClick;
|
|
53
|
+
window.hyperclay.disableOnClick = disableOnClick;
|
|
54
|
+
window.h = window.hyperclay;
|
|
@@ -18,11 +18,25 @@ export function enableAdminResourcesOnPageLoad () {
|
|
|
18
18
|
if (!isEditMode) return;
|
|
19
19
|
|
|
20
20
|
onDomReady(() => {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
enableAdminResources();
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Runtime toggle functions
|
|
26
|
+
export function enableAdminResources() {
|
|
27
|
+
document.querySelectorAll('[edit-mode-resource]:is(style, link, script)[type^="inert/"]').forEach(resource => {
|
|
28
|
+
resource.type = resource.type.replace(/inert\//g, '');
|
|
29
|
+
resource.replaceWith(resource.cloneNode(true));
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function disableAdminResources() {
|
|
34
|
+
document.querySelectorAll('[edit-mode-resource]:is(style, link, script)').forEach(resource => {
|
|
35
|
+
const currentType = resource.getAttribute('type') || 'text/javascript';
|
|
36
|
+
if (!currentType.startsWith('inert/')) {
|
|
37
|
+
resource.setAttribute('type', `inert/${currentType}`);
|
|
24
38
|
resource.replaceWith(resource.cloneNode(true));
|
|
25
|
-
}
|
|
39
|
+
}
|
|
26
40
|
});
|
|
27
41
|
}
|
|
28
42
|
|
|
@@ -30,4 +44,10 @@ export function enableAdminResourcesOnPageLoad () {
|
|
|
30
44
|
export function init() {
|
|
31
45
|
disableAdminResourcesBeforeSave();
|
|
32
46
|
enableAdminResourcesOnPageLoad();
|
|
33
|
-
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Export to window
|
|
50
|
+
window.hyperclay = window.hyperclay || {};
|
|
51
|
+
window.hyperclay.enableAdminResources = enableAdminResources;
|
|
52
|
+
window.hyperclay.disableAdminResources = disableAdminResources;
|
|
53
|
+
window.h = window.hyperclay;
|
|
@@ -111,7 +111,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
111
111
|
* @param {Function} callback - Optional callback for custom handling
|
|
112
112
|
*/
|
|
113
113
|
export function savePage(callback = () => {}) {
|
|
114
|
-
if (!isEditMode) {
|
|
114
|
+
if (!isEditMode && !window.hyperclay?.testMode) {
|
|
115
115
|
return;
|
|
116
116
|
}
|
|
117
117
|
|
|
@@ -73,11 +73,21 @@ export function getPageContents() {
|
|
|
73
73
|
* });
|
|
74
74
|
*/
|
|
75
75
|
export function savePage(callback = () => {}) {
|
|
76
|
-
if (
|
|
76
|
+
if (saveInProgress) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (!isEditMode && !window.hyperclay?.testMode) {
|
|
77
80
|
return;
|
|
78
81
|
}
|
|
79
82
|
|
|
80
|
-
|
|
83
|
+
let currentContents;
|
|
84
|
+
try {
|
|
85
|
+
currentContents = getPageContents();
|
|
86
|
+
} catch (err) {
|
|
87
|
+
console.error('savePage: getPageContents failed', err);
|
|
88
|
+
callback({msg: err.message, msgType: "error"});
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
81
91
|
saveInProgress = true;
|
|
82
92
|
|
|
83
93
|
// Test mode: skip network request, return mock success
|
|
@@ -24,22 +24,35 @@ function init () {
|
|
|
24
24
|
}
|
|
25
25
|
});
|
|
26
26
|
|
|
27
|
-
// elem.val.project returns the value of the nearest "project" attribute
|
|
28
|
-
// elem.val.project = "hello world" sets the value of the nearest "project" attribute
|
|
27
|
+
// elem.val.project returns the value of the nearest element with "project" attribute
|
|
28
|
+
// elem.val.project = "hello world" sets the value of the nearest element with "project" attribute
|
|
29
|
+
// For form elements (input/select/textarea), uses the value property; otherwise uses the attribute
|
|
29
30
|
Object.defineProperty(HTMLElement.prototype, 'val', {
|
|
30
31
|
configurable: true,
|
|
31
32
|
get: function() {
|
|
32
33
|
let element = this;
|
|
33
34
|
|
|
35
|
+
const isFormElement = (elem) =>
|
|
36
|
+
elem.tagName === 'INPUT' || elem.tagName === 'SELECT' || elem.tagName === 'TEXTAREA';
|
|
37
|
+
|
|
34
38
|
const handler = {
|
|
35
39
|
get(target, prop) {
|
|
36
|
-
return nearest(element, `[${prop}], .${prop}`, elem =>
|
|
40
|
+
return nearest(element, `[${prop}], .${prop}`, elem => {
|
|
41
|
+
if (isFormElement(elem)) {
|
|
42
|
+
return elem.value;
|
|
43
|
+
}
|
|
44
|
+
return elem.getAttribute(prop);
|
|
45
|
+
});
|
|
37
46
|
},
|
|
38
47
|
set(target, prop, value) {
|
|
39
48
|
const foundElem = nearest(element, `[${prop}], .${prop}`);
|
|
40
49
|
|
|
41
50
|
if (foundElem) {
|
|
42
|
-
foundElem
|
|
51
|
+
if (isFormElement(foundElem)) {
|
|
52
|
+
foundElem.value = value;
|
|
53
|
+
} else {
|
|
54
|
+
foundElem.setAttribute(prop, value);
|
|
55
|
+
}
|
|
43
56
|
}
|
|
44
57
|
|
|
45
58
|
return true;
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
// Events module - combines all event attribute handlers
|
|
2
2
|
import { init as initOnclickaway } from './onclickaway.js';
|
|
3
3
|
import { init as initOnclone } from './onclone.js';
|
|
4
|
+
import { init as initOnmutation } from './onmutation.js';
|
|
4
5
|
import { init as initOnpagemutation } from './onpagemutation.js';
|
|
5
6
|
import { init as initOnrender } from './onrender.js';
|
|
6
7
|
|
|
7
8
|
function init() {
|
|
8
9
|
initOnclickaway();
|
|
9
10
|
initOnclone();
|
|
11
|
+
initOnmutation();
|
|
10
12
|
initOnpagemutation();
|
|
11
13
|
initOnrender();
|
|
12
14
|
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/*
|
|
2
|
+
[onmutation] - Trigger code when this element or its children change
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
<div onmutation="console.log('My content changed')">
|
|
6
|
+
<span contenteditable>Edit me</span>
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
Unlike [onglobalmutation]/[onpagemutation] which fires on ANY DOM change,
|
|
10
|
+
[onmutation] only fires when the element itself or its descendants mutate.
|
|
11
|
+
*/
|
|
12
|
+
import Mutation from "../utilities/mutation.js";
|
|
13
|
+
|
|
14
|
+
const observers = new WeakMap();
|
|
15
|
+
|
|
16
|
+
function setupMutationObserver(element) {
|
|
17
|
+
if (observers.has(element)) return;
|
|
18
|
+
|
|
19
|
+
const executeMutation = async () => {
|
|
20
|
+
try {
|
|
21
|
+
const code = element.getAttribute('onmutation');
|
|
22
|
+
if (!code) return;
|
|
23
|
+
const asyncFn = new Function(`return (async function() { ${code} })`)();
|
|
24
|
+
await asyncFn.call(element);
|
|
25
|
+
} catch (error) {
|
|
26
|
+
console.error('Error in onmutation execution:', error);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const observer = new MutationObserver(() => {
|
|
31
|
+
executeMutation();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
observer.observe(element, {
|
|
35
|
+
childList: true,
|
|
36
|
+
subtree: true,
|
|
37
|
+
characterData: true,
|
|
38
|
+
attributes: true
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
observers.set(element, observer);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function teardownMutationObserver(element) {
|
|
45
|
+
const observer = observers.get(element);
|
|
46
|
+
if (observer) {
|
|
47
|
+
observer.disconnect();
|
|
48
|
+
observers.delete(element);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function init() {
|
|
53
|
+
// Set up existing elements
|
|
54
|
+
document.querySelectorAll('[onmutation]').forEach(setupMutationObserver);
|
|
55
|
+
|
|
56
|
+
// Watch for dynamically added elements with onmutation
|
|
57
|
+
Mutation.onAddElement({
|
|
58
|
+
selectorFilter: '[onmutation]',
|
|
59
|
+
debounce: 200
|
|
60
|
+
}, (changes) => {
|
|
61
|
+
changes.forEach(({ element }) => {
|
|
62
|
+
setupMutationObserver(element);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Clean up when elements are removed
|
|
67
|
+
Mutation.onRemoveElement({
|
|
68
|
+
selectorFilter: '[onmutation]',
|
|
69
|
+
debounce: 200
|
|
70
|
+
}, (changes) => {
|
|
71
|
+
changes.forEach(({ element }) => {
|
|
72
|
+
teardownMutationObserver(element);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Watch for attribute removal
|
|
77
|
+
Mutation.onAttribute({
|
|
78
|
+
selectorFilter: '[onmutation]',
|
|
79
|
+
debounce: 200
|
|
80
|
+
}, (changes) => {
|
|
81
|
+
changes.forEach(({ element, attribute, newValue }) => {
|
|
82
|
+
if (attribute === 'onmutation' && newValue === null) {
|
|
83
|
+
teardownMutationObserver(element);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export { init };
|
|
90
|
+
export default init;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/*
|
|
2
|
+
[onpagemutation] / [onglobalmutation] - Trigger code when ANY element on the page changes
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
<span onglobalmutation="this.textContent = All('li').length">0</span>
|
|
6
|
+
<span onpagemutation="this.textContent = All('li').length">0</span>
|
|
7
|
+
|
|
8
|
+
Both attributes are equivalent - onglobalmutation is the preferred name for clarity.
|
|
9
|
+
*/
|
|
10
|
+
import Mutation from "../utilities/mutation.js";
|
|
11
|
+
|
|
12
|
+
function init() {
|
|
13
|
+
const executeGlobalMutation = async element => {
|
|
14
|
+
try {
|
|
15
|
+
// Support both onglobalmutation and onpagemutation (legacy)
|
|
16
|
+
const code = element.getAttribute('onglobalmutation') || element.getAttribute('onpagemutation');
|
|
17
|
+
const asyncFn = new Function(`return (async function() { ${code} })`)();
|
|
18
|
+
await asyncFn.call(element);
|
|
19
|
+
} catch (error) {
|
|
20
|
+
console.error('Error in onglobalmutation/onpagemutation execution:', error);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
Mutation.onAnyChange({
|
|
25
|
+
debounce: 200,
|
|
26
|
+
omitChangeDetails: true
|
|
27
|
+
}, () => {
|
|
28
|
+
document.querySelectorAll('[onglobalmutation], [onpagemutation]').forEach(executeGlobalMutation);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
export { init };
|
|
32
|
+
export default init;
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
How to use:
|
|
6
6
|
- add `sortable` attribute to an element to make children sortable
|
|
7
7
|
- e.g. <div sortable></div>
|
|
8
|
+
- add `onsorting` attribute to execute code during drag
|
|
9
|
+
- e.g. <ul sortable onsorting="console.log('Dragging!')"></ul>
|
|
8
10
|
- add `onsorted` attribute to execute code when items are sorted
|
|
9
11
|
- e.g. <ul sortable onsorted="console.log('Items reordered!')"></ul>
|
|
10
12
|
|
|
@@ -39,7 +41,20 @@ function makeSortable(sortableElem, Sortable) {
|
|
|
39
41
|
options.handle = '[sortable-handle]';
|
|
40
42
|
}
|
|
41
43
|
|
|
42
|
-
// Add
|
|
44
|
+
// Add onsorting callback if attribute exists (fires during drag)
|
|
45
|
+
const onsortingCode = sortableElem.getAttribute('onsorting');
|
|
46
|
+
if (onsortingCode) {
|
|
47
|
+
options.onMove = function(evt) {
|
|
48
|
+
try {
|
|
49
|
+
const asyncFn = new Function(`return (async function(evt) { ${onsortingCode} })`)();
|
|
50
|
+
asyncFn.call(sortableElem, evt);
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.error('Error in onsorting execution:', error);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Add onsorted callback if attribute exists (fires after drop)
|
|
43
58
|
const onsortedCode = sortableElem.getAttribute('onsorted');
|
|
44
59
|
if (onsortedCode) {
|
|
45
60
|
options.onEnd = function(evt) {
|
|
@@ -336,6 +336,28 @@ const defaultPlugins = {
|
|
|
336
336
|
});
|
|
337
337
|
|
|
338
338
|
return this;
|
|
339
|
+
},
|
|
340
|
+
|
|
341
|
+
pluck(attr) {
|
|
342
|
+
return this.map(el => el.getAttribute(attr));
|
|
343
|
+
},
|
|
344
|
+
|
|
345
|
+
unique() {
|
|
346
|
+
return [...new Set(this)];
|
|
347
|
+
},
|
|
348
|
+
|
|
349
|
+
sortBy(fn) {
|
|
350
|
+
if (typeof fn === 'string') {
|
|
351
|
+
const attr = fn;
|
|
352
|
+
fn = el => el.getAttribute(attr);
|
|
353
|
+
}
|
|
354
|
+
return [...this].sort((a, b) => {
|
|
355
|
+
const aVal = fn(a);
|
|
356
|
+
const bVal = fn(b);
|
|
357
|
+
if (aVal < bVal) return -1;
|
|
358
|
+
if (aVal > bVal) return 1;
|
|
359
|
+
return 0;
|
|
360
|
+
});
|
|
339
361
|
}
|
|
340
362
|
}
|
|
341
363
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* HyperclayJS v1.
|
|
2
|
+
* HyperclayJS v1.8.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.
|
|
@@ -8,11 +8,11 @@
|
|
|
8
8
|
*
|
|
9
9
|
* <script type="module">
|
|
10
10
|
* // With window.hyperclay (default presets include export-to-window):
|
|
11
|
-
* await import('https://cdn.jsdelivr.net/npm/hyperclayjs@1/hyperclay.js?preset=minimal');
|
|
11
|
+
* await import('https://cdn.jsdelivr.net/npm/hyperclayjs@1/src/hyperclay.js?preset=minimal');
|
|
12
12
|
* const { toast, savePage } = window.hyperclay;
|
|
13
13
|
*
|
|
14
14
|
* // ES modules only (exclude export-to-window):
|
|
15
|
-
* const hyperclay = await import('https://cdn.jsdelivr.net/npm/hyperclayjs@1/hyperclay.js?preset=minimal&exclude=export-to-window');
|
|
15
|
+
* const hyperclay = await import('https://cdn.jsdelivr.net/npm/hyperclayjs@1/src/hyperclay.js?preset=minimal&exclude=export-to-window');
|
|
16
16
|
* const modules = window.hyperclayModules;
|
|
17
17
|
* </script>
|
|
18
18
|
*
|
|
@@ -1,15 +1,5 @@
|
|
|
1
1
|
{
|
|
2
2
|
"rawDependencies": {
|
|
3
|
-
"__tests__/All.test.js": [
|
|
4
|
-
"dom-utilities/All.js"
|
|
5
|
-
],
|
|
6
|
-
"babel.config.js": [],
|
|
7
|
-
"build/build-loader.js": [],
|
|
8
|
-
"build/generate-dependency-graph.js": [],
|
|
9
|
-
"build/generate-load-jsdelivr.js": [],
|
|
10
|
-
"build/generate-readme.js": [],
|
|
11
|
-
"build/hyperclay.template.js": [],
|
|
12
|
-
"build/update-index-url.js": [],
|
|
13
3
|
"communication/behaviorCollector.js": [],
|
|
14
4
|
"communication/sendMessage.js": [
|
|
15
5
|
"communication/behaviorCollector.js",
|
|
@@ -89,9 +79,6 @@
|
|
|
89
79
|
"core/savePage.js",
|
|
90
80
|
"dom-utilities/onDomReady.js"
|
|
91
81
|
],
|
|
92
|
-
"coverage/lcov-report/block-navigation.js": [],
|
|
93
|
-
"coverage/lcov-report/prettify.js": [],
|
|
94
|
-
"coverage/lcov-report/sorter.js": [],
|
|
95
82
|
"custom-attributes/ajaxElements.js": [
|
|
96
83
|
"dom-utilities/getDataFromForm.js"
|
|
97
84
|
],
|
|
@@ -103,6 +90,7 @@
|
|
|
103
90
|
"custom-attributes/events.js": [
|
|
104
91
|
"custom-attributes/onclickaway.js",
|
|
105
92
|
"custom-attributes/onclone.js",
|
|
93
|
+
"custom-attributes/onmutation.js",
|
|
106
94
|
"custom-attributes/onpagemutation.js",
|
|
107
95
|
"custom-attributes/onrender.js"
|
|
108
96
|
],
|
|
@@ -113,6 +101,9 @@
|
|
|
113
101
|
"custom-attributes/onaftersave.js": [],
|
|
114
102
|
"custom-attributes/onclickaway.js": [],
|
|
115
103
|
"custom-attributes/onclone.js": [],
|
|
104
|
+
"custom-attributes/onmutation.js": [
|
|
105
|
+
"utilities/mutation.js"
|
|
106
|
+
],
|
|
116
107
|
"custom-attributes/onpagemutation.js": [
|
|
117
108
|
"utilities/mutation.js"
|
|
118
109
|
],
|
|
@@ -132,7 +123,6 @@
|
|
|
132
123
|
"dom-utilities/onDomReady.js": [],
|
|
133
124
|
"dom-utilities/onLoad.js": [],
|
|
134
125
|
"hyperclay.js": [],
|
|
135
|
-
"jest.config.js": [],
|
|
136
126
|
"string-utilities/copy-to-clipboard.js": [],
|
|
137
127
|
"string-utilities/query.js": [],
|
|
138
128
|
"string-utilities/slugify.js": [],
|
|
@@ -161,7 +151,7 @@
|
|
|
161
151
|
"save-core": {
|
|
162
152
|
"name": "save-core",
|
|
163
153
|
"category": "core",
|
|
164
|
-
"size": 6.
|
|
154
|
+
"size": 6.5,
|
|
165
155
|
"files": [
|
|
166
156
|
"core/savePageCore.js"
|
|
167
157
|
],
|
|
@@ -218,7 +208,7 @@
|
|
|
218
208
|
"edit-mode-helpers": {
|
|
219
209
|
"name": "edit-mode-helpers",
|
|
220
210
|
"category": "core",
|
|
221
|
-
"size":
|
|
211
|
+
"size": 7.500000000000001,
|
|
222
212
|
"files": [
|
|
223
213
|
"core/adminSystem.js",
|
|
224
214
|
"core/adminContenteditable.js",
|
|
@@ -274,7 +264,7 @@
|
|
|
274
264
|
"event-attrs": {
|
|
275
265
|
"name": "event-attrs",
|
|
276
266
|
"category": "custom-attributes",
|
|
277
|
-
"size":
|
|
267
|
+
"size": 4.1000000000000005,
|
|
278
268
|
"files": [
|
|
279
269
|
"custom-attributes/events.js",
|
|
280
270
|
"custom-attributes/onclickaway.js",
|
|
@@ -298,7 +288,7 @@
|
|
|
298
288
|
"sortable": {
|
|
299
289
|
"name": "sortable",
|
|
300
290
|
"category": "custom-attributes",
|
|
301
|
-
"size":
|
|
291
|
+
"size": 3.4,
|
|
302
292
|
"files": [
|
|
303
293
|
"custom-attributes/sortable.js"
|
|
304
294
|
],
|
|
@@ -308,7 +298,7 @@
|
|
|
308
298
|
"dom-helpers": {
|
|
309
299
|
"name": "dom-helpers",
|
|
310
300
|
"category": "custom-attributes",
|
|
311
|
-
"size":
|
|
301
|
+
"size": 6.2,
|
|
312
302
|
"files": [
|
|
313
303
|
"custom-attributes/domHelpers.js"
|
|
314
304
|
],
|
|
@@ -340,7 +330,7 @@
|
|
|
340
330
|
"dialogs": {
|
|
341
331
|
"name": "dialogs",
|
|
342
332
|
"category": "ui",
|
|
343
|
-
"size":
|
|
333
|
+
"size": 7.7,
|
|
344
334
|
"files": [
|
|
345
335
|
"ui/prompts.js"
|
|
346
336
|
],
|
|
@@ -396,7 +386,7 @@
|
|
|
396
386
|
"the-modal": {
|
|
397
387
|
"name": "the-modal",
|
|
398
388
|
"category": "ui",
|
|
399
|
-
"size":
|
|
389
|
+
"size": 21.8,
|
|
400
390
|
"files": [
|
|
401
391
|
"ui/theModal.js"
|
|
402
392
|
],
|
|
@@ -512,7 +502,7 @@
|
|
|
512
502
|
"all-js": {
|
|
513
503
|
"name": "all-js",
|
|
514
504
|
"category": "dom-utilities",
|
|
515
|
-
"size": 14,
|
|
505
|
+
"size": 14.4,
|
|
516
506
|
"files": [
|
|
517
507
|
"dom-utilities/All.js"
|
|
518
508
|
],
|
package/{ui → src/ui}/prompts.js
RENAMED
|
@@ -3,7 +3,7 @@ import onDomReady from "../dom-utilities/onDomReady.js";
|
|
|
3
3
|
import toast from "./toast.js";
|
|
4
4
|
import copyToClipboard from "../string-utilities/copy-to-clipboard.js";
|
|
5
5
|
|
|
6
|
-
const CLOSE_BUTTON_SVG = `<svg
|
|
6
|
+
const CLOSE_BUTTON_SVG = `<svg viewBox="0 0 134 134" fill="none" xmlns="http://www.w3.org/2000/svg"><path class="micromodal__close-bg" d="M132 132.5 1 1.5h131v131Z" /><path class="micromodal__close-x" fill-rule="evenodd" clip-rule="evenodd" d="M0 0h3v1.5h1.5V3H6v1.5h1.5V6H9v1.5h1.5V9H12v1.5h1.5V12H15v1.5h1.5V15H18v1.5h1.5V18H21v1.5h1.5V21H24v1.5h1.5V24H27v1.5h1.5V27H30v1.5h1.5V30H33v1.5h1.5V33H36v1.5h1.5V36H39v1.5h1.5V39H42v1.5h1.5V42H45v1.5h1.5V45H48v1.5h1.5V48H51v1.5h1.5V51H54v1.5h1.5V54H57v1.5h1.5V57H60v1.5h1.5V60H63v1.5h1.5V63H66v1.5h1.5V66H69v1.5h1.5V69H72v1.5h1.5V72H75v1.5h1.5V75H78v1.5h1.5V78H81v1.5h1.5V81H84v1.5h1.5V84H87v1.5h1.5V87H90v1.5h1.5V90H93v1.5h1.5V93H96v1.5h1.5V96H99v1.5h1.5V99h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v3h-3V132H129v-1.5h-1.5V129H126v-1.5h-1.5V126H123v-1.5h-1.5V123H120v-1.5h-1.5V120H117v-1.5h-1.5V117H114v-1.5h-1.5V114H111v-1.5h-1.5V111H108v-1.5h-1.5V108H105v-1.5h-1.5V105H102v-1.5h-1.5V102H99v-1.5h-1.5V99H96v-1.5h-1.5V96H93v-1.5h-1.5V93H90v-1.5h-1.5V90H87v-1.5h-1.5V87H84v-1.5h-1.5V84H81v-1.5h-1.5V81H78v-1.5h-1.5V78H75v-1.5h-1.5V75H72v-1.5h-1.5V72H69v-1.5h-1.5V69H66v-1.5h-1.5V66H63v-1.5h-1.5V63H60v-1.5h-1.5V60H57v-1.5h-1.5V57H54v-1.5h-1.5V54H51v-1.5h-1.5V51H48v-1.5h-1.5V48H45v-1.5h-1.5V45H42v-1.5h-1.5V42H39v-1.5h-1.5V39H36v-1.5h-1.5V36H33v-1.5h-1.5V33H30v-1.5h-1.5V30H27v-1.5h-1.5V27H24v-1.5h-1.5V24H21v-1.5h-1.5V21H18v-1.5h-1.5V18H15v-1.5h-1.5V15H12v-1.5h-1.5V12H9v-1.5H7.5V9H6V7.5H4.5V6H3V4.5H1.5V3H0V0ZM108.8 22h5.2v5.1h-2.6v2.6H109v2.6h-2.6v2.6h-2.6v2.5h-2.6v5.2h2.6V45h2.6v2.6h2.6v2.6h2.5v2.6h2.6V58h-5.1v-2.6h-2.6V53h-2.6v-2.6h-2.6v-2.6h-2.5v-2.6h-5.2v2.6H91v2.6h-2.6v2.6h-2.6v2.5h-2.6V58H78v-5.1h2.6v-2.6H83v-2.6h2.6v-2.6h2.6v-2.5h2.6v-5.2h-2.6V35h-2.6v-2.6h-2.6v-2.6h-2.5v-2.6H78V22h5.2v2.6h2.5V27h2.6v2.6h2.6v2.6h2.5v2.6h5.2v-2.6h2.5v-2.6h2.6v-2.6h2.6v-2.5h2.5V22Z" /></svg>`;
|
|
7
7
|
const CONFIRM_BUTTON_SVG = `<div style="width: 28px;"><svg viewBox="0 0 60 33" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M34.5 0.75H43.5V5.25H49V9.75H54.5V14.25H60V18.75H54.5V23.25H49V27.75H43.5V32.25H34.5V27.75H40V23.25H45.5V18.75H0V14.25H45.5V9.75H40V5.25H34.5V0.75Z" fill="white"/></svg></div>`;
|
|
8
8
|
|
|
9
9
|
function createModal(promptText, yesCallback, extraContent = "", includeInput = false, defaultValue = "") {
|
|
@@ -70,11 +70,11 @@ export function consent(promptText, yesCallback, extraContent = "") {
|
|
|
70
70
|
*/
|
|
71
71
|
export function tell(promptText, ...content) {
|
|
72
72
|
const contentHtml = content.length > 0
|
|
73
|
-
? content.map(c => `<div class="
|
|
73
|
+
? content.map(c => `<div class="micromodal__tell-content">${c}</div>`).join("")
|
|
74
74
|
: "";
|
|
75
75
|
|
|
76
|
-
themodal.html = `<div class="
|
|
77
|
-
<div class="
|
|
76
|
+
themodal.html = `<div class="micromodal__tell">
|
|
77
|
+
<div class="micromodal__tell-title">${promptText}</div>
|
|
78
78
|
${contentHtml}
|
|
79
79
|
</div>`;
|
|
80
80
|
themodal.closeHtml = CLOSE_BUTTON_SVG;
|
|
@@ -98,27 +98,22 @@ export function tell(promptText, ...content) {
|
|
|
98
98
|
|
|
99
99
|
/**
|
|
100
100
|
* Display a modal with a code snippet and copy functionality
|
|
101
|
-
*
|
|
101
|
+
* @param {string} title - The modal heading
|
|
102
|
+
* @param {string} content - The code to display
|
|
103
|
+
* @param {string} extraContent - Optional warning/info text below the copy button
|
|
102
104
|
*/
|
|
103
|
-
export function snippet(title, content,
|
|
104
|
-
const {
|
|
105
|
-
extraContent = 'Save this, it won\'t be shown again. Expires in 1 year.'
|
|
106
|
-
} = options;
|
|
105
|
+
export function snippet(title, content, extraContent = '') {
|
|
107
106
|
|
|
108
107
|
// Create the modal content with copy button
|
|
109
108
|
const modalContent = `
|
|
110
|
-
<div class="
|
|
111
|
-
<
|
|
112
|
-
<pre class="text-white font-mono text-sm whitespace-nowrap">${content}</pre>
|
|
113
|
-
</div>
|
|
109
|
+
<div class="snippet-code-block">
|
|
110
|
+
<pre>${content}</pre>
|
|
114
111
|
</div>
|
|
115
112
|
|
|
116
|
-
<button type="button" class="
|
|
117
|
-
<span class="whitespace-nowrap select-none inline-block group-active:translate-x-[1.5px] group-active:translate-y-[1.5px]">copy</span>
|
|
118
|
-
</button>
|
|
113
|
+
<button type="button" class="micromodal__secondary-btn copy-snippet-btn" style="margin-bottom: 14px;">copy</button>
|
|
119
114
|
|
|
120
115
|
${extraContent ? `
|
|
121
|
-
<div class="
|
|
116
|
+
<div class="snippet-warning">
|
|
122
117
|
${extraContent}
|
|
123
118
|
</div>
|
|
124
119
|
` : ''}
|
|
@@ -131,7 +126,7 @@ export function snippet(title, content, options = {}) {
|
|
|
131
126
|
</div>`;
|
|
132
127
|
|
|
133
128
|
themodal.closeHtml = CLOSE_BUTTON_SVG;
|
|
134
|
-
themodal.yes =
|
|
129
|
+
themodal.yes = '';
|
|
135
130
|
|
|
136
131
|
const promise = new Promise((resolve) => {
|
|
137
132
|
// Local copy function
|
|
@@ -429,6 +429,7 @@ const modalCss = `<style class="micromodal-css">
|
|
|
429
429
|
align-items: center;
|
|
430
430
|
width: 100%;
|
|
431
431
|
height: 39px;
|
|
432
|
+
line-height: 0;
|
|
432
433
|
border: 3px solid;
|
|
433
434
|
border-top-color: #94BA6F;
|
|
434
435
|
border-left-color: #94BA6F;
|
|
@@ -449,6 +450,66 @@ const modalCss = `<style class="micromodal-css">
|
|
|
449
450
|
border-right-color: #94BA6F;
|
|
450
451
|
}
|
|
451
452
|
|
|
453
|
+
.micromodal button.micromodal__secondary-btn {
|
|
454
|
+
display: flex;
|
|
455
|
+
justify-content: center;
|
|
456
|
+
align-items: center;
|
|
457
|
+
width: 100%;
|
|
458
|
+
height: 39px;
|
|
459
|
+
line-height: 0;
|
|
460
|
+
border: 3px solid;
|
|
461
|
+
border-top-color: #474C65;
|
|
462
|
+
border-left-color: #474C65;
|
|
463
|
+
border-bottom-color: #131725;
|
|
464
|
+
border-right-color: #131725;
|
|
465
|
+
background-color: #1D1F2F;
|
|
466
|
+
color: #E5E7EB;
|
|
467
|
+
font-family: inherit;
|
|
468
|
+
font-size: 16px;
|
|
469
|
+
font-weight: bold;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
.micromodal button.micromodal__secondary-btn:focus,
|
|
473
|
+
.micromodal button.micromodal__secondary-btn:hover {
|
|
474
|
+
background-color: #232639;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
.micromodal button.micromodal__secondary-btn:active {
|
|
478
|
+
border-top-color: #131725;
|
|
479
|
+
border-left-color: #131725;
|
|
480
|
+
border-bottom-color: #474C65;
|
|
481
|
+
border-right-color: #474C65;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
.micromodal:has(.snippet-code-block) .micromodal__content {
|
|
485
|
+
margin-bottom: 0;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
.micromodal .snippet-code-block {
|
|
489
|
+
background-color: #292E54;
|
|
490
|
+
padding: 1rem;
|
|
491
|
+
margin-bottom: 14px;
|
|
492
|
+
max-width: 420px;
|
|
493
|
+
overflow-x: auto;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
.micromodal .snippet-code-block pre {
|
|
497
|
+
color: white;
|
|
498
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
499
|
+
font-size: 0.875rem;
|
|
500
|
+
white-space: nowrap;
|
|
501
|
+
margin: 0;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
.micromodal .snippet-warning {
|
|
505
|
+
padding: 0.75rem;
|
|
506
|
+
border: 2px solid #989742;
|
|
507
|
+
background-color: #1E1E11;
|
|
508
|
+
font-size: 0.875rem;
|
|
509
|
+
color: #FBF7B7;
|
|
510
|
+
max-width: 420px;
|
|
511
|
+
}
|
|
512
|
+
|
|
452
513
|
.micromodal button.micromodal__close {
|
|
453
514
|
clip-path: polygon(0% 4%, 0% 0%, 100% 0%, 100% 100%, 94% 100%);
|
|
454
515
|
position: absolute;
|
|
@@ -456,6 +517,46 @@ const modalCss = `<style class="micromodal-css">
|
|
|
456
517
|
right: -1px;
|
|
457
518
|
width: 68px;
|
|
458
519
|
}
|
|
520
|
+
|
|
521
|
+
.micromodal .micromodal__close-bg {
|
|
522
|
+
fill: #1D2032;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
.micromodal .micromodal__close:hover .micromodal__close-bg {
|
|
526
|
+
fill: #212543;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
.micromodal .micromodal__close-x {
|
|
530
|
+
fill: #fff;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
.micromodal .micromodal__tell {
|
|
534
|
+
max-width: 440px;
|
|
535
|
+
margin-bottom: 28px;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
.micromodal .micromodal__tell > * + * {
|
|
539
|
+
margin-top: 20px;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
.micromodal .micromodal__tell-title {
|
|
543
|
+
font-size: 20px;
|
|
544
|
+
font-weight: bold;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
.micromodal .micromodal__tell-content {
|
|
548
|
+
font-size: 16px;
|
|
549
|
+
font-weight: normal;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
@media (min-width: 640px) {
|
|
553
|
+
.micromodal .micromodal__tell-title {
|
|
554
|
+
font-size: 22px;
|
|
555
|
+
}
|
|
556
|
+
.micromodal .micromodal__tell-content {
|
|
557
|
+
font-size: 18px;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
459
560
|
</style>`;
|
|
460
561
|
|
|
461
562
|
const modalHtml = `<div class="micromodal" id="micromodal" aria-hidden="true">
|
package/{ui → src/ui}/toast.js
RENAMED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// a nice, simple alert
|
|
2
2
|
// ❗️ don't use too much text!
|
|
3
3
|
|
|
4
|
-
// Default modern icons
|
|
4
|
+
// Default modern icons (normalized to 48x48 viewBox)
|
|
5
5
|
const defaultIcons = {
|
|
6
|
-
success: `<svg viewBox="0 0 48
|
|
7
|
-
error: `<svg viewBox="0 0
|
|
6
|
+
success: `<svg viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M13.9404 23.9475L21.9099 31.224L35.1906 15.9045M3 4.5H44.9804V44.309H3V4.5Z" stroke="#33D131" stroke-width="4.3"/></svg>`,
|
|
7
|
+
error: `<svg viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M32.7383 14.4045L14 33.1429M32.7451 33.1429L14.0068 14.4046M3.01 4H44.99V43.809H3.01V4Z" stroke="#FF4450" stroke-width="4"/></svg>`
|
|
8
8
|
};
|
|
9
9
|
|
|
10
10
|
// Hyperclay icons
|
|
@@ -100,6 +100,7 @@ const modernStyles = `
|
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
[data-toast-theme="modern"] .toast-icon svg {
|
|
103
|
+
display: block;
|
|
103
104
|
width: 22px;
|
|
104
105
|
height: 22px;
|
|
105
106
|
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { isEditMode, isOwner } from "./isAdminOfCurrentResource.js";
|
|
2
|
-
import onDomReady from "../dom-utilities/onDomReady.js";
|
|
3
|
-
import {beforeSave} from "./savePage.js";
|
|
4
|
-
|
|
5
|
-
export function disableContentEditableBeforeSave () {
|
|
6
|
-
beforeSave(docElem => {
|
|
7
|
-
docElem.querySelectorAll('[edit-mode-contenteditable]').forEach(resource => {
|
|
8
|
-
const originalValue = resource.getAttribute("contenteditable");
|
|
9
|
-
resource.setAttribute("inert-contenteditable", originalValue);
|
|
10
|
-
resource.removeAttribute("contenteditable");
|
|
11
|
-
});
|
|
12
|
-
});
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function enableContentEditableForAdminOnPageLoad () {
|
|
16
|
-
if (!isEditMode) return;
|
|
17
|
-
|
|
18
|
-
onDomReady(() => {
|
|
19
|
-
document.querySelectorAll('[edit-mode-contenteditable]').forEach(resource => {
|
|
20
|
-
let originalValue = resource.getAttribute("inert-contenteditable");
|
|
21
|
-
|
|
22
|
-
if (!["false", "plaintext-only"].includes(originalValue)) {
|
|
23
|
-
originalValue = "true";
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
resource.setAttribute("contenteditable", originalValue);
|
|
27
|
-
resource.removeAttribute("inert-contenteditable");
|
|
28
|
-
});
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Auto-initialize
|
|
33
|
-
export function init() {
|
|
34
|
-
disableContentEditableBeforeSave();
|
|
35
|
-
enableContentEditableForAdminOnPageLoad();
|
|
36
|
-
}
|
package/core/adminOnClick.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { isEditMode, isOwner } from "./isAdminOfCurrentResource.js";
|
|
2
|
-
import onDomReady from "../dom-utilities/onDomReady.js";
|
|
3
|
-
import {beforeSave} from "./savePage.js";
|
|
4
|
-
|
|
5
|
-
export function disableOnClickBeforeSave () {
|
|
6
|
-
beforeSave(docElem => {
|
|
7
|
-
docElem.querySelectorAll('[edit-mode-onclick]').forEach(resource => {
|
|
8
|
-
const originalValue = resource.getAttribute("onclick");
|
|
9
|
-
resource.setAttribute("inert-onclick", originalValue);
|
|
10
|
-
resource.removeAttribute("onclick");
|
|
11
|
-
});
|
|
12
|
-
});
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function enableOnClickForAdminOnPageLoad () {
|
|
16
|
-
if (!isEditMode) return;
|
|
17
|
-
|
|
18
|
-
onDomReady(() => {
|
|
19
|
-
document.querySelectorAll('[edit-mode-onclick]').forEach(resource => {
|
|
20
|
-
const originalValue = resource.getAttribute("inert-onclick");
|
|
21
|
-
resource.setAttribute("onclick", originalValue);
|
|
22
|
-
resource.removeAttribute("inert-onclick");
|
|
23
|
-
});
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Auto-initialize
|
|
28
|
-
export function init() {
|
|
29
|
-
disableOnClickBeforeSave();
|
|
30
|
-
enableOnClickForAdminOnPageLoad();
|
|
31
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import Mutation from "../utilities/mutation.js";
|
|
2
|
-
|
|
3
|
-
function init() {
|
|
4
|
-
const executePageMutation = async element => {
|
|
5
|
-
try {
|
|
6
|
-
const code = element.getAttribute('onpagemutation');
|
|
7
|
-
const asyncFn = new Function(`return (async function() { ${code} })`)();
|
|
8
|
-
await asyncFn.call(element);
|
|
9
|
-
} catch (error) {
|
|
10
|
-
console.error('Error in onpagemutation execution:', error);
|
|
11
|
-
}
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
Mutation.onAnyChange({
|
|
15
|
-
debounce: 200,
|
|
16
|
-
omitChangeDetails: true
|
|
17
|
-
}, () => document.querySelectorAll('[onpagemutation]').forEach(executePageMutation));
|
|
18
|
-
}
|
|
19
|
-
export { init };
|
|
20
|
-
export default init;
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|