The cell-editor was already complete (drafts, row-blur saves, etag concurrency, validation). This commit adds the missing row-level ops: - "+ Add row" appends a draft row inline; first cell focused. Row-blur POSTs to <dir>/form.html (the existing form-create endpoint); 201 swaps the synthetic id for the server-returned URL/ETag. Empty rows the user walks away from are silently discarded. - Right-click a row → "Delete row" (or "Delete N rows" when a cell range spans multiple rows). DELETE the row YAML with If-Match; 412 surfaces a conflict warning. - Multi-row clipboard paste creates new rows for grid content that extends past the last existing row, instead of dropping cells past the end. Each new row saves via its own row-blur. - Empty rows now have a 2.4em minimum height so a freshly-added row is visible. Without the floor it collapses to cell-padding (~8px) and looks like a divider line. Server-side: no new endpoints. Form-create (POST <dir>/form.html → 201 + Location) and file-API DELETE carry the new client capabilities. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
205 lines
6 KiB
CSS
205 lines
6 KiB
CSS
/* tables/ — directory-of-YAML table view. Reuses tokens from shared/base.css. */
|
|
|
|
.table-main {
|
|
padding: var(--spacing-md);
|
|
max-width: 100%;
|
|
}
|
|
|
|
.table-description {
|
|
margin: 0 0 var(--spacing-md);
|
|
color: var(--color-text-muted);
|
|
font-size: 0.95rem;
|
|
}
|
|
|
|
.table-status {
|
|
margin: 0 0 var(--spacing-md);
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
background: var(--color-bg-warning, #fff8e6);
|
|
border: 1px solid var(--color-border, #d6cfa3);
|
|
border-radius: var(--radius-sm, 4px);
|
|
}
|
|
|
|
.table-toolbar {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
gap: var(--spacing-md);
|
|
margin: 0 0 var(--spacing-sm);
|
|
}
|
|
|
|
.table-toolbar__left,
|
|
.table-toolbar__right {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--spacing-sm);
|
|
}
|
|
|
|
#table-add-row {
|
|
text-decoration: none;
|
|
}
|
|
|
|
.table-rowcount {
|
|
color: var(--color-text-muted);
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.table-scroll {
|
|
overflow: auto;
|
|
max-height: calc(100vh - 200px);
|
|
border: 1px solid var(--color-border, #d8d8d8);
|
|
border-radius: var(--radius-sm, 4px);
|
|
}
|
|
|
|
.zddc-table {
|
|
border-collapse: collapse;
|
|
width: 100%;
|
|
font-size: 0.95rem;
|
|
}
|
|
|
|
.zddc-table thead {
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 2;
|
|
background: var(--color-bg-elevated, #f5f5f5);
|
|
}
|
|
|
|
.zddc-table__title-row .zddc-table__th {
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
text-align: left;
|
|
font-weight: 600;
|
|
border-bottom: 1px solid var(--color-border, #d8d8d8);
|
|
cursor: pointer;
|
|
user-select: none;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.zddc-table__title-row .zddc-table__th:hover {
|
|
background: var(--color-bg-hover, rgba(0, 0, 0, 0.04));
|
|
}
|
|
|
|
.zddc-table__filter-row .zddc-table__filter-cell {
|
|
padding: 4px var(--spacing-sm);
|
|
border-bottom: 1px solid var(--color-border, #d8d8d8);
|
|
background: var(--color-bg-elevated, #f5f5f5);
|
|
}
|
|
|
|
.zddc-table__filter-text,
|
|
.zddc-table__filter-enum {
|
|
width: 100%;
|
|
box-sizing: border-box;
|
|
padding: 2px 4px;
|
|
font-size: 0.85rem;
|
|
border: 1px solid var(--color-border, #d0d0d0);
|
|
border-radius: 3px;
|
|
background: var(--color-bg, #fff);
|
|
color: var(--color-text, #111);
|
|
}
|
|
|
|
.zddc-table__filter-enum {
|
|
min-height: 1.8em;
|
|
}
|
|
|
|
.zddc-table__row:nth-child(even) {
|
|
background: var(--color-bg-zebra, rgba(0, 0, 0, 0.02));
|
|
}
|
|
|
|
/* Minimum row height so a freshly-added row (every cell empty) stays
|
|
visible — without this the row collapses to just cell padding and
|
|
looks like a thin divider line. Acts as a floor; rows with content
|
|
grow naturally to fit the text. */
|
|
.zddc-table__row {
|
|
height: 2.4em;
|
|
}
|
|
|
|
.zddc-table__row--readonly {
|
|
color: var(--color-text-muted);
|
|
}
|
|
|
|
.zddc-table__cell {
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
border-bottom: 1px solid var(--color-border-soft, rgba(0, 0, 0, 0.06));
|
|
vertical-align: top;
|
|
cursor: cell;
|
|
/* Hide the browser's default outline; the grid pattern renders
|
|
its own selection chrome via the --selected class. */
|
|
outline: none;
|
|
}
|
|
|
|
/* Currently-selected cell — Excel-style focus ring. The 2px outset
|
|
border doesn't push surrounding cells around because outline is
|
|
used instead of border. */
|
|
.zddc-table__cell--selected {
|
|
outline: 2px solid var(--color-accent, #2868c8);
|
|
outline-offset: -2px;
|
|
background: var(--color-bg-selected, rgba(40, 104, 200, 0.08));
|
|
}
|
|
|
|
/* Cells in the multi-cell range get a fainter highlight; the focus
|
|
cell (the one with --selected) stays brighter so the anchor /
|
|
focus distinction is visible. */
|
|
.zddc-table__cell--in-range:not(.zddc-table__cell--selected) {
|
|
background: var(--color-bg-range, rgba(40, 104, 200, 0.05));
|
|
}
|
|
|
|
/* Inline cell-editor input: occupies the cell verbatim, no border so
|
|
it visually replaces the cell text. The selected outline on the
|
|
surrounding td still shows. */
|
|
.zddc-table__cell-input {
|
|
width: 100%;
|
|
box-sizing: border-box;
|
|
padding: 0;
|
|
margin: 0;
|
|
border: none;
|
|
background: var(--color-bg, #fff);
|
|
color: var(--color-text, #111);
|
|
font: inherit;
|
|
outline: none;
|
|
}
|
|
|
|
/* Row-save state markers (Phase 3). The first cell of the row gets a
|
|
left-border swatch; the row tooltip on hover surfaces the state.
|
|
Colors track the state's urgency: dirty (subtle), saving (info),
|
|
queued (warm), invalid/stale (warning), errored (alert). */
|
|
.zddc-table__row--dirty td:first-child { box-shadow: inset 3px 0 0 var(--color-info, #4a90e2); }
|
|
.zddc-table__row--saving td:first-child { box-shadow: inset 3px 0 0 var(--color-muted, #888); }
|
|
.zddc-table__row--queued td:first-child { box-shadow: inset 3px 0 0 var(--color-warm, #d4a017); }
|
|
.zddc-table__row--stale td:first-child { box-shadow: inset 3px 0 0 var(--color-warning, #e8a33d); background: var(--color-bg-warning, rgba(232, 163, 61, 0.06)); }
|
|
.zddc-table__row--invalid td:first-child { box-shadow: inset 3px 0 0 var(--color-warning, #e8a33d); }
|
|
.zddc-table__row--errored td:first-child { box-shadow: inset 3px 0 0 var(--color-error, #c14242); background: var(--color-bg-error, rgba(193, 66, 66, 0.06)); }
|
|
|
|
/* Per-cell invalid marker — small red corner triangle, Excel-style.
|
|
The hover tooltip carries the validation message via title attr. */
|
|
.zddc-table__cell--invalid {
|
|
position: relative;
|
|
}
|
|
.zddc-table__cell--invalid::after {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0;
|
|
right: 0;
|
|
width: 0;
|
|
height: 0;
|
|
border-style: solid;
|
|
border-width: 0 6px 6px 0;
|
|
border-color: transparent var(--color-error, #c14242) transparent transparent;
|
|
}
|
|
|
|
/* Status bar (table-status) when used as the stale-row prompt host. */
|
|
.table-status.table-status--prompt {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--spacing-sm);
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
background: var(--color-bg-warning, rgba(232, 163, 61, 0.08));
|
|
border: 1px solid var(--color-warning, #e8a33d);
|
|
border-radius: var(--radius-sm, 4px);
|
|
margin-bottom: var(--spacing-sm);
|
|
color: var(--color-text, #111);
|
|
}
|
|
|
|
.table-empty {
|
|
padding: var(--spacing-lg) var(--spacing-md);
|
|
text-align: center;
|
|
color: var(--color-text-muted);
|
|
font-style: italic;
|
|
}
|