最後活躍 5 hours ago

修訂 e9f783a99823b817d54fe5c4d25d3d5e9eab9604

coffee.html 原始檔案
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>