最后活跃于 4 hours ago

jordan's Avatar jordan 修订了这个 Gist 4 hours ago. 转到此修订

1 file changed, 162 insertions

coffee.html(文件已创建)

@@ -0,0 +1,162 @@
1 + <!DOCTYPE html>
2 + <html
3 + editmode="false"
4 + pageowner="false"
5 + savestatus="saved"
6 + >
7 +
8 + <head>
9 + <title>Coffee Inventory</title>
10 + <meta charset="utf-8">
11 + <meta name="viewport" content="width=device-width, initial-scale=1">
12 + <script src="https://cdn.jsdelivr.net/npm/hyperclayjs@1.24.3/src/hyperclay.js?features=save-core,save-system,autosave,unsaved-warning,edit-mode-helpers,persist,snapshot,option-visibility,edit-mode,event-attrs,ajax-elements,sortable,movable,dom-helpers,input-helpers,onaftersave,save-freeze,dialogs,toast,the-modal,mutation,nearest,cookie,throttle,debounce,cache-bust,dom-ready,all-js,style-injection,form-data,hyper-morph,slugify,copy-to-clipboard,query-params,send-message,file-upload,live-sync,export-to-window,view-mode-excludes-edit-modules" type="module"></script>
13 + <style data-name="option-visibility" mutations-ignore=""></style>
14 + <style>
15 + body { max-width: 600px; margin: 0 auto; padding: 1rem; }
16 + .box fieldset { margin-bottom: 1rem; }
17 + .box label { display: block; margin-bottom: 0.5rem; font-size: 0.85rem; color: #555; }
18 + input[type="number"] { font-size: 1rem; width: 3rem; text-align: center; }
19 + </style>
20 + </head>
21 +
22 + <body style="">
23 +
24 + <h1>Coffee inventory</h1>
25 + <p>This is a small app to keep track of how many Cometeer coffee pods I have left per box.</p>
26 +
27 + <p save-ignore="">
28 + Total: <strong id="total">0 pods (0 boxes)</strong>
29 + </p>
30 +
31 + <hr>
32 +
33 + <div
34 + id="boxes-list"
35 + sortable=""
36 + >
37 +
38 + <!-- Hidden template — cloned by addBox() -->
39 + <div
40 + class="box"
41 + id="box-template"
42 + style="display: none;"
43 + save-ignore=""
44 + >
45 + <fieldset>
46 + <legend>
47 + <input
48 + type="text"
49 + class="box-name"
50 + placeholder="Coffee name"
51 + value=""
52 + oninput="this.setAttribute('value', this.value)"
53 + >
54 + </legend>
55 + <p><label>
56 + Pods remaining:
57 + <br>
58 + <button
59 + type="button"
60 + onclick="decrement(this)"
61 + >-</button>
62 + <input
63 + type="number"
64 + class="pod-count"
65 + value="8"
66 + min="0"
67 + max="8"
68 + oninput="this.setAttribute('value', this.value); updateTotal()"
69 + >
70 + <button
71 + type="button"
72 + onclick="increment(this)"
73 + >+</button>
74 + / 8
75 + </label></p>
76 + <p><button
77 + type="button"
78 + onclick="deleteBox(this)"
79 + >Delete</button></p>
80 + </fieldset>
81 + </div>
82 +
83 + </div>
84 +
85 + <button
86 + type="button"
87 + onclick="addBox()"
88 + >+ Add Box</button>
89 +
90 + <script>
91 + function increment(btn) {
92 + const input = btn.closest('fieldset').querySelector('.pod-count');
93 + const n = parseInt(input.value);
94 + if (n < 8) {
95 + input.value = n + 1;
96 + input.setAttribute('value', n + 1);
97 + updateTotal();
98 + }
99 + }
100 +
101 + function decrement(btn) {
102 + const input = btn.closest('fieldset').querySelector('.pod-count');
103 + const n = parseInt(input.value);
104 + if (n > 0) {
105 + input.value = n - 1;
106 + input.setAttribute('value', n - 1);
107 + updateTotal();
108 + }
109 + }
110 +
111 + function deleteBox(btn) {
112 + consent('Delete this box?', () => {
113 + btn.closest('.box').remove();
114 + updateTotal();
115 + });
116 + }
117 +
118 + function addBox() {
119 + const template = document.getElementById('box-template');
120 + const clone = template.cloneNode(true);
121 + clone.id = 'box-' + Date.now();
122 + clone.removeAttribute('save-ignore');
123 + clone.style.display = '';
124 + clone.querySelector('.box-name').value = '';
125 + clone.querySelector('.box-name').setAttribute('value', '');
126 + clone.querySelector('.pod-count').value = '8';
127 + clone.querySelector('.pod-count').setAttribute('value', '8');
128 + document.getElementById('boxes-list').appendChild(clone);
129 + updateTotal();
130 + }
131 +
132 + function updateTotal() {
133 + const inputs = document.querySelectorAll('.box:not(#box-template) .pod-count');
134 + const total = Array.from(inputs).reduce((sum, el) => sum + (parseInt(el.value) || 0), 0);
135 + document.getElementById('total').textContent = total + ' pods (' + inputs.length + ' boxes)';
136 + }
137 +
138 + document.addEventListener('DOMContentLoaded', updateTotal);
139 +
140 + // Fast save: 500ms after last input or structural change
141 + let saveTimer;
142 + function scheduleSave() {
143 + clearTimeout(saveTimer);
144 + saveTimer = setTimeout(() => window.hyperclay.savePage(), 500);
145 + }
146 +
147 + window.addEventListener('load', () => {
148 + document.addEventListener('input', e => {
149 + if (e.target.matches('.pod-count, .box-name')) {
150 + scheduleSave();
151 + }
152 + });
153 + new MutationObserver(scheduleSave).observe(
154 + document.getElementById('boxes-list'),
155 + { childList: true }
156 + );
157 + });
158 + </script>
159 +
160 + </body>
161 +
162 + </html>
上一页 下一页