forked from
quillmatiq.com/augment
Fork of Chiri for Astro for my blog
1<div class="tag-card">
2 <div class="tag-container">
3 <div id="tag-component" class="tag-component">
4 <button id="add-button" class="add-button">
5 <svg class="add-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16">
6 <path
7 d="M7.75 2a.75.75 0 0 1 .75.75V7h4.25a.75.75 0 0 1 0 1.5H8.5v4.25a.75.75 0 0 1-1.5 0V8.5H2.75a.75.75 0 0 1 0-1.5H7V2.75A.75.75 0 0 1 7.75 2Z"
8 ></path>
9 </svg>
10 </button>
11
12 <div id="input-state" class="input-state">
13 <input id="tag-input" type="text" placeholder="Tag Name" class="tag-input" />
14 <button id="confirm-button" class="confirm-button disabled">
15 <svg class="confirm-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16">
16 <path
17 d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0Z"
18 ></path>
19 </svg>
20 </button>
21 <button id="cancel-button" class="cancel-button">
22 <svg class="cancel-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16">
23 <path
24 d="M3.72 3.72a.75.75 0 0 1 1.06 0L8 6.94l3.22-3.22a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734L9.06 8l3.22 3.22a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L8 9.06l-3.22 3.22a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042L6.94 8 3.72 4.78a.75.75 0 0 1 0-1.06Z"
25 ></path>
26 </svg>
27 </button>
28 </div>
29
30 <div id="tag-display" class="tag-display">
31 <span id="tag-text" class="tag-text"></span>
32 <button id="delete-button" class="delete-button">
33 <svg class="delete-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16">
34 <path
35 d="M11 1.75V3h2.25a.75.75 0 0 1 0 1.5H2.75a.75.75 0 0 1 0-1.5H5V1.75C5 .784 5.784 0 6.75 0h2.5C10.216 0 11 .784 11 1.75ZM4.496 6.675l.66 6.6a.25.25 0 0 0 .249.225h5.19a.25.25 0 0 0 .249-.225l.66-6.6a.75.75 0 0 1 1.492.149l-.66 6.6A1.748 1.748 0 0 1 10.595 15h-5.19a1.75 1.75 0 0 1-1.741-1.575l-.66-6.6a.75.75 0 1 1 1.492-.15ZM6.5 1.75V3h3V1.75a.25.25 0 0 0-.25-.25h-2.5a.25.25 0 0 0-.25.25Z"
36 ></path>
37 </svg>
38 </button>
39 </div>
40 </div>
41 </div>
42</div>
43
44<script is:inline>
45 function setupTag() {
46 let currentState = 'add' // 'add', 'input', 'display'
47 let tagValue = localStorage.getItem('tagValue') || ''
48
49 const addButton = document.getElementById('add-button')
50 const inputState = document.getElementById('input-state')
51 const tagDisplay = document.getElementById('tag-display')
52 const tagInput = document.getElementById('tag-input')
53 const confirmButton = document.getElementById('confirm-button')
54 const cancelButton = document.getElementById('cancel-button')
55 const deleteButton = document.getElementById('delete-button')
56 const tagText = document.getElementById('tag-text')
57
58 if (tagValue) {
59 addButton.classList.add('hidden')
60 inputState.classList.remove('active')
61 tagDisplay.classList.add('active')
62 tagText.textContent = truncateText(tagValue)
63 currentState = 'display'
64 } else {
65 addButton.classList.remove('hidden')
66 inputState.classList.remove('active')
67 tagDisplay.classList.remove('active')
68 tagInput.value = ''
69 currentState = 'add'
70 }
71
72 // Switch to input state
73 function switchToInput() {
74 currentState = 'input'
75 addButton.classList.add('hidden')
76 setTimeout(() => {
77 inputState.classList.add('active')
78 tagInput.focus()
79 }, 100)
80 }
81
82 // Switch to display state
83 function switchToDisplay() {
84 currentState = 'display'
85 inputState.classList.remove('active')
86 setTimeout(() => {
87 tagDisplay.classList.add('active')
88 tagText.textContent = truncateText(tagValue)
89 // Store to localStorage
90 localStorage.setItem('tagValue', tagValue)
91 }, 300)
92 }
93
94 // Return to add state
95 function switchToAdd() {
96 if (currentState === 'display') {
97 tagDisplay.classList.remove('active')
98 } else if (currentState === 'input') {
99 inputState.classList.remove('active')
100 }
101 currentState = 'add'
102 setTimeout(() => {
103 addButton.classList.remove('hidden')
104 tagValue = ''
105 tagInput.value = ''
106 localStorage.removeItem('tagValue')
107 updateConfirmButton()
108 }, 250)
109 }
110
111 // Update confirm button state
112 function updateConfirmButton() {
113 if (tagInput.value.trim()) {
114 confirmButton.classList.remove('disabled')
115 } else {
116 confirmButton.classList.add('disabled')
117 }
118 }
119
120 // Truncate text to max 24 characters
121 function truncateText(text, maxLength = 24) {
122 if (text.length <= maxLength) {
123 return text
124 }
125 return text.substring(0, maxLength) + '...'
126 }
127
128 // Bind events
129 addButton.onclick = switchToInput
130
131 tagInput.oninput = function (e) {
132 const inputValue = e.target.value
133 if (inputValue.length > 24) {
134 e.target.value = inputValue.substring(0, 24)
135 tagValue = inputValue
136 } else {
137 tagValue = inputValue
138 }
139 updateConfirmButton()
140 }
141
142 tagInput.onkeydown = function (e) {
143 if (e.key === 'Enter' && tagValue.trim()) {
144 switchToDisplay()
145 } else if (e.key === 'Escape') {
146 switchToAdd()
147 }
148 }
149
150 confirmButton.onclick = function () {
151 if (tagValue.trim()) {
152 switchToDisplay()
153 }
154 }
155
156 cancelButton.onclick = switchToAdd
157 deleteButton.onclick = switchToAdd
158 }
159
160 setupTag()
161
162 document.addEventListener('astro:page-load', setupTag)
163</script>
164
165<style>
166 .tag-card {
167 border: 1px solid var(--border);
168 width: 100%;
169 height: 12rem;
170 display: flex;
171 align-items: center;
172 justify-content: center;
173 border-radius: 10px;
174 }
175
176 .tag-container {
177 display: flex;
178 justify-content: center;
179 align-items: center;
180 width: 100%;
181 padding: 0 2rem;
182 overflow: hidden;
183 }
184
185 .tag-component {
186 display: flex;
187 align-items: center;
188 justify-content: center;
189 gap: 0.5rem;
190 position: relative;
191 width: 100%;
192 min-height: 40px;
193 }
194
195 .add-button {
196 background-color: var(--text-primary);
197 color: var(--bg);
198 border: none;
199 border-radius: 50%;
200 width: 36px;
201 height: 36px;
202 cursor: pointer;
203 display: flex;
204 align-items: center;
205 justify-content: center;
206 transition:
207 all 0.3s ease,
208 visibility 0s;
209 position: absolute;
210 top: 50%;
211 left: 50%;
212 transform: translate(-50%, -50%) scale(1);
213 z-index: 1;
214 opacity: 1;
215 visibility: visible;
216 overflow: visible;
217 will-change: transform;
218 -webkit-transform: translate(-50%, -50%) scale(1);
219 box-sizing: border-box;
220 padding: 0;
221 margin: 0;
222 }
223
224 .add-button.hidden {
225 opacity: 0;
226 transform: translate(-50%, -50%) scale(0.8);
227 pointer-events: none;
228 visibility: hidden;
229 transition:
230 all 0.3s ease,
231 visibility 0s 0.2s;
232 }
233
234 .add-icon,
235 .confirm-icon,
236 .cancel-icon,
237 .delete-icon {
238 fill: currentColor;
239 width: 1rem;
240 height: 1rem;
241 position: relative;
242 display: block;
243 transform: translateZ(0);
244 }
245
246 .add-button:hover {
247 opacity: 0.8;
248 transform: translate(-50%, -50%) scale(1);
249 -webkit-transform: translate(-50%, -50%) scale(1);
250 }
251
252 .input-state {
253 display: flex;
254 align-items: center;
255 gap: 0.125rem;
256 background-color: var(--astro-code-background);
257 border: 1px solid var(--code-bg);
258 border-radius: 18px;
259 padding: 0.28125rem 0.375rem;
260 width: 40px;
261 max-width: 40px;
262 opacity: 0;
263 overflow: hidden;
264 transition: all 0.3s ease;
265 position: relative;
266 z-index: 2;
267 pointer-events: none;
268 }
269
270 .input-state.active {
271 width: 10rem;
272 max-width: 10rem;
273 opacity: 1;
274 pointer-events: all;
275 }
276
277 .tag-input {
278 background: transparent;
279 border: none;
280 outline: none;
281 color: var(--text-primary);
282 font-size: 0.9rem;
283 font-family: var(--mono);
284 min-width: 4rem;
285 padding: 0.25rem 0.5rem;
286 opacity: 0;
287 transform: translateX(-8px);
288 transition:
289 opacity 0.2s ease 0.15s,
290 transform 0.2s ease 0.15s;
291 }
292
293 .input-state.active .tag-input {
294 opacity: 1;
295 transform: translateX(0);
296 }
297
298 .tag-input::placeholder {
299 color: var(--text-tertiary);
300 }
301
302 .confirm-button,
303 .cancel-button {
304 background: none;
305 border: none;
306 border-radius: 50%;
307 width: 24px;
308 height: 24px;
309 cursor: pointer;
310 display: flex;
311 align-items: center;
312 justify-content: center;
313 font-size: 0.8rem;
314 opacity: 0;
315 transform: scale(0.8);
316 transition: all 0.2s ease;
317 position: relative;
318 min-width: 24px;
319 min-height: 24px;
320 }
321
322 .confirm-button::before,
323 .cancel-button::before {
324 content: '';
325 position: absolute;
326 top: 50%;
327 left: 50%;
328 transform: translate(-50%, -50%);
329 border-radius: 50%;
330 background-color: var(--code-bg);
331 opacity: 0;
332 transition: opacity 0.2s ease;
333 z-index: -1;
334 width: 28px;
335 height: 28px;
336 }
337
338 .confirm-button:hover::before,
339 .cancel-button:hover::before {
340 opacity: 1;
341 }
342
343 .input-state.active .confirm-button,
344 .input-state.active .cancel-button {
345 transform: scale(1);
346 }
347
348 .confirm-button {
349 color: var(--text-primary);
350 opacity: 0.6;
351 }
352
353 .confirm-button.disabled {
354 opacity: 0.3;
355 cursor: not-allowed;
356 }
357
358 .confirm-button:not(.disabled):hover {
359 opacity: 1;
360 }
361
362 .confirm-button.disabled:hover::before {
363 opacity: 0;
364 }
365
366 .cancel-button {
367 color: var(--text-primary);
368 opacity: 0.6;
369 }
370
371 .cancel-button:hover {
372 opacity: 1;
373 }
374
375 .tag-display {
376 display: flex;
377 align-items: center;
378 gap: 0.25rem;
379 background-color: var(--astro-code-background);
380 border: 1px solid var(--code-bg);
381 border-radius: 18px;
382 padding: 0.28125rem 0.325rem 0.28125rem 0.75rem;
383 position: absolute;
384 top: 50%;
385 left: 50%;
386 transform: translate(-50%, -50%);
387 z-index: 3;
388 opacity: 0;
389 pointer-events: none;
390 transition: all 0.25s ease-out;
391 will-change: transform, opacity;
392 }
393
394 .tag-display > * {
395 margin-right: 0.125rem;
396 }
397
398 .tag-display > *:last-child {
399 margin-right: 0;
400 }
401
402 .tag-display.active {
403 opacity: 1;
404 transform: translate(-50%, -50%);
405 pointer-events: all;
406 }
407
408 .tag-text {
409 color: var(--text-primary);
410 font-size: 0.9rem;
411 font-family: var(--mono);
412 }
413
414 .delete-button {
415 background: none;
416 border: none;
417 border-radius: 50%;
418 width: 24px;
419 height: 24px;
420 cursor: pointer;
421 display: flex;
422 align-items: center;
423 justify-content: center;
424 font-size: 0.8rem;
425 color: var(--text-primary);
426 opacity: 0.6;
427 transition: all 0.2s ease;
428 position: relative;
429 min-width: 24px;
430 min-height: 24px;
431 }
432
433 .delete-button::before {
434 content: '';
435 position: absolute;
436 top: 50%;
437 left: 50%;
438 transform: translate(-50%, -50%);
439 border-radius: 50%;
440 background-color: var(--code-bg);
441 opacity: 0;
442 transition: opacity 0.2s ease;
443 z-index: -1;
444 width: 28px;
445 height: 28px;
446 }
447
448 .delete-button:hover {
449 opacity: 1;
450 }
451
452 .delete-button:hover::before {
453 opacity: 1;
454 }
455</style>