autumnnote 1.1.0 → 1.2.0
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 +8 -7
- package/dist/autumnnote.css +67 -3
- package/dist/autumnnote.es.js +202 -14
- package/dist/autumnnote.es.js.map +1 -1
- package/dist/autumnnote.umd.js +202 -14
- package/dist/autumnnote.umd.js.map +1 -1
- package/package.json +22 -3
- package/src/js/index.js +1 -1
- package/src/js/module/BubbleToolbar.js +219 -15
- package/src/js/module/Toolbar.js +13 -0
- package/src/js/settings.js +1 -1
- package/src/styles/autumnnote.scss +82 -3
package/README.md
CHANGED
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
<p align="center"><img src="image/banner.png" width="120" alt="AutumnNote Banner"/></p>
|
|
4
4
|
|
|
5
|
-
[](https://www.npmjs.com/package/autumnnote)
|
|
6
|
+
[](https://www.npmjs.com/package/autumnnote)
|
|
7
|
+
[](https://bundlephobia.com/package/autumnnote)
|
|
8
|
+
[](https://github.com/cmm-cmm/Autumn-Note)
|
|
6
9
|
[](https://github.com/cmm-cmm/Autumn-Note/actions/workflows/pages.yml)
|
|
7
10
|
[](https://developer.mozilla.org/en-US/docs/Web/JavaScript)
|
|
8
|
-
[](https://vitejs.dev/)
|
|
9
|
-
[](https://vitest.dev/)
|
|
10
11
|
[](https://opensource.org/licenses/MIT)
|
|
11
12
|
[](#)
|
|
12
|
-
[](types/index.d.ts)
|
|
13
14
|
|
|
14
15
|
A modern, lightweight WYSIWYG rich-text editor built with vanilla JavaScript (ES2022+) — no jQuery, no heavy dependencies.
|
|
15
16
|
|
|
@@ -87,7 +88,7 @@ Right-click inside the editor opens a context menu with: **Undo**, **Redo**, **C
|
|
|
87
88
|
- **Auto-fallback** — unknown codes or missing keys fall back to English
|
|
88
89
|
|
|
89
90
|
### UI
|
|
90
|
-
- **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
|
|
91
92
|
- **Sticky toolbar** — `stickyToolbar: true` pins the toolbar to the viewport top; configurable offset for fixed nav bars
|
|
92
93
|
- **Dark / light theme** — `theme: 'dark'` or `theme: 'light'` (default); full SCSS variable coverage
|
|
93
94
|
- **Image resizer** — drag handle on selected image to resize proportionally
|
|
@@ -99,7 +100,7 @@ Right-click inside the editor opens a context menu with: **Undo**, **Redo**, **C
|
|
|
99
100
|
- **Read-only mode** — `readOnly: true` renders a non-editable preview with toolbar hidden; toggle at runtime via `editor.setDisabled()`
|
|
100
101
|
- **Auto-save** — `autoSave: true` persists content to `localStorage` on every change; key configurable via `autoSaveKey`
|
|
101
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`
|
|
102
|
-
- **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`
|
|
103
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` ``
|
|
104
105
|
- **Custom focus ring** — `focusColor` accepts any CSS colour string to override the default blue focus ring
|
|
105
106
|
- **Spellcheck** — browser spellcheck enabled by default (`spellcheck: true`)
|
|
@@ -608,7 +609,7 @@ Build output in `dist/`:
|
|
|
608
609
|
|
|
609
610
|
## Comparison
|
|
610
611
|
|
|
611
|
-
The table below compares AutumnNote against editors commonly used in this category. Specific library names are
|
|
612
|
+
The table below compares AutumnNote against editors commonly used in this category — such as Summernote, Quill, TinyMCE, and Froala. Specific library names are kept generic to avoid direct bias; the comparison is based on publicly documented feature sets.
|
|
612
613
|
|
|
613
614
|
| Feature | Editor A | Editor B | Editor C | **AutumnNote** |
|
|
614
615
|
|---|---|---|---|---|
|
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,12 +5839,23 @@ var Toolbar = class {
|
|
|
5838
5839
|
};
|
|
5839
5840
|
const openPopup = () => {
|
|
5840
5841
|
isOpen = true;
|
|
5842
|
+
const rect = btn.getBoundingClientRect();
|
|
5841
5843
|
popup.style.display = "block";
|
|
5844
|
+
const pw = popup.offsetWidth;
|
|
5845
|
+
const ph = popup.offsetHeight;
|
|
5846
|
+
let left = rect.left;
|
|
5847
|
+
let top = rect.bottom + 4;
|
|
5848
|
+
if (left + pw > window.innerWidth - 8) left = Math.max(8, window.innerWidth - pw - 8);
|
|
5849
|
+
if (top + ph > window.innerHeight - 8) top = rect.top - ph - 4;
|
|
5850
|
+
popup.style.left = `${left}px`;
|
|
5851
|
+
popup.style.top = `${top}px`;
|
|
5842
5852
|
btn.setAttribute("aria-expanded", "true");
|
|
5843
5853
|
};
|
|
5844
5854
|
const closePopup = () => {
|
|
5845
5855
|
isOpen = false;
|
|
5846
5856
|
popup.style.display = "none";
|
|
5857
|
+
popup.style.top = "";
|
|
5858
|
+
popup.style.left = "";
|
|
5847
5859
|
btn.setAttribute("aria-expanded", "false");
|
|
5848
5860
|
setHighlight(0, 0);
|
|
5849
5861
|
};
|
|
@@ -12969,7 +12981,7 @@ var ICONS = {
|
|
|
12969
12981
|
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
12982
|
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
12983
|
};
|
|
12972
|
-
var COLOR_PRESETS = [
|
|
12984
|
+
var COLOR_PRESETS$1 = [
|
|
12973
12985
|
"#000000",
|
|
12974
12986
|
"#434343",
|
|
12975
12987
|
"#666666",
|
|
@@ -13257,7 +13269,7 @@ var ContextMenu = class {
|
|
|
13257
13269
|
}
|
|
13258
13270
|
if (it.colorPalette) {
|
|
13259
13271
|
const palette = createElement("div", { class: "an-context-color-palette" });
|
|
13260
|
-
COLOR_PRESETS.forEach((color) => {
|
|
13272
|
+
COLOR_PRESETS$1.forEach((color) => {
|
|
13261
13273
|
const sw = createElement("div", {
|
|
13262
13274
|
class: "an-context-color-swatch",
|
|
13263
13275
|
title: color,
|
|
@@ -14916,9 +14928,35 @@ var MarkdownShortcuts = class {
|
|
|
14916
14928
|
* controlled by `bubbleToolbarItems` (array of button name strings).
|
|
14917
14929
|
*
|
|
14918
14930
|
* Built-in names: 'bold', 'italic', 'underline', 'strikethrough',
|
|
14919
|
-
* 'link', 'foreColor', 'removeFormat', 'inlineCode'.
|
|
14931
|
+
* 'link', 'foreColor', 'hiliteColor', 'removeFormat', 'inlineCode'.
|
|
14920
14932
|
* Any name not found in the built-in map is silently ignored.
|
|
14921
14933
|
*/
|
|
14934
|
+
var COLOR_PRESETS = [
|
|
14935
|
+
"#000000",
|
|
14936
|
+
"#434343",
|
|
14937
|
+
"#666666",
|
|
14938
|
+
"#999999",
|
|
14939
|
+
"#b7b7b7",
|
|
14940
|
+
"#cccccc",
|
|
14941
|
+
"#efefef",
|
|
14942
|
+
"#ffffff",
|
|
14943
|
+
"#ff0000",
|
|
14944
|
+
"#ff9900",
|
|
14945
|
+
"#ffff00",
|
|
14946
|
+
"#00ff00",
|
|
14947
|
+
"#00ffff",
|
|
14948
|
+
"#4a86e8",
|
|
14949
|
+
"#9900ff",
|
|
14950
|
+
"#ff00ff",
|
|
14951
|
+
"#f4cccc",
|
|
14952
|
+
"#fce5cd",
|
|
14953
|
+
"#fff2cc",
|
|
14954
|
+
"#d9ead3",
|
|
14955
|
+
"#d0e0e3",
|
|
14956
|
+
"#c9daf8",
|
|
14957
|
+
"#d9d2e9",
|
|
14958
|
+
"#ead1dc"
|
|
14959
|
+
];
|
|
14922
14960
|
var _S = "stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"";
|
|
14923
14961
|
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
14962
|
var _ICONS = {
|
|
@@ -14928,6 +14966,7 @@ var _ICONS = {
|
|
|
14928
14966
|
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
14967
|
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
14968
|
foreColor: _svg("<path d=\"M4 20L12 4L20 20\"/><line x1=\"7.5\" y1=\"14\" x2=\"16.5\" y2=\"14\"/>"),
|
|
14969
|
+
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
14970
|
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
14971
|
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
14972
|
};
|
|
@@ -14937,11 +14976,13 @@ var _ACTIONS = {
|
|
|
14937
14976
|
underline: (ctx) => ctx.invoke("editor.underline"),
|
|
14938
14977
|
strikethrough: (ctx) => ctx.invoke("editor.strikethrough"),
|
|
14939
14978
|
link: (ctx) => ctx.invoke("linkDialog.show"),
|
|
14940
|
-
|
|
14941
|
-
const
|
|
14942
|
-
if (
|
|
14979
|
+
removeFormat: (ctx) => {
|
|
14980
|
+
const editable = ctx.layoutInfo && ctx.layoutInfo.editable;
|
|
14981
|
+
if (!editable) return;
|
|
14982
|
+
editable.focus();
|
|
14983
|
+
document.execCommand("removeFormat");
|
|
14984
|
+
ctx.invoke("editor.afterCommand");
|
|
14943
14985
|
},
|
|
14944
|
-
removeFormat: (ctx) => ctx.invoke("editor.removeFormat"),
|
|
14945
14986
|
inlineCode: (ctx) => ctx.invoke("editor.inlineCode")
|
|
14946
14987
|
};
|
|
14947
14988
|
var _ACTIVE = {
|
|
@@ -14950,6 +14991,10 @@ var _ACTIVE = {
|
|
|
14950
14991
|
underline: () => document.queryCommandState("underline"),
|
|
14951
14992
|
strikethrough: () => document.queryCommandState("strikeThrough")
|
|
14952
14993
|
};
|
|
14994
|
+
var _COLOR_TYPES = {
|
|
14995
|
+
foreColor: true,
|
|
14996
|
+
hiliteColor: true
|
|
14997
|
+
};
|
|
14953
14998
|
var BubbleToolbar = class {
|
|
14954
14999
|
/** @param {import('../Context.js').Context} context */
|
|
14955
15000
|
constructor(context) {
|
|
@@ -14957,6 +15002,10 @@ var BubbleToolbar = class {
|
|
|
14957
15002
|
this.options = context.options;
|
|
14958
15003
|
/** @type {HTMLElement|null} */
|
|
14959
15004
|
this._el = null;
|
|
15005
|
+
/** @type {HTMLElement|null} */
|
|
15006
|
+
this._picker = null;
|
|
15007
|
+
this._pickerType = null;
|
|
15008
|
+
this._savedRange = null;
|
|
14960
15009
|
this._visible = false;
|
|
14961
15010
|
this._rafId = null;
|
|
14962
15011
|
this._contextMenuOpen = false;
|
|
@@ -14969,6 +15018,7 @@ var BubbleToolbar = class {
|
|
|
14969
15018
|
initialize() {
|
|
14970
15019
|
if (!this.options.bubbleToolbar) return this;
|
|
14971
15020
|
this._build();
|
|
15021
|
+
this._buildColorPicker();
|
|
14972
15022
|
const d1 = on(document, "selectionchange", this._onSelectionChange);
|
|
14973
15023
|
const d2 = on(document, "mousedown", this._onMousedown);
|
|
14974
15024
|
const d3 = on(document, "keydown", this._onKeydown);
|
|
@@ -14986,6 +15036,8 @@ var BubbleToolbar = class {
|
|
|
14986
15036
|
destroy() {
|
|
14987
15037
|
if (this._el && this._el.parentNode) this._el.parentNode.removeChild(this._el);
|
|
14988
15038
|
this._el = null;
|
|
15039
|
+
if (this._picker && this._picker.parentNode) this._picker.parentNode.removeChild(this._picker);
|
|
15040
|
+
this._picker = null;
|
|
14989
15041
|
this._disposers.forEach((d) => d());
|
|
14990
15042
|
this._disposers = [];
|
|
14991
15043
|
cancelAnimationFrame(this._rafId);
|
|
@@ -15001,25 +15053,41 @@ var BubbleToolbar = class {
|
|
|
15001
15053
|
"underline",
|
|
15002
15054
|
"link",
|
|
15003
15055
|
"foreColor",
|
|
15056
|
+
"hiliteColor",
|
|
15004
15057
|
"removeFormat"
|
|
15005
15058
|
];
|
|
15006
15059
|
for (const name of items) {
|
|
15007
|
-
if (!_ICONS[name]
|
|
15060
|
+
if (!_ICONS[name]) continue;
|
|
15008
15061
|
const btn = document.createElement("button");
|
|
15009
15062
|
btn.type = "button";
|
|
15010
|
-
btn.className = "an-bubble-btn";
|
|
15011
15063
|
btn.dataset.name = name;
|
|
15012
15064
|
btn.setAttribute("aria-label", name);
|
|
15013
|
-
|
|
15065
|
+
if (_COLOR_TYPES[name]) {
|
|
15066
|
+
btn.className = "an-bubble-btn an-bubble-btn--color";
|
|
15067
|
+
const iconWrap = document.createElement("span");
|
|
15068
|
+
iconWrap.className = "an-bubble-btn-svg";
|
|
15069
|
+
iconWrap.innerHTML = _ICONS[name];
|
|
15070
|
+
const strip = document.createElement("span");
|
|
15071
|
+
strip.className = "an-bubble-color-strip";
|
|
15072
|
+
strip.style.background = name === "foreColor" ? "#000000" : "transparent";
|
|
15073
|
+
btn.appendChild(iconWrap);
|
|
15074
|
+
btn.appendChild(strip);
|
|
15075
|
+
} else {
|
|
15076
|
+
btn.className = "an-bubble-btn";
|
|
15077
|
+
btn.innerHTML = _ICONS[name];
|
|
15078
|
+
}
|
|
15014
15079
|
btn.addEventListener("mousedown", (e) => {
|
|
15015
15080
|
e.preventDefault();
|
|
15016
15081
|
});
|
|
15017
15082
|
btn.addEventListener("click", (e) => {
|
|
15018
15083
|
e.preventDefault();
|
|
15019
15084
|
e.stopPropagation();
|
|
15085
|
+
if (_COLOR_TYPES[name]) {
|
|
15086
|
+
this._openColorPicker(name, btn);
|
|
15087
|
+
return;
|
|
15088
|
+
}
|
|
15020
15089
|
this.context.invoke("editor.focus");
|
|
15021
|
-
_ACTIONS[name](this.context);
|
|
15022
|
-
this.context.invoke("editor.afterCommand");
|
|
15090
|
+
if (_ACTIONS[name]) _ACTIONS[name](this.context);
|
|
15023
15091
|
this._syncActive();
|
|
15024
15092
|
});
|
|
15025
15093
|
el.appendChild(btn);
|
|
@@ -15027,6 +15095,100 @@ var BubbleToolbar = class {
|
|
|
15027
15095
|
document.body.appendChild(el);
|
|
15028
15096
|
this._el = el;
|
|
15029
15097
|
}
|
|
15098
|
+
_buildColorPicker() {
|
|
15099
|
+
const picker = document.createElement("div");
|
|
15100
|
+
picker.className = "an-bubble-color-picker";
|
|
15101
|
+
picker.style.display = "none";
|
|
15102
|
+
picker.addEventListener("mousedown", (e) => e.preventDefault());
|
|
15103
|
+
const palette = document.createElement("div");
|
|
15104
|
+
palette.className = "an-context-color-palette";
|
|
15105
|
+
COLOR_PRESETS.forEach((color) => {
|
|
15106
|
+
const sw = document.createElement("div");
|
|
15107
|
+
sw.className = "an-context-color-swatch";
|
|
15108
|
+
sw.title = color;
|
|
15109
|
+
sw.style.background = color;
|
|
15110
|
+
sw.setAttribute("role", "button");
|
|
15111
|
+
sw.setAttribute("aria-label", color);
|
|
15112
|
+
sw.addEventListener("click", (e) => {
|
|
15113
|
+
e.stopPropagation();
|
|
15114
|
+
this._applyColor(this._pickerType, color);
|
|
15115
|
+
});
|
|
15116
|
+
palette.appendChild(sw);
|
|
15117
|
+
});
|
|
15118
|
+
const noColorBtn = document.createElement("div");
|
|
15119
|
+
noColorBtn.className = "an-context-color-swatch an-context-color-none";
|
|
15120
|
+
noColorBtn.title = "No highlight";
|
|
15121
|
+
noColorBtn.setAttribute("role", "button");
|
|
15122
|
+
noColorBtn.setAttribute("aria-label", "No highlight");
|
|
15123
|
+
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>`;
|
|
15124
|
+
noColorBtn.addEventListener("click", (e) => {
|
|
15125
|
+
e.stopPropagation();
|
|
15126
|
+
this._applyColor("hiliteColor", "transparent");
|
|
15127
|
+
});
|
|
15128
|
+
const customRow = document.createElement("div");
|
|
15129
|
+
customRow.className = "an-context-color-custom";
|
|
15130
|
+
const colorInput = document.createElement("input");
|
|
15131
|
+
colorInput.type = "color";
|
|
15132
|
+
colorInput.value = "#000000";
|
|
15133
|
+
colorInput.title = "Custom color";
|
|
15134
|
+
colorInput.addEventListener("mousedown", (e) => e.stopPropagation());
|
|
15135
|
+
colorInput.addEventListener("change", () => {
|
|
15136
|
+
this._applyColor(this._pickerType, colorInput.value);
|
|
15137
|
+
});
|
|
15138
|
+
const customLabel = document.createElement("span");
|
|
15139
|
+
customLabel.textContent = "Custom…";
|
|
15140
|
+
customRow.appendChild(colorInput);
|
|
15141
|
+
customRow.appendChild(customLabel);
|
|
15142
|
+
picker.appendChild(palette);
|
|
15143
|
+
picker.appendChild(customRow);
|
|
15144
|
+
document.body.appendChild(picker);
|
|
15145
|
+
this._picker = picker;
|
|
15146
|
+
this._picker._paletteEl = palette;
|
|
15147
|
+
this._picker._noColorBtn = noColorBtn;
|
|
15148
|
+
this._picker._colorInput = colorInput;
|
|
15149
|
+
}
|
|
15150
|
+
_openColorPicker(type, anchorBtn) {
|
|
15151
|
+
const sel = window.getSelection();
|
|
15152
|
+
if (sel && sel.rangeCount > 0) this._savedRange = sel.getRangeAt(0).cloneRange();
|
|
15153
|
+
this._pickerType = type;
|
|
15154
|
+
const palette = this._picker._paletteEl;
|
|
15155
|
+
const noColorBtn = this._picker._noColorBtn;
|
|
15156
|
+
if (type === "hiliteColor") {
|
|
15157
|
+
if (!palette.contains(noColorBtn)) palette.appendChild(noColorBtn);
|
|
15158
|
+
} else if (palette.contains(noColorBtn)) palette.removeChild(noColorBtn);
|
|
15159
|
+
this._picker._colorInput.value = type === "foreColor" ? "#000000" : "#ffff00";
|
|
15160
|
+
this._picker.style.display = "block";
|
|
15161
|
+
const pw = this._picker.offsetWidth;
|
|
15162
|
+
const ph = this._picker.offsetHeight;
|
|
15163
|
+
const toolbarRect = this._el.getBoundingClientRect();
|
|
15164
|
+
let top = toolbarRect.top - ph - 6;
|
|
15165
|
+
if (top < 8) top = toolbarRect.bottom + 6;
|
|
15166
|
+
let left = anchorBtn.getBoundingClientRect().left;
|
|
15167
|
+
left = Math.max(8, Math.min(left, window.innerWidth - pw - 8));
|
|
15168
|
+
this._picker.style.left = `${left}px`;
|
|
15169
|
+
this._picker.style.top = `${top}px`;
|
|
15170
|
+
}
|
|
15171
|
+
_closeColorPicker() {
|
|
15172
|
+
if (this._picker) this._picker.style.display = "none";
|
|
15173
|
+
this._pickerType = null;
|
|
15174
|
+
}
|
|
15175
|
+
/** Restore the saved selection, apply execCommand, update the color strip, then close the picker. */
|
|
15176
|
+
_applyColor(type, color) {
|
|
15177
|
+
const editable = this.context.layoutInfo && this.context.layoutInfo.editable;
|
|
15178
|
+
if (!editable || !this._savedRange) return;
|
|
15179
|
+
editable.focus();
|
|
15180
|
+
const sel = window.getSelection();
|
|
15181
|
+
sel.removeAllRanges();
|
|
15182
|
+
sel.addRange(this._savedRange.cloneRange());
|
|
15183
|
+
document.execCommand(type, false, color);
|
|
15184
|
+
this.context.invoke("editor.afterCommand");
|
|
15185
|
+
const name = type === "hiliteColor" ? "hiliteColor" : "foreColor";
|
|
15186
|
+
const btn = this._el && this._el.querySelector(`[data-name="${name}"]`);
|
|
15187
|
+
const strip = btn && btn.querySelector(".an-bubble-color-strip");
|
|
15188
|
+
if (strip) strip.style.background = color === "transparent" ? "transparent" : color;
|
|
15189
|
+
this._closeColorPicker();
|
|
15190
|
+
this._syncActive();
|
|
15191
|
+
}
|
|
15030
15192
|
_show(rect) {
|
|
15031
15193
|
if (!this._el) return;
|
|
15032
15194
|
const el = this._el;
|
|
@@ -15043,12 +15205,14 @@ var BubbleToolbar = class {
|
|
|
15043
15205
|
el.style.left = `${left}px`;
|
|
15044
15206
|
el.style.visibility = "";
|
|
15045
15207
|
this._syncActive();
|
|
15208
|
+
this._syncColorStrips();
|
|
15046
15209
|
this._visible = true;
|
|
15047
15210
|
}
|
|
15048
15211
|
_hide() {
|
|
15049
15212
|
if (!this._el) return;
|
|
15050
15213
|
this._el.style.display = "none";
|
|
15051
15214
|
this._visible = false;
|
|
15215
|
+
this._closeColorPicker();
|
|
15052
15216
|
}
|
|
15053
15217
|
_syncActive() {
|
|
15054
15218
|
if (!this._el) return;
|
|
@@ -15057,10 +15221,30 @@ var BubbleToolbar = class {
|
|
|
15057
15221
|
btn.classList.toggle("an-active", !!(activeFn && activeFn()));
|
|
15058
15222
|
});
|
|
15059
15223
|
}
|
|
15224
|
+
/** Read the current selection's color and update the color-strip indicators. */
|
|
15225
|
+
_syncColorStrips() {
|
|
15226
|
+
if (!this._el) return;
|
|
15227
|
+
const sel = window.getSelection();
|
|
15228
|
+
if (!sel || !sel.rangeCount) return;
|
|
15229
|
+
let node = sel.getRangeAt(0).startContainer;
|
|
15230
|
+
if (node.nodeType === Node.TEXT_NODE) node = node.parentElement;
|
|
15231
|
+
if (!node) return;
|
|
15232
|
+
const cs = window.getComputedStyle(node);
|
|
15233
|
+
const foreBtn = this._el.querySelector("[data-name=\"foreColor\"]");
|
|
15234
|
+
const foreStrip = foreBtn && foreBtn.querySelector(".an-bubble-color-strip");
|
|
15235
|
+
if (foreStrip) foreStrip.style.background = cs.color || "#000000";
|
|
15236
|
+
const hiliteBtn = this._el.querySelector("[data-name=\"hiliteColor\"]");
|
|
15237
|
+
const hiliteStrip = hiliteBtn && hiliteBtn.querySelector(".an-bubble-color-strip");
|
|
15238
|
+
if (hiliteStrip) {
|
|
15239
|
+
const bg = cs.backgroundColor;
|
|
15240
|
+
hiliteStrip.style.background = !bg || bg === "rgba(0, 0, 0, 0)" || bg === "transparent" ? "transparent" : bg;
|
|
15241
|
+
}
|
|
15242
|
+
}
|
|
15060
15243
|
_onSelectionChange() {
|
|
15061
15244
|
cancelAnimationFrame(this._rafId);
|
|
15062
15245
|
this._rafId = requestAnimationFrame(() => {
|
|
15063
15246
|
if (this._contextMenuOpen) return;
|
|
15247
|
+
if (this._picker && this._picker.style.display !== "none") return;
|
|
15064
15248
|
const sel = window.getSelection();
|
|
15065
15249
|
if (!sel || sel.isCollapsed || !sel.rangeCount) {
|
|
15066
15250
|
this._hide();
|
|
@@ -15085,11 +15269,15 @@ var BubbleToolbar = class {
|
|
|
15085
15269
|
_onMousedown(e) {
|
|
15086
15270
|
if (!this._visible) return;
|
|
15087
15271
|
if (this._el && this._el.contains(e.target)) return;
|
|
15272
|
+
if (this._picker && this._picker.contains(e.target)) return;
|
|
15088
15273
|
if (this.context.layoutInfo.editable.contains(e.target)) return;
|
|
15089
15274
|
this._hide();
|
|
15090
15275
|
}
|
|
15091
15276
|
_onKeydown(e) {
|
|
15092
|
-
if (e.key === "Escape"
|
|
15277
|
+
if (e.key === "Escape") {
|
|
15278
|
+
if (this._picker && this._picker.style.display !== "none") this._closeColorPicker();
|
|
15279
|
+
else if (this._visible) this._hide();
|
|
15280
|
+
}
|
|
15093
15281
|
}
|
|
15094
15282
|
_onContextMenu() {
|
|
15095
15283
|
this._hide();
|
|
@@ -15878,7 +16066,7 @@ var AutumnNote = {
|
|
|
15878
16066
|
registerModule(name, ModuleClass) {
|
|
15879
16067
|
_customModules.set(name, ModuleClass);
|
|
15880
16068
|
},
|
|
15881
|
-
version: "1.
|
|
16069
|
+
version: "1.1.1"
|
|
15882
16070
|
};
|
|
15883
16071
|
/**
|
|
15884
16072
|
* @param {string|Element|NodeList|Element[]} selector
|