diff --git a/PlanTempus.Application/Features/Services/Components/ServiceDetailGeneral/Default.cshtml b/PlanTempus.Application/Features/Services/Components/ServiceDetailGeneral/Default.cshtml index a176741..207805d 100644 --- a/PlanTempus.Application/Features/Services/Components/ServiceDetailGeneral/Default.cshtml +++ b/PlanTempus.Application/Features/Services/Components/ServiceDetailGeneral/Default.cshtml @@ -14,44 +14,45 @@ @Model.LabelCategory - -
+ Kombi-behandlinger Klip Farve Behandlinger Styling -
+
@Model.LabelCalendarColor - -
- Rød - Pink - Lilla - Mørk lilla - Indigo - Blå - Lyseblå - Cyan - Teal - Grøn - Lysegrøn - Lime - Gul - Amber - Orange - Mørk orange -
+ + Rød + Pink + Lilla + Mørk lilla + Indigo + Blå + Lyseblå + Cyan + Teal + Grøn + Lysegrøn + Lime + Gul + Amber + Orange + Mørk orange +
@@ -123,7 +124,10 @@ @Model.LabelImage - @Model.LabelUploadImage + + + @Model.LabelUploadImage + diff --git a/PlanTempus.Application/wwwroot/css/cash.css b/PlanTempus.Application/wwwroot/css/cash.css index f7b8ba9..e75a52f 100644 --- a/PlanTempus.Application/wwwroot/css/cash.css +++ b/PlanTempus.Application/wwwroot/css/cash.css @@ -655,19 +655,9 @@ swp-auto-id { =========================================== */ swp-note-field { textarea { - width: 100%; min-height: 80px; padding: var(--spacing-6); - font-size: var(--font-size-base); - font-family: var(--font-family); - border: 1px solid var(--color-border); border-radius: var(--radius-md); - resize: vertical; - - &:focus { - outline: none; - border-color: var(--color-teal); - } } } diff --git a/PlanTempus.Application/wwwroot/css/components.css b/PlanTempus.Application/wwwroot/css/components.css index 410495a..1aa6172 100644 --- a/PlanTempus.Application/wwwroot/css/components.css +++ b/PlanTempus.Application/wwwroot/css/components.css @@ -678,6 +678,34 @@ swp-user-email { /* =========================================== FORM INPUTS (shared base styling) =========================================== */ + +/* Global textarea styling */ +textarea { + width: 100%; + padding: var(--spacing-3) var(--spacing-4); + font-size: var(--font-size-base); + font-family: var(--font-family); + color: var(--color-text); + background: var(--color-background-alt); + border: 1px solid var(--color-border); + border-radius: var(--radius-sm); + resize: vertical; + + &::placeholder { + color: var(--color-text-muted); + } + + &:hover { + background: var(--color-background); + } + + &:focus { + outline: none; + background: var(--color-surface); + border-color: var(--color-teal); + } +} + swp-form-group { display: flex; flex-direction: column; @@ -861,28 +889,6 @@ swp-form-input { } } - /* Textarea */ - textarea { - width: 100%; - padding: 10px 12px; - border: 1px solid var(--color-border); - border-radius: 6px; - font-size: 14px; - font-family: var(--font-family); - color: var(--color-text); - background: var(--color-surface); - resize: vertical; - - &::placeholder { - color: var(--color-text-muted); - } - - &:focus { - outline: none; - border-color: var(--color-teal); - } - } - /* Date range inputs */ swp-date-range { display: flex; diff --git a/PlanTempus.Application/wwwroot/css/controls.css b/PlanTempus.Application/wwwroot/css/controls.css index af3ee2e..b146e3a 100644 --- a/PlanTempus.Application/wwwroot/css/controls.css +++ b/PlanTempus.Application/wwwroot/css/controls.css @@ -183,11 +183,11 @@ swp-notification-intro { } /* =========================================== - SELECT DROPDOWN (Popover API) + SELECT DROPDOWN =========================================== */ swp-select { position: relative; - display: inline-block; + display: block; } swp-select button { @@ -202,8 +202,7 @@ swp-select button { border: 1px solid transparent; cursor: pointer; transition: all 150ms ease; - min-width: 160px; - anchor-name: --select-trigger; + width: 100%; &:hover { background: var(--color-background); @@ -227,35 +226,37 @@ swp-select button i { transition: transform 150ms ease; } -swp-select button[aria-expanded="true"] i { +swp-select.open button i { transform: rotate(180deg); } -swp-select [popover] { +swp-select-dropdown { + display: none; position: absolute; - position-anchor: --select-trigger; - top: anchor(bottom); - left: anchor(left); - margin: var(--spacing-1) 0 0 0; + top: 100%; + left: 0; + z-index: 100; + margin-top: var(--spacing-1); padding: var(--spacing-2); + min-width: 100%; background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-md); box-shadow: var(--shadow-lg); - min-width: anchor-size(width); max-height: 280px; overflow-y: auto; -} - -swp-select [popover]:popover-open { - display: flex; flex-direction: column; gap: 2px; } +swp-select.open swp-select-dropdown { + display: flex; +} + swp-select-option { display: flex; align-items: center; + gap: var(--spacing-3); padding: var(--spacing-2) var(--spacing-3); border-radius: var(--radius-sm); cursor: pointer; @@ -272,4 +273,21 @@ swp-select-option { font-weight: var(--font-weight-medium); color: var(--color-teal); } + + &.highlighted { + background: var(--color-background-alt); + outline: 2px solid var(--color-teal); + outline-offset: -2px; + } +} + +/* =========================================== + COLOR DOT (for color pickers) + =========================================== */ +swp-color-dot { + width: 18px; + height: 12px; + border-radius: 3px; + flex-shrink: 0; + background: var(--b-primary); } diff --git a/PlanTempus.Application/wwwroot/ts/modules/controls.ts b/PlanTempus.Application/wwwroot/ts/modules/controls.ts index 03eadc3..010d007 100644 --- a/PlanTempus.Application/wwwroot/ts/modules/controls.ts +++ b/PlanTempus.Application/wwwroot/ts/modules/controls.ts @@ -3,7 +3,7 @@ * * Handles generic UI controls functionality: * - Toggle sliders (Ja/Nej switches) - * - Select dropdowns (Popover API) + * - Select dropdowns */ /** @@ -38,20 +38,20 @@ export class ControlsController { /** * Initialize all select dropdowns on the page - * Uses Popover API for dropdown behavior */ private initSelectDropdowns(): void { document.querySelectorAll('swp-select').forEach(select => { const trigger = select.querySelector('button'); - const popover = select.querySelector('[popover]') as HTMLElement | null; + const dropdown = select.querySelector('swp-select-dropdown'); const options = select.querySelectorAll('swp-select-option'); - if (!trigger || !popover) return; + if (!trigger || !dropdown) return; - // Update aria-expanded on toggle - popover.addEventListener('toggle', (e: Event) => { - const event = e as ToggleEvent; - trigger.setAttribute('aria-expanded', event.newState === 'open' ? 'true' : 'false'); + // Toggle dropdown on button click + trigger.addEventListener('click', (e) => { + e.stopPropagation(); + const isOpen = select.classList.toggle('open'); + trigger.setAttribute('aria-expanded', isOpen ? 'true' : 'false'); }); // Handle option selection @@ -70,11 +70,18 @@ export class ControlsController { valueEl.textContent = label; } + // Update color dot if present (for color pickers) + const triggerDot = trigger.querySelector('swp-color-dot'); + if (triggerDot && value) { + triggerDot.className = `is-${value}`; + } + // Update data-value on select element (select as HTMLElement).dataset.value = value; - // Close popover - popover.hidePopover(); + // Close dropdown + select.classList.remove('open'); + trigger.setAttribute('aria-expanded', 'false'); // Dispatch custom event select.dispatchEvent(new CustomEvent('change', { @@ -84,5 +91,54 @@ export class ControlsController { }); }); }); + + // Click outside to close any open dropdown + document.addEventListener('click', () => { + this.closeAllSelects(); + }); + + // Keyboard navigation + document.addEventListener('keydown', (e) => { + const openSelect = document.querySelector('swp-select.open'); + if (!openSelect) return; + + // Escape to close + if (e.key === 'Escape') { + this.closeAllSelects(); + return; + } + + // Letter key to jump to option + if (e.key.length === 1 && /[a-zA-ZæøåÆØÅ]/.test(e.key)) { + const options = openSelect.querySelectorAll('swp-select-option'); + const letter = e.key.toLowerCase(); + + for (const option of options) { + const text = option.textContent?.trim().toLowerCase() || ''; + if (text.startsWith(letter)) { + // Scroll into view and highlight + option.scrollIntoView({ block: 'nearest' }); + options.forEach(o => o.classList.remove('highlighted')); + option.classList.add('highlighted'); + break; + } + } + } + + // Enter to select highlighted option + if (e.key === 'Enter') { + const highlighted = openSelect.querySelector('swp-select-option.highlighted') as HTMLElement; + if (highlighted) { + highlighted.click(); + } + } + }); + } + + private closeAllSelects(): void { + document.querySelectorAll('swp-select.open').forEach(select => { + select.classList.remove('open'); + select.querySelector('button')?.setAttribute('aria-expanded', 'false'); + }); } }