Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/trueberryless/the-thinking-hub. The central knowledge hub for 8 key Computer Science thinking modes. the-thinking-hub.felixs.dev
Something went wrong. Try again.
12 kB · 471 lines
Astro
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472<div class="tag-card"> <div class="tag-container"> <div id="tag-component" class="tag-component"> <button id="add-button" class="add-button"> <svg class="add-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16" > <path 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"></path> </svg> </button>
<div id="input-state" class="input-state"> <input id="tag-input" type="text" placeholder="Tag Name" class="tag-input" /> <button id="confirm-button" class="confirm-button disabled"> <svg class="confirm-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16" > <path 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"></path> </svg> </button> <button id="cancel-button" class="cancel-button"> <svg class="cancel-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16" > <path 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"></path> </svg> </button> </div>
<div id="tag-display" class="tag-display"> <span id="tag-text" class="tag-text"></span> <button id="delete-button" class="delete-button"> <svg class="delete-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16" > <path 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"></path> </svg> </button> </div> </div> </div></div>
<script is:inline> function setupTag() { let currentState = 'add' // 'add', 'input', 'display' let tagValue = localStorage.getItem('tagValue') || ''
const addButton = document.getElementById('add-button') const inputState = document.getElementById('input-state') const tagDisplay = document.getElementById('tag-display') const tagInput = document.getElementById('tag-input') const confirmButton = document.getElementById('confirm-button') const cancelButton = document.getElementById('cancel-button') const deleteButton = document.getElementById('delete-button') const tagText = document.getElementById('tag-text')
if (tagValue) { addButton.classList.add('hidden') inputState.classList.remove('active') tagDisplay.classList.add('active') tagText.textContent = truncateText(tagValue) currentState = 'display' } else { addButton.classList.remove('hidden') inputState.classList.remove('active') tagDisplay.classList.remove('active') tagInput.value = '' currentState = 'add' }
// Switch to input state function switchToInput() { currentState = 'input' addButton.classList.add('hidden') setTimeout(() => { inputState.classList.add('active') tagInput.focus() }, 100) }
// Switch to display state function switchToDisplay() { currentState = 'display' inputState.classList.remove('active') setTimeout(() => { tagDisplay.classList.add('active') tagText.textContent = truncateText(tagValue) // Store to localStorage localStorage.setItem('tagValue', tagValue) }, 300) }
// Return to add state function switchToAdd() { if (currentState === 'display') { tagDisplay.classList.remove('active') } else if (currentState === 'input') { inputState.classList.remove('active') } currentState = 'add' setTimeout(() => { addButton.classList.remove('hidden') tagValue = '' tagInput.value = '' localStorage.removeItem('tagValue') updateConfirmButton() }, 250) }
// Update confirm button state function updateConfirmButton() { if (tagInput.value.trim()) { confirmButton.classList.remove('disabled') } else { confirmButton.classList.add('disabled') } }
// Truncate text to max 24 characters function truncateText(text, maxLength = 24) { if (text.length <= maxLength) { return text } return text.substring(0, maxLength) + '...' }
// Bind events addButton.onclick = switchToInput
tagInput.oninput = function (e) { const inputValue = e.target.value if (inputValue.length > 24) { e.target.value = inputValue.substring(0, 24) tagValue = inputValue } else { tagValue = inputValue } updateConfirmButton() }
tagInput.onkeydown = function (e) { if (e.key === 'Enter' && tagValue.trim()) { switchToDisplay() } else if (e.key === 'Escape') { switchToAdd() } }
confirmButton.onclick = function () { if (tagValue.trim()) { switchToDisplay() } }
cancelButton.onclick = switchToAdd deleteButton.onclick = switchToAdd }
setupTag()
document.addEventListener('astro:page-load', setupTag)</script>
<style> .tag-card { border: 1px solid var(--border); width: 100%; height: 12rem; display: flex; align-items: center; justify-content: center; border-radius: 10px; }
.tag-container { display: flex; justify-content: center; align-items: center; width: 100%; padding: 0 2rem; overflow: hidden; }
.tag-component { display: flex; align-items: center; justify-content: center; gap: 0.5rem; position: relative; width: 100%; min-height: 40px; }
.add-button { background-color: var(--text-primary); color: var(--bg); border: none; border-radius: 50%; width: 36px; height: 36px; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all 0.3s ease, visibility 0s; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%) scale(1); z-index: 1; opacity: 1; visibility: visible; overflow: visible; will-change: transform; -webkit-transform: translate(-50%, -50%) scale(1); box-sizing: border-box; padding: 0; margin: 0; }
.add-button.hidden { opacity: 0; transform: translate(-50%, -50%) scale(0.8); pointer-events: none; visibility: hidden; transition: all 0.3s ease, visibility 0s 0.2s; }
.add-icon, .confirm-icon, .cancel-icon, .delete-icon { fill: currentColor; width: 1rem; height: 1rem; position: relative; display: block; transform: translateZ(0); }
.add-button:hover { opacity: 0.8; transform: translate(-50%, -50%) scale(1); -webkit-transform: translate(-50%, -50%) scale(1); }
.input-state { display: flex; align-items: center; gap: 0.125rem; background-color: var(--astro-code-background); border: 1px solid var(--code-bg); border-radius: 18px; padding: 0.28125rem 0.375rem; width: 40px; max-width: 40px; opacity: 0; overflow: hidden; transition: all 0.3s ease; position: relative; z-index: 2; pointer-events: none; }
.input-state.active { width: 10rem; max-width: 10rem; opacity: 1; pointer-events: all; }
.tag-input { background: transparent; border: none; outline: none; color: var(--text-primary); font-size: 0.9rem; font-family: var(--mono); min-width: 4rem; padding: 0.25rem 0.5rem; opacity: 0; transform: translateX(-8px); transition: opacity 0.2s ease 0.15s, transform 0.2s ease 0.15s; }
.input-state.active .tag-input { opacity: 1; transform: translateX(0); }
.tag-input::placeholder { color: var(--text-tertiary); }
.confirm-button, .cancel-button { background: none; border: none; border-radius: 50%; width: 24px; height: 24px; cursor: pointer; display: flex; align-items: center; justify-content: center; font-size: 0.8rem; opacity: 0; transform: scale(0.8); transition: all 0.2s ease; position: relative; min-width: 24px; min-height: 24px; }
.confirm-button::before, .cancel-button::before { content: ''; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); border-radius: 50%; background-color: var(--code-bg); opacity: 0; transition: opacity 0.2s ease; z-index: -1; width: 28px; height: 28px; }
.confirm-button:hover::before, .cancel-button:hover::before { opacity: 1; }
.input-state.active .confirm-button, .input-state.active .cancel-button { transform: scale(1); }
.confirm-button { color: var(--text-primary); opacity: 0.6; }
.confirm-button.disabled { opacity: 0.3; cursor: not-allowed; }
.confirm-button:not(.disabled):hover { opacity: 1; }
.confirm-button.disabled:hover::before { opacity: 0; }
.cancel-button { color: var(--text-primary); opacity: 0.6; }
.cancel-button:hover { opacity: 1; }
.tag-display { display: flex; align-items: center; gap: 0.25rem; background-color: var(--astro-code-background); border: 1px solid var(--code-bg); border-radius: 18px; padding: 0.28125rem 0.325rem 0.28125rem 0.75rem; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 3; opacity: 0; pointer-events: none; transition: all 0.25s ease-out; will-change: transform, opacity; }
.tag-display > * { margin-right: 0.125rem; }
.tag-display > *:last-child { margin-right: 0; }
.tag-display.active { opacity: 1; transform: translate(-50%, -50%); pointer-events: all; }
.tag-text { color: var(--text-primary); font-size: 0.9rem; font-family: var(--mono); }
.delete-button { background: none; border: none; border-radius: 50%; width: 24px; height: 24px; cursor: pointer; display: flex; align-items: center; justify-content: center; font-size: 0.8rem; color: var(--text-primary); opacity: 0.6; transition: all 0.2s ease; position: relative; min-width: 24px; min-height: 24px; }
.delete-button::before { content: ''; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); border-radius: 50%; background-color: var(--code-bg); opacity: 0; transition: opacity 0.2s ease; z-index: -1; width: 28px; height: 28px; }
.delete-button:hover { opacity: 1; }
.delete-button:hover::before { opacity: 1; }</style>