smartrte-react 1.0.0-beta.13 → 1.0.0-beta.15

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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.0-beta.15
4
+
5
+ - The Blockquote toolbar button now shows as active (pressed) when the caret is inside a blockquote, matching every other toggle-style tool (Bold, Bulleted list, etc.) — previously it never reflected caret/selection state at all.
6
+
7
+ ## 1.0.0-beta.14
8
+
9
+ - Fix toolbar dropdowns still landing far from their trigger inside a dialog centered via the standalone CSS `translate` property (e.g. Tailwind's `translate-x-[-50%] translate-y-[-50%]` compiled to `translate` rather than the legacy `transform` property, depending on Tailwind/browser version) — `findFixedPositioningContainer` only checked `transform`, missing that `translate`/`rotate`/`scale` (CSS Transforms Level 2's independent transform properties) each independently establish a `position: fixed` containing block too. Root-caused via a live console diagnostic against the real page rather than guessing. See `docs/bugs/toolbar-overlay-misplaced-standalone-translate-property.md`.
10
+
3
11
  ## 1.0.0-beta.13
4
12
 
5
13
  - Fix toolbar dropdowns still landing far from their trigger inside a dialog whose ancestor uses `contain: layout` without `paint` — `1.0.0-beta.11`'s `contain` fix only checked for `paint`/`strict`/`content`, missing that bare `layout` alone also establishes a `position: fixed` containing block per spec (confirmed directly in real Chromium). Menus rendered hundreds of pixels from their trigger, overlapping unrelated page content, with no error. See `docs/bugs/toolbar-overlay-misplaced-inside-contain-layout-ancestor.md`.
@@ -501,6 +501,10 @@ export const CanonicalAuthorityEditor = forwardRef(function CanonicalAuthorityEd
501
501
  return "paragraph";
502
502
  };
503
503
  const currentBlockType = blockTypeAt(runtime.editor.selection.head);
504
+ // Mirrors toggleBlockquote's own ancestor lookup exactly, so "the button
505
+ // looks active" and "clicking it again unwraps" can never drift apart -
506
+ // same discipline as listStyleActive for the list toggle buttons.
507
+ const currentBlockquoteActive = Boolean([...runtime.editor.resolve({ pos: runtime.editor.selection.head }).ancestors].reverse().find((node) => node.type === "blockquote"));
504
508
  const currentListPreset = currentListParts.length === 1 && typeof rootList?.attrs?.preset === "string"
505
509
  ? rootList.attrs.preset
506
510
  : "";
@@ -1633,7 +1637,7 @@ export const CanonicalAuthorityEditor = forwardRef(function CanonicalAuthorityEd
1633
1637
  } }), t.fontSize && _jsx(ToolbarButton, { icon: "fontSize", label: "Font size", disabled: readOnly, onClick: () => applyAttributedMark("fontSize"), widePromote: true }), t.fontFamily && _jsx(ToolbarButton, { icon: "fontFamily", label: "Font family", disabled: readOnly, onClick: () => applyAttributedMark("fontFamily"), widePromote: true }), showMoreTextStyles && _jsx(ToolbarDropdown, { icon: "textColor", label: "More text styles", priority: 2, children: textStylesMenuItems })] }), _jsxs(ToolbarGroup, { children: [t.blockType && _jsxs("select", { className: "srte-block-type-select", "aria-label": "Block type", title: "Block type", value: currentBlockType, disabled: readOnly, onChange: (event) => transactBlock(setBlockTypeCommand(runtime.editor.document, blockScope(), {
1634
1638
  type: event.target.value === "paragraph" ? "paragraph" : event.target.value === "code_block" ? "code_block" : "heading",
1635
1639
  attrs: event.target.value.startsWith("heading-") ? { level: Number(event.target.value.slice(8)) } : {},
1636
- }, blockContext())), children: [_jsx("option", { value: "paragraph", children: "Paragraph" }), Array.from({ length: 6 }, (_, index) => _jsxs("option", { value: `heading-${index + 1}`, children: ["Heading ", index + 1] }, index + 1)), _jsx("option", { value: "code_block", children: "Code block" })] }), t.alignLeft && _jsx(ToolbarButton, { icon: "alignLeft", label: "Align left", ariaLabel: "Align left", iconOnly: true, disabled: readOnly, onClick: () => transactBlock(setBlockAttributes(runtime.editor.document, blockScope(), { attrs: { align: "left" } }, blockContext())) }), t.alignCenter && _jsx(ToolbarButton, { icon: "alignCenter", label: "Align center", ariaLabel: "Align center", iconOnly: true, disabled: readOnly, onClick: () => transactBlock(setBlockAttributes(runtime.editor.document, blockScope(), { attrs: { align: "center" } }, blockContext())) }), t.alignRight && _jsx(ToolbarButton, { icon: "alignRight", label: "Align right", ariaLabel: "Align right", iconOnly: true, disabled: readOnly, onClick: () => transactBlock(setBlockAttributes(runtime.editor.document, blockScope(), { attrs: { align: "right" } }, blockContext())) }), t.alignJustify && _jsx(ToolbarButton, { icon: "alignJustify", label: "Justify", ariaLabel: "Align justify", iconOnly: true, disabled: readOnly, onClick: () => transactBlock(setBlockAttributes(runtime.editor.document, blockScope(), { attrs: { align: "justify" } }, blockContext())) }), t.lineHeight && _jsx(ToolbarDropdown, { icon: "lineHeight", label: "Line spacing", priority: 2, children: lineHeightMenuItems }), t.quote && _jsx(ToolbarButton, { icon: "quote", label: "Quote", ariaLabel: "Blockquote", disabled: readOnly, onClick: toggleBlockquote }), _jsx(ToolbarDropdown, { icon: "moveUp", label: "More paragraph tools", priority: 2, children: paragraphToolsMenuItems })] }), _jsxs(ToolbarGroup, { children: [t.bulletedList && (t.listPreset ? _jsxs("span", { className: "srte-split-control", children: [_jsx(ToolbarButton, { icon: "bulletedList", label: "Bulleted list", ariaLabel: "Bulleted list", pressed: listStyleActive("disc"), disabled: readOnly, narrowIconOnly: true, onClick: () => toggleList("disc") }), _jsxs("select", { "aria-label": "Bulleted list style", title: "Bulleted list style", disabled: readOnly || (currentListScope.kind === "list-selection" && currentListParts.length !== 1), value: currentBulletPreset, onChange: (event) => { const preset = event.target.value; if (preset)
1640
+ }, blockContext())), children: [_jsx("option", { value: "paragraph", children: "Paragraph" }), Array.from({ length: 6 }, (_, index) => _jsxs("option", { value: `heading-${index + 1}`, children: ["Heading ", index + 1] }, index + 1)), _jsx("option", { value: "code_block", children: "Code block" })] }), t.alignLeft && _jsx(ToolbarButton, { icon: "alignLeft", label: "Align left", ariaLabel: "Align left", iconOnly: true, disabled: readOnly, onClick: () => transactBlock(setBlockAttributes(runtime.editor.document, blockScope(), { attrs: { align: "left" } }, blockContext())) }), t.alignCenter && _jsx(ToolbarButton, { icon: "alignCenter", label: "Align center", ariaLabel: "Align center", iconOnly: true, disabled: readOnly, onClick: () => transactBlock(setBlockAttributes(runtime.editor.document, blockScope(), { attrs: { align: "center" } }, blockContext())) }), t.alignRight && _jsx(ToolbarButton, { icon: "alignRight", label: "Align right", ariaLabel: "Align right", iconOnly: true, disabled: readOnly, onClick: () => transactBlock(setBlockAttributes(runtime.editor.document, blockScope(), { attrs: { align: "right" } }, blockContext())) }), t.alignJustify && _jsx(ToolbarButton, { icon: "alignJustify", label: "Justify", ariaLabel: "Align justify", iconOnly: true, disabled: readOnly, onClick: () => transactBlock(setBlockAttributes(runtime.editor.document, blockScope(), { attrs: { align: "justify" } }, blockContext())) }), t.lineHeight && _jsx(ToolbarDropdown, { icon: "lineHeight", label: "Line spacing", priority: 2, children: lineHeightMenuItems }), t.quote && _jsx(ToolbarButton, { icon: "quote", label: "Quote", ariaLabel: "Blockquote", pressed: currentBlockquoteActive, disabled: readOnly, onClick: toggleBlockquote }), _jsx(ToolbarDropdown, { icon: "moveUp", label: "More paragraph tools", priority: 2, children: paragraphToolsMenuItems })] }), _jsxs(ToolbarGroup, { children: [t.bulletedList && (t.listPreset ? _jsxs("span", { className: "srte-split-control", children: [_jsx(ToolbarButton, { icon: "bulletedList", label: "Bulleted list", ariaLabel: "Bulleted list", pressed: listStyleActive("disc"), disabled: readOnly, narrowIconOnly: true, onClick: () => toggleList("disc") }), _jsxs("select", { "aria-label": "Bulleted list style", title: "Bulleted list style", disabled: readOnly || (currentListScope.kind === "list-selection" && currentListParts.length !== 1), value: currentBulletPreset, onChange: (event) => { const preset = event.target.value; if (preset)
1637
1641
  applyListPreset(preset); }, children: [_jsx("option", { value: "", children: "Bulleted list style" }), SMART_LIST_PRESETS.filter((preset) => preset.kind === "bullet").map((preset) => _jsx("option", { value: preset.id, children: preset.label }, preset.id))] })] }) : _jsx(ToolbarButton, { icon: "bulletedList", label: "Bulleted list", ariaLabel: "Bulleted list", pressed: listStyleActive("disc"), disabled: readOnly, narrowIconOnly: true, onClick: () => toggleList("disc") })), t.numberedList && (t.listPreset ? _jsxs("span", { className: "srte-split-control", children: [_jsx(ToolbarButton, { icon: "numberedList", label: "Numbered list", ariaLabel: "Numbered list", pressed: listStyleActive("decimal"), disabled: readOnly, narrowIconOnly: true, onClick: () => toggleList("decimal") }), _jsxs("select", { "aria-label": "Numbered list style", title: "Numbered list style", disabled: readOnly || (currentListScope.kind === "list-selection" && currentListParts.length !== 1), value: currentOrderedPreset, onChange: (event) => { const preset = event.target.value; if (preset)
1638
1642
  applyListPreset(preset); }, children: [_jsx("option", { value: "", children: "Numbered list style" }), SMART_LIST_PRESETS.filter((preset) => preset.kind === "ordered").map((preset) => _jsx("option", { value: preset.id, children: preset.label }, preset.id))] })] }) : _jsx(ToolbarButton, { icon: "numberedList", label: "Numbered list", ariaLabel: "Numbered list", pressed: listStyleActive("decimal"), disabled: readOnly, narrowIconOnly: true, onClick: () => toggleList("decimal") })), t.checklist && _jsx(ToolbarButton, { icon: "checklist", label: "Checklist", ariaLabel: "Checklist", pressed: listStyleActive("disc", true), disabled: readOnly, narrowIconOnly: true, onClick: () => toggleList("disc", true) }), _jsxs(ToolbarDropdown, { icon: "restart", label: "More list tools", priority: 2, children: [listPresetSelect, t.listPreset && _jsx("div", { className: "srte-menu-separator" }), listToolsMenuItems] })] }), _jsxs(ToolbarGroup, { children: [t.link && _jsx(ToolbarButton, { icon: "link", label: "Link", ariaLabel: "Insert or edit link", disabled: readOnly, onClick: openLinkPopover }), showImageTool && _jsx(ToolbarButton, { icon: "image", label: "Image", ariaLabel: "Insert image", disabled: readOnly, onClick: () => setMediaKind("image") }), t.removeLink && _jsx(ToolbarButton, { icon: "unlink", label: "Remove link", disabled: readOnly, onClick: removeLink, widePromote: true }), t.insertFormula && _jsx(ToolbarButton, { icon: "formula", label: "Insert formula", disabled: readOnly, widePromote: true, onClick: (event) => {
1639
1643
  const rect = event.currentTarget.getBoundingClientRect();
@@ -9,6 +9,23 @@
9
9
  * (`translate-x-[-50%] translate-y-[-50%]`), confirmed live via a Sootr
10
10
  * host rendering the editor inside such a dialog
11
11
  * (docs/bugs/toolbar-menu-misplaced-inside-transformed-ancestor.md).
12
+ * - `translate`, `rotate`, `scale` (CSS Transforms Level 2's *independent*
13
+ * transform properties, distinct from the legacy composite `transform`
14
+ * property) - a FOURTH real Sootr dialog kept misplacing every dropdown
15
+ * after all three fixes above shipped, still using the exact same
16
+ * `translate-x-[-50%] translate-y-[-50%]` Tailwind centering classes the
17
+ * very first fix (above) was written for - but a current Tailwind/browser
18
+ * combination compiles those classes to the standalone `translate: -50%
19
+ * -50%` CSS property, not `transform: translate(-50%, -50%)`. Confirmed
20
+ * directly in real Chromium: an ancestor with only `translate` set (its
21
+ * own computed `transform` reads `none`) still fully establishes a
22
+ * `position: fixed` containing block, per the CSS Transforms Level 2
23
+ * spec - the original fix's own doc comment described the right *symptom*
24
+ * (a Radix/shadcn Dialog's centering translate) but checked the wrong
25
+ * *property* for it, because at the time it was written the transform
26
+ * ended up composited into `transform` rather than emitted as `translate`
27
+ * directly. Both are checked now, since either can appear depending on
28
+ * the host's exact Tailwind/browser version.
12
29
  * - `contain: layout | paint | strict | content` (CSS Containment) - a
13
30
  * common perf/isolation optimization on large scrollable containers, e.g.
14
31
  * a second, different Sootr dialog reported still misplacing every
@@ -69,8 +86,9 @@ export declare function getFixedPositioningOrigin(el: HTMLElement): {
69
86
  * the actual, much narrower, clipping ancestor.
70
87
  *
71
88
  * Deliberately NOT applied for a containing block established only by
72
- * `transform`/`perspective`/`filter`/`backdrop-filter`/`will-change`, or by
73
- * `contain: layout` with no `paint`/`strict`/`content` alongside it - none
89
+ * `transform`/`translate`/`rotate`/`scale`/`perspective`/`filter`/
90
+ * `backdrop-filter`/`will-change`, or by `contain: layout` with no
91
+ * `paint`/`strict`/`content` alongside it - none
74
92
  * of those clip overflow (`layout` containment only isolates layout
75
93
  * calculations, it does not imply paint containment - see the spec note
76
94
  * above `findFixedPositioningContainer`), so a menu is free to visually
@@ -9,6 +9,23 @@
9
9
  * (`translate-x-[-50%] translate-y-[-50%]`), confirmed live via a Sootr
10
10
  * host rendering the editor inside such a dialog
11
11
  * (docs/bugs/toolbar-menu-misplaced-inside-transformed-ancestor.md).
12
+ * - `translate`, `rotate`, `scale` (CSS Transforms Level 2's *independent*
13
+ * transform properties, distinct from the legacy composite `transform`
14
+ * property) - a FOURTH real Sootr dialog kept misplacing every dropdown
15
+ * after all three fixes above shipped, still using the exact same
16
+ * `translate-x-[-50%] translate-y-[-50%]` Tailwind centering classes the
17
+ * very first fix (above) was written for - but a current Tailwind/browser
18
+ * combination compiles those classes to the standalone `translate: -50%
19
+ * -50%` CSS property, not `transform: translate(-50%, -50%)`. Confirmed
20
+ * directly in real Chromium: an ancestor with only `translate` set (its
21
+ * own computed `transform` reads `none`) still fully establishes a
22
+ * `position: fixed` containing block, per the CSS Transforms Level 2
23
+ * spec - the original fix's own doc comment described the right *symptom*
24
+ * (a Radix/shadcn Dialog's centering translate) but checked the wrong
25
+ * *property* for it, because at the time it was written the transform
26
+ * ended up composited into `transform` rather than emitted as `translate`
27
+ * directly. Both are checked now, since either can appear depending on
28
+ * the host's exact Tailwind/browser version.
12
29
  * - `contain: layout | paint | strict | content` (CSS Containment) - a
13
30
  * common perf/isolation optimization on large scrollable containers, e.g.
14
31
  * a second, different Sootr dialog reported still misplacing every
@@ -52,10 +69,13 @@ export function findFixedPositioningContainer(el) {
52
69
  while (node) {
53
70
  const style = window.getComputedStyle(node);
54
71
  if ((style.transform && style.transform !== "none") ||
72
+ (style.translate && style.translate !== "none") ||
73
+ (style.rotate && style.rotate !== "none") ||
74
+ (style.scale && style.scale !== "none") ||
55
75
  (style.perspective && style.perspective !== "none") ||
56
76
  (style.filter && style.filter !== "none") ||
57
77
  (style.backdropFilter && style.backdropFilter !== "none") ||
58
- (style.willChange && /transform|perspective|filter/.test(style.willChange)) ||
78
+ (style.willChange && /transform|perspective|filter|translate|rotate|scale/.test(style.willChange)) ||
59
79
  (style.contain && /\b(layout|paint|strict|content)\b/.test(style.contain))) {
60
80
  return node;
61
81
  }
@@ -87,8 +107,9 @@ export function getFixedPositioningOrigin(el) {
87
107
  * the actual, much narrower, clipping ancestor.
88
108
  *
89
109
  * Deliberately NOT applied for a containing block established only by
90
- * `transform`/`perspective`/`filter`/`backdrop-filter`/`will-change`, or by
91
- * `contain: layout` with no `paint`/`strict`/`content` alongside it - none
110
+ * `transform`/`translate`/`rotate`/`scale`/`perspective`/`filter`/
111
+ * `backdrop-filter`/`will-change`, or by `contain: layout` with no
112
+ * `paint`/`strict`/`content` alongside it - none
92
113
  * of those clip overflow (`layout` containment only isolates layout
93
114
  * calculations, it does not imply paint containment - see the spec note
94
115
  * above `findFixedPositioningContainer`), so a menu is free to visually
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "smartrte-react",
3
- "version": "1.0.0-beta.13",
3
+ "version": "1.0.0-beta.15",
4
4
  "description": "A powerful, feature-rich Rich Text Editor for React with support for tables, mathematical formulas (LaTeX/KaTeX), and media management",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -84,7 +84,7 @@
84
84
  "lucide-react": "^1.35.0",
85
85
  "mammoth": "^1.11.0",
86
86
  "pdfjs-dist": "^5.4.530",
87
- "smartrte-core": "^1.0.0-beta.7"
87
+ "smartrte-core": "^1.0.0-beta.8"
88
88
  },
89
89
  "scripts": {
90
90
  "build": "rm -rf dist && tsc -p tsconfig.json",