Adds double-click to edit support for rates

Enables quick editing of salary rates by double-clicking card inputs

Introduces functionality to:
- Open rates drawer on double-click
- Automatically focus and select corresponding input
- Add temporary highlight to edited row
This commit is contained in:
Janus C. H. Knudsen 2026-01-14 18:34:05 +01:00
parent 8b2a630861
commit 5fab58ff6f
5 changed files with 152 additions and 1304 deletions

View file

@ -8,45 +8,40 @@ Reference for alle genbrugelige komponenter. **LAV ALDRIG EN NY KOMPONENT HVIS D
**VIGTIGT:** Disse base patterns skal ALTID bruges som foundation for nye features.
### Grid + Subgrid Table Pattern
### Data Table Pattern (ANBEFALET)
Alle tabeller skal bruge dette pattern:
Alle nye tabeller skal bruge `swp-data-table` fra components.css:
```html
<swp-feature-table>
<swp-feature-table-header> <!-- extends swp-table-header-base -->
<swp-feature-cell>Kolonne 1</swp-feature-cell>
</swp-feature-table-header>
<swp-feature-table-body> <!-- extends swp-table-body-base -->
<swp-feature-row> <!-- extends swp-table-row-base -->
<swp-feature-cell>Data</swp-feature-cell>
</swp-feature-row>
</swp-feature-table-body>
</swp-feature-table>
<swp-card class="feature-context">
<swp-data-table>
<swp-data-table-header>
<swp-data-table-cell>Kolonne 1</swp-data-table-cell>
<swp-data-table-cell>Kolonne 2</swp-data-table-cell>
</swp-data-table-header>
<swp-data-table-row>
<swp-data-table-cell>Data 1</swp-data-table-cell>
<swp-data-table-cell>Data 2</swp-data-table-cell>
</swp-data-table-row>
</swp-data-table>
</swp-card>
```
**CSS Pattern:**
```css
swp-feature-table {
display: grid;
grid-template-columns: /* feature-specific */;
/* I feature CSS - definer kun kolonner via context class */
swp-card.feature-context swp-data-table {
grid-template-columns: 1fr 120px 80px;
}
swp-feature-table-header,
swp-feature-table-body {
display: grid;
grid-column: 1 / -1;
grid-template-columns: subgrid;
}
swp-feature-row {
display: grid;
grid-column: 1 / -1;
grid-template-columns: subgrid;
align-items: center;
/* Kolonne-specifik styling med nth-child */
swp-card.feature-context swp-data-table-cell:nth-child(2) {
font-family: var(--font-mono);
}
```
**VIGTIGT:** Context class skal være på `swp-card`, IKKE en wrapper div!
### List Item Pattern
Alle lister (notifikationer, bookinger, attentions) bruger:
@ -211,57 +206,56 @@ Automatisk dot via `::before` pseudo-element.
---
## Tables - Grid + Subgrid Pattern
## Tables - swp-data-table Pattern
### Struktur (ALTID følg dette mønster)
**BRUG ALTID `swp-data-table`** for nye tabeller. Den generiske komponent er defineret i components.css.
### Struktur
```html
<swp-[feature]-table>
<swp-[feature]-table-header>
<swp-[feature]-cell>Kolonne 1</swp-[feature]-cell>
<swp-[feature]-cell>Kolonne 2</swp-[feature]-cell>
</swp-[feature]-table-header>
<swp-[feature]-table-body>
<swp-[feature]-row>
<swp-[feature]-cell>Data 1</swp-[feature]-cell>
<swp-[feature]-cell>Data 2</swp-[feature]-cell>
</swp-[feature]-row>
</swp-[feature]-table-body>
</swp-[feature]-table>
<swp-card class="context-class">
<swp-data-table>
<swp-data-table-header>
<swp-data-table-cell>Kolonne 1</swp-data-table-cell>
<swp-data-table-cell>Kolonne 2</swp-data-table-cell>
</swp-data-table-header>
<swp-data-table-row>
<swp-data-table-cell>Data 1</swp-data-table-cell>
<swp-data-table-cell>Data 2</swp-data-table-cell>
</swp-data-table-row>
</swp-data-table>
</swp-card>
```
### CSS Pattern
```css
swp-[feature]-table {
display: grid;
grid-template-columns: /* definer kolonner her */;
/* Definer kolonner via context class på swp-card */
swp-card.context-class swp-data-table {
grid-template-columns: 1fr 120px 80px;
}
swp-[feature]-table-header,
swp-[feature]-table-body {
display: grid;
grid-column: 1 / -1;
grid-template-columns: subgrid;
}
swp-[feature]-row {
display: grid;
grid-column: 1 / -1;
grid-template-columns: subgrid;
align-items: center;
/* Kolonne-specifik styling med nth-child */
swp-card.context-class swp-data-table-cell:nth-child(2) {
font-family: var(--font-mono);
}
```
### Eksisterende tabeller
| Feature | Container | Row | CSS fil |
|---------|-----------|-----|---------|
| Cash | `swp-cash-table` | `swp-cash-table-row` | cash.css |
| Employees | `swp-employee-table` | `swp-employee-row` | employees.css |
| Salary | `swp-salary-table` | `swp-salary-table-row` | employees.css |
| **Data (generisk)** | `swp-data-table` | `swp-data-table-row` | components.css |
| Bookings (dashboard) | `swp-booking-list` | `swp-booking-item` | bookings.css |
| Feature | Card Class | CSS fil |
|---------|------------|---------|
| Employees list | `swp-card.employees-list` | employees.css |
| Salary history | `swp-card.salary-history` | employees.css |
| Invoice history | `swp-card.invoice-history` | account.css |
| Stats bookings | `swp-card.stats-bookings` | employees.css |
| Cash | `swp-cash-table` (kompleks) | cash.css |
**Lister (ikke tabeller):**
| Feature | Container | Item | CSS fil |
|---------|-----------|------|---------|
| Bookings | `swp-booking-list` | `swp-booking-item` | bookings.css |
| Notifications | `swp-notification-list` | `swp-notification-item` | notifications.css |
| Attentions | `swp-attention-list` | `swp-attention-item` | attentions.css |
@ -269,22 +263,28 @@ swp-[feature]-row {
## Table Cells - Standard Styling
Base styling er i components.css. Tilpas kun via context class:
```css
/* Header cells */
swp-[feature]-table-header swp-[feature]-cell {
/* Header cells (automatisk fra components.css) */
swp-data-table-header swp-data-table-cell {
font-size: var(--font-size-xs);
font-weight: var(--font-weight-semibold);
text-transform: uppercase;
letter-spacing: 0.5px;
color: var(--color-text-secondary);
}
/* Body cells */
swp-[feature]-cell {
padding: var(--spacing-5);
font-size: var(--font-size-base); /* ALTID base, ikke sm */
/* Body cells (automatisk fra components.css) */
swp-data-table-cell {
padding: var(--spacing-4);
font-size: var(--font-size-base);
color: var(--color-text);
}
/* Feature-specifik tilpasning via context */
swp-card.my-feature swp-data-table-cell:nth-child(3) {
text-align: right;
}
```
---
@ -428,73 +428,29 @@ Simpel liste med tekst + badge (f.eks. planlagt fravær).
---
## Salary Table (employees.css)
## Salary History Table (employees.css)
Bruger Grid + Subgrid mønsteret.
Bruger `swp-data-table` med `.salary-history` context class.
```html
<swp-salary-table>
<swp-salary-table-header>
<swp-salary-table-cell>Periode</swp-salary-table-cell>
<swp-salary-table-cell>Bruttoløn</swp-salary-table-cell>
<swp-salary-table-cell></swp-salary-table-cell>
</swp-salary-table-header>
<swp-salary-table-body>
<swp-salary-table-row>
<swp-salary-table-cell>Januar 2026</swp-salary-table-cell>
<swp-salary-table-cell class="mono">34.063,50 kr</swp-salary-table-cell>
<swp-salary-table-cell><i class="ph ph-caret-right"></i></swp-salary-table-cell>
</swp-salary-table-row>
</swp-salary-table-body>
</swp-salary-table>
```
Rækker har hover-effekt og chevron bliver teal ved hover.
---
## Data Table - Generisk (components.css)
Generisk tabel med Grid + Subgrid.
**Struktur:**
- `swp-data-table` = grid (kolonner i feature CSS)
- `swp-data-table-header` = subgrid (celler direkte i)
- `swp-data-table-row` = subgrid
- `swp-data-table-cell` = celler
**Brug:** Wrap i container med klasse der definerer kolonner.
```css
/* I feature CSS (f.eks. employees.css) */
.stats-bookings swp-data-table {
grid-template-columns: 90px 60px 1fr 1fr 80px 100px 100px;
}
/* Kolonne-specifik styling med nth-child */
.stats-bookings swp-data-table-row swp-data-table-cell:nth-child(1),
.stats-bookings swp-data-table-row swp-data-table-cell:nth-child(2) {
font-family: var(--font-mono);
color: var(--color-text-secondary);
}
```
```html
<swp-card class="stats-bookings">
<swp-card class="salary-history">
<swp-section-label>Lønhistorik</swp-section-label>
<swp-data-table>
<swp-data-table-header>
<swp-data-table-cell>Kolonne 1</swp-data-table-cell>
<swp-data-table-cell>Kolonne 2</swp-data-table-cell>
<swp-data-table-cell>Periode</swp-data-table-cell>
<swp-data-table-cell>Bruttoløn</swp-data-table-cell>
<swp-data-table-cell></swp-data-table-cell>
</swp-data-table-header>
<swp-data-table-row>
<swp-data-table-cell>Data 1</swp-data-table-cell>
<swp-data-table-cell>Data 2</swp-data-table-cell>
<swp-data-table-cell>Januar 2026</swp-data-table-cell>
<swp-data-table-cell class="mono">34.063,50 kr</swp-data-table-cell>
<swp-data-table-cell><i class="ph ph-caret-right"></i></swp-data-table-cell>
</swp-data-table-row>
</swp-data-table>
</swp-card>
```
**Kolonne-styling:** Brug `nth-child()` i feature CSS frem for klasser på celler.
Rækker har hover-effekt og chevron bliver teal ved hover.
---
@ -579,11 +535,13 @@ Dashed border knap til tilføjelse af elementer.
|-----|---------|
| `design-tokens.css` | Farver, spacing, fonts, shadows |
| `design-system.css` | Base resets, typography |
| `page.css` | Page structure |
| `components.css` | Buttons, badges, cards, section-label, add-button, avatars, icon-btn, data-table |
| `page.css` | Page structure, sticky-header |
| `components.css` | Buttons, badges, cards, section-label, add-button, avatars, icon-btn, **swp-data-table** |
| `stats.css` | Stat cards, stat rows |
| `tabs.css` | Tab bar, tab content |
| `employees.css` | Employee table, user info, edit forms, document lists, salary table |
| `employees.css` | User info, edit forms, document lists, context styles (.employees-list, .salary-history, .stats-bookings) |
| `account.css` | Account/billing styles, context styles (.invoice-history) |
| `bookings.css` | Booking list items |
| `notifications.css` | Notification items |
| `attentions.css` | Attention items |
| `cash.css` | Cash register (swp-cash-table - kompleks, ikke migreret) |

View file

@ -814,6 +814,23 @@ swp-simple-item {
}
}
/* ===========================================
FOCUS HIGHLIGHT (double-click to edit)
=========================================== */
@keyframes focus-blink {
0%, 100% { background: transparent; }
25%, 75% { background: var(--bg-teal-medium); }
}
swp-data-row.focus-highlight {
animation: focus-blink 1s ease-out;
& input {
outline: 2px solid var(--color-teal);
outline-offset: 2px;
}
}
/* ===========================================
STATS BOOKINGS TABLE
=========================================== */

File diff suppressed because one or more lines are too long

View file

@ -211,6 +211,7 @@ class RatesSyncController {
this.setupCheckboxListeners();
this.setupInputListeners();
this.setupDoubleClickToEdit();
}
/**
@ -319,4 +320,50 @@ class RatesSyncController {
const formattedValue = this.formatNumber(value);
cardInput.value = `${formattedValue} ${unit}`;
}
/**
* Setup double-click on salary card inputs to open drawer and focus field
*/
private setupDoubleClickToEdit(): void {
document.addEventListener('dblclick', (e: Event) => {
const target = e.target as HTMLElement;
const input = target.closest<HTMLInputElement>('input[id^="value-"]');
if (!input || !input.id) return;
// Extract key from value-{key}
const match = input.id.match(/^value-(.+)$/);
if (!match) return;
const rateKey = match[1];
this.openDrawerAndFocus(rateKey);
});
}
/**
* Open drawer and focus the corresponding field with highlight
*/
private openDrawerAndFocus(rateKey: string): void {
// Open the drawer
const trigger = document.querySelector<HTMLElement>('[data-drawer-trigger="rates-drawer"]');
trigger?.click();
// Wait for drawer to open, then focus field
requestAnimationFrame(() => {
const drawerInput = document.getElementById(`rate-${rateKey}`) as HTMLInputElement | null;
if (!drawerInput) return;
// Focus the input
drawerInput.focus();
drawerInput.select();
// Add highlight to row
const row = drawerInput.closest<HTMLElement>('swp-data-row');
if (row) {
row.classList.add('focus-highlight');
// Remove class after animation
setTimeout(() => row.classList.remove('focus-highlight'), 1000);
}
});
}
}