@domternal/angular 0.15.0 → 1.0.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 +18 -5
- package/dist/README.md +18 -5
- package/dist/fesm2022/domternal-angular.mjs +188 -310
- package/dist/fesm2022/domternal-angular.mjs.map +1 -1
- package/dist/types/domternal-angular.d.ts +29 -21
- package/package.json +5 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { viewChild, input, output, signal, inject, NgZone, afterNextRender, effect, untracked, forwardRef, ViewEncapsulation, ChangeDetectionStrategy, Component, ElementRef, computed } from '@angular/core';
|
|
3
3
|
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
4
|
-
import { Document, Paragraph, Text, BaseKeymap, History, Editor, refocusEditorAfterCommand, positionFloatingOnce, defaultIcons, ToolbarController, defaultBubbleContexts, PluginKey, createBubbleMenuPlugin, createFloatingMenuPlugin, FloatingMenuController, positionFloating } from '@domternal/core';
|
|
4
|
+
import { Document, Paragraph, Text, BaseKeymap, History, Editor, refocusEditorAfterCommand, positionFloatingOnce, defaultIcons, ToolbarController, defaultBubbleContexts, PluginKey, createBubbleShouldShow, createBubbleMenuPlugin, buildBubbleItemMaps, resolveBubbleNames, resolveBubbleMenuItems, createFloatingMenuPlugin, FloatingMenuController, positionFloating } from '@domternal/core';
|
|
5
5
|
export { Editor } from '@domternal/core';
|
|
6
6
|
import { DomSanitizer } from '@angular/platform-browser';
|
|
7
7
|
|
|
@@ -206,8 +206,8 @@ class DomternalEditorComponent {
|
|
|
206
206
|
this.editorCreated.emit(editor);
|
|
207
207
|
});
|
|
208
208
|
}
|
|
209
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
210
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.
|
|
209
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: DomternalEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
210
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.20", type: DomternalEditorComponent, isStandalone: true, selector: "domternal-editor", inputs: { extensions: { classPropertyName: "extensions", publicName: "extensions", isSignal: true, isRequired: false, transformFunction: null }, history: { classPropertyName: "history", publicName: "history", isSignal: true, isRequired: false, transformFunction: null }, content: { classPropertyName: "content", publicName: "content", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, preset: { classPropertyName: "preset", publicName: "preset", isSignal: true, isRequired: false, transformFunction: null }, autofocus: { classPropertyName: "autofocus", publicName: "autofocus", isSignal: true, isRequired: false, transformFunction: null }, outputFormat: { classPropertyName: "outputFormat", publicName: "outputFormat", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { editorCreated: "editorCreated", contentUpdated: "contentUpdated", selectionChanged: "selectionChanged", focusChanged: "focusChanged", blurChanged: "blurChanged", editorDestroyed: "editorDestroyed" }, host: { attributes: { "data-dm-editor-ui": "" }, classAttribute: "dm-editor" }, providers: [
|
|
211
211
|
{
|
|
212
212
|
provide: NG_VALUE_ACCESSOR,
|
|
213
213
|
useExisting: forwardRef(() => DomternalEditorComponent),
|
|
@@ -215,7 +215,7 @@ class DomternalEditorComponent {
|
|
|
215
215
|
},
|
|
216
216
|
], viewQueries: [{ propertyName: "editorRef", first: true, predicate: ["editorRef"], descendants: true, isSignal: true }], ngImport: i0, template: '<div #editorRef></div>', isInline: true, styles: [":host{display:block}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
217
217
|
}
|
|
218
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
218
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: DomternalEditorComponent, decorators: [{
|
|
219
219
|
type: Component,
|
|
220
220
|
args: [{ selector: 'domternal-editor', template: '<div #editorRef></div>', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: { class: 'dm-editor', 'data-dm-editor-ui': '' }, providers: [
|
|
221
221
|
{
|
|
@@ -685,8 +685,8 @@ class DomternalToolbarComponent {
|
|
|
685
685
|
const buttons = this.elRef.nativeElement.querySelectorAll('.dm-toolbar-button');
|
|
686
686
|
buttons[idx]?.focus();
|
|
687
687
|
}
|
|
688
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
689
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
688
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: DomternalToolbarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
689
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: DomternalToolbarComponent, isStandalone: true, selector: "domternal-toolbar", inputs: { editor: { classPropertyName: "editor", publicName: "editor", isSignal: true, isRequired: true, transformFunction: null }, icons: { classPropertyName: "icons", publicName: "icons", isSignal: true, isRequired: false, transformFunction: null }, layout: { classPropertyName: "layout", publicName: "layout", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "toolbar", "data-dm-editor-ui": "" }, listeners: { "keydown": "onKeydown($event)" }, properties: { "attr.aria-label": "\"Editor formatting\"" }, classAttribute: "dm-toolbar" }, ngImport: i0, template: `
|
|
690
690
|
@for (group of groups(); track group.name; let gi = $index) {
|
|
691
691
|
@if (gi > 0) {
|
|
692
692
|
<div class="dm-toolbar-separator" role="separator"></div>
|
|
@@ -788,7 +788,7 @@ class DomternalToolbarComponent {
|
|
|
788
788
|
}
|
|
789
789
|
`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
790
790
|
}
|
|
791
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
791
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: DomternalToolbarComponent, decorators: [{
|
|
792
792
|
type: Component,
|
|
793
793
|
args: [{
|
|
794
794
|
selector: 'domternal-toolbar',
|
|
@@ -905,15 +905,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
905
905
|
}]
|
|
906
906
|
}], ctorParameters: () => [], propDecorators: { editor: [{ type: i0.Input, args: [{ isSignal: true, alias: "editor", required: true }] }], icons: [{ type: i0.Input, args: [{ isSignal: true, alias: "icons", required: false }] }], layout: [{ type: i0.Input, args: [{ isSignal: true, alias: "layout", required: false }] }] } });
|
|
907
907
|
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
const name = $pos.node(d).type.name;
|
|
912
|
-
if (name === 'tableCell' || name === 'tableHeader')
|
|
913
|
-
return true;
|
|
914
|
-
}
|
|
915
|
-
return false;
|
|
916
|
-
}
|
|
908
|
+
/* The item pipeline (which list this selection gets, what the schema allows,
|
|
909
|
+
which separators survive) is core's. This component renders the answer and
|
|
910
|
+
owns nothing about how it is reached. */
|
|
917
911
|
class DomternalBubbleMenuComponent {
|
|
918
912
|
editor = input.required(...(ngDevMode ? [{ debugName: "editor" }] : /* istanbul ignore next */ []));
|
|
919
913
|
shouldShow = input(...(ngDevMode ? [undefined, { debugName: "shouldShow" }] : /* istanbul ignore next */ []));
|
|
@@ -922,7 +916,17 @@ class DomternalBubbleMenuComponent {
|
|
|
922
916
|
updateDelay = input(0, ...(ngDevMode ? [{ debugName: "updateDelay" }] : /* istanbul ignore next */ []));
|
|
923
917
|
/** Fixed item names (e.g. ['bold', 'italic', 'code']). Omit for auto mode (all format items). */
|
|
924
918
|
items = input(undefined, ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
|
|
925
|
-
/**
|
|
919
|
+
/**
|
|
920
|
+
* Context-aware: map context names to item arrays, `true` for all valid
|
|
921
|
+
* items, or `null` to disable.
|
|
922
|
+
*
|
|
923
|
+
* Typed through core's `BubbleContexts` rather than the same shape written
|
|
924
|
+
* out here. Angular emits an input's declared type into the package types,
|
|
925
|
+
* and an inline union came out of that emit as `Record<string, true |
|
|
926
|
+
* string[]>`: the `null` that disables a context was missing from the
|
|
927
|
+
* published type, so the documented way to switch one off did not
|
|
928
|
+
* type-check. A named type survives the trip intact.
|
|
929
|
+
*/
|
|
926
930
|
contexts = input(undefined, ...(ngDevMode ? [{ debugName: "contexts" }] : /* istanbul ignore next */ []));
|
|
927
931
|
/**
|
|
928
932
|
* Custom icon overrides. Falls back to default Phosphor icons for unmapped keys.
|
|
@@ -946,6 +950,15 @@ class DomternalBubbleMenuComponent {
|
|
|
946
950
|
}
|
|
947
951
|
/** Internal - updated on transactions. Not meant to be set from outside. */
|
|
948
952
|
resolvedItems = signal([], ...(ngDevMode ? [{ debugName: "resolvedItems" }] : /* istanbul ignore next */ []));
|
|
953
|
+
/**
|
|
954
|
+
* The list core hands back, stored once. A default or fallback list comes
|
|
955
|
+
* back by reference and a signal set to its current value notifies nobody,
|
|
956
|
+
* so those passes cost nothing; a list resolved from names is rebuilt each
|
|
957
|
+
* time, as it always was.
|
|
958
|
+
*/
|
|
959
|
+
setResolvedItems(items) {
|
|
960
|
+
this.resolvedItems.set(items);
|
|
961
|
+
}
|
|
949
962
|
/** Internal - true when the BlockContextMenu extension is loaded; toggles the "..." trailing button. */
|
|
950
963
|
showBlockMenuButton = signal(false, ...(ngDevMode ? [{ debugName: "showBlockMenuButton" }] : /* istanbul ignore next */ []));
|
|
951
964
|
/** Internal - true when the selection spans more than one top-level block; the "..." trigger is disabled in that case because block-level commands have no unambiguous target. */
|
|
@@ -954,6 +967,12 @@ class DomternalBubbleMenuComponent {
|
|
|
954
967
|
isNodeSelection = signal(false, ...(ngDevMode ? [{ debugName: "isNodeSelection" }] : /* istanbul ignore next */ []));
|
|
955
968
|
/** Internal - true when the NotionColorPicker extension is loaded; toggles the "A" color trigger. */
|
|
956
969
|
showColorPickerButton = signal(false, ...(ngDevMode ? [{ debugName: "showColorPickerButton" }] : /* istanbul ignore next */ []));
|
|
970
|
+
/**
|
|
971
|
+
* The trailing triggers, resolved once for the template: each is read twice,
|
|
972
|
+
* for the button and for the separator that may lead it.
|
|
973
|
+
*/
|
|
974
|
+
showColorTrigger = computed(() => this.showColorPickerButton() && !this.isNodeSelection(), ...(ngDevMode ? [{ debugName: "showColorTrigger" }] : /* istanbul ignore next */ []));
|
|
975
|
+
showBlockTrigger = computed(() => this.showBlockMenuButton() && !this.isNodeSelection(), ...(ngDevMode ? [{ debugName: "showBlockTrigger" }] : /* istanbul ignore next */ []));
|
|
957
976
|
/** notionColorPicker extension loaded (immutable per editor). */
|
|
958
977
|
hasNotionColorPicker = false;
|
|
959
978
|
/** Current text color CSS variable expression for the trigger glyph (null = default). */
|
|
@@ -967,12 +986,15 @@ class DomternalBubbleMenuComponent {
|
|
|
967
986
|
sanitizer = inject(DomSanitizer);
|
|
968
987
|
ngZone = inject(NgZone);
|
|
969
988
|
activeVersion = signal(0, ...(ngDevMode ? [{ debugName: "activeVersion" }] : /* istanbul ignore next */ []));
|
|
970
|
-
|
|
971
|
-
|
|
989
|
+
/** Rebuilt per editor by `setupItemTracking`; empty until then. */
|
|
990
|
+
maps = {
|
|
991
|
+
itemMap: new Map(),
|
|
992
|
+
dropdownMap: new Map(),
|
|
993
|
+
bubbleDefaults: new Map(),
|
|
994
|
+
};
|
|
972
995
|
activeMap = new Map();
|
|
973
996
|
disabledMap = new Map();
|
|
974
997
|
htmlCache = new Map();
|
|
975
|
-
bubbleDefaults = new Map();
|
|
976
998
|
transactionHandler = null;
|
|
977
999
|
// Dropdown state for bubble-menu-embedded dropdowns (e.g. text-align).
|
|
978
1000
|
openDropdown = signal(null, ...(ngDevMode ? [{ debugName: "openDropdown" }] : /* istanbul ignore next */ []));
|
|
@@ -983,11 +1005,8 @@ class DomternalBubbleMenuComponent {
|
|
|
983
1005
|
dropdownCaret = '<svg class="dm-dropdown-caret" width="10" height="10" viewBox="0 0 10 10">' +
|
|
984
1006
|
'<path d="M2 4l3 3 3-3" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>';
|
|
985
1007
|
constructor() {
|
|
986
|
-
// Each component instance needs a unique plugin key so that multiple
|
|
987
|
-
//
|
|
988
|
-
// crypto.randomUUID over Math.random() for collision-free uniqueness
|
|
989
|
-
// (Math.random across two simultaneous mounts has a small but real
|
|
990
|
-
// collision probability, and SSR may share the random seed).
|
|
1008
|
+
// Each component instance needs a unique plugin key so that multiple bubble-
|
|
1009
|
+
// menus mounted in the same editor do not collide.
|
|
991
1010
|
const crypto = globalThis.crypto;
|
|
992
1011
|
const suffix = crypto?.randomUUID?.().slice(0, 8) ?? Math.random().toString(36).slice(2, 8);
|
|
993
1012
|
this.pluginKey = new PluginKey('angularBubbleMenu-' + suffix);
|
|
@@ -998,37 +1017,15 @@ class DomternalBubbleMenuComponent {
|
|
|
998
1017
|
});
|
|
999
1018
|
afterNextRender(() => {
|
|
1000
1019
|
const editor = this.editor();
|
|
1020
|
+
/* Item tracking first, and the order is load-bearing: it is what builds
|
|
1021
|
+
`this.maps`, and the visibility rule below reads that object once.
|
|
1022
|
+
With the plugin registered first the rule closed over an empty map,
|
|
1023
|
+
and `bubbleDefaults.has(...)` was false for every node selection, so
|
|
1024
|
+
an image never got a menu at all. */
|
|
1025
|
+
this.setupItemTracking(editor);
|
|
1001
1026
|
const ctxs = this.resolveContexts(editor);
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
if (ctxs) {
|
|
1005
|
-
shouldShowFn = ({ state }) => {
|
|
1006
|
-
const context = this.detectContext(state.selection, ctxs);
|
|
1007
|
-
if (!context)
|
|
1008
|
-
return false;
|
|
1009
|
-
if (context in ctxs) {
|
|
1010
|
-
const val = ctxs[context];
|
|
1011
|
-
if (val === null)
|
|
1012
|
-
return false;
|
|
1013
|
-
return val === true || (Array.isArray(val) && val.length > 0);
|
|
1014
|
-
}
|
|
1015
|
-
return this.bubbleDefaults.has(context);
|
|
1016
|
-
};
|
|
1017
|
-
}
|
|
1018
|
-
else {
|
|
1019
|
-
// Auto/items mode: show when any endpoint's parent allows marks
|
|
1020
|
-
shouldShowFn = ({ state }) => {
|
|
1021
|
-
if (state.selection.empty)
|
|
1022
|
-
return false;
|
|
1023
|
-
if (state.selection.node)
|
|
1024
|
-
return this.bubbleDefaults.has(state.selection.node.type.name);
|
|
1025
|
-
if (isInsideTableCell(state.selection.$from))
|
|
1026
|
-
return false;
|
|
1027
|
-
return state.selection.$from.parent.type.spec.marks !== ''
|
|
1028
|
-
|| state.selection.$to.parent.type.spec.marks !== '';
|
|
1029
|
-
};
|
|
1030
|
-
}
|
|
1031
|
-
}
|
|
1027
|
+
const shouldShowFn = this.shouldShow() ??
|
|
1028
|
+
createBubbleShouldShow(this.maps, ctxs);
|
|
1032
1029
|
const plugin = createBubbleMenuPlugin({
|
|
1033
1030
|
pluginKey: this.pluginKey,
|
|
1034
1031
|
editor,
|
|
@@ -1039,7 +1036,6 @@ class DomternalBubbleMenuComponent {
|
|
|
1039
1036
|
updateDelay: this.updateDelay(),
|
|
1040
1037
|
});
|
|
1041
1038
|
editor.registerPlugin(plugin);
|
|
1042
|
-
this.setupItemTracking(editor);
|
|
1043
1039
|
});
|
|
1044
1040
|
}
|
|
1045
1041
|
ngOnDestroy() {
|
|
@@ -1114,170 +1110,29 @@ class DomternalBubbleMenuComponent {
|
|
|
1114
1110
|
}));
|
|
1115
1111
|
}
|
|
1116
1112
|
// === Internal ===
|
|
1117
|
-
buildItemMap(editor) {
|
|
1118
|
-
this.itemMap.clear();
|
|
1119
|
-
this.dropdownMap.clear();
|
|
1120
|
-
for (const item of editor.toolbarItems) {
|
|
1121
|
-
if (item.type === 'button') {
|
|
1122
|
-
this.itemMap.set(item.name, item);
|
|
1123
|
-
}
|
|
1124
|
-
else if (item.type === 'dropdown') {
|
|
1125
|
-
this.dropdownMap.set(item.name, item);
|
|
1126
|
-
for (const sub of item.items) {
|
|
1127
|
-
this.itemMap.set(sub.name, sub);
|
|
1128
|
-
}
|
|
1129
|
-
}
|
|
1130
|
-
}
|
|
1131
|
-
}
|
|
1132
|
-
resolveNames(names) {
|
|
1133
|
-
const result = [];
|
|
1134
|
-
let sepIdx = 0;
|
|
1135
|
-
for (const name of names) {
|
|
1136
|
-
if (name === '|') {
|
|
1137
|
-
result.push({ type: 'separator', name: `sep-${String(sepIdx++)}` });
|
|
1138
|
-
}
|
|
1139
|
-
else {
|
|
1140
|
-
const dropdown = this.dropdownMap.get(name);
|
|
1141
|
-
if (dropdown) {
|
|
1142
|
-
result.push(dropdown);
|
|
1143
|
-
continue;
|
|
1144
|
-
}
|
|
1145
|
-
const item = this.itemMap.get(name);
|
|
1146
|
-
if (item)
|
|
1147
|
-
result.push(item);
|
|
1148
|
-
}
|
|
1149
|
-
}
|
|
1150
|
-
return result;
|
|
1151
|
-
}
|
|
1152
|
-
getFormatItems() {
|
|
1153
|
-
return Array.from(this.itemMap.values())
|
|
1154
|
-
.filter(item => item.group === 'format')
|
|
1155
|
-
.sort((a, b) => (b.priority ?? 100) - (a.priority ?? 100));
|
|
1156
|
-
}
|
|
1157
|
-
detectContext(selection, ctxs) {
|
|
1158
|
-
// CellSelection (duck-type: has $anchorCell) - never show bubble menu
|
|
1159
|
-
if ('$anchorCell' in selection)
|
|
1160
|
-
return null;
|
|
1161
|
-
if (selection.node)
|
|
1162
|
-
return selection.node.type.name;
|
|
1163
|
-
if (selection.empty)
|
|
1164
|
-
return null;
|
|
1165
|
-
// TextSelection inside a table cell → 'table' context.
|
|
1166
|
-
// Cross-cell TextSelection (drag across cells) → hide bubble menu.
|
|
1167
|
-
const fromCell = this.findCellNode(selection.$from);
|
|
1168
|
-
if (fromCell) {
|
|
1169
|
-
const toCell = this.findCellNode(selection.$to);
|
|
1170
|
-
if (toCell && fromCell !== toCell)
|
|
1171
|
-
return null;
|
|
1172
|
-
return 'table';
|
|
1173
|
-
}
|
|
1174
|
-
const fromName = selection.$from.parent.type.name;
|
|
1175
|
-
if (fromName in ctxs)
|
|
1176
|
-
return fromName;
|
|
1177
|
-
if ('text' in ctxs && selection.$from.parent.type.spec.marks !== '')
|
|
1178
|
-
return 'text';
|
|
1179
|
-
// Cross-block: also check $to so bold/italic is offered when any endpoint allows marks
|
|
1180
|
-
const toName = selection.$to.parent.type.name;
|
|
1181
|
-
if (toName in ctxs)
|
|
1182
|
-
return toName;
|
|
1183
|
-
if ('text' in ctxs && selection.$to.parent.type.spec.marks !== '')
|
|
1184
|
-
return 'text';
|
|
1185
|
-
return null;
|
|
1186
|
-
}
|
|
1187
|
-
findCellNode(pos) {
|
|
1188
|
-
for (let d = pos.depth; d > 0; d--) {
|
|
1189
|
-
const node = pos.node(d);
|
|
1190
|
-
if (node.type.name === 'tableCell' || node.type.name === 'tableHeader') {
|
|
1191
|
-
return node;
|
|
1192
|
-
}
|
|
1193
|
-
}
|
|
1194
|
-
return null;
|
|
1195
|
-
}
|
|
1196
|
-
/** Filters out mark items that the context node type doesn't allow (e.g. bold on codeBlock) */
|
|
1197
|
-
filterBySchema(editor, contextName, items) {
|
|
1198
|
-
if (contextName === 'text' || contextName === 'table')
|
|
1199
|
-
return items;
|
|
1200
|
-
const schema = editor.state.schema;
|
|
1201
|
-
if (!schema)
|
|
1202
|
-
return items;
|
|
1203
|
-
const nodeType = schema.nodes[contextName];
|
|
1204
|
-
if (!nodeType)
|
|
1205
|
-
return items;
|
|
1206
|
-
return items.filter(item => {
|
|
1207
|
-
const markName = typeof item.isActive === 'string' ? item.isActive : null;
|
|
1208
|
-
if (!markName)
|
|
1209
|
-
return true;
|
|
1210
|
-
const markType = schema.marks[markName];
|
|
1211
|
-
if (!markType)
|
|
1212
|
-
return true;
|
|
1213
|
-
return nodeType.allowsMarkType(markType);
|
|
1214
|
-
});
|
|
1215
|
-
}
|
|
1216
|
-
buildBubbleDefaults(editor) {
|
|
1217
|
-
this.bubbleDefaults.clear();
|
|
1218
|
-
const byCtx = new Map();
|
|
1219
|
-
const addItem = (btn) => {
|
|
1220
|
-
const ctx = btn['bubbleMenu'];
|
|
1221
|
-
if (!ctx)
|
|
1222
|
-
return;
|
|
1223
|
-
let arr = byCtx.get(ctx);
|
|
1224
|
-
if (!arr) {
|
|
1225
|
-
arr = [];
|
|
1226
|
-
byCtx.set(ctx, arr);
|
|
1227
|
-
}
|
|
1228
|
-
arr.push(btn);
|
|
1229
|
-
};
|
|
1230
|
-
for (const item of editor.toolbarItems) {
|
|
1231
|
-
if (item.type === 'button')
|
|
1232
|
-
addItem(item);
|
|
1233
|
-
else if (item.type === 'dropdown') {
|
|
1234
|
-
for (const sub of item.items)
|
|
1235
|
-
addItem(sub);
|
|
1236
|
-
}
|
|
1237
|
-
}
|
|
1238
|
-
for (const [ctx, items] of byCtx) {
|
|
1239
|
-
items.sort((a, b) => (b.priority ?? 100) - (a.priority ?? 100));
|
|
1240
|
-
const result = [];
|
|
1241
|
-
let lastGroup;
|
|
1242
|
-
let sepIdx = 0;
|
|
1243
|
-
for (const item of items) {
|
|
1244
|
-
if (lastGroup !== undefined && item.group !== lastGroup) {
|
|
1245
|
-
result.push({ type: 'separator', name: `bsep-${String(sepIdx++)}` });
|
|
1246
|
-
}
|
|
1247
|
-
result.push(item);
|
|
1248
|
-
lastGroup = item.group;
|
|
1249
|
-
}
|
|
1250
|
-
this.bubbleDefaults.set(ctx, result);
|
|
1251
|
-
}
|
|
1252
|
-
}
|
|
1253
1113
|
setupItemTracking(editor) {
|
|
1254
|
-
|
|
1255
|
-
|
|
1114
|
+
/* One pipeline, built once per editor. Every renderer asks core the same
|
|
1115
|
+
question, so none of them answers it. */
|
|
1116
|
+
this.maps = buildBubbleItemMaps(editor);
|
|
1256
1117
|
this.showBlockMenuButton.set(editor.extensionManager.extensions.some((e) => e.name === 'blockContextMenu'));
|
|
1257
1118
|
// Presence cached; the show decision needs a live listener (per sync below).
|
|
1258
1119
|
this.hasNotionColorPicker =
|
|
1259
1120
|
editor.extensionManager.extensions.some((e) => e.name === 'notionColorPicker');
|
|
1260
|
-
const
|
|
1261
|
-
const
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1121
|
+
const fallbackItems = resolveBubbleNames(this.items() ?? ['bold', 'italic', 'underline'], this.maps.itemMap, this.maps.dropdownMap);
|
|
1122
|
+
const refreshItems = () => {
|
|
1123
|
+
this.setResolvedItems(resolveBubbleMenuItems({
|
|
1124
|
+
editor,
|
|
1125
|
+
maps: this.maps,
|
|
1126
|
+
contexts: this.resolveContexts(editor),
|
|
1127
|
+
fallbackItems,
|
|
1128
|
+
}));
|
|
1129
|
+
};
|
|
1130
|
+
refreshItems();
|
|
1268
1131
|
this.transactionHandler = () => {
|
|
1269
1132
|
this.ngZone.run(() => {
|
|
1270
1133
|
const sel = editor.state.selection;
|
|
1271
1134
|
this.isNodeSelection.set(!!sel.node);
|
|
1272
|
-
|
|
1273
|
-
this.updateContextItems(editor);
|
|
1274
|
-
}
|
|
1275
|
-
else if (sel.node && this.bubbleDefaults.has(sel.node.type.name)) {
|
|
1276
|
-
this.resolvedItems.set(this.bubbleDefaults.get(sel.node.type.name) ?? []);
|
|
1277
|
-
}
|
|
1278
|
-
else {
|
|
1279
|
-
this.resolvedItems.set(defaultItems);
|
|
1280
|
-
}
|
|
1135
|
+
refreshItems();
|
|
1281
1136
|
this.updateStates(editor);
|
|
1282
1137
|
this.syncTrailingButtonsState(editor);
|
|
1283
1138
|
this.activeVersion.update((v) => v + 1);
|
|
@@ -1332,35 +1187,6 @@ class DomternalBubbleMenuComponent {
|
|
|
1332
1187
|
this.currentBgColorVar.set(bgToken ? `var(--dm-block-bg-${bgToken})` : null);
|
|
1333
1188
|
this.hasAnyColor.set(textToken !== null || bgToken !== null);
|
|
1334
1189
|
}
|
|
1335
|
-
updateContextItems(editor) {
|
|
1336
|
-
const ctxs = this.resolveContexts(editor);
|
|
1337
|
-
if (!ctxs)
|
|
1338
|
-
return;
|
|
1339
|
-
const ctx = this.detectContext(editor.state.selection, ctxs);
|
|
1340
|
-
if (!ctx) {
|
|
1341
|
-
this.resolvedItems.set([]);
|
|
1342
|
-
return;
|
|
1343
|
-
}
|
|
1344
|
-
if (ctx in ctxs) {
|
|
1345
|
-
const val = ctxs[ctx];
|
|
1346
|
-
if (val === null || (Array.isArray(val) && val.length === 0)) {
|
|
1347
|
-
this.resolvedItems.set([]);
|
|
1348
|
-
return;
|
|
1349
|
-
}
|
|
1350
|
-
if (val === true) {
|
|
1351
|
-
this.resolvedItems.set(this.filterBySchema(editor, ctx, this.getFormatItems()));
|
|
1352
|
-
}
|
|
1353
|
-
else if (Array.isArray(val)) {
|
|
1354
|
-
const resolved = this.resolveNames(val);
|
|
1355
|
-
const buttons = resolved.filter((i) => i.type !== 'separator');
|
|
1356
|
-
const filtered = new Set(this.filterBySchema(editor, ctx, buttons).map(b => b.name));
|
|
1357
|
-
this.resolvedItems.set(resolved.filter(i => i.type === 'separator' || filtered.has(i.name)));
|
|
1358
|
-
}
|
|
1359
|
-
}
|
|
1360
|
-
else {
|
|
1361
|
-
this.resolvedItems.set(this.bubbleDefaults.get(ctx) ?? []);
|
|
1362
|
-
}
|
|
1363
|
-
}
|
|
1364
1190
|
updateStates(editor) {
|
|
1365
1191
|
let canProxy = null;
|
|
1366
1192
|
try {
|
|
@@ -1440,21 +1266,13 @@ class DomternalBubbleMenuComponent {
|
|
|
1440
1266
|
if (!isOpening)
|
|
1441
1267
|
return;
|
|
1442
1268
|
this.openDropdown.set(dropdown.name);
|
|
1443
|
-
// NOTE: we deliberately do NOT broadcast `dm:dismiss-overlays` here.
|
|
1444
|
-
//
|
|
1445
|
-
//
|
|
1446
|
-
// Other overlays (color picker, link popover) close themselves via
|
|
1447
|
-
// their own click-outside handlers when the user clicks our trigger,
|
|
1448
|
-
// so cooperative dismissal still works.
|
|
1269
|
+
// NOTE: we deliberately do NOT broadcast `dm:dismiss-overlays` here. The
|
|
1270
|
+
// bubble menu plugin in core listens for that event and would hide itself -
|
|
1271
|
+
// taking our just-opened dropdown's trigger with it.
|
|
1449
1272
|
const editorElBroadcast = this.menuEl().nativeElement.closest('.dm-editor');
|
|
1450
|
-
// Position the panel against the trigger after Angular renders it.
|
|
1451
|
-
//
|
|
1452
|
-
//
|
|
1453
|
-
// / --dm-button-active-color). Reparenting into `.dm-editor` was
|
|
1454
|
-
// tempting for consistency with the color picker, but that dropped
|
|
1455
|
-
// the active-state colors because those tokens aren't defined at
|
|
1456
|
-
// editor scope. floating-ui handles cross-element positioning fine
|
|
1457
|
-
// without reparenting.
|
|
1273
|
+
// Position the panel against the trigger after Angular renders it. We
|
|
1274
|
+
// deliberately keep the panel INSIDE `.dm-bubble-menu` so it inherits the
|
|
1275
|
+
// bubble-menu-scoped button tokens (--dm-button-active-bg / --dm-button-
|
|
1458
1276
|
requestAnimationFrame(() => {
|
|
1459
1277
|
const trigger = event?.currentTarget
|
|
1460
1278
|
?? this.menuEl().nativeElement.querySelector(`[data-dropdown="${dropdown.name}"]`);
|
|
@@ -1523,8 +1341,8 @@ class DomternalBubbleMenuComponent {
|
|
|
1523
1341
|
this.dropdownCleanupFloating = null;
|
|
1524
1342
|
this.openDropdown.set(null);
|
|
1525
1343
|
}
|
|
1526
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1527
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
1344
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: DomternalBubbleMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1345
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: DomternalBubbleMenuComponent, isStandalone: true, selector: "domternal-bubble-menu", inputs: { editor: { classPropertyName: "editor", publicName: "editor", isSignal: true, isRequired: true, transformFunction: null }, shouldShow: { classPropertyName: "shouldShow", publicName: "shouldShow", isSignal: true, isRequired: false, transformFunction: null }, placement: { classPropertyName: "placement", publicName: "placement", isSignal: true, isRequired: false, transformFunction: null }, offset: { classPropertyName: "offset", publicName: "offset", isSignal: true, isRequired: false, transformFunction: null }, updateDelay: { classPropertyName: "updateDelay", publicName: "updateDelay", isSignal: true, isRequired: false, transformFunction: null }, items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, contexts: { classPropertyName: "contexts", publicName: "contexts", isSignal: true, isRequired: false, transformFunction: null }, icons: { classPropertyName: "icons", publicName: "icons", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "menuEl", first: true, predicate: ["menuEl"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
1528
1346
|
<div #menuEl class="dm-bubble-menu" role="toolbar" aria-label="Text formatting">
|
|
1529
1347
|
@for (item of resolvedItems(); track item.name) {
|
|
1530
1348
|
@if (item.type === 'separator') {
|
|
@@ -1571,8 +1389,10 @@ class DomternalBubbleMenuComponent {
|
|
|
1571
1389
|
(click)="executeCommand(asButton(item), $event)"></button>
|
|
1572
1390
|
}
|
|
1573
1391
|
}
|
|
1574
|
-
@if (
|
|
1575
|
-
|
|
1392
|
+
@if (showColorTrigger()) {
|
|
1393
|
+
@if (resolvedItems().length > 0) {
|
|
1394
|
+
<span class="dm-toolbar-separator" role="separator"></span>
|
|
1395
|
+
}
|
|
1576
1396
|
<button #colorBtn type="button" class="dm-toolbar-button dm-ncp-trigger"
|
|
1577
1397
|
[class.dm-toolbar-button--active]="hasAnyColor()"
|
|
1578
1398
|
title="Text and background color"
|
|
@@ -1586,8 +1406,10 @@ class DomternalBubbleMenuComponent {
|
|
|
1586
1406
|
[style.background-color]="currentBgColorVar()"></span>
|
|
1587
1407
|
</button>
|
|
1588
1408
|
}
|
|
1589
|
-
@if (
|
|
1590
|
-
|
|
1409
|
+
@if (showBlockTrigger()) {
|
|
1410
|
+
@if (resolvedItems().length > 0 || showColorTrigger()) {
|
|
1411
|
+
<span class="dm-toolbar-separator" role="separator"></span>
|
|
1412
|
+
}
|
|
1591
1413
|
<button #blockMenuBtn type="button" class="dm-toolbar-button"
|
|
1592
1414
|
[disabled]="blockMenuButtonDisabled()"
|
|
1593
1415
|
[title]="blockMenuButtonDisabled() ? 'Block actions (select within a single block)' : 'More options'"
|
|
@@ -1601,7 +1423,7 @@ class DomternalBubbleMenuComponent {
|
|
|
1601
1423
|
</div>
|
|
1602
1424
|
`, isInline: true, styles: [":host{display:contents}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
1603
1425
|
}
|
|
1604
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1426
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: DomternalBubbleMenuComponent, decorators: [{
|
|
1605
1427
|
type: Component,
|
|
1606
1428
|
args: [{ selector: 'domternal-bubble-menu', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: `
|
|
1607
1429
|
<div #menuEl class="dm-bubble-menu" role="toolbar" aria-label="Text formatting">
|
|
@@ -1650,8 +1472,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
1650
1472
|
(click)="executeCommand(asButton(item), $event)"></button>
|
|
1651
1473
|
}
|
|
1652
1474
|
}
|
|
1653
|
-
@if (
|
|
1654
|
-
|
|
1475
|
+
@if (showColorTrigger()) {
|
|
1476
|
+
@if (resolvedItems().length > 0) {
|
|
1477
|
+
<span class="dm-toolbar-separator" role="separator"></span>
|
|
1478
|
+
}
|
|
1655
1479
|
<button #colorBtn type="button" class="dm-toolbar-button dm-ncp-trigger"
|
|
1656
1480
|
[class.dm-toolbar-button--active]="hasAnyColor()"
|
|
1657
1481
|
title="Text and background color"
|
|
@@ -1665,8 +1489,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
1665
1489
|
[style.background-color]="currentBgColorVar()"></span>
|
|
1666
1490
|
</button>
|
|
1667
1491
|
}
|
|
1668
|
-
@if (
|
|
1669
|
-
|
|
1492
|
+
@if (showBlockTrigger()) {
|
|
1493
|
+
@if (resolvedItems().length > 0 || showColorTrigger()) {
|
|
1494
|
+
<span class="dm-toolbar-separator" role="separator"></span>
|
|
1495
|
+
}
|
|
1670
1496
|
<button #blockMenuBtn type="button" class="dm-toolbar-button"
|
|
1671
1497
|
[disabled]="blockMenuButtonDisabled()"
|
|
1672
1498
|
[title]="blockMenuButtonDisabled() ? 'Block actions (select within a single block)' : 'More options'"
|
|
@@ -1857,8 +1683,8 @@ class DomternalFloatingMenuComponent {
|
|
|
1857
1683
|
return;
|
|
1858
1684
|
}
|
|
1859
1685
|
}
|
|
1860
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1861
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
1686
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: DomternalFloatingMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1687
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: DomternalFloatingMenuComponent, isStandalone: true, selector: "domternal-floating-menu", inputs: { editor: { classPropertyName: "editor", publicName: "editor", isSignal: true, isRequired: true, transformFunction: null }, shouldShow: { classPropertyName: "shouldShow", publicName: "shouldShow", isSignal: true, isRequired: false, transformFunction: null }, offset: { classPropertyName: "offset", publicName: "offset", isSignal: true, isRequired: false, transformFunction: null }, items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, keymap: { classPropertyName: "keymap", publicName: "keymap", isSignal: true, isRequired: false, transformFunction: null }, icons: { classPropertyName: "icons", publicName: "icons", isSignal: true, isRequired: false, transformFunction: null }, requireExplicitTrigger: { classPropertyName: "requireExplicitTrigger", publicName: "requireExplicitTrigger", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "menuEl", first: true, predicate: ["menuEl"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
1862
1688
|
<div
|
|
1863
1689
|
#menuEl
|
|
1864
1690
|
class="dm-floating-menu"
|
|
@@ -1904,7 +1730,7 @@ class DomternalFloatingMenuComponent {
|
|
|
1904
1730
|
</div>
|
|
1905
1731
|
`, isInline: true, styles: [":host{display:contents}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
1906
1732
|
}
|
|
1907
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1733
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: DomternalFloatingMenuComponent, decorators: [{
|
|
1908
1734
|
type: Component,
|
|
1909
1735
|
args: [{ selector: 'domternal-floating-menu', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: `
|
|
1910
1736
|
<div
|
|
@@ -1959,10 +1785,10 @@ const CATEGORY_ICONS = {
|
|
|
1959
1785
|
'Animals & Nature': '\u{1F431}',
|
|
1960
1786
|
'Food & Drink': '\u{1F355}',
|
|
1961
1787
|
'Travel & Places': '\u{1F697}',
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1788
|
+
Activities: '\u{26BD}',
|
|
1789
|
+
Objects: '\u{1F4A1}',
|
|
1790
|
+
Symbols: '\u{1F523}',
|
|
1791
|
+
Flags: '\u{1F3C1}',
|
|
1966
1792
|
};
|
|
1967
1793
|
class DomternalEmojiPickerComponent {
|
|
1968
1794
|
editor = input.required(...(ngDevMode ? [{ debugName: "editor" }] : /* istanbul ignore next */ []));
|
|
@@ -1999,8 +1825,7 @@ class DomternalEmojiPickerComponent {
|
|
|
1999
1825
|
if (searchFn) {
|
|
2000
1826
|
return searchFn(query);
|
|
2001
1827
|
}
|
|
2002
|
-
return this.emojis().filter((item) => item.name.includes(query) ||
|
|
2003
|
-
item.group.toLowerCase().includes(query));
|
|
1828
|
+
return this.emojis().filter((item) => item.name.includes(query) || item.group.toLowerCase().includes(query));
|
|
2004
1829
|
}, ...(ngDevMode ? [{ debugName: "filteredEmojis" }] : /* istanbul ignore next */ []));
|
|
2005
1830
|
frequentlyUsed = computed(() => {
|
|
2006
1831
|
// Re-evaluate when panel opens (isOpen changes)
|
|
@@ -2017,12 +1842,17 @@ class DomternalEmojiPickerComponent {
|
|
|
2017
1842
|
const nameMap = storage['_nameMap'];
|
|
2018
1843
|
if (!nameMap)
|
|
2019
1844
|
return [];
|
|
2020
|
-
return names
|
|
1845
|
+
return names
|
|
1846
|
+
.slice(0, 16)
|
|
1847
|
+
.map((n) => nameMap.get(n))
|
|
1848
|
+
.filter(Boolean);
|
|
2021
1849
|
}, ...(ngDevMode ? [{ debugName: "frequentlyUsed" }] : /* istanbul ignore next */ []));
|
|
2022
1850
|
constructor() {
|
|
2023
1851
|
effect(() => {
|
|
2024
1852
|
const editor = this.editor();
|
|
2025
|
-
untracked(() => {
|
|
1853
|
+
untracked(() => {
|
|
1854
|
+
this.setupEventListener(editor);
|
|
1855
|
+
});
|
|
2026
1856
|
});
|
|
2027
1857
|
}
|
|
2028
1858
|
ngOnDestroy() {
|
|
@@ -2064,7 +1894,8 @@ class DomternalEmojiPickerComponent {
|
|
|
2064
1894
|
// Focus first emoji swatch after scroll completes
|
|
2065
1895
|
setTimeout(() => {
|
|
2066
1896
|
const firstSwatch = label.nextElementSibling;
|
|
2067
|
-
if (firstSwatch instanceof HTMLElement &&
|
|
1897
|
+
if (firstSwatch instanceof HTMLElement &&
|
|
1898
|
+
firstSwatch.classList.contains('dm-emoji-swatch')) {
|
|
2068
1899
|
firstSwatch.focus();
|
|
2069
1900
|
}
|
|
2070
1901
|
}, 50);
|
|
@@ -2089,7 +1920,7 @@ class DomternalEmojiPickerComponent {
|
|
|
2089
1920
|
return;
|
|
2090
1921
|
}
|
|
2091
1922
|
const cols = 8;
|
|
2092
|
-
let next
|
|
1923
|
+
let next;
|
|
2093
1924
|
switch (event.key) {
|
|
2094
1925
|
case 'ArrowRight':
|
|
2095
1926
|
event.preventDefault();
|
|
@@ -2112,7 +1943,8 @@ class DomternalEmojiPickerComponent {
|
|
|
2112
1943
|
event.preventDefault();
|
|
2113
1944
|
swatches[idx]?.click();
|
|
2114
1945
|
return;
|
|
2115
|
-
default:
|
|
1946
|
+
default:
|
|
1947
|
+
return;
|
|
2116
1948
|
}
|
|
2117
1949
|
swatches[next]?.focus();
|
|
2118
1950
|
}
|
|
@@ -2184,14 +2016,18 @@ class DomternalEmojiPickerComponent {
|
|
|
2184
2016
|
!this.elRef.nativeElement.contains(target) &&
|
|
2185
2017
|
target !== this.anchorEl &&
|
|
2186
2018
|
!this.anchorEl?.contains(target)) {
|
|
2187
|
-
this.ngZone.run(() => {
|
|
2019
|
+
this.ngZone.run(() => {
|
|
2020
|
+
this.close();
|
|
2021
|
+
});
|
|
2188
2022
|
}
|
|
2189
2023
|
};
|
|
2190
2024
|
document.addEventListener('mousedown', this.clickOutsideHandler);
|
|
2191
2025
|
this.keydownHandler = (e) => {
|
|
2192
2026
|
if (e.key === 'Escape' && this.isOpen()) {
|
|
2193
2027
|
e.preventDefault();
|
|
2194
|
-
this.ngZone.run(() => {
|
|
2028
|
+
this.ngZone.run(() => {
|
|
2029
|
+
this.close();
|
|
2030
|
+
});
|
|
2195
2031
|
}
|
|
2196
2032
|
};
|
|
2197
2033
|
document.addEventListener('keydown', this.keydownHandler);
|
|
@@ -2227,8 +2063,8 @@ class DomternalEmojiPickerComponent {
|
|
|
2227
2063
|
this.eventHandler = null;
|
|
2228
2064
|
}
|
|
2229
2065
|
}
|
|
2230
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
2231
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
2066
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: DomternalEmojiPickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2067
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: DomternalEmojiPickerComponent, isStandalone: true, selector: "domternal-emoji-picker", inputs: { editor: { classPropertyName: "editor", publicName: "editor", isSignal: true, isRequired: true, transformFunction: null }, emojis: { classPropertyName: "emojis", publicName: "emojis", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "dm-emoji-picker-host" }, ngImport: i0, template: `
|
|
2232
2068
|
@if (isOpen()) {
|
|
2233
2069
|
<div class="dm-emoji-picker">
|
|
2234
2070
|
<div class="dm-emoji-picker-search">
|
|
@@ -2255,11 +2091,18 @@ class DomternalEmojiPickerComponent {
|
|
|
2255
2091
|
[attr.aria-label]="cat"
|
|
2256
2092
|
(mousedown)="$event.preventDefault()"
|
|
2257
2093
|
(click)="scrollToCategory(cat)"
|
|
2258
|
-
>
|
|
2094
|
+
>
|
|
2095
|
+
{{ categoryIcon(cat) }}
|
|
2096
|
+
</button>
|
|
2259
2097
|
}
|
|
2260
2098
|
</div>
|
|
2261
2099
|
|
|
2262
|
-
<div
|
|
2100
|
+
<div
|
|
2101
|
+
class="dm-emoji-picker-grid"
|
|
2102
|
+
#grid
|
|
2103
|
+
(scroll)="onGridScroll()"
|
|
2104
|
+
(keydown)="onGridKeydown($event)"
|
|
2105
|
+
>
|
|
2263
2106
|
@if (searchQuery()) {
|
|
2264
2107
|
@for (item of filteredEmojis(); track item.name) {
|
|
2265
2108
|
<button
|
|
@@ -2270,9 +2113,10 @@ class DomternalEmojiPickerComponent {
|
|
|
2270
2113
|
[attr.aria-label]="formatName(item.name)"
|
|
2271
2114
|
(mousedown)="$event.preventDefault()"
|
|
2272
2115
|
(click)="selectEmoji(item)"
|
|
2273
|
-
>
|
|
2274
|
-
|
|
2275
|
-
|
|
2116
|
+
>
|
|
2117
|
+
{{ item.emoji }}
|
|
2118
|
+
</button>
|
|
2119
|
+
} @empty {
|
|
2276
2120
|
<div class="dm-emoji-picker-empty">No emoji found</div>
|
|
2277
2121
|
}
|
|
2278
2122
|
} @else {
|
|
@@ -2287,7 +2131,9 @@ class DomternalEmojiPickerComponent {
|
|
|
2287
2131
|
[attr.aria-label]="formatName(item.name)"
|
|
2288
2132
|
(mousedown)="$event.preventDefault()"
|
|
2289
2133
|
(click)="selectEmoji(item)"
|
|
2290
|
-
>
|
|
2134
|
+
>
|
|
2135
|
+
{{ item.emoji }}
|
|
2136
|
+
</button>
|
|
2291
2137
|
}
|
|
2292
2138
|
}
|
|
2293
2139
|
@for (cat of categoryNames(); track cat) {
|
|
@@ -2301,7 +2147,9 @@ class DomternalEmojiPickerComponent {
|
|
|
2301
2147
|
[attr.aria-label]="formatName(item.name)"
|
|
2302
2148
|
(mousedown)="$event.preventDefault()"
|
|
2303
2149
|
(click)="selectEmoji(item)"
|
|
2304
|
-
>
|
|
2150
|
+
>
|
|
2151
|
+
{{ item.emoji }}
|
|
2152
|
+
</button>
|
|
2305
2153
|
}
|
|
2306
2154
|
}
|
|
2307
2155
|
}
|
|
@@ -2310,13 +2158,13 @@ class DomternalEmojiPickerComponent {
|
|
|
2310
2158
|
}
|
|
2311
2159
|
`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
2312
2160
|
}
|
|
2313
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
2161
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: DomternalEmojiPickerComponent, decorators: [{
|
|
2314
2162
|
type: Component,
|
|
2315
2163
|
args: [{
|
|
2316
2164
|
selector: 'domternal-emoji-picker',
|
|
2317
2165
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
2318
2166
|
encapsulation: ViewEncapsulation.None,
|
|
2319
|
-
host: {
|
|
2167
|
+
host: { class: 'dm-emoji-picker-host' },
|
|
2320
2168
|
template: `
|
|
2321
2169
|
@if (isOpen()) {
|
|
2322
2170
|
<div class="dm-emoji-picker">
|
|
@@ -2344,11 +2192,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
2344
2192
|
[attr.aria-label]="cat"
|
|
2345
2193
|
(mousedown)="$event.preventDefault()"
|
|
2346
2194
|
(click)="scrollToCategory(cat)"
|
|
2347
|
-
>
|
|
2195
|
+
>
|
|
2196
|
+
{{ categoryIcon(cat) }}
|
|
2197
|
+
</button>
|
|
2348
2198
|
}
|
|
2349
2199
|
</div>
|
|
2350
2200
|
|
|
2351
|
-
<div
|
|
2201
|
+
<div
|
|
2202
|
+
class="dm-emoji-picker-grid"
|
|
2203
|
+
#grid
|
|
2204
|
+
(scroll)="onGridScroll()"
|
|
2205
|
+
(keydown)="onGridKeydown($event)"
|
|
2206
|
+
>
|
|
2352
2207
|
@if (searchQuery()) {
|
|
2353
2208
|
@for (item of filteredEmojis(); track item.name) {
|
|
2354
2209
|
<button
|
|
@@ -2359,9 +2214,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
2359
2214
|
[attr.aria-label]="formatName(item.name)"
|
|
2360
2215
|
(mousedown)="$event.preventDefault()"
|
|
2361
2216
|
(click)="selectEmoji(item)"
|
|
2362
|
-
>
|
|
2363
|
-
|
|
2364
|
-
|
|
2217
|
+
>
|
|
2218
|
+
{{ item.emoji }}
|
|
2219
|
+
</button>
|
|
2220
|
+
} @empty {
|
|
2365
2221
|
<div class="dm-emoji-picker-empty">No emoji found</div>
|
|
2366
2222
|
}
|
|
2367
2223
|
} @else {
|
|
@@ -2376,7 +2232,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
2376
2232
|
[attr.aria-label]="formatName(item.name)"
|
|
2377
2233
|
(mousedown)="$event.preventDefault()"
|
|
2378
2234
|
(click)="selectEmoji(item)"
|
|
2379
|
-
>
|
|
2235
|
+
>
|
|
2236
|
+
{{ item.emoji }}
|
|
2237
|
+
</button>
|
|
2380
2238
|
}
|
|
2381
2239
|
}
|
|
2382
2240
|
@for (cat of categoryNames(); track cat) {
|
|
@@ -2390,7 +2248,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
2390
2248
|
[attr.aria-label]="formatName(item.name)"
|
|
2391
2249
|
(mousedown)="$event.preventDefault()"
|
|
2392
2250
|
(click)="selectEmoji(item)"
|
|
2393
|
-
>
|
|
2251
|
+
>
|
|
2252
|
+
{{ item.emoji }}
|
|
2253
|
+
</button>
|
|
2394
2254
|
}
|
|
2395
2255
|
}
|
|
2396
2256
|
}
|
|
@@ -2401,6 +2261,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
2401
2261
|
}]
|
|
2402
2262
|
}], ctorParameters: () => [], propDecorators: { editor: [{ type: i0.Input, args: [{ isSignal: true, alias: "editor", required: true }] }], emojis: [{ type: i0.Input, args: [{ isSignal: true, alias: "emojis", required: true }] }] } });
|
|
2403
2263
|
|
|
2264
|
+
function paletteFromExtensionOptions(options) {
|
|
2265
|
+
if (typeof options !== 'object' || options === null || !('palette' in options))
|
|
2266
|
+
return [];
|
|
2267
|
+
const palette = options.palette;
|
|
2268
|
+
if (!Array.isArray(palette) || !palette.every((token) => typeof token === 'string')) {
|
|
2269
|
+
return [];
|
|
2270
|
+
}
|
|
2271
|
+
return [...palette];
|
|
2272
|
+
}
|
|
2404
2273
|
/**
|
|
2405
2274
|
* Display labels for the named-token palette. Used in tooltips / aria labels;
|
|
2406
2275
|
* unknown tokens fall back to a title-cased version of the raw key.
|
|
@@ -2438,7 +2307,9 @@ class DomternalNotionColorPickerComponent {
|
|
|
2438
2307
|
constructor() {
|
|
2439
2308
|
effect(() => {
|
|
2440
2309
|
const editor = this.editor();
|
|
2441
|
-
untracked(() => {
|
|
2310
|
+
untracked(() => {
|
|
2311
|
+
this.setupEventListener(editor);
|
|
2312
|
+
});
|
|
2442
2313
|
});
|
|
2443
2314
|
}
|
|
2444
2315
|
ngOnDestroy() {
|
|
@@ -2478,7 +2349,7 @@ class DomternalNotionColorPickerComponent {
|
|
|
2478
2349
|
const idx = active ? swatches.indexOf(active) : -1;
|
|
2479
2350
|
if (idx === -1)
|
|
2480
2351
|
return;
|
|
2481
|
-
let next
|
|
2352
|
+
let next;
|
|
2482
2353
|
switch (event.key) {
|
|
2483
2354
|
case 'ArrowRight':
|
|
2484
2355
|
event.preventDefault();
|
|
@@ -2600,13 +2471,17 @@ class DomternalNotionColorPickerComponent {
|
|
|
2600
2471
|
return;
|
|
2601
2472
|
if (this.anchorEl?.contains(target))
|
|
2602
2473
|
return;
|
|
2603
|
-
this.ngZone.run(() => {
|
|
2474
|
+
this.ngZone.run(() => {
|
|
2475
|
+
this.close({ refocus: false });
|
|
2476
|
+
});
|
|
2604
2477
|
};
|
|
2605
2478
|
document.addEventListener('mousedown', this.clickOutsideHandler);
|
|
2606
2479
|
this.keydownHandler = (e) => {
|
|
2607
2480
|
if (e.key === 'Escape' && this.isOpen()) {
|
|
2608
2481
|
e.preventDefault();
|
|
2609
|
-
this.ngZone.run(() => {
|
|
2482
|
+
this.ngZone.run(() => {
|
|
2483
|
+
this.close({ refocus: true });
|
|
2484
|
+
});
|
|
2610
2485
|
}
|
|
2611
2486
|
};
|
|
2612
2487
|
document.addEventListener('keydown', this.keydownHandler);
|
|
@@ -2619,11 +2494,15 @@ class DomternalNotionColorPickerComponent {
|
|
|
2619
2494
|
// (hideout, route change). Positioning against a detached anchor is
|
|
2620
2495
|
// a no-op; close instead. Matches React/Vue behaviour.
|
|
2621
2496
|
if (this.anchorEl && !this.anchorEl.isConnected) {
|
|
2622
|
-
this.ngZone.run(() => {
|
|
2497
|
+
this.ngZone.run(() => {
|
|
2498
|
+
this.close({ refocus: false });
|
|
2499
|
+
});
|
|
2623
2500
|
return;
|
|
2624
2501
|
}
|
|
2625
2502
|
if (this.editor().state.selection.empty) {
|
|
2626
|
-
this.ngZone.run(() => {
|
|
2503
|
+
this.ngZone.run(() => {
|
|
2504
|
+
this.close({ refocus: false });
|
|
2505
|
+
});
|
|
2627
2506
|
}
|
|
2628
2507
|
else {
|
|
2629
2508
|
// Selection changed but stays non-empty (e.g. shift+arrow): refresh
|
|
@@ -2677,8 +2556,7 @@ class DomternalNotionColorPickerComponent {
|
|
|
2677
2556
|
}
|
|
2678
2557
|
readPalette() {
|
|
2679
2558
|
const ext = this.editor().extensionManager.extensions.find((e) => e.name === 'notionColorPicker');
|
|
2680
|
-
|
|
2681
|
-
return options?.palette ? [...options.palette] : [];
|
|
2559
|
+
return paletteFromExtensionOptions(ext?.options);
|
|
2682
2560
|
}
|
|
2683
2561
|
getStorage() {
|
|
2684
2562
|
const storage = this.editor().storage;
|
|
@@ -2702,8 +2580,8 @@ class DomternalNotionColorPickerComponent {
|
|
|
2702
2580
|
this.eventHandler = null;
|
|
2703
2581
|
}
|
|
2704
2582
|
}
|
|
2705
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
2706
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
2583
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: DomternalNotionColorPickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2584
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: DomternalNotionColorPickerComponent, isStandalone: true, selector: "domternal-notion-color-picker", inputs: { editor: { classPropertyName: "editor", publicName: "editor", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: `
|
|
2707
2585
|
@if (isOpen()) {
|
|
2708
2586
|
<div
|
|
2709
2587
|
class="dm-notion-color-picker"
|
|
@@ -2777,7 +2655,7 @@ class DomternalNotionColorPickerComponent {
|
|
|
2777
2655
|
}
|
|
2778
2656
|
`, isInline: true, styles: [":host{display:contents}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
2779
2657
|
}
|
|
2780
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
2658
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: DomternalNotionColorPickerComponent, decorators: [{
|
|
2781
2659
|
type: Component,
|
|
2782
2660
|
args: [{ selector: 'domternal-notion-color-picker', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: `
|
|
2783
2661
|
@if (isOpen()) {
|