@ctrl-ui/react 0.9.5 → 0.9.8
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 +38 -2
- package/dist/action-bar.js +16 -8
- package/dist/activity.d.ts +4 -2
- package/dist/activity.js +5 -2
- package/dist/audio-visualizer.js +1 -1
- package/dist/blocks/agent-run-clock.d.ts +5 -0
- package/dist/blocks/agent-run-clock.js +52 -0
- package/dist/blocks/agent-run-sidebar.d.ts +13 -0
- package/dist/blocks/agent-run-sidebar.js +101 -0
- package/dist/blocks/agent-run-status.d.ts +32 -0
- package/dist/blocks/agent-run-status.js +27 -0
- package/dist/blocks/agent-run.d.ts +82 -0
- package/dist/blocks/agent-run.js +262 -0
- package/dist/blocks/coding-agent.d.ts +1 -1
- package/dist/blocks/coding-agent.js +50 -69
- package/dist/blocks/design-canvas-data.d.ts +1 -1
- package/dist/blocks/design-canvas-inspector.js +1 -1
- package/dist/blocks/design-canvas.d.ts +2 -1
- package/dist/blocks/design-canvas.js +56 -42
- package/dist/blocks/discussion.js +1 -1
- package/dist/blocks/file-explorer.js +66 -47
- package/dist/blocks/settings.js +37 -41
- package/dist/blocks/team-chat.js +2 -1
- package/dist/blocks/theme-toggle.d.ts +2 -1
- package/dist/blocks/theme-toggle.js +13 -4
- package/dist/chat-message.js +33 -39
- package/dist/hooks/use-mobile.d.ts +1 -1
- package/dist/hooks/use-mobile.js +1 -1
- package/dist/knob-contracts/badge-knobs.d.ts +1 -1
- package/dist/knob-contracts/badge-knobs.js +3 -1
- package/dist/knob-contracts/button-knobs.d.ts +1 -1
- package/dist/knob-contracts/button-knobs.js +1 -0
- package/dist/knob-contracts/calendar-knobs.d.ts +1 -1
- package/dist/knob-contracts/calendar-knobs.js +9 -1
- package/dist/knob-contracts/card-knobs.d.ts +1 -1
- package/dist/knob-contracts/card-knobs.js +5 -1
- package/dist/knob-contracts/choice-knobs.d.ts +1 -1
- package/dist/knob-contracts/choice-knobs.js +1 -0
- package/dist/knob-contracts/collapsible-knobs.d.ts +1 -1
- package/dist/knob-contracts/collapsible-knobs.js +5 -1
- package/dist/knob-contracts/emoji-picker-knobs.d.ts +1 -1
- package/dist/knob-contracts/emoji-picker-knobs.js +3 -1
- package/dist/knob-contracts/field-knobs.d.ts +1 -1
- package/dist/knob-contracts/field-knobs.js +17 -1
- package/dist/knob-contracts/infinite-canvas-knobs.d.ts +1 -1
- package/dist/knob-contracts/infinite-canvas-knobs.js +1 -7
- package/dist/knob-contracts/item-knobs.d.ts +1 -1
- package/dist/knob-contracts/item-knobs.js +5 -1
- package/dist/knob-contracts/kbd-knobs.d.ts +1 -1
- package/dist/knob-contracts/kbd-knobs.js +4 -1
- package/dist/knob-contracts/page-layout-knobs.d.ts +1 -1
- package/dist/knob-contracts/page-layout-knobs.js +1 -0
- package/dist/knob-contracts/pagination-knobs.d.ts +1 -1
- package/dist/knob-contracts/pagination-knobs.js +2 -1
- package/dist/knob-contracts/popup-knobs.d.ts +1 -1
- package/dist/knob-contracts/popup-knobs.js +9 -0
- package/dist/knob-contracts/range-knobs.d.ts +1 -1
- package/dist/knob-contracts/range-knobs.js +2 -0
- package/dist/knob-contracts/sidebar-knobs.d.ts +1 -1
- package/dist/knob-contracts/sidebar-knobs.js +2 -0
- package/dist/knob-contracts/swap-knobs.d.ts +4 -0
- package/dist/knob-contracts/swap-knobs.js +9 -0
- package/dist/knob-contracts/tabs-knobs.d.ts +1 -1
- package/dist/knob-contracts/tabs-knobs.js +3 -0
- package/dist/knob-contracts/timeline-knobs.d.ts +1 -1
- package/dist/knob-contracts/timeline-knobs.js +5 -1
- package/dist/knob-contracts/tool-approval-knobs.d.ts +4 -0
- package/dist/knob-contracts/tool-approval-knobs.js +32 -0
- package/dist/knob-contracts/tree-knobs.d.ts +1 -1
- package/dist/knob-contracts/tree-knobs.js +3 -1
- package/dist/lib/infinite-canvas-camera-store.d.ts +65 -0
- package/dist/lib/infinite-canvas-camera-store.js +233 -0
- package/dist/lib/infinite-canvas-camera.d.ts +33 -0
- package/dist/lib/infinite-canvas-camera.js +98 -0
- package/dist/lib/infinite-canvas-navigation.d.ts +19 -0
- package/dist/lib/infinite-canvas-navigation.js +395 -0
- package/dist/markdown-editor/bubble-menu.d.ts +6 -0
- package/dist/markdown-editor/bubble-menu.js +45 -0
- package/dist/markdown-editor/toolbar.d.ts +17 -1
- package/dist/markdown-editor/toolbar.js +103 -81
- package/dist/skin.d.ts +2 -0
- package/dist/styles/index.css +11 -10
- package/dist/styles/recipes/activity.css +12 -0
- package/dist/styles/recipes/avatar.css +1 -1
- package/dist/styles/recipes/badge.css +17 -0
- package/dist/styles/recipes/button-group.css +12 -6
- package/dist/styles/recipes/button.css +20 -3
- package/dist/styles/recipes/calendar.css +82 -6
- package/dist/styles/recipes/card.css +40 -5
- package/dist/styles/recipes/chat-message-edit.css +6 -21
- package/dist/styles/recipes/choice.css +9 -2
- package/dist/styles/recipes/collapsible.css +8 -1
- package/dist/styles/recipes/color-picker-values.css +4 -0
- package/dist/styles/recipes/emoji-picker.css +29 -8
- package/dist/styles/recipes/field-parts.css +80 -5
- package/dist/styles/recipes/field.css +113 -40
- package/dist/styles/recipes/infinite-canvas.css +17 -57
- package/dist/styles/recipes/item.css +33 -5
- package/dist/styles/recipes/kbd.css +26 -6
- package/dist/styles/recipes/markdown-editor.css +12 -0
- package/dist/styles/recipes/morphing-panel.css +1 -1
- package/dist/styles/recipes/page-layout.css +18 -2
- package/dist/styles/recipes/pagination.css +8 -0
- package/dist/styles/recipes/popup-controls.css +9 -0
- package/dist/styles/recipes/popup-drawer.css +1 -0
- package/dist/styles/recipes/popup-lists.css +55 -6
- package/dist/styles/recipes/popup-motion.css +7 -1
- package/dist/styles/recipes/popup.css +10 -0
- package/dist/styles/recipes/range.css +16 -2
- package/dist/styles/recipes/sidebar-mobile.css +1 -1
- package/dist/styles/recipes/sidebar-parts.css +17 -1
- package/dist/styles/recipes/sidebar.css +11 -0
- package/dist/styles/recipes/swap.css +143 -0
- package/dist/styles/recipes/tabs.css +41 -8
- package/dist/styles/recipes/timeline.css +36 -2
- package/dist/styles/recipes/tool-approval.css +267 -0
- package/dist/styles/recipes/track-highlight.css +19 -6
- package/dist/styles/recipes/tree.css +33 -0
- package/dist/styles/theme.css +17 -6
- package/dist/tool-approval.d.ts +49 -0
- package/dist/tool-approval.js +260 -0
- package/dist/ui/alert-dialog.js +1 -1
- package/dist/ui/badge.js +1 -1
- package/dist/ui/calendar.js +9 -4
- package/dist/ui/card.d.ts +3 -1
- package/dist/ui/card.js +2 -1
- package/dist/ui/code.js +13 -3
- package/dist/ui/command.js +1 -0
- package/dist/ui/dialog.js +1 -1
- package/dist/ui/dropdown-menu.d.ts +1 -1
- package/dist/ui/dropdown-menu.js +23 -6
- package/dist/ui/dropzone.js +10 -11
- package/dist/ui/field.d.ts +6 -2
- package/dist/ui/field.js +4 -2
- package/dist/ui/infinite-canvas.d.ts +19 -22
- package/dist/ui/infinite-canvas.js +264 -325
- package/dist/ui/morphing-panel.d.ts +5 -1
- package/dist/ui/morphing-panel.js +4 -2
- package/dist/ui/rich-tooltip.d.ts +2 -1
- package/dist/ui/rich-tooltip.js +32 -29
- package/dist/ui/select.js +2 -2
- package/dist/ui/sidebar-mobile.js +1 -1
- package/dist/ui/sidebar-provider.d.ts +3 -1
- package/dist/ui/sidebar-provider.js +3 -2
- package/dist/ui/sidebar-resize-rail.js +1 -1
- package/dist/ui/sidebar.d.ts +6 -5
- package/dist/ui/sidebar.js +10 -9
- package/dist/ui/swap.d.ts +14 -0
- package/dist/ui/swap.js +69 -0
- package/dist/ui/tabs.d.ts +1 -1
- package/dist/ui/tabs.js +2 -13
- package/dist/ui/timeline.d.ts +1 -1
- package/dist/ui/timeline.js +2 -2
- package/dist/ui/typography.d.ts +11 -5
- package/dist/ui/typography.js +18 -6
- package/dist/user-ask.js +1 -1
- package/lint/biome-preset.json +3 -0
- package/lint/class-policy.mjs +17 -0
- package/lint/eslint-plugin.mjs +87 -0
- package/lint/grit-rules.mjs +40 -0
- package/lint/no-component-style-classes.grit +16 -0
- package/lint/no-raw-type-classes.grit +3 -4
- package/lint/write-grit-rules.mjs +19 -0
- package/package.json +12 -3
- package/dist/styles/type-scale.css +0 -72
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
import { pinchTransform, wheelDeltaMultiplier, wheelDeltaPixels, zoomAroundPoint } from "./infinite-canvas-camera.js";
|
|
2
|
+
//#region src/lib/infinite-canvas-navigation.ts
|
|
3
|
+
const INFINITE_CANVAS_KEY_SHORTCUTS = "ArrowLeft ArrowRight ArrowUp ArrowDown + - 0 1";
|
|
4
|
+
const ARROW_KEY_DIRECTIONS = {
|
|
5
|
+
ArrowLeft: {
|
|
6
|
+
x: -1,
|
|
7
|
+
y: 0
|
|
8
|
+
},
|
|
9
|
+
ArrowRight: {
|
|
10
|
+
x: 1,
|
|
11
|
+
y: 0
|
|
12
|
+
},
|
|
13
|
+
ArrowUp: {
|
|
14
|
+
x: 0,
|
|
15
|
+
y: -1
|
|
16
|
+
},
|
|
17
|
+
ArrowDown: {
|
|
18
|
+
x: 0,
|
|
19
|
+
y: 1
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
const KEYBOARD_ZOOM_FACTORS = {
|
|
23
|
+
"+": 1.15,
|
|
24
|
+
"=": 1.15,
|
|
25
|
+
"-": 1 / 1.15
|
|
26
|
+
};
|
|
27
|
+
const KEYBOARD_STEP_PX = 32;
|
|
28
|
+
const WHEEL_ZOOM_SPEED = .004;
|
|
29
|
+
const MAX_WHEEL_ZOOM_DELTA = 50;
|
|
30
|
+
const NOTCHED_WHEEL_DELTA = 40;
|
|
31
|
+
const CLICK_SLOP_PX = 3;
|
|
32
|
+
const MIDDLE_BUTTON = 1;
|
|
33
|
+
const SCROLLABLE_OVERFLOW = /auto|scroll|overlay/;
|
|
34
|
+
const SPACE_CONSUMING_TARGET = [
|
|
35
|
+
"input",
|
|
36
|
+
"textarea",
|
|
37
|
+
"select",
|
|
38
|
+
"button",
|
|
39
|
+
"a[href]",
|
|
40
|
+
"[contenteditable]:not([contenteditable='false'])",
|
|
41
|
+
...[
|
|
42
|
+
"button",
|
|
43
|
+
"checkbox",
|
|
44
|
+
"combobox",
|
|
45
|
+
"menuitem",
|
|
46
|
+
"option",
|
|
47
|
+
"radio",
|
|
48
|
+
"slider",
|
|
49
|
+
"spinbutton",
|
|
50
|
+
"switch",
|
|
51
|
+
"tab",
|
|
52
|
+
"textbox"
|
|
53
|
+
].map((role) => `[role='${role}']`)
|
|
54
|
+
].join(", ");
|
|
55
|
+
function keyboardStep(shiftKey) {
|
|
56
|
+
return shiftKey ? 96 : KEYBOARD_STEP_PX;
|
|
57
|
+
}
|
|
58
|
+
function runInfiniteCanvasKeyCommand(camera, key, shiftKey) {
|
|
59
|
+
const direction = ARROW_KEY_DIRECTIONS[key];
|
|
60
|
+
if (direction) {
|
|
61
|
+
const current = camera.getTransform();
|
|
62
|
+
const step = keyboardStep(shiftKey);
|
|
63
|
+
camera.move({
|
|
64
|
+
...current,
|
|
65
|
+
x: current.x - direction.x * step,
|
|
66
|
+
y: current.y - direction.y * step
|
|
67
|
+
}, "keyboard");
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
const zoomFactor = KEYBOARD_ZOOM_FACTORS[key];
|
|
71
|
+
if (zoomFactor) camera.zoomBy(zoomFactor, { reason: "keyboard" });
|
|
72
|
+
else if (key === "0") camera.reset({ reason: "keyboard" });
|
|
73
|
+
else if (key === "1") camera.fit({ reason: "keyboard" });
|
|
74
|
+
else return false;
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
function canScrollFurther(element, delta) {
|
|
78
|
+
if (element.scrollHeight <= element.clientHeight && element.scrollWidth <= element.clientWidth) return false;
|
|
79
|
+
const { overflowX, overflowY } = getComputedStyle(element);
|
|
80
|
+
const verticalRoom = delta.y < 0 ? element.scrollTop > 0 : Math.ceil(element.scrollTop + element.clientHeight) < element.scrollHeight;
|
|
81
|
+
const horizontalRoom = delta.x < 0 ? element.scrollLeft > 0 : Math.ceil(element.scrollLeft + element.clientWidth) < element.scrollWidth;
|
|
82
|
+
return delta.y !== 0 && SCROLLABLE_OVERFLOW.test(overflowY) && verticalRoom || delta.x !== 0 && SCROLLABLE_OVERFLOW.test(overflowX) && horizontalRoom;
|
|
83
|
+
}
|
|
84
|
+
function nestedScrollerConsumes(target, root, delta) {
|
|
85
|
+
for (let element = target instanceof Element ? target : null; element && element !== root; element = element.parentElement) if (canScrollFurther(element, delta)) return true;
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
function gesturePointerIds(active) {
|
|
89
|
+
return active.kind === "pan" ? [active.pointerId] : active.pointerIds;
|
|
90
|
+
}
|
|
91
|
+
function clickAfterGesture(ended) {
|
|
92
|
+
if (ended.kind === "pinch") return "click";
|
|
93
|
+
if (!(ended.explicit || ended.moved)) return null;
|
|
94
|
+
return ended.button === MIDDLE_BUTTON ? "auxclick" : "click";
|
|
95
|
+
}
|
|
96
|
+
function gestureScale(event) {
|
|
97
|
+
return "scale" in event && typeof event.scale === "number" ? event.scale : null;
|
|
98
|
+
}
|
|
99
|
+
function gestureClientPoint(event) {
|
|
100
|
+
if (!("clientX" in event && "clientY" in event)) return null;
|
|
101
|
+
const { clientX, clientY } = event;
|
|
102
|
+
return typeof clientX === "number" && typeof clientY === "number" ? {
|
|
103
|
+
clientX,
|
|
104
|
+
clientY
|
|
105
|
+
} : null;
|
|
106
|
+
}
|
|
107
|
+
function attachInfiniteCanvasNavigation(root, camera, readOptions) {
|
|
108
|
+
const listeners = new AbortController();
|
|
109
|
+
const { signal } = listeners;
|
|
110
|
+
const touches = /* @__PURE__ */ new Map();
|
|
111
|
+
let gesture = null;
|
|
112
|
+
let spaceHeld = false;
|
|
113
|
+
let safariPinchStartScale = null;
|
|
114
|
+
let suppressedClick = null;
|
|
115
|
+
let suppressedClickTimer;
|
|
116
|
+
let pointerFocus = false;
|
|
117
|
+
let pointerFocusTimer;
|
|
118
|
+
function syncPanReady() {
|
|
119
|
+
root.toggleAttribute("data-pan-ready", spaceHeld || readOptions().panOnDrag === "anywhere");
|
|
120
|
+
}
|
|
121
|
+
function suppressNextClick(type) {
|
|
122
|
+
suppressedClick = type;
|
|
123
|
+
clearTimeout(suppressedClickTimer);
|
|
124
|
+
suppressedClickTimer = setTimeout(() => {
|
|
125
|
+
suppressedClick = null;
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
function startPan(event, explicit) {
|
|
129
|
+
root.setPointerCapture(event.pointerId);
|
|
130
|
+
root.toggleAttribute("data-panning", true);
|
|
131
|
+
gesture = {
|
|
132
|
+
kind: "pan",
|
|
133
|
+
pointerId: event.pointerId,
|
|
134
|
+
button: event.button,
|
|
135
|
+
explicit,
|
|
136
|
+
moved: false,
|
|
137
|
+
startPointer: {
|
|
138
|
+
x: event.clientX,
|
|
139
|
+
y: event.clientY
|
|
140
|
+
},
|
|
141
|
+
startTransform: camera.getTransform()
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
function startPinch() {
|
|
145
|
+
const [first, second] = [...touches.entries()];
|
|
146
|
+
if (!(first && second)) return;
|
|
147
|
+
for (const [pointerId] of [first, second]) if (!root.hasPointerCapture(pointerId)) root.setPointerCapture(pointerId);
|
|
148
|
+
root.toggleAttribute("data-panning", true);
|
|
149
|
+
gesture = {
|
|
150
|
+
kind: "pinch",
|
|
151
|
+
pointerIds: [first[0], second[0]],
|
|
152
|
+
startDistance: Math.max(1, Math.hypot(first[1].x - second[1].x, first[1].y - second[1].y)),
|
|
153
|
+
startMidpoint: camera.toLocalPoint({
|
|
154
|
+
clientX: (first[1].x + second[1].x) / 2,
|
|
155
|
+
clientY: (first[1].y + second[1].y) / 2
|
|
156
|
+
}),
|
|
157
|
+
startTransform: camera.getTransform()
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function involvesPointer(pointerId) {
|
|
161
|
+
return gesture !== null && gesturePointerIds(gesture).includes(pointerId);
|
|
162
|
+
}
|
|
163
|
+
function finishGesture() {
|
|
164
|
+
const ended = gesture;
|
|
165
|
+
if (!ended) return;
|
|
166
|
+
gesture = null;
|
|
167
|
+
root.removeAttribute("data-panning");
|
|
168
|
+
for (const pointerId of gesturePointerIds(ended)) if (root.hasPointerCapture(pointerId)) root.releasePointerCapture(pointerId);
|
|
169
|
+
const click = clickAfterGesture(ended);
|
|
170
|
+
if (click) suppressNextClick(click);
|
|
171
|
+
}
|
|
172
|
+
function claim(event) {
|
|
173
|
+
event.preventDefault();
|
|
174
|
+
event.stopPropagation();
|
|
175
|
+
}
|
|
176
|
+
function isNavigableTarget(target) {
|
|
177
|
+
return target === root || target instanceof Node && camera.contentElement()?.contains(target) === true;
|
|
178
|
+
}
|
|
179
|
+
function markPointerFocus() {
|
|
180
|
+
pointerFocus = true;
|
|
181
|
+
clearTimeout(pointerFocusTimer);
|
|
182
|
+
pointerFocusTimer = setTimeout(() => {
|
|
183
|
+
pointerFocus = false;
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
function claimsTouchPinch(event) {
|
|
187
|
+
if (event.pointerType !== "touch") return false;
|
|
188
|
+
if (event.isPrimary) touches.clear();
|
|
189
|
+
touches.set(event.pointerId, {
|
|
190
|
+
x: event.clientX,
|
|
191
|
+
y: event.clientY
|
|
192
|
+
});
|
|
193
|
+
if (touches.size < 2) return false;
|
|
194
|
+
claim(event);
|
|
195
|
+
if (touches.size === 2) startPinch();
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
function isExplicitPan(event) {
|
|
199
|
+
if (!isNavigableTarget(event.target)) return false;
|
|
200
|
+
if (event.button === MIDDLE_BUTTON) return true;
|
|
201
|
+
return event.button === 0 && (spaceHeld || readOptions().panOnDrag === "anywhere");
|
|
202
|
+
}
|
|
203
|
+
function claimNavigationPointer(event) {
|
|
204
|
+
markPointerFocus();
|
|
205
|
+
if (claimsTouchPinch(event) || gesture || !event.isPrimary || !isExplicitPan(event)) return;
|
|
206
|
+
claim(event);
|
|
207
|
+
startPan(event, true);
|
|
208
|
+
}
|
|
209
|
+
function followPointer(event) {
|
|
210
|
+
if (touches.has(event.pointerId)) touches.set(event.pointerId, {
|
|
211
|
+
x: event.clientX,
|
|
212
|
+
y: event.clientY
|
|
213
|
+
});
|
|
214
|
+
if (!(gesture && involvesPointer(event.pointerId))) return;
|
|
215
|
+
if (gesture.kind === "pan") {
|
|
216
|
+
const deltaX = event.clientX - gesture.startPointer.x;
|
|
217
|
+
const deltaY = event.clientY - gesture.startPointer.y;
|
|
218
|
+
if (Math.hypot(deltaX, deltaY) > CLICK_SLOP_PX) gesture.moved = true;
|
|
219
|
+
const { startTransform } = gesture;
|
|
220
|
+
camera.move({
|
|
221
|
+
...startTransform,
|
|
222
|
+
x: startTransform.x + deltaX,
|
|
223
|
+
y: startTransform.y + deltaY
|
|
224
|
+
}, "pointer");
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
const [first, second] = gesture.pointerIds.map((pointerId) => touches.get(pointerId));
|
|
228
|
+
if (!(first && second)) return;
|
|
229
|
+
const midpoint = camera.toLocalPoint({
|
|
230
|
+
clientX: (first.x + second.x) / 2,
|
|
231
|
+
clientY: (first.y + second.y) / 2
|
|
232
|
+
});
|
|
233
|
+
const ratio = Math.hypot(first.x - second.x, first.y - second.y) / gesture.startDistance;
|
|
234
|
+
const nextScale = camera.limitScale(gesture.startTransform.scale * ratio);
|
|
235
|
+
camera.move(pinchTransform(gesture.startTransform, gesture.startMidpoint, midpoint, nextScale), "pinch");
|
|
236
|
+
}
|
|
237
|
+
function releasePointer(event) {
|
|
238
|
+
touches.delete(event.pointerId);
|
|
239
|
+
if (involvesPointer(event.pointerId)) finishGesture();
|
|
240
|
+
}
|
|
241
|
+
function zoomWithWheel(event) {
|
|
242
|
+
event.preventDefault();
|
|
243
|
+
if (safariPinchStartScale !== null) return;
|
|
244
|
+
const current = camera.getTransform();
|
|
245
|
+
const zoomDelta = event.deltaY * wheelDeltaMultiplier(event.deltaMode, root.clientHeight);
|
|
246
|
+
const boundedDelta = Math.min(Math.max(zoomDelta, -50), MAX_WHEEL_ZOOM_DELTA);
|
|
247
|
+
const nextScale = camera.limitScale(current.scale * Math.exp(-boundedDelta * WHEEL_ZOOM_SPEED));
|
|
248
|
+
camera.move(zoomAroundPoint(current, nextScale, camera.toLocalPoint(event)), "wheel", Math.abs(zoomDelta) >= NOTCHED_WHEEL_DELTA);
|
|
249
|
+
}
|
|
250
|
+
function handleWheel(event) {
|
|
251
|
+
readOptions().onWheel?.(event);
|
|
252
|
+
if (event.defaultPrevented) return;
|
|
253
|
+
if (event.ctrlKey || event.metaKey) {
|
|
254
|
+
zoomWithWheel(event);
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
const delta = wheelDeltaPixels(event, root.clientHeight);
|
|
258
|
+
if (nestedScrollerConsumes(event.target, root, delta)) return;
|
|
259
|
+
event.preventDefault();
|
|
260
|
+
const current = camera.getTransform();
|
|
261
|
+
camera.move({
|
|
262
|
+
...current,
|
|
263
|
+
x: current.x - delta.x,
|
|
264
|
+
y: current.y - delta.y
|
|
265
|
+
}, "wheel");
|
|
266
|
+
}
|
|
267
|
+
function startSafariPinch(event) {
|
|
268
|
+
event.preventDefault();
|
|
269
|
+
safariPinchStartScale = camera.getTransform().scale;
|
|
270
|
+
}
|
|
271
|
+
function followSafariPinch(event) {
|
|
272
|
+
event.preventDefault();
|
|
273
|
+
const scale = gestureScale(event);
|
|
274
|
+
if (safariPinchStartScale === null || scale === null || gesture?.kind === "pinch") return;
|
|
275
|
+
const clientPoint = gestureClientPoint(event);
|
|
276
|
+
const origin = clientPoint ? camera.toLocalPoint(clientPoint) : {
|
|
277
|
+
x: root.clientWidth / 2,
|
|
278
|
+
y: root.clientHeight / 2
|
|
279
|
+
};
|
|
280
|
+
camera.move(zoomAroundPoint(camera.getTransform(), camera.limitScale(safariPinchStartScale * scale), origin), "pinch");
|
|
281
|
+
}
|
|
282
|
+
function endSafariPinch(event) {
|
|
283
|
+
event.preventDefault();
|
|
284
|
+
safariPinchStartScale = null;
|
|
285
|
+
}
|
|
286
|
+
function armSpacePan(event) {
|
|
287
|
+
if (event.code !== "Space") return;
|
|
288
|
+
if (spaceHeld) {
|
|
289
|
+
event.preventDefault();
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
if (event.defaultPrevented || event.repeat || event.isComposing || event.ctrlKey || event.metaKey || event.altKey) return;
|
|
293
|
+
const { target } = event;
|
|
294
|
+
if (target instanceof HTMLElement && (target.isContentEditable || target.matches(SPACE_CONSUMING_TARGET))) return;
|
|
295
|
+
if (!(root.contains(document.activeElement) || root.matches(":hover"))) return;
|
|
296
|
+
event.preventDefault();
|
|
297
|
+
spaceHeld = true;
|
|
298
|
+
syncPanReady();
|
|
299
|
+
}
|
|
300
|
+
function disarmSpacePan(event) {
|
|
301
|
+
if (event.code !== "Space" || !spaceHeld) return;
|
|
302
|
+
spaceHeld = false;
|
|
303
|
+
syncPanReady();
|
|
304
|
+
}
|
|
305
|
+
function releaseHeldInput() {
|
|
306
|
+
spaceHeld = false;
|
|
307
|
+
safariPinchStartScale = null;
|
|
308
|
+
touches.clear();
|
|
309
|
+
finishGesture();
|
|
310
|
+
syncPanReady();
|
|
311
|
+
}
|
|
312
|
+
function swallowClickAfterPan(event) {
|
|
313
|
+
if (event.type !== suppressedClick) return;
|
|
314
|
+
suppressedClick = null;
|
|
315
|
+
event.preventDefault();
|
|
316
|
+
event.stopPropagation();
|
|
317
|
+
}
|
|
318
|
+
function revealKeyboardFocus(event) {
|
|
319
|
+
if (pointerFocus || !(event.target instanceof Element)) return;
|
|
320
|
+
if (camera.contentElement()?.contains(event.target)) camera.revealElement(event.target);
|
|
321
|
+
}
|
|
322
|
+
function absorbNativeScroll() {
|
|
323
|
+
const { scrollLeft, scrollTop } = root;
|
|
324
|
+
if (scrollLeft === 0 && scrollTop === 0) return;
|
|
325
|
+
root.scrollTo(0, 0);
|
|
326
|
+
const current = camera.getTransform();
|
|
327
|
+
camera.moveNow({
|
|
328
|
+
...current,
|
|
329
|
+
x: current.x - scrollLeft,
|
|
330
|
+
y: current.y - scrollTop
|
|
331
|
+
}, "reveal");
|
|
332
|
+
}
|
|
333
|
+
root.addEventListener("pointerdown", claimNavigationPointer, {
|
|
334
|
+
capture: true,
|
|
335
|
+
signal
|
|
336
|
+
});
|
|
337
|
+
root.addEventListener("pointermove", followPointer, {
|
|
338
|
+
capture: true,
|
|
339
|
+
signal
|
|
340
|
+
});
|
|
341
|
+
root.addEventListener("pointerup", releasePointer, {
|
|
342
|
+
capture: true,
|
|
343
|
+
signal
|
|
344
|
+
});
|
|
345
|
+
root.addEventListener("pointercancel", releasePointer, {
|
|
346
|
+
capture: true,
|
|
347
|
+
signal
|
|
348
|
+
});
|
|
349
|
+
root.addEventListener("lostpointercapture", (event) => {
|
|
350
|
+
if (event.target === root && involvesPointer(event.pointerId)) finishGesture();
|
|
351
|
+
}, { signal });
|
|
352
|
+
root.addEventListener("click", swallowClickAfterPan, {
|
|
353
|
+
capture: true,
|
|
354
|
+
signal
|
|
355
|
+
});
|
|
356
|
+
root.addEventListener("auxclick", swallowClickAfterPan, {
|
|
357
|
+
capture: true,
|
|
358
|
+
signal
|
|
359
|
+
});
|
|
360
|
+
root.addEventListener("mousedown", (event) => {
|
|
361
|
+
if (event.button === MIDDLE_BUTTON && isNavigableTarget(event.target)) event.preventDefault();
|
|
362
|
+
}, { signal });
|
|
363
|
+
root.addEventListener("wheel", handleWheel, {
|
|
364
|
+
passive: false,
|
|
365
|
+
signal
|
|
366
|
+
});
|
|
367
|
+
root.addEventListener("gesturestart", startSafariPinch, { signal });
|
|
368
|
+
root.addEventListener("gesturechange", followSafariPinch, { signal });
|
|
369
|
+
root.addEventListener("gestureend", endSafariPinch, { signal });
|
|
370
|
+
root.addEventListener("focusin", revealKeyboardFocus, { signal });
|
|
371
|
+
root.addEventListener("scroll", absorbNativeScroll, { signal });
|
|
372
|
+
window.addEventListener("keydown", armSpacePan, { signal });
|
|
373
|
+
window.addEventListener("keyup", disarmSpacePan, { signal });
|
|
374
|
+
window.addEventListener("blur", releaseHeldInput, { signal });
|
|
375
|
+
document.addEventListener("visibilitychange", () => {
|
|
376
|
+
if (document.visibilityState === "hidden") releaseHeldInput();
|
|
377
|
+
}, { signal });
|
|
378
|
+
syncPanReady();
|
|
379
|
+
return {
|
|
380
|
+
beginBackgroundPan(event) {
|
|
381
|
+
if (gesture || event.defaultPrevented || event.target !== root || !event.isPrimary || event.button !== 0) return;
|
|
382
|
+
if (readOptions().panOnDrag === "background") startPan(event, false);
|
|
383
|
+
},
|
|
384
|
+
syncPanReady,
|
|
385
|
+
detach() {
|
|
386
|
+
listeners.abort();
|
|
387
|
+
clearTimeout(suppressedClickTimer);
|
|
388
|
+
clearTimeout(pointerFocusTimer);
|
|
389
|
+
releaseHeldInput();
|
|
390
|
+
root.removeAttribute("data-pan-ready");
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
//#endregion
|
|
395
|
+
export { ARROW_KEY_DIRECTIONS, INFINITE_CANVAS_KEY_SHORTCUTS, attachInfiniteCanvasNavigation, keyboardStep, runInfiniteCanvasKeyCommand };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { TooltipProvider } from "../ui/tooltip.js";
|
|
3
|
+
import { useMarkdownEditor } from "./context.js";
|
|
4
|
+
import { Toolbar, ToolbarSeparator } from "../ui/toolbar.js";
|
|
5
|
+
import { MarkdownEditorFormatButton, MarkdownEditorHeading, MarkdownEditorLink, useMarkdownEditorFormatActions } from "./toolbar.js";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
import { NodeSelection } from "@tiptap/pm/state";
|
|
8
|
+
import { BubbleMenu } from "@tiptap/react/menus";
|
|
9
|
+
//#region src/markdown-editor/bubble-menu.tsx
|
|
10
|
+
const showForFormattableText = ({ editor, view, state, element }) => {
|
|
11
|
+
const { selection } = state;
|
|
12
|
+
return !(selection instanceof NodeSelection) && !selection.empty && state.doc.textBetween(selection.from, selection.to).trim() !== "" && !editor.isActive("codeBlock") && editor.isEditable && (view.hasFocus() || element.contains(document.activeElement));
|
|
13
|
+
};
|
|
14
|
+
const floatingOptions = {
|
|
15
|
+
placement: "top",
|
|
16
|
+
offset: 8,
|
|
17
|
+
flip: { padding: 8 },
|
|
18
|
+
shift: { padding: 8 }
|
|
19
|
+
};
|
|
20
|
+
function MarkdownEditorBubbleMenu({ children }) {
|
|
21
|
+
const { editor, disabled, source } = useMarkdownEditor();
|
|
22
|
+
const { marks } = useMarkdownEditorFormatActions();
|
|
23
|
+
if (!editor || disabled || source) return null;
|
|
24
|
+
return /* @__PURE__ */ jsx(BubbleMenu, {
|
|
25
|
+
editor,
|
|
26
|
+
shouldShow: showForFormattableText,
|
|
27
|
+
options: floatingOptions,
|
|
28
|
+
"data-control-ui": "markdown-editor",
|
|
29
|
+
"data-control-family": "markdown-editor",
|
|
30
|
+
"data-slot": "bubble-menu",
|
|
31
|
+
children: /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(Toolbar, {
|
|
32
|
+
"aria-label": "Selection formatting",
|
|
33
|
+
variant: "floating",
|
|
34
|
+
children: [
|
|
35
|
+
/* @__PURE__ */ jsx(MarkdownEditorHeading, {}),
|
|
36
|
+
/* @__PURE__ */ jsx(ToolbarSeparator, {}),
|
|
37
|
+
marks.map((action) => /* @__PURE__ */ jsx(MarkdownEditorFormatButton, { action }, action.label)),
|
|
38
|
+
/* @__PURE__ */ jsx(MarkdownEditorLink, {}),
|
|
39
|
+
children
|
|
40
|
+
]
|
|
41
|
+
}) })
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
export { MarkdownEditorBubbleMenu };
|
|
@@ -1,8 +1,24 @@
|
|
|
1
|
-
import { ReactNode } from "react";
|
|
1
|
+
import { MouseEventHandler, ReactNode } from "react";
|
|
2
|
+
import { BoldIcon } from "lucide-react";
|
|
2
3
|
//#region src/markdown-editor/toolbar.d.ts
|
|
4
|
+
type MarkdownEditorFormatAction = {
|
|
5
|
+
label: string;
|
|
6
|
+
Icon: typeof BoldIcon;
|
|
7
|
+
active: boolean;
|
|
8
|
+
run: () => void;
|
|
9
|
+
};
|
|
10
|
+
export declare function useMarkdownEditorFormatActions(): {
|
|
11
|
+
marks: MarkdownEditorFormatAction[];
|
|
12
|
+
blocks: MarkdownEditorFormatAction[];
|
|
13
|
+
};
|
|
14
|
+
export declare function MarkdownEditorFormatButton({ action, onMouseDown }: {
|
|
15
|
+
action: MarkdownEditorFormatAction;
|
|
16
|
+
onMouseDown?: MouseEventHandler<HTMLButtonElement>;
|
|
17
|
+
}): import("react").JSX.Element;
|
|
3
18
|
export declare function MarkdownEditorToolbar({ children }: {
|
|
4
19
|
children?: ReactNode;
|
|
5
20
|
}): import("react").JSX.Element;
|
|
21
|
+
export declare function MarkdownEditorHeading(): import("react").JSX.Element;
|
|
6
22
|
export declare function MarkdownEditorLink(): import("react").JSX.Element;
|
|
7
23
|
export declare function MarkdownEditorImage(): import("react").JSX.Element;
|
|
8
24
|
export declare function MarkdownEditorUploads(): import("react").JSX.Element;
|
|
@@ -17,80 +17,110 @@ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
|
17
17
|
import { BoldIcon, ChevronDownIcon, CodeIcon, ImageIcon, ItalicIcon, LinkIcon, ListChecksIcon, ListIcon, ListOrderedIcon, QuoteIcon, RedoIcon, SquareCodeIcon, StrikethroughIcon, UndoIcon } from "lucide-react";
|
|
18
18
|
import { useEditorState } from "@tiptap/react";
|
|
19
19
|
//#region src/markdown-editor/toolbar.tsx
|
|
20
|
+
function useMarkdownEditorFormatActions() {
|
|
21
|
+
const { editor } = useMarkdownEditor();
|
|
22
|
+
const state = useEditorState({
|
|
23
|
+
editor,
|
|
24
|
+
selector: ({ editor: current }) => ({
|
|
25
|
+
bold: Boolean(current?.isActive("bold")),
|
|
26
|
+
italic: Boolean(current?.isActive("italic")),
|
|
27
|
+
strike: Boolean(current?.isActive("strike")),
|
|
28
|
+
code: Boolean(current?.isActive("code")),
|
|
29
|
+
bulletList: Boolean(current?.isActive("bulletList")),
|
|
30
|
+
orderedList: Boolean(current?.isActive("orderedList")),
|
|
31
|
+
taskList: Boolean(current?.isActive("taskList")),
|
|
32
|
+
blockquote: Boolean(current?.isActive("blockquote")),
|
|
33
|
+
codeBlock: Boolean(current?.isActive("codeBlock"))
|
|
34
|
+
})
|
|
35
|
+
});
|
|
36
|
+
const focused = () => editor?.chain().focus();
|
|
37
|
+
return {
|
|
38
|
+
marks: [
|
|
39
|
+
{
|
|
40
|
+
label: "Bold",
|
|
41
|
+
Icon: BoldIcon,
|
|
42
|
+
active: Boolean(state?.bold),
|
|
43
|
+
run: () => focused()?.toggleBold().run()
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
label: "Italic",
|
|
47
|
+
Icon: ItalicIcon,
|
|
48
|
+
active: Boolean(state?.italic),
|
|
49
|
+
run: () => focused()?.toggleItalic().run()
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
label: "Strikethrough",
|
|
53
|
+
Icon: StrikethroughIcon,
|
|
54
|
+
active: Boolean(state?.strike),
|
|
55
|
+
run: () => focused()?.toggleStrike().run()
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
label: "Inline code",
|
|
59
|
+
Icon: CodeIcon,
|
|
60
|
+
active: Boolean(state?.code),
|
|
61
|
+
run: () => focused()?.toggleCode().run()
|
|
62
|
+
}
|
|
63
|
+
],
|
|
64
|
+
blocks: [
|
|
65
|
+
{
|
|
66
|
+
label: "Bullet list",
|
|
67
|
+
Icon: ListIcon,
|
|
68
|
+
active: Boolean(state?.bulletList),
|
|
69
|
+
run: () => focused()?.toggleBulletList().run()
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
label: "Numbered list",
|
|
73
|
+
Icon: ListOrderedIcon,
|
|
74
|
+
active: Boolean(state?.orderedList),
|
|
75
|
+
run: () => focused()?.toggleOrderedList().run()
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
label: "Task list",
|
|
79
|
+
Icon: ListChecksIcon,
|
|
80
|
+
active: Boolean(state?.taskList),
|
|
81
|
+
run: () => focused()?.toggleTaskList().run()
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
label: "Quote",
|
|
85
|
+
Icon: QuoteIcon,
|
|
86
|
+
active: Boolean(state?.blockquote),
|
|
87
|
+
run: () => focused()?.toggleBlockquote().run()
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
label: "Code block",
|
|
91
|
+
Icon: SquareCodeIcon,
|
|
92
|
+
active: Boolean(state?.codeBlock),
|
|
93
|
+
run: () => focused()?.toggleCodeBlock().run()
|
|
94
|
+
}
|
|
95
|
+
]
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function MarkdownEditorFormatButton({ action, onMouseDown }) {
|
|
99
|
+
const { editor, disabled, source } = useMarkdownEditor();
|
|
100
|
+
const { label, Icon, active, run } = action;
|
|
101
|
+
return /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
|
|
102
|
+
render: /* @__PURE__ */ jsx(ToolbarButton, {
|
|
103
|
+
type: "button",
|
|
104
|
+
iconOnly: true,
|
|
105
|
+
"aria-label": label,
|
|
106
|
+
"aria-pressed": active,
|
|
107
|
+
disabled: disabled || source || !editor,
|
|
108
|
+
onMouseDown,
|
|
109
|
+
onClick: run
|
|
110
|
+
}),
|
|
111
|
+
children: /* @__PURE__ */ jsx(Icon, { "aria-hidden": "true" })
|
|
112
|
+
}), /* @__PURE__ */ jsx(TooltipContent, { children: label })] });
|
|
113
|
+
}
|
|
20
114
|
function MarkdownEditorToolbar({ children }) {
|
|
21
115
|
const { editor, disabled, source, sourceRequired, setSource, uploads } = useMarkdownEditor();
|
|
22
|
-
const
|
|
116
|
+
const { marks, blocks } = useMarkdownEditorFormatActions();
|
|
117
|
+
const history = useEditorState({
|
|
23
118
|
editor,
|
|
24
119
|
selector: ({ editor: current }) => ({
|
|
25
|
-
bold: current?.isActive("bold"),
|
|
26
|
-
italic: current?.isActive("italic"),
|
|
27
|
-
strike: current?.isActive("strike"),
|
|
28
|
-
code: current?.isActive("code"),
|
|
29
|
-
bulletList: current?.isActive("bulletList"),
|
|
30
|
-
orderedList: current?.isActive("orderedList"),
|
|
31
|
-
taskList: current?.isActive("taskList"),
|
|
32
|
-
blockquote: current?.isActive("blockquote"),
|
|
33
|
-
codeBlock: current?.isActive("codeBlock"),
|
|
34
120
|
undo: current?.can().undo(),
|
|
35
121
|
redo: current?.can().redo()
|
|
36
122
|
})
|
|
37
123
|
});
|
|
38
|
-
const actions = [
|
|
39
|
-
{
|
|
40
|
-
label: "Bold",
|
|
41
|
-
Icon: BoldIcon,
|
|
42
|
-
active: state?.bold,
|
|
43
|
-
run: () => editor?.chain().focus().toggleBold().run()
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
label: "Italic",
|
|
47
|
-
Icon: ItalicIcon,
|
|
48
|
-
active: state?.italic,
|
|
49
|
-
run: () => editor?.chain().focus().toggleItalic().run()
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
label: "Strikethrough",
|
|
53
|
-
Icon: StrikethroughIcon,
|
|
54
|
-
active: state?.strike,
|
|
55
|
-
run: () => editor?.chain().focus().toggleStrike().run()
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
label: "Inline code",
|
|
59
|
-
Icon: CodeIcon,
|
|
60
|
-
active: state?.code,
|
|
61
|
-
run: () => editor?.chain().focus().toggleCode().run()
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
label: "Bullet list",
|
|
65
|
-
Icon: ListIcon,
|
|
66
|
-
active: state?.bulletList,
|
|
67
|
-
run: () => editor?.chain().focus().toggleBulletList().run()
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
label: "Numbered list",
|
|
71
|
-
Icon: ListOrderedIcon,
|
|
72
|
-
active: state?.orderedList,
|
|
73
|
-
run: () => editor?.chain().focus().toggleOrderedList().run()
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
label: "Task list",
|
|
77
|
-
Icon: ListChecksIcon,
|
|
78
|
-
active: state?.taskList,
|
|
79
|
-
run: () => editor?.chain().focus().toggleTaskList().run()
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
label: "Quote",
|
|
83
|
-
Icon: QuoteIcon,
|
|
84
|
-
active: state?.blockquote,
|
|
85
|
-
run: () => editor?.chain().focus().toggleBlockquote().run()
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
label: "Code block",
|
|
89
|
-
Icon: SquareCodeIcon,
|
|
90
|
-
active: state?.codeBlock,
|
|
91
|
-
run: () => editor?.chain().focus().toggleCodeBlock().run()
|
|
92
|
-
}
|
|
93
|
-
];
|
|
94
124
|
return /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsx("div", {
|
|
95
125
|
"data-control-ui": "markdown-editor",
|
|
96
126
|
"data-control-family": "markdown-editor",
|
|
@@ -106,18 +136,10 @@ function MarkdownEditorToolbar({ children }) {
|
|
|
106
136
|
children: [
|
|
107
137
|
/* @__PURE__ */ jsx(MarkdownEditorHeading, {}),
|
|
108
138
|
/* @__PURE__ */ jsx(ToolbarSeparator, {}),
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
"aria-label": label,
|
|
114
|
-
"aria-pressed": Boolean(active),
|
|
115
|
-
disabled: disabled || source || !editor,
|
|
116
|
-
onMouseDown: (event) => event.preventDefault(),
|
|
117
|
-
onClick: run
|
|
118
|
-
}),
|
|
119
|
-
children: /* @__PURE__ */ jsx(Icon, { "aria-hidden": "true" })
|
|
120
|
-
}), /* @__PURE__ */ jsx(TooltipContent, { children: label })] }, label)),
|
|
139
|
+
[...marks, ...blocks].map((action) => /* @__PURE__ */ jsx(MarkdownEditorFormatButton, {
|
|
140
|
+
action,
|
|
141
|
+
onMouseDown: (event) => event.preventDefault()
|
|
142
|
+
}, action.label)),
|
|
121
143
|
/* @__PURE__ */ jsx(MarkdownEditorLink, {}),
|
|
122
144
|
/* @__PURE__ */ jsx(MarkdownEditorImage, {}),
|
|
123
145
|
/* @__PURE__ */ jsx(ToolbarSeparator, {}),
|
|
@@ -125,7 +147,7 @@ function MarkdownEditorToolbar({ children }) {
|
|
|
125
147
|
type: "button",
|
|
126
148
|
iconOnly: true,
|
|
127
149
|
"aria-label": "Undo",
|
|
128
|
-
disabled: disabled || source || !
|
|
150
|
+
disabled: disabled || source || !history?.undo,
|
|
129
151
|
onClick: () => editor?.chain().focus().undo().run(),
|
|
130
152
|
children: /* @__PURE__ */ jsx(UndoIcon, { "aria-hidden": "true" })
|
|
131
153
|
}),
|
|
@@ -133,7 +155,7 @@ function MarkdownEditorToolbar({ children }) {
|
|
|
133
155
|
type: "button",
|
|
134
156
|
iconOnly: true,
|
|
135
157
|
"aria-label": "Redo",
|
|
136
|
-
disabled: disabled || source || !
|
|
158
|
+
disabled: disabled || source || !history?.redo,
|
|
137
159
|
onClick: () => editor?.chain().focus().redo().run(),
|
|
138
160
|
children: /* @__PURE__ */ jsx(RedoIcon, { "aria-hidden": "true" })
|
|
139
161
|
}),
|
|
@@ -395,4 +417,4 @@ function MarkdownEditorUploads() {
|
|
|
395
417
|
}) : null] });
|
|
396
418
|
}
|
|
397
419
|
//#endregion
|
|
398
|
-
export { MarkdownEditorImage, MarkdownEditorLink, MarkdownEditorToolbar, MarkdownEditorUploads };
|
|
420
|
+
export { MarkdownEditorFormatButton, MarkdownEditorHeading, MarkdownEditorImage, MarkdownEditorLink, MarkdownEditorToolbar, MarkdownEditorUploads, useMarkdownEditorFormatActions };
|