feat: the ribbon's Insert tab carries the zone options

The two variant flags (a first page of its own, separate even pages) and the
header/footer distances were reachable only from the expanded toolbar. The
ribbon mode now has them in an Options menu beside the zone buttons.
This commit is contained in:
Steffen Becker committed 2026-09-06 19:07:23 +02:00
1 parent cb39ef018c
commit 6d4a7e08df
6 files changed
+59 -3

No files matched your search

+6
View File
@@ -93,6 +93,12 @@ at every height. A flex row cannot reserve width in part of its height, so it mo
- **Each wrapper names the menu it owns.** One shared open-menu id means every `clickOutside`
sees every mousedown; without the id each wrapper would close a sibling's open menu, and the
click on one of its rows would never land.
- **The header/footer switches ride the Insert tab's Options menu.** Different first page,
different odd/even and the two zone distances used to be in `ToolbarExpanded` alone, so the
default chrome could reach the first-page and even-page variants only through an import.
- **Two inputs wear `.file-input`**: the document one in `App.svelte` and the Insert tab's
picture one. The ribbon's comes first in the DOM, so a bare class selector opens a picture
chooser whenever that tab is showing — the browser runs qualify theirs by `accept`.
## Pieces
+3
View File
@@ -1295,6 +1295,9 @@
bind:pageOrientation
bind:pageFormat
bind:extraHfSections
bind:hfDistances
bind:differentFirstPage
bind:differentOddEven
bind:hyphenate
bind:pageNumbering
bind:pageDecor
+8 -2
View File
@@ -25,7 +25,7 @@
import { DEFAULT_MARGINS, type PageMargins } from '../../storage/pageMargins';
import type { Orientation } from '../../storage/pageOrientation';
import type { PageFormat } from '../../storage/pageFormat';
import type { HfSet, HfZone } from '../../storage/headerFooter';
import { DEFAULT_HF_DISTANCES, type HfDistances, type HfSet, type HfZone } from '../../storage/headerFooter';
import type { DocumentLanguage } from '../../storage/documentLanguage';
import { DEFAULT_TAB_INTERVAL_CM } from '../../storage/tabInterval';
import { DEFAULT_PAGE_NUMBERING, type PageNumbering } from '../../storage/pageNumbering';
@@ -53,6 +53,9 @@
pageOrientation = $bindable<Orientation>('portrait'),
pageFormat = $bindable<PageFormat>('A4'),
extraHfSections = $bindable<HfSet[]>([]),
hfDistances = $bindable(DEFAULT_HF_DISTANCES),
differentFirstPage = $bindable(false),
differentOddEven = $bindable(false),
hyphenate = $bindable(false),
pageNumbering = $bindable(DEFAULT_PAGE_NUMBERING),
pageDecor = $bindable(EMPTY_PAGE_DECOR),
@@ -93,6 +96,9 @@
pageOrientation?: Orientation;
pageFormat?: PageFormat;
extraHfSections?: HfSet[];
hfDistances?: HfDistances;
differentFirstPage?: boolean;
differentOddEven?: boolean;
hyphenate?: boolean;
pageNumbering?: PageNumbering;
pageDecor?: PageDecor;
@@ -358,7 +364,7 @@
{#if tab === 'home'}
<HomeTab {editor} {tick} bind:showFormattingMarks {onManageStyles} {onFind} onParagraphDialog={() => (paragraphDialogOpen = true)} />
{:else if tab === 'insert'}
<InsertTab {editor} {tick} {hfActive} {pageMargins} {pageOrientation} {pageFormat} {onEditZone} {onManageTableStyles} {onAutoText} />
<InsertTab {editor} {tick} {hfActive} {pageMargins} {pageOrientation} {pageFormat} bind:hfDistances bind:differentFirstPage bind:differentOddEven {onEditZone} {onManageTableStyles} {onAutoText} />
{:else if tab === 'layout'}
<LayoutTab {editor} {tick} {hfActive} bind:pageMargins bind:pageOrientation bind:pageFormat bind:extraHfSections bind:hyphenate bind:pageNumbering bind:pageDecor bind:lineNumbering bind:foldMarks onParagraphDialog={() => (paragraphDialogOpen = true)} />
{:else if tab === 'references'}
@@ -18,13 +18,16 @@
import { pageDimsCm, type PageFormat } from '../../../storage/pageFormat';
import { cmToPx, type PageMargins } from '../../../storage/pageMargins';
import type { Orientation } from '../../../storage/pageOrientation';
import type { HfZone } from '../../../storage/headerFooter';
import { clampHfDistance, DEFAULT_HF_DISTANCES, type HfDistances, type HfZone } from '../../../storage/headerFooter';
import type { StyleFamily } from '../../../styles/styleSheet';
import { t } from '../../../i18n/i18n.svelte';
import { shortcutHint } from '../../../editor/shortcuts';
let {
editor, tick, hfActive = null, pageMargins, pageOrientation, pageFormat,
hfDistances = $bindable(DEFAULT_HF_DISTANCES),
differentFirstPage = $bindable(false),
differentOddEven = $bindable(false),
onEditZone, onManageTableStyles, onAutoText,
}: {
editor: Editor | null;
@@ -33,6 +36,9 @@
pageMargins: PageMargins;
pageOrientation: Orientation;
pageFormat: PageFormat;
hfDistances?: HfDistances;
differentFirstPage?: boolean;
differentOddEven?: boolean;
onEditZone?: (zone: HfZone) => void;
onManageTableStyles?: (family: StyleFamily) => void;
onAutoText?: () => void;
@@ -254,6 +260,35 @@
</div>
{/if}
</div>
<div class="rb-menu-wrap" use:clickOutside={'hfOptions'}>
<RibbonButton variant="big" icon="settings" label={t().ribbon.hfOptions} title={t().toolbarExpanded.headerFooter} caret active={isMenuOpen('hfOptions')} onclick={() => toggleMenu('hfOptions')} />
{#if isMenuOpen('hfOptions')}
<div class="ribbon-menu" use:anchored role="menu">
<label class="check-row" title={t().toolbarExpanded.differentFirstPageHint}>
<input type="checkbox" bind:checked={differentFirstPage} />
{t().toolbarExpanded.differentFirstPage}
</label>
<label class="check-row" title={t().toolbarExpanded.differentOddEvenHint}>
<input type="checkbox" bind:checked={differentOddEven} />
{t().toolbarExpanded.differentOddEven}
</label>
<div class="rb-menu-label">{t().toolbarExpanded.position}</div>
{#each ['header', 'footer'] as const as axis}
<label class="num-row">
<span>{t().toolbarExpanded.hfDist[axis]}</span>
<input
type="number"
min="0"
max="10"
step="0.1"
value={hfDistances[axis]}
onchange={(e) => (hfDistances = { ...hfDistances, [axis]: clampHfDistance(Number(e.currentTarget.value)) })}
/>
</label>
{/each}
</div>
{/if}
</div>
</RibbonGroup>
<div class="ribbon-sep"></div>
@@ -332,6 +367,10 @@
.rb-menu-wrap { position: relative; }
.check-row { display: flex; align-items: center; gap: 6px; padding: 2px 12px 6px; white-space: nowrap; }
.num-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 2px 12px 6px; white-space: nowrap; }
.num-row input { width: 72px; }
.file-input {
position: absolute;
width: 0;
+1
View File
@@ -969,6 +969,7 @@ const de: Messages = {
equation: 'Formel',
header: 'Kopfzeile',
footer: 'Fußzeile',
hfOptions: 'Optionen',
link: 'Link',
bookmark: 'Textmarke',
table: 'Tabelle',
+1
View File
@@ -975,6 +975,7 @@ const en = {
equation: 'Equation',
header: 'Header',
footer: 'Footer',
hfOptions: 'Options',
link: 'Link',
bookmark: 'Bookmark',
table: 'Table',