autumnnote 1.1.1 → 1.2.1
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/README.md +3 -3
- package/dist/autumnnote.css +67 -3
- package/dist/autumnnote.es.js +205 -15
- package/dist/autumnnote.es.js.map +1 -1
- package/dist/autumnnote.umd.js +205 -15
- package/dist/autumnnote.umd.js.map +1 -1
- package/dist/image/banner.png +0 -0
- package/dist/image/favicon.ico +0 -0
- package/package.json +1 -1
- package/src/js/module/BubbleToolbar.js +219 -15
- package/src/js/module/Toolbar.js +22 -2
- package/src/js/settings.js +1 -1
- package/src/styles/autumnnote.scss +82 -3
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Autumn Note
|
|
2
2
|
|
|
3
|
-
<p align="center"><img src="image/banner.png" width="120" alt="AutumnNote Banner"/></p>
|
|
3
|
+
<p align="center"><img src="public/image/banner.png" width="120" alt="AutumnNote Banner"/></p>
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/autumnnote)
|
|
6
6
|
[](https://www.npmjs.com/package/autumnnote)
|
|
@@ -88,7 +88,7 @@ Right-click inside the editor opens a context menu with: **Undo**, **Redo**, **C
|
|
|
88
88
|
- **Auto-fallback** — unknown codes or missing keys fall back to English
|
|
89
89
|
|
|
90
90
|
### UI
|
|
91
|
-
- **Toolbar** — fully configurable button groups; overflow strategy: `wrap` (default) or `scroll`; FontAwesome icons with built-in SVG fallback
|
|
91
|
+
- **Toolbar** — fully configurable button groups; overflow strategy: `wrap` (default) or `scroll`; on viewports ≤ 640 px the toolbar automatically switches to a single horizontally-scrollable row regardless of the `toolbarOverflow` setting; FontAwesome icons with built-in SVG fallback
|
|
92
92
|
- **Sticky toolbar** — `stickyToolbar: true` pins the toolbar to the viewport top; configurable offset for fixed nav bars
|
|
93
93
|
- **Dark / light theme** — `theme: 'dark'` or `theme: 'light'` (default); full SCSS variable coverage
|
|
94
94
|
- **Image resizer** — drag handle on selected image to resize proportionally
|
|
@@ -100,7 +100,7 @@ Right-click inside the editor opens a context menu with: **Undo**, **Redo**, **C
|
|
|
100
100
|
- **Read-only mode** — `readOnly: true` renders a non-editable preview with toolbar hidden; toggle at runtime via `editor.setDisabled()`
|
|
101
101
|
- **Auto-save** — `autoSave: true` persists content to `localStorage` on every change; key configurable via `autoSaveKey`
|
|
102
102
|
- **Auto-save restore** — when `autoSave` and `autoSaveRestore` are both `true`, a dismissible banner prompts the user to restore or discard a previously saved draft on load; configurable age window via `autoSaveRestoreTimeout`
|
|
103
|
-
- **Bubble toolbar** — `bubbleToolbar: true` shows a compact floating toolbar above selected text
|
|
103
|
+
- **Bubble toolbar** — `bubbleToolbar: true` shows a compact floating toolbar above selected text; default buttons: bold, italic, underline, strikethrough, link, **text colour**, **highlight colour**, remove format, inline code; each colour button displays a live colour-strip indicator and opens an inline colour palette (matching the context menu); button set configurable via `bubbleToolbarItems`
|
|
104
104
|
- **Markdown shortcuts** — `markdownShortcuts: true` (default) converts Markdown syntax typed in the editor into HTML in real time: `# ` → H1–H3, `> ` → blockquote, `- ` / `* ` → unordered list, `1. ` → ordered list, `[ ] ` → checklist, `---` → HR, ` ``` ` → code block; inline: `**bold**`, `*italic*`, `~~strikethrough~~`, `` `code` ``
|
|
105
105
|
- **Custom focus ring** — `focusColor` accepts any CSS colour string to override the default blue focus ring
|
|
106
106
|
- **Spellcheck** — browser spellcheck enabled by default (`spellcheck: true`)
|
package/dist/autumnnote.css
CHANGED
|
@@ -68,6 +68,7 @@
|
|
|
68
68
|
background: #f9fafb;
|
|
69
69
|
border-bottom: 1px solid #d1d5db;
|
|
70
70
|
user-select: none;
|
|
71
|
+
border-radius: 6px 6px 0 0;
|
|
71
72
|
}
|
|
72
73
|
|
|
73
74
|
.an-btn-group {
|
|
@@ -140,9 +141,7 @@
|
|
|
140
141
|
|
|
141
142
|
.an-table-picker-popup {
|
|
142
143
|
display: none;
|
|
143
|
-
position:
|
|
144
|
-
top: calc(100% + 4px);
|
|
145
|
-
left: 0;
|
|
144
|
+
position: fixed;
|
|
146
145
|
background: #ffffff;
|
|
147
146
|
border: 1px solid #d1d5db;
|
|
148
147
|
border-radius: 6px;
|
|
@@ -654,6 +653,11 @@
|
|
|
654
653
|
font-size: 11px;
|
|
655
654
|
color: #6b7280;
|
|
656
655
|
user-select: none;
|
|
656
|
+
border-radius: 0 0 6px 6px;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
.an-container > .an-editable:last-child {
|
|
660
|
+
border-radius: 0 0 6px 6px;
|
|
657
661
|
}
|
|
658
662
|
|
|
659
663
|
.an-status-info {
|
|
@@ -1725,6 +1729,30 @@
|
|
|
1725
1729
|
border-radius: 2px;
|
|
1726
1730
|
}
|
|
1727
1731
|
|
|
1732
|
+
@media (max-width: 640px) {
|
|
1733
|
+
.an-toolbar {
|
|
1734
|
+
flex-wrap: nowrap;
|
|
1735
|
+
overflow-x: auto;
|
|
1736
|
+
scrollbar-width: none;
|
|
1737
|
+
-ms-overflow-style: none;
|
|
1738
|
+
}
|
|
1739
|
+
.an-toolbar::-webkit-scrollbar {
|
|
1740
|
+
display: none;
|
|
1741
|
+
}
|
|
1742
|
+
.an-select {
|
|
1743
|
+
min-width: 72px;
|
|
1744
|
+
max-width: 90px;
|
|
1745
|
+
font-size: 11px;
|
|
1746
|
+
}
|
|
1747
|
+
.an-select-style {
|
|
1748
|
+
min-width: 62px;
|
|
1749
|
+
max-width: 76px;
|
|
1750
|
+
}
|
|
1751
|
+
.an-select-narrow {
|
|
1752
|
+
min-width: 50px;
|
|
1753
|
+
max-width: 66px;
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1728
1756
|
.an-dir-rtl .an-toolbar {
|
|
1729
1757
|
direction: rtl;
|
|
1730
1758
|
}
|
|
@@ -1910,6 +1938,27 @@
|
|
|
1910
1938
|
background: #dbeafe;
|
|
1911
1939
|
color: #3b82f6;
|
|
1912
1940
|
}
|
|
1941
|
+
.an-bubble-btn.an-bubble-btn--color {
|
|
1942
|
+
flex-direction: column;
|
|
1943
|
+
gap: 1px;
|
|
1944
|
+
padding: 4px 0 3px;
|
|
1945
|
+
}
|
|
1946
|
+
.an-bubble-btn.an-bubble-btn--color .an-bubble-btn-svg {
|
|
1947
|
+
display: flex;
|
|
1948
|
+
align-items: center;
|
|
1949
|
+
justify-content: center;
|
|
1950
|
+
line-height: 0;
|
|
1951
|
+
}
|
|
1952
|
+
.an-bubble-btn.an-bubble-btn--color .an-bubble-btn-svg svg {
|
|
1953
|
+
display: block;
|
|
1954
|
+
}
|
|
1955
|
+
.an-bubble-btn.an-bubble-btn--color .an-bubble-color-strip {
|
|
1956
|
+
display: block;
|
|
1957
|
+
width: 14px;
|
|
1958
|
+
height: 3px;
|
|
1959
|
+
border-radius: 1px;
|
|
1960
|
+
pointer-events: none;
|
|
1961
|
+
}
|
|
1913
1962
|
.an-theme-dark .an-bubble-btn {
|
|
1914
1963
|
color: #cdd6f4;
|
|
1915
1964
|
}
|
|
@@ -1921,6 +1970,21 @@
|
|
|
1921
1970
|
color: #93c5fd;
|
|
1922
1971
|
}
|
|
1923
1972
|
|
|
1973
|
+
.an-bubble-color-picker {
|
|
1974
|
+
position: fixed;
|
|
1975
|
+
z-index: 10041;
|
|
1976
|
+
background: #ffffff;
|
|
1977
|
+
border: 1px solid #d1d5db;
|
|
1978
|
+
border-radius: 8px;
|
|
1979
|
+
box-shadow: 0 8px 28px rgba(0, 0, 0, 0.14);
|
|
1980
|
+
padding: 4px 0;
|
|
1981
|
+
min-width: 160px;
|
|
1982
|
+
}
|
|
1983
|
+
.an-theme-dark .an-bubble-color-picker {
|
|
1984
|
+
background: #24273a;
|
|
1985
|
+
border-color: #3f3f5f;
|
|
1986
|
+
}
|
|
1987
|
+
|
|
1924
1988
|
.an-mention-dropdown {
|
|
1925
1989
|
display: none;
|
|
1926
1990
|
position: fixed;
|
package/dist/autumnnote.es.js
CHANGED
|
@@ -1455,6 +1455,7 @@ var defaultOptions = {
|
|
|
1455
1455
|
"underline",
|
|
1456
1456
|
"link",
|
|
1457
1457
|
"foreColor",
|
|
1458
|
+
"hiliteColor",
|
|
1458
1459
|
"removeFormat"
|
|
1459
1460
|
],
|
|
1460
1461
|
mention: null
|
|
@@ -5838,7 +5839,18 @@ var Toolbar = class {
|
|
|
5838
5839
|
};
|
|
5839
5840
|
const openPopup = () => {
|
|
5840
5841
|
isOpen = true;
|
|
5842
|
+
const rect = btn.getBoundingClientRect();
|
|
5843
|
+
popup.style.visibility = "hidden";
|
|
5841
5844
|
popup.style.display = "block";
|
|
5845
|
+
const pw = popup.offsetWidth;
|
|
5846
|
+
const ph = popup.offsetHeight;
|
|
5847
|
+
let left = rect.left;
|
|
5848
|
+
let top = rect.bottom + 4;
|
|
5849
|
+
if (left + pw > window.innerWidth - 8) left = Math.max(8, window.innerWidth - pw - 8);
|
|
5850
|
+
if (top + ph > window.innerHeight - 8) top = rect.top - ph - 4;
|
|
5851
|
+
popup.style.left = `${left}px`;
|
|
5852
|
+
popup.style.top = `${top}px`;
|
|
5853
|
+
popup.style.visibility = "";
|
|
5842
5854
|
btn.setAttribute("aria-expanded", "true");
|
|
5843
5855
|
};
|
|
5844
5856
|
const closePopup = () => {
|
|
@@ -5870,9 +5882,11 @@ var Toolbar = class {
|
|
|
5870
5882
|
const d5 = on(document, "click", () => {
|
|
5871
5883
|
if (isOpen) closePopup();
|
|
5872
5884
|
});
|
|
5873
|
-
this._disposers.push(d1, d2, d3, d4, d5)
|
|
5885
|
+
this._disposers.push(d1, d2, d3, d4, d5, () => {
|
|
5886
|
+
if (popup.parentNode) popup.parentNode.removeChild(popup);
|
|
5887
|
+
});
|
|
5874
5888
|
wrap.appendChild(btn);
|
|
5875
|
-
|
|
5889
|
+
document.body.appendChild(popup);
|
|
5876
5890
|
return wrap;
|
|
5877
5891
|
}
|
|
5878
5892
|
/**
|
|
@@ -12969,7 +12983,7 @@ var ICONS = {
|
|
|
12969
12983
|
noColor: `<svg xmlns="http://www.w3.org/2000/svg" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><line x1="4" y1="4" x2="20" y2="20"/><line x1="20" y1="4" x2="4" y2="20"/></svg>`,
|
|
12970
12984
|
back: `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"/></svg>`
|
|
12971
12985
|
};
|
|
12972
|
-
var COLOR_PRESETS = [
|
|
12986
|
+
var COLOR_PRESETS$1 = [
|
|
12973
12987
|
"#000000",
|
|
12974
12988
|
"#434343",
|
|
12975
12989
|
"#666666",
|
|
@@ -13257,7 +13271,7 @@ var ContextMenu = class {
|
|
|
13257
13271
|
}
|
|
13258
13272
|
if (it.colorPalette) {
|
|
13259
13273
|
const palette = createElement("div", { class: "an-context-color-palette" });
|
|
13260
|
-
COLOR_PRESETS.forEach((color) => {
|
|
13274
|
+
COLOR_PRESETS$1.forEach((color) => {
|
|
13261
13275
|
const sw = createElement("div", {
|
|
13262
13276
|
class: "an-context-color-swatch",
|
|
13263
13277
|
title: color,
|
|
@@ -14916,9 +14930,35 @@ var MarkdownShortcuts = class {
|
|
|
14916
14930
|
* controlled by `bubbleToolbarItems` (array of button name strings).
|
|
14917
14931
|
*
|
|
14918
14932
|
* Built-in names: 'bold', 'italic', 'underline', 'strikethrough',
|
|
14919
|
-
* 'link', 'foreColor', 'removeFormat', 'inlineCode'.
|
|
14933
|
+
* 'link', 'foreColor', 'hiliteColor', 'removeFormat', 'inlineCode'.
|
|
14920
14934
|
* Any name not found in the built-in map is silently ignored.
|
|
14921
14935
|
*/
|
|
14936
|
+
var COLOR_PRESETS = [
|
|
14937
|
+
"#000000",
|
|
14938
|
+
"#434343",
|
|
14939
|
+
"#666666",
|
|
14940
|
+
"#999999",
|
|
14941
|
+
"#b7b7b7",
|
|
14942
|
+
"#cccccc",
|
|
14943
|
+
"#efefef",
|
|
14944
|
+
"#ffffff",
|
|
14945
|
+
"#ff0000",
|
|
14946
|
+
"#ff9900",
|
|
14947
|
+
"#ffff00",
|
|
14948
|
+
"#00ff00",
|
|
14949
|
+
"#00ffff",
|
|
14950
|
+
"#4a86e8",
|
|
14951
|
+
"#9900ff",
|
|
14952
|
+
"#ff00ff",
|
|
14953
|
+
"#f4cccc",
|
|
14954
|
+
"#fce5cd",
|
|
14955
|
+
"#fff2cc",
|
|
14956
|
+
"#d9ead3",
|
|
14957
|
+
"#d0e0e3",
|
|
14958
|
+
"#c9daf8",
|
|
14959
|
+
"#d9d2e9",
|
|
14960
|
+
"#ead1dc"
|
|
14961
|
+
];
|
|
14922
14962
|
var _S = "stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"";
|
|
14923
14963
|
var _svg = (p) => `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" ${_S} style="display:block">${p}</svg>`;
|
|
14924
14964
|
var _ICONS = {
|
|
@@ -14928,6 +14968,7 @@ var _ICONS = {
|
|
|
14928
14968
|
strikethrough: _svg("<path d=\"M17.3 12H6.7\"/><path d=\"M10 6.5C10 5.1 11.1 4 12.5 4c1.4 0 2.5 1.1 2.5 2.5 0 .8-.4 1.5-1 2\"/><path d=\"M14 17.5C14 19 12.9 20 11.5 20 10.1 20 9 18.9 9 17.5c0-.8.4-1.5 1-2\"/>"),
|
|
14929
14969
|
link: _svg("<path d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\"/><path d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\"/>"),
|
|
14930
14970
|
foreColor: _svg("<path d=\"M4 20L12 4L20 20\"/><line x1=\"7.5\" y1=\"14\" x2=\"16.5\" y2=\"14\"/>"),
|
|
14971
|
+
hiliteColor: _svg("<path d=\"M3 21v-4l9-9 4 4-9 9z\"/><path d=\"M12 8l4 4\"/><line x1=\"3\" y1=\"21\" x2=\"21\" y2=\"21\"/>"),
|
|
14931
14972
|
removeFormat: _svg("<path d=\"m7 21-4.3-4.3c-1-1-1-2.5 0-3.4l9.6-9.6c1-1 2.5-1 3.4 0l5.6 5.6c1 1 1 2.5 0 3.4L13 21\"/><path d=\"M22 21H7\"/><path d=\"m5 11 9 9\"/>"),
|
|
14932
14973
|
inlineCode: _svg("<path d=\"M8 3H7a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5c0 1.1.9 2 2 2h1\"/><path d=\"M16 3h1a2 2 0 0 1 2 2v5c0 1.1.9 2 2 2a2 2 0 0 1-2 2v5a2 2 0 0 1-2 2h-1\"/>")
|
|
14933
14974
|
};
|
|
@@ -14937,11 +14978,13 @@ var _ACTIONS = {
|
|
|
14937
14978
|
underline: (ctx) => ctx.invoke("editor.underline"),
|
|
14938
14979
|
strikethrough: (ctx) => ctx.invoke("editor.strikethrough"),
|
|
14939
14980
|
link: (ctx) => ctx.invoke("linkDialog.show"),
|
|
14940
|
-
|
|
14941
|
-
const
|
|
14942
|
-
if (
|
|
14981
|
+
removeFormat: (ctx) => {
|
|
14982
|
+
const editable = ctx.layoutInfo && ctx.layoutInfo.editable;
|
|
14983
|
+
if (!editable) return;
|
|
14984
|
+
editable.focus();
|
|
14985
|
+
document.execCommand("removeFormat");
|
|
14986
|
+
ctx.invoke("editor.afterCommand");
|
|
14943
14987
|
},
|
|
14944
|
-
removeFormat: (ctx) => ctx.invoke("editor.removeFormat"),
|
|
14945
14988
|
inlineCode: (ctx) => ctx.invoke("editor.inlineCode")
|
|
14946
14989
|
};
|
|
14947
14990
|
var _ACTIVE = {
|
|
@@ -14950,6 +14993,10 @@ var _ACTIVE = {
|
|
|
14950
14993
|
underline: () => document.queryCommandState("underline"),
|
|
14951
14994
|
strikethrough: () => document.queryCommandState("strikeThrough")
|
|
14952
14995
|
};
|
|
14996
|
+
var _COLOR_TYPES = {
|
|
14997
|
+
foreColor: true,
|
|
14998
|
+
hiliteColor: true
|
|
14999
|
+
};
|
|
14953
15000
|
var BubbleToolbar = class {
|
|
14954
15001
|
/** @param {import('../Context.js').Context} context */
|
|
14955
15002
|
constructor(context) {
|
|
@@ -14957,6 +15004,10 @@ var BubbleToolbar = class {
|
|
|
14957
15004
|
this.options = context.options;
|
|
14958
15005
|
/** @type {HTMLElement|null} */
|
|
14959
15006
|
this._el = null;
|
|
15007
|
+
/** @type {HTMLElement|null} */
|
|
15008
|
+
this._picker = null;
|
|
15009
|
+
this._pickerType = null;
|
|
15010
|
+
this._savedRange = null;
|
|
14960
15011
|
this._visible = false;
|
|
14961
15012
|
this._rafId = null;
|
|
14962
15013
|
this._contextMenuOpen = false;
|
|
@@ -14969,6 +15020,7 @@ var BubbleToolbar = class {
|
|
|
14969
15020
|
initialize() {
|
|
14970
15021
|
if (!this.options.bubbleToolbar) return this;
|
|
14971
15022
|
this._build();
|
|
15023
|
+
this._buildColorPicker();
|
|
14972
15024
|
const d1 = on(document, "selectionchange", this._onSelectionChange);
|
|
14973
15025
|
const d2 = on(document, "mousedown", this._onMousedown);
|
|
14974
15026
|
const d3 = on(document, "keydown", this._onKeydown);
|
|
@@ -14986,6 +15038,8 @@ var BubbleToolbar = class {
|
|
|
14986
15038
|
destroy() {
|
|
14987
15039
|
if (this._el && this._el.parentNode) this._el.parentNode.removeChild(this._el);
|
|
14988
15040
|
this._el = null;
|
|
15041
|
+
if (this._picker && this._picker.parentNode) this._picker.parentNode.removeChild(this._picker);
|
|
15042
|
+
this._picker = null;
|
|
14989
15043
|
this._disposers.forEach((d) => d());
|
|
14990
15044
|
this._disposers = [];
|
|
14991
15045
|
cancelAnimationFrame(this._rafId);
|
|
@@ -15001,25 +15055,41 @@ var BubbleToolbar = class {
|
|
|
15001
15055
|
"underline",
|
|
15002
15056
|
"link",
|
|
15003
15057
|
"foreColor",
|
|
15058
|
+
"hiliteColor",
|
|
15004
15059
|
"removeFormat"
|
|
15005
15060
|
];
|
|
15006
15061
|
for (const name of items) {
|
|
15007
|
-
if (!_ICONS[name]
|
|
15062
|
+
if (!_ICONS[name]) continue;
|
|
15008
15063
|
const btn = document.createElement("button");
|
|
15009
15064
|
btn.type = "button";
|
|
15010
|
-
btn.className = "an-bubble-btn";
|
|
15011
15065
|
btn.dataset.name = name;
|
|
15012
15066
|
btn.setAttribute("aria-label", name);
|
|
15013
|
-
|
|
15067
|
+
if (_COLOR_TYPES[name]) {
|
|
15068
|
+
btn.className = "an-bubble-btn an-bubble-btn--color";
|
|
15069
|
+
const iconWrap = document.createElement("span");
|
|
15070
|
+
iconWrap.className = "an-bubble-btn-svg";
|
|
15071
|
+
iconWrap.innerHTML = _ICONS[name];
|
|
15072
|
+
const strip = document.createElement("span");
|
|
15073
|
+
strip.className = "an-bubble-color-strip";
|
|
15074
|
+
strip.style.background = name === "foreColor" ? "#000000" : "transparent";
|
|
15075
|
+
btn.appendChild(iconWrap);
|
|
15076
|
+
btn.appendChild(strip);
|
|
15077
|
+
} else {
|
|
15078
|
+
btn.className = "an-bubble-btn";
|
|
15079
|
+
btn.innerHTML = _ICONS[name];
|
|
15080
|
+
}
|
|
15014
15081
|
btn.addEventListener("mousedown", (e) => {
|
|
15015
15082
|
e.preventDefault();
|
|
15016
15083
|
});
|
|
15017
15084
|
btn.addEventListener("click", (e) => {
|
|
15018
15085
|
e.preventDefault();
|
|
15019
15086
|
e.stopPropagation();
|
|
15087
|
+
if (_COLOR_TYPES[name]) {
|
|
15088
|
+
this._openColorPicker(name, btn);
|
|
15089
|
+
return;
|
|
15090
|
+
}
|
|
15020
15091
|
this.context.invoke("editor.focus");
|
|
15021
|
-
_ACTIONS[name](this.context);
|
|
15022
|
-
this.context.invoke("editor.afterCommand");
|
|
15092
|
+
if (_ACTIONS[name]) _ACTIONS[name](this.context);
|
|
15023
15093
|
this._syncActive();
|
|
15024
15094
|
});
|
|
15025
15095
|
el.appendChild(btn);
|
|
@@ -15027,6 +15097,100 @@ var BubbleToolbar = class {
|
|
|
15027
15097
|
document.body.appendChild(el);
|
|
15028
15098
|
this._el = el;
|
|
15029
15099
|
}
|
|
15100
|
+
_buildColorPicker() {
|
|
15101
|
+
const picker = document.createElement("div");
|
|
15102
|
+
picker.className = "an-bubble-color-picker";
|
|
15103
|
+
picker.style.display = "none";
|
|
15104
|
+
picker.addEventListener("mousedown", (e) => e.preventDefault());
|
|
15105
|
+
const palette = document.createElement("div");
|
|
15106
|
+
palette.className = "an-context-color-palette";
|
|
15107
|
+
COLOR_PRESETS.forEach((color) => {
|
|
15108
|
+
const sw = document.createElement("div");
|
|
15109
|
+
sw.className = "an-context-color-swatch";
|
|
15110
|
+
sw.title = color;
|
|
15111
|
+
sw.style.background = color;
|
|
15112
|
+
sw.setAttribute("role", "button");
|
|
15113
|
+
sw.setAttribute("aria-label", color);
|
|
15114
|
+
sw.addEventListener("click", (e) => {
|
|
15115
|
+
e.stopPropagation();
|
|
15116
|
+
this._applyColor(this._pickerType, color);
|
|
15117
|
+
});
|
|
15118
|
+
palette.appendChild(sw);
|
|
15119
|
+
});
|
|
15120
|
+
const noColorBtn = document.createElement("div");
|
|
15121
|
+
noColorBtn.className = "an-context-color-swatch an-context-color-none";
|
|
15122
|
+
noColorBtn.title = "No highlight";
|
|
15123
|
+
noColorBtn.setAttribute("role", "button");
|
|
15124
|
+
noColorBtn.setAttribute("aria-label", "No highlight");
|
|
15125
|
+
noColorBtn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><line x1="4" y1="4" x2="20" y2="20"/><line x1="20" y1="4" x2="4" y2="20"/></svg>`;
|
|
15126
|
+
noColorBtn.addEventListener("click", (e) => {
|
|
15127
|
+
e.stopPropagation();
|
|
15128
|
+
this._applyColor("hiliteColor", "transparent");
|
|
15129
|
+
});
|
|
15130
|
+
const customRow = document.createElement("div");
|
|
15131
|
+
customRow.className = "an-context-color-custom";
|
|
15132
|
+
const colorInput = document.createElement("input");
|
|
15133
|
+
colorInput.type = "color";
|
|
15134
|
+
colorInput.value = "#000000";
|
|
15135
|
+
colorInput.title = "Custom color";
|
|
15136
|
+
colorInput.addEventListener("mousedown", (e) => e.stopPropagation());
|
|
15137
|
+
colorInput.addEventListener("change", () => {
|
|
15138
|
+
this._applyColor(this._pickerType, colorInput.value);
|
|
15139
|
+
});
|
|
15140
|
+
const customLabel = document.createElement("span");
|
|
15141
|
+
customLabel.textContent = "Custom…";
|
|
15142
|
+
customRow.appendChild(colorInput);
|
|
15143
|
+
customRow.appendChild(customLabel);
|
|
15144
|
+
picker.appendChild(palette);
|
|
15145
|
+
picker.appendChild(customRow);
|
|
15146
|
+
document.body.appendChild(picker);
|
|
15147
|
+
this._picker = picker;
|
|
15148
|
+
this._picker._paletteEl = palette;
|
|
15149
|
+
this._picker._noColorBtn = noColorBtn;
|
|
15150
|
+
this._picker._colorInput = colorInput;
|
|
15151
|
+
}
|
|
15152
|
+
_openColorPicker(type, anchorBtn) {
|
|
15153
|
+
const sel = window.getSelection();
|
|
15154
|
+
if (sel && sel.rangeCount > 0) this._savedRange = sel.getRangeAt(0).cloneRange();
|
|
15155
|
+
this._pickerType = type;
|
|
15156
|
+
const palette = this._picker._paletteEl;
|
|
15157
|
+
const noColorBtn = this._picker._noColorBtn;
|
|
15158
|
+
if (type === "hiliteColor") {
|
|
15159
|
+
if (!palette.contains(noColorBtn)) palette.appendChild(noColorBtn);
|
|
15160
|
+
} else if (palette.contains(noColorBtn)) palette.removeChild(noColorBtn);
|
|
15161
|
+
this._picker._colorInput.value = type === "foreColor" ? "#000000" : "#ffff00";
|
|
15162
|
+
this._picker.style.display = "block";
|
|
15163
|
+
const pw = this._picker.offsetWidth;
|
|
15164
|
+
const ph = this._picker.offsetHeight;
|
|
15165
|
+
const toolbarRect = this._el.getBoundingClientRect();
|
|
15166
|
+
let top = toolbarRect.top - ph - 6;
|
|
15167
|
+
if (top < 8) top = toolbarRect.bottom + 6;
|
|
15168
|
+
let left = anchorBtn.getBoundingClientRect().left;
|
|
15169
|
+
left = Math.max(8, Math.min(left, window.innerWidth - pw - 8));
|
|
15170
|
+
this._picker.style.left = `${left}px`;
|
|
15171
|
+
this._picker.style.top = `${top}px`;
|
|
15172
|
+
}
|
|
15173
|
+
_closeColorPicker() {
|
|
15174
|
+
if (this._picker) this._picker.style.display = "none";
|
|
15175
|
+
this._pickerType = null;
|
|
15176
|
+
}
|
|
15177
|
+
/** Restore the saved selection, apply execCommand, update the color strip, then close the picker. */
|
|
15178
|
+
_applyColor(type, color) {
|
|
15179
|
+
const editable = this.context.layoutInfo && this.context.layoutInfo.editable;
|
|
15180
|
+
if (!editable || !this._savedRange) return;
|
|
15181
|
+
editable.focus();
|
|
15182
|
+
const sel = window.getSelection();
|
|
15183
|
+
sel.removeAllRanges();
|
|
15184
|
+
sel.addRange(this._savedRange.cloneRange());
|
|
15185
|
+
document.execCommand(type, false, color);
|
|
15186
|
+
this.context.invoke("editor.afterCommand");
|
|
15187
|
+
const name = type === "hiliteColor" ? "hiliteColor" : "foreColor";
|
|
15188
|
+
const btn = this._el && this._el.querySelector(`[data-name="${name}"]`);
|
|
15189
|
+
const strip = btn && btn.querySelector(".an-bubble-color-strip");
|
|
15190
|
+
if (strip) strip.style.background = color === "transparent" ? "transparent" : color;
|
|
15191
|
+
this._closeColorPicker();
|
|
15192
|
+
this._syncActive();
|
|
15193
|
+
}
|
|
15030
15194
|
_show(rect) {
|
|
15031
15195
|
if (!this._el) return;
|
|
15032
15196
|
const el = this._el;
|
|
@@ -15043,12 +15207,14 @@ var BubbleToolbar = class {
|
|
|
15043
15207
|
el.style.left = `${left}px`;
|
|
15044
15208
|
el.style.visibility = "";
|
|
15045
15209
|
this._syncActive();
|
|
15210
|
+
this._syncColorStrips();
|
|
15046
15211
|
this._visible = true;
|
|
15047
15212
|
}
|
|
15048
15213
|
_hide() {
|
|
15049
15214
|
if (!this._el) return;
|
|
15050
15215
|
this._el.style.display = "none";
|
|
15051
15216
|
this._visible = false;
|
|
15217
|
+
this._closeColorPicker();
|
|
15052
15218
|
}
|
|
15053
15219
|
_syncActive() {
|
|
15054
15220
|
if (!this._el) return;
|
|
@@ -15057,10 +15223,30 @@ var BubbleToolbar = class {
|
|
|
15057
15223
|
btn.classList.toggle("an-active", !!(activeFn && activeFn()));
|
|
15058
15224
|
});
|
|
15059
15225
|
}
|
|
15226
|
+
/** Read the current selection's color and update the color-strip indicators. */
|
|
15227
|
+
_syncColorStrips() {
|
|
15228
|
+
if (!this._el) return;
|
|
15229
|
+
const sel = window.getSelection();
|
|
15230
|
+
if (!sel || !sel.rangeCount) return;
|
|
15231
|
+
let node = sel.getRangeAt(0).startContainer;
|
|
15232
|
+
if (node.nodeType === Node.TEXT_NODE) node = node.parentElement;
|
|
15233
|
+
if (!node) return;
|
|
15234
|
+
const cs = window.getComputedStyle(node);
|
|
15235
|
+
const foreBtn = this._el.querySelector("[data-name=\"foreColor\"]");
|
|
15236
|
+
const foreStrip = foreBtn && foreBtn.querySelector(".an-bubble-color-strip");
|
|
15237
|
+
if (foreStrip) foreStrip.style.background = cs.color || "#000000";
|
|
15238
|
+
const hiliteBtn = this._el.querySelector("[data-name=\"hiliteColor\"]");
|
|
15239
|
+
const hiliteStrip = hiliteBtn && hiliteBtn.querySelector(".an-bubble-color-strip");
|
|
15240
|
+
if (hiliteStrip) {
|
|
15241
|
+
const bg = cs.backgroundColor;
|
|
15242
|
+
hiliteStrip.style.background = !bg || bg === "rgba(0, 0, 0, 0)" || bg === "transparent" ? "transparent" : bg;
|
|
15243
|
+
}
|
|
15244
|
+
}
|
|
15060
15245
|
_onSelectionChange() {
|
|
15061
15246
|
cancelAnimationFrame(this._rafId);
|
|
15062
15247
|
this._rafId = requestAnimationFrame(() => {
|
|
15063
15248
|
if (this._contextMenuOpen) return;
|
|
15249
|
+
if (this._picker && this._picker.style.display !== "none") return;
|
|
15064
15250
|
const sel = window.getSelection();
|
|
15065
15251
|
if (!sel || sel.isCollapsed || !sel.rangeCount) {
|
|
15066
15252
|
this._hide();
|
|
@@ -15085,11 +15271,15 @@ var BubbleToolbar = class {
|
|
|
15085
15271
|
_onMousedown(e) {
|
|
15086
15272
|
if (!this._visible) return;
|
|
15087
15273
|
if (this._el && this._el.contains(e.target)) return;
|
|
15274
|
+
if (this._picker && this._picker.contains(e.target)) return;
|
|
15088
15275
|
if (this.context.layoutInfo.editable.contains(e.target)) return;
|
|
15089
15276
|
this._hide();
|
|
15090
15277
|
}
|
|
15091
15278
|
_onKeydown(e) {
|
|
15092
|
-
if (e.key === "Escape"
|
|
15279
|
+
if (e.key === "Escape") {
|
|
15280
|
+
if (this._picker && this._picker.style.display !== "none") this._closeColorPicker();
|
|
15281
|
+
else if (this._visible) this._hide();
|
|
15282
|
+
}
|
|
15093
15283
|
}
|
|
15094
15284
|
_onContextMenu() {
|
|
15095
15285
|
this._hide();
|