diff --git a/docs/architecture/ribbon.md b/docs/architecture/ribbon.md index a0a1186..23007a3 100644 --- a/docs/architecture/ribbon.md +++ b/docs/architecture/ribbon.md @@ -84,6 +84,10 @@ at every height. A flex row cannot reserve width in part of its height, so it mo - **A reused picker anchors its panel itself**, and no prop says when it opened. `pinPanels`, one action on the band, watches for a mounted absolutely-positioned node and pins that; the ColorPicker places its own and only needs the band-aware top. +- **Insert caption sits in three tabs**: References, where both products keep it, plus the + picture/shape and Table Layout contextual tabs, which are open anyway when you caption + something. Each mounts its own `CaptionDialog`; it reads the caret for its category, so the + copies need no props of their own. - **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. diff --git a/src/lib/components/ribbon/tabs/FrameTabs.svelte b/src/lib/components/ribbon/tabs/FrameTabs.svelte index 1230354..499feff 100644 --- a/src/lib/components/ribbon/tabs/FrameTabs.svelte +++ b/src/lib/components/ribbon/tabs/FrameTabs.svelte @@ -5,6 +5,7 @@ import RibbonButton from '../RibbonButton.svelte'; import ColorPicker from '../../ColorPicker.svelte'; import ShapePicker from '../../ShapePicker.svelte'; + import CaptionDialog from '../../CaptionDialog.svelte'; import type { WrapMode } from '../../../editor/extensions/image'; import type { ShapeKind } from '../../../editor/extensions/textBox'; import { t } from '../../../i18n/i18n.svelte'; @@ -29,6 +30,8 @@ { key: 'topBottom', icon: 'wrapTopBottom', label: () => t().image.wrapTopBottom }, ]; + let captionOpen = $state(false); + function setWrap(w: WrapMode) { if (which === 'picture') editor?.chain().focus().setImageWrap(w).run(); else editor?.chain().focus().setTextBoxAttrs({ wrap: w }).run(); @@ -41,6 +44,21 @@ {/each} +
+ + + + (captionOpen = true)} + /> + + {#if which === 'picture'}
@@ -113,6 +131,8 @@ {/if} + + {#snippet fillIcon()}{/snippet} {#snippet strokeIcon()}{/snippet} diff --git a/src/lib/components/ribbon/tabs/TableTabs.svelte b/src/lib/components/ribbon/tabs/TableTabs.svelte index c6bc0f6..e727fae 100644 --- a/src/lib/components/ribbon/tabs/TableTabs.svelte +++ b/src/lib/components/ribbon/tabs/TableTabs.svelte @@ -11,6 +11,7 @@ import TableSplitDialog from '../../TableSplitDialog.svelte'; import TableSortDialog from '../../TableSortDialog.svelte'; import TableFormulaDialog from '../../TableFormulaDialog.svelte'; + import CaptionDialog from '../../CaptionDialog.svelte'; import { captionClicks, anchored, clickOutside, isMenuOpen, toggleMenu, closeMenu } from '../menu.svelte'; import { isHeaderStyled } from '../../../editor/extensions/tableHeaderRow'; import { DEFAULT_CELL_PADDING, parseCellPadding, type CellPadding } from '../../../editor/extensions/tableCellPadding'; @@ -28,6 +29,8 @@ which: 'design' | 'layout'; } = $props(); + let captionOpen = $state(false); + function run(cmd: (chain: ChainedCommands) => ChainedCommands) { if (!editor) return; cmd(editor.chain().focus()).run(); @@ -302,8 +305,25 @@ onclick={() => setNumberRecognition(!numberRecognition())} /> + +
+ + + + (captionOpen = true)} + /> + {/if} + + {#snippet shadeIcon()}{/snippet}