@agent-native/core 0.39.2 → 0.40.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 +1 -1
- package/dist/action.js +12 -0
- package/dist/action.js.map +1 -1
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +5 -1
- package/dist/cli/create.js.map +1 -1
- package/dist/cli/skills.d.ts +4 -3
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +756 -694
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/blocks/AiEditableField.d.ts +8 -0
- package/dist/client/blocks/AiEditableField.d.ts.map +1 -0
- package/dist/client/blocks/AiEditableField.js +10 -0
- package/dist/client/blocks/AiEditableField.js.map +1 -0
- package/dist/client/blocks/BlockView.d.ts +3 -3
- package/dist/client/blocks/BlockView.d.ts.map +1 -1
- package/dist/client/blocks/BlockView.js +15 -3
- package/dist/client/blocks/BlockView.js.map +1 -1
- package/dist/client/blocks/SchemaBlockEditor.js +2 -2
- package/dist/client/blocks/SchemaBlockEditor.js.map +1 -1
- package/dist/client/blocks/index.d.ts +5 -2
- package/dist/client/blocks/index.d.ts.map +1 -1
- package/dist/client/blocks/index.js +6 -3
- package/dist/client/blocks/index.js.map +1 -1
- package/dist/client/blocks/library/ApiEndpointBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/ApiEndpointBlock.js +20 -6
- package/dist/client/blocks/library/ApiEndpointBlock.js.map +1 -1
- package/dist/client/blocks/library/DiffBlock.d.ts +29 -0
- package/dist/client/blocks/library/DiffBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/DiffBlock.js +190 -30
- package/dist/client/blocks/library/DiffBlock.js.map +1 -1
- package/dist/client/blocks/library/FileTreeBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/FileTreeBlock.js +46 -7
- package/dist/client/blocks/library/FileTreeBlock.js.map +1 -1
- package/dist/client/blocks/library/HighlightedCode.d.ts +10 -0
- package/dist/client/blocks/library/HighlightedCode.d.ts.map +1 -0
- package/dist/client/blocks/library/HighlightedCode.js +92 -0
- package/dist/client/blocks/library/HighlightedCode.js.map +1 -0
- package/dist/client/blocks/library/JsonExplorerBlock.d.ts +9 -4
- package/dist/client/blocks/library/JsonExplorerBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/JsonExplorerBlock.js +66 -30
- package/dist/client/blocks/library/JsonExplorerBlock.js.map +1 -1
- package/dist/client/blocks/library/MermaidBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/MermaidBlock.js +73 -44
- package/dist/client/blocks/library/MermaidBlock.js.map +1 -1
- package/dist/client/blocks/library/OpenApiSpecBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/OpenApiSpecBlock.js +3 -2
- package/dist/client/blocks/library/OpenApiSpecBlock.js.map +1 -1
- package/dist/client/blocks/library/checklist.d.ts.map +1 -1
- package/dist/client/blocks/library/checklist.js +1 -0
- package/dist/client/blocks/library/checklist.js.map +1 -1
- package/dist/client/blocks/library/code-tabs.d.ts.map +1 -1
- package/dist/client/blocks/library/code-tabs.js +183 -102
- package/dist/client/blocks/library/code-tabs.js.map +1 -1
- package/dist/client/blocks/library/columns.config.d.ts +60 -0
- package/dist/client/blocks/library/columns.config.d.ts.map +1 -0
- package/dist/client/blocks/library/columns.config.js +37 -0
- package/dist/client/blocks/library/columns.config.js.map +1 -0
- package/dist/client/blocks/library/columns.d.ts +25 -0
- package/dist/client/blocks/library/columns.d.ts.map +1 -0
- package/dist/client/blocks/library/columns.js +199 -0
- package/dist/client/blocks/library/columns.js.map +1 -0
- package/dist/client/blocks/library/dev-doc-ui.d.ts +2 -1
- package/dist/client/blocks/library/dev-doc-ui.d.ts.map +1 -1
- package/dist/client/blocks/library/dev-doc-ui.js +2 -1
- package/dist/client/blocks/library/dev-doc-ui.js.map +1 -1
- package/dist/client/blocks/library/html.d.ts +1 -1
- package/dist/client/blocks/library/html.d.ts.map +1 -1
- package/dist/client/blocks/library/html.js +34 -4
- package/dist/client/blocks/library/html.js.map +1 -1
- package/dist/client/blocks/library/json-explorer.config.d.ts +3 -1
- package/dist/client/blocks/library/json-explorer.config.d.ts.map +1 -1
- package/dist/client/blocks/library/json-explorer.config.js +30 -1
- package/dist/client/blocks/library/json-explorer.config.js.map +1 -1
- package/dist/client/blocks/library/server-specs.d.ts.map +1 -1
- package/dist/client/blocks/library/server-specs.js +13 -3
- package/dist/client/blocks/library/server-specs.js.map +1 -1
- package/dist/client/blocks/library/specs.d.ts +4 -4
- package/dist/client/blocks/library/specs.d.ts.map +1 -1
- package/dist/client/blocks/library/specs.js +21 -16
- package/dist/client/blocks/library/specs.js.map +1 -1
- package/dist/client/blocks/library/table.config.d.ts +3 -0
- package/dist/client/blocks/library/table.config.d.ts.map +1 -1
- package/dist/client/blocks/library/table.config.js +13 -1
- package/dist/client/blocks/library/table.config.js.map +1 -1
- package/dist/client/blocks/library/table.d.ts.map +1 -1
- package/dist/client/blocks/library/table.js +90 -9
- package/dist/client/blocks/library/table.js.map +1 -1
- package/dist/client/blocks/library/tabs.config.d.ts +16 -8
- package/dist/client/blocks/library/tabs.config.d.ts.map +1 -1
- package/dist/client/blocks/library/tabs.config.js +10 -4
- package/dist/client/blocks/library/tabs.config.js.map +1 -1
- package/dist/client/blocks/library/tabs.d.ts.map +1 -1
- package/dist/client/blocks/library/tabs.js +146 -21
- package/dist/client/blocks/library/tabs.js.map +1 -1
- package/dist/client/blocks/server.d.ts +2 -1
- package/dist/client/blocks/server.d.ts.map +1 -1
- package/dist/client/blocks/server.js +1 -0
- package/dist/client/blocks/server.js.map +1 -1
- package/dist/client/blocks/types.d.ts +99 -9
- package/dist/client/blocks/types.d.ts.map +1 -1
- package/dist/client/blocks/types.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +2 -2
- package/dist/client/index.js.map +1 -1
- package/dist/client/rich-markdown-editor/BubbleToolbar.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/BubbleToolbar.js +13 -3
- package/dist/client/rich-markdown-editor/BubbleToolbar.js.map +1 -1
- package/dist/client/rich-markdown-editor/DragHandle.d.ts +49 -4
- package/dist/client/rich-markdown-editor/DragHandle.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/DragHandle.js +656 -88
- package/dist/client/rich-markdown-editor/DragHandle.js.map +1 -1
- package/dist/client/rich-markdown-editor/RegistryBlockNode.d.ts +10 -1
- package/dist/client/rich-markdown-editor/RegistryBlockNode.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/RegistryBlockNode.js +180 -15
- package/dist/client/rich-markdown-editor/RegistryBlockNode.js.map +1 -1
- package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts +2 -1
- package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/SharedRichEditor.js +3 -1
- package/dist/client/rich-markdown-editor/SharedRichEditor.js.map +1 -1
- package/dist/client/rich-markdown-editor/SlashCommandMenu.d.ts +5 -0
- package/dist/client/rich-markdown-editor/SlashCommandMenu.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/SlashCommandMenu.js +33 -5
- package/dist/client/rich-markdown-editor/SlashCommandMenu.js.map +1 -1
- package/dist/client/rich-markdown-editor/index.d.ts +3 -3
- package/dist/client/rich-markdown-editor/index.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/index.js +2 -2
- package/dist/client/rich-markdown-editor/index.js.map +1 -1
- package/dist/client/rich-markdown-editor/registrySlashCommands.d.ts +14 -0
- package/dist/client/rich-markdown-editor/registrySlashCommands.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/registrySlashCommands.js +38 -0
- package/dist/client/rich-markdown-editor/registrySlashCommands.js.map +1 -1
- package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts +1 -0
- package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/useCollabReconcile.js +4 -0
- package/dist/client/rich-markdown-editor/useCollabReconcile.js.map +1 -1
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +17 -1
- package/dist/db/client.js.map +1 -1
- package/dist/sharing/access.d.ts +4 -2
- package/dist/sharing/access.d.ts.map +1 -1
- package/dist/sharing/access.js +8 -3
- package/dist/sharing/access.js.map +1 -1
- package/dist/sharing/actions/set-resource-visibility.d.ts.map +1 -1
- package/dist/sharing/actions/set-resource-visibility.js +2 -3
- package/dist/sharing/actions/set-resource-visibility.js.map +1 -1
- package/dist/sharing/registry.d.ts +13 -0
- package/dist/sharing/registry.d.ts.map +1 -1
- package/dist/sharing/registry.js.map +1 -1
- package/dist/styles/rich-markdown-editor.css +15 -0
- package/package.json +16 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Extension } from "@tiptap/core";
|
|
2
|
-
import { Plugin, PluginKey, NodeSelection } from "@tiptap/pm/state";
|
|
2
|
+
import { Plugin, PluginKey, NodeSelection, TextSelection, } from "@tiptap/pm/state";
|
|
3
3
|
/**
|
|
4
4
|
* Default editor-wrapper CSS selector the drag handle scopes itself to.
|
|
5
5
|
*
|
|
@@ -12,6 +12,206 @@ import { Plugin, PluginKey, NodeSelection } from "@tiptap/pm/state";
|
|
|
12
12
|
export const DEFAULT_DRAG_HANDLE_WRAPPER_SELECTOR = ".visual-editor-wrapper";
|
|
13
13
|
const dragHandleKey = new PluginKey("dragHandle");
|
|
14
14
|
const HOVER_SIDE_OUTSET_REM = 8;
|
|
15
|
+
const SIDE_DROP_ZONE_RATIO = 0.28;
|
|
16
|
+
const SIDE_DROP_ZONE_MIN_PX = 48;
|
|
17
|
+
const SIDE_DROP_ZONE_MAX_PX = 140;
|
|
18
|
+
const DRAG_HANDLE_MENU_STYLE_ID = "an-rich-md-drag-menu-styles";
|
|
19
|
+
const DRAG_HANDLE_MENU_WIDTH = 220;
|
|
20
|
+
const DRAG_HANDLE_MENU_GAP = 6;
|
|
21
|
+
const DRAG_HANDLE_MENU_VIEWPORT_PADDING = 8;
|
|
22
|
+
const dragHandleRegistrations = new Set();
|
|
23
|
+
let dragHandleGlobalHoverListeners = 0;
|
|
24
|
+
let activeDragRegistration = null;
|
|
25
|
+
const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
|
|
26
|
+
const editorArea = (registration) => {
|
|
27
|
+
const rect = registration.view.dom.getBoundingClientRect();
|
|
28
|
+
return rect.width * rect.height;
|
|
29
|
+
};
|
|
30
|
+
const updateRegisteredHover = (clientX, clientY) => {
|
|
31
|
+
if (activeDragRegistration) {
|
|
32
|
+
for (const registration of dragHandleRegistrations) {
|
|
33
|
+
registration.hideHover?.();
|
|
34
|
+
}
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const candidates = [];
|
|
38
|
+
for (const registration of dragHandleRegistrations) {
|
|
39
|
+
if (!registration.view.dom.isConnected || !registration.canHover?.()) {
|
|
40
|
+
registration.hideHover?.();
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
const block = registration.findHoverBlock?.(clientX, clientY);
|
|
44
|
+
if (block) {
|
|
45
|
+
candidates.push({ registration, block });
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
registration.hideHover?.();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
candidates.sort((a, b) => editorArea(a.registration) - editorArea(b.registration));
|
|
52
|
+
const active = candidates[0] ?? null;
|
|
53
|
+
for (const registration of dragHandleRegistrations) {
|
|
54
|
+
if (registration !== active?.registration)
|
|
55
|
+
registration.hideHover?.();
|
|
56
|
+
}
|
|
57
|
+
active?.registration.showHoverBlock?.(active.block);
|
|
58
|
+
};
|
|
59
|
+
const handleGlobalHoverMove = (event) => {
|
|
60
|
+
updateRegisteredHover(event.clientX, event.clientY);
|
|
61
|
+
};
|
|
62
|
+
const retainGlobalHoverListener = () => {
|
|
63
|
+
dragHandleGlobalHoverListeners += 1;
|
|
64
|
+
if (dragHandleGlobalHoverListeners === 1) {
|
|
65
|
+
document.addEventListener("mousemove", handleGlobalHoverMove);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
const releaseGlobalHoverListener = () => {
|
|
69
|
+
dragHandleGlobalHoverListeners = Math.max(0, dragHandleGlobalHoverListeners - 1);
|
|
70
|
+
if (dragHandleGlobalHoverListeners === 0) {
|
|
71
|
+
document.removeEventListener("mousemove", handleGlobalHoverMove);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
const ensureDragHandleMenuStyles = () => {
|
|
75
|
+
if (document.getElementById(DRAG_HANDLE_MENU_STYLE_ID))
|
|
76
|
+
return;
|
|
77
|
+
const style = document.createElement("style");
|
|
78
|
+
style.id = DRAG_HANDLE_MENU_STYLE_ID;
|
|
79
|
+
style.textContent = `
|
|
80
|
+
.an-rich-md-drag-menu {
|
|
81
|
+
position: fixed;
|
|
82
|
+
z-index: 9999;
|
|
83
|
+
width: ${DRAG_HANDLE_MENU_WIDTH}px;
|
|
84
|
+
padding: 4px;
|
|
85
|
+
border: 1px solid hsl(var(--border, 214.3 31.8% 91.4%));
|
|
86
|
+
border-radius: 7px;
|
|
87
|
+
background: hsl(var(--popover, 0 0% 100%));
|
|
88
|
+
color: hsl(var(--popover-foreground, var(--foreground, 222.2 84% 4.9%)));
|
|
89
|
+
box-shadow:
|
|
90
|
+
0 12px 32px rgb(15 23 42 / 0.16),
|
|
91
|
+
0 2px 8px rgb(15 23 42 / 0.08);
|
|
92
|
+
font-family: inherit;
|
|
93
|
+
font-size: 13px;
|
|
94
|
+
line-height: 1.35;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.an-rich-md-drag-menu__item {
|
|
98
|
+
display: flex;
|
|
99
|
+
width: 100%;
|
|
100
|
+
align-items: center;
|
|
101
|
+
gap: 9px;
|
|
102
|
+
border: 0;
|
|
103
|
+
border-radius: 5px;
|
|
104
|
+
background: transparent;
|
|
105
|
+
color: inherit;
|
|
106
|
+
cursor: pointer;
|
|
107
|
+
font: inherit;
|
|
108
|
+
letter-spacing: 0;
|
|
109
|
+
padding: 7px 8px;
|
|
110
|
+
text-align: left;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.an-rich-md-drag-menu__item:hover,
|
|
114
|
+
.an-rich-md-drag-menu__item:focus-visible {
|
|
115
|
+
background: hsl(var(--accent, 210 40% 96.1%));
|
|
116
|
+
color: hsl(var(--accent-foreground, var(--foreground, 222.2 84% 4.9%)));
|
|
117
|
+
outline: none;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.an-rich-md-drag-menu__item[data-danger="true"] {
|
|
121
|
+
color: hsl(var(--destructive, 0 84.2% 60.2%));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.an-rich-md-drag-menu__item[data-danger="true"]:hover,
|
|
125
|
+
.an-rich-md-drag-menu__item[data-danger="true"]:focus-visible {
|
|
126
|
+
background: hsl(var(--destructive, 0 84.2% 60.2%) / 0.1);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.an-rich-md-drag-menu__icon {
|
|
130
|
+
position: relative;
|
|
131
|
+
flex: 0 0 auto;
|
|
132
|
+
width: 18px;
|
|
133
|
+
height: 18px;
|
|
134
|
+
color: hsl(var(--muted-foreground, 215.4 16.3% 46.9%));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.an-rich-md-drag-menu__item[data-danger="true"] .an-rich-md-drag-menu__icon {
|
|
138
|
+
color: currentColor;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.an-rich-md-drag-menu__icon::before,
|
|
142
|
+
.an-rich-md-drag-menu__icon::after {
|
|
143
|
+
content: "";
|
|
144
|
+
position: absolute;
|
|
145
|
+
box-sizing: border-box;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.an-rich-md-drag-menu__icon--duplicate::before,
|
|
149
|
+
.an-rich-md-drag-menu__icon--duplicate::after {
|
|
150
|
+
width: 11px;
|
|
151
|
+
height: 11px;
|
|
152
|
+
border: 1.5px solid currentColor;
|
|
153
|
+
border-radius: 2px;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.an-rich-md-drag-menu__icon--duplicate::before {
|
|
157
|
+
left: 6px;
|
|
158
|
+
top: 2px;
|
|
159
|
+
opacity: 0.55;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.an-rich-md-drag-menu__icon--duplicate::after {
|
|
163
|
+
left: 2px;
|
|
164
|
+
top: 6px;
|
|
165
|
+
background: hsl(var(--popover, 0 0% 100%));
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.an-rich-md-drag-menu__icon--insert::before {
|
|
169
|
+
left: 3px;
|
|
170
|
+
top: 8px;
|
|
171
|
+
width: 12px;
|
|
172
|
+
height: 1.5px;
|
|
173
|
+
border-radius: 999px;
|
|
174
|
+
background: currentColor;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.an-rich-md-drag-menu__icon--insert::after {
|
|
178
|
+
left: 8px;
|
|
179
|
+
top: 3px;
|
|
180
|
+
width: 1.5px;
|
|
181
|
+
height: 12px;
|
|
182
|
+
border-radius: 999px;
|
|
183
|
+
background: currentColor;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.an-rich-md-drag-menu__icon--delete::before {
|
|
187
|
+
left: 4px;
|
|
188
|
+
top: 7px;
|
|
189
|
+
width: 10px;
|
|
190
|
+
height: 11px;
|
|
191
|
+
border: 1.5px solid currentColor;
|
|
192
|
+
border-top: 0;
|
|
193
|
+
border-radius: 0 0 2px 2px;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.an-rich-md-drag-menu__icon--delete::after {
|
|
197
|
+
left: 3px;
|
|
198
|
+
top: 4px;
|
|
199
|
+
width: 12px;
|
|
200
|
+
height: 1.5px;
|
|
201
|
+
border-radius: 999px;
|
|
202
|
+
background: currentColor;
|
|
203
|
+
box-shadow: 3px -2.5px 0 -0.4px currentColor;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.an-rich-md-drag-menu__label {
|
|
207
|
+
min-width: 0;
|
|
208
|
+
overflow: hidden;
|
|
209
|
+
text-overflow: ellipsis;
|
|
210
|
+
white-space: nowrap;
|
|
211
|
+
}
|
|
212
|
+
`;
|
|
213
|
+
document.head.appendChild(style);
|
|
214
|
+
};
|
|
15
215
|
/**
|
|
16
216
|
* App-agnostic Tiptap extension providing a Notion-style left-margin drag grip
|
|
17
217
|
* (the `::` handle), block selection, and drag-to-reorder over top-level block
|
|
@@ -21,10 +221,10 @@ const HOVER_SIDE_OUTSET_REM = 8;
|
|
|
21
221
|
* - On hover over any top-level block, a `.drag-handle` grip appears in the left
|
|
22
222
|
* margin (forgiving hit zone extends {@link HOVER_SIDE_OUTSET_REM}rem to the
|
|
23
223
|
* sides and into the gap above/between blocks).
|
|
24
|
-
* -
|
|
25
|
-
* a small threshold starts a reorder, showing a floating clone
|
|
26
|
-
* (`.notion-drag-preview`) and a `.notion-drop-indicator` line.
|
|
27
|
-
* cancels.
|
|
224
|
+
* - Single-clicking the grip selects the block and opens a block action menu.
|
|
225
|
+
* Dragging past a small threshold starts a reorder, showing a floating clone
|
|
226
|
+
* preview (`.notion-drag-preview`) and a `.notion-drop-indicator` line.
|
|
227
|
+
* `Escape` cancels.
|
|
28
228
|
* - While dragging, the source block carries `.notion-block--dragging` and the
|
|
29
229
|
* document element carries `.notion-editor-is-dragging` so apps can style the
|
|
30
230
|
* in-flight state. Apps own all of these CSS class names.
|
|
@@ -44,15 +244,23 @@ export const DragHandle = Extension.create({
|
|
|
44
244
|
addOptions() {
|
|
45
245
|
return {
|
|
46
246
|
wrapperSelector: DEFAULT_DRAG_HANDLE_WRAPPER_SELECTOR,
|
|
247
|
+
getDragTransferData: undefined,
|
|
248
|
+
receiveDragTransferData: undefined,
|
|
47
249
|
};
|
|
48
250
|
},
|
|
49
251
|
addProseMirrorPlugins() {
|
|
50
252
|
const editor = this.editor;
|
|
51
253
|
const wrapperSelector = this.options.wrapperSelector;
|
|
254
|
+
const getDragTransferData = this.options.getDragTransferData;
|
|
255
|
+
const receiveDragTransferData = this.options.receiveDragTransferData;
|
|
256
|
+
const handleDrop = this.options.handleDrop;
|
|
52
257
|
let handle = null;
|
|
258
|
+
let menu = null;
|
|
259
|
+
let menuContext = null;
|
|
53
260
|
let currentBlock = null;
|
|
54
261
|
let dragStartPos = null;
|
|
55
262
|
let dragSession = null;
|
|
263
|
+
let currentRegistration = null;
|
|
56
264
|
const getHoverSideOutset = () => {
|
|
57
265
|
const rootFontSize = Number.parseFloat(getComputedStyle(document.documentElement).fontSize);
|
|
58
266
|
return ((Number.isFinite(rootFontSize) ? rootFontSize : 16) *
|
|
@@ -72,6 +280,13 @@ export const DragHandle = Extension.create({
|
|
|
72
280
|
});
|
|
73
281
|
return blocks;
|
|
74
282
|
};
|
|
283
|
+
const registrationForView = (editorView) => {
|
|
284
|
+
for (const registration of dragHandleRegistrations) {
|
|
285
|
+
if (registration.view === editorView)
|
|
286
|
+
return registration;
|
|
287
|
+
}
|
|
288
|
+
return null;
|
|
289
|
+
};
|
|
75
290
|
const findForgivingBlock = (editorView, clientX, clientY) => {
|
|
76
291
|
const blocks = getTopLevelBlocks(editorView);
|
|
77
292
|
if (blocks.length === 0)
|
|
@@ -122,11 +337,9 @@ export const DragHandle = Extension.create({
|
|
|
122
337
|
handle.style.top = `${block.rect.top - wrapperRect.top + 2}px`;
|
|
123
338
|
handle.style.left = "-24px";
|
|
124
339
|
};
|
|
125
|
-
const
|
|
126
|
-
if (dragStartPos === null)
|
|
127
|
-
return null;
|
|
340
|
+
const selectBlockAt = (editorView, pos) => {
|
|
128
341
|
try {
|
|
129
|
-
const sel = NodeSelection.create(editorView.state.doc,
|
|
342
|
+
const sel = NodeSelection.create(editorView.state.doc, pos);
|
|
130
343
|
editorView.dispatch(editorView.state.tr.setSelection(sel));
|
|
131
344
|
editorView.focus();
|
|
132
345
|
return sel;
|
|
@@ -162,8 +375,8 @@ export const DragHandle = Extension.create({
|
|
|
162
375
|
document.body.appendChild(preview);
|
|
163
376
|
return preview;
|
|
164
377
|
};
|
|
165
|
-
const createDropLine = (
|
|
166
|
-
const wrapper = view.dom.closest(wrapperSelector);
|
|
378
|
+
const createDropLine = (registration) => {
|
|
379
|
+
const wrapper = registration.view.dom.closest(registration.wrapperSelector);
|
|
167
380
|
if (!wrapper)
|
|
168
381
|
return null;
|
|
169
382
|
const line = document.createElement("div");
|
|
@@ -171,23 +384,244 @@ export const DragHandle = Extension.create({
|
|
|
171
384
|
wrapper.appendChild(line);
|
|
172
385
|
return line;
|
|
173
386
|
};
|
|
174
|
-
const
|
|
387
|
+
const forceHideHandle = () => {
|
|
388
|
+
if (handle) {
|
|
389
|
+
handle.style.display = "none";
|
|
390
|
+
handle.setAttribute("aria-expanded", "false");
|
|
391
|
+
}
|
|
392
|
+
currentBlock = null;
|
|
393
|
+
dragStartPos = null;
|
|
394
|
+
};
|
|
395
|
+
const closeMenu = ({ hideGrip = false } = {}) => {
|
|
396
|
+
menu?.remove();
|
|
397
|
+
menu = null;
|
|
398
|
+
menuContext = null;
|
|
399
|
+
handle?.setAttribute("aria-expanded", "false");
|
|
400
|
+
document.removeEventListener("mousedown", handleMenuDocumentMouseDown, {
|
|
401
|
+
capture: true,
|
|
402
|
+
});
|
|
403
|
+
document.removeEventListener("keydown", handleMenuKeyDown, {
|
|
404
|
+
capture: true,
|
|
405
|
+
});
|
|
406
|
+
window.removeEventListener("resize", handleMenuViewportChange);
|
|
407
|
+
window.removeEventListener("scroll", handleMenuViewportChange, {
|
|
408
|
+
capture: true,
|
|
409
|
+
});
|
|
410
|
+
if (hideGrip)
|
|
411
|
+
forceHideHandle();
|
|
412
|
+
};
|
|
413
|
+
const resolveMenuContext = (context) => {
|
|
414
|
+
const latestBlock = getTopLevelBlocks(context.view).find((block) => block.node === context.sourceBlock);
|
|
415
|
+
const sourcePos = latestBlock?.pmPos ?? context.sourcePos;
|
|
416
|
+
const sourceNode = context.view.state.doc.nodeAt(sourcePos);
|
|
417
|
+
if (!sourceNode)
|
|
418
|
+
return null;
|
|
419
|
+
return {
|
|
420
|
+
...context,
|
|
421
|
+
sourcePos,
|
|
422
|
+
sourceNode,
|
|
423
|
+
sourceNodeSize: sourceNode.nodeSize,
|
|
424
|
+
};
|
|
425
|
+
};
|
|
426
|
+
const focusSelectionNear = (view, tr, pos, bias) => {
|
|
427
|
+
tr.setSelection(TextSelection.near(tr.doc.resolve(clamp(pos, 0, tr.doc.content.size)), bias));
|
|
428
|
+
view.dispatch(tr.scrollIntoView());
|
|
429
|
+
view.focus();
|
|
430
|
+
};
|
|
431
|
+
const duplicateBlock = (context) => {
|
|
432
|
+
const resolved = resolveMenuContext(context);
|
|
433
|
+
if (!resolved)
|
|
434
|
+
return;
|
|
435
|
+
const insertPos = resolved.sourcePos + resolved.sourceNodeSize;
|
|
436
|
+
const tr = resolved.view.state.tr.insert(insertPos, resolved.sourceNode);
|
|
437
|
+
try {
|
|
438
|
+
tr.setSelection(NodeSelection.create(tr.doc, insertPos));
|
|
439
|
+
resolved.view.dispatch(tr.scrollIntoView());
|
|
440
|
+
resolved.view.focus();
|
|
441
|
+
}
|
|
442
|
+
catch {
|
|
443
|
+
focusSelectionNear(resolved.view, tr, insertPos, 1);
|
|
444
|
+
}
|
|
445
|
+
};
|
|
446
|
+
const deleteBlock = (context) => {
|
|
447
|
+
const resolved = resolveMenuContext(context);
|
|
448
|
+
if (!resolved)
|
|
449
|
+
return;
|
|
450
|
+
const { view, sourcePos, sourceNodeSize } = resolved;
|
|
451
|
+
const paragraph = view.state.schema.nodes.paragraph;
|
|
452
|
+
const sourceEnd = sourcePos + sourceNodeSize;
|
|
453
|
+
if (view.state.doc.childCount <= 1 && paragraph) {
|
|
454
|
+
const replacement = paragraph.createAndFill() ?? paragraph.create();
|
|
455
|
+
const tr = view.state.tr.replaceWith(sourcePos, sourceEnd, replacement);
|
|
456
|
+
focusSelectionNear(view, tr, sourcePos + 1, 1);
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
const tr = view.state.tr.delete(sourcePos, sourceEnd);
|
|
460
|
+
const selectionBias = sourcePos >= tr.doc.content.size ? -1 : 1;
|
|
461
|
+
focusSelectionNear(view, tr, sourcePos, selectionBias);
|
|
462
|
+
};
|
|
463
|
+
const insertParagraphBelow = (context) => {
|
|
464
|
+
const resolved = resolveMenuContext(context);
|
|
465
|
+
const paragraph = resolved?.view.state.schema.nodes.paragraph;
|
|
466
|
+
if (!resolved || !paragraph)
|
|
467
|
+
return;
|
|
468
|
+
const insertPos = resolved.sourcePos + resolved.sourceNodeSize;
|
|
469
|
+
const paragraphNode = paragraph.createAndFill() ?? paragraph.create();
|
|
470
|
+
const tr = resolved.view.state.tr.insert(insertPos, paragraphNode);
|
|
471
|
+
tr.setSelection(TextSelection.create(tr.doc, insertPos + 1));
|
|
472
|
+
resolved.view.dispatch(tr.scrollIntoView());
|
|
473
|
+
resolved.view.focus();
|
|
474
|
+
};
|
|
475
|
+
const positionMenu = (anchorRect) => {
|
|
476
|
+
if (!menu)
|
|
477
|
+
return;
|
|
478
|
+
const viewportWidth = window.visualViewport?.width ?? window.innerWidth;
|
|
479
|
+
const viewportHeight = window.visualViewport?.height ?? window.innerHeight;
|
|
480
|
+
const menuHeight = menu.offsetHeight || 118;
|
|
481
|
+
const preferredLeft = anchorRect.right + DRAG_HANDLE_MENU_GAP;
|
|
482
|
+
const alternateLeft = anchorRect.left - DRAG_HANDLE_MENU_WIDTH - DRAG_HANDLE_MENU_GAP;
|
|
483
|
+
const left = preferredLeft +
|
|
484
|
+
DRAG_HANDLE_MENU_WIDTH +
|
|
485
|
+
DRAG_HANDLE_MENU_VIEWPORT_PADDING <=
|
|
486
|
+
viewportWidth
|
|
487
|
+
? preferredLeft
|
|
488
|
+
: alternateLeft;
|
|
489
|
+
menu.style.left = `${clamp(left, DRAG_HANDLE_MENU_VIEWPORT_PADDING, viewportWidth -
|
|
490
|
+
DRAG_HANDLE_MENU_WIDTH -
|
|
491
|
+
DRAG_HANDLE_MENU_VIEWPORT_PADDING)}px`;
|
|
492
|
+
menu.style.top = `${clamp(anchorRect.top - 4, DRAG_HANDLE_MENU_VIEWPORT_PADDING, viewportHeight - menuHeight - DRAG_HANDLE_MENU_VIEWPORT_PADDING)}px`;
|
|
493
|
+
};
|
|
494
|
+
const createMenuItem = (label, iconModifier, action, options = {}) => {
|
|
495
|
+
const button = document.createElement("button");
|
|
496
|
+
button.type = "button";
|
|
497
|
+
button.className = "an-rich-md-drag-menu__item";
|
|
498
|
+
button.setAttribute("role", "menuitem");
|
|
499
|
+
button.setAttribute("data-plan-interactive", "true");
|
|
500
|
+
if (options.danger)
|
|
501
|
+
button.setAttribute("data-danger", "true");
|
|
502
|
+
const icon = document.createElement("span");
|
|
503
|
+
icon.className = `an-rich-md-drag-menu__icon an-rich-md-drag-menu__icon--${iconModifier}`;
|
|
504
|
+
icon.setAttribute("aria-hidden", "true");
|
|
505
|
+
const labelElement = document.createElement("span");
|
|
506
|
+
labelElement.className = "an-rich-md-drag-menu__label";
|
|
507
|
+
labelElement.textContent = label;
|
|
508
|
+
button.append(icon, labelElement);
|
|
509
|
+
button.addEventListener("mousedown", (event) => {
|
|
510
|
+
event.preventDefault();
|
|
511
|
+
});
|
|
512
|
+
button.addEventListener("click", (event) => {
|
|
513
|
+
event.preventDefault();
|
|
514
|
+
event.stopPropagation();
|
|
515
|
+
const context = menuContext;
|
|
516
|
+
if (!context)
|
|
517
|
+
return;
|
|
518
|
+
closeMenu({ hideGrip: true });
|
|
519
|
+
action(context);
|
|
520
|
+
});
|
|
521
|
+
return button;
|
|
522
|
+
};
|
|
523
|
+
const openMenu = (context, anchorRect) => {
|
|
524
|
+
const resolved = resolveMenuContext(context);
|
|
525
|
+
if (!resolved)
|
|
526
|
+
return;
|
|
527
|
+
closeMenu();
|
|
528
|
+
selectBlockAt(resolved.view, resolved.sourcePos);
|
|
529
|
+
ensureDragHandleMenuStyles();
|
|
530
|
+
const el = document.createElement("div");
|
|
531
|
+
el.className = "an-rich-md-drag-menu";
|
|
532
|
+
el.setAttribute("role", "menu");
|
|
533
|
+
el.setAttribute("aria-label", "Block actions");
|
|
534
|
+
el.setAttribute("data-plan-interactive", "true");
|
|
535
|
+
el.append(createMenuItem("Duplicate", "duplicate", duplicateBlock), createMenuItem("Delete", "delete", deleteBlock, { danger: true }), createMenuItem("Insert block below", "insert", insertParagraphBelow));
|
|
536
|
+
menu = el;
|
|
537
|
+
menuContext = {
|
|
538
|
+
view: resolved.view,
|
|
539
|
+
sourceBlock: resolved.sourceBlock,
|
|
540
|
+
sourcePos: resolved.sourcePos,
|
|
541
|
+
sourceNodeSize: resolved.sourceNodeSize,
|
|
542
|
+
};
|
|
543
|
+
document.body.appendChild(el);
|
|
544
|
+
positionMenu(anchorRect);
|
|
545
|
+
handle?.setAttribute("aria-expanded", "true");
|
|
546
|
+
document.addEventListener("mousedown", handleMenuDocumentMouseDown, {
|
|
547
|
+
capture: true,
|
|
548
|
+
});
|
|
549
|
+
document.addEventListener("keydown", handleMenuKeyDown, {
|
|
550
|
+
capture: true,
|
|
551
|
+
});
|
|
552
|
+
window.addEventListener("resize", handleMenuViewportChange);
|
|
553
|
+
window.addEventListener("scroll", handleMenuViewportChange, {
|
|
554
|
+
capture: true,
|
|
555
|
+
});
|
|
556
|
+
el.querySelector("button")?.focus({
|
|
557
|
+
preventScroll: true,
|
|
558
|
+
});
|
|
559
|
+
};
|
|
560
|
+
const findDropTarget = (registration, clientX, clientY) => {
|
|
561
|
+
const view = registration.view;
|
|
175
562
|
const block = findForgivingBlock(view, clientX, clientY);
|
|
176
563
|
if (!block)
|
|
177
564
|
return null;
|
|
178
565
|
const node = view.state.doc.nodeAt(block.pmPos);
|
|
179
566
|
if (!node)
|
|
180
567
|
return null;
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
568
|
+
let placement;
|
|
569
|
+
const withinBlockY = clientY >= block.rect.top && clientY <= block.rect.bottom;
|
|
570
|
+
const withinSideDropBand = clientY >= block.rect.top + block.rect.height * 0.2 &&
|
|
571
|
+
clientY <= block.rect.bottom - block.rect.height * 0.2;
|
|
572
|
+
const sideZoneWidth = clamp(block.rect.width * SIDE_DROP_ZONE_RATIO, SIDE_DROP_ZONE_MIN_PX, SIDE_DROP_ZONE_MAX_PX);
|
|
573
|
+
if (registration.handleDrop &&
|
|
574
|
+
withinBlockY &&
|
|
575
|
+
withinSideDropBand &&
|
|
576
|
+
clientX <= block.rect.left + sideZoneWidth) {
|
|
577
|
+
placement = "left";
|
|
578
|
+
}
|
|
579
|
+
else if (registration.handleDrop &&
|
|
580
|
+
withinBlockY &&
|
|
581
|
+
withinSideDropBand &&
|
|
582
|
+
clientX >= block.rect.right - sideZoneWidth) {
|
|
583
|
+
placement = "right";
|
|
584
|
+
}
|
|
585
|
+
else {
|
|
586
|
+
placement =
|
|
587
|
+
clientY < block.rect.top ||
|
|
588
|
+
(clientY <= block.rect.bottom &&
|
|
589
|
+
clientY < block.rect.top + block.rect.height / 2)
|
|
590
|
+
? "before"
|
|
591
|
+
: "after";
|
|
592
|
+
}
|
|
593
|
+
const before = placement === "before" || placement === "left";
|
|
184
594
|
return {
|
|
595
|
+
registration,
|
|
596
|
+
view,
|
|
185
597
|
block: block.node,
|
|
186
|
-
|
|
187
|
-
pos:
|
|
598
|
+
placement,
|
|
599
|
+
pos: before ? block.pmPos : block.pmPos + node.nodeSize,
|
|
600
|
+
targetPos: block.pmPos,
|
|
601
|
+
targetNodeSize: node.nodeSize,
|
|
188
602
|
rect: block.rect,
|
|
189
603
|
};
|
|
190
604
|
};
|
|
605
|
+
const findAnyDropTarget = (session, clientX, clientY) => {
|
|
606
|
+
const candidates = [];
|
|
607
|
+
for (const registration of dragHandleRegistrations) {
|
|
608
|
+
if (!registration.view.dom.isConnected)
|
|
609
|
+
continue;
|
|
610
|
+
if (registration.view !== session.view &&
|
|
611
|
+
session.sourceBlock.contains(registration.view.dom)) {
|
|
612
|
+
continue;
|
|
613
|
+
}
|
|
614
|
+
const target = findDropTarget(registration, clientX, clientY);
|
|
615
|
+
if (target)
|
|
616
|
+
candidates.push(target);
|
|
617
|
+
}
|
|
618
|
+
candidates.sort((a, b) => {
|
|
619
|
+
const aRect = a.view.dom.getBoundingClientRect();
|
|
620
|
+
const bRect = b.view.dom.getBoundingClientRect();
|
|
621
|
+
return aRect.width * aRect.height - bRect.width * bRect.height;
|
|
622
|
+
});
|
|
623
|
+
return candidates[0] ?? null;
|
|
624
|
+
};
|
|
191
625
|
const positionDragPreview = (session, clientX, clientY) => {
|
|
192
626
|
if (!session.preview)
|
|
193
627
|
return;
|
|
@@ -196,34 +630,52 @@ export const DragHandle = Extension.create({
|
|
|
196
630
|
const updateDropLine = (session, target) => {
|
|
197
631
|
const sourceEnd = session.sourcePos + session.sourceNodeSize;
|
|
198
632
|
if (!target ||
|
|
199
|
-
target.
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
633
|
+
(target.view === session.view &&
|
|
634
|
+
(target.pos === session.sourcePos ||
|
|
635
|
+
target.pos === sourceEnd ||
|
|
636
|
+
(target.pos > session.sourcePos && target.pos < sourceEnd)))) {
|
|
637
|
+
session.dropTarget = null;
|
|
203
638
|
session.dropLine?.remove();
|
|
204
639
|
session.dropLine = null;
|
|
205
640
|
return;
|
|
206
641
|
}
|
|
207
|
-
|
|
208
|
-
session.dropLine = createDropLine(session.view);
|
|
209
|
-
if (!session.dropLine)
|
|
210
|
-
return;
|
|
211
|
-
const wrapper = session.view.dom.closest(wrapperSelector);
|
|
642
|
+
const wrapper = target.view.dom.closest(target.registration.wrapperSelector);
|
|
212
643
|
if (!wrapper)
|
|
213
644
|
return;
|
|
645
|
+
if (!session.dropLine || session.dropLine.parentElement !== wrapper) {
|
|
646
|
+
session.dropLine?.remove();
|
|
647
|
+
session.dropLine = createDropLine(target.registration);
|
|
648
|
+
}
|
|
649
|
+
if (!session.dropLine)
|
|
650
|
+
return;
|
|
214
651
|
const wrapperRect = wrapper.getBoundingClientRect();
|
|
215
|
-
const editorRect =
|
|
216
|
-
|
|
217
|
-
|
|
652
|
+
const editorRect = target.view.dom.getBoundingClientRect();
|
|
653
|
+
session.dropTarget = target;
|
|
654
|
+
if (target.placement === "left" || target.placement === "right") {
|
|
655
|
+
const left = target.placement === "left" ? target.rect.left : target.rect.right;
|
|
656
|
+
session.dropLine.style.left = `${left - wrapperRect.left}px`;
|
|
657
|
+
session.dropLine.style.top = `${target.rect.top - wrapperRect.top}px`;
|
|
658
|
+
session.dropLine.style.width = "3px";
|
|
659
|
+
session.dropLine.style.height = `${target.rect.height}px`;
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
const top = target.placement === "before" ? target.rect.top : target.rect.bottom;
|
|
218
663
|
session.dropLine.style.left = `${editorRect.left - wrapperRect.left}px`;
|
|
219
664
|
session.dropLine.style.top = `${top - wrapperRect.top}px`;
|
|
220
665
|
session.dropLine.style.width = `${editorRect.width}px`;
|
|
666
|
+
session.dropLine.style.height = "3px";
|
|
221
667
|
};
|
|
222
668
|
const createHandle = () => {
|
|
223
669
|
const el = document.createElement("div");
|
|
224
670
|
el.className = "drag-handle";
|
|
225
671
|
el.contentEditable = "false";
|
|
226
672
|
el.draggable = false;
|
|
673
|
+
el.tabIndex = 0;
|
|
674
|
+
el.setAttribute("role", "button");
|
|
675
|
+
el.setAttribute("aria-label", "Open block menu or drag to reorder");
|
|
676
|
+
el.setAttribute("aria-haspopup", "menu");
|
|
677
|
+
el.setAttribute("aria-expanded", "false");
|
|
678
|
+
el.title = "Open block menu or drag to reorder";
|
|
227
679
|
el.innerHTML = `<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor">
|
|
228
680
|
<circle cx="5.5" cy="3" r="1.5"/><circle cx="10.5" cy="3" r="1.5"/>
|
|
229
681
|
<circle cx="5.5" cy="8" r="1.5"/><circle cx="10.5" cy="8" r="1.5"/>
|
|
@@ -232,61 +684,149 @@ export const DragHandle = Extension.create({
|
|
|
232
684
|
return el;
|
|
233
685
|
};
|
|
234
686
|
const hideHandle = () => {
|
|
235
|
-
if (
|
|
236
|
-
|
|
237
|
-
|
|
687
|
+
if (menu)
|
|
688
|
+
return;
|
|
689
|
+
forceHideHandle();
|
|
238
690
|
};
|
|
239
691
|
const removeDragListeners = () => {
|
|
240
692
|
document.removeEventListener("mousemove", handleDocumentMouseMove);
|
|
241
693
|
document.removeEventListener("mouseup", handleDocumentMouseUp);
|
|
242
694
|
document.removeEventListener("keydown", handleDocumentKeyDown);
|
|
243
695
|
};
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
696
|
+
function handleMenuDocumentMouseDown(event) {
|
|
697
|
+
const target = event.target;
|
|
698
|
+
if (!(target instanceof Node))
|
|
699
|
+
return;
|
|
700
|
+
if (menu?.contains(target) || handle?.contains(target))
|
|
701
|
+
return;
|
|
702
|
+
closeMenu({ hideGrip: true });
|
|
703
|
+
}
|
|
704
|
+
function handleMenuKeyDown(event) {
|
|
705
|
+
if (!menu)
|
|
706
|
+
return;
|
|
707
|
+
if (event.key === "Escape") {
|
|
708
|
+
event.preventDefault();
|
|
709
|
+
closeMenu({ hideGrip: true });
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
if (event.key !== "ArrowDown" &&
|
|
713
|
+
event.key !== "ArrowUp" &&
|
|
714
|
+
event.key !== "Home" &&
|
|
715
|
+
event.key !== "End") {
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
const buttons = Array.from(menu.querySelectorAll("button"));
|
|
719
|
+
if (buttons.length === 0)
|
|
720
|
+
return;
|
|
721
|
+
event.preventDefault();
|
|
722
|
+
const activeIndex = buttons.indexOf(document.activeElement);
|
|
723
|
+
let nextIndex = activeIndex < 0 ? 0 : activeIndex;
|
|
724
|
+
if (event.key === "ArrowDown") {
|
|
725
|
+
nextIndex = (nextIndex + 1) % buttons.length;
|
|
726
|
+
}
|
|
727
|
+
else if (event.key === "ArrowUp") {
|
|
728
|
+
nextIndex = (nextIndex - 1 + buttons.length) % buttons.length;
|
|
729
|
+
}
|
|
730
|
+
else if (event.key === "Home") {
|
|
731
|
+
nextIndex = 0;
|
|
732
|
+
}
|
|
733
|
+
else if (event.key === "End") {
|
|
734
|
+
nextIndex = buttons.length - 1;
|
|
735
|
+
}
|
|
736
|
+
buttons[nextIndex]?.focus({ preventScroll: true });
|
|
737
|
+
}
|
|
738
|
+
function handleMenuViewportChange() {
|
|
739
|
+
closeMenu({ hideGrip: true });
|
|
740
|
+
}
|
|
741
|
+
const finishDragSession = (commit, event) => {
|
|
261
742
|
const session = dragSession;
|
|
262
743
|
if (!session)
|
|
263
744
|
return;
|
|
264
745
|
removeDragListeners();
|
|
265
|
-
if (commit && session.dragging && session.
|
|
746
|
+
if (commit && session.dragging && session.dropTarget) {
|
|
266
747
|
const sourceStart = session.sourcePos;
|
|
267
748
|
const sourceEnd = session.sourcePos + session.sourceNodeSize;
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
749
|
+
const target = session.dropTarget;
|
|
750
|
+
const dropPos = target.pos;
|
|
751
|
+
if (target.view !== session.view ||
|
|
752
|
+
(dropPos !== sourceStart &&
|
|
753
|
+
dropPos !== sourceEnd &&
|
|
754
|
+
!(dropPos > sourceStart && dropPos < sourceEnd))) {
|
|
272
755
|
const sourceNode = session.view.state.doc.nodeAt(sourceStart);
|
|
273
756
|
if (sourceNode) {
|
|
274
|
-
const
|
|
275
|
-
const
|
|
276
|
-
.
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
757
|
+
const sourceRegistration = registrationForView(session.view);
|
|
758
|
+
const transferData = sourceRegistration?.getDragTransferData?.({
|
|
759
|
+
view: session.view,
|
|
760
|
+
node: sourceNode,
|
|
761
|
+
pos: sourceStart,
|
|
762
|
+
});
|
|
763
|
+
const targetNode = target.view.state.doc.nodeAt(target.targetPos);
|
|
764
|
+
const handled = !!targetNode &&
|
|
765
|
+
(target.registration.handleDrop?.(transferData, {
|
|
766
|
+
view: target.view,
|
|
767
|
+
sourceView: session.view,
|
|
768
|
+
sourceNode,
|
|
769
|
+
sourcePos: sourceStart,
|
|
770
|
+
sourceNodeSize: sourceNode.nodeSize,
|
|
771
|
+
targetNode,
|
|
772
|
+
targetPos: target.targetPos,
|
|
773
|
+
targetNodeSize: target.targetNodeSize,
|
|
774
|
+
insertPos: dropPos,
|
|
775
|
+
placement: target.placement,
|
|
776
|
+
}) ??
|
|
777
|
+
false);
|
|
778
|
+
if (handled) {
|
|
779
|
+
target.view.focus();
|
|
780
|
+
}
|
|
781
|
+
else if (target.view === session.view) {
|
|
782
|
+
const insertPos = dropPos > sourceStart ? dropPos - sourceNode.nodeSize : dropPos;
|
|
783
|
+
const tr = session.view.state.tr
|
|
784
|
+
.delete(sourceStart, sourceEnd)
|
|
785
|
+
.insert(insertPos, sourceNode);
|
|
786
|
+
tr.setSelection(NodeSelection.create(tr.doc, insertPos));
|
|
787
|
+
session.view.dispatch(tr.scrollIntoView());
|
|
788
|
+
session.view.focus();
|
|
789
|
+
}
|
|
790
|
+
else {
|
|
791
|
+
try {
|
|
792
|
+
const targetNode = target.view.state.schema.nodeFromJSON(sourceNode.toJSON());
|
|
793
|
+
target.registration.receiveDragTransferData?.(transferData, {
|
|
794
|
+
view: target.view,
|
|
795
|
+
node: targetNode,
|
|
796
|
+
pos: dropPos,
|
|
797
|
+
sourceView: session.view,
|
|
798
|
+
});
|
|
799
|
+
const insertTr = target.view.state.tr.insert(dropPos, targetNode);
|
|
800
|
+
insertTr.setSelection(NodeSelection.create(insertTr.doc, dropPos));
|
|
801
|
+
target.view.dispatch(insertTr.scrollIntoView());
|
|
802
|
+
const deleteTr = session.view.state.tr.delete(sourceStart, sourceEnd);
|
|
803
|
+
session.view.dispatch(deleteTr);
|
|
804
|
+
target.view.focus();
|
|
805
|
+
}
|
|
806
|
+
catch {
|
|
807
|
+
// If the target schema cannot accept this node, leave the
|
|
808
|
+
// source document untouched.
|
|
809
|
+
}
|
|
810
|
+
}
|
|
281
811
|
}
|
|
282
812
|
}
|
|
283
813
|
}
|
|
284
|
-
else if (!session.dragging) {
|
|
285
|
-
|
|
814
|
+
else if (commit && !session.dragging && event) {
|
|
815
|
+
openMenu({
|
|
816
|
+
view: session.view,
|
|
817
|
+
sourceBlock: session.sourceBlock,
|
|
818
|
+
sourcePos: session.sourcePos,
|
|
819
|
+
sourceNodeSize: session.sourceNodeSize,
|
|
820
|
+
}, handle?.getBoundingClientRect() ??
|
|
821
|
+
session.sourceBlock.getBoundingClientRect());
|
|
286
822
|
}
|
|
287
823
|
cleanupDragVisuals();
|
|
288
824
|
dragSession = null;
|
|
289
|
-
|
|
825
|
+
if (activeDragRegistration === currentRegistration) {
|
|
826
|
+
activeDragRegistration = null;
|
|
827
|
+
}
|
|
828
|
+
if (session.dragging || !commit)
|
|
829
|
+
hideHandle();
|
|
290
830
|
};
|
|
291
831
|
const beginDragSession = (session, event) => {
|
|
292
832
|
session.dragging = true;
|
|
@@ -294,7 +834,7 @@ export const DragHandle = Extension.create({
|
|
|
294
834
|
session.sourceBlock.classList.add("notion-block--dragging");
|
|
295
835
|
document.documentElement.classList.add("notion-editor-is-dragging");
|
|
296
836
|
positionDragPreview(session, event.clientX, event.clientY);
|
|
297
|
-
updateDropLine(session,
|
|
837
|
+
updateDropLine(session, findAnyDropTarget(session, event.clientX, event.clientY));
|
|
298
838
|
};
|
|
299
839
|
function handleDocumentMouseMove(event) {
|
|
300
840
|
if (!dragSession)
|
|
@@ -307,11 +847,11 @@ export const DragHandle = Extension.create({
|
|
|
307
847
|
if (!dragSession.dragging)
|
|
308
848
|
return;
|
|
309
849
|
positionDragPreview(dragSession, event.clientX, event.clientY);
|
|
310
|
-
updateDropLine(dragSession,
|
|
850
|
+
updateDropLine(dragSession, findAnyDropTarget(dragSession, event.clientX, event.clientY));
|
|
311
851
|
}
|
|
312
852
|
function handleDocumentMouseUp(event) {
|
|
313
853
|
event.preventDefault();
|
|
314
|
-
finishDragSession(true);
|
|
854
|
+
finishDragSession(true, event);
|
|
315
855
|
}
|
|
316
856
|
function handleDocumentKeyDown(event) {
|
|
317
857
|
if (event.key !== "Escape")
|
|
@@ -323,16 +863,31 @@ export const DragHandle = Extension.create({
|
|
|
323
863
|
new Plugin({
|
|
324
864
|
key: dragHandleKey,
|
|
325
865
|
view(editorView) {
|
|
866
|
+
const registration = {
|
|
867
|
+
view: editorView,
|
|
868
|
+
wrapperSelector,
|
|
869
|
+
getDragTransferData,
|
|
870
|
+
receiveDragTransferData,
|
|
871
|
+
handleDrop,
|
|
872
|
+
canHover: () => !!handle && !menu && !dragSession && editor.isEditable,
|
|
873
|
+
findHoverBlock: (clientX, clientY) => findForgivingBlock(editorView, clientX, clientY),
|
|
874
|
+
showHoverBlock: (block) => showHandleForBlock(editorView, block),
|
|
875
|
+
hideHover: () => hideHandle(),
|
|
876
|
+
};
|
|
877
|
+
currentRegistration = registration;
|
|
878
|
+
dragHandleRegistrations.add(registration);
|
|
879
|
+
retainGlobalHoverListener();
|
|
326
880
|
handle = createHandle();
|
|
327
|
-
const handleDocumentHoverMove = createDocumentHoverMove(editorView);
|
|
328
881
|
const wrapper = editorView.dom.closest(wrapperSelector);
|
|
329
882
|
if (wrapper) {
|
|
330
883
|
wrapper.style.position = "relative";
|
|
331
884
|
wrapper.appendChild(handle);
|
|
332
885
|
}
|
|
333
|
-
document.addEventListener("mousemove", handleDocumentHoverMove);
|
|
334
886
|
handle.addEventListener("mousedown", (e) => {
|
|
335
887
|
e.stopPropagation();
|
|
888
|
+
if (e.button !== 0)
|
|
889
|
+
return;
|
|
890
|
+
closeMenu();
|
|
336
891
|
if (!editor.isEditable) {
|
|
337
892
|
e.preventDefault();
|
|
338
893
|
return;
|
|
@@ -353,43 +908,56 @@ export const DragHandle = Extension.create({
|
|
|
353
908
|
dragging: false,
|
|
354
909
|
preview: null,
|
|
355
910
|
dropLine: null,
|
|
356
|
-
|
|
911
|
+
dropTarget: null,
|
|
357
912
|
};
|
|
913
|
+
activeDragRegistration = registration;
|
|
358
914
|
document.addEventListener("mousemove", handleDocumentMouseMove);
|
|
359
915
|
document.addEventListener("mouseup", handleDocumentMouseUp);
|
|
360
916
|
document.addEventListener("keydown", handleDocumentKeyDown);
|
|
361
917
|
});
|
|
918
|
+
handle.addEventListener("keydown", (e) => {
|
|
919
|
+
if (e.key !== "Enter" && e.key !== " ")
|
|
920
|
+
return;
|
|
921
|
+
e.preventDefault();
|
|
922
|
+
e.stopPropagation();
|
|
923
|
+
closeMenu();
|
|
924
|
+
if (!editor.isEditable || !currentBlock || dragStartPos === null) {
|
|
925
|
+
return;
|
|
926
|
+
}
|
|
927
|
+
const sourceNode = editorView.state.doc.nodeAt(dragStartPos);
|
|
928
|
+
if (!sourceNode)
|
|
929
|
+
return;
|
|
930
|
+
openMenu({
|
|
931
|
+
view: editorView,
|
|
932
|
+
sourceBlock: currentBlock,
|
|
933
|
+
sourcePos: dragStartPos,
|
|
934
|
+
sourceNodeSize: sourceNode.nodeSize,
|
|
935
|
+
}, handle?.getBoundingClientRect() ??
|
|
936
|
+
currentBlock.getBoundingClientRect());
|
|
937
|
+
});
|
|
362
938
|
return {
|
|
363
939
|
destroy() {
|
|
364
|
-
|
|
940
|
+
closeMenu({ hideGrip: true });
|
|
365
941
|
finishDragSession(false);
|
|
942
|
+
releaseGlobalHoverListener();
|
|
943
|
+
dragHandleRegistrations.delete(registration);
|
|
944
|
+
if (activeDragRegistration === registration) {
|
|
945
|
+
activeDragRegistration = null;
|
|
946
|
+
}
|
|
366
947
|
handle?.remove();
|
|
367
948
|
handle = null;
|
|
949
|
+
currentRegistration = null;
|
|
368
950
|
},
|
|
369
951
|
};
|
|
370
952
|
},
|
|
371
953
|
props: {
|
|
372
954
|
handleDOMEvents: {
|
|
373
|
-
mousemove(
|
|
374
|
-
|
|
375
|
-
return false;
|
|
376
|
-
if (!editor.isEditable) {
|
|
377
|
-
hideHandle();
|
|
378
|
-
return false;
|
|
379
|
-
}
|
|
380
|
-
if (dragSession)
|
|
381
|
-
return false;
|
|
382
|
-
const block = findForgivingBlock(view, event.clientX, event.clientY);
|
|
383
|
-
if (!block) {
|
|
384
|
-
hideHandle();
|
|
385
|
-
return false;
|
|
386
|
-
}
|
|
387
|
-
if (block.node === currentBlock)
|
|
388
|
-
return false;
|
|
389
|
-
showHandleForBlock(view, block);
|
|
955
|
+
mousemove(_view, event) {
|
|
956
|
+
updateRegisteredHover(event.clientX, event.clientY);
|
|
390
957
|
return false;
|
|
391
958
|
},
|
|
392
959
|
drop() {
|
|
960
|
+
closeMenu({ hideGrip: true });
|
|
393
961
|
finishDragSession(false);
|
|
394
962
|
hideHandle();
|
|
395
963
|
return false;
|