feat: US-006 - Frontend: Icon picker as compact dropdown
This commit is contained in:
@@ -626,7 +626,84 @@
|
||||
box-shadow: 0 0 0 2px var(--bg-surface), 0 0 0 4px var(--accent);
|
||||
}
|
||||
|
||||
/* Icon picker */
|
||||
/* Icon picker dropdown */
|
||||
.icon-picker-dropdown {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.icon-picker-trigger {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
padding: var(--space-2) var(--space-3);
|
||||
background: var(--bg-surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
cursor: pointer;
|
||||
transition: all var(--transition-base);
|
||||
min-height: 44px;
|
||||
}
|
||||
|
||||
.icon-picker-trigger:hover {
|
||||
background: var(--bg-hover);
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.icon-picker-trigger svg {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.icon-picker-trigger span {
|
||||
flex: 1;
|
||||
text-align: left;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.trigger-chevron {
|
||||
transition: transform var(--transition-base);
|
||||
}
|
||||
|
||||
.icon-picker-trigger.open .trigger-chevron {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.icon-picker-content {
|
||||
position: absolute;
|
||||
top: calc(100% + var(--space-1));
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: var(--bg-surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
z-index: 100;
|
||||
display: none;
|
||||
max-height: 300px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.icon-picker-content.visible {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.icon-search-input {
|
||||
width: 100%;
|
||||
padding: var(--space-2);
|
||||
border: none;
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: var(--bg-base);
|
||||
color: var(--text-primary);
|
||||
font-size: var(--text-sm);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.icon-search-input:focus {
|
||||
border-bottom-color: var(--accent);
|
||||
}
|
||||
|
||||
.icon-picker-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
@@ -634,9 +711,6 @@
|
||||
max-height: 250px;
|
||||
overflow-y: auto;
|
||||
padding: var(--space-2);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--bg-base);
|
||||
}
|
||||
|
||||
.icon-option {
|
||||
@@ -649,6 +723,7 @@
|
||||
cursor: pointer;
|
||||
transition: all var(--transition-base);
|
||||
background: var(--bg-surface);
|
||||
min-height: 44px;
|
||||
}
|
||||
|
||||
.icon-option:hover {
|
||||
@@ -1125,7 +1200,17 @@
|
||||
<!-- Icon -->
|
||||
<div class="form-field">
|
||||
<label class="form-label">Icon</label>
|
||||
<div class="icon-picker-grid" id="iconPicker"></div>
|
||||
<div class="icon-picker-dropdown">
|
||||
<button type="button" class="icon-picker-trigger" id="iconPickerTrigger" onclick="toggleIconPicker()">
|
||||
<i data-lucide="smile" id="selectedIconDisplay"></i>
|
||||
<span>Select Icon</span>
|
||||
<i data-lucide="chevron-down" class="trigger-chevron" id="iconPickerChevron"></i>
|
||||
</button>
|
||||
<div class="icon-picker-content" id="iconPickerContent">
|
||||
<input type="text" id="iconSearch" class="icon-search-input" placeholder="Search icons..." oninput="filterIcons()">
|
||||
<div class="icon-picker-grid" id="iconPicker"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Priority -->
|
||||
@@ -1855,6 +1940,69 @@
|
||||
<i data-lucide="${icon}"></i>
|
||||
</div>`
|
||||
).join('');
|
||||
|
||||
// Update trigger button with selected icon
|
||||
const selectedIconDisplay = document.getElementById('selectedIconDisplay');
|
||||
selectedIconDisplay.setAttribute('data-lucide', selectedIcon);
|
||||
|
||||
lucide.createIcons();
|
||||
}
|
||||
|
||||
// Toggle icon picker dropdown
|
||||
function toggleIconPicker() {
|
||||
const content = document.getElementById('iconPickerContent');
|
||||
const trigger = document.getElementById('iconPickerTrigger');
|
||||
const isOpen = content.classList.contains('visible');
|
||||
|
||||
if (isOpen) {
|
||||
closeIconPicker();
|
||||
} else {
|
||||
openIconPicker();
|
||||
}
|
||||
}
|
||||
|
||||
// Open icon picker dropdown
|
||||
function openIconPicker() {
|
||||
const content = document.getElementById('iconPickerContent');
|
||||
const trigger = document.getElementById('iconPickerTrigger');
|
||||
const search = document.getElementById('iconSearch');
|
||||
|
||||
content.classList.add('visible');
|
||||
trigger.classList.add('open');
|
||||
|
||||
// Reset search
|
||||
search.value = '';
|
||||
filterIcons();
|
||||
|
||||
// Focus search input
|
||||
setTimeout(() => search.focus(), 50);
|
||||
}
|
||||
|
||||
// Close icon picker dropdown
|
||||
function closeIconPicker() {
|
||||
const content = document.getElementById('iconPickerContent');
|
||||
const trigger = document.getElementById('iconPickerTrigger');
|
||||
|
||||
content.classList.remove('visible');
|
||||
trigger.classList.remove('open');
|
||||
}
|
||||
|
||||
// Filter icons based on search query
|
||||
function filterIcons() {
|
||||
const searchQuery = document.getElementById('iconSearch').value.toLowerCase();
|
||||
const iconPickerContainer = document.getElementById('iconPicker');
|
||||
|
||||
const filteredIcons = commonIcons.filter(icon =>
|
||||
icon.toLowerCase().includes(searchQuery)
|
||||
);
|
||||
|
||||
iconPickerContainer.innerHTML = filteredIcons.map(icon =>
|
||||
`<div class="icon-option ${icon === selectedIcon ? 'selected' : ''}"
|
||||
onclick="selectIcon('${icon}')">
|
||||
<i data-lucide="${icon}"></i>
|
||||
</div>`
|
||||
).join('');
|
||||
|
||||
lucide.createIcons();
|
||||
}
|
||||
|
||||
@@ -1864,13 +2012,20 @@
|
||||
|
||||
// Update icon options
|
||||
const iconOptions = document.querySelectorAll('.icon-option');
|
||||
iconOptions.forEach((option, index) => {
|
||||
if (commonIcons[index] === icon) {
|
||||
option.classList.add('selected');
|
||||
} else {
|
||||
option.classList.remove('selected');
|
||||
}
|
||||
iconOptions.forEach(option => {
|
||||
option.classList.remove('selected');
|
||||
});
|
||||
|
||||
// Add selected class to clicked option
|
||||
event.target.closest('.icon-option')?.classList.add('selected');
|
||||
|
||||
// Update trigger button display
|
||||
const selectedIconDisplay = document.getElementById('selectedIconDisplay');
|
||||
selectedIconDisplay.setAttribute('data-lucide', icon);
|
||||
lucide.createIcons();
|
||||
|
||||
// Close dropdown after selection
|
||||
closeIconPicker();
|
||||
}
|
||||
|
||||
// Update frequency params based on selected type
|
||||
@@ -2472,6 +2627,18 @@
|
||||
}
|
||||
});
|
||||
|
||||
// Click outside icon picker to close
|
||||
document.addEventListener('click', (e) => {
|
||||
const dropdown = document.querySelector('.icon-picker-dropdown');
|
||||
const content = document.getElementById('iconPickerContent');
|
||||
|
||||
if (content && content.classList.contains('visible')) {
|
||||
if (!dropdown.contains(e.target)) {
|
||||
closeIconPicker();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Restore collapsed/expanded state from localStorage
|
||||
restoreWeeklySummaryState();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user