@panphora/hyper-cms 0.5.0 → 0.6.1
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 +124 -6
- package/dist/hypercms.min.js +607 -50
- package/package.json +1 -1
- package/src/enhance.js +97 -0
- package/src/events.js +89 -9
- package/src/form-builder.js +143 -20
- package/src/form-rules.js +20 -6
- package/src/hypercms.js +31 -4
- package/src/refresh.js +10 -3
- package/src/shell.js +13 -2
- package/src/templates.js +310 -30
- package/src/theme/pixel-quiet.overrides.css +35 -1
- package/src/theme.generated.css +509 -35
- package/src/toggle.js +88 -0
- package/src/vendor/mirk.vendor.js +17 -2
package/src/toggle.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// Floating bottom-right button that toggles the CMS open/closed, so a page
|
|
2
|
+
// owner can reopen the CMS after closing it (the shell's X is the only other
|
|
3
|
+
// affordance, and nothing reopens it). Injected at page load only when the
|
|
4
|
+
// visitor is in edit mode AND the page actually has cms rules, so regular
|
|
5
|
+
// visitors and rules-less pages (hypercms rides in broad presets) never see it.
|
|
6
|
+
// Runtime-only chrome: no-save + snapshot-remove keep it out of every save and
|
|
7
|
+
// snapshot (a static no-save element written into the file would be stripped
|
|
8
|
+
// from disk on the first save), save-ignore keeps it out of autosave/undo,
|
|
9
|
+
// mirroring the shell's own attributes.
|
|
10
|
+
|
|
11
|
+
const TOGGLE_ID = 'hcms-toggle'
|
|
12
|
+
const STYLE_ID = 'hcms-toggle-style'
|
|
13
|
+
|
|
14
|
+
const STYLE = `
|
|
15
|
+
#hcms-toggle {
|
|
16
|
+
position: fixed; right: 16px; bottom: 16px; z-index: 2147482900;
|
|
17
|
+
display: inline-flex; align-items: center; gap: 7px; padding: 9px 14px;
|
|
18
|
+
border: 1px solid #3a3f58; background: #14161f; color: #f2f3f7;
|
|
19
|
+
font: 500 13px/1 system-ui, sans-serif; border-radius: 999px; cursor: pointer;
|
|
20
|
+
box-shadow: 0 10px 28px -12px rgba(0, 0, 0, .55);
|
|
21
|
+
}
|
|
22
|
+
#hcms-toggle:hover { background: #1d2030; }
|
|
23
|
+
#hcms-toggle .hcms-toggle__close { display: none; }
|
|
24
|
+
body.hcms-open #hcms-toggle .hcms-toggle__open { display: none; }
|
|
25
|
+
body.hcms-open #hcms-toggle .hcms-toggle__close { display: inline; }
|
|
26
|
+
body.hcms-open:not(.hcms-overlay):not(.hcms-side-left) #hcms-toggle { right: calc(380px + 16px); }
|
|
27
|
+
body.hcms-open.hcms-overlay #hcms-toggle { display: none; }
|
|
28
|
+
`
|
|
29
|
+
|
|
30
|
+
// Mirrors hyperclayjs core/isAdminOfCurrentResource.js: an explicit
|
|
31
|
+
// ?editmode=true|false param wins, then the window.__hyperclayEditMode global
|
|
32
|
+
// (standalone opt-in), then the platform's isAdminOfCurrentResource cookie.
|
|
33
|
+
// Replicated here rather than read off window.hyperclay because the edit-mode
|
|
34
|
+
// module isn't part of the cms preset, so the global isn't reliably present.
|
|
35
|
+
export function detectEditMode({ search = '', cookie = '', forced = null } = {}) {
|
|
36
|
+
const str = typeof search === 'string' ? search : ''
|
|
37
|
+
const qIndex = str.indexOf('?')
|
|
38
|
+
const query = qIndex === -1 ? str : str.slice(qIndex + 1)
|
|
39
|
+
const param = new URLSearchParams(query).get('editmode')
|
|
40
|
+
if (param) return param === 'true'
|
|
41
|
+
if (forced != null) return Boolean(forced)
|
|
42
|
+
return /(?:^|;\s*)isAdminOfCurrentResource=[^;]/.test(cookie)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function injectToggle({ open, close, isOpen }, doc = document) {
|
|
46
|
+
const existing = doc.getElementById(TOGGLE_ID)
|
|
47
|
+
if (existing) return existing
|
|
48
|
+
if (!doc.getElementById(STYLE_ID)) {
|
|
49
|
+
const style = doc.createElement('style')
|
|
50
|
+
style.id = STYLE_ID
|
|
51
|
+
style.setAttribute('snapshot-remove', '')
|
|
52
|
+
style.textContent = STYLE
|
|
53
|
+
doc.head.appendChild(style)
|
|
54
|
+
}
|
|
55
|
+
const btn = doc.createElement('button')
|
|
56
|
+
btn.type = 'button'
|
|
57
|
+
btn.id = TOGGLE_ID
|
|
58
|
+
btn.setAttribute('no-save', '')
|
|
59
|
+
btn.setAttribute('snapshot-remove', '')
|
|
60
|
+
btn.setAttribute('save-ignore', '')
|
|
61
|
+
btn.setAttribute('aria-label', 'Toggle content editor')
|
|
62
|
+
btn.innerHTML = '<span class="hcms-toggle__open">Edit content</span><span class="hcms-toggle__close">Close editor</span>'
|
|
63
|
+
btn.addEventListener('click', async () => {
|
|
64
|
+
try {
|
|
65
|
+
if (isOpen()) close()
|
|
66
|
+
else await open()
|
|
67
|
+
} catch (err) {
|
|
68
|
+
console.warn('hypercms: toggle failed to open the CMS', err)
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
doc.body.appendChild(btn)
|
|
72
|
+
return btn
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Inject at page load when in edit mode and the page has cms rules. DOM-ready
|
|
76
|
+
// deferral matters when the module evaluates while the document is still
|
|
77
|
+
// parsing (the rules tag or <body> may not exist yet).
|
|
78
|
+
export function maybeInjectToggle(api) {
|
|
79
|
+
if (typeof window === 'undefined' || typeof document === 'undefined') return
|
|
80
|
+
const forced = window.__hyperclayEditMode != null ? window.__hyperclayEditMode : null
|
|
81
|
+
if (!detectEditMode({ search: window.location.search, cookie: document.cookie, forced })) return
|
|
82
|
+
const run = () => { if (document.body && api.hasRules(document)) injectToggle(api) }
|
|
83
|
+
if (document.readyState === 'loading') {
|
|
84
|
+
document.addEventListener('DOMContentLoaded', run, { once: true })
|
|
85
|
+
} else {
|
|
86
|
+
run()
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
/* VENDORED from mirk-
|
|
1
|
+
/* VENDORED from mirk-interface/mirk.js — DO NOT EDIT. Regenerate: npm run build:theme */
|
|
2
2
|
if (typeof window !== "undefined" && typeof document !== "undefined") {
|
|
3
3
|
/*
|
|
4
4
|
* mirk.js — the delegated runtime for the mirk UI kit, v2.
|
|
5
|
-
* https://github.com/
|
|
5
|
+
* https://github.com/panphora/mirk-interface
|
|
6
6
|
*
|
|
7
7
|
* One listener per interaction on `document`. No init(), no MutationObserver:
|
|
8
8
|
* every current AND future element is handled, so injected / re-rendered /
|
|
@@ -162,6 +162,21 @@ if (typeof window !== "undefined" && typeof document !== "undefined") {
|
|
|
162
162
|
if (tags && e.target === tags) tags.querySelector(".mirk-tags__input")?.focus();
|
|
163
163
|
});
|
|
164
164
|
|
|
165
|
+
// Collapsible chip — open the panel, collapse back to the chip, toggle the
|
|
166
|
+
// before/after field table. State lives in classes on the markup, so a
|
|
167
|
+
// saved-and-reopened chip keeps whatever it was; this only flips them live.
|
|
168
|
+
document.addEventListener("click", (e) => {
|
|
169
|
+
const t = e.target.closest("[data-mirk-chip]");
|
|
170
|
+
if (!t) return;
|
|
171
|
+
const action = t.getAttribute("data-mirk-chip");
|
|
172
|
+
if (action === "open") t.closest(".mirk-chip")?.classList.add("mirk-chip--open");
|
|
173
|
+
else if (action === "collapse") t.closest(".mirk-chip")?.classList.remove("mirk-chip--open");
|
|
174
|
+
else if (action === "changes") {
|
|
175
|
+
const on = t.closest(".mirk-chip__panel")?.classList.toggle("is-changes");
|
|
176
|
+
t.textContent = on ? "(hide changes)" : "(view changes)";
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
|
|
165
180
|
// Copy button — delegated so dynamically-added buttons work too.
|
|
166
181
|
// [data-copy] copies the component's innerHTML (clean markup).
|
|
167
182
|
// [data-copy="text"] copies textContent (for <pre>/<code> source blocks
|