From 71ffa5d1be787ef238dfd2a4615ce61d02d8d61b Mon Sep 17 00:00:00 2001 From: ajnart Date: Sun, 23 Aug 2026 00:59:02 +0200 Subject: [PATCH 1/3] fix: keep responsive layout editors stable --- .../app/[locale]/boards/[name]/settings/_layout.tsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/apps/nextjs/src/app/[locale]/boards/[name]/settings/_layout.tsx b/apps/nextjs/src/app/[locale]/boards/[name]/settings/_layout.tsx index 20bd78ba3..abc2ac077 100644 --- a/apps/nextjs/src/app/[locale]/boards/[name]/settings/_layout.tsx +++ b/apps/nextjs/src/app/[locale]/boards/[name]/settings/_layout.tsx @@ -111,9 +111,6 @@ export const LayoutSettingsContent = ({ board, form, isSaving, saveSettingsAsync const nextBreakpoint = getNextCustomBreakpoint(form.values.layouts); const baseLayout = form.values.layouts.find((layout) => layout.role === "base"); - const sortedLayouts = form.values.layouts - .map((layout, index) => ({ layout, index })) - .toSorted((entryA, entryB) => entryA.layout.breakpoint - entryB.layout.breakpoint); return ( @@ -129,7 +126,7 @@ export const LayoutSettingsContent = ({ board, form, isSaving, saveSettingsAsync disabled={nextBreakpoint === null || !baseLayout} onClick={() => { if (nextBreakpoint === null || !baseLayout) return; - form.setFieldValue("layouts", [ + const layouts = [ ...form.values.layouts, { id: createId(), @@ -140,14 +137,15 @@ export const LayoutSettingsContent = ({ board, form, isSaving, saveSettingsAsync breakpoint: nextBreakpoint, role: "custom", }, - ]); + ].toSorted((layoutA, layoutB) => layoutA.breakpoint - layoutB.breakpoint); + form.setFieldValue("layouts", layouts); }} > {tBoard("setting.section.layout.responsive.action.add")} - {sortedLayouts.map(({ layout, index }) => { + {form.values.layouts.map((layout, index) => { const persistedLayout = board.layouts.find((candidate) => candidate.id === layout.id); const sourceLayout = persistedLayout ?? board.layouts.find((candidate) => candidate.role === "base"); const customBreakpoints = form.values.layouts From 9eafd4f7a7f60f6a62840aacb857c265d0e2d244 Mon Sep 17 00:00:00 2001 From: ajnart Date: Sun, 23 Aug 2026 01:13:00 +0200 Subject: [PATCH 2/3] fix: preserve custom layout role type --- apps/nextjs/src/app/[locale]/boards/[name]/settings/_layout.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/nextjs/src/app/[locale]/boards/[name]/settings/_layout.tsx b/apps/nextjs/src/app/[locale]/boards/[name]/settings/_layout.tsx index abc2ac077..ffdf2f80e 100644 --- a/apps/nextjs/src/app/[locale]/boards/[name]/settings/_layout.tsx +++ b/apps/nextjs/src/app/[locale]/boards/[name]/settings/_layout.tsx @@ -135,7 +135,7 @@ export const LayoutSettingsContent = ({ board, form, isSaving, saveSettingsAsync leftGutterColumnCount: baseLayout.leftGutterColumnCount, rightGutterColumnCount: baseLayout.rightGutterColumnCount, breakpoint: nextBreakpoint, - role: "custom", + role: "custom" as const, }, ].toSorted((layoutA, layoutB) => layoutA.breakpoint - layoutB.breakpoint); form.setFieldValue("layouts", layouts); From b6fc27e44d4e62f3ebe993ef3b57359e6e6c8a04 Mon Sep 17 00:00:00 2001 From: ajnart Date: Sun, 23 Aug 2026 01:24:36 +0200 Subject: [PATCH 3/3] fix: preserve layout validation errors --- .../boards/[name]/settings/_layout.tsx | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/apps/nextjs/src/app/[locale]/boards/[name]/settings/_layout.tsx b/apps/nextjs/src/app/[locale]/boards/[name]/settings/_layout.tsx index ffdf2f80e..64f36ea03 100644 --- a/apps/nextjs/src/app/[locale]/boards/[name]/settings/_layout.tsx +++ b/apps/nextjs/src/app/[locale]/boards/[name]/settings/_layout.tsx @@ -126,19 +126,18 @@ export const LayoutSettingsContent = ({ board, form, isSaving, saveSettingsAsync disabled={nextBreakpoint === null || !baseLayout} onClick={() => { if (nextBreakpoint === null || !baseLayout) return; - const layouts = [ - ...form.values.layouts, - { - id: createId(), - name: tBoard("setting.section.layout.custom.defaultName"), - columnCount: baseLayout.columnCount, - leftGutterColumnCount: baseLayout.leftGutterColumnCount, - rightGutterColumnCount: baseLayout.rightGutterColumnCount, - breakpoint: nextBreakpoint, - role: "custom" as const, - }, - ].toSorted((layoutA, layoutB) => layoutA.breakpoint - layoutB.breakpoint); - form.setFieldValue("layouts", layouts); + const newLayout: FormValues["layouts"][number] = { + id: createId(), + name: tBoard("setting.section.layout.custom.defaultName"), + columnCount: baseLayout.columnCount, + leftGutterColumnCount: baseLayout.leftGutterColumnCount, + rightGutterColumnCount: baseLayout.rightGutterColumnCount, + breakpoint: nextBreakpoint, + role: "custom", + }; + let insertionIndex = form.values.layouts.findIndex((layout) => layout.breakpoint > nextBreakpoint); + if (insertionIndex === -1) insertionIndex = form.values.layouts.length; + form.insertListItem("layouts", newLayout, insertionIndex); }} > {tBoard("setting.section.layout.responsive.action.add")}