From fa9b3362340e6175f70884820c9419f576fbe388 Mon Sep 17 00:00:00 2001 From: Brylie Christopher Oxley Date: Sat, 12 Sep 2026 10:18:58 +0300 Subject: [PATCH 1/3] Fix redundant title control when Board's primary field is also groupBy (closes #104) When a Collection's primaryFieldKey and Board's groupBy name the same select field, the card previously showed two separate editable controls for the identical value: the title PropertyValueCell and the "Move to column" select. The title now falls back to the same plain, non-editable label already used when there's no primary field, leaving the column select as the one editable control. Calendar never had this issue since its entry title is already always non-editable plain text. Co-Authored-By: Claude Sonnet 5 --- docs/specifications/collection-views.md | 1 + src/lib/components/BoardCollectionView.svelte | 11 ++++++- .../BoardCollectionView.svelte.test.ts | 32 +++++++++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/docs/specifications/collection-views.md b/docs/specifications/collection-views.md index 79eee0e..82a25d1 100644 --- a/docs/specifications/collection-views.md +++ b/docs/specifications/collection-views.md @@ -86,6 +86,7 @@ Board and Calendar cards used to title themselves off "the first `text`-type fie - **Resolution: `resolvePrimaryField(schema, primaryFieldKey)`.** Returns the schema field named by `primaryFieldKey` when it exists and is an eligible type; otherwise falls back to the first `text` field in schema order — the exact pre-#96 implicit rule — so a Collection created before this feature, or one whose chosen primary field was since deleted or retyped away, keeps showing the same title without a migration step. The fallback only ever considers `text` fields, not every eligible type, so this returns `undefined` both when the schema has no eligible field at all and when it has eligible fields (e.g. a lone `number` or `select` field) but none of type `text` (Board/Calendar's card/entry title then reads "Untitled", same as before). Board and Calendar both call this in place of their old `schema.find(p => p.type === 'text')` line; `FieldMenu`'s primary-field indicator (next bullet) calls it too, so the star always matches what's actually titling a card. - **Eligible types: everything except `relation`.** A `relation` value is a list of record IDs with no display string of its own (data-model.md's `PropertyValue`) — every other type (`text`, `number`, `date`, `select`, `checkbox`) already renders as one displayable value via `PropertyValueCell`, so `setPrimaryField` rejects choosing a `relation` field (`ValidationError`), and a retype that turns the current primary field _into_ `relation` clears `primaryFieldKey` in the same transaction rather than leaving it pointing at a now-invalid field. Deleting the current primary field clears it the same way. Both repairs mirror the existing `deleteCollectionProperty`/`updateCollectionProperty` pattern of fixing up stale schema references in one transaction rather than leaving a dangling key for a reader to notice later. - **Display value: `primaryFieldDisplayValue(value, property)`** (`src/lib/data/views.ts`) — a plain-text rendering of any eligible type's value (a `select` value resolves through `property.options` to its label; `checkbox` renders "Checked"/empty), used for Board/Calendar's card-title `aria-label`s and Calendar's static entry-title text. Board's own card _editor_ still renders the primary field as a full `PropertyValueCell` (so whichever type is chosen — not just `text` — stays directly editable inline); this function is only for the places a plain string is needed instead of an editable cell. +- **Colliding with `groupBy` (issue #104).** `primaryFieldKey` and Board/Calendar's `groupBy` (§3) are independent choices — nothing stops a user from pointing both at the same `select`/`date` field. Calendar's entry title is _always_ the plain, non-editable `primaryFieldDisplayValue` text described above (it never renders the title as a `PropertyValueCell` at all), so this collision doesn't create a duplicate control there. Board's card title normally _is_ a directly-editable `PropertyValueCell` (previous bullet), which — only when `titleProperty.key === groupProperty.key` — would otherwise sit right above the "Move to column" `` (§3) as a second, redundant editable control for the identical value. Board resolves this by falling back to the same plain non-editable label it already uses when there's no primary field at all (`titleEditableViaCell` in `BoardCollectionView.svelte`): the "Move to column" select stays the one editable control for that value, and the title cell for every other field keeps behaving exactly as described above. Nothing prevents choosing the same field for both — there's no correctness reason to forbid it — this only changes which control is editable when the choice collides. +- **Colliding with `groupBy` or `swimlaneBy` (issue #104).** `primaryFieldKey` and Board/Calendar's `groupBy` (§3), or Board's `swimlaneBy`, are independent choices — nothing stops a user from pointing any of them at the same `select`/`date` field. Calendar's entry title is _always_ the plain, non-editable `primaryFieldDisplayValue` text described above (it never renders the title as a `PropertyValueCell` at all), so this collision doesn't create a duplicate control there. Board's card title normally _is_ a directly-editable `PropertyValueCell` (previous bullet), which — when `titleProperty.key` matches either `groupProperty.key` or `swimlaneProperty.key` — would otherwise sit right above the matching "Move to column"/"Move to swimlane" `` (a keyboard/screen-reader-accessible alternative to drag-and-drop), or the card's own field editor if the grouping property is also a visible field — updates that record's existing grouping property (`setCollectionCell`); it is never a reorder of Collection membership or a record copy. Manual sort mode (`config.sort.mode === 'manual'`, the default) additionally supports dragging a card to a specific position within a column — that ordering is **session-local component state** (`manualOrder: Record`), not written to the block or the Collection (see §8 for why manual order specifically stays ephemeral even though the rest of `viewConfig` is now a draft that gets explicitly saved — §9). +**Board** (`BoardCollectionView.svelte`) — `config.groupBy` names the `select` property driving columns; a dropdown lets the user switch it if the schema has more than one, or the collection gets prompted to add one (`appendCollectionField`) if it has none, rather than silently rendering nothing. Cards show the Collection's resolved primary field (§7) as the title — normally a directly-editable `PropertyValueCell`, but a plain non-editable label when that field is also `groupBy` or `swimlaneBy` (§7's issue #104 collision handling) — plus whatever other schema properties are in `visibleProperties`. Moving a card between columns — via native HTML5 drag-and-drop, a card's own "Move to" `` and a parallel "Move to swimlane" `