@blockslides/extension-bubble-menu-preset 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +65 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +65 -0
- package/dist/index.js.map +1 -1
- package/package.json +9 -9
- package/src/bubble-menu-preset.ts +76 -0
- package/src/index.ts +1 -0
package/dist/index.cjs
CHANGED
|
@@ -222,6 +222,44 @@ function buildMenuElement(editor, opts) {
|
|
|
222
222
|
element.tabIndex = 0;
|
|
223
223
|
const toolbar = document.createElement("div");
|
|
224
224
|
toolbar.className = "bs-bmp-toolbar";
|
|
225
|
+
let fontFamilySelect = null;
|
|
226
|
+
let fontSizeSelect = null;
|
|
227
|
+
const normalizeFontFamily = (value) => {
|
|
228
|
+
var _a2;
|
|
229
|
+
if (!value) return value;
|
|
230
|
+
return ((_a2 = value.split(",")[0]) == null ? void 0 : _a2.trim().replace(/^['"]|['"]$/g, "")) || value;
|
|
231
|
+
};
|
|
232
|
+
const getComputedFontStyles = () => {
|
|
233
|
+
var _a2;
|
|
234
|
+
if (typeof window === "undefined") return { fontFamily: void 0, fontSize: void 0 };
|
|
235
|
+
const sel = window.getSelection();
|
|
236
|
+
const node = sel == null ? void 0 : sel.anchorNode;
|
|
237
|
+
const el = (_a2 = node instanceof HTMLElement ? node : node == null ? void 0 : node.parentElement) != null ? _a2 : void 0;
|
|
238
|
+
if (!el) return { fontFamily: void 0, fontSize: void 0 };
|
|
239
|
+
const styles = window.getComputedStyle(el);
|
|
240
|
+
return {
|
|
241
|
+
fontFamily: normalizeFontFamily(styles.fontFamily || void 0),
|
|
242
|
+
fontSize: styles.fontSize || void 0
|
|
243
|
+
};
|
|
244
|
+
};
|
|
245
|
+
const ensureOptionExists = (select, value) => {
|
|
246
|
+
const exists = Array.from(select.options).some((opt) => opt.value === value);
|
|
247
|
+
if (!exists) {
|
|
248
|
+
const option = document.createElement("option");
|
|
249
|
+
option.value = value;
|
|
250
|
+
option.textContent = value;
|
|
251
|
+
select.appendChild(option);
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
const setSelectValue = (select, value) => {
|
|
255
|
+
if (!select) return;
|
|
256
|
+
if (!value) {
|
|
257
|
+
select.selectedIndex = -1;
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
ensureOptionExists(select, value);
|
|
261
|
+
select.value = value;
|
|
262
|
+
};
|
|
225
263
|
const popovers = [];
|
|
226
264
|
const cleanupFns = [];
|
|
227
265
|
const getCommand = (name) => {
|
|
@@ -295,6 +333,7 @@ function buildMenuElement(editor, opts) {
|
|
|
295
333
|
runWithFocus(() => runChainCommand("setFontFamily", select.value));
|
|
296
334
|
(_b = (_a2 = editor.commands).setMeta) == null ? void 0 : _b.call(_a2, "bubbleMenu", "updatePosition");
|
|
297
335
|
});
|
|
336
|
+
fontFamilySelect = select;
|
|
298
337
|
wrapper.appendChild(select);
|
|
299
338
|
toolbar.appendChild(wrapper);
|
|
300
339
|
};
|
|
@@ -317,6 +356,7 @@ function buildMenuElement(editor, opts) {
|
|
|
317
356
|
runWithFocus(() => runChainCommand("setFontSize", select.value));
|
|
318
357
|
(_b = (_a2 = editor.commands).setMeta) == null ? void 0 : _b.call(_a2, "bubbleMenu", "updatePosition");
|
|
319
358
|
});
|
|
359
|
+
fontSizeSelect = select;
|
|
320
360
|
wrapper.appendChild(select);
|
|
321
361
|
toolbar.appendChild(wrapper);
|
|
322
362
|
};
|
|
@@ -480,6 +520,31 @@ function buildMenuElement(editor, opts) {
|
|
|
480
520
|
}
|
|
481
521
|
});
|
|
482
522
|
element.appendChild(toolbar);
|
|
523
|
+
const syncSelectionState = () => {
|
|
524
|
+
const attrs = editor.getAttributes("textStyle") || {};
|
|
525
|
+
let family = attrs.fontFamily;
|
|
526
|
+
let size = attrs.fontSize;
|
|
527
|
+
if (!family || !size) {
|
|
528
|
+
const computed = getComputedFontStyles();
|
|
529
|
+
family = family || computed.fontFamily;
|
|
530
|
+
size = size || computed.fontSize;
|
|
531
|
+
}
|
|
532
|
+
setSelectValue(fontFamilySelect, normalizeFontFamily(family));
|
|
533
|
+
setSelectValue(fontSizeSelect, size);
|
|
534
|
+
};
|
|
535
|
+
const handleSelectionUpdate = () => syncSelectionState();
|
|
536
|
+
const handleTransaction = ({ transaction }) => {
|
|
537
|
+
if (!transaction || transaction.docChanged || transaction.selectionSet) {
|
|
538
|
+
syncSelectionState();
|
|
539
|
+
}
|
|
540
|
+
};
|
|
541
|
+
editor.on("selectionUpdate", handleSelectionUpdate);
|
|
542
|
+
editor.on("transaction", handleTransaction);
|
|
543
|
+
cleanupFns.push(() => {
|
|
544
|
+
editor.off("selectionUpdate", handleSelectionUpdate);
|
|
545
|
+
editor.off("transaction", handleTransaction);
|
|
546
|
+
});
|
|
547
|
+
syncSelectionState();
|
|
483
548
|
return {
|
|
484
549
|
element,
|
|
485
550
|
cleanup: () => {
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/bubble-menu-preset.ts"],"sourcesContent":["export * from './bubble-menu-preset.js'\nexport { BubbleMenuPreset as default } from './bubble-menu-preset.js'\n\n\n\n","import { Extension } from '@blockslides/core'\nimport type { Editor } from '@blockslides/core'\nimport {\n BubbleMenuPlugin,\n type BubbleMenuPluginProps,\n type BubbleMenuOptions,\n} from '@blockslides/extension-bubble-menu'\n\ntype BubbleMenuPresetItem =\n | 'undo'\n | 'redo'\n | 'fontFamily'\n | 'fontSize'\n | 'bold'\n | 'italic'\n | 'underline'\n | 'textColor'\n | 'highlightColor'\n | 'link'\n | 'align'\n\ntype TextAlignValue = 'left' | 'center' | 'right' | 'justify'\n\nexport interface BubbleMenuPresetOptions\n extends Omit<BubbleMenuOptions, 'element'> {\n /**\n * Optional custom element to use for the menu. If omitted, a default\n * element with built-in buttons is rendered.\n */\n element?: HTMLElement | null\n\n /**\n * Order of built-in controls to render.\n */\n items?: BubbleMenuPresetItem[]\n\n /**\n * Additional class names to attach to the menu element to allow easy\n * style overrides.\n */\n className?: string\n\n /**\n * Inject default CSS. Set to false to opt out if you provide your own styles.\n */\n injectStyles?: boolean\n\n /**\n * Palette for text color swatches.\n */\n textColors?: string[]\n\n /**\n * Palette for highlight swatches.\n */\n highlightColors?: string[]\n\n /**\n * Fonts exposed in the font picker.\n */\n fonts?: string[]\n\n /**\n * Font sizes (any CSS length, e.g. \"16px\", \"1rem\").\n */\n fontSizes?: string[]\n\n /**\n * Alignments to expose in the align control.\n */\n alignments?: TextAlignValue[]\n}\n\ntype Cleanup = () => void\n\nconst STYLE_ID = 'blockslides-bubble-menu-preset-styles'\n\nconst DEFAULT_ITEMS: BubbleMenuPresetItem[] = [\n 'undo',\n 'redo',\n 'fontFamily',\n 'fontSize',\n 'bold',\n 'italic',\n 'underline',\n 'textColor',\n 'highlightColor',\n 'link',\n 'align',\n]\n\nconst DEFAULT_FONTS = [\n 'Inter',\n 'Arial',\n 'Helvetica',\n 'Times New Roman',\n 'Georgia',\n 'Courier New',\n 'Monaco',\n]\n\nconst DEFAULT_FONT_SIZES = ['12px', '14px', '16px', '18px', '20px', '24px', '32px', '40px']\n\nconst DEFAULT_ALIGNMENTS: TextAlignValue[] = ['left', 'center', 'right', 'justify']\n\n// A rich palette approximating the attached reference.\nconst DEFAULT_COLOR_PALETTE: string[] = [\n '#000000',\n '#434343',\n '#666666',\n '#999999',\n '#b7b7b7',\n '#cccccc',\n '#d9d9d9',\n '#efefef',\n '#f3f3f3',\n '#ffffff',\n '#e60000',\n '#ff0000',\n '#ff9900',\n '#ffff00',\n '#00ff00',\n '#00ffff',\n '#4a86e8',\n '#0000ff',\n '#9900ff',\n '#ff00ff',\n '#f4cccc',\n '#fce5cd',\n '#fff2cc',\n '#d9ead3',\n '#d0e0e3',\n '#cfe2f3',\n '#d9d2e9',\n '#ead1dc',\n '#f9cb9c',\n '#ffe599',\n '#b6d7a8',\n '#a2c4c9',\n '#9fc5e8',\n '#b4a7d6',\n '#d5a6bd',\n '#e06666',\n '#f6b26b',\n '#ffd966',\n '#93c47d',\n '#76a5af',\n '#6fa8dc',\n '#8e7cc3',\n '#c27ba0',\n '#cc0000',\n '#e69138',\n '#f1c232',\n '#6aa84f',\n '#45818e',\n '#3d85c6',\n '#674ea7',\n '#a64d79',\n '#990000',\n '#b45f06',\n '#bf9000',\n '#38761d',\n '#134f5c',\n '#0b5394',\n '#351c75',\n '#741b47',\n '#660000',\n '#783f04',\n '#7f6000',\n '#274e13',\n '#0c343d',\n '#073763',\n '#20124d',\n '#4c1130',\n]\n\nconst DEFAULT_HIGHLIGHT_PALETTE = DEFAULT_COLOR_PALETTE\n\nconst DEFAULT_LABELS: Record<BubbleMenuPresetItem, string> = {\n undo: 'Undo',\n redo: 'Redo',\n fontFamily: 'Font',\n fontSize: 'Size',\n bold: 'B',\n italic: 'I',\n underline: 'U',\n textColor: 'A',\n highlightColor: 'Hi',\n link: 'Link',\n align: 'Align',\n}\n\nconst ICONS = {\n textColor: `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M5 20h14\"/><path d=\"M7 20l5-14 5 14\"/><path d=\"M10.7 14h2.6\"/></svg>`,\n highlightColor: `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m11 18 2-2h6\"/><path d=\"m2 22 3-3h6l3-3-6-6-3 3v6z\"/><path d=\"M14 7l3-3 3 3-3 3z\"/></svg>`,\n}\n\ninterface MenuBuildResult {\n element: HTMLElement\n cleanup: Cleanup\n}\n\nexport const BubbleMenuPreset = Extension.create<BubbleMenuPresetOptions>({\n name: 'bubbleMenuPreset',\n\n addOptions() {\n return {\n element: null,\n items: DEFAULT_ITEMS,\n className: '',\n injectStyles: true,\n textColors: DEFAULT_COLOR_PALETTE,\n highlightColors: DEFAULT_HIGHLIGHT_PALETTE,\n fonts: DEFAULT_FONTS,\n fontSizes: DEFAULT_FONT_SIZES,\n alignments: DEFAULT_ALIGNMENTS,\n pluginKey: 'bubbleMenuPreset',\n updateDelay: 250,\n resizeDelay: 60,\n appendTo: undefined,\n shouldShow: null,\n options: {\n placement: 'top',\n strategy: 'absolute',\n offset: 8,\n flip: {},\n shift: {},\n },\n }\n },\n\n addProseMirrorPlugins() {\n const options = this.options\n const editor = this.editor\n\n const usingCustomElement = !!options.element\n const { element, cleanup } =\n options.element && typeof document !== 'undefined'\n ? { element: options.element, cleanup: () => {} }\n : buildMenuElement(editor, {\n items: options.items ?? DEFAULT_ITEMS,\n className: options.className ?? '',\n injectStyles: options.injectStyles !== false,\n textColors: options.textColors ?? DEFAULT_COLOR_PALETTE,\n highlightColors: options.highlightColors ?? DEFAULT_HIGHLIGHT_PALETTE,\n fonts: options.fonts ?? DEFAULT_FONTS,\n fontSizes: options.fontSizes ?? DEFAULT_FONT_SIZES,\n alignments: options.alignments ?? DEFAULT_ALIGNMENTS,\n })\n\n this.storage.element = element\n this.storage.cleanup = cleanup\n this.storage.usingCustomElement = usingCustomElement\n\n const pluginOptions: BubbleMenuPluginProps = {\n pluginKey: options.pluginKey ?? 'bubbleMenuPreset',\n editor,\n element: element,\n updateDelay: options.updateDelay,\n resizeDelay: options.resizeDelay,\n appendTo: options.appendTo,\n options: options.options,\n getReferencedVirtualElement: options.getReferencedVirtualElement,\n shouldShow: options.shouldShow ?? undefined,\n }\n\n return [BubbleMenuPlugin(pluginOptions)]\n },\n\n onDestroy() {\n const el = this.storage.element as HTMLElement | undefined\n const cleanup = this.storage.cleanup as Cleanup | undefined\n const usingCustomElement = this.storage.usingCustomElement as boolean | undefined\n if (cleanup) {\n cleanup()\n }\n if (el && !usingCustomElement) {\n el.remove()\n }\n },\n})\n\nfunction buildMenuElement(\n editor: Editor,\n opts: {\n items: BubbleMenuPresetItem[]\n className: string\n injectStyles: boolean\n textColors: string[]\n highlightColors: string[]\n fonts: string[]\n fontSizes: string[]\n alignments: TextAlignValue[]\n },\n): MenuBuildResult {\n if (opts.injectStyles) {\n injectStyles()\n }\n\n const element = document.createElement('div')\n element.className = `bs-bubble-menu-preset ${opts.className ?? ''}`.trim()\n element.setAttribute('data-bubble-menu-preset', 'true')\n element.tabIndex = 0\n\n const toolbar = document.createElement('div')\n toolbar.className = 'bs-bmp-toolbar'\n\n const popovers: HTMLElement[] = []\n const cleanupFns: Cleanup[] = []\n\n const getCommand = (name: string): any => (editor.commands as any)?.[name]\n const runChainCommand = (name: string, ...args: any[]) => {\n const chain = (editor.chain as any)?.()\n if (!chain || typeof chain[name] !== 'function') return false\n const runner = typeof chain.focus === 'function' ? chain.focus() : chain\n if (typeof runner[name] !== 'function') return false\n return runner[name](...args).run?.() ?? false\n }\n\n const closePopovers = () => {\n popovers.forEach((p) => p.classList.add('bs-bmp-hidden'))\n }\n\n const runWithFocus = (fn?: () => boolean) => {\n if (!fn) return false\n return !!fn()\n }\n\n const addButton = (\n item: BubbleMenuPresetItem,\n label: string,\n onClick: () => void,\n opts: { disabled?: boolean; title?: string } = {},\n ) => {\n const btn = document.createElement('button')\n btn.type = 'button'\n btn.className = `bs-bmp-btn bs-bmp-btn-${item}`\n btn.textContent = label\n if (opts.title) {\n btn.title = opts.title\n btn.setAttribute('aria-label', opts.title)\n }\n if (opts.disabled) {\n btn.disabled = true\n btn.classList.add('bs-bmp-disabled')\n } else {\n btn.addEventListener('click', () => {\n closePopovers()\n onClick()\n })\n }\n toolbar.appendChild(btn)\n }\n\n const addUndoRedo = () => {\n const hasUndo = typeof getCommand('undo') === 'function'\n const hasRedo = typeof getCommand('redo') === 'function'\n addButton('undo', DEFAULT_LABELS.undo, () => runWithFocus(() => runChainCommand('undo')), {\n disabled: !hasUndo,\n title: 'Undo',\n })\n addButton('redo', DEFAULT_LABELS.redo, () => runWithFocus(() => runChainCommand('redo')), {\n disabled: !hasRedo,\n title: 'Redo',\n })\n }\n\n const addFontFamily = () => {\n const hasCommand = typeof getCommand('setFontFamily') === 'function'\n const wrapper = document.createElement('div')\n wrapper.className = 'bs-bmp-select'\n const select = document.createElement('select')\n select.className = 'bs-bmp-select-input'\n select.title = 'Font family'\n opts.fonts.forEach((font) => {\n const option = document.createElement('option')\n option.value = font\n option.textContent = font\n option.style.fontFamily = font\n select.appendChild(option)\n })\n select.disabled = !hasCommand\n select.addEventListener('change', () => {\n runWithFocus(() => runChainCommand('setFontFamily', select.value))\n editor.commands.setMeta?.('bubbleMenu', 'updatePosition')\n })\n wrapper.appendChild(select)\n toolbar.appendChild(wrapper)\n }\n\n const addFontSize = () => {\n const hasCommand = typeof getCommand('setFontSize') === 'function'\n const wrapper = document.createElement('div')\n wrapper.className = 'bs-bmp-select'\n const select = document.createElement('select')\n select.className = 'bs-bmp-select-input'\n select.title = 'Font size'\n opts.fontSizes.forEach((size) => {\n const option = document.createElement('option')\n option.value = size\n option.textContent = size\n select.appendChild(option)\n })\n select.disabled = !hasCommand\n select.addEventListener('change', () => {\n runWithFocus(() => runChainCommand('setFontSize', select.value))\n editor.commands.setMeta?.('bubbleMenu', 'updatePosition')\n })\n wrapper.appendChild(select)\n toolbar.appendChild(wrapper)\n }\n\n const addToggleMark = (item: BubbleMenuPresetItem, commandName: string, title: string) => {\n const fn = getCommand(commandName) as (() => boolean) | undefined\n const disabled = typeof fn !== 'function'\n addButton(\n item,\n DEFAULT_LABELS[item],\n () => {\n runWithFocus(() => runChainCommand(commandName))\n },\n { disabled, title },\n )\n }\n\n const addTextColor = () => {\n const hasSet = typeof getCommand('setColor') === 'function'\n const hasUnset = typeof getCommand('unsetColor') === 'function'\n const { popover, toggle, destroy } = createColorPopover({\n label: DEFAULT_LABELS.textColor,\n title: 'Text color',\n colors: opts.textColors,\n onSelect: (color) => {\n if (!hasSet) return\n runWithFocus(() => runChainCommand('setColor', color))\n editor.commands.setMeta?.('bubbleMenu', 'updatePosition')\n },\n onClear: () => {\n if (!hasUnset) return\n runWithFocus(() => runChainCommand('unsetColor'))\n editor.commands.setMeta?.('bubbleMenu', 'updatePosition')\n },\n onToggle: () => editor.commands.setMeta?.('bubbleMenu', 'updatePosition'),\n })\n popovers.push(popover)\n cleanupFns.push(destroy)\n toolbar.appendChild(toggle)\n toolbar.appendChild(popover)\n }\n\n const addHighlightColor = () => {\n const hasToggle = typeof getCommand('toggleHighlight') === 'function'\n const hasUnset = typeof getCommand('unsetHighlight') === 'function'\n const { popover, toggle, destroy } = createColorPopover({\n label: DEFAULT_LABELS.highlightColor,\n title: 'Highlight color',\n colors: opts.highlightColors,\n onSelect: (color) => {\n if (!hasToggle) return\n runWithFocus(() => runChainCommand('toggleHighlight', { color }))\n editor.commands.setMeta?.('bubbleMenu', 'updatePosition')\n },\n onClear: () => {\n if (!hasUnset) return\n runWithFocus(() => runChainCommand('unsetHighlight'))\n editor.commands.setMeta?.('bubbleMenu', 'updatePosition')\n },\n onToggle: () => editor.commands.setMeta?.('bubbleMenu', 'updatePosition'),\n })\n popovers.push(popover)\n cleanupFns.push(destroy)\n toolbar.appendChild(toggle)\n toolbar.appendChild(popover)\n }\n\n const addLink = () => {\n const hasToggle = typeof getCommand('toggleLink') === 'function'\n const hasUnset = typeof getCommand('unsetLink') === 'function'\n addButton(\n 'link',\n DEFAULT_LABELS.link,\n () => {\n const currentHref = editor.getAttributes('link')?.href ?? ''\n const href = window.prompt('Link URL', currentHref)\n if (href === null) return\n if (!href) {\n if (hasUnset) {\n runWithFocus(() => runChainCommand('unsetLink'))\n }\n return\n }\n if (hasToggle) {\n runWithFocus(() => runChainCommand('toggleLink', { href }))\n }\n },\n { disabled: !hasToggle && !hasUnset, title: 'Insert link' },\n )\n }\n\n const addAlign = () => {\n const hasSet = typeof getCommand('setTextAlign') === 'function'\n const hasToggle = typeof getCommand('toggleTextAlign') === 'function'\n const wrapper = document.createElement('div')\n wrapper.className = 'bs-bmp-select'\n const select = document.createElement('select')\n select.className = 'bs-bmp-select-input'\n select.title = 'Align'\n opts.alignments.forEach((alignment) => {\n const option = document.createElement('option')\n option.value = alignment\n option.textContent = alignment.charAt(0).toUpperCase() + alignment.slice(1)\n select.appendChild(option)\n })\n select.disabled = !hasSet && !hasToggle\n select.addEventListener('change', () => {\n if (hasToggle) {\n runWithFocus(() => runChainCommand('toggleTextAlign', select.value))\n } else if (hasSet) {\n runWithFocus(() => runChainCommand('setTextAlign', select.value))\n }\n editor.commands.setMeta?.('bubbleMenu', 'updatePosition')\n })\n wrapper.appendChild(select)\n toolbar.appendChild(wrapper)\n }\n\n // Build in the order requested.\n opts.items.forEach((item) => {\n switch (item) {\n case 'undo':\n addUndoRedo()\n break\n case 'redo':\n // handled in undo block to keep buttons together; skip.\n break\n case 'fontFamily':\n addFontFamily()\n break\n case 'fontSize':\n addFontSize()\n break\n case 'bold':\n addToggleMark('bold', 'toggleBold', 'Bold')\n break\n case 'italic':\n addToggleMark('italic', 'toggleItalic', 'Italic')\n break\n case 'underline':\n addToggleMark('underline', 'toggleUnderline', 'Underline')\n break\n case 'textColor':\n addTextColor()\n break\n case 'highlightColor':\n addHighlightColor()\n break\n case 'link':\n addLink()\n break\n case 'align':\n addAlign()\n break\n default:\n break\n }\n })\n\n element.appendChild(toolbar)\n\n return {\n element,\n cleanup: () => {\n popovers.forEach((p) => p.remove())\n cleanupFns.forEach((fn) => fn())\n element.replaceChildren()\n },\n }\n}\n\nfunction createColorPopover(args: {\n label: string\n title: string\n colors: string[]\n onSelect: (color: string) => void\n onClear: () => void\n onToggle?: () => void\n}): { toggle: HTMLElement; popover: HTMLElement; destroy: () => void } {\n const toggle = document.createElement('button')\n toggle.type = 'button'\n toggle.className = 'bs-bmp-btn bs-bmp-btn-color'\n toggle.title = args.title\n toggle.setAttribute('aria-expanded', 'false')\n toggle.setAttribute('aria-label', args.title)\n if (args.label === DEFAULT_LABELS.textColor && ICONS.textColor) {\n toggle.innerHTML = ICONS.textColor\n } else if (args.label === DEFAULT_LABELS.highlightColor && ICONS.highlightColor) {\n toggle.innerHTML = ICONS.highlightColor\n } else {\n toggle.textContent = args.label\n }\n\n const popover = document.createElement('div')\n popover.className = 'bs-bmp-popover bs-bmp-hidden'\n popover.setAttribute('role', 'menu')\n\n const header = document.createElement('div')\n header.className = 'bs-bmp-popover-header'\n const noneBtn = document.createElement('button')\n noneBtn.type = 'button'\n noneBtn.className = 'bs-bmp-btn bs-bmp-btn-ghost'\n noneBtn.textContent = 'None'\n noneBtn.addEventListener('click', () => {\n args.onClear()\n hide()\n })\n header.appendChild(noneBtn)\n popover.appendChild(header)\n\n const grid = document.createElement('div')\n grid.className = 'bs-bmp-color-grid'\n const columns = 10\n grid.style.setProperty('--bs-bmp-grid-columns', String(columns))\n\n args.colors.forEach((color) => {\n const swatch = document.createElement('button')\n swatch.type = 'button'\n swatch.className = 'bs-bmp-color-swatch'\n swatch.style.backgroundColor = color\n swatch.setAttribute('aria-label', color)\n swatch.addEventListener('click', (event) => {\n event.stopPropagation()\n args.onSelect(color)\n hide()\n })\n grid.appendChild(swatch)\n })\n\n popover.appendChild(grid)\n\n const customRow = document.createElement('div')\n customRow.className = 'bs-bmp-popover-footer'\n const customLabel = document.createElement('span')\n customLabel.textContent = 'Custom'\n const customInput = document.createElement('input')\n customInput.type = 'color'\n customInput.className = 'bs-bmp-color-input'\n customInput.addEventListener('input', () => {\n args.onSelect(customInput.value)\n })\n customRow.appendChild(customLabel)\n customRow.appendChild(customInput)\n popover.appendChild(customRow)\n\n const hide = () => {\n popover.classList.add('bs-bmp-hidden')\n toggle.setAttribute('aria-expanded', 'false')\n args.onToggle?.()\n }\n\n const show = () => {\n popover.classList.remove('bs-bmp-hidden')\n toggle.setAttribute('aria-expanded', 'true')\n args.onToggle?.()\n }\n\n const toggleHandler = (event: MouseEvent) => {\n event.stopPropagation()\n if (popover.classList.contains('bs-bmp-hidden')) {\n show()\n } else {\n hide()\n }\n }\n toggle.addEventListener('click', toggleHandler)\n\n const outsideHandler = (event: MouseEvent) => {\n if (popover.contains(event.target as Node) || toggle.contains(event.target as Node)) {\n return\n }\n hide()\n }\n\n document.addEventListener(\n 'click',\n outsideHandler,\n { capture: true },\n )\n\n const destroy = () => {\n document.removeEventListener('click', outsideHandler, { capture: true } as any)\n toggle.removeEventListener('click', toggleHandler)\n }\n\n return { toggle, popover, destroy }\n}\n\nfunction injectStyles() {\n if (typeof document === 'undefined') return\n if (document.getElementById(STYLE_ID)) return\n\n const style = document.createElement('style')\n style.id = STYLE_ID\n style.textContent = `\n.bs-bubble-menu-preset {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n background: #ffffff;\n border: 1px solid #e5e7eb;\n padding: 8px 10px;\n border-radius: 10px;\n box-shadow: 0 10px 30px rgba(0,0,0,0.12);\n color: #111827;\n font-family: Inter, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n z-index: 9999;\n}\n.bs-bmp-toolbar {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n}\n.bs-bmp-btn {\n border: none;\n background: transparent;\n color: inherit;\n padding: 6px 8px;\n border-radius: 12px;\n cursor: pointer;\n font-size: 13px;\n line-height: 1;\n transition: background-color 120ms ease, color 120ms ease, box-shadow 120ms ease;\n}\n.bs-bmp-btn:hover {\n background: #f3f4f6;\n}\n.bs-bmp-btn:disabled {\n opacity: 0.45;\n cursor: not-allowed;\n}\n.bs-bmp-select {\n display: inline-flex;\n align-items: center;\n}\n.bs-bmp-select-input {\n border: 1px solid #e5e7eb;\n background: #ffffff;\n color: inherit;\n padding: 6px 12px 6px 10px;\n border-radius: 12px;\n font-size: 13px;\n outline: none;\n}\n.bs-bmp-select-input:focus {\n border-color: #4f46e5;\n box-shadow: 0 0 0 2px rgba(79,70,229,0.15);\n}\n.bs-bmp-popover {\n position: absolute;\n margin-top: 6px;\n padding: 10px;\n background: #ffffff;\n border: 1px solid #e5e7eb;\n border-radius: 12px;\n box-shadow: 0 12px 40px rgba(15, 23, 42, 0.18);\n z-index: 999;\n}\n.bs-bmp-hidden {\n display: none;\n}\n.bs-bmp-popover-header,\n.bs-bmp-popover-footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 6px;\n}\n.bs-bmp-btn-ghost {\n background: transparent;\n border: none;\n color: inherit;\n padding: 4px 6px;\n border-radius: 8px;\n cursor: pointer;\n}\n.bs-bmp-btn-ghost:hover {\n background: #f3f4f6;\n}\n.bs-bmp-color-grid {\n display: grid;\n grid-template-columns: repeat(var(--bs-bmp-grid-columns, 10), 22px);\n gap: 4px;\n margin-bottom: 8px;\n}\n.bs-bmp-color-swatch {\n width: 22px;\n height: 22px;\n border-radius: 50%;\n border: 1px solid #e5e7eb;\n cursor: pointer;\n padding: 0;\n}\n.bs-bmp-color-swatch:hover {\n outline: 2px solid #4f46e5;\n outline-offset: 2px;\n}\n.bs-bmp-color-input {\n width: 32px;\n height: 28px;\n padding: 0;\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n background: #ffffff;\n}\n`\n\n document.head.appendChild(style)\n}\n\n\n\n\n\n"],"mappings":"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","names":["_a","opts"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/bubble-menu-preset.ts"],"sourcesContent":["export * from './bubble-menu-preset.js'\nexport { BubbleMenuPreset as default } from './bubble-menu-preset.js'\n\n\n\n\n","import { Extension } from '@blockslides/core'\nimport type { Editor } from '@blockslides/core'\nimport {\n BubbleMenuPlugin,\n type BubbleMenuPluginProps,\n type BubbleMenuOptions,\n} from '@blockslides/extension-bubble-menu'\n\ntype BubbleMenuPresetItem =\n | 'undo'\n | 'redo'\n | 'fontFamily'\n | 'fontSize'\n | 'bold'\n | 'italic'\n | 'underline'\n | 'textColor'\n | 'highlightColor'\n | 'link'\n | 'align'\n\ntype TextAlignValue = 'left' | 'center' | 'right' | 'justify'\n\nexport interface BubbleMenuPresetOptions\n extends Omit<BubbleMenuOptions, 'element'> {\n /**\n * Optional custom element to use for the menu. If omitted, a default\n * element with built-in buttons is rendered.\n */\n element?: HTMLElement | null\n\n /**\n * Order of built-in controls to render.\n */\n items?: BubbleMenuPresetItem[]\n\n /**\n * Additional class names to attach to the menu element to allow easy\n * style overrides.\n */\n className?: string\n\n /**\n * Inject default CSS. Set to false to opt out if you provide your own styles.\n */\n injectStyles?: boolean\n\n /**\n * Palette for text color swatches.\n */\n textColors?: string[]\n\n /**\n * Palette for highlight swatches.\n */\n highlightColors?: string[]\n\n /**\n * Fonts exposed in the font picker.\n */\n fonts?: string[]\n\n /**\n * Font sizes (any CSS length, e.g. \"16px\", \"1rem\").\n */\n fontSizes?: string[]\n\n /**\n * Alignments to expose in the align control.\n */\n alignments?: TextAlignValue[]\n}\n\ntype Cleanup = () => void\n\nconst STYLE_ID = 'blockslides-bubble-menu-preset-styles'\n\nconst DEFAULT_ITEMS: BubbleMenuPresetItem[] = [\n 'undo',\n 'redo',\n 'fontFamily',\n 'fontSize',\n 'bold',\n 'italic',\n 'underline',\n 'textColor',\n 'highlightColor',\n 'link',\n 'align',\n]\n\nconst DEFAULT_FONTS = [\n 'Inter',\n 'Arial',\n 'Helvetica',\n 'Times New Roman',\n 'Georgia',\n 'Courier New',\n 'Monaco',\n]\n\nconst DEFAULT_FONT_SIZES = ['12px', '14px', '16px', '18px', '20px', '24px', '32px', '40px']\n\nconst DEFAULT_ALIGNMENTS: TextAlignValue[] = ['left', 'center', 'right', 'justify']\n\n// A rich palette approximating the attached reference.\nconst DEFAULT_COLOR_PALETTE: string[] = [\n '#000000',\n '#434343',\n '#666666',\n '#999999',\n '#b7b7b7',\n '#cccccc',\n '#d9d9d9',\n '#efefef',\n '#f3f3f3',\n '#ffffff',\n '#e60000',\n '#ff0000',\n '#ff9900',\n '#ffff00',\n '#00ff00',\n '#00ffff',\n '#4a86e8',\n '#0000ff',\n '#9900ff',\n '#ff00ff',\n '#f4cccc',\n '#fce5cd',\n '#fff2cc',\n '#d9ead3',\n '#d0e0e3',\n '#cfe2f3',\n '#d9d2e9',\n '#ead1dc',\n '#f9cb9c',\n '#ffe599',\n '#b6d7a8',\n '#a2c4c9',\n '#9fc5e8',\n '#b4a7d6',\n '#d5a6bd',\n '#e06666',\n '#f6b26b',\n '#ffd966',\n '#93c47d',\n '#76a5af',\n '#6fa8dc',\n '#8e7cc3',\n '#c27ba0',\n '#cc0000',\n '#e69138',\n '#f1c232',\n '#6aa84f',\n '#45818e',\n '#3d85c6',\n '#674ea7',\n '#a64d79',\n '#990000',\n '#b45f06',\n '#bf9000',\n '#38761d',\n '#134f5c',\n '#0b5394',\n '#351c75',\n '#741b47',\n '#660000',\n '#783f04',\n '#7f6000',\n '#274e13',\n '#0c343d',\n '#073763',\n '#20124d',\n '#4c1130',\n]\n\nconst DEFAULT_HIGHLIGHT_PALETTE = DEFAULT_COLOR_PALETTE\n\nconst DEFAULT_LABELS: Record<BubbleMenuPresetItem, string> = {\n undo: 'Undo',\n redo: 'Redo',\n fontFamily: 'Font',\n fontSize: 'Size',\n bold: 'B',\n italic: 'I',\n underline: 'U',\n textColor: 'A',\n highlightColor: 'Hi',\n link: 'Link',\n align: 'Align',\n}\n\nconst ICONS = {\n textColor: `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M5 20h14\"/><path d=\"M7 20l5-14 5 14\"/><path d=\"M10.7 14h2.6\"/></svg>`,\n highlightColor: `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m11 18 2-2h6\"/><path d=\"m2 22 3-3h6l3-3-6-6-3 3v6z\"/><path d=\"M14 7l3-3 3 3-3 3z\"/></svg>`,\n}\n\ninterface MenuBuildResult {\n element: HTMLElement\n cleanup: Cleanup\n}\n\nexport const BubbleMenuPreset = Extension.create<BubbleMenuPresetOptions>({\n name: 'bubbleMenuPreset',\n\n addOptions() {\n return {\n element: null,\n items: DEFAULT_ITEMS,\n className: '',\n injectStyles: true,\n textColors: DEFAULT_COLOR_PALETTE,\n highlightColors: DEFAULT_HIGHLIGHT_PALETTE,\n fonts: DEFAULT_FONTS,\n fontSizes: DEFAULT_FONT_SIZES,\n alignments: DEFAULT_ALIGNMENTS,\n pluginKey: 'bubbleMenuPreset',\n updateDelay: 250,\n resizeDelay: 60,\n appendTo: undefined,\n shouldShow: null,\n options: {\n placement: 'top',\n strategy: 'absolute',\n offset: 8,\n flip: {},\n shift: {},\n },\n }\n },\n\n addProseMirrorPlugins() {\n const options = this.options\n const editor = this.editor\n\n const usingCustomElement = !!options.element\n const { element, cleanup } =\n options.element && typeof document !== 'undefined'\n ? { element: options.element, cleanup: () => {} }\n : buildMenuElement(editor, {\n items: options.items ?? DEFAULT_ITEMS,\n className: options.className ?? '',\n injectStyles: options.injectStyles !== false,\n textColors: options.textColors ?? DEFAULT_COLOR_PALETTE,\n highlightColors: options.highlightColors ?? DEFAULT_HIGHLIGHT_PALETTE,\n fonts: options.fonts ?? DEFAULT_FONTS,\n fontSizes: options.fontSizes ?? DEFAULT_FONT_SIZES,\n alignments: options.alignments ?? DEFAULT_ALIGNMENTS,\n })\n\n this.storage.element = element\n this.storage.cleanup = cleanup\n this.storage.usingCustomElement = usingCustomElement\n\n const pluginOptions: BubbleMenuPluginProps = {\n pluginKey: options.pluginKey ?? 'bubbleMenuPreset',\n editor,\n element: element,\n updateDelay: options.updateDelay,\n resizeDelay: options.resizeDelay,\n appendTo: options.appendTo,\n options: options.options,\n getReferencedVirtualElement: options.getReferencedVirtualElement,\n shouldShow: options.shouldShow ?? undefined,\n }\n\n return [BubbleMenuPlugin(pluginOptions)]\n },\n\n onDestroy() {\n const el = this.storage.element as HTMLElement | undefined\n const cleanup = this.storage.cleanup as Cleanup | undefined\n const usingCustomElement = this.storage.usingCustomElement as boolean | undefined\n if (cleanup) {\n cleanup()\n }\n if (el && !usingCustomElement) {\n el.remove()\n }\n },\n})\n\nfunction buildMenuElement(\n editor: Editor,\n opts: {\n items: BubbleMenuPresetItem[]\n className: string\n injectStyles: boolean\n textColors: string[]\n highlightColors: string[]\n fonts: string[]\n fontSizes: string[]\n alignments: TextAlignValue[]\n },\n): MenuBuildResult {\n if (opts.injectStyles) {\n injectStyles()\n }\n\n const element = document.createElement('div')\n element.className = `bs-bubble-menu-preset ${opts.className ?? ''}`.trim()\n element.setAttribute('data-bubble-menu-preset', 'true')\n element.tabIndex = 0\n\n const toolbar = document.createElement('div')\n toolbar.className = 'bs-bmp-toolbar'\n\n let fontFamilySelect: HTMLSelectElement | null = null\n let fontSizeSelect: HTMLSelectElement | null = null\n\n const normalizeFontFamily = (value?: string | null) => {\n if (!value) return value\n // take the first family name and strip quotes\n return value.split(',')[0]?.trim().replace(/^['\"]|['\"]$/g, '') || value\n }\n\n const getComputedFontStyles = () => {\n if (typeof window === 'undefined') return { fontFamily: undefined, fontSize: undefined }\n const sel = window.getSelection()\n const node = sel?.anchorNode\n const el = (node instanceof HTMLElement ? node : node?.parentElement) ?? undefined\n if (!el) return { fontFamily: undefined, fontSize: undefined }\n const styles = window.getComputedStyle(el)\n return {\n fontFamily: normalizeFontFamily(styles.fontFamily || undefined),\n fontSize: styles.fontSize || undefined,\n }\n }\n\n const ensureOptionExists = (select: HTMLSelectElement, value: string) => {\n const exists = Array.from(select.options).some(opt => opt.value === value)\n if (!exists) {\n const option = document.createElement('option')\n option.value = value\n option.textContent = value\n select.appendChild(option)\n }\n }\n\n const setSelectValue = (select: HTMLSelectElement | null, value?: string | null) => {\n if (!select) return\n if (!value) {\n select.selectedIndex = -1\n return\n }\n ensureOptionExists(select, value)\n select.value = value\n }\n\n const popovers: HTMLElement[] = []\n const cleanupFns: Cleanup[] = []\n\n const getCommand = (name: string): any => (editor.commands as any)?.[name]\n const runChainCommand = (name: string, ...args: any[]) => {\n const chain = (editor.chain as any)?.()\n if (!chain || typeof chain[name] !== 'function') return false\n const runner = typeof chain.focus === 'function' ? chain.focus() : chain\n if (typeof runner[name] !== 'function') return false\n return runner[name](...args).run?.() ?? false\n }\n\n const closePopovers = () => {\n popovers.forEach((p) => p.classList.add('bs-bmp-hidden'))\n }\n\n const runWithFocus = (fn?: () => boolean) => {\n if (!fn) return false\n return !!fn()\n }\n\n const addButton = (\n item: BubbleMenuPresetItem,\n label: string,\n onClick: () => void,\n opts: { disabled?: boolean; title?: string } = {},\n ) => {\n const btn = document.createElement('button')\n btn.type = 'button'\n btn.className = `bs-bmp-btn bs-bmp-btn-${item}`\n btn.textContent = label\n if (opts.title) {\n btn.title = opts.title\n btn.setAttribute('aria-label', opts.title)\n }\n if (opts.disabled) {\n btn.disabled = true\n btn.classList.add('bs-bmp-disabled')\n } else {\n btn.addEventListener('click', () => {\n closePopovers()\n onClick()\n })\n }\n toolbar.appendChild(btn)\n }\n\n const addUndoRedo = () => {\n const hasUndo = typeof getCommand('undo') === 'function'\n const hasRedo = typeof getCommand('redo') === 'function'\n addButton('undo', DEFAULT_LABELS.undo, () => runWithFocus(() => runChainCommand('undo')), {\n disabled: !hasUndo,\n title: 'Undo',\n })\n addButton('redo', DEFAULT_LABELS.redo, () => runWithFocus(() => runChainCommand('redo')), {\n disabled: !hasRedo,\n title: 'Redo',\n })\n }\n\n const addFontFamily = () => {\n const hasCommand = typeof getCommand('setFontFamily') === 'function'\n const wrapper = document.createElement('div')\n wrapper.className = 'bs-bmp-select'\n const select = document.createElement('select')\n select.className = 'bs-bmp-select-input'\n select.title = 'Font family'\n opts.fonts.forEach((font) => {\n const option = document.createElement('option')\n option.value = font\n option.textContent = font\n option.style.fontFamily = font\n select.appendChild(option)\n })\n select.disabled = !hasCommand\n select.addEventListener('change', () => {\n runWithFocus(() => runChainCommand('setFontFamily', select.value))\n editor.commands.setMeta?.('bubbleMenu', 'updatePosition')\n })\n fontFamilySelect = select\n wrapper.appendChild(select)\n toolbar.appendChild(wrapper)\n }\n\n const addFontSize = () => {\n const hasCommand = typeof getCommand('setFontSize') === 'function'\n const wrapper = document.createElement('div')\n wrapper.className = 'bs-bmp-select'\n const select = document.createElement('select')\n select.className = 'bs-bmp-select-input'\n select.title = 'Font size'\n opts.fontSizes.forEach((size) => {\n const option = document.createElement('option')\n option.value = size\n option.textContent = size\n select.appendChild(option)\n })\n select.disabled = !hasCommand\n select.addEventListener('change', () => {\n runWithFocus(() => runChainCommand('setFontSize', select.value))\n editor.commands.setMeta?.('bubbleMenu', 'updatePosition')\n })\n fontSizeSelect = select\n wrapper.appendChild(select)\n toolbar.appendChild(wrapper)\n }\n\n const addToggleMark = (item: BubbleMenuPresetItem, commandName: string, title: string) => {\n const fn = getCommand(commandName) as (() => boolean) | undefined\n const disabled = typeof fn !== 'function'\n addButton(\n item,\n DEFAULT_LABELS[item],\n () => {\n runWithFocus(() => runChainCommand(commandName))\n },\n { disabled, title },\n )\n }\n\n const addTextColor = () => {\n const hasSet = typeof getCommand('setColor') === 'function'\n const hasUnset = typeof getCommand('unsetColor') === 'function'\n const { popover, toggle, destroy } = createColorPopover({\n label: DEFAULT_LABELS.textColor,\n title: 'Text color',\n colors: opts.textColors,\n onSelect: (color) => {\n if (!hasSet) return\n runWithFocus(() => runChainCommand('setColor', color))\n editor.commands.setMeta?.('bubbleMenu', 'updatePosition')\n },\n onClear: () => {\n if (!hasUnset) return\n runWithFocus(() => runChainCommand('unsetColor'))\n editor.commands.setMeta?.('bubbleMenu', 'updatePosition')\n },\n onToggle: () => editor.commands.setMeta?.('bubbleMenu', 'updatePosition'),\n })\n popovers.push(popover)\n cleanupFns.push(destroy)\n toolbar.appendChild(toggle)\n toolbar.appendChild(popover)\n }\n\n const addHighlightColor = () => {\n const hasToggle = typeof getCommand('toggleHighlight') === 'function'\n const hasUnset = typeof getCommand('unsetHighlight') === 'function'\n const { popover, toggle, destroy } = createColorPopover({\n label: DEFAULT_LABELS.highlightColor,\n title: 'Highlight color',\n colors: opts.highlightColors,\n onSelect: (color) => {\n if (!hasToggle) return\n runWithFocus(() => runChainCommand('toggleHighlight', { color }))\n editor.commands.setMeta?.('bubbleMenu', 'updatePosition')\n },\n onClear: () => {\n if (!hasUnset) return\n runWithFocus(() => runChainCommand('unsetHighlight'))\n editor.commands.setMeta?.('bubbleMenu', 'updatePosition')\n },\n onToggle: () => editor.commands.setMeta?.('bubbleMenu', 'updatePosition'),\n })\n popovers.push(popover)\n cleanupFns.push(destroy)\n toolbar.appendChild(toggle)\n toolbar.appendChild(popover)\n }\n\n const addLink = () => {\n const hasToggle = typeof getCommand('toggleLink') === 'function'\n const hasUnset = typeof getCommand('unsetLink') === 'function'\n addButton(\n 'link',\n DEFAULT_LABELS.link,\n () => {\n const currentHref = editor.getAttributes('link')?.href ?? ''\n const href = window.prompt('Link URL', currentHref)\n if (href === null) return\n if (!href) {\n if (hasUnset) {\n runWithFocus(() => runChainCommand('unsetLink'))\n }\n return\n }\n if (hasToggle) {\n runWithFocus(() => runChainCommand('toggleLink', { href }))\n }\n },\n { disabled: !hasToggle && !hasUnset, title: 'Insert link' },\n )\n }\n\n const addAlign = () => {\n const hasSet = typeof getCommand('setTextAlign') === 'function'\n const hasToggle = typeof getCommand('toggleTextAlign') === 'function'\n const wrapper = document.createElement('div')\n wrapper.className = 'bs-bmp-select'\n const select = document.createElement('select')\n select.className = 'bs-bmp-select-input'\n select.title = 'Align'\n opts.alignments.forEach((alignment) => {\n const option = document.createElement('option')\n option.value = alignment\n option.textContent = alignment.charAt(0).toUpperCase() + alignment.slice(1)\n select.appendChild(option)\n })\n select.disabled = !hasSet && !hasToggle\n select.addEventListener('change', () => {\n if (hasToggle) {\n runWithFocus(() => runChainCommand('toggleTextAlign', select.value))\n } else if (hasSet) {\n runWithFocus(() => runChainCommand('setTextAlign', select.value))\n }\n editor.commands.setMeta?.('bubbleMenu', 'updatePosition')\n })\n wrapper.appendChild(select)\n toolbar.appendChild(wrapper)\n }\n\n // Build in the order requested.\n opts.items.forEach((item) => {\n switch (item) {\n case 'undo':\n addUndoRedo()\n break\n case 'redo':\n // handled in undo block to keep buttons together; skip.\n break\n case 'fontFamily':\n addFontFamily()\n break\n case 'fontSize':\n addFontSize()\n break\n case 'bold':\n addToggleMark('bold', 'toggleBold', 'Bold')\n break\n case 'italic':\n addToggleMark('italic', 'toggleItalic', 'Italic')\n break\n case 'underline':\n addToggleMark('underline', 'toggleUnderline', 'Underline')\n break\n case 'textColor':\n addTextColor()\n break\n case 'highlightColor':\n addHighlightColor()\n break\n case 'link':\n addLink()\n break\n case 'align':\n addAlign()\n break\n default:\n break\n }\n })\n\n element.appendChild(toolbar)\n\n const syncSelectionState = () => {\n const attrs = editor.getAttributes('textStyle') || {}\n let family = attrs.fontFamily\n let size = attrs.fontSize\n\n // If not explicitly set via marks, fall back to computed DOM styles\n if (!family || !size) {\n const computed = getComputedFontStyles()\n family = family || computed.fontFamily\n size = size || computed.fontSize\n }\n\n setSelectValue(fontFamilySelect, normalizeFontFamily(family))\n setSelectValue(fontSizeSelect, size)\n }\n\n const handleSelectionUpdate = () => syncSelectionState()\n const handleTransaction = ({ transaction }: { transaction?: any }) => {\n if (!transaction || transaction.docChanged || transaction.selectionSet) {\n syncSelectionState()\n }\n }\n\n editor.on('selectionUpdate', handleSelectionUpdate)\n editor.on('transaction', handleTransaction)\n cleanupFns.push(() => {\n editor.off('selectionUpdate', handleSelectionUpdate)\n editor.off('transaction', handleTransaction)\n })\n\n syncSelectionState()\n\n return {\n element,\n cleanup: () => {\n popovers.forEach((p) => p.remove())\n cleanupFns.forEach((fn) => fn())\n element.replaceChildren()\n },\n }\n}\n\nfunction createColorPopover(args: {\n label: string\n title: string\n colors: string[]\n onSelect: (color: string) => void\n onClear: () => void\n onToggle?: () => void\n}): { toggle: HTMLElement; popover: HTMLElement; destroy: () => void } {\n const toggle = document.createElement('button')\n toggle.type = 'button'\n toggle.className = 'bs-bmp-btn bs-bmp-btn-color'\n toggle.title = args.title\n toggle.setAttribute('aria-expanded', 'false')\n toggle.setAttribute('aria-label', args.title)\n if (args.label === DEFAULT_LABELS.textColor && ICONS.textColor) {\n toggle.innerHTML = ICONS.textColor\n } else if (args.label === DEFAULT_LABELS.highlightColor && ICONS.highlightColor) {\n toggle.innerHTML = ICONS.highlightColor\n } else {\n toggle.textContent = args.label\n }\n\n const popover = document.createElement('div')\n popover.className = 'bs-bmp-popover bs-bmp-hidden'\n popover.setAttribute('role', 'menu')\n\n const header = document.createElement('div')\n header.className = 'bs-bmp-popover-header'\n const noneBtn = document.createElement('button')\n noneBtn.type = 'button'\n noneBtn.className = 'bs-bmp-btn bs-bmp-btn-ghost'\n noneBtn.textContent = 'None'\n noneBtn.addEventListener('click', () => {\n args.onClear()\n hide()\n })\n header.appendChild(noneBtn)\n popover.appendChild(header)\n\n const grid = document.createElement('div')\n grid.className = 'bs-bmp-color-grid'\n const columns = 10\n grid.style.setProperty('--bs-bmp-grid-columns', String(columns))\n\n args.colors.forEach((color) => {\n const swatch = document.createElement('button')\n swatch.type = 'button'\n swatch.className = 'bs-bmp-color-swatch'\n swatch.style.backgroundColor = color\n swatch.setAttribute('aria-label', color)\n swatch.addEventListener('click', (event) => {\n event.stopPropagation()\n args.onSelect(color)\n hide()\n })\n grid.appendChild(swatch)\n })\n\n popover.appendChild(grid)\n\n const customRow = document.createElement('div')\n customRow.className = 'bs-bmp-popover-footer'\n const customLabel = document.createElement('span')\n customLabel.textContent = 'Custom'\n const customInput = document.createElement('input')\n customInput.type = 'color'\n customInput.className = 'bs-bmp-color-input'\n customInput.addEventListener('input', () => {\n args.onSelect(customInput.value)\n })\n customRow.appendChild(customLabel)\n customRow.appendChild(customInput)\n popover.appendChild(customRow)\n\n const hide = () => {\n popover.classList.add('bs-bmp-hidden')\n toggle.setAttribute('aria-expanded', 'false')\n args.onToggle?.()\n }\n\n const show = () => {\n popover.classList.remove('bs-bmp-hidden')\n toggle.setAttribute('aria-expanded', 'true')\n args.onToggle?.()\n }\n\n const toggleHandler = (event: MouseEvent) => {\n event.stopPropagation()\n if (popover.classList.contains('bs-bmp-hidden')) {\n show()\n } else {\n hide()\n }\n }\n toggle.addEventListener('click', toggleHandler)\n\n const outsideHandler = (event: MouseEvent) => {\n if (popover.contains(event.target as Node) || toggle.contains(event.target as Node)) {\n return\n }\n hide()\n }\n\n document.addEventListener(\n 'click',\n outsideHandler,\n { capture: true },\n )\n\n const destroy = () => {\n document.removeEventListener('click', outsideHandler, { capture: true } as any)\n toggle.removeEventListener('click', toggleHandler)\n }\n\n return { toggle, popover, destroy }\n}\n\nfunction injectStyles() {\n if (typeof document === 'undefined') return\n if (document.getElementById(STYLE_ID)) return\n\n const style = document.createElement('style')\n style.id = STYLE_ID\n style.textContent = `\n.bs-bubble-menu-preset {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n background: #ffffff;\n border: 1px solid #e5e7eb;\n padding: 8px 10px;\n border-radius: 10px;\n box-shadow: 0 10px 30px rgba(0,0,0,0.12);\n color: #111827;\n font-family: Inter, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n z-index: 9999;\n}\n.bs-bmp-toolbar {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n}\n.bs-bmp-btn {\n border: none;\n background: transparent;\n color: inherit;\n padding: 6px 8px;\n border-radius: 12px;\n cursor: pointer;\n font-size: 13px;\n line-height: 1;\n transition: background-color 120ms ease, color 120ms ease, box-shadow 120ms ease;\n}\n.bs-bmp-btn:hover {\n background: #f3f4f6;\n}\n.bs-bmp-btn:disabled {\n opacity: 0.45;\n cursor: not-allowed;\n}\n.bs-bmp-select {\n display: inline-flex;\n align-items: center;\n}\n.bs-bmp-select-input {\n border: 1px solid #e5e7eb;\n background: #ffffff;\n color: inherit;\n padding: 6px 12px 6px 10px;\n border-radius: 12px;\n font-size: 13px;\n outline: none;\n}\n.bs-bmp-select-input:focus {\n border-color: #4f46e5;\n box-shadow: 0 0 0 2px rgba(79,70,229,0.15);\n}\n.bs-bmp-popover {\n position: absolute;\n margin-top: 6px;\n padding: 10px;\n background: #ffffff;\n border: 1px solid #e5e7eb;\n border-radius: 12px;\n box-shadow: 0 12px 40px rgba(15, 23, 42, 0.18);\n z-index: 999;\n}\n.bs-bmp-hidden {\n display: none;\n}\n.bs-bmp-popover-header,\n.bs-bmp-popover-footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 6px;\n}\n.bs-bmp-btn-ghost {\n background: transparent;\n border: none;\n color: inherit;\n padding: 4px 6px;\n border-radius: 8px;\n cursor: pointer;\n}\n.bs-bmp-btn-ghost:hover {\n background: #f3f4f6;\n}\n.bs-bmp-color-grid {\n display: grid;\n grid-template-columns: repeat(var(--bs-bmp-grid-columns, 10), 22px);\n gap: 4px;\n margin-bottom: 8px;\n}\n.bs-bmp-color-swatch {\n width: 22px;\n height: 22px;\n border-radius: 50%;\n border: 1px solid #e5e7eb;\n cursor: pointer;\n padding: 0;\n}\n.bs-bmp-color-swatch:hover {\n outline: 2px solid #4f46e5;\n outline-offset: 2px;\n}\n.bs-bmp-color-input {\n width: 32px;\n height: 28px;\n padding: 0;\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n background: #ffffff;\n}\n`\n\n document.head.appendChild(style)\n}\n\n\n\n\n\n"],"mappings":"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","names":["_a","opts"]}
|
package/dist/index.js
CHANGED
|
@@ -197,6 +197,44 @@ function buildMenuElement(editor, opts) {
|
|
|
197
197
|
element.tabIndex = 0;
|
|
198
198
|
const toolbar = document.createElement("div");
|
|
199
199
|
toolbar.className = "bs-bmp-toolbar";
|
|
200
|
+
let fontFamilySelect = null;
|
|
201
|
+
let fontSizeSelect = null;
|
|
202
|
+
const normalizeFontFamily = (value) => {
|
|
203
|
+
var _a2;
|
|
204
|
+
if (!value) return value;
|
|
205
|
+
return ((_a2 = value.split(",")[0]) == null ? void 0 : _a2.trim().replace(/^['"]|['"]$/g, "")) || value;
|
|
206
|
+
};
|
|
207
|
+
const getComputedFontStyles = () => {
|
|
208
|
+
var _a2;
|
|
209
|
+
if (typeof window === "undefined") return { fontFamily: void 0, fontSize: void 0 };
|
|
210
|
+
const sel = window.getSelection();
|
|
211
|
+
const node = sel == null ? void 0 : sel.anchorNode;
|
|
212
|
+
const el = (_a2 = node instanceof HTMLElement ? node : node == null ? void 0 : node.parentElement) != null ? _a2 : void 0;
|
|
213
|
+
if (!el) return { fontFamily: void 0, fontSize: void 0 };
|
|
214
|
+
const styles = window.getComputedStyle(el);
|
|
215
|
+
return {
|
|
216
|
+
fontFamily: normalizeFontFamily(styles.fontFamily || void 0),
|
|
217
|
+
fontSize: styles.fontSize || void 0
|
|
218
|
+
};
|
|
219
|
+
};
|
|
220
|
+
const ensureOptionExists = (select, value) => {
|
|
221
|
+
const exists = Array.from(select.options).some((opt) => opt.value === value);
|
|
222
|
+
if (!exists) {
|
|
223
|
+
const option = document.createElement("option");
|
|
224
|
+
option.value = value;
|
|
225
|
+
option.textContent = value;
|
|
226
|
+
select.appendChild(option);
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
const setSelectValue = (select, value) => {
|
|
230
|
+
if (!select) return;
|
|
231
|
+
if (!value) {
|
|
232
|
+
select.selectedIndex = -1;
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
ensureOptionExists(select, value);
|
|
236
|
+
select.value = value;
|
|
237
|
+
};
|
|
200
238
|
const popovers = [];
|
|
201
239
|
const cleanupFns = [];
|
|
202
240
|
const getCommand = (name) => {
|
|
@@ -270,6 +308,7 @@ function buildMenuElement(editor, opts) {
|
|
|
270
308
|
runWithFocus(() => runChainCommand("setFontFamily", select.value));
|
|
271
309
|
(_b = (_a2 = editor.commands).setMeta) == null ? void 0 : _b.call(_a2, "bubbleMenu", "updatePosition");
|
|
272
310
|
});
|
|
311
|
+
fontFamilySelect = select;
|
|
273
312
|
wrapper.appendChild(select);
|
|
274
313
|
toolbar.appendChild(wrapper);
|
|
275
314
|
};
|
|
@@ -292,6 +331,7 @@ function buildMenuElement(editor, opts) {
|
|
|
292
331
|
runWithFocus(() => runChainCommand("setFontSize", select.value));
|
|
293
332
|
(_b = (_a2 = editor.commands).setMeta) == null ? void 0 : _b.call(_a2, "bubbleMenu", "updatePosition");
|
|
294
333
|
});
|
|
334
|
+
fontSizeSelect = select;
|
|
295
335
|
wrapper.appendChild(select);
|
|
296
336
|
toolbar.appendChild(wrapper);
|
|
297
337
|
};
|
|
@@ -455,6 +495,31 @@ function buildMenuElement(editor, opts) {
|
|
|
455
495
|
}
|
|
456
496
|
});
|
|
457
497
|
element.appendChild(toolbar);
|
|
498
|
+
const syncSelectionState = () => {
|
|
499
|
+
const attrs = editor.getAttributes("textStyle") || {};
|
|
500
|
+
let family = attrs.fontFamily;
|
|
501
|
+
let size = attrs.fontSize;
|
|
502
|
+
if (!family || !size) {
|
|
503
|
+
const computed = getComputedFontStyles();
|
|
504
|
+
family = family || computed.fontFamily;
|
|
505
|
+
size = size || computed.fontSize;
|
|
506
|
+
}
|
|
507
|
+
setSelectValue(fontFamilySelect, normalizeFontFamily(family));
|
|
508
|
+
setSelectValue(fontSizeSelect, size);
|
|
509
|
+
};
|
|
510
|
+
const handleSelectionUpdate = () => syncSelectionState();
|
|
511
|
+
const handleTransaction = ({ transaction }) => {
|
|
512
|
+
if (!transaction || transaction.docChanged || transaction.selectionSet) {
|
|
513
|
+
syncSelectionState();
|
|
514
|
+
}
|
|
515
|
+
};
|
|
516
|
+
editor.on("selectionUpdate", handleSelectionUpdate);
|
|
517
|
+
editor.on("transaction", handleTransaction);
|
|
518
|
+
cleanupFns.push(() => {
|
|
519
|
+
editor.off("selectionUpdate", handleSelectionUpdate);
|
|
520
|
+
editor.off("transaction", handleTransaction);
|
|
521
|
+
});
|
|
522
|
+
syncSelectionState();
|
|
458
523
|
return {
|
|
459
524
|
element,
|
|
460
525
|
cleanup: () => {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/bubble-menu-preset.ts"],"sourcesContent":["import { Extension } from '@blockslides/core'\nimport type { Editor } from '@blockslides/core'\nimport {\n BubbleMenuPlugin,\n type BubbleMenuPluginProps,\n type BubbleMenuOptions,\n} from '@blockslides/extension-bubble-menu'\n\ntype BubbleMenuPresetItem =\n | 'undo'\n | 'redo'\n | 'fontFamily'\n | 'fontSize'\n | 'bold'\n | 'italic'\n | 'underline'\n | 'textColor'\n | 'highlightColor'\n | 'link'\n | 'align'\n\ntype TextAlignValue = 'left' | 'center' | 'right' | 'justify'\n\nexport interface BubbleMenuPresetOptions\n extends Omit<BubbleMenuOptions, 'element'> {\n /**\n * Optional custom element to use for the menu. If omitted, a default\n * element with built-in buttons is rendered.\n */\n element?: HTMLElement | null\n\n /**\n * Order of built-in controls to render.\n */\n items?: BubbleMenuPresetItem[]\n\n /**\n * Additional class names to attach to the menu element to allow easy\n * style overrides.\n */\n className?: string\n\n /**\n * Inject default CSS. Set to false to opt out if you provide your own styles.\n */\n injectStyles?: boolean\n\n /**\n * Palette for text color swatches.\n */\n textColors?: string[]\n\n /**\n * Palette for highlight swatches.\n */\n highlightColors?: string[]\n\n /**\n * Fonts exposed in the font picker.\n */\n fonts?: string[]\n\n /**\n * Font sizes (any CSS length, e.g. \"16px\", \"1rem\").\n */\n fontSizes?: string[]\n\n /**\n * Alignments to expose in the align control.\n */\n alignments?: TextAlignValue[]\n}\n\ntype Cleanup = () => void\n\nconst STYLE_ID = 'blockslides-bubble-menu-preset-styles'\n\nconst DEFAULT_ITEMS: BubbleMenuPresetItem[] = [\n 'undo',\n 'redo',\n 'fontFamily',\n 'fontSize',\n 'bold',\n 'italic',\n 'underline',\n 'textColor',\n 'highlightColor',\n 'link',\n 'align',\n]\n\nconst DEFAULT_FONTS = [\n 'Inter',\n 'Arial',\n 'Helvetica',\n 'Times New Roman',\n 'Georgia',\n 'Courier New',\n 'Monaco',\n]\n\nconst DEFAULT_FONT_SIZES = ['12px', '14px', '16px', '18px', '20px', '24px', '32px', '40px']\n\nconst DEFAULT_ALIGNMENTS: TextAlignValue[] = ['left', 'center', 'right', 'justify']\n\n// A rich palette approximating the attached reference.\nconst DEFAULT_COLOR_PALETTE: string[] = [\n '#000000',\n '#434343',\n '#666666',\n '#999999',\n '#b7b7b7',\n '#cccccc',\n '#d9d9d9',\n '#efefef',\n '#f3f3f3',\n '#ffffff',\n '#e60000',\n '#ff0000',\n '#ff9900',\n '#ffff00',\n '#00ff00',\n '#00ffff',\n '#4a86e8',\n '#0000ff',\n '#9900ff',\n '#ff00ff',\n '#f4cccc',\n '#fce5cd',\n '#fff2cc',\n '#d9ead3',\n '#d0e0e3',\n '#cfe2f3',\n '#d9d2e9',\n '#ead1dc',\n '#f9cb9c',\n '#ffe599',\n '#b6d7a8',\n '#a2c4c9',\n '#9fc5e8',\n '#b4a7d6',\n '#d5a6bd',\n '#e06666',\n '#f6b26b',\n '#ffd966',\n '#93c47d',\n '#76a5af',\n '#6fa8dc',\n '#8e7cc3',\n '#c27ba0',\n '#cc0000',\n '#e69138',\n '#f1c232',\n '#6aa84f',\n '#45818e',\n '#3d85c6',\n '#674ea7',\n '#a64d79',\n '#990000',\n '#b45f06',\n '#bf9000',\n '#38761d',\n '#134f5c',\n '#0b5394',\n '#351c75',\n '#741b47',\n '#660000',\n '#783f04',\n '#7f6000',\n '#274e13',\n '#0c343d',\n '#073763',\n '#20124d',\n '#4c1130',\n]\n\nconst DEFAULT_HIGHLIGHT_PALETTE = DEFAULT_COLOR_PALETTE\n\nconst DEFAULT_LABELS: Record<BubbleMenuPresetItem, string> = {\n undo: 'Undo',\n redo: 'Redo',\n fontFamily: 'Font',\n fontSize: 'Size',\n bold: 'B',\n italic: 'I',\n underline: 'U',\n textColor: 'A',\n highlightColor: 'Hi',\n link: 'Link',\n align: 'Align',\n}\n\nconst ICONS = {\n textColor: `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M5 20h14\"/><path d=\"M7 20l5-14 5 14\"/><path d=\"M10.7 14h2.6\"/></svg>`,\n highlightColor: `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m11 18 2-2h6\"/><path d=\"m2 22 3-3h6l3-3-6-6-3 3v6z\"/><path d=\"M14 7l3-3 3 3-3 3z\"/></svg>`,\n}\n\ninterface MenuBuildResult {\n element: HTMLElement\n cleanup: Cleanup\n}\n\nexport const BubbleMenuPreset = Extension.create<BubbleMenuPresetOptions>({\n name: 'bubbleMenuPreset',\n\n addOptions() {\n return {\n element: null,\n items: DEFAULT_ITEMS,\n className: '',\n injectStyles: true,\n textColors: DEFAULT_COLOR_PALETTE,\n highlightColors: DEFAULT_HIGHLIGHT_PALETTE,\n fonts: DEFAULT_FONTS,\n fontSizes: DEFAULT_FONT_SIZES,\n alignments: DEFAULT_ALIGNMENTS,\n pluginKey: 'bubbleMenuPreset',\n updateDelay: 250,\n resizeDelay: 60,\n appendTo: undefined,\n shouldShow: null,\n options: {\n placement: 'top',\n strategy: 'absolute',\n offset: 8,\n flip: {},\n shift: {},\n },\n }\n },\n\n addProseMirrorPlugins() {\n const options = this.options\n const editor = this.editor\n\n const usingCustomElement = !!options.element\n const { element, cleanup } =\n options.element && typeof document !== 'undefined'\n ? { element: options.element, cleanup: () => {} }\n : buildMenuElement(editor, {\n items: options.items ?? DEFAULT_ITEMS,\n className: options.className ?? '',\n injectStyles: options.injectStyles !== false,\n textColors: options.textColors ?? DEFAULT_COLOR_PALETTE,\n highlightColors: options.highlightColors ?? DEFAULT_HIGHLIGHT_PALETTE,\n fonts: options.fonts ?? DEFAULT_FONTS,\n fontSizes: options.fontSizes ?? DEFAULT_FONT_SIZES,\n alignments: options.alignments ?? DEFAULT_ALIGNMENTS,\n })\n\n this.storage.element = element\n this.storage.cleanup = cleanup\n this.storage.usingCustomElement = usingCustomElement\n\n const pluginOptions: BubbleMenuPluginProps = {\n pluginKey: options.pluginKey ?? 'bubbleMenuPreset',\n editor,\n element: element,\n updateDelay: options.updateDelay,\n resizeDelay: options.resizeDelay,\n appendTo: options.appendTo,\n options: options.options,\n getReferencedVirtualElement: options.getReferencedVirtualElement,\n shouldShow: options.shouldShow ?? undefined,\n }\n\n return [BubbleMenuPlugin(pluginOptions)]\n },\n\n onDestroy() {\n const el = this.storage.element as HTMLElement | undefined\n const cleanup = this.storage.cleanup as Cleanup | undefined\n const usingCustomElement = this.storage.usingCustomElement as boolean | undefined\n if (cleanup) {\n cleanup()\n }\n if (el && !usingCustomElement) {\n el.remove()\n }\n },\n})\n\nfunction buildMenuElement(\n editor: Editor,\n opts: {\n items: BubbleMenuPresetItem[]\n className: string\n injectStyles: boolean\n textColors: string[]\n highlightColors: string[]\n fonts: string[]\n fontSizes: string[]\n alignments: TextAlignValue[]\n },\n): MenuBuildResult {\n if (opts.injectStyles) {\n injectStyles()\n }\n\n const element = document.createElement('div')\n element.className = `bs-bubble-menu-preset ${opts.className ?? ''}`.trim()\n element.setAttribute('data-bubble-menu-preset', 'true')\n element.tabIndex = 0\n\n const toolbar = document.createElement('div')\n toolbar.className = 'bs-bmp-toolbar'\n\n const popovers: HTMLElement[] = []\n const cleanupFns: Cleanup[] = []\n\n const getCommand = (name: string): any => (editor.commands as any)?.[name]\n const runChainCommand = (name: string, ...args: any[]) => {\n const chain = (editor.chain as any)?.()\n if (!chain || typeof chain[name] !== 'function') return false\n const runner = typeof chain.focus === 'function' ? chain.focus() : chain\n if (typeof runner[name] !== 'function') return false\n return runner[name](...args).run?.() ?? false\n }\n\n const closePopovers = () => {\n popovers.forEach((p) => p.classList.add('bs-bmp-hidden'))\n }\n\n const runWithFocus = (fn?: () => boolean) => {\n if (!fn) return false\n return !!fn()\n }\n\n const addButton = (\n item: BubbleMenuPresetItem,\n label: string,\n onClick: () => void,\n opts: { disabled?: boolean; title?: string } = {},\n ) => {\n const btn = document.createElement('button')\n btn.type = 'button'\n btn.className = `bs-bmp-btn bs-bmp-btn-${item}`\n btn.textContent = label\n if (opts.title) {\n btn.title = opts.title\n btn.setAttribute('aria-label', opts.title)\n }\n if (opts.disabled) {\n btn.disabled = true\n btn.classList.add('bs-bmp-disabled')\n } else {\n btn.addEventListener('click', () => {\n closePopovers()\n onClick()\n })\n }\n toolbar.appendChild(btn)\n }\n\n const addUndoRedo = () => {\n const hasUndo = typeof getCommand('undo') === 'function'\n const hasRedo = typeof getCommand('redo') === 'function'\n addButton('undo', DEFAULT_LABELS.undo, () => runWithFocus(() => runChainCommand('undo')), {\n disabled: !hasUndo,\n title: 'Undo',\n })\n addButton('redo', DEFAULT_LABELS.redo, () => runWithFocus(() => runChainCommand('redo')), {\n disabled: !hasRedo,\n title: 'Redo',\n })\n }\n\n const addFontFamily = () => {\n const hasCommand = typeof getCommand('setFontFamily') === 'function'\n const wrapper = document.createElement('div')\n wrapper.className = 'bs-bmp-select'\n const select = document.createElement('select')\n select.className = 'bs-bmp-select-input'\n select.title = 'Font family'\n opts.fonts.forEach((font) => {\n const option = document.createElement('option')\n option.value = font\n option.textContent = font\n option.style.fontFamily = font\n select.appendChild(option)\n })\n select.disabled = !hasCommand\n select.addEventListener('change', () => {\n runWithFocus(() => runChainCommand('setFontFamily', select.value))\n editor.commands.setMeta?.('bubbleMenu', 'updatePosition')\n })\n wrapper.appendChild(select)\n toolbar.appendChild(wrapper)\n }\n\n const addFontSize = () => {\n const hasCommand = typeof getCommand('setFontSize') === 'function'\n const wrapper = document.createElement('div')\n wrapper.className = 'bs-bmp-select'\n const select = document.createElement('select')\n select.className = 'bs-bmp-select-input'\n select.title = 'Font size'\n opts.fontSizes.forEach((size) => {\n const option = document.createElement('option')\n option.value = size\n option.textContent = size\n select.appendChild(option)\n })\n select.disabled = !hasCommand\n select.addEventListener('change', () => {\n runWithFocus(() => runChainCommand('setFontSize', select.value))\n editor.commands.setMeta?.('bubbleMenu', 'updatePosition')\n })\n wrapper.appendChild(select)\n toolbar.appendChild(wrapper)\n }\n\n const addToggleMark = (item: BubbleMenuPresetItem, commandName: string, title: string) => {\n const fn = getCommand(commandName) as (() => boolean) | undefined\n const disabled = typeof fn !== 'function'\n addButton(\n item,\n DEFAULT_LABELS[item],\n () => {\n runWithFocus(() => runChainCommand(commandName))\n },\n { disabled, title },\n )\n }\n\n const addTextColor = () => {\n const hasSet = typeof getCommand('setColor') === 'function'\n const hasUnset = typeof getCommand('unsetColor') === 'function'\n const { popover, toggle, destroy } = createColorPopover({\n label: DEFAULT_LABELS.textColor,\n title: 'Text color',\n colors: opts.textColors,\n onSelect: (color) => {\n if (!hasSet) return\n runWithFocus(() => runChainCommand('setColor', color))\n editor.commands.setMeta?.('bubbleMenu', 'updatePosition')\n },\n onClear: () => {\n if (!hasUnset) return\n runWithFocus(() => runChainCommand('unsetColor'))\n editor.commands.setMeta?.('bubbleMenu', 'updatePosition')\n },\n onToggle: () => editor.commands.setMeta?.('bubbleMenu', 'updatePosition'),\n })\n popovers.push(popover)\n cleanupFns.push(destroy)\n toolbar.appendChild(toggle)\n toolbar.appendChild(popover)\n }\n\n const addHighlightColor = () => {\n const hasToggle = typeof getCommand('toggleHighlight') === 'function'\n const hasUnset = typeof getCommand('unsetHighlight') === 'function'\n const { popover, toggle, destroy } = createColorPopover({\n label: DEFAULT_LABELS.highlightColor,\n title: 'Highlight color',\n colors: opts.highlightColors,\n onSelect: (color) => {\n if (!hasToggle) return\n runWithFocus(() => runChainCommand('toggleHighlight', { color }))\n editor.commands.setMeta?.('bubbleMenu', 'updatePosition')\n },\n onClear: () => {\n if (!hasUnset) return\n runWithFocus(() => runChainCommand('unsetHighlight'))\n editor.commands.setMeta?.('bubbleMenu', 'updatePosition')\n },\n onToggle: () => editor.commands.setMeta?.('bubbleMenu', 'updatePosition'),\n })\n popovers.push(popover)\n cleanupFns.push(destroy)\n toolbar.appendChild(toggle)\n toolbar.appendChild(popover)\n }\n\n const addLink = () => {\n const hasToggle = typeof getCommand('toggleLink') === 'function'\n const hasUnset = typeof getCommand('unsetLink') === 'function'\n addButton(\n 'link',\n DEFAULT_LABELS.link,\n () => {\n const currentHref = editor.getAttributes('link')?.href ?? ''\n const href = window.prompt('Link URL', currentHref)\n if (href === null) return\n if (!href) {\n if (hasUnset) {\n runWithFocus(() => runChainCommand('unsetLink'))\n }\n return\n }\n if (hasToggle) {\n runWithFocus(() => runChainCommand('toggleLink', { href }))\n }\n },\n { disabled: !hasToggle && !hasUnset, title: 'Insert link' },\n )\n }\n\n const addAlign = () => {\n const hasSet = typeof getCommand('setTextAlign') === 'function'\n const hasToggle = typeof getCommand('toggleTextAlign') === 'function'\n const wrapper = document.createElement('div')\n wrapper.className = 'bs-bmp-select'\n const select = document.createElement('select')\n select.className = 'bs-bmp-select-input'\n select.title = 'Align'\n opts.alignments.forEach((alignment) => {\n const option = document.createElement('option')\n option.value = alignment\n option.textContent = alignment.charAt(0).toUpperCase() + alignment.slice(1)\n select.appendChild(option)\n })\n select.disabled = !hasSet && !hasToggle\n select.addEventListener('change', () => {\n if (hasToggle) {\n runWithFocus(() => runChainCommand('toggleTextAlign', select.value))\n } else if (hasSet) {\n runWithFocus(() => runChainCommand('setTextAlign', select.value))\n }\n editor.commands.setMeta?.('bubbleMenu', 'updatePosition')\n })\n wrapper.appendChild(select)\n toolbar.appendChild(wrapper)\n }\n\n // Build in the order requested.\n opts.items.forEach((item) => {\n switch (item) {\n case 'undo':\n addUndoRedo()\n break\n case 'redo':\n // handled in undo block to keep buttons together; skip.\n break\n case 'fontFamily':\n addFontFamily()\n break\n case 'fontSize':\n addFontSize()\n break\n case 'bold':\n addToggleMark('bold', 'toggleBold', 'Bold')\n break\n case 'italic':\n addToggleMark('italic', 'toggleItalic', 'Italic')\n break\n case 'underline':\n addToggleMark('underline', 'toggleUnderline', 'Underline')\n break\n case 'textColor':\n addTextColor()\n break\n case 'highlightColor':\n addHighlightColor()\n break\n case 'link':\n addLink()\n break\n case 'align':\n addAlign()\n break\n default:\n break\n }\n })\n\n element.appendChild(toolbar)\n\n return {\n element,\n cleanup: () => {\n popovers.forEach((p) => p.remove())\n cleanupFns.forEach((fn) => fn())\n element.replaceChildren()\n },\n }\n}\n\nfunction createColorPopover(args: {\n label: string\n title: string\n colors: string[]\n onSelect: (color: string) => void\n onClear: () => void\n onToggle?: () => void\n}): { toggle: HTMLElement; popover: HTMLElement; destroy: () => void } {\n const toggle = document.createElement('button')\n toggle.type = 'button'\n toggle.className = 'bs-bmp-btn bs-bmp-btn-color'\n toggle.title = args.title\n toggle.setAttribute('aria-expanded', 'false')\n toggle.setAttribute('aria-label', args.title)\n if (args.label === DEFAULT_LABELS.textColor && ICONS.textColor) {\n toggle.innerHTML = ICONS.textColor\n } else if (args.label === DEFAULT_LABELS.highlightColor && ICONS.highlightColor) {\n toggle.innerHTML = ICONS.highlightColor\n } else {\n toggle.textContent = args.label\n }\n\n const popover = document.createElement('div')\n popover.className = 'bs-bmp-popover bs-bmp-hidden'\n popover.setAttribute('role', 'menu')\n\n const header = document.createElement('div')\n header.className = 'bs-bmp-popover-header'\n const noneBtn = document.createElement('button')\n noneBtn.type = 'button'\n noneBtn.className = 'bs-bmp-btn bs-bmp-btn-ghost'\n noneBtn.textContent = 'None'\n noneBtn.addEventListener('click', () => {\n args.onClear()\n hide()\n })\n header.appendChild(noneBtn)\n popover.appendChild(header)\n\n const grid = document.createElement('div')\n grid.className = 'bs-bmp-color-grid'\n const columns = 10\n grid.style.setProperty('--bs-bmp-grid-columns', String(columns))\n\n args.colors.forEach((color) => {\n const swatch = document.createElement('button')\n swatch.type = 'button'\n swatch.className = 'bs-bmp-color-swatch'\n swatch.style.backgroundColor = color\n swatch.setAttribute('aria-label', color)\n swatch.addEventListener('click', (event) => {\n event.stopPropagation()\n args.onSelect(color)\n hide()\n })\n grid.appendChild(swatch)\n })\n\n popover.appendChild(grid)\n\n const customRow = document.createElement('div')\n customRow.className = 'bs-bmp-popover-footer'\n const customLabel = document.createElement('span')\n customLabel.textContent = 'Custom'\n const customInput = document.createElement('input')\n customInput.type = 'color'\n customInput.className = 'bs-bmp-color-input'\n customInput.addEventListener('input', () => {\n args.onSelect(customInput.value)\n })\n customRow.appendChild(customLabel)\n customRow.appendChild(customInput)\n popover.appendChild(customRow)\n\n const hide = () => {\n popover.classList.add('bs-bmp-hidden')\n toggle.setAttribute('aria-expanded', 'false')\n args.onToggle?.()\n }\n\n const show = () => {\n popover.classList.remove('bs-bmp-hidden')\n toggle.setAttribute('aria-expanded', 'true')\n args.onToggle?.()\n }\n\n const toggleHandler = (event: MouseEvent) => {\n event.stopPropagation()\n if (popover.classList.contains('bs-bmp-hidden')) {\n show()\n } else {\n hide()\n }\n }\n toggle.addEventListener('click', toggleHandler)\n\n const outsideHandler = (event: MouseEvent) => {\n if (popover.contains(event.target as Node) || toggle.contains(event.target as Node)) {\n return\n }\n hide()\n }\n\n document.addEventListener(\n 'click',\n outsideHandler,\n { capture: true },\n )\n\n const destroy = () => {\n document.removeEventListener('click', outsideHandler, { capture: true } as any)\n toggle.removeEventListener('click', toggleHandler)\n }\n\n return { toggle, popover, destroy }\n}\n\nfunction injectStyles() {\n if (typeof document === 'undefined') return\n if (document.getElementById(STYLE_ID)) return\n\n const style = document.createElement('style')\n style.id = STYLE_ID\n style.textContent = `\n.bs-bubble-menu-preset {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n background: #ffffff;\n border: 1px solid #e5e7eb;\n padding: 8px 10px;\n border-radius: 10px;\n box-shadow: 0 10px 30px rgba(0,0,0,0.12);\n color: #111827;\n font-family: Inter, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n z-index: 9999;\n}\n.bs-bmp-toolbar {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n}\n.bs-bmp-btn {\n border: none;\n background: transparent;\n color: inherit;\n padding: 6px 8px;\n border-radius: 12px;\n cursor: pointer;\n font-size: 13px;\n line-height: 1;\n transition: background-color 120ms ease, color 120ms ease, box-shadow 120ms ease;\n}\n.bs-bmp-btn:hover {\n background: #f3f4f6;\n}\n.bs-bmp-btn:disabled {\n opacity: 0.45;\n cursor: not-allowed;\n}\n.bs-bmp-select {\n display: inline-flex;\n align-items: center;\n}\n.bs-bmp-select-input {\n border: 1px solid #e5e7eb;\n background: #ffffff;\n color: inherit;\n padding: 6px 12px 6px 10px;\n border-radius: 12px;\n font-size: 13px;\n outline: none;\n}\n.bs-bmp-select-input:focus {\n border-color: #4f46e5;\n box-shadow: 0 0 0 2px rgba(79,70,229,0.15);\n}\n.bs-bmp-popover {\n position: absolute;\n margin-top: 6px;\n padding: 10px;\n background: #ffffff;\n border: 1px solid #e5e7eb;\n border-radius: 12px;\n box-shadow: 0 12px 40px rgba(15, 23, 42, 0.18);\n z-index: 999;\n}\n.bs-bmp-hidden {\n display: none;\n}\n.bs-bmp-popover-header,\n.bs-bmp-popover-footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 6px;\n}\n.bs-bmp-btn-ghost {\n background: transparent;\n border: none;\n color: inherit;\n padding: 4px 6px;\n border-radius: 8px;\n cursor: pointer;\n}\n.bs-bmp-btn-ghost:hover {\n background: #f3f4f6;\n}\n.bs-bmp-color-grid {\n display: grid;\n grid-template-columns: repeat(var(--bs-bmp-grid-columns, 10), 22px);\n gap: 4px;\n margin-bottom: 8px;\n}\n.bs-bmp-color-swatch {\n width: 22px;\n height: 22px;\n border-radius: 50%;\n border: 1px solid #e5e7eb;\n cursor: pointer;\n padding: 0;\n}\n.bs-bmp-color-swatch:hover {\n outline: 2px solid #4f46e5;\n outline-offset: 2px;\n}\n.bs-bmp-color-input {\n width: 32px;\n height: 28px;\n padding: 0;\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n background: #ffffff;\n}\n`\n\n document.head.appendChild(style)\n}\n\n\n\n\n\n"],"mappings":"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","names":["_a","opts"]}
|
|
1
|
+
{"version":3,"sources":["../src/bubble-menu-preset.ts"],"sourcesContent":["import { Extension } from '@blockslides/core'\nimport type { Editor } from '@blockslides/core'\nimport {\n BubbleMenuPlugin,\n type BubbleMenuPluginProps,\n type BubbleMenuOptions,\n} from '@blockslides/extension-bubble-menu'\n\ntype BubbleMenuPresetItem =\n | 'undo'\n | 'redo'\n | 'fontFamily'\n | 'fontSize'\n | 'bold'\n | 'italic'\n | 'underline'\n | 'textColor'\n | 'highlightColor'\n | 'link'\n | 'align'\n\ntype TextAlignValue = 'left' | 'center' | 'right' | 'justify'\n\nexport interface BubbleMenuPresetOptions\n extends Omit<BubbleMenuOptions, 'element'> {\n /**\n * Optional custom element to use for the menu. If omitted, a default\n * element with built-in buttons is rendered.\n */\n element?: HTMLElement | null\n\n /**\n * Order of built-in controls to render.\n */\n items?: BubbleMenuPresetItem[]\n\n /**\n * Additional class names to attach to the menu element to allow easy\n * style overrides.\n */\n className?: string\n\n /**\n * Inject default CSS. Set to false to opt out if you provide your own styles.\n */\n injectStyles?: boolean\n\n /**\n * Palette for text color swatches.\n */\n textColors?: string[]\n\n /**\n * Palette for highlight swatches.\n */\n highlightColors?: string[]\n\n /**\n * Fonts exposed in the font picker.\n */\n fonts?: string[]\n\n /**\n * Font sizes (any CSS length, e.g. \"16px\", \"1rem\").\n */\n fontSizes?: string[]\n\n /**\n * Alignments to expose in the align control.\n */\n alignments?: TextAlignValue[]\n}\n\ntype Cleanup = () => void\n\nconst STYLE_ID = 'blockslides-bubble-menu-preset-styles'\n\nconst DEFAULT_ITEMS: BubbleMenuPresetItem[] = [\n 'undo',\n 'redo',\n 'fontFamily',\n 'fontSize',\n 'bold',\n 'italic',\n 'underline',\n 'textColor',\n 'highlightColor',\n 'link',\n 'align',\n]\n\nconst DEFAULT_FONTS = [\n 'Inter',\n 'Arial',\n 'Helvetica',\n 'Times New Roman',\n 'Georgia',\n 'Courier New',\n 'Monaco',\n]\n\nconst DEFAULT_FONT_SIZES = ['12px', '14px', '16px', '18px', '20px', '24px', '32px', '40px']\n\nconst DEFAULT_ALIGNMENTS: TextAlignValue[] = ['left', 'center', 'right', 'justify']\n\n// A rich palette approximating the attached reference.\nconst DEFAULT_COLOR_PALETTE: string[] = [\n '#000000',\n '#434343',\n '#666666',\n '#999999',\n '#b7b7b7',\n '#cccccc',\n '#d9d9d9',\n '#efefef',\n '#f3f3f3',\n '#ffffff',\n '#e60000',\n '#ff0000',\n '#ff9900',\n '#ffff00',\n '#00ff00',\n '#00ffff',\n '#4a86e8',\n '#0000ff',\n '#9900ff',\n '#ff00ff',\n '#f4cccc',\n '#fce5cd',\n '#fff2cc',\n '#d9ead3',\n '#d0e0e3',\n '#cfe2f3',\n '#d9d2e9',\n '#ead1dc',\n '#f9cb9c',\n '#ffe599',\n '#b6d7a8',\n '#a2c4c9',\n '#9fc5e8',\n '#b4a7d6',\n '#d5a6bd',\n '#e06666',\n '#f6b26b',\n '#ffd966',\n '#93c47d',\n '#76a5af',\n '#6fa8dc',\n '#8e7cc3',\n '#c27ba0',\n '#cc0000',\n '#e69138',\n '#f1c232',\n '#6aa84f',\n '#45818e',\n '#3d85c6',\n '#674ea7',\n '#a64d79',\n '#990000',\n '#b45f06',\n '#bf9000',\n '#38761d',\n '#134f5c',\n '#0b5394',\n '#351c75',\n '#741b47',\n '#660000',\n '#783f04',\n '#7f6000',\n '#274e13',\n '#0c343d',\n '#073763',\n '#20124d',\n '#4c1130',\n]\n\nconst DEFAULT_HIGHLIGHT_PALETTE = DEFAULT_COLOR_PALETTE\n\nconst DEFAULT_LABELS: Record<BubbleMenuPresetItem, string> = {\n undo: 'Undo',\n redo: 'Redo',\n fontFamily: 'Font',\n fontSize: 'Size',\n bold: 'B',\n italic: 'I',\n underline: 'U',\n textColor: 'A',\n highlightColor: 'Hi',\n link: 'Link',\n align: 'Align',\n}\n\nconst ICONS = {\n textColor: `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M5 20h14\"/><path d=\"M7 20l5-14 5 14\"/><path d=\"M10.7 14h2.6\"/></svg>`,\n highlightColor: `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m11 18 2-2h6\"/><path d=\"m2 22 3-3h6l3-3-6-6-3 3v6z\"/><path d=\"M14 7l3-3 3 3-3 3z\"/></svg>`,\n}\n\ninterface MenuBuildResult {\n element: HTMLElement\n cleanup: Cleanup\n}\n\nexport const BubbleMenuPreset = Extension.create<BubbleMenuPresetOptions>({\n name: 'bubbleMenuPreset',\n\n addOptions() {\n return {\n element: null,\n items: DEFAULT_ITEMS,\n className: '',\n injectStyles: true,\n textColors: DEFAULT_COLOR_PALETTE,\n highlightColors: DEFAULT_HIGHLIGHT_PALETTE,\n fonts: DEFAULT_FONTS,\n fontSizes: DEFAULT_FONT_SIZES,\n alignments: DEFAULT_ALIGNMENTS,\n pluginKey: 'bubbleMenuPreset',\n updateDelay: 250,\n resizeDelay: 60,\n appendTo: undefined,\n shouldShow: null,\n options: {\n placement: 'top',\n strategy: 'absolute',\n offset: 8,\n flip: {},\n shift: {},\n },\n }\n },\n\n addProseMirrorPlugins() {\n const options = this.options\n const editor = this.editor\n\n const usingCustomElement = !!options.element\n const { element, cleanup } =\n options.element && typeof document !== 'undefined'\n ? { element: options.element, cleanup: () => {} }\n : buildMenuElement(editor, {\n items: options.items ?? DEFAULT_ITEMS,\n className: options.className ?? '',\n injectStyles: options.injectStyles !== false,\n textColors: options.textColors ?? DEFAULT_COLOR_PALETTE,\n highlightColors: options.highlightColors ?? DEFAULT_HIGHLIGHT_PALETTE,\n fonts: options.fonts ?? DEFAULT_FONTS,\n fontSizes: options.fontSizes ?? DEFAULT_FONT_SIZES,\n alignments: options.alignments ?? DEFAULT_ALIGNMENTS,\n })\n\n this.storage.element = element\n this.storage.cleanup = cleanup\n this.storage.usingCustomElement = usingCustomElement\n\n const pluginOptions: BubbleMenuPluginProps = {\n pluginKey: options.pluginKey ?? 'bubbleMenuPreset',\n editor,\n element: element,\n updateDelay: options.updateDelay,\n resizeDelay: options.resizeDelay,\n appendTo: options.appendTo,\n options: options.options,\n getReferencedVirtualElement: options.getReferencedVirtualElement,\n shouldShow: options.shouldShow ?? undefined,\n }\n\n return [BubbleMenuPlugin(pluginOptions)]\n },\n\n onDestroy() {\n const el = this.storage.element as HTMLElement | undefined\n const cleanup = this.storage.cleanup as Cleanup | undefined\n const usingCustomElement = this.storage.usingCustomElement as boolean | undefined\n if (cleanup) {\n cleanup()\n }\n if (el && !usingCustomElement) {\n el.remove()\n }\n },\n})\n\nfunction buildMenuElement(\n editor: Editor,\n opts: {\n items: BubbleMenuPresetItem[]\n className: string\n injectStyles: boolean\n textColors: string[]\n highlightColors: string[]\n fonts: string[]\n fontSizes: string[]\n alignments: TextAlignValue[]\n },\n): MenuBuildResult {\n if (opts.injectStyles) {\n injectStyles()\n }\n\n const element = document.createElement('div')\n element.className = `bs-bubble-menu-preset ${opts.className ?? ''}`.trim()\n element.setAttribute('data-bubble-menu-preset', 'true')\n element.tabIndex = 0\n\n const toolbar = document.createElement('div')\n toolbar.className = 'bs-bmp-toolbar'\n\n let fontFamilySelect: HTMLSelectElement | null = null\n let fontSizeSelect: HTMLSelectElement | null = null\n\n const normalizeFontFamily = (value?: string | null) => {\n if (!value) return value\n // take the first family name and strip quotes\n return value.split(',')[0]?.trim().replace(/^['\"]|['\"]$/g, '') || value\n }\n\n const getComputedFontStyles = () => {\n if (typeof window === 'undefined') return { fontFamily: undefined, fontSize: undefined }\n const sel = window.getSelection()\n const node = sel?.anchorNode\n const el = (node instanceof HTMLElement ? node : node?.parentElement) ?? undefined\n if (!el) return { fontFamily: undefined, fontSize: undefined }\n const styles = window.getComputedStyle(el)\n return {\n fontFamily: normalizeFontFamily(styles.fontFamily || undefined),\n fontSize: styles.fontSize || undefined,\n }\n }\n\n const ensureOptionExists = (select: HTMLSelectElement, value: string) => {\n const exists = Array.from(select.options).some(opt => opt.value === value)\n if (!exists) {\n const option = document.createElement('option')\n option.value = value\n option.textContent = value\n select.appendChild(option)\n }\n }\n\n const setSelectValue = (select: HTMLSelectElement | null, value?: string | null) => {\n if (!select) return\n if (!value) {\n select.selectedIndex = -1\n return\n }\n ensureOptionExists(select, value)\n select.value = value\n }\n\n const popovers: HTMLElement[] = []\n const cleanupFns: Cleanup[] = []\n\n const getCommand = (name: string): any => (editor.commands as any)?.[name]\n const runChainCommand = (name: string, ...args: any[]) => {\n const chain = (editor.chain as any)?.()\n if (!chain || typeof chain[name] !== 'function') return false\n const runner = typeof chain.focus === 'function' ? chain.focus() : chain\n if (typeof runner[name] !== 'function') return false\n return runner[name](...args).run?.() ?? false\n }\n\n const closePopovers = () => {\n popovers.forEach((p) => p.classList.add('bs-bmp-hidden'))\n }\n\n const runWithFocus = (fn?: () => boolean) => {\n if (!fn) return false\n return !!fn()\n }\n\n const addButton = (\n item: BubbleMenuPresetItem,\n label: string,\n onClick: () => void,\n opts: { disabled?: boolean; title?: string } = {},\n ) => {\n const btn = document.createElement('button')\n btn.type = 'button'\n btn.className = `bs-bmp-btn bs-bmp-btn-${item}`\n btn.textContent = label\n if (opts.title) {\n btn.title = opts.title\n btn.setAttribute('aria-label', opts.title)\n }\n if (opts.disabled) {\n btn.disabled = true\n btn.classList.add('bs-bmp-disabled')\n } else {\n btn.addEventListener('click', () => {\n closePopovers()\n onClick()\n })\n }\n toolbar.appendChild(btn)\n }\n\n const addUndoRedo = () => {\n const hasUndo = typeof getCommand('undo') === 'function'\n const hasRedo = typeof getCommand('redo') === 'function'\n addButton('undo', DEFAULT_LABELS.undo, () => runWithFocus(() => runChainCommand('undo')), {\n disabled: !hasUndo,\n title: 'Undo',\n })\n addButton('redo', DEFAULT_LABELS.redo, () => runWithFocus(() => runChainCommand('redo')), {\n disabled: !hasRedo,\n title: 'Redo',\n })\n }\n\n const addFontFamily = () => {\n const hasCommand = typeof getCommand('setFontFamily') === 'function'\n const wrapper = document.createElement('div')\n wrapper.className = 'bs-bmp-select'\n const select = document.createElement('select')\n select.className = 'bs-bmp-select-input'\n select.title = 'Font family'\n opts.fonts.forEach((font) => {\n const option = document.createElement('option')\n option.value = font\n option.textContent = font\n option.style.fontFamily = font\n select.appendChild(option)\n })\n select.disabled = !hasCommand\n select.addEventListener('change', () => {\n runWithFocus(() => runChainCommand('setFontFamily', select.value))\n editor.commands.setMeta?.('bubbleMenu', 'updatePosition')\n })\n fontFamilySelect = select\n wrapper.appendChild(select)\n toolbar.appendChild(wrapper)\n }\n\n const addFontSize = () => {\n const hasCommand = typeof getCommand('setFontSize') === 'function'\n const wrapper = document.createElement('div')\n wrapper.className = 'bs-bmp-select'\n const select = document.createElement('select')\n select.className = 'bs-bmp-select-input'\n select.title = 'Font size'\n opts.fontSizes.forEach((size) => {\n const option = document.createElement('option')\n option.value = size\n option.textContent = size\n select.appendChild(option)\n })\n select.disabled = !hasCommand\n select.addEventListener('change', () => {\n runWithFocus(() => runChainCommand('setFontSize', select.value))\n editor.commands.setMeta?.('bubbleMenu', 'updatePosition')\n })\n fontSizeSelect = select\n wrapper.appendChild(select)\n toolbar.appendChild(wrapper)\n }\n\n const addToggleMark = (item: BubbleMenuPresetItem, commandName: string, title: string) => {\n const fn = getCommand(commandName) as (() => boolean) | undefined\n const disabled = typeof fn !== 'function'\n addButton(\n item,\n DEFAULT_LABELS[item],\n () => {\n runWithFocus(() => runChainCommand(commandName))\n },\n { disabled, title },\n )\n }\n\n const addTextColor = () => {\n const hasSet = typeof getCommand('setColor') === 'function'\n const hasUnset = typeof getCommand('unsetColor') === 'function'\n const { popover, toggle, destroy } = createColorPopover({\n label: DEFAULT_LABELS.textColor,\n title: 'Text color',\n colors: opts.textColors,\n onSelect: (color) => {\n if (!hasSet) return\n runWithFocus(() => runChainCommand('setColor', color))\n editor.commands.setMeta?.('bubbleMenu', 'updatePosition')\n },\n onClear: () => {\n if (!hasUnset) return\n runWithFocus(() => runChainCommand('unsetColor'))\n editor.commands.setMeta?.('bubbleMenu', 'updatePosition')\n },\n onToggle: () => editor.commands.setMeta?.('bubbleMenu', 'updatePosition'),\n })\n popovers.push(popover)\n cleanupFns.push(destroy)\n toolbar.appendChild(toggle)\n toolbar.appendChild(popover)\n }\n\n const addHighlightColor = () => {\n const hasToggle = typeof getCommand('toggleHighlight') === 'function'\n const hasUnset = typeof getCommand('unsetHighlight') === 'function'\n const { popover, toggle, destroy } = createColorPopover({\n label: DEFAULT_LABELS.highlightColor,\n title: 'Highlight color',\n colors: opts.highlightColors,\n onSelect: (color) => {\n if (!hasToggle) return\n runWithFocus(() => runChainCommand('toggleHighlight', { color }))\n editor.commands.setMeta?.('bubbleMenu', 'updatePosition')\n },\n onClear: () => {\n if (!hasUnset) return\n runWithFocus(() => runChainCommand('unsetHighlight'))\n editor.commands.setMeta?.('bubbleMenu', 'updatePosition')\n },\n onToggle: () => editor.commands.setMeta?.('bubbleMenu', 'updatePosition'),\n })\n popovers.push(popover)\n cleanupFns.push(destroy)\n toolbar.appendChild(toggle)\n toolbar.appendChild(popover)\n }\n\n const addLink = () => {\n const hasToggle = typeof getCommand('toggleLink') === 'function'\n const hasUnset = typeof getCommand('unsetLink') === 'function'\n addButton(\n 'link',\n DEFAULT_LABELS.link,\n () => {\n const currentHref = editor.getAttributes('link')?.href ?? ''\n const href = window.prompt('Link URL', currentHref)\n if (href === null) return\n if (!href) {\n if (hasUnset) {\n runWithFocus(() => runChainCommand('unsetLink'))\n }\n return\n }\n if (hasToggle) {\n runWithFocus(() => runChainCommand('toggleLink', { href }))\n }\n },\n { disabled: !hasToggle && !hasUnset, title: 'Insert link' },\n )\n }\n\n const addAlign = () => {\n const hasSet = typeof getCommand('setTextAlign') === 'function'\n const hasToggle = typeof getCommand('toggleTextAlign') === 'function'\n const wrapper = document.createElement('div')\n wrapper.className = 'bs-bmp-select'\n const select = document.createElement('select')\n select.className = 'bs-bmp-select-input'\n select.title = 'Align'\n opts.alignments.forEach((alignment) => {\n const option = document.createElement('option')\n option.value = alignment\n option.textContent = alignment.charAt(0).toUpperCase() + alignment.slice(1)\n select.appendChild(option)\n })\n select.disabled = !hasSet && !hasToggle\n select.addEventListener('change', () => {\n if (hasToggle) {\n runWithFocus(() => runChainCommand('toggleTextAlign', select.value))\n } else if (hasSet) {\n runWithFocus(() => runChainCommand('setTextAlign', select.value))\n }\n editor.commands.setMeta?.('bubbleMenu', 'updatePosition')\n })\n wrapper.appendChild(select)\n toolbar.appendChild(wrapper)\n }\n\n // Build in the order requested.\n opts.items.forEach((item) => {\n switch (item) {\n case 'undo':\n addUndoRedo()\n break\n case 'redo':\n // handled in undo block to keep buttons together; skip.\n break\n case 'fontFamily':\n addFontFamily()\n break\n case 'fontSize':\n addFontSize()\n break\n case 'bold':\n addToggleMark('bold', 'toggleBold', 'Bold')\n break\n case 'italic':\n addToggleMark('italic', 'toggleItalic', 'Italic')\n break\n case 'underline':\n addToggleMark('underline', 'toggleUnderline', 'Underline')\n break\n case 'textColor':\n addTextColor()\n break\n case 'highlightColor':\n addHighlightColor()\n break\n case 'link':\n addLink()\n break\n case 'align':\n addAlign()\n break\n default:\n break\n }\n })\n\n element.appendChild(toolbar)\n\n const syncSelectionState = () => {\n const attrs = editor.getAttributes('textStyle') || {}\n let family = attrs.fontFamily\n let size = attrs.fontSize\n\n // If not explicitly set via marks, fall back to computed DOM styles\n if (!family || !size) {\n const computed = getComputedFontStyles()\n family = family || computed.fontFamily\n size = size || computed.fontSize\n }\n\n setSelectValue(fontFamilySelect, normalizeFontFamily(family))\n setSelectValue(fontSizeSelect, size)\n }\n\n const handleSelectionUpdate = () => syncSelectionState()\n const handleTransaction = ({ transaction }: { transaction?: any }) => {\n if (!transaction || transaction.docChanged || transaction.selectionSet) {\n syncSelectionState()\n }\n }\n\n editor.on('selectionUpdate', handleSelectionUpdate)\n editor.on('transaction', handleTransaction)\n cleanupFns.push(() => {\n editor.off('selectionUpdate', handleSelectionUpdate)\n editor.off('transaction', handleTransaction)\n })\n\n syncSelectionState()\n\n return {\n element,\n cleanup: () => {\n popovers.forEach((p) => p.remove())\n cleanupFns.forEach((fn) => fn())\n element.replaceChildren()\n },\n }\n}\n\nfunction createColorPopover(args: {\n label: string\n title: string\n colors: string[]\n onSelect: (color: string) => void\n onClear: () => void\n onToggle?: () => void\n}): { toggle: HTMLElement; popover: HTMLElement; destroy: () => void } {\n const toggle = document.createElement('button')\n toggle.type = 'button'\n toggle.className = 'bs-bmp-btn bs-bmp-btn-color'\n toggle.title = args.title\n toggle.setAttribute('aria-expanded', 'false')\n toggle.setAttribute('aria-label', args.title)\n if (args.label === DEFAULT_LABELS.textColor && ICONS.textColor) {\n toggle.innerHTML = ICONS.textColor\n } else if (args.label === DEFAULT_LABELS.highlightColor && ICONS.highlightColor) {\n toggle.innerHTML = ICONS.highlightColor\n } else {\n toggle.textContent = args.label\n }\n\n const popover = document.createElement('div')\n popover.className = 'bs-bmp-popover bs-bmp-hidden'\n popover.setAttribute('role', 'menu')\n\n const header = document.createElement('div')\n header.className = 'bs-bmp-popover-header'\n const noneBtn = document.createElement('button')\n noneBtn.type = 'button'\n noneBtn.className = 'bs-bmp-btn bs-bmp-btn-ghost'\n noneBtn.textContent = 'None'\n noneBtn.addEventListener('click', () => {\n args.onClear()\n hide()\n })\n header.appendChild(noneBtn)\n popover.appendChild(header)\n\n const grid = document.createElement('div')\n grid.className = 'bs-bmp-color-grid'\n const columns = 10\n grid.style.setProperty('--bs-bmp-grid-columns', String(columns))\n\n args.colors.forEach((color) => {\n const swatch = document.createElement('button')\n swatch.type = 'button'\n swatch.className = 'bs-bmp-color-swatch'\n swatch.style.backgroundColor = color\n swatch.setAttribute('aria-label', color)\n swatch.addEventListener('click', (event) => {\n event.stopPropagation()\n args.onSelect(color)\n hide()\n })\n grid.appendChild(swatch)\n })\n\n popover.appendChild(grid)\n\n const customRow = document.createElement('div')\n customRow.className = 'bs-bmp-popover-footer'\n const customLabel = document.createElement('span')\n customLabel.textContent = 'Custom'\n const customInput = document.createElement('input')\n customInput.type = 'color'\n customInput.className = 'bs-bmp-color-input'\n customInput.addEventListener('input', () => {\n args.onSelect(customInput.value)\n })\n customRow.appendChild(customLabel)\n customRow.appendChild(customInput)\n popover.appendChild(customRow)\n\n const hide = () => {\n popover.classList.add('bs-bmp-hidden')\n toggle.setAttribute('aria-expanded', 'false')\n args.onToggle?.()\n }\n\n const show = () => {\n popover.classList.remove('bs-bmp-hidden')\n toggle.setAttribute('aria-expanded', 'true')\n args.onToggle?.()\n }\n\n const toggleHandler = (event: MouseEvent) => {\n event.stopPropagation()\n if (popover.classList.contains('bs-bmp-hidden')) {\n show()\n } else {\n hide()\n }\n }\n toggle.addEventListener('click', toggleHandler)\n\n const outsideHandler = (event: MouseEvent) => {\n if (popover.contains(event.target as Node) || toggle.contains(event.target as Node)) {\n return\n }\n hide()\n }\n\n document.addEventListener(\n 'click',\n outsideHandler,\n { capture: true },\n )\n\n const destroy = () => {\n document.removeEventListener('click', outsideHandler, { capture: true } as any)\n toggle.removeEventListener('click', toggleHandler)\n }\n\n return { toggle, popover, destroy }\n}\n\nfunction injectStyles() {\n if (typeof document === 'undefined') return\n if (document.getElementById(STYLE_ID)) return\n\n const style = document.createElement('style')\n style.id = STYLE_ID\n style.textContent = `\n.bs-bubble-menu-preset {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n background: #ffffff;\n border: 1px solid #e5e7eb;\n padding: 8px 10px;\n border-radius: 10px;\n box-shadow: 0 10px 30px rgba(0,0,0,0.12);\n color: #111827;\n font-family: Inter, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n z-index: 9999;\n}\n.bs-bmp-toolbar {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n}\n.bs-bmp-btn {\n border: none;\n background: transparent;\n color: inherit;\n padding: 6px 8px;\n border-radius: 12px;\n cursor: pointer;\n font-size: 13px;\n line-height: 1;\n transition: background-color 120ms ease, color 120ms ease, box-shadow 120ms ease;\n}\n.bs-bmp-btn:hover {\n background: #f3f4f6;\n}\n.bs-bmp-btn:disabled {\n opacity: 0.45;\n cursor: not-allowed;\n}\n.bs-bmp-select {\n display: inline-flex;\n align-items: center;\n}\n.bs-bmp-select-input {\n border: 1px solid #e5e7eb;\n background: #ffffff;\n color: inherit;\n padding: 6px 12px 6px 10px;\n border-radius: 12px;\n font-size: 13px;\n outline: none;\n}\n.bs-bmp-select-input:focus {\n border-color: #4f46e5;\n box-shadow: 0 0 0 2px rgba(79,70,229,0.15);\n}\n.bs-bmp-popover {\n position: absolute;\n margin-top: 6px;\n padding: 10px;\n background: #ffffff;\n border: 1px solid #e5e7eb;\n border-radius: 12px;\n box-shadow: 0 12px 40px rgba(15, 23, 42, 0.18);\n z-index: 999;\n}\n.bs-bmp-hidden {\n display: none;\n}\n.bs-bmp-popover-header,\n.bs-bmp-popover-footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 6px;\n}\n.bs-bmp-btn-ghost {\n background: transparent;\n border: none;\n color: inherit;\n padding: 4px 6px;\n border-radius: 8px;\n cursor: pointer;\n}\n.bs-bmp-btn-ghost:hover {\n background: #f3f4f6;\n}\n.bs-bmp-color-grid {\n display: grid;\n grid-template-columns: repeat(var(--bs-bmp-grid-columns, 10), 22px);\n gap: 4px;\n margin-bottom: 8px;\n}\n.bs-bmp-color-swatch {\n width: 22px;\n height: 22px;\n border-radius: 50%;\n border: 1px solid #e5e7eb;\n cursor: pointer;\n padding: 0;\n}\n.bs-bmp-color-swatch:hover {\n outline: 2px solid #4f46e5;\n outline-offset: 2px;\n}\n.bs-bmp-color-input {\n width: 32px;\n height: 28px;\n padding: 0;\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n background: #ffffff;\n}\n`\n\n document.head.appendChild(style)\n}\n\n\n\n\n\n"],"mappings":";AAAA,SAAS,iBAAiB;AAE1B;AAAA,EACE;AAAA,OAGK;AAqEP,IAAM,WAAW;AAEjB,IAAM,gBAAwC;AAAA,EAC5C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,gBAAgB;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,qBAAqB,CAAC,QAAQ,QAAQ,QAAQ,QAAQ,QAAQ,QAAQ,QAAQ,MAAM;AAE1F,IAAM,qBAAuC,CAAC,QAAQ,UAAU,SAAS,SAAS;AAGlF,IAAM,wBAAkC;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,4BAA4B;AAElC,IAAM,iBAAuD;AAAA,EAC3D,MAAM;AAAA,EACN,MAAM;AAAA,EACN,YAAY;AAAA,EACZ,UAAU;AAAA,EACV,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,WAAW;AAAA,EACX,gBAAgB;AAAA,EAChB,MAAM;AAAA,EACN,OAAO;AACT;AAEA,IAAM,QAAQ;AAAA,EACZ,WAAW;AAAA,EACX,gBAAgB;AAClB;AAOO,IAAM,mBAAmB,UAAU,OAAgC;AAAA,EACxE,MAAM;AAAA,EAEN,aAAa;AACX,WAAO;AAAA,MACL,SAAS;AAAA,MACT,OAAO;AAAA,MACP,WAAW;AAAA,MACX,cAAc;AAAA,MACd,YAAY;AAAA,MACZ,iBAAiB;AAAA,MACjB,OAAO;AAAA,MACP,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,WAAW;AAAA,MACX,aAAa;AAAA,MACb,aAAa;AAAA,MACb,UAAU;AAAA,MACV,YAAY;AAAA,MACZ,SAAS;AAAA,QACP,WAAW;AAAA,QACX,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,MAAM,CAAC;AAAA,QACP,OAAO,CAAC;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAAA,EAEA,wBAAwB;AAvO1B;AAwOI,UAAM,UAAU,KAAK;AACrB,UAAM,SAAS,KAAK;AAEpB,UAAM,qBAAqB,CAAC,CAAC,QAAQ;AACrC,UAAM,EAAE,SAAS,QAAQ,IACvB,QAAQ,WAAW,OAAO,aAAa,cACnC,EAAE,SAAS,QAAQ,SAAS,SAAS,MAAM;AAAA,IAAC,EAAE,IAC9C,iBAAiB,QAAQ;AAAA,MACvB,QAAO,aAAQ,UAAR,YAAiB;AAAA,MACxB,YAAW,aAAQ,cAAR,YAAqB;AAAA,MAChC,cAAc,QAAQ,iBAAiB;AAAA,MACvC,aAAY,aAAQ,eAAR,YAAsB;AAAA,MAClC,kBAAiB,aAAQ,oBAAR,YAA2B;AAAA,MAC5C,QAAO,aAAQ,UAAR,YAAiB;AAAA,MACxB,YAAW,aAAQ,cAAR,YAAqB;AAAA,MAChC,aAAY,aAAQ,eAAR,YAAsB;AAAA,IACpC,CAAC;AAEP,SAAK,QAAQ,UAAU;AACvB,SAAK,QAAQ,UAAU;AACvB,SAAK,QAAQ,qBAAqB;AAElC,UAAM,gBAAuC;AAAA,MAC3C,YAAW,aAAQ,cAAR,YAAqB;AAAA,MAChC;AAAA,MACA;AAAA,MACA,aAAa,QAAQ;AAAA,MACrB,aAAa,QAAQ;AAAA,MACrB,UAAU,QAAQ;AAAA,MAClB,SAAS,QAAQ;AAAA,MACjB,6BAA6B,QAAQ;AAAA,MACrC,aAAY,aAAQ,eAAR,YAAsB;AAAA,IACpC;AAEA,WAAO,CAAC,iBAAiB,aAAa,CAAC;AAAA,EACzC;AAAA,EAEA,YAAY;AACV,UAAM,KAAK,KAAK,QAAQ;AACxB,UAAM,UAAU,KAAK,QAAQ;AAC7B,UAAM,qBAAqB,KAAK,QAAQ;AACxC,QAAI,SAAS;AACX,cAAQ;AAAA,IACV;AACA,QAAI,MAAM,CAAC,oBAAoB;AAC7B,SAAG,OAAO;AAAA,IACZ;AAAA,EACF;AACF,CAAC;AAED,SAAS,iBACP,QACA,MAUiB;AAtSnB;AAuSE,MAAI,KAAK,cAAc;AACrB,iBAAa;AAAA,EACf;AAEA,QAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,UAAQ,YAAY,0BAAyB,UAAK,cAAL,YAAkB,EAAE,GAAG,KAAK;AACzE,UAAQ,aAAa,2BAA2B,MAAM;AACtD,UAAQ,WAAW;AAEnB,QAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,UAAQ,YAAY;AAEpB,MAAI,mBAA6C;AACjD,MAAI,iBAA2C;AAE/C,QAAM,sBAAsB,CAAC,UAA0B;AAtTzD,QAAAA;AAuTI,QAAI,CAAC,MAAO,QAAO;AAEnB,aAAOA,MAAA,MAAM,MAAM,GAAG,EAAE,CAAC,MAAlB,gBAAAA,IAAqB,OAAO,QAAQ,gBAAgB,QAAO;AAAA,EACpE;AAEA,QAAM,wBAAwB,MAAM;AA5TtC,QAAAA;AA6TI,QAAI,OAAO,WAAW,YAAa,QAAO,EAAE,YAAY,QAAW,UAAU,OAAU;AACvF,UAAM,MAAM,OAAO,aAAa;AAChC,UAAM,OAAO,2BAAK;AAClB,UAAM,MAAMA,MAAA,gBAAgB,cAAc,OAAO,6BAAM,kBAA3C,OAAAA,MAA6D;AACzE,QAAI,CAAC,GAAI,QAAO,EAAE,YAAY,QAAW,UAAU,OAAU;AAC7D,UAAM,SAAS,OAAO,iBAAiB,EAAE;AACzC,WAAO;AAAA,MACL,YAAY,oBAAoB,OAAO,cAAc,MAAS;AAAA,MAC9D,UAAU,OAAO,YAAY;AAAA,IAC/B;AAAA,EACF;AAEA,QAAM,qBAAqB,CAAC,QAA2B,UAAkB;AACvE,UAAM,SAAS,MAAM,KAAK,OAAO,OAAO,EAAE,KAAK,SAAO,IAAI,UAAU,KAAK;AACzE,QAAI,CAAC,QAAQ;AACX,YAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,aAAO,QAAQ;AACf,aAAO,cAAc;AACrB,aAAO,YAAY,MAAM;AAAA,IAC3B;AAAA,EACF;AAEA,QAAM,iBAAiB,CAAC,QAAkC,UAA0B;AAClF,QAAI,CAAC,OAAQ;AACb,QAAI,CAAC,OAAO;AACV,aAAO,gBAAgB;AACvB;AAAA,IACF;AACA,uBAAmB,QAAQ,KAAK;AAChC,WAAO,QAAQ;AAAA,EACjB;AAEA,QAAM,WAA0B,CAAC;AACjC,QAAM,aAAwB,CAAC;AAE/B,QAAM,aAAa,CAAC,SAAmB;AAhWzC,QAAAA;AAgW6C,YAAAA,MAAA,OAAO,aAAP,gBAAAA,IAA0B;AAAA;AACrE,QAAM,kBAAkB,CAAC,SAAiB,SAAgB;AAjW5D,QAAAA,KAAA;AAkWI,UAAM,SAASA,MAAA,OAAO,UAAP,gBAAAA,IAAA;AACf,QAAI,CAAC,SAAS,OAAO,MAAM,IAAI,MAAM,WAAY,QAAO;AACxD,UAAM,SAAS,OAAO,MAAM,UAAU,aAAa,MAAM,MAAM,IAAI;AACnE,QAAI,OAAO,OAAO,IAAI,MAAM,WAAY,QAAO;AAC/C,YAAO,wBAAO,IAAI,EAAE,GAAG,IAAI,GAAE,QAAtB,4CAAiC;AAAA,EAC1C;AAEA,QAAM,gBAAgB,MAAM;AAC1B,aAAS,QAAQ,CAAC,MAAM,EAAE,UAAU,IAAI,eAAe,CAAC;AAAA,EAC1D;AAEA,QAAM,eAAe,CAAC,OAAuB;AAC3C,QAAI,CAAC,GAAI,QAAO;AAChB,WAAO,CAAC,CAAC,GAAG;AAAA,EACd;AAEA,QAAM,YAAY,CAChB,MACA,OACA,SACAC,QAA+C,CAAC,MAC7C;AACH,UAAM,MAAM,SAAS,cAAc,QAAQ;AAC3C,QAAI,OAAO;AACX,QAAI,YAAY,yBAAyB,IAAI;AAC7C,QAAI,cAAc;AAClB,QAAIA,MAAK,OAAO;AACd,UAAI,QAAQA,MAAK;AACjB,UAAI,aAAa,cAAcA,MAAK,KAAK;AAAA,IAC3C;AACA,QAAIA,MAAK,UAAU;AACjB,UAAI,WAAW;AACf,UAAI,UAAU,IAAI,iBAAiB;AAAA,IACrC,OAAO;AACL,UAAI,iBAAiB,SAAS,MAAM;AAClC,sBAAc;AACd,gBAAQ;AAAA,MACV,CAAC;AAAA,IACH;AACA,YAAQ,YAAY,GAAG;AAAA,EACzB;AAEA,QAAM,cAAc,MAAM;AACxB,UAAM,UAAU,OAAO,WAAW,MAAM,MAAM;AAC9C,UAAM,UAAU,OAAO,WAAW,MAAM,MAAM;AAC9C,cAAU,QAAQ,eAAe,MAAM,MAAM,aAAa,MAAM,gBAAgB,MAAM,CAAC,GAAG;AAAA,MACxF,UAAU,CAAC;AAAA,MACX,OAAO;AAAA,IACT,CAAC;AACD,cAAU,QAAQ,eAAe,MAAM,MAAM,aAAa,MAAM,gBAAgB,MAAM,CAAC,GAAG;AAAA,MACxF,UAAU,CAAC;AAAA,MACX,OAAO;AAAA,IACT,CAAC;AAAA,EACH;AAEA,QAAM,gBAAgB,MAAM;AAC1B,UAAM,aAAa,OAAO,WAAW,eAAe,MAAM;AAC1D,UAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,YAAQ,YAAY;AACpB,UAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,WAAO,YAAY;AACnB,WAAO,QAAQ;AACf,SAAK,MAAM,QAAQ,CAAC,SAAS;AAC3B,YAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,aAAO,QAAQ;AACf,aAAO,cAAc;AACrB,aAAO,MAAM,aAAa;AAC1B,aAAO,YAAY,MAAM;AAAA,IAC3B,CAAC;AACD,WAAO,WAAW,CAAC;AACnB,WAAO,iBAAiB,UAAU,MAAM;AAxa5C,UAAAD,KAAA;AAyaM,mBAAa,MAAM,gBAAgB,iBAAiB,OAAO,KAAK,CAAC;AACjE,aAAAA,MAAA,OAAO,UAAS,YAAhB,wBAAAA,KAA0B,cAAc;AAAA,IAC1C,CAAC;AACD,uBAAmB;AACnB,YAAQ,YAAY,MAAM;AAC1B,YAAQ,YAAY,OAAO;AAAA,EAC7B;AAEA,QAAM,cAAc,MAAM;AACxB,UAAM,aAAa,OAAO,WAAW,aAAa,MAAM;AACxD,UAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,YAAQ,YAAY;AACpB,UAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,WAAO,YAAY;AACnB,WAAO,QAAQ;AACf,SAAK,UAAU,QAAQ,CAAC,SAAS;AAC/B,YAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,aAAO,QAAQ;AACf,aAAO,cAAc;AACrB,aAAO,YAAY,MAAM;AAAA,IAC3B,CAAC;AACD,WAAO,WAAW,CAAC;AACnB,WAAO,iBAAiB,UAAU,MAAM;AA/b5C,UAAAA,KAAA;AAgcM,mBAAa,MAAM,gBAAgB,eAAe,OAAO,KAAK,CAAC;AAC/D,aAAAA,MAAA,OAAO,UAAS,YAAhB,wBAAAA,KAA0B,cAAc;AAAA,IAC1C,CAAC;AACD,qBAAiB;AACjB,YAAQ,YAAY,MAAM;AAC1B,YAAQ,YAAY,OAAO;AAAA,EAC7B;AAEA,QAAM,gBAAgB,CAAC,MAA4B,aAAqB,UAAkB;AACxF,UAAM,KAAK,WAAW,WAAW;AACjC,UAAM,WAAW,OAAO,OAAO;AAC/B;AAAA,MACE;AAAA,MACA,eAAe,IAAI;AAAA,MACnB,MAAM;AACJ,qBAAa,MAAM,gBAAgB,WAAW,CAAC;AAAA,MACjD;AAAA,MACA,EAAE,UAAU,MAAM;AAAA,IACpB;AAAA,EACF;AAEA,QAAM,eAAe,MAAM;AACzB,UAAM,SAAS,OAAO,WAAW,UAAU,MAAM;AACjD,UAAM,WAAW,OAAO,WAAW,YAAY,MAAM;AACrD,UAAM,EAAE,SAAS,QAAQ,QAAQ,IAAI,mBAAmB;AAAA,MACtD,OAAO,eAAe;AAAA,MACtB,OAAO;AAAA,MACP,QAAQ,KAAK;AAAA,MACb,UAAU,CAAC,UAAU;AA5d3B,YAAAA,KAAA;AA6dQ,YAAI,CAAC,OAAQ;AACb,qBAAa,MAAM,gBAAgB,YAAY,KAAK,CAAC;AACrD,eAAAA,MAAA,OAAO,UAAS,YAAhB,wBAAAA,KAA0B,cAAc;AAAA,MAC1C;AAAA,MACA,SAAS,MAAM;AAjerB,YAAAA,KAAA;AAkeQ,YAAI,CAAC,SAAU;AACf,qBAAa,MAAM,gBAAgB,YAAY,CAAC;AAChD,eAAAA,MAAA,OAAO,UAAS,YAAhB,wBAAAA,KAA0B,cAAc;AAAA,MAC1C;AAAA,MACA,UAAU,MAAG;AAtenB,YAAAA,KAAA;AAsesB,sBAAAA,MAAA,OAAO,UAAS,YAAhB,wBAAAA,KAA0B,cAAc;AAAA;AAAA,IAC1D,CAAC;AACD,aAAS,KAAK,OAAO;AACrB,eAAW,KAAK,OAAO;AACvB,YAAQ,YAAY,MAAM;AAC1B,YAAQ,YAAY,OAAO;AAAA,EAC7B;AAEA,QAAM,oBAAoB,MAAM;AAC9B,UAAM,YAAY,OAAO,WAAW,iBAAiB,MAAM;AAC3D,UAAM,WAAW,OAAO,WAAW,gBAAgB,MAAM;AACzD,UAAM,EAAE,SAAS,QAAQ,QAAQ,IAAI,mBAAmB;AAAA,MACtD,OAAO,eAAe;AAAA,MACtB,OAAO;AAAA,MACP,QAAQ,KAAK;AAAA,MACb,UAAU,CAAC,UAAU;AArf3B,YAAAA,KAAA;AAsfQ,YAAI,CAAC,UAAW;AAChB,qBAAa,MAAM,gBAAgB,mBAAmB,EAAE,MAAM,CAAC,CAAC;AAChE,eAAAA,MAAA,OAAO,UAAS,YAAhB,wBAAAA,KAA0B,cAAc;AAAA,MAC1C;AAAA,MACA,SAAS,MAAM;AA1frB,YAAAA,KAAA;AA2fQ,YAAI,CAAC,SAAU;AACf,qBAAa,MAAM,gBAAgB,gBAAgB,CAAC;AACpD,eAAAA,MAAA,OAAO,UAAS,YAAhB,wBAAAA,KAA0B,cAAc;AAAA,MAC1C;AAAA,MACA,UAAU,MAAG;AA/fnB,YAAAA,KAAA;AA+fsB,sBAAAA,MAAA,OAAO,UAAS,YAAhB,wBAAAA,KAA0B,cAAc;AAAA;AAAA,IAC1D,CAAC;AACD,aAAS,KAAK,OAAO;AACrB,eAAW,KAAK,OAAO;AACvB,YAAQ,YAAY,MAAM;AAC1B,YAAQ,YAAY,OAAO;AAAA,EAC7B;AAEA,QAAM,UAAU,MAAM;AACpB,UAAM,YAAY,OAAO,WAAW,YAAY,MAAM;AACtD,UAAM,WAAW,OAAO,WAAW,WAAW,MAAM;AACpD;AAAA,MACE;AAAA,MACA,eAAe;AAAA,MACf,MAAM;AA7gBZ,YAAAA,KAAA;AA8gBQ,cAAM,eAAc,MAAAA,MAAA,OAAO,cAAc,MAAM,MAA3B,gBAAAA,IAA8B,SAA9B,YAAsC;AAC1D,cAAM,OAAO,OAAO,OAAO,YAAY,WAAW;AAClD,YAAI,SAAS,KAAM;AACnB,YAAI,CAAC,MAAM;AACT,cAAI,UAAU;AACZ,yBAAa,MAAM,gBAAgB,WAAW,CAAC;AAAA,UACjD;AACA;AAAA,QACF;AACA,YAAI,WAAW;AACb,uBAAa,MAAM,gBAAgB,cAAc,EAAE,KAAK,CAAC,CAAC;AAAA,QAC5D;AAAA,MACF;AAAA,MACA,EAAE,UAAU,CAAC,aAAa,CAAC,UAAU,OAAO,cAAc;AAAA,IAC5D;AAAA,EACF;AAEA,QAAM,WAAW,MAAM;AACrB,UAAM,SAAS,OAAO,WAAW,cAAc,MAAM;AACrD,UAAM,YAAY,OAAO,WAAW,iBAAiB,MAAM;AAC3D,UAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,YAAQ,YAAY;AACpB,UAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,WAAO,YAAY;AACnB,WAAO,QAAQ;AACf,SAAK,WAAW,QAAQ,CAAC,cAAc;AACrC,YAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,aAAO,QAAQ;AACf,aAAO,cAAc,UAAU,OAAO,CAAC,EAAE,YAAY,IAAI,UAAU,MAAM,CAAC;AAC1E,aAAO,YAAY,MAAM;AAAA,IAC3B,CAAC;AACD,WAAO,WAAW,CAAC,UAAU,CAAC;AAC9B,WAAO,iBAAiB,UAAU,MAAM;AA9iB5C,UAAAA,KAAA;AA+iBM,UAAI,WAAW;AACb,qBAAa,MAAM,gBAAgB,mBAAmB,OAAO,KAAK,CAAC;AAAA,MACrE,WAAW,QAAQ;AACjB,qBAAa,MAAM,gBAAgB,gBAAgB,OAAO,KAAK,CAAC;AAAA,MAClE;AACA,aAAAA,MAAA,OAAO,UAAS,YAAhB,wBAAAA,KAA0B,cAAc;AAAA,IAC1C,CAAC;AACD,YAAQ,YAAY,MAAM;AAC1B,YAAQ,YAAY,OAAO;AAAA,EAC7B;AAGA,OAAK,MAAM,QAAQ,CAAC,SAAS;AAC3B,YAAQ,MAAM;AAAA,MACZ,KAAK;AACH,oBAAY;AACZ;AAAA,MACF,KAAK;AAEH;AAAA,MACF,KAAK;AACH,sBAAc;AACd;AAAA,MACF,KAAK;AACH,oBAAY;AACZ;AAAA,MACF,KAAK;AACH,sBAAc,QAAQ,cAAc,MAAM;AAC1C;AAAA,MACF,KAAK;AACH,sBAAc,UAAU,gBAAgB,QAAQ;AAChD;AAAA,MACF,KAAK;AACH,sBAAc,aAAa,mBAAmB,WAAW;AACzD;AAAA,MACF,KAAK;AACH,qBAAa;AACb;AAAA,MACF,KAAK;AACH,0BAAkB;AAClB;AAAA,MACF,KAAK;AACH,gBAAQ;AACR;AAAA,MACF,KAAK;AACH,iBAAS;AACT;AAAA,MACF;AACE;AAAA,IACJ;AAAA,EACF,CAAC;AAED,UAAQ,YAAY,OAAO;AAE3B,QAAM,qBAAqB,MAAM;AAC/B,UAAM,QAAQ,OAAO,cAAc,WAAW,KAAK,CAAC;AACpD,QAAI,SAAS,MAAM;AACnB,QAAI,OAAO,MAAM;AAGjB,QAAI,CAAC,UAAU,CAAC,MAAM;AACpB,YAAM,WAAW,sBAAsB;AACvC,eAAS,UAAU,SAAS;AAC5B,aAAO,QAAQ,SAAS;AAAA,IAC1B;AAEA,mBAAe,kBAAkB,oBAAoB,MAAM,CAAC;AAC5D,mBAAe,gBAAgB,IAAI;AAAA,EACrC;AAEA,QAAM,wBAAwB,MAAM,mBAAmB;AACvD,QAAM,oBAAoB,CAAC,EAAE,YAAY,MAA6B;AACpE,QAAI,CAAC,eAAe,YAAY,cAAc,YAAY,cAAc;AACtE,yBAAmB;AAAA,IACrB;AAAA,EACF;AAEA,SAAO,GAAG,mBAAmB,qBAAqB;AAClD,SAAO,GAAG,eAAe,iBAAiB;AAC1C,aAAW,KAAK,MAAM;AACpB,WAAO,IAAI,mBAAmB,qBAAqB;AACnD,WAAO,IAAI,eAAe,iBAAiB;AAAA,EAC7C,CAAC;AAED,qBAAmB;AAEnB,SAAO;AAAA,IACL;AAAA,IACA,SAAS,MAAM;AACb,eAAS,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;AAClC,iBAAW,QAAQ,CAAC,OAAO,GAAG,CAAC;AAC/B,cAAQ,gBAAgB;AAAA,IAC1B;AAAA,EACF;AACF;AAEA,SAAS,mBAAmB,MAO2C;AACrE,QAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,SAAO,OAAO;AACd,SAAO,YAAY;AACnB,SAAO,QAAQ,KAAK;AACpB,SAAO,aAAa,iBAAiB,OAAO;AAC5C,SAAO,aAAa,cAAc,KAAK,KAAK;AAC5C,MAAI,KAAK,UAAU,eAAe,aAAa,MAAM,WAAW;AAC9D,WAAO,YAAY,MAAM;AAAA,EAC3B,WAAW,KAAK,UAAU,eAAe,kBAAkB,MAAM,gBAAgB;AAC/E,WAAO,YAAY,MAAM;AAAA,EAC3B,OAAO;AACL,WAAO,cAAc,KAAK;AAAA,EAC5B;AAEA,QAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,UAAQ,YAAY;AACpB,UAAQ,aAAa,QAAQ,MAAM;AAEnC,QAAM,SAAS,SAAS,cAAc,KAAK;AAC3C,SAAO,YAAY;AACnB,QAAM,UAAU,SAAS,cAAc,QAAQ;AAC/C,UAAQ,OAAO;AACf,UAAQ,YAAY;AACpB,UAAQ,cAAc;AACtB,UAAQ,iBAAiB,SAAS,MAAM;AACtC,SAAK,QAAQ;AACb,SAAK;AAAA,EACP,CAAC;AACD,SAAO,YAAY,OAAO;AAC1B,UAAQ,YAAY,MAAM;AAE1B,QAAM,OAAO,SAAS,cAAc,KAAK;AACzC,OAAK,YAAY;AACjB,QAAM,UAAU;AAChB,OAAK,MAAM,YAAY,yBAAyB,OAAO,OAAO,CAAC;AAE/D,OAAK,OAAO,QAAQ,CAAC,UAAU;AAC7B,UAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,WAAO,OAAO;AACd,WAAO,YAAY;AACnB,WAAO,MAAM,kBAAkB;AAC/B,WAAO,aAAa,cAAc,KAAK;AACvC,WAAO,iBAAiB,SAAS,CAAC,UAAU;AAC1C,YAAM,gBAAgB;AACtB,WAAK,SAAS,KAAK;AACnB,WAAK;AAAA,IACP,CAAC;AACD,SAAK,YAAY,MAAM;AAAA,EACzB,CAAC;AAED,UAAQ,YAAY,IAAI;AAExB,QAAM,YAAY,SAAS,cAAc,KAAK;AAC9C,YAAU,YAAY;AACtB,QAAM,cAAc,SAAS,cAAc,MAAM;AACjD,cAAY,cAAc;AAC1B,QAAM,cAAc,SAAS,cAAc,OAAO;AAClD,cAAY,OAAO;AACnB,cAAY,YAAY;AACxB,cAAY,iBAAiB,SAAS,MAAM;AAC1C,SAAK,SAAS,YAAY,KAAK;AAAA,EACjC,CAAC;AACD,YAAU,YAAY,WAAW;AACjC,YAAU,YAAY,WAAW;AACjC,UAAQ,YAAY,SAAS;AAE7B,QAAM,OAAO,MAAM;AAztBrB;AA0tBI,YAAQ,UAAU,IAAI,eAAe;AACrC,WAAO,aAAa,iBAAiB,OAAO;AAC5C,eAAK,aAAL;AAAA,EACF;AAEA,QAAM,OAAO,MAAM;AA/tBrB;AAguBI,YAAQ,UAAU,OAAO,eAAe;AACxC,WAAO,aAAa,iBAAiB,MAAM;AAC3C,eAAK,aAAL;AAAA,EACF;AAEA,QAAM,gBAAgB,CAAC,UAAsB;AAC3C,UAAM,gBAAgB;AACtB,QAAI,QAAQ,UAAU,SAAS,eAAe,GAAG;AAC/C,WAAK;AAAA,IACP,OAAO;AACL,WAAK;AAAA,IACP;AAAA,EACF;AACA,SAAO,iBAAiB,SAAS,aAAa;AAE9C,QAAM,iBAAiB,CAAC,UAAsB;AAC5C,QAAI,QAAQ,SAAS,MAAM,MAAc,KAAK,OAAO,SAAS,MAAM,MAAc,GAAG;AACnF;AAAA,IACF;AACA,SAAK;AAAA,EACP;AAEA,WAAS;AAAA,IACP;AAAA,IACA;AAAA,IACA,EAAE,SAAS,KAAK;AAAA,EAClB;AAEA,QAAM,UAAU,MAAM;AACpB,aAAS,oBAAoB,SAAS,gBAAgB,EAAE,SAAS,KAAK,CAAQ;AAC9E,WAAO,oBAAoB,SAAS,aAAa;AAAA,EACnD;AAEA,SAAO,EAAE,QAAQ,SAAS,QAAQ;AACpC;AAEA,SAAS,eAAe;AACtB,MAAI,OAAO,aAAa,YAAa;AACrC,MAAI,SAAS,eAAe,QAAQ,EAAG;AAEvC,QAAM,QAAQ,SAAS,cAAc,OAAO;AAC5C,QAAM,KAAK;AACX,QAAM,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiHpB,WAAS,KAAK,YAAY,KAAK;AACjC;","names":["_a","opts"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blockslides/extension-bubble-menu-preset",
|
|
3
3
|
"description": "Opinionated bubble menu preset with built-in formatting actions",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.3",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"blockslides",
|
|
7
7
|
"bubble menu",
|
|
@@ -29,23 +29,23 @@
|
|
|
29
29
|
],
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@blockslides/core": "^0.1.0",
|
|
32
|
-
"@blockslides/extension-highlight": "^0.1.0",
|
|
33
32
|
"@blockslides/extension-bubble-menu": "^0.1.0",
|
|
34
33
|
"@blockslides/extension-text-align": "^0.1.0",
|
|
34
|
+
"@blockslides/extension-highlight": "^0.1.0",
|
|
35
35
|
"@blockslides/extension-link": "^0.1.0",
|
|
36
|
-
"@blockslides/pm": "^0.1.0",
|
|
37
36
|
"@blockslides/extension-text-style": "^0.1.0",
|
|
38
|
-
"@blockslides/extensions": "^0.1.0"
|
|
37
|
+
"@blockslides/extensions": "^0.1.0",
|
|
38
|
+
"@blockslides/pm": "^0.1.0"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
|
-
"@blockslides/extension-bubble-menu": "^0.1.0",
|
|
42
|
-
"@blockslides/extension-highlight": "^0.1.0",
|
|
43
41
|
"@blockslides/core": "^0.1.0",
|
|
42
|
+
"@blockslides/extension-bubble-menu": "^0.1.0",
|
|
43
|
+
"@blockslides/extension-link": "^0.1.0",
|
|
44
44
|
"@blockslides/extension-text-align": "^0.1.0",
|
|
45
|
-
"@blockslides/extension-
|
|
45
|
+
"@blockslides/extension-highlight": "^0.1.0",
|
|
46
46
|
"@blockslides/extensions": "^0.1.0",
|
|
47
|
-
"@blockslides/
|
|
48
|
-
"@blockslides/
|
|
47
|
+
"@blockslides/extension-text-style": "^0.1.0",
|
|
48
|
+
"@blockslides/pm": "^0.1.0"
|
|
49
49
|
},
|
|
50
50
|
"repository": {
|
|
51
51
|
"type": "git",
|
|
@@ -305,6 +305,48 @@ function buildMenuElement(
|
|
|
305
305
|
const toolbar = document.createElement('div')
|
|
306
306
|
toolbar.className = 'bs-bmp-toolbar'
|
|
307
307
|
|
|
308
|
+
let fontFamilySelect: HTMLSelectElement | null = null
|
|
309
|
+
let fontSizeSelect: HTMLSelectElement | null = null
|
|
310
|
+
|
|
311
|
+
const normalizeFontFamily = (value?: string | null) => {
|
|
312
|
+
if (!value) return value
|
|
313
|
+
// take the first family name and strip quotes
|
|
314
|
+
return value.split(',')[0]?.trim().replace(/^['"]|['"]$/g, '') || value
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const getComputedFontStyles = () => {
|
|
318
|
+
if (typeof window === 'undefined') return { fontFamily: undefined, fontSize: undefined }
|
|
319
|
+
const sel = window.getSelection()
|
|
320
|
+
const node = sel?.anchorNode
|
|
321
|
+
const el = (node instanceof HTMLElement ? node : node?.parentElement) ?? undefined
|
|
322
|
+
if (!el) return { fontFamily: undefined, fontSize: undefined }
|
|
323
|
+
const styles = window.getComputedStyle(el)
|
|
324
|
+
return {
|
|
325
|
+
fontFamily: normalizeFontFamily(styles.fontFamily || undefined),
|
|
326
|
+
fontSize: styles.fontSize || undefined,
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const ensureOptionExists = (select: HTMLSelectElement, value: string) => {
|
|
331
|
+
const exists = Array.from(select.options).some(opt => opt.value === value)
|
|
332
|
+
if (!exists) {
|
|
333
|
+
const option = document.createElement('option')
|
|
334
|
+
option.value = value
|
|
335
|
+
option.textContent = value
|
|
336
|
+
select.appendChild(option)
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const setSelectValue = (select: HTMLSelectElement | null, value?: string | null) => {
|
|
341
|
+
if (!select) return
|
|
342
|
+
if (!value) {
|
|
343
|
+
select.selectedIndex = -1
|
|
344
|
+
return
|
|
345
|
+
}
|
|
346
|
+
ensureOptionExists(select, value)
|
|
347
|
+
select.value = value
|
|
348
|
+
}
|
|
349
|
+
|
|
308
350
|
const popovers: HTMLElement[] = []
|
|
309
351
|
const cleanupFns: Cleanup[] = []
|
|
310
352
|
|
|
@@ -384,6 +426,7 @@ function buildMenuElement(
|
|
|
384
426
|
runWithFocus(() => runChainCommand('setFontFamily', select.value))
|
|
385
427
|
editor.commands.setMeta?.('bubbleMenu', 'updatePosition')
|
|
386
428
|
})
|
|
429
|
+
fontFamilySelect = select
|
|
387
430
|
wrapper.appendChild(select)
|
|
388
431
|
toolbar.appendChild(wrapper)
|
|
389
432
|
}
|
|
@@ -406,6 +449,7 @@ function buildMenuElement(
|
|
|
406
449
|
runWithFocus(() => runChainCommand('setFontSize', select.value))
|
|
407
450
|
editor.commands.setMeta?.('bubbleMenu', 'updatePosition')
|
|
408
451
|
})
|
|
452
|
+
fontSizeSelect = select
|
|
409
453
|
wrapper.appendChild(select)
|
|
410
454
|
toolbar.appendChild(wrapper)
|
|
411
455
|
}
|
|
@@ -567,6 +611,38 @@ function buildMenuElement(
|
|
|
567
611
|
|
|
568
612
|
element.appendChild(toolbar)
|
|
569
613
|
|
|
614
|
+
const syncSelectionState = () => {
|
|
615
|
+
const attrs = editor.getAttributes('textStyle') || {}
|
|
616
|
+
let family = attrs.fontFamily
|
|
617
|
+
let size = attrs.fontSize
|
|
618
|
+
|
|
619
|
+
// If not explicitly set via marks, fall back to computed DOM styles
|
|
620
|
+
if (!family || !size) {
|
|
621
|
+
const computed = getComputedFontStyles()
|
|
622
|
+
family = family || computed.fontFamily
|
|
623
|
+
size = size || computed.fontSize
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
setSelectValue(fontFamilySelect, normalizeFontFamily(family))
|
|
627
|
+
setSelectValue(fontSizeSelect, size)
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
const handleSelectionUpdate = () => syncSelectionState()
|
|
631
|
+
const handleTransaction = ({ transaction }: { transaction?: any }) => {
|
|
632
|
+
if (!transaction || transaction.docChanged || transaction.selectionSet) {
|
|
633
|
+
syncSelectionState()
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
editor.on('selectionUpdate', handleSelectionUpdate)
|
|
638
|
+
editor.on('transaction', handleTransaction)
|
|
639
|
+
cleanupFns.push(() => {
|
|
640
|
+
editor.off('selectionUpdate', handleSelectionUpdate)
|
|
641
|
+
editor.off('transaction', handleTransaction)
|
|
642
|
+
})
|
|
643
|
+
|
|
644
|
+
syncSelectionState()
|
|
645
|
+
|
|
570
646
|
return {
|
|
571
647
|
element,
|
|
572
648
|
cleanup: () => {
|
package/src/index.ts
CHANGED