@agent-native/toolkit 0.5.1 → 0.7.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 +17 -1
- package/dist/chat-history/ChatHistoryList.d.ts +103 -0
- package/dist/chat-history/ChatHistoryList.d.ts.map +1 -0
- package/dist/chat-history/ChatHistoryList.js +109 -0
- package/dist/chat-history/ChatHistoryList.js.map +1 -0
- package/dist/chat-history/ChatHistoryList.spec.d.ts +2 -0
- package/dist/chat-history/ChatHistoryList.spec.d.ts.map +1 -0
- package/dist/chat-history/ChatHistoryList.spec.js +283 -0
- package/dist/chat-history/ChatHistoryList.spec.js.map +1 -0
- package/dist/chat-history/index.d.ts +2 -0
- package/dist/chat-history/index.d.ts.map +1 -0
- package/dist/chat-history/index.js +2 -0
- package/dist/chat-history/index.js.map +1 -0
- package/dist/chat-history.css +314 -0
- package/dist/composer/AgentComposerFrame.d.ts +19 -0
- package/dist/composer/AgentComposerFrame.d.ts.map +1 -0
- package/dist/composer/AgentComposerFrame.js +14 -0
- package/dist/composer/AgentComposerFrame.js.map +1 -0
- package/dist/composer/ComposerPlusMenu.d.ts +17 -0
- package/dist/composer/ComposerPlusMenu.d.ts.map +1 -0
- package/dist/composer/ComposerPlusMenu.js +501 -0
- package/dist/composer/ComposerPlusMenu.js.map +1 -0
- package/dist/composer/MentionPopover.d.ts +29 -0
- package/dist/composer/MentionPopover.d.ts.map +1 -0
- package/dist/composer/MentionPopover.js +180 -0
- package/dist/composer/MentionPopover.js.map +1 -0
- package/dist/composer/PastedTextChip.d.ts +9 -0
- package/dist/composer/PastedTextChip.d.ts.map +1 -0
- package/dist/composer/PastedTextChip.js +47 -0
- package/dist/composer/PastedTextChip.js.map +1 -0
- package/dist/composer/PromptComposer.d.ts +114 -0
- package/dist/composer/PromptComposer.d.ts.map +1 -0
- package/dist/composer/PromptComposer.js +302 -0
- package/dist/composer/PromptComposer.js.map +1 -0
- package/dist/composer/PromptComposer.spec.d.ts +2 -0
- package/dist/composer/PromptComposer.spec.d.ts.map +1 -0
- package/dist/composer/PromptComposer.spec.js +64 -0
- package/dist/composer/PromptComposer.spec.js.map +1 -0
- package/dist/composer/RealtimeVoiceMode.d.ts +104 -0
- package/dist/composer/RealtimeVoiceMode.d.ts.map +1 -0
- package/dist/composer/RealtimeVoiceMode.js +245 -0
- package/dist/composer/RealtimeVoiceMode.js.map +1 -0
- package/dist/composer/RealtimeVoiceMode.spec.d.ts +2 -0
- package/dist/composer/RealtimeVoiceMode.spec.d.ts.map +1 -0
- package/dist/composer/RealtimeVoiceMode.spec.js +439 -0
- package/dist/composer/RealtimeVoiceMode.spec.js.map +1 -0
- package/dist/composer/TiptapComposer.d.ts +221 -0
- package/dist/composer/TiptapComposer.d.ts.map +1 -0
- package/dist/composer/TiptapComposer.js +1838 -0
- package/dist/composer/TiptapComposer.js.map +1 -0
- package/dist/composer/TiptapComposer.spec.d.ts +2 -0
- package/dist/composer/TiptapComposer.spec.d.ts.map +1 -0
- package/dist/composer/TiptapComposer.spec.js +277 -0
- package/dist/composer/TiptapComposer.spec.js.map +1 -0
- package/dist/composer/VoiceButton.d.ts +25 -0
- package/dist/composer/VoiceButton.d.ts.map +1 -0
- package/dist/composer/VoiceButton.js +100 -0
- package/dist/composer/VoiceButton.js.map +1 -0
- package/dist/composer/VoiceButton.spec.d.ts +2 -0
- package/dist/composer/VoiceButton.spec.d.ts.map +1 -0
- package/dist/composer/VoiceButton.spec.js +19 -0
- package/dist/composer/VoiceButton.spec.js.map +1 -0
- package/dist/composer/asset-picker-url.d.ts +16 -0
- package/dist/composer/asset-picker-url.d.ts.map +1 -0
- package/dist/composer/asset-picker-url.js +64 -0
- package/dist/composer/asset-picker-url.js.map +1 -0
- package/dist/composer/asset-picker-url.spec.d.ts +2 -0
- package/dist/composer/asset-picker-url.spec.d.ts.map +1 -0
- package/dist/composer/asset-picker-url.spec.js +23 -0
- package/dist/composer/asset-picker-url.spec.js.map +1 -0
- package/dist/composer/attachment-accept.d.ts +9 -0
- package/dist/composer/attachment-accept.d.ts.map +1 -0
- package/dist/composer/attachment-accept.js +65 -0
- package/dist/composer/attachment-accept.js.map +1 -0
- package/dist/composer/attachment-accept.spec.d.ts +2 -0
- package/dist/composer/attachment-accept.spec.d.ts.map +1 -0
- package/dist/composer/attachment-accept.spec.js +15 -0
- package/dist/composer/attachment-accept.spec.js.map +1 -0
- package/dist/composer/draft-key.d.ts +2 -0
- package/dist/composer/draft-key.d.ts.map +1 -0
- package/dist/composer/draft-key.js +8 -0
- package/dist/composer/draft-key.js.map +1 -0
- package/dist/composer/draft-key.spec.d.ts +2 -0
- package/dist/composer/draft-key.spec.d.ts.map +1 -0
- package/dist/composer/draft-key.spec.js +13 -0
- package/dist/composer/draft-key.spec.js.map +1 -0
- package/dist/composer/extensions/FileReference.d.ts +3 -0
- package/dist/composer/extensions/FileReference.d.ts.map +1 -0
- package/dist/composer/extensions/FileReference.js +36 -0
- package/dist/composer/extensions/FileReference.js.map +1 -0
- package/dist/composer/extensions/MentionReference.d.ts +3 -0
- package/dist/composer/extensions/MentionReference.d.ts.map +1 -0
- package/dist/composer/extensions/MentionReference.js +66 -0
- package/dist/composer/extensions/MentionReference.js.map +1 -0
- package/dist/composer/extensions/SkillReference.d.ts +3 -0
- package/dist/composer/extensions/SkillReference.d.ts.map +1 -0
- package/dist/composer/extensions/SkillReference.js +40 -0
- package/dist/composer/extensions/SkillReference.js.map +1 -0
- package/dist/composer/index.d.ts +14 -0
- package/dist/composer/index.d.ts.map +1 -0
- package/dist/composer/index.js +13 -0
- package/dist/composer/index.js.map +1 -0
- package/dist/composer/pasted-text.d.ts +31 -0
- package/dist/composer/pasted-text.d.ts.map +1 -0
- package/dist/composer/pasted-text.js +130 -0
- package/dist/composer/pasted-text.js.map +1 -0
- package/dist/composer/pasted-text.spec.d.ts +2 -0
- package/dist/composer/pasted-text.spec.d.ts.map +1 -0
- package/dist/composer/pasted-text.spec.js +107 -0
- package/dist/composer/pasted-text.spec.js.map +1 -0
- package/dist/composer/prompt-attachments.d.ts +18 -0
- package/dist/composer/prompt-attachments.d.ts.map +1 -0
- package/dist/composer/prompt-attachments.js +65 -0
- package/dist/composer/prompt-attachments.js.map +1 -0
- package/dist/composer/prompt-attachments.spec.d.ts +2 -0
- package/dist/composer/prompt-attachments.spec.d.ts.map +1 -0
- package/dist/composer/prompt-attachments.spec.js +63 -0
- package/dist/composer/prompt-attachments.spec.js.map +1 -0
- package/dist/composer/realtime-voice-audio-level.d.ts +14 -0
- package/dist/composer/realtime-voice-audio-level.d.ts.map +1 -0
- package/dist/composer/realtime-voice-audio-level.js +47 -0
- package/dist/composer/realtime-voice-audio-level.js.map +1 -0
- package/dist/composer/realtime-voice-audio-level.spec.d.ts +2 -0
- package/dist/composer/realtime-voice-audio-level.spec.d.ts.map +1 -0
- package/dist/composer/realtime-voice-audio-level.spec.js +25 -0
- package/dist/composer/realtime-voice-audio-level.spec.js.map +1 -0
- package/dist/composer/realtime-voice-transcript.d.ts +26 -0
- package/dist/composer/realtime-voice-transcript.d.ts.map +1 -0
- package/dist/composer/realtime-voice-transcript.js +103 -0
- package/dist/composer/realtime-voice-transcript.js.map +1 -0
- package/dist/composer/realtime-voice-transcript.spec.d.ts +2 -0
- package/dist/composer/realtime-voice-transcript.spec.d.ts.map +1 -0
- package/dist/composer/realtime-voice-transcript.spec.js +76 -0
- package/dist/composer/realtime-voice-transcript.spec.js.map +1 -0
- package/dist/composer/runtime-adapters.d.ts +163 -0
- package/dist/composer/runtime-adapters.d.ts.map +1 -0
- package/dist/composer/runtime-adapters.js +130 -0
- package/dist/composer/runtime-adapters.js.map +1 -0
- package/dist/composer/types.d.ts +60 -0
- package/dist/composer/types.d.ts.map +1 -0
- package/dist/composer/types.js +2 -0
- package/dist/composer/types.js.map +1 -0
- package/dist/composer/use-file-search.d.ts +6 -0
- package/dist/composer/use-file-search.d.ts.map +1 -0
- package/dist/composer/use-file-search.js +53 -0
- package/dist/composer/use-file-search.js.map +1 -0
- package/dist/composer/use-mention-search.d.ts +6 -0
- package/dist/composer/use-mention-search.d.ts.map +1 -0
- package/dist/composer/use-mention-search.js +75 -0
- package/dist/composer/use-mention-search.js.map +1 -0
- package/dist/composer/use-skills.d.ts +7 -0
- package/dist/composer/use-skills.d.ts.map +1 -0
- package/dist/composer/use-skills.js +40 -0
- package/dist/composer/use-skills.js.map +1 -0
- package/dist/composer/useRealtimeVoiceMode.d.ts +179 -0
- package/dist/composer/useRealtimeVoiceMode.d.ts.map +1 -0
- package/dist/composer/useRealtimeVoiceMode.js +1657 -0
- package/dist/composer/useRealtimeVoiceMode.js.map +1 -0
- package/dist/composer/useRealtimeVoiceMode.spec.d.ts +2 -0
- package/dist/composer/useRealtimeVoiceMode.spec.d.ts.map +1 -0
- package/dist/composer/useRealtimeVoiceMode.spec.js +618 -0
- package/dist/composer/useRealtimeVoiceMode.spec.js.map +1 -0
- package/dist/composer/useVoiceDictation.d.ts +57 -0
- package/dist/composer/useVoiceDictation.d.ts.map +1 -0
- package/dist/composer/useVoiceDictation.js +1004 -0
- package/dist/composer/useVoiceDictation.js.map +1 -0
- package/dist/composer/useVoiceDictation.spec.d.ts +2 -0
- package/dist/composer/useVoiceDictation.spec.d.ts.map +1 -0
- package/dist/composer/useVoiceDictation.spec.js +32 -0
- package/dist/composer/useVoiceDictation.spec.js.map +1 -0
- package/dist/editor/RegistryBlockContext.d.ts +68 -0
- package/dist/editor/RegistryBlockContext.d.ts.map +1 -0
- package/dist/editor/RegistryBlockContext.js +10 -0
- package/dist/editor/RegistryBlockContext.js.map +1 -0
- package/dist/editor/RegistryBlockNode.d.ts +66 -0
- package/dist/editor/RegistryBlockNode.d.ts.map +1 -0
- package/dist/editor/RegistryBlockNode.js +406 -0
- package/dist/editor/RegistryBlockNode.js.map +1 -0
- package/dist/editor/RegistryBlockNode.spec.d.ts +2 -0
- package/dist/editor/RegistryBlockNode.spec.d.ts.map +1 -0
- package/dist/editor/RegistryBlockNode.spec.js +215 -0
- package/dist/editor/RegistryBlockNode.spec.js.map +1 -0
- package/dist/editor/index.d.ts +2 -0
- package/dist/editor/index.d.ts.map +1 -1
- package/dist/editor/index.js +2 -0
- package/dist/editor/index.js.map +1 -1
- package/dist/editor/registrySlashCommands.d.ts +68 -0
- package/dist/editor/registrySlashCommands.d.ts.map +1 -0
- package/dist/editor/registrySlashCommands.js +51 -0
- package/dist/editor/registrySlashCommands.js.map +1 -0
- package/package.json +32 -1
- package/src/app-shell/header-actions.tsx +84 -0
- package/src/app-shell/index.ts +1 -0
- package/src/chat-history/ChatHistoryList.spec.tsx +453 -0
- package/src/chat-history/ChatHistoryList.tsx +469 -0
- package/src/chat-history/index.ts +7 -0
- package/src/chat-history.css +314 -0
- package/src/collab-ui/AgentPresenceChip.tsx +69 -0
- package/src/collab-ui/LiveCursorOverlay.tsx +241 -0
- package/src/collab-ui/PresenceBar.tsx +346 -0
- package/src/collab-ui/RecentEditHighlights.tsx +225 -0
- package/src/collab-ui/RemoteSelectionRings.tsx +227 -0
- package/src/collab-ui/agent-identity.ts +16 -0
- package/src/collab-ui/index.ts +39 -0
- package/src/collab-ui/lead-client.ts +32 -0
- package/src/collab-ui/types.spec.ts +18 -0
- package/src/collab-ui/types.ts +96 -0
- package/src/composer/AgentComposerFrame.tsx +59 -0
- package/src/composer/ComposerPlusMenu.tsx +983 -0
- package/src/composer/MentionPopover.tsx +412 -0
- package/src/composer/PastedTextChip.tsx +150 -0
- package/src/composer/PromptComposer.spec.ts +73 -0
- package/src/composer/PromptComposer.tsx +718 -0
- package/src/composer/RealtimeVoiceMode.spec.tsx +771 -0
- package/src/composer/RealtimeVoiceMode.tsx +856 -0
- package/src/composer/TiptapComposer.spec.ts +390 -0
- package/src/composer/TiptapComposer.tsx +2936 -0
- package/src/composer/VoiceButton.spec.ts +33 -0
- package/src/composer/VoiceButton.tsx +255 -0
- package/src/composer/asset-picker-url.spec.ts +56 -0
- package/src/composer/asset-picker-url.ts +80 -0
- package/src/composer/attachment-accept.spec.ts +22 -0
- package/src/composer/attachment-accept.ts +71 -0
- package/src/composer/draft-key.spec.ts +19 -0
- package/src/composer/draft-key.ts +7 -0
- package/src/composer/extensions/FileReference.tsx +55 -0
- package/src/composer/extensions/MentionReference.tsx +93 -0
- package/src/composer/extensions/SkillReference.tsx +56 -0
- package/src/composer/index.ts +79 -0
- package/src/composer/pasted-text.spec.ts +162 -0
- package/src/composer/pasted-text.ts +160 -0
- package/src/composer/prompt-attachments.spec.ts +80 -0
- package/src/composer/prompt-attachments.ts +93 -0
- package/src/composer/realtime-voice-audio-level.spec.ts +32 -0
- package/src/composer/realtime-voice-audio-level.ts +64 -0
- package/src/composer/realtime-voice-transcript.spec.ts +95 -0
- package/src/composer/realtime-voice-transcript.ts +134 -0
- package/src/composer/runtime-adapters.tsx +343 -0
- package/src/composer/types.ts +66 -0
- package/src/composer/use-file-search.ts +64 -0
- package/src/composer/use-mention-search.ts +90 -0
- package/src/composer/use-skills.ts +45 -0
- package/src/composer/useRealtimeVoiceMode.spec.ts +818 -0
- package/src/composer/useRealtimeVoiceMode.tsx +2117 -0
- package/src/composer/useVoiceDictation.spec.ts +45 -0
- package/src/composer/useVoiceDictation.ts +1191 -0
- package/src/context-ui/ContextMeter.tsx +101 -0
- package/src/context-ui/ContextSegmentRow.tsx +184 -0
- package/src/context-ui/ContextTreemap.tsx +125 -0
- package/src/context-ui/ContextXRayPanel.tsx +377 -0
- package/src/context-ui/format.ts +42 -0
- package/src/context-ui/index.ts +15 -0
- package/src/context-ui/types.ts +43 -0
- package/src/design-tweaks/index.ts +1 -0
- package/src/design-tweaks/visual-style-controls.tsx +1084 -0
- package/src/editor/BubbleToolbar.tsx +253 -0
- package/src/editor/CodeBlockNode.tsx +254 -0
- package/src/editor/DragHandle.spec.ts +588 -0
- package/src/editor/DragHandle.ts +1437 -0
- package/src/editor/ImageExtension.roundtrip.spec.ts +117 -0
- package/src/editor/ImageExtension.ts +306 -0
- package/src/editor/RegistryBlockContext.tsx +107 -0
- package/src/editor/RegistryBlockNode.spec.ts +279 -0
- package/src/editor/RegistryBlockNode.tsx +652 -0
- package/src/editor/RichMarkdownEditor.collab.spec.ts +79 -0
- package/src/editor/RichMarkdownEditor.roundtrip.spec.ts +226 -0
- package/src/editor/RichMarkdownEditor.tsx +81 -0
- package/src/editor/RunId.ts +61 -0
- package/src/editor/SharedRichEditor.tsx +321 -0
- package/src/editor/SlashCommandMenu.tsx +310 -0
- package/src/editor/disableHistory.spec.ts +85 -0
- package/src/editor/extensions.ts +402 -0
- package/src/editor/gfmDoc.spec.ts +230 -0
- package/src/editor/gfmDoc.ts +92 -0
- package/src/editor/index.ts +93 -0
- package/src/editor/registrySlashCommands.ts +131 -0
- package/src/editor/structuralReorderUndo.spec.ts +144 -0
- package/src/editor/surgical-apply.spec.ts +279 -0
- package/src/editor/surgical-apply.ts +192 -0
- package/src/editor/useCollabReconcile.concurrent.spec.ts +422 -0
- package/src/editor/useCollabReconcile.idempotent.spec.ts +324 -0
- package/src/editor/useCollabReconcile.ts +589 -0
- package/src/editor.css +510 -0
- package/src/hooks/index.ts +2 -0
- package/src/hooks/use-mobile.tsx +21 -0
- package/src/hooks/use-toast.ts +188 -0
- package/src/index.ts +8 -0
- package/src/onboarding/index.ts +1 -0
- package/src/provider.tsx +47 -0
- package/src/sharing/VisibilityBadge.spec.ts +96 -0
- package/src/sharing/VisibilityBadge.tsx +37 -0
- package/src/sharing/index.ts +4 -0
- package/src/styles.css +21 -0
- package/src/ui/accordion.tsx +56 -0
- package/src/ui/alert-dialog.tsx +141 -0
- package/src/ui/alert.tsx +59 -0
- package/src/ui/aspect-ratio.tsx +5 -0
- package/src/ui/avatar.tsx +48 -0
- package/src/ui/badge.tsx +37 -0
- package/src/ui/breadcrumb.tsx +115 -0
- package/src/ui/button.tsx +78 -0
- package/src/ui/calendar.tsx +214 -0
- package/src/ui/card.tsx +86 -0
- package/src/ui/carousel.tsx +260 -0
- package/src/ui/chart.tsx +375 -0
- package/src/ui/checkbox.tsx +28 -0
- package/src/ui/collapsible.tsx +9 -0
- package/src/ui/command.spec.tsx +51 -0
- package/src/ui/command.tsx +174 -0
- package/src/ui/context-menu.tsx +202 -0
- package/src/ui/date-picker.tsx +64 -0
- package/src/ui/dialog.spec.tsx +43 -0
- package/src/ui/dialog.tsx +146 -0
- package/src/ui/drawer.tsx +126 -0
- package/src/ui/dropdown-menu.tsx +218 -0
- package/src/ui/form.tsx +177 -0
- package/src/ui/hover-card.tsx +27 -0
- package/src/ui/index.ts +52 -0
- package/src/ui/input-otp.tsx +69 -0
- package/src/ui/input.tsx +22 -0
- package/src/ui/label.tsx +24 -0
- package/src/ui/logo.tsx +36 -0
- package/src/ui/menubar.tsx +235 -0
- package/src/ui/navigation-menu.tsx +128 -0
- package/src/ui/pagination.tsx +121 -0
- package/src/ui/popover.tsx +44 -0
- package/src/ui/progress.tsx +26 -0
- package/src/ui/radio-group.tsx +42 -0
- package/src/ui/resizable.tsx +43 -0
- package/src/ui/scroll-area.tsx +46 -0
- package/src/ui/select.tsx +158 -0
- package/src/ui/separator.tsx +29 -0
- package/src/ui/sheet.tsx +160 -0
- package/src/ui/sidebar.tsx +777 -0
- package/src/ui/skeleton.tsx +18 -0
- package/src/ui/slider.tsx +26 -0
- package/src/ui/sonner.tsx +58 -0
- package/src/ui/spinner.tsx +17 -0
- package/src/ui/switch.tsx +27 -0
- package/src/ui/table.tsx +117 -0
- package/src/ui/tabs.tsx +53 -0
- package/src/ui/textarea.tsx +23 -0
- package/src/ui/toast.tsx +127 -0
- package/src/ui/toaster.tsx +33 -0
- package/src/ui/toggle-group.tsx +59 -0
- package/src/ui/toggle.tsx +43 -0
- package/src/ui/tooltip.tsx +53 -0
- package/src/ui/use-toast.ts +3 -0
- package/src/utils.ts +8 -0
|
@@ -0,0 +1,588 @@
|
|
|
1
|
+
// @vitest-environment happy-dom
|
|
2
|
+
|
|
3
|
+
import { Editor } from "@tiptap/core";
|
|
4
|
+
import StarterKit from "@tiptap/starter-kit";
|
|
5
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
6
|
+
|
|
7
|
+
import { DragHandle, type DragHandleOptions } from "./DragHandle.js";
|
|
8
|
+
|
|
9
|
+
function makeRect({
|
|
10
|
+
left,
|
|
11
|
+
top,
|
|
12
|
+
width,
|
|
13
|
+
height,
|
|
14
|
+
}: {
|
|
15
|
+
left: number;
|
|
16
|
+
top: number;
|
|
17
|
+
width: number;
|
|
18
|
+
height: number;
|
|
19
|
+
}): DOMRect {
|
|
20
|
+
return {
|
|
21
|
+
x: left,
|
|
22
|
+
y: top,
|
|
23
|
+
left,
|
|
24
|
+
top,
|
|
25
|
+
width,
|
|
26
|
+
height,
|
|
27
|
+
right: left + width,
|
|
28
|
+
bottom: top + height,
|
|
29
|
+
toJSON: () => ({}),
|
|
30
|
+
} as DOMRect;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function setRect(element: Element, rect: DOMRect): void {
|
|
34
|
+
Object.defineProperty(element, "getBoundingClientRect", {
|
|
35
|
+
configurable: true,
|
|
36
|
+
value: () => rect,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function mountEditor(
|
|
41
|
+
content: string,
|
|
42
|
+
options: Partial<DragHandleOptions> = {},
|
|
43
|
+
rect: DOMRect = makeRect({ left: 24, top: 0, width: 640, height: 400 }),
|
|
44
|
+
parent: HTMLElement = document.body,
|
|
45
|
+
hoverPoint: { x: number; y: number } = { x: 32, y: rect.top + 12 },
|
|
46
|
+
): {
|
|
47
|
+
editor: Editor;
|
|
48
|
+
wrapper: HTMLElement;
|
|
49
|
+
handle: HTMLElement;
|
|
50
|
+
} {
|
|
51
|
+
const wrapper = document.createElement("div");
|
|
52
|
+
wrapper.className = "visual-editor-wrapper";
|
|
53
|
+
setRect(wrapper, rect);
|
|
54
|
+
parent.appendChild(wrapper);
|
|
55
|
+
|
|
56
|
+
const element = document.createElement("div");
|
|
57
|
+
wrapper.appendChild(element);
|
|
58
|
+
|
|
59
|
+
const editor = new Editor({
|
|
60
|
+
element,
|
|
61
|
+
extensions: [StarterKit, DragHandle.configure(options)],
|
|
62
|
+
content,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
setRect(editor.view.dom, rect);
|
|
66
|
+
|
|
67
|
+
let index = 0;
|
|
68
|
+
editor.state.doc.forEach((_node, offset) => {
|
|
69
|
+
const dom = editor.view.nodeDOM(offset);
|
|
70
|
+
if (dom instanceof HTMLElement) {
|
|
71
|
+
setRect(
|
|
72
|
+
dom,
|
|
73
|
+
makeRect({
|
|
74
|
+
left: 24,
|
|
75
|
+
top: rect.top + index * 40,
|
|
76
|
+
width: 640,
|
|
77
|
+
height: 24,
|
|
78
|
+
}),
|
|
79
|
+
);
|
|
80
|
+
index += 1;
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
document.dispatchEvent(
|
|
85
|
+
new MouseEvent("mousemove", {
|
|
86
|
+
bubbles: true,
|
|
87
|
+
clientX: hoverPoint.x,
|
|
88
|
+
clientY: hoverPoint.y,
|
|
89
|
+
}),
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const handle = wrapper.querySelector<HTMLElement>(".drag-handle");
|
|
93
|
+
if (!handle) throw new Error("Expected drag handle to mount");
|
|
94
|
+
setRect(
|
|
95
|
+
handle,
|
|
96
|
+
makeRect({ left: 0, top: rect.top + 2, width: 24, height: 24 }),
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
return { editor, wrapper, handle };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function hoverAt(x: number, y: number): void {
|
|
103
|
+
document.dispatchEvent(
|
|
104
|
+
new MouseEvent("mousemove", {
|
|
105
|
+
bubbles: true,
|
|
106
|
+
clientX: x,
|
|
107
|
+
clientY: y,
|
|
108
|
+
}),
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function clickHandle(handle: HTMLElement): void {
|
|
113
|
+
handle.dispatchEvent(
|
|
114
|
+
new MouseEvent("mousedown", {
|
|
115
|
+
bubbles: true,
|
|
116
|
+
button: 0,
|
|
117
|
+
clientX: 12,
|
|
118
|
+
clientY: 12,
|
|
119
|
+
}),
|
|
120
|
+
);
|
|
121
|
+
document.dispatchEvent(
|
|
122
|
+
new MouseEvent("mouseup", {
|
|
123
|
+
bubbles: true,
|
|
124
|
+
button: 0,
|
|
125
|
+
clientX: 12,
|
|
126
|
+
clientY: 12,
|
|
127
|
+
}),
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function getMenuItems(): HTMLButtonElement[] {
|
|
132
|
+
return Array.from(
|
|
133
|
+
document.querySelectorAll<HTMLButtonElement>(".an-rich-md-drag-menu__item"),
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function clickMenuItem(label: string): void {
|
|
138
|
+
const item = getMenuItems().find((button) =>
|
|
139
|
+
button.textContent?.includes(label),
|
|
140
|
+
);
|
|
141
|
+
if (!item) throw new Error(`Expected menu item "${label}"`);
|
|
142
|
+
item.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function childText(editor: Editor, index: number): string {
|
|
146
|
+
return editor.state.doc.child(index).textContent;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
afterEach(() => {
|
|
150
|
+
document.body.innerHTML = "";
|
|
151
|
+
document
|
|
152
|
+
.querySelectorAll("#an-rich-md-drag-menu-styles")
|
|
153
|
+
.forEach((node) => node.remove());
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
describe("DragHandle menu", () => {
|
|
157
|
+
it("opens the block menu on a single click", () => {
|
|
158
|
+
const { editor, handle } = mountEditor("<p>First</p><p>Second</p>");
|
|
159
|
+
|
|
160
|
+
try {
|
|
161
|
+
clickHandle(handle);
|
|
162
|
+
|
|
163
|
+
expect(getMenuItems().map((item) => item.textContent)).toEqual([
|
|
164
|
+
"Duplicate",
|
|
165
|
+
"Delete",
|
|
166
|
+
"Insert block below",
|
|
167
|
+
]);
|
|
168
|
+
} finally {
|
|
169
|
+
editor.destroy();
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it("inserts an empty focused paragraph below the current block", () => {
|
|
174
|
+
const { editor, handle } = mountEditor("<p>First</p><p>Second</p>");
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
clickHandle(handle);
|
|
178
|
+
clickMenuItem("Insert block below");
|
|
179
|
+
|
|
180
|
+
expect(editor.state.doc.childCount).toBe(3);
|
|
181
|
+
expect(childText(editor, 0)).toBe("First");
|
|
182
|
+
expect(childText(editor, 1)).toBe("");
|
|
183
|
+
expect(childText(editor, 2)).toBe("Second");
|
|
184
|
+
expect(editor.state.selection.$from.parent.type.name).toBe("paragraph");
|
|
185
|
+
expect(editor.state.selection.$from.parent.textContent).toBe("");
|
|
186
|
+
} finally {
|
|
187
|
+
editor.destroy();
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it("duplicates and deletes the current block from the menu", () => {
|
|
192
|
+
const { editor, handle } = mountEditor("<p>First</p><p>Second</p>");
|
|
193
|
+
|
|
194
|
+
try {
|
|
195
|
+
clickHandle(handle);
|
|
196
|
+
clickMenuItem("Duplicate");
|
|
197
|
+
|
|
198
|
+
expect(editor.state.doc.childCount).toBe(3);
|
|
199
|
+
expect(childText(editor, 0)).toBe("First");
|
|
200
|
+
expect(childText(editor, 1)).toBe("First");
|
|
201
|
+
expect(childText(editor, 2)).toBe("Second");
|
|
202
|
+
|
|
203
|
+
document.dispatchEvent(
|
|
204
|
+
new MouseEvent("mousemove", {
|
|
205
|
+
bubbles: true,
|
|
206
|
+
clientX: 32,
|
|
207
|
+
clientY: 12,
|
|
208
|
+
}),
|
|
209
|
+
);
|
|
210
|
+
clickHandle(handle);
|
|
211
|
+
clickMenuItem("Delete");
|
|
212
|
+
|
|
213
|
+
expect(editor.state.doc.childCount).toBe(2);
|
|
214
|
+
expect(childText(editor, 0)).toBe("First");
|
|
215
|
+
expect(childText(editor, 1)).toBe("Second");
|
|
216
|
+
} finally {
|
|
217
|
+
editor.destroy();
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it("keeps drag-to-reorder behavior when the handle is moved", () => {
|
|
222
|
+
const { editor, handle } = mountEditor("<p>First</p><p>Second</p>");
|
|
223
|
+
|
|
224
|
+
try {
|
|
225
|
+
handle.dispatchEvent(
|
|
226
|
+
new MouseEvent("mousedown", {
|
|
227
|
+
bubbles: true,
|
|
228
|
+
button: 0,
|
|
229
|
+
clientX: 12,
|
|
230
|
+
clientY: 12,
|
|
231
|
+
}),
|
|
232
|
+
);
|
|
233
|
+
document.dispatchEvent(
|
|
234
|
+
new MouseEvent("mousemove", {
|
|
235
|
+
bubbles: true,
|
|
236
|
+
clientX: 12,
|
|
237
|
+
clientY: 56,
|
|
238
|
+
}),
|
|
239
|
+
);
|
|
240
|
+
document.dispatchEvent(
|
|
241
|
+
new MouseEvent("mouseup", {
|
|
242
|
+
bubbles: true,
|
|
243
|
+
button: 0,
|
|
244
|
+
clientX: 12,
|
|
245
|
+
clientY: 56,
|
|
246
|
+
}),
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
expect(document.querySelector(".an-rich-md-drag-menu")).toBeNull();
|
|
250
|
+
expect(editor.state.doc.childCount).toBe(2);
|
|
251
|
+
expect(childText(editor, 0)).toBe("Second");
|
|
252
|
+
expect(childText(editor, 1)).toBe("First");
|
|
253
|
+
} finally {
|
|
254
|
+
editor.destroy();
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it("moves a block across registered editor regions and passes transfer payloads", () => {
|
|
259
|
+
const transferData = { blockId: "source-block-1", extra: "side-map data" };
|
|
260
|
+
const getDragTransferData = vi.fn(() => transferData);
|
|
261
|
+
const receiveDragTransferData = vi.fn();
|
|
262
|
+
const source = mountEditor(
|
|
263
|
+
"<p>Move me</p><p>Keep source</p>",
|
|
264
|
+
{ getDragTransferData },
|
|
265
|
+
makeRect({ left: 24, top: 0, width: 640, height: 120 }),
|
|
266
|
+
);
|
|
267
|
+
const target = mountEditor(
|
|
268
|
+
"<p>Target first</p><p>Target second</p>",
|
|
269
|
+
{ receiveDragTransferData },
|
|
270
|
+
makeRect({ left: 24, top: 160, width: 640, height: 120 }),
|
|
271
|
+
);
|
|
272
|
+
|
|
273
|
+
try {
|
|
274
|
+
hoverAt(32, 12);
|
|
275
|
+
source.handle.dispatchEvent(
|
|
276
|
+
new MouseEvent("mousedown", {
|
|
277
|
+
bubbles: true,
|
|
278
|
+
button: 0,
|
|
279
|
+
clientX: 12,
|
|
280
|
+
clientY: 12,
|
|
281
|
+
}),
|
|
282
|
+
);
|
|
283
|
+
document.dispatchEvent(
|
|
284
|
+
new MouseEvent("mousemove", {
|
|
285
|
+
bubbles: true,
|
|
286
|
+
clientX: 32,
|
|
287
|
+
clientY: 164,
|
|
288
|
+
}),
|
|
289
|
+
);
|
|
290
|
+
document.dispatchEvent(
|
|
291
|
+
new MouseEvent("mouseup", {
|
|
292
|
+
bubbles: true,
|
|
293
|
+
button: 0,
|
|
294
|
+
clientX: 32,
|
|
295
|
+
clientY: 164,
|
|
296
|
+
}),
|
|
297
|
+
);
|
|
298
|
+
|
|
299
|
+
expect(getDragTransferData).toHaveBeenCalledTimes(1);
|
|
300
|
+
expect(getDragTransferData).toHaveBeenCalledWith({
|
|
301
|
+
view: source.editor.view,
|
|
302
|
+
node: expect.objectContaining({ textContent: "Move me" }),
|
|
303
|
+
pos: 0,
|
|
304
|
+
});
|
|
305
|
+
expect(receiveDragTransferData).toHaveBeenCalledTimes(1);
|
|
306
|
+
expect(receiveDragTransferData).toHaveBeenCalledWith(transferData, {
|
|
307
|
+
view: target.editor.view,
|
|
308
|
+
node: expect.objectContaining({ textContent: "Move me" }),
|
|
309
|
+
pos: 0,
|
|
310
|
+
sourceView: source.editor.view,
|
|
311
|
+
});
|
|
312
|
+
expect(source.editor.state.doc.childCount).toBe(1);
|
|
313
|
+
expect(childText(source.editor, 0)).toBe("Keep source");
|
|
314
|
+
expect(target.editor.state.doc.childCount).toBe(3);
|
|
315
|
+
expect(childText(target.editor, 0)).toBe("Move me");
|
|
316
|
+
expect(childText(target.editor, 1)).toBe("Target first");
|
|
317
|
+
expect(childText(target.editor, 2)).toBe("Target second");
|
|
318
|
+
} finally {
|
|
319
|
+
source.editor.destroy();
|
|
320
|
+
target.editor.destroy();
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
it("lets a host handle left/right side drops without ProseMirror moving the node", () => {
|
|
325
|
+
const transferData = { blockId: "source-block-1", extra: "side-map data" };
|
|
326
|
+
const getDragTransferData = vi.fn(() => transferData);
|
|
327
|
+
const handleDrop = vi.fn(() => true);
|
|
328
|
+
const source = mountEditor(
|
|
329
|
+
"<p>Move me</p><p>Keep source</p>",
|
|
330
|
+
{ getDragTransferData },
|
|
331
|
+
makeRect({ left: 24, top: 0, width: 640, height: 120 }),
|
|
332
|
+
);
|
|
333
|
+
const target = mountEditor(
|
|
334
|
+
"<p>Target first</p><p>Target second</p>",
|
|
335
|
+
{ handleDrop },
|
|
336
|
+
makeRect({ left: 24, top: 160, width: 640, height: 120 }),
|
|
337
|
+
);
|
|
338
|
+
|
|
339
|
+
try {
|
|
340
|
+
hoverAt(32, 12);
|
|
341
|
+
source.handle.dispatchEvent(
|
|
342
|
+
new MouseEvent("mousedown", {
|
|
343
|
+
bubbles: true,
|
|
344
|
+
button: 0,
|
|
345
|
+
clientX: 12,
|
|
346
|
+
clientY: 12,
|
|
347
|
+
}),
|
|
348
|
+
);
|
|
349
|
+
document.dispatchEvent(
|
|
350
|
+
new MouseEvent("mousemove", {
|
|
351
|
+
bubbles: true,
|
|
352
|
+
clientX: 660,
|
|
353
|
+
clientY: 172,
|
|
354
|
+
}),
|
|
355
|
+
);
|
|
356
|
+
document.dispatchEvent(
|
|
357
|
+
new MouseEvent("mouseup", {
|
|
358
|
+
bubbles: true,
|
|
359
|
+
button: 0,
|
|
360
|
+
clientX: 660,
|
|
361
|
+
clientY: 172,
|
|
362
|
+
}),
|
|
363
|
+
);
|
|
364
|
+
|
|
365
|
+
expect(getDragTransferData).toHaveBeenCalledTimes(1);
|
|
366
|
+
expect(handleDrop).toHaveBeenCalledTimes(1);
|
|
367
|
+
expect(handleDrop).toHaveBeenCalledWith(
|
|
368
|
+
transferData,
|
|
369
|
+
expect.objectContaining({
|
|
370
|
+
view: target.editor.view,
|
|
371
|
+
sourceView: source.editor.view,
|
|
372
|
+
sourceNode: expect.objectContaining({ textContent: "Move me" }),
|
|
373
|
+
targetNode: expect.objectContaining({ textContent: "Target first" }),
|
|
374
|
+
placement: "right",
|
|
375
|
+
targetPos: 0,
|
|
376
|
+
}),
|
|
377
|
+
);
|
|
378
|
+
expect(source.editor.state.doc.childCount).toBe(2);
|
|
379
|
+
expect(childText(source.editor, 0)).toBe("Move me");
|
|
380
|
+
expect(childText(source.editor, 1)).toBe("Keep source");
|
|
381
|
+
expect(target.editor.state.doc.childCount).toBe(2);
|
|
382
|
+
expect(childText(target.editor, 0)).toBe("Target first");
|
|
383
|
+
expect(childText(target.editor, 1)).toBe("Target second");
|
|
384
|
+
} finally {
|
|
385
|
+
source.editor.destroy();
|
|
386
|
+
target.editor.destroy();
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
it("shows only the innermost drag grip when editor regions overlap", () => {
|
|
391
|
+
const outer = mountEditor(
|
|
392
|
+
"<p>Outer container</p>",
|
|
393
|
+
{},
|
|
394
|
+
makeRect({ left: 24, top: 0, width: 640, height: 320 }),
|
|
395
|
+
);
|
|
396
|
+
const outerBlock = outer.editor.view.nodeDOM(0);
|
|
397
|
+
if (outerBlock instanceof HTMLElement) {
|
|
398
|
+
setRect(
|
|
399
|
+
outerBlock,
|
|
400
|
+
makeRect({ left: 24, top: 0, width: 640, height: 280 }),
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
const inner = mountEditor(
|
|
404
|
+
"<p>Inner block</p>",
|
|
405
|
+
{},
|
|
406
|
+
makeRect({ left: 48, top: 160, width: 300, height: 90 }),
|
|
407
|
+
outer.wrapper,
|
|
408
|
+
{ x: 60, y: 172 },
|
|
409
|
+
);
|
|
410
|
+
|
|
411
|
+
try {
|
|
412
|
+
hoverAt(60, 172);
|
|
413
|
+
|
|
414
|
+
expect(inner.handle.style.display).toBe("flex");
|
|
415
|
+
expect(outer.handle.style.display).toBe("none");
|
|
416
|
+
} finally {
|
|
417
|
+
inner.editor.destroy();
|
|
418
|
+
outer.editor.destroy();
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
it("shows the outer container grip when hovering the shared left gutter", () => {
|
|
423
|
+
// A flush-left nested region (like the first column of a `columns` block)
|
|
424
|
+
// shares its container's left-margin gutter. Hovering that gutter — where the
|
|
425
|
+
// grip lives — must select the CONTAINER so it can be dragged to reorder,
|
|
426
|
+
// even though the nested editor is the smaller area. (Regression: nested
|
|
427
|
+
// column editors used to win everywhere, leaving the columns block ungrabbable.)
|
|
428
|
+
const outer = mountEditor(
|
|
429
|
+
"<p>Outer container</p>",
|
|
430
|
+
{},
|
|
431
|
+
makeRect({ left: 24, top: 0, width: 640, height: 320 }),
|
|
432
|
+
);
|
|
433
|
+
const outerBlock = outer.editor.view.nodeDOM(0);
|
|
434
|
+
if (outerBlock instanceof HTMLElement) {
|
|
435
|
+
setRect(
|
|
436
|
+
outerBlock,
|
|
437
|
+
makeRect({ left: 24, top: 0, width: 640, height: 280 }),
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
// Inner region mounted flush-left (block left === container left), mirroring a
|
|
441
|
+
// column that starts at the container's left edge.
|
|
442
|
+
const inner = mountEditor(
|
|
443
|
+
"<p>Inner block</p>",
|
|
444
|
+
{},
|
|
445
|
+
makeRect({ left: 24, top: 160, width: 300, height: 90 }),
|
|
446
|
+
outer.wrapper,
|
|
447
|
+
{ x: 60, y: 172 },
|
|
448
|
+
);
|
|
449
|
+
|
|
450
|
+
try {
|
|
451
|
+
// Over the inner body, the innermost (smallest) editor still wins.
|
|
452
|
+
hoverAt(60, 172);
|
|
453
|
+
expect(inner.handle.style.display).toBe("flex");
|
|
454
|
+
expect(outer.handle.style.display).toBe("none");
|
|
455
|
+
|
|
456
|
+
// In the shared left-margin gutter AT THE NESTED BLOCK'S ROW, the inner
|
|
457
|
+
// block owns the grip band — so a block inside a flush-left column stays
|
|
458
|
+
// grabbable there to drag OUT of / BETWEEN columns. Its grip glyph renders
|
|
459
|
+
// in this exact gutter, so flipping to the container here would make the
|
|
460
|
+
// block's own grip unclickable (the whole point of column drag handles).
|
|
461
|
+
hoverAt(10, 172);
|
|
462
|
+
expect(inner.handle.style.display).toBe("flex");
|
|
463
|
+
expect(outer.handle.style.display).toBe("none");
|
|
464
|
+
|
|
465
|
+
// In the gutter ABOVE the nested region (a row the container alone
|
|
466
|
+
// occupies), the container still wins, so the columns block itself remains
|
|
467
|
+
// grabbable — it just yields the rows where a nested block's grip lives.
|
|
468
|
+
hoverAt(10, 40);
|
|
469
|
+
expect(outer.handle.style.display).toBe("flex");
|
|
470
|
+
expect(inner.handle.style.display).toBe("none");
|
|
471
|
+
} finally {
|
|
472
|
+
inner.editor.destroy();
|
|
473
|
+
outer.editor.destroy();
|
|
474
|
+
}
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
it("moves a block from a nested editor region out to the parent editor", () => {
|
|
478
|
+
const target = mountEditor(
|
|
479
|
+
"<p>Outer target</p>",
|
|
480
|
+
{},
|
|
481
|
+
makeRect({ left: 24, top: 0, width: 640, height: 320 }),
|
|
482
|
+
);
|
|
483
|
+
const targetBlock = target.editor.view.nodeDOM(0);
|
|
484
|
+
if (targetBlock instanceof HTMLElement) {
|
|
485
|
+
setRect(
|
|
486
|
+
targetBlock,
|
|
487
|
+
makeRect({ left: 24, top: 0, width: 640, height: 280 }),
|
|
488
|
+
);
|
|
489
|
+
}
|
|
490
|
+
const source = mountEditor(
|
|
491
|
+
"<p>Nested move</p><p>Nested keep</p>",
|
|
492
|
+
{},
|
|
493
|
+
makeRect({ left: 48, top: 160, width: 300, height: 120 }),
|
|
494
|
+
target.wrapper,
|
|
495
|
+
{ x: 60, y: 172 },
|
|
496
|
+
);
|
|
497
|
+
|
|
498
|
+
try {
|
|
499
|
+
hoverAt(60, 172);
|
|
500
|
+
source.handle.dispatchEvent(
|
|
501
|
+
new MouseEvent("mousedown", {
|
|
502
|
+
bubbles: true,
|
|
503
|
+
button: 0,
|
|
504
|
+
clientX: 60,
|
|
505
|
+
clientY: 172,
|
|
506
|
+
}),
|
|
507
|
+
);
|
|
508
|
+
document.dispatchEvent(
|
|
509
|
+
new MouseEvent("mousemove", {
|
|
510
|
+
bubbles: true,
|
|
511
|
+
clientX: 32,
|
|
512
|
+
clientY: 12,
|
|
513
|
+
}),
|
|
514
|
+
);
|
|
515
|
+
document.dispatchEvent(
|
|
516
|
+
new MouseEvent("mouseup", {
|
|
517
|
+
bubbles: true,
|
|
518
|
+
button: 0,
|
|
519
|
+
clientX: 32,
|
|
520
|
+
clientY: 12,
|
|
521
|
+
}),
|
|
522
|
+
);
|
|
523
|
+
|
|
524
|
+
expect(source.editor.state.doc.childCount).toBe(1);
|
|
525
|
+
expect(childText(source.editor, 0)).toBe("Nested keep");
|
|
526
|
+
expect(target.editor.state.doc.childCount).toBe(2);
|
|
527
|
+
expect(childText(target.editor, 0)).toBe("Nested move");
|
|
528
|
+
expect(childText(target.editor, 1)).toBe("Outer target");
|
|
529
|
+
} finally {
|
|
530
|
+
source.editor.destroy();
|
|
531
|
+
target.editor.destroy();
|
|
532
|
+
}
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
it("keeps a non-left-aligned block's grip alive while the cursor approaches it", () => {
|
|
536
|
+
// A right column / tab body renders its grip in a gap, NOT the page-left
|
|
537
|
+
// gutter. Moving the cursor from that block's body toward its grip crosses
|
|
538
|
+
// into the neighbour's wide forgiving zone (and even its content), so the
|
|
539
|
+
// plain picker flips hover to the neighbour and the grip vanishes before the
|
|
540
|
+
// cursor reaches it. The keepalive must hold the grip across that approach.
|
|
541
|
+
// Here the right grip glyph sits at x:320–344 — LEFT of the 28px gutter band
|
|
542
|
+
// and inside the left region's content [24,336], so without keepalive the
|
|
543
|
+
// left region wins at x=330 and the right grip disappears.
|
|
544
|
+
const left = mountEditor(
|
|
545
|
+
"<p>Left region</p>",
|
|
546
|
+
{},
|
|
547
|
+
makeRect({ left: 24, top: 160, width: 312, height: 90 }),
|
|
548
|
+
document.body,
|
|
549
|
+
{ x: 100, y: 172 },
|
|
550
|
+
);
|
|
551
|
+
setRect(
|
|
552
|
+
left.editor.view.nodeDOM(0) as HTMLElement,
|
|
553
|
+
makeRect({ left: 24, top: 160, width: 312, height: 24 }),
|
|
554
|
+
);
|
|
555
|
+
const right = mountEditor(
|
|
556
|
+
"<p>Right region</p>",
|
|
557
|
+
{},
|
|
558
|
+
makeRect({ left: 360, top: 160, width: 300, height: 90 }),
|
|
559
|
+
document.body,
|
|
560
|
+
{ x: 500, y: 172 },
|
|
561
|
+
);
|
|
562
|
+
setRect(
|
|
563
|
+
right.editor.view.nodeDOM(0) as HTMLElement,
|
|
564
|
+
makeRect({ left: 360, top: 160, width: 300, height: 24 }),
|
|
565
|
+
);
|
|
566
|
+
// The right block's grip glyph, rendered in the inter-region gap.
|
|
567
|
+
setRect(
|
|
568
|
+
right.handle,
|
|
569
|
+
makeRect({ left: 320, top: 162, width: 24, height: 24 }),
|
|
570
|
+
);
|
|
571
|
+
|
|
572
|
+
try {
|
|
573
|
+
// Over the right block's body → its grip shows.
|
|
574
|
+
hoverAt(500, 172);
|
|
575
|
+
expect(right.handle.style.display).toBe("flex");
|
|
576
|
+
|
|
577
|
+
// Cursor moved left onto the right block's grip glyph (x=330). This is
|
|
578
|
+
// inside the LEFT region's content, so the plain picker would hand hover to
|
|
579
|
+
// the left region — the keepalive must keep the RIGHT grip visible.
|
|
580
|
+
hoverAt(330, 172);
|
|
581
|
+
expect(right.handle.style.display).toBe("flex");
|
|
582
|
+
expect(left.handle.style.display).toBe("none");
|
|
583
|
+
} finally {
|
|
584
|
+
right.editor.destroy();
|
|
585
|
+
left.editor.destroy();
|
|
586
|
+
}
|
|
587
|
+
});
|
|
588
|
+
});
|