@panphora/hyper-cms 0.4.2 → 0.5.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.
@@ -0,0 +1,197 @@
1
+ /* VENDORED from mirk-ui-kit/mirk.js — DO NOT EDIT. Regenerate: npm run build:theme */
2
+ if (typeof window !== "undefined" && typeof document !== "undefined") {
3
+ /*
4
+ * mirk.js — the delegated runtime for the mirk UI kit, v2.
5
+ * https://github.com/davidmiranda/mirk-ui-kit
6
+ *
7
+ * One listener per interaction on `document`. No init(), no MutationObserver:
8
+ * every current AND future element is handled, so injected / re-rendered /
9
+ * saved-and-reopened markup just works. Idempotent; safe to include twice.
10
+ *
11
+ * The markup carries the initial state — chips are real DOM, the slider fill is
12
+ * an inline --mirk-value, native inputs hold their own value — so the page is
13
+ * correct before this script runs. JS only enhances the transitions.
14
+ */
15
+ (function () {
16
+ if (window.__mirk) return;
17
+ window.__mirk = true;
18
+
19
+ // Crisp × for remove affordances — a font glyph renders at a different weight
20
+ // and baseline in every typeface, so draw it as a tiny square-capped vector.
21
+ const X_SVG = '<svg viewBox="0 0 16 16" aria-hidden="true"><path d="M4 4 12 12M12 4 4 12" stroke="currentColor" stroke-width="2" stroke-linecap="square" fill="none"/></svg>';
22
+
23
+ // Number stepper — bevel buttons drive the native input.
24
+ document.addEventListener("click", (e) => {
25
+ const step = e.target.closest(".mirk-number__step");
26
+ if (!step) return;
27
+ const input = step.closest(".mirk-number").querySelector("input[type=number]");
28
+ if (!input) return;
29
+ step.dataset.step === "up" ? input.stepUp() : input.stepDown();
30
+ input.dispatchEvent(new Event("change", { bubbles: true }));
31
+ });
32
+
33
+ // Slider — mirror the value into --mirk-value on the wrapper (fill width + nub
34
+ // left both read it). The markup ships an inline --mirk-value matching value=,
35
+ // so the fill is correct before this runs; this only handles dragging.
36
+ document.addEventListener("input", (e) => {
37
+ const input = e.target.closest(".mirk-slider__input");
38
+ if (!input) return;
39
+ input.closest(".mirk-slider").style.setProperty("--mirk-value", `${input.value}%`);
40
+ });
41
+
42
+ // File picker — show the chosen file as a link to it, with a × to clear. The kit
43
+ // has no upload, so the link is a local object URL; an app swaps in the uploaded
44
+ // URL. State lives in the input's files.
45
+ document.addEventListener("change", (e) => {
46
+ const input = e.target.closest(".mirk-file__input");
47
+ if (!input || !input.files.length) return;
48
+ const root = input.closest(".mirk-file");
49
+ const slot = root.querySelector(".mirk-file__name");
50
+ if (!slot) return;
51
+ const file = input.files[0];
52
+ const link = document.createElement("a");
53
+ link.className = "mirk-file__name";
54
+ link.dataset.filled = "";
55
+ link.href = URL.createObjectURL(file);
56
+ link.target = "_blank";
57
+ link.rel = "noopener";
58
+ link.textContent = file.name;
59
+ slot.replaceWith(link);
60
+ if (!root.querySelector(".mirk-file__remove")) {
61
+ const remove = document.createElement("button");
62
+ remove.type = "button";
63
+ remove.className = "mirk-file__remove";
64
+ remove.setAttribute("aria-label", "Remove file");
65
+ remove.innerHTML = X_SVG;
66
+ link.after(remove);
67
+ }
68
+ });
69
+
70
+ // Image input — FileReader thumbnail preview.
71
+ document.addEventListener("change", (e) => {
72
+ const input = e.target.closest(".mirk-image__input");
73
+ if (!input || !input.files.length) return;
74
+ const root = input.closest(".mirk-image");
75
+ const preview = root.querySelector(".mirk-image__preview");
76
+ if (!preview) return;
77
+ const placeholder = root.querySelector(".mirk-image__placeholder");
78
+ const reader = new FileReader();
79
+ reader.onload = (ev) => {
80
+ preview.src = ev.target.result;
81
+ preview.removeAttribute("hidden");
82
+ if (placeholder) placeholder.setAttribute("hidden", "");
83
+ // compact variant: swap the upload button for the thumbnail
84
+ root.querySelector(".mirk-image__thumb")?.removeAttribute("hidden");
85
+ root.querySelector(".mirk-image__upload")?.setAttribute("hidden", "");
86
+ };
87
+ reader.readAsDataURL(input.files[0]);
88
+ });
89
+
90
+ // File / image remove — clear the field and return to the empty state.
91
+ document.addEventListener("click", (e) => {
92
+ const fileRemove = e.target.closest(".mirk-file__remove");
93
+ if (fileRemove) {
94
+ const root = fileRemove.closest(".mirk-file");
95
+ const input = root?.querySelector(".mirk-file__input");
96
+ const link = root?.querySelector(".mirk-file__name");
97
+ if (input) input.value = "";
98
+ if (link) {
99
+ const span = document.createElement("span");
100
+ span.className = "mirk-file__name";
101
+ span.textContent = "No file chosen";
102
+ link.replaceWith(span);
103
+ }
104
+ fileRemove.remove();
105
+ return;
106
+ }
107
+ const imageRemove = e.target.closest(".mirk-image__remove");
108
+ if (imageRemove) {
109
+ const root = imageRemove.closest(".mirk-image");
110
+ const input = root?.querySelector(".mirk-image__input");
111
+ const preview = root?.querySelector(".mirk-image__preview");
112
+ if (input) input.value = "";
113
+ if (preview) { preview.removeAttribute("src"); preview.setAttribute("hidden", ""); }
114
+ root?.querySelector(".mirk-image__thumb")?.setAttribute("hidden", "");
115
+ root?.querySelector(".mirk-image__upload")?.removeAttribute("hidden");
116
+ }
117
+ });
118
+
119
+ // Tags — Enter / comma adds a real chip, × or Backspace-on-empty removes one.
120
+ // Chips are DOM elements (and carry a hidden input), so outerHTML keeps them.
121
+ function makeChip(value, round) {
122
+ const text = document.createElement("span");
123
+ text.textContent = value;
124
+ const hidden = document.createElement("input");
125
+ hidden.type = "hidden"; hidden.name = "tags[]"; hidden.value = value;
126
+ const remove = document.createElement("button");
127
+ remove.type = "button"; remove.className = "mirk-tags__remove"; remove.textContent = "×";
128
+
129
+ const chip = document.createElement("span");
130
+ chip.className = "mirk-tags__chip";
131
+ if (round) {
132
+ const inner = document.createElement("span");
133
+ inner.className = "mirk-tags__chip-inner";
134
+ inner.append(text, hidden, remove);
135
+ chip.append(inner);
136
+ } else {
137
+ chip.append(text, hidden, remove);
138
+ }
139
+ return chip;
140
+ }
141
+
142
+ document.addEventListener("keydown", (e) => {
143
+ const input = e.target.closest(".mirk-tags__input");
144
+ if (!input) return;
145
+ const tags = input.closest(".mirk-tags");
146
+ if (e.key === "Enter" || e.key === ",") {
147
+ const value = input.value.trim();
148
+ if (!value) return;
149
+ e.preventDefault();
150
+ input.before(makeChip(value, tags.classList.contains("mirk-tags--round")));
151
+ input.value = "";
152
+ } else if (e.key === "Backspace" && !input.value) {
153
+ const chips = tags.querySelectorAll(".mirk-tags__chip");
154
+ chips[chips.length - 1]?.remove();
155
+ }
156
+ });
157
+
158
+ document.addEventListener("click", (e) => {
159
+ const remove = e.target.closest(".mirk-tags__remove");
160
+ if (remove) { remove.closest(".mirk-tags__chip").remove(); return; }
161
+ const tags = e.target.closest(".mirk-tags");
162
+ if (tags && e.target === tags) tags.querySelector(".mirk-tags__input")?.focus();
163
+ });
164
+
165
+ // Copy button — delegated so dynamically-added buttons work too.
166
+ // [data-copy] copies the component's innerHTML (clean markup).
167
+ // [data-copy="text"] copies textContent (for <pre>/<code> source blocks
168
+ // whose displayed HTML must paste as raw characters).
169
+ document.addEventListener("click", (e) => {
170
+ const btn = e.target.closest("[data-copy-btn]");
171
+ if (!btn) return;
172
+ const copyable = btn.closest("[data-copy]");
173
+ if (!copyable) return;
174
+
175
+ const clone = copyable.cloneNode(true);
176
+ clone.querySelectorAll("[data-copy-btn]").forEach((b) => b.remove());
177
+
178
+ const mode = copyable.getAttribute("data-copy");
179
+ const payload = mode === "text"
180
+ ? clone.textContent.replace(/^\s+|\s+$/g, "")
181
+ : clone.innerHTML
182
+ .replace(/\s+data-copy(="[^"]*")?/g, "")
183
+ .replace(/^\s*\n/gm, "")
184
+ .trim();
185
+
186
+ navigator.clipboard.writeText(payload).then(() => {
187
+ const original = btn.textContent;
188
+ btn.textContent = "copied";
189
+ btn.dataset.copied = "";
190
+ setTimeout(() => { btn.textContent = original; delete btn.dataset.copied; }, 1200);
191
+ }).catch(() => {
192
+ btn.textContent = "error";
193
+ setTimeout(() => { btn.textContent = "copy"; }, 1200);
194
+ });
195
+ });
196
+ })();
197
+ }