@jvs-milkdown/crepe 1.2.16 → 1.2.17
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/lib/cjs/feature/block-edit/index.js +35 -16
- package/lib/cjs/feature/block-edit/index.js.map +1 -1
- package/lib/cjs/feature/toolbar/index.js +47 -16
- package/lib/cjs/feature/toolbar/index.js.map +1 -1
- package/lib/cjs/index.js +210 -39
- package/lib/cjs/index.js.map +1 -1
- package/lib/esm/feature/block-edit/index.js +36 -17
- package/lib/esm/feature/block-edit/index.js.map +1 -1
- package/lib/esm/feature/toolbar/index.js +48 -17
- package/lib/esm/feature/toolbar/index.js.map +1 -1
- package/lib/esm/index.js +211 -40
- package/lib/esm/index.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/types/feature/block-edit/menu/component.d.ts.map +1 -1
- package/lib/types/feature/fixed-toolbar/outline-panel.d.ts.map +1 -1
- package/lib/types/feature/toolbar/component.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/feature/block-edit/menu/component.tsx +41 -17
- package/src/feature/fixed-toolbar/outline-panel.tsx +219 -32
- package/src/feature/toolbar/component.tsx +19 -0
package/lib/cjs/index.js
CHANGED
|
@@ -3092,27 +3092,39 @@ const Menu = vue.defineComponent({
|
|
|
3092
3092
|
let activeTextColor = null;
|
|
3093
3093
|
let activeBgColor = null;
|
|
3094
3094
|
if (node && node.nodeSize > 2) {
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
if (
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3095
|
+
const schema = ctx.get(core.schemaCtx);
|
|
3096
|
+
const tcHasMark = schema.marks[textColorSchema.id];
|
|
3097
|
+
const bcHasMark = schema.marks[bgColorSchema.id];
|
|
3098
|
+
if (tcHasMark || bcHasMark) {
|
|
3099
|
+
node.descendants((childNode) => {
|
|
3100
|
+
if (childNode.isText) {
|
|
3101
|
+
if (tcHasMark && !activeTextColor) {
|
|
3102
|
+
const tcType = textColorSchema.type(ctx);
|
|
3103
|
+
const tcMark = childNode.marks.find(
|
|
3104
|
+
(m) => m.type === tcType
|
|
3105
|
+
);
|
|
3106
|
+
if (tcMark) activeTextColor = tcMark.attrs.color;
|
|
3107
|
+
}
|
|
3108
|
+
if (bcHasMark && !activeBgColor) {
|
|
3109
|
+
const bcType = bgColorSchema.type(ctx);
|
|
3110
|
+
const bcMark = childNode.marks.find(
|
|
3111
|
+
(m) => m.type === bcType
|
|
3112
|
+
);
|
|
3113
|
+
if (bcMark) activeBgColor = bcMark.attrs.color;
|
|
3114
|
+
}
|
|
3106
3115
|
}
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
}
|
|
3116
|
+
return false;
|
|
3117
|
+
});
|
|
3118
|
+
}
|
|
3110
3119
|
}
|
|
3111
3120
|
const setBlockColor = (colorValue, isBg) => {
|
|
3112
3121
|
if (pos === null || pos === void 0) return;
|
|
3113
3122
|
const { tr } = view.state;
|
|
3114
3123
|
const targetNode = tr.doc.nodeAt(pos);
|
|
3115
3124
|
if (!targetNode) return;
|
|
3125
|
+
const schema = ctx.get(core.schemaCtx);
|
|
3126
|
+
const hasMark = isBg ? schema.marks[bgColorSchema.id] : schema.marks[textColorSchema.id];
|
|
3127
|
+
if (!hasMark) return;
|
|
3116
3128
|
const markType = isBg ? bgColorSchema.type(ctx) : textColorSchema.type(ctx);
|
|
3117
3129
|
const start = pos + 1;
|
|
3118
3130
|
const end = pos + targetNode.nodeSize - 1;
|
|
@@ -3131,10 +3143,17 @@ const Menu = vue.defineComponent({
|
|
|
3131
3143
|
const { tr } = view.state;
|
|
3132
3144
|
const targetNode = tr.doc.nodeAt(pos);
|
|
3133
3145
|
if (!targetNode) return;
|
|
3146
|
+
const schema = ctx.get(core.schemaCtx);
|
|
3147
|
+
const tcHasMark = schema.marks[textColorSchema.id];
|
|
3148
|
+
const bcHasMark = schema.marks[bgColorSchema.id];
|
|
3134
3149
|
const start = pos + 1;
|
|
3135
3150
|
const end = pos + targetNode.nodeSize - 1;
|
|
3136
|
-
|
|
3137
|
-
|
|
3151
|
+
if (tcHasMark) {
|
|
3152
|
+
tr.removeMark(start, end, textColorSchema.type(ctx));
|
|
3153
|
+
}
|
|
3154
|
+
if (bcHasMark) {
|
|
3155
|
+
tr.removeMark(start, end, bgColorSchema.type(ctx));
|
|
3156
|
+
}
|
|
3138
3157
|
view.dispatch(tr);
|
|
3139
3158
|
showColorMenu.value = false;
|
|
3140
3159
|
hide();
|
|
@@ -6165,6 +6184,10 @@ const Toolbar = vue.defineComponent({
|
|
|
6165
6184
|
return { textColor: null, bgColor: null };
|
|
6166
6185
|
const view = ctx.get(core.editorViewCtx);
|
|
6167
6186
|
const { state } = view;
|
|
6187
|
+
const schema = ctx.get(core.schemaCtx);
|
|
6188
|
+
const tcHasMark = schema.marks[textColorSchema.id];
|
|
6189
|
+
const bcHasMark = schema.marks[bgColorSchema.id];
|
|
6190
|
+
if (!tcHasMark || !bcHasMark) return { textColor: null, bgColor: null };
|
|
6168
6191
|
const tcType = textColorSchema.type(ctx);
|
|
6169
6192
|
const bcType = bgColorSchema.type(ctx);
|
|
6170
6193
|
const { $cursor, ranges } = state.selection;
|
|
@@ -6209,6 +6232,10 @@ const Toolbar = vue.defineComponent({
|
|
|
6209
6232
|
const { state, dispatch } = view;
|
|
6210
6233
|
const { tr } = state;
|
|
6211
6234
|
const { from, to, empty } = state.selection;
|
|
6235
|
+
const schema = ctx.get(core.schemaCtx);
|
|
6236
|
+
const tcHasMark = schema.marks[textColorSchema.id];
|
|
6237
|
+
const bcHasMark = schema.marks[bgColorSchema.id];
|
|
6238
|
+
if (!tcHasMark || !bcHasMark) return;
|
|
6212
6239
|
const textColorType = textColorSchema.type(ctx);
|
|
6213
6240
|
const bgColorType = bgColorSchema.type(ctx);
|
|
6214
6241
|
if (empty) {
|
|
@@ -6230,6 +6257,10 @@ const Toolbar = vue.defineComponent({
|
|
|
6230
6257
|
return { fontFamily: null, fontSize: null };
|
|
6231
6258
|
const view = ctx.get(core.editorViewCtx);
|
|
6232
6259
|
const { state } = view;
|
|
6260
|
+
const schema = ctx.get(core.schemaCtx);
|
|
6261
|
+
const ffHasMark = schema.marks[fontFamilySchema.id];
|
|
6262
|
+
const fsHasMark = schema.marks[fontSizeSchema.id];
|
|
6263
|
+
if (!ffHasMark || !fsHasMark) return { fontFamily: null, fontSize: null };
|
|
6233
6264
|
const ffType = fontFamilySchema.type(ctx);
|
|
6234
6265
|
const fsType = fontSizeSchema.type(ctx);
|
|
6235
6266
|
const { $cursor, ranges } = state.selection;
|
|
@@ -9205,6 +9236,11 @@ const OutlinePanel = vue.defineComponent({
|
|
|
9205
9236
|
const activeId = vue.ref("");
|
|
9206
9237
|
const collapsedIds = vue.ref(/* @__PURE__ */ new Set());
|
|
9207
9238
|
let scrollLock = false;
|
|
9239
|
+
const clickedActiveId = vue.ref(null);
|
|
9240
|
+
const clearClickedActive = () => {
|
|
9241
|
+
if (scrollLock) return;
|
|
9242
|
+
clickedActiveId.value = null;
|
|
9243
|
+
};
|
|
9208
9244
|
const hasChildren = (index) => {
|
|
9209
9245
|
const current = items.value[index];
|
|
9210
9246
|
if (!current) return false;
|
|
@@ -9259,6 +9295,10 @@ const OutlinePanel = vue.defineComponent({
|
|
|
9259
9295
|
if (scrollLock) return;
|
|
9260
9296
|
const view = props.ctx.get(core.editorViewCtx);
|
|
9261
9297
|
if (!view || !view.dom) return;
|
|
9298
|
+
if (clickedActiveId.value) {
|
|
9299
|
+
activeId.value = clickedActiveId.value;
|
|
9300
|
+
return;
|
|
9301
|
+
}
|
|
9262
9302
|
const headings = Array.from(
|
|
9263
9303
|
view.dom.querySelectorAll("h1, h2, h3, h4, h5, h6")
|
|
9264
9304
|
);
|
|
@@ -9266,14 +9306,39 @@ const OutlinePanel = vue.defineComponent({
|
|
|
9266
9306
|
activeId.value = "";
|
|
9267
9307
|
return;
|
|
9268
9308
|
}
|
|
9269
|
-
|
|
9270
|
-
|
|
9271
|
-
|
|
9309
|
+
let scrollContainer = view.dom.parentElement;
|
|
9310
|
+
while (scrollContainer && scrollContainer !== document.body) {
|
|
9311
|
+
const style = window.getComputedStyle(scrollContainer);
|
|
9312
|
+
const overflowY = style.overflowY;
|
|
9313
|
+
if ((overflowY === "auto" || overflowY === "scroll") && scrollContainer.scrollHeight > scrollContainer.clientHeight) {
|
|
9314
|
+
break;
|
|
9315
|
+
}
|
|
9316
|
+
scrollContainer = scrollContainer.parentElement;
|
|
9317
|
+
}
|
|
9318
|
+
const isRootScroll = !scrollContainer || scrollContainer === document.body || scrollContainer === document.documentElement;
|
|
9319
|
+
const actualScrollContainer = isRootScroll ? document.scrollingElement || document.documentElement || document.body : scrollContainer;
|
|
9320
|
+
const rootNode = view.dom.getRootNode();
|
|
9321
|
+
let toolbar = rootNode.querySelector(
|
|
9272
9322
|
".milkdown-fixed-toolbar"
|
|
9273
9323
|
);
|
|
9324
|
+
if (!toolbar) {
|
|
9325
|
+
toolbar = document.querySelector(
|
|
9326
|
+
".milkdown-fixed-toolbar"
|
|
9327
|
+
);
|
|
9328
|
+
}
|
|
9274
9329
|
const toolbarHeight = (toolbar == null ? void 0 : toolbar.offsetHeight) || 0;
|
|
9275
|
-
const containerRect =
|
|
9276
|
-
const
|
|
9330
|
+
const containerRect = actualScrollContainer.getBoundingClientRect();
|
|
9331
|
+
const containerTop = isRootScroll ? 0 : Math.max(0, containerRect.top);
|
|
9332
|
+
let scrollTop = actualScrollContainer.scrollTop;
|
|
9333
|
+
let clientHeight = actualScrollContainer.clientHeight;
|
|
9334
|
+
let scrollHeight = actualScrollContainer.scrollHeight;
|
|
9335
|
+
if (isRootScroll) {
|
|
9336
|
+
scrollTop = window.scrollY || document.documentElement.scrollTop;
|
|
9337
|
+
clientHeight = window.innerHeight || document.documentElement.clientHeight;
|
|
9338
|
+
scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight;
|
|
9339
|
+
}
|
|
9340
|
+
const isAtBottom = scrollTop + clientHeight >= scrollHeight - 15;
|
|
9341
|
+
const threshold = isAtBottom ? containerTop + clientHeight : containerTop + toolbarHeight + 50;
|
|
9277
9342
|
let active = headings[0];
|
|
9278
9343
|
for (const h2 of headings) {
|
|
9279
9344
|
const rect = h2.getBoundingClientRect();
|
|
@@ -9283,43 +9348,120 @@ const OutlinePanel = vue.defineComponent({
|
|
|
9283
9348
|
break;
|
|
9284
9349
|
}
|
|
9285
9350
|
}
|
|
9286
|
-
|
|
9351
|
+
const activeItem = items.value.find((item) => {
|
|
9352
|
+
try {
|
|
9353
|
+
const activePos = view.posAtDOM(active, 0);
|
|
9354
|
+
const isPosMatch = item.pos === activePos - 1;
|
|
9355
|
+
let isDepthMatch = false;
|
|
9356
|
+
const $pos = view.state.doc.resolve(activePos);
|
|
9357
|
+
for (let d = $pos.depth; d >= 0; d--) {
|
|
9358
|
+
if ($pos.node(d).type.name === "heading") {
|
|
9359
|
+
if (item.pos === $pos.before(d)) {
|
|
9360
|
+
isDepthMatch = true;
|
|
9361
|
+
break;
|
|
9362
|
+
}
|
|
9363
|
+
}
|
|
9364
|
+
}
|
|
9365
|
+
if (isPosMatch || isDepthMatch) {
|
|
9366
|
+
return true;
|
|
9367
|
+
}
|
|
9368
|
+
} catch (e) {
|
|
9369
|
+
}
|
|
9370
|
+
const dom = view.nodeDOM(item.pos);
|
|
9371
|
+
const domMatch = dom === active || dom instanceof HTMLElement && active && dom.contains(active);
|
|
9372
|
+
if (domMatch) {
|
|
9373
|
+
return true;
|
|
9374
|
+
}
|
|
9375
|
+
return false;
|
|
9376
|
+
});
|
|
9377
|
+
const newActiveId = activeItem ? activeItem.id : (active == null ? void 0 : active.id) || "";
|
|
9378
|
+
activeId.value = newActiveId;
|
|
9287
9379
|
};
|
|
9288
|
-
const scrollToHeading = (
|
|
9380
|
+
const scrollToHeading = (item) => {
|
|
9289
9381
|
const view = props.ctx.get(core.editorViewCtx);
|
|
9290
9382
|
if (!view) return;
|
|
9291
9383
|
try {
|
|
9292
|
-
|
|
9293
|
-
|
|
9294
|
-
|
|
9384
|
+
let headingEl = view.nodeDOM(item.pos);
|
|
9385
|
+
if (!headingEl) {
|
|
9386
|
+
try {
|
|
9387
|
+
const domAt = view.domAtPos(item.pos);
|
|
9388
|
+
let node = domAt.node;
|
|
9389
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
9390
|
+
node = node.parentElement;
|
|
9391
|
+
}
|
|
9392
|
+
if (node instanceof HTMLElement) {
|
|
9393
|
+
headingEl = node.closest(
|
|
9394
|
+
"h1, h2, h3, h4, h5, h6"
|
|
9395
|
+
);
|
|
9396
|
+
if (!headingEl && domAt.offset < node.childNodes.length) {
|
|
9397
|
+
const child = node.childNodes[domAt.offset];
|
|
9398
|
+
if (child instanceof HTMLElement) {
|
|
9399
|
+
headingEl = child.closest("h1, h2, h3, h4, h5, h6") || child.querySelector(
|
|
9400
|
+
"h1, h2, h3, h4, h5, h6"
|
|
9401
|
+
);
|
|
9402
|
+
}
|
|
9403
|
+
}
|
|
9404
|
+
}
|
|
9405
|
+
} catch (e) {
|
|
9406
|
+
}
|
|
9407
|
+
}
|
|
9408
|
+
if (!headingEl) {
|
|
9409
|
+
headingEl = view.dom.querySelector(
|
|
9410
|
+
`[id="${item.id}"]`
|
|
9411
|
+
);
|
|
9412
|
+
}
|
|
9295
9413
|
if (!headingEl) return;
|
|
9296
|
-
|
|
9297
|
-
|
|
9414
|
+
let scrollContainer = view.dom.parentElement;
|
|
9415
|
+
while (scrollContainer && scrollContainer !== document.body) {
|
|
9416
|
+
const style = window.getComputedStyle(scrollContainer);
|
|
9417
|
+
const overflowY = style.overflowY;
|
|
9418
|
+
if ((overflowY === "auto" || overflowY === "scroll") && scrollContainer.scrollHeight > scrollContainer.clientHeight) {
|
|
9419
|
+
break;
|
|
9420
|
+
}
|
|
9421
|
+
scrollContainer = scrollContainer.parentElement;
|
|
9422
|
+
}
|
|
9423
|
+
const isRootScroll = !scrollContainer || scrollContainer === document.body || scrollContainer === document.documentElement;
|
|
9424
|
+
const actualScrollContainer = isRootScroll ? document.scrollingElement || document.documentElement || document.body : scrollContainer;
|
|
9298
9425
|
scrollLock = true;
|
|
9299
|
-
const
|
|
9426
|
+
const rootNode = view.dom.getRootNode();
|
|
9427
|
+
let toolbar = rootNode.querySelector(
|
|
9300
9428
|
".milkdown-fixed-toolbar"
|
|
9301
9429
|
);
|
|
9430
|
+
if (!toolbar) {
|
|
9431
|
+
toolbar = document.querySelector(
|
|
9432
|
+
".milkdown-fixed-toolbar"
|
|
9433
|
+
);
|
|
9434
|
+
}
|
|
9302
9435
|
const toolbarHeight = (toolbar == null ? void 0 : toolbar.offsetHeight) || 0;
|
|
9303
|
-
const
|
|
9436
|
+
const containerRectTop = isRootScroll ? 0 : actualScrollContainer.getBoundingClientRect().top;
|
|
9304
9437
|
const headingRect = headingEl.getBoundingClientRect();
|
|
9305
|
-
|
|
9306
|
-
|
|
9307
|
-
behavior: "smooth"
|
|
9308
|
-
});
|
|
9438
|
+
const targetTop = actualScrollContainer.scrollTop + headingRect.top - containerRectTop - toolbarHeight - 20;
|
|
9439
|
+
const scrollTarget = isRootScroll ? window : actualScrollContainer;
|
|
9309
9440
|
let timer;
|
|
9310
9441
|
const onScrollEnd = () => {
|
|
9311
9442
|
clearTimeout(timer);
|
|
9312
9443
|
timer = setTimeout(() => {
|
|
9313
9444
|
scrollLock = false;
|
|
9314
|
-
|
|
9445
|
+
scrollTarget.removeEventListener("scroll", onScrollEnd);
|
|
9315
9446
|
checkActive();
|
|
9316
9447
|
}, 150);
|
|
9317
9448
|
};
|
|
9318
|
-
|
|
9449
|
+
scrollTarget.addEventListener("scroll", onScrollEnd);
|
|
9319
9450
|
setTimeout(() => {
|
|
9320
9451
|
scrollLock = false;
|
|
9321
|
-
|
|
9452
|
+
scrollTarget.removeEventListener("scroll", onScrollEnd);
|
|
9322
9453
|
}, 2e3);
|
|
9454
|
+
if (isRootScroll) {
|
|
9455
|
+
window.scrollTo({
|
|
9456
|
+
top: targetTop,
|
|
9457
|
+
behavior: "smooth"
|
|
9458
|
+
});
|
|
9459
|
+
} else {
|
|
9460
|
+
actualScrollContainer.scrollTo({
|
|
9461
|
+
top: targetTop,
|
|
9462
|
+
behavior: "smooth"
|
|
9463
|
+
});
|
|
9464
|
+
}
|
|
9323
9465
|
} catch (e) {
|
|
9324
9466
|
scrollLock = false;
|
|
9325
9467
|
updateOutline();
|
|
@@ -9330,6 +9472,27 @@ const OutlinePanel = vue.defineComponent({
|
|
|
9330
9472
|
vue.onMounted(() => {
|
|
9331
9473
|
updateOutline();
|
|
9332
9474
|
checkActive();
|
|
9475
|
+
window.addEventListener("wheel", clearClickedActive, { passive: true });
|
|
9476
|
+
window.addEventListener("touchmove", clearClickedActive, {
|
|
9477
|
+
passive: true
|
|
9478
|
+
});
|
|
9479
|
+
window.addEventListener("pointerdown", clearClickedActive, {
|
|
9480
|
+
passive: true
|
|
9481
|
+
});
|
|
9482
|
+
const keyHandler = (e) => {
|
|
9483
|
+
if ([
|
|
9484
|
+
"ArrowUp",
|
|
9485
|
+
"ArrowDown",
|
|
9486
|
+
"PageUp",
|
|
9487
|
+
"PageDown",
|
|
9488
|
+
"Home",
|
|
9489
|
+
"End"
|
|
9490
|
+
].includes(e.key)) {
|
|
9491
|
+
clearClickedActive();
|
|
9492
|
+
}
|
|
9493
|
+
};
|
|
9494
|
+
window.addEventListener("keydown", keyHandler, { passive: true });
|
|
9495
|
+
vue.onMounted._keyHandler = keyHandler;
|
|
9333
9496
|
interval = setInterval(() => {
|
|
9334
9497
|
if (viewState.value.outlineVisible) {
|
|
9335
9498
|
updateOutline();
|
|
@@ -9346,6 +9509,13 @@ const OutlinePanel = vue.defineComponent({
|
|
|
9346
9509
|
if (interval) clearInterval(interval);
|
|
9347
9510
|
if (pollInterval) clearInterval(pollInterval);
|
|
9348
9511
|
window.removeEventListener("scroll", checkActive, true);
|
|
9512
|
+
window.removeEventListener("wheel", clearClickedActive);
|
|
9513
|
+
window.removeEventListener("touchmove", clearClickedActive);
|
|
9514
|
+
window.removeEventListener("pointerdown", clearClickedActive);
|
|
9515
|
+
const keyHandler = vue.onMounted._keyHandler;
|
|
9516
|
+
if (keyHandler) {
|
|
9517
|
+
window.removeEventListener("keydown", keyHandler);
|
|
9518
|
+
}
|
|
9349
9519
|
});
|
|
9350
9520
|
let startX = 0;
|
|
9351
9521
|
let startWidth = 0;
|
|
@@ -9473,7 +9643,8 @@ const OutlinePanel = vue.defineComponent({
|
|
|
9473
9643
|
key: item.id,
|
|
9474
9644
|
onClick: () => {
|
|
9475
9645
|
activeId.value = item.id;
|
|
9476
|
-
|
|
9646
|
+
clickedActiveId.value = item.id;
|
|
9647
|
+
scrollToHeading(item);
|
|
9477
9648
|
},
|
|
9478
9649
|
style: {
|
|
9479
9650
|
display: "flex",
|
|
@@ -9530,7 +9701,7 @@ const OutlinePanel = vue.defineComponent({
|
|
|
9530
9701
|
e.currentTarget.style.backgroundColor = "transparent";
|
|
9531
9702
|
}
|
|
9532
9703
|
},
|
|
9533
|
-
"\
|
|
9704
|
+
"\u25BC",
|
|
9534
9705
|
" "
|
|
9535
9706
|
),
|
|
9536
9707
|
/* @__PURE__ */ vue.h(
|