feat(notes): swap heading and paragraph fonts with a button
All checks were successful
CI / verify (push) Successful in 1m7s

Replace the heading/paragraph toggle with the intended UX: keep both font
dropdowns visible and add a button that exchanges the two fonts via a new
swapFonts store action.
This commit is contained in:
Julien Calixte
2026-06-30 00:47:10 +02:00
parent 30eadaf21b
commit 84a252e926
3 changed files with 63 additions and 36 deletions

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import { computed, ref } from "vue"
import { computed } from "vue"
import ThemeSwap from "@/components/ThemeSwap.vue"
@@ -32,15 +32,13 @@ const sortedFontFamilies = computed(() => {
})
const fontSizes = Array.from({ length: 7 }, (_, i) => `${9 + i * 2}pt`)
// false → the dropdown edits the heading font, true → the paragraph font.
const editingBody = ref(false)
const activeFont = computed({
get: () =>
editingBody.value
? store.userSettings?.chosenBodyFont
: store.userSettings?.chosenHeadingFont,
set: (value) =>
editingBody.value ? store.setBodyFont(value!) : store.setHeadingFont(value!)
const headingFont = computed({
get: () => store.userSettings?.chosenHeadingFont,
set: (value) => store.setHeadingFont(value!)
})
const bodyFont = computed({
get: () => store.userSettings?.chosenBodyFont,
set: (value) => store.setBodyFont(value!)
})
const fontSize = computed({
get: () => store.userSettings?.chosenFontSize,
@@ -51,22 +49,37 @@ const fontSize = computed({
<template>
<div class="font-change">
<div>
<label class="font-toggle">
<span class="font-label" :class="{ active: !editingBody }">h</span>
<input
type="checkbox"
class="toggle toggle-sm"
v-model="editingBody"
aria-label="Switch between heading and paragraph font"
/>
<span class="font-label" :class="{ active: editingBody }">p</span>
</label>
<select
id="font-target"
class="select"
v-model="activeFont"
:aria-label="editingBody ? 'Paragraph font' : 'Heading font'"
<label for="heading-font" class="font-label">h</label>
<select id="heading-font" class="select" v-model="headingFont">
<option v-for="font in sortedFontFamilies" :key="font" :value="font">
{{ font }}
</option>
</select>
<button
type="button"
class="btn btn-ghost btn-sm btn-circle"
aria-label="Swap heading and paragraph fonts"
@click="store.swapFonts()"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M7 10h14l-4 -4" />
<path d="M17 14h-14l4 4" />
</svg>
</button>
<label for="body-font" class="font-label">p</label>
<select id="body-font" class="select" v-model="bodyFont">
<option v-for="font in sortedFontFamilies" :key="font" :value="font">
{{ font }}
</option>
@@ -101,17 +114,6 @@ const fontSize = computed({
}
}
.font-toggle {
display: flex;
align-items: center;
gap: 0.5rem;
cursor: pointer;
.font-label.active {
opacity: 1;
}
}
.font-label {
font-weight: bold;
font-size: 0.75rem;

View File

@@ -147,6 +147,24 @@ describe("userRepo store — synchronous mutations", () => {
expect(persisted.chosenHeadingFont).toBe("Serif")
expect(persisted.chosenBodyFont).toBe("Sans")
})
it("swapFonts exchanges the heading and body fonts and persists", () => {
const store = useUserRepoStore()
store.user = "alice"
store.repo = "notes"
store.setHeadingFont("Lora")
store.setBodyFont("Inter")
store.swapFonts()
expect(store.userSettings?.chosenHeadingFont).toBe("Inter")
expect(store.userSettings?.chosenBodyFont).toBe("Lora")
const persisted = JSON.parse(
localStorage.getItem("remanso:layout:alice:notes") as string
)
expect(persisted.chosenHeadingFont).toBe("Inter")
expect(persisted.chosenBodyFont).toBe("Lora")
})
})
describe("userRepo store — setUserRepo", () => {

View File

@@ -317,6 +317,13 @@ export const useUserRepoStore = defineStore("USER_REPO_STATE", {
}
this.userSettings.chosenBodyFont = font
this._persistLayout()
},
swapFonts() {
if (!this.userSettings) return
const { chosenHeadingFont, chosenBodyFont } = this.userSettings
this.userSettings.chosenHeadingFont = chosenBodyFont
this.userSettings.chosenBodyFont = chosenHeadingFont
this._persistLayout()
}
}
})