Spaces:
Running
Running
Download index.html from sunole/trying-selection-list: direct link, hf CLI and curl.
- Browser
- Download file 17.7 kB
-
https://huggingface.co/spaces/sunole/trying-selection-list/resolve/main/index.html
- Command line
-
hf download hf://spaces/sunole/trying-selection-list/index.html
-
curl -L -o index.html https://huggingface.co/spaces/sunole/trying-selection-list/resolve/main/index.html
17.7 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Interactive Selectable List with Buy/Sell</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| .fade-in { | |
| animation: fadeIn 0.3s ease-in-out; | |
| } | |
| @keyframes fadeIn { | |
| from { opacity: 0; transform: translateY(-10px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| .item-selected { | |
| transform: scale(0.98); | |
| box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); | |
| } | |
| .checkbox-custom { | |
| appearance: none; | |
| -webkit-appearance: none; | |
| width: 20px; | |
| height: 20px; | |
| border: 2px solid #4f46e5; | |
| border-radius: 4px; | |
| outline: none; | |
| cursor: pointer; | |
| position: relative; | |
| transition: all 0.2s; | |
| } | |
| .checkbox-custom:checked { | |
| background-color: #4f46e5; | |
| } | |
| .checkbox-custom:checked::after { | |
| content: '\f00c'; | |
| font-family: 'Font Awesome 6 Free'; | |
| font-weight: 900; | |
| position: absolute; | |
| color: white; | |
| font-size: 12px; | |
| top: 50%; | |
| left: 50%; | |
| transform: translate(-50%, -50%); | |
| } | |
| .bounce { | |
| animation: bounce 0.5s; | |
| } | |
| @keyframes bounce { | |
| 0%, 100% { transform: translateY(0); } | |
| 50% { transform: translateY(-5px); } | |
| } | |
| .action-btn { | |
| padding: 0.25rem 0.75rem; | |
| border-radius: 0.375rem; | |
| font-size: 0.75rem; | |
| font-weight: 500; | |
| transition: all 0.2s; | |
| cursor: pointer; | |
| user-select: none; | |
| } | |
| .buy-btn { | |
| background-color: #10b981; | |
| color: white; | |
| } | |
| .buy-btn:hover { | |
| background-color: #059669; | |
| } | |
| .sell-btn { | |
| background-color: #ef4444; | |
| color: white; | |
| } | |
| .sell-btn:hover { | |
| background-color: #dc2626; | |
| } | |
| .close-btn { | |
| background-color: #3b82f6; | |
| color: white; | |
| } | |
| .close-btn:hover { | |
| background-color: #2563eb; | |
| } | |
| .action-container { | |
| display: flex; | |
| gap: 0.5rem; | |
| margin-left: 1rem; | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-50 min-h-screen flex items-center justify-center p-4"> | |
| <div class="w-full max-w-md mx-auto"> | |
| <div class="bg-white rounded-xl shadow-lg overflow-hidden transition-all duration-300"> | |
| <div class="p-6"> | |
| <h2 class="text-2xl font-bold text-indigo-600 mb-4 flex items-center"> | |
| <i class="fas fa-list-check mr-2"></i> Selectable Items | |
| </h2> | |
| <div class="mb-4 relative"> | |
| <input | |
| type="text" | |
| id="searchInput" | |
| placeholder="Search items..." | |
| class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none transition" | |
| > | |
| <i class="fas fa-search absolute right-3 top-3 text-gray-400"></i> | |
| </div> | |
| <div class="flex justify-between items-center mb-3"> | |
| <span class="text-sm text-gray-600" id="selectedCount">0 selected</span> | |
| <button | |
| id="clearAllBtn" | |
| class="text-sm text-indigo-600 hover:text-indigo-800 transition hidden" | |
| > | |
| Clear all | |
| </button> | |
| </div> | |
| <div class="border border-gray-200 rounded-lg overflow-hidden"> | |
| <div id="itemsContainer" class="max-h-96 overflow-y-auto"> | |
| <!-- Items will be inserted here by JavaScript --> | |
| </div> | |
| </div> | |
| <div class="mt-4 flex justify-between items-center"> | |
| <div class="text-sm text-gray-500" id="totalItems">Total: 0 items</div> | |
| <button | |
| id="submitBtn" | |
| class="px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition disabled:opacity-50 disabled:cursor-not-allowed" | |
| disabled | |
| > | |
| Confirm Selection | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="mt-8 bg-white rounded-xl shadow-lg p-6"> | |
| <h3 class="text-lg font-semibold text-gray-800 mb-3">Add Custom Items</h3> | |
| <div class="flex gap-2"> | |
| <input | |
| type="text" | |
| id="newItemInput" | |
| placeholder="Enter new item" | |
| class="flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 outline-none transition" | |
| > | |
| <button | |
| id="addItemBtn" | |
| class="px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition" | |
| > | |
| <i class="fas fa-plus"></i> Add | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // Sample initial data | |
| const initialItems = [ | |
| "Apple", "Banana", "Cherry", "Date", "Elderberry", | |
| "Fig", "Grape", "Honeydew", "Kiwi", "Lemon", | |
| "Mango", "Nectarine", "Orange", "Peach", "Quince" | |
| ]; | |
| // Load data from localStorage or initialize | |
| const savedData = localStorage.getItem('selectableListData'); | |
| let items, selectedItems, itemActions; | |
| if (savedData) { | |
| const parsedData = JSON.parse(savedData); | |
| items = parsedData.items || [...initialItems]; | |
| selectedItems = parsedData.selectedItems || []; | |
| itemActions = parsedData.itemActions || {}; | |
| } else { | |
| items = [...initialItems]; | |
| selectedItems = []; | |
| itemActions = {}; | |
| } | |
| // Initialize item actions for any new items | |
| items.forEach(item => { | |
| if (!itemActions.hasOwnProperty(item)) { | |
| itemActions[item] = null; | |
| } | |
| }); | |
| // Save data to localStorage | |
| function saveToLocalStorage() { | |
| const dataToSave = { | |
| items: items, | |
| selectedItems: selectedItems, | |
| itemActions: itemActions | |
| }; | |
| localStorage.setItem('selectableListData', JSON.stringify(dataToSave)); | |
| } | |
| // DOM elements | |
| const itemsContainer = document.getElementById('itemsContainer'); | |
| const searchInput = document.getElementById('searchInput'); | |
| const selectedCount = document.getElementById('selectedCount'); | |
| const clearAllBtn = document.getElementById('clearAllBtn'); | |
| const submitBtn = document.getElementById('submitBtn'); | |
| const totalItems = document.getElementById('totalItems'); | |
| const newItemInput = document.getElementById('newItemInput'); | |
| const addItemBtn = document.getElementById('addItemBtn'); | |
| // Render items | |
| function renderItems(itemsToRender = items) { | |
| itemsContainer.innerHTML = ''; | |
| if (itemsToRender.length === 0) { | |
| itemsContainer.innerHTML = ` | |
| <div class="p-4 text-center text-gray-500"> | |
| <i class="fas fa-inbox text-3xl mb-2 text-gray-300"></i> | |
| <p>No items found</p> | |
| </div> | |
| `; | |
| return; | |
| } | |
| itemsToRender.forEach((item, index) => { | |
| const isSelected = selectedItems.includes(item); | |
| const action = itemActions[item]; | |
| const itemElement = document.createElement('div'); | |
| itemElement.className = `p-3 border-b border-gray-100 flex items-center justify-between cursor-pointer transition ${isSelected ? 'bg-indigo-50 item-selected' : 'hover:bg-gray-50'} fade-in`; | |
| // Create action buttons based on current state | |
| let actionButtons = ''; | |
| if (action === 'buy' || action === 'sell') { | |
| actionButtons = ` | |
| <div class="action-container"> | |
| <div class="action-btn close-btn" data-item="${item}" data-action="close"> | |
| Close ${action === 'buy' ? 'Buy' : 'Sell'} | |
| </div> | |
| </div> | |
| `; | |
| } else { | |
| actionButtons = ` | |
| <div class="action-container"> | |
| <div class="action-btn buy-btn" data-item="${item}" data-action="buy"> | |
| Buy | |
| </div> | |
| <div class="action-btn sell-btn" data-item="${item}" data-action="sell"> | |
| Sell | |
| </div> | |
| </div> | |
| `; | |
| } | |
| itemElement.innerHTML = ` | |
| <span class="flex-1 ${isSelected ? 'font-medium text-indigo-700' : 'text-gray-700'}">${item}</span> | |
| ${actionButtons} | |
| <input | |
| type="checkbox" | |
| class="checkbox-custom" | |
| ${isSelected ? 'checked' : ''} | |
| data-item="${item}" | |
| > | |
| `; | |
| // Add click handler for the item row | |
| itemElement.addEventListener('click', (e) => { | |
| // Don't toggle selection if clicking on action buttons | |
| if (!e.target.closest('.action-btn')) { | |
| toggleSelection(item); | |
| } | |
| }); | |
| // Add click handlers for action buttons | |
| const actionBtns = itemElement.querySelectorAll('.action-btn'); | |
| actionBtns.forEach(btn => { | |
| btn.addEventListener('click', (e) => { | |
| e.stopPropagation(); | |
| const action = btn.dataset.action; | |
| handleItemAction(item, action); | |
| }); | |
| }); | |
| itemsContainer.appendChild(itemElement); | |
| }); | |
| updateUI(); | |
| } | |
| // Handle buy/sell/close actions | |
| function handleItemAction(item, action) { | |
| if (action === 'buy' || action === 'sell') { | |
| itemActions[item] = action; | |
| } else if (action === 'close') { | |
| itemActions[item] = null; | |
| } | |
| // Add animation to the changed element | |
| const itemElement = document.querySelector(`[data-item="${item}"]`).closest('div'); | |
| if (itemElement) { | |
| itemElement.classList.add('bounce'); | |
| setTimeout(() => { | |
| itemElement.classList.remove('bounce'); | |
| }, 500); | |
| } | |
| saveToLocalStorage(); | |
| renderItems(getFilteredItems()); | |
| } | |
| // Toggle item selection | |
| function toggleSelection(item) { | |
| const index = selectedItems.indexOf(item); | |
| if (index === -1) { | |
| selectedItems.push(item); | |
| } else { | |
| selectedItems.splice(index, 1); | |
| } | |
| saveToLocalStorage(); | |
| renderItems(getFilteredItems()); | |
| // Add bounce animation to the checkbox | |
| const checkbox = document.querySelector(`input[data-item="${item}"]`); | |
| if (checkbox) { | |
| checkbox.parentElement.classList.add('bounce'); | |
| setTimeout(() => { | |
| checkbox.parentElement.classList.remove('bounce'); | |
| }, 500); | |
| } | |
| } | |
| // Filter items based on search input | |
| function getFilteredItems() { | |
| const searchTerm = searchInput.value.toLowerCase(); | |
| return items.filter(item => | |
| item.toLowerCase().includes(searchTerm) | |
| ); | |
| } | |
| // Update UI elements | |
| function updateUI() { | |
| selectedCount.textContent = `${selectedItems.length} selected`; | |
| totalItems.textContent = `Total: ${items.length} items`; | |
| // Toggle clear all button | |
| if (selectedItems.length > 0) { | |
| clearAllBtn.classList.remove('hidden'); | |
| submitBtn.disabled = false; | |
| } else { | |
| clearAllBtn.classList.add('hidden'); | |
| submitBtn.disabled = true; | |
| } | |
| } | |
| // Clear all selections | |
| function clearAllSelections() { | |
| selectedItems = []; | |
| saveToLocalStorage(); | |
| renderItems(getFilteredItems()); | |
| } | |
| // Add new item | |
| function addNewItem() { | |
| const newItem = newItemInput.value.trim(); | |
| if (newItem && !items.includes(newItem)) { | |
| items.unshift(newItem); | |
| itemActions[newItem] = null; // Initialize action for new item | |
| newItemInput.value = ''; | |
| saveToLocalStorage(); | |
| renderItems(getFilteredItems()); | |
| // Highlight the newly added item | |
| setTimeout(() => { | |
| const firstItem = itemsContainer.firstChild; | |
| if (firstItem) { | |
| firstItem.classList.add('bg-green-50'); | |
| setTimeout(() => { | |
| firstItem.classList.remove('bg-green-50'); | |
| }, 2000); | |
| } | |
| }, 100); | |
| } | |
| } | |
| // Event listeners | |
| searchInput.addEventListener('input', () => { | |
| renderItems(getFilteredItems()); | |
| }); | |
| clearAllBtn.addEventListener('click', clearAllSelections); | |
| submitBtn.addEventListener('click', () => { | |
| // Prepare action summary | |
| let actionSummary = []; | |
| for (const item in itemActions) { | |
| if (itemActions[item]) { | |
| actionSummary.push(`${item} (${itemActions[item]})`); | |
| } | |
| } | |
| alert(`Selected items: ${selectedItems.join(', ')}\n\nActions:\n${actionSummary.join('\n')}`); | |
| }); | |
| newItemInput.addEventListener('keypress', (e) => { | |
| if (e.key === 'Enter') { | |
| addNewItem(); | |
| } | |
| }); | |
| addItemBtn.addEventListener('click', addNewItem); | |
| // Initialize | |
| renderItems(); | |
| }); | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=sunole/trying-selection-list" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |