( doc: DatabaseDoc, viewId: string, fieldId: string, direction: 'left' | 'right' )
| 169 | * or for a non-table view. The stored order is normalized first (missing fields |
| 170 | * appended, stale ids dropped) so it stays consistent with `doc.fields`. */ |
| 171 | export function moveColumn( |
| 172 | doc: DatabaseDoc, |
| 173 | viewId: string, |
| 174 | fieldId: string, |
| 175 | direction: 'left' | 'right' |
| 176 | ): DatabaseDoc { |
| 177 | const view = doc.views.find((v) => v.id === viewId) |
| 178 | if (!view || view.type !== 'table') return doc |
| 179 | const fieldIds = doc.fields.map((f) => f.id) |
| 180 | const stored = view.columnOrder ?? fieldIds |
| 181 | const order = [...stored, ...fieldIds.filter((id) => !stored.includes(id))].filter((id) => |
| 182 | fieldIds.includes(id) |
| 183 | ) |
| 184 | const hidden = new Set(view.hiddenFieldIds ?? []) |
| 185 | const visible = order.filter((id) => !hidden.has(id)) |
| 186 | const vi = visible.indexOf(fieldId) |
| 187 | if (vi === -1) return doc |
| 188 | const vj = direction === 'left' ? vi - 1 : vi + 1 |
| 189 | if (vj < 0 || vj >= visible.length) return doc // already at the edge |
| 190 | const oi = order.indexOf(fieldId) |
| 191 | const oj = order.indexOf(visible[vj]) |
| 192 | const next = [...order] |
| 193 | ;[next[oi], next[oj]] = [next[oj], next[oi]] |
| 194 | return updateView(doc, viewId, { columnOrder: next }) |
| 195 | } |
| 196 | |
| 197 | /** Move `fieldId` to sit immediately before `targetFieldId` in a table view's |
| 198 | * column order — used by header drag-and-drop (#317). No-op for a non-table |
no test coverage detected