|
13 | 13 | * This is unstable and not part of the public API and should not be used by
|
14 | 14 | * production systems. This file may be update/removed without notice.
|
15 | 15 | */
|
| 16 | +import type {BlockMap} from 'BlockMap'; |
16 | 17 | import type ContentState from 'ContentState';
|
17 | 18 | import type {DraftBlockType} from 'DraftBlockType';
|
18 | 19 | import type {DraftEditorCommand} from 'DraftEditorCommand';
|
@@ -172,7 +173,12 @@ const NestedRichTextEditorUtil: RichTextUtils = {
|
172 | 173 | return EditorState.push(
|
173 | 174 | editorState,
|
174 | 175 | withoutBlockStyle,
|
175 |
| - 'change-block-type', |
| 176 | + withoutBlockStyle |
| 177 | + .getBlockMap() |
| 178 | + .get(currentBlock.getKey()) |
| 179 | + .getType() === 'unstyled' |
| 180 | + ? 'change-block-type' |
| 181 | + : 'adjust-depth', |
176 | 182 | );
|
177 | 183 | }
|
178 | 184 |
|
@@ -350,117 +356,10 @@ const NestedRichTextEditorUtil: RichTextUtils = {
|
350 | 356 | // on un-tab
|
351 | 357 | } else {
|
352 | 358 | // if the block isn't nested, do nothing
|
353 |
| - const parentKey = block.getParentKey(); |
354 |
| - if (parentKey == null) { |
| 359 | + if (block.getParentKey() == null) { |
355 | 360 | return editorState;
|
356 | 361 | }
|
357 |
| - const parent = blockMap.get(parentKey); |
358 |
| - const existingChildren = parent.getChildKeys(); |
359 |
| - const blockIndex = existingChildren.indexOf(key); |
360 |
| - if (blockIndex === 0 || blockIndex === existingChildren.count() - 1) { |
361 |
| - blockMap = DraftTreeOperations.moveChildUp(blockMap, key); |
362 |
| - } else { |
363 |
| - // split the block into [0, blockIndex] in parent & the rest in a new block |
364 |
| - const prevChildren = existingChildren.slice(0, blockIndex + 1); |
365 |
| - const nextChildren = existingChildren.slice(blockIndex + 1); |
366 |
| - blockMap = blockMap.set( |
367 |
| - parentKey, |
368 |
| - parent.merge({children: prevChildren}), |
369 |
| - ); |
370 |
| - const newBlock = new ContentBlockNode({ |
371 |
| - key: generateRandomKey(), |
372 |
| - text: '', |
373 |
| - depth: parent.getDepth(), |
374 |
| - type: parent.getType(), |
375 |
| - children: nextChildren, |
376 |
| - parent: parent.getParentKey(), |
377 |
| - }); |
378 |
| - // add new block just before its the original next sibling in the block map |
379 |
| - // TODO(T33894878): Remove the map reordering code & fix converter after launch |
380 |
| - invariant( |
381 |
| - nextSiblingKey != null, |
382 |
| - 'block must have a next sibling here', |
383 |
| - ); |
384 |
| - const blocks = blockMap.toSeq(); |
385 |
| - blockMap = blocks |
386 |
| - .takeUntil(block => block.getKey() === nextSiblingKey) |
387 |
| - .concat( |
388 |
| - [[newBlock.getKey(), newBlock]], |
389 |
| - blocks.skipUntil(block => block.getKey() === nextSiblingKey), |
390 |
| - ) |
391 |
| - .toOrderedMap(); |
392 |
| - |
393 |
| - // set the nextChildren's parent to the new block |
394 |
| - blockMap = blockMap.map( |
395 |
| - block => |
396 |
| - nextChildren.includes(block.getKey()) |
397 |
| - ? block.merge({parent: newBlock.getKey()}) |
398 |
| - : block, |
399 |
| - ); |
400 |
| - // update the next/previous pointers for the children at the split |
401 |
| - blockMap = blockMap |
402 |
| - .set(key, block.merge({nextSibling: null})) |
403 |
| - .set( |
404 |
| - nextSiblingKey, |
405 |
| - blockMap.get(nextSiblingKey).merge({prevSibling: null}), |
406 |
| - ); |
407 |
| - const parentNextSiblingKey = parent.getNextSiblingKey(); |
408 |
| - if (parentNextSiblingKey != null) { |
409 |
| - blockMap = DraftTreeOperations.updateSibling( |
410 |
| - blockMap, |
411 |
| - newBlock.getKey(), |
412 |
| - parentNextSiblingKey, |
413 |
| - ); |
414 |
| - } |
415 |
| - blockMap = DraftTreeOperations.updateSibling( |
416 |
| - blockMap, |
417 |
| - parentKey, |
418 |
| - newBlock.getKey(), |
419 |
| - ); |
420 |
| - blockMap = DraftTreeOperations.moveChildUp(blockMap, key); |
421 |
| - } |
422 |
| - |
423 |
| - // on untab, we also want to unnest any sibling blocks that become two levels deep |
424 |
| - // ensure that block's old parent does not have a non-leaf as its first child. |
425 |
| - let childWasUntabbed = false; |
426 |
| - if (parentKey != null) { |
427 |
| - let parent = blockMap.get(parentKey); |
428 |
| - while (parent != null) { |
429 |
| - const children = parent.getChildKeys(); |
430 |
| - const firstChildKey = children.first(); |
431 |
| - invariant( |
432 |
| - firstChildKey != null, |
433 |
| - 'parent must have at least one child', |
434 |
| - ); |
435 |
| - const firstChild = blockMap.get(firstChildKey); |
436 |
| - if (firstChild.getChildKeys().count() === 0) { |
437 |
| - break; |
438 |
| - } else { |
439 |
| - blockMap = DraftTreeOperations.moveChildUp(blockMap, firstChildKey); |
440 |
| - parent = blockMap.get(parentKey); |
441 |
| - childWasUntabbed = true; |
442 |
| - } |
443 |
| - } |
444 |
| - } |
445 |
| - |
446 |
| - // now, we may be in a state with two non-leaf blocks of the same type |
447 |
| - // next to each other |
448 |
| - if (childWasUntabbed && parentKey != null) { |
449 |
| - const parent = blockMap.get(parentKey); |
450 |
| - const prevSiblingKey = |
451 |
| - parent != null // parent may have been deleted |
452 |
| - ? parent.getPrevSiblingKey() |
453 |
| - : null; |
454 |
| - if (prevSiblingKey != null && parent.getChildKeys().count() > 0) { |
455 |
| - const prevSibling = blockMap.get(prevSiblingKey); |
456 |
| - if (prevSibling != null && prevSibling.getChildKeys().count() > 0) { |
457 |
| - blockMap = DraftTreeOperations.mergeBlocks( |
458 |
| - blockMap, |
459 |
| - prevSiblingKey, |
460 |
| - ); |
461 |
| - } |
462 |
| - } |
463 |
| - } |
| 362 | + blockMap = onUntab(blockMap, block); |
464 | 363 | }
|
465 | 364 | content = editorState.getCurrentContent().merge({
|
466 | 365 | blockMap: blockMap,
|
@@ -574,12 +473,129 @@ const NestedRichTextEditorUtil: RichTextUtils = {
|
574 | 473 | return null;
|
575 | 474 | }
|
576 | 475 |
|
| 476 | + const depth = block.getDepth(); |
577 | 477 | if (type !== 'unstyled') {
|
| 478 | + if ( |
| 479 | + (type === 'unordered-list-item' || type === 'ordered-list-item') && |
| 480 | + depth > 0 |
| 481 | + ) { |
| 482 | + let newBlockMap = onUntab(content.getBlockMap(), block); |
| 483 | + newBlockMap = newBlockMap.set( |
| 484 | + key, |
| 485 | + newBlockMap.get(key).merge({depth: depth - 1}), |
| 486 | + ); |
| 487 | + return content.merge({blockMap: newBlockMap}); |
| 488 | + } |
578 | 489 | return DraftModifier.setBlockType(content, selection, 'unstyled');
|
579 | 490 | }
|
580 | 491 | }
|
581 | 492 | return null;
|
582 | 493 | },
|
583 | 494 | };
|
584 | 495 |
|
| 496 | +const onUntab = (blockMap: BlockMap, block: ContentBlockNode): BlockMap => { |
| 497 | + const key = block.getKey(); |
| 498 | + const parentKey = block.getParentKey(); |
| 499 | + const nextSiblingKey = block.getNextSiblingKey(); |
| 500 | + if (parentKey == null) { |
| 501 | + return blockMap; |
| 502 | + } |
| 503 | + const parent = blockMap.get(parentKey); |
| 504 | + const existingChildren = parent.getChildKeys(); |
| 505 | + const blockIndex = existingChildren.indexOf(key); |
| 506 | + if (blockIndex === 0 || blockIndex === existingChildren.count() - 1) { |
| 507 | + blockMap = DraftTreeOperations.moveChildUp(blockMap, key); |
| 508 | + } else { |
| 509 | + // split the block into [0, blockIndex] in parent & the rest in a new block |
| 510 | + const prevChildren = existingChildren.slice(0, blockIndex + 1); |
| 511 | + const nextChildren = existingChildren.slice(blockIndex + 1); |
| 512 | + blockMap = blockMap.set(parentKey, parent.merge({children: prevChildren})); |
| 513 | + const newBlock = new ContentBlockNode({ |
| 514 | + key: generateRandomKey(), |
| 515 | + text: '', |
| 516 | + depth: parent.getDepth(), |
| 517 | + type: parent.getType(), |
| 518 | + children: nextChildren, |
| 519 | + parent: parent.getParentKey(), |
| 520 | + }); |
| 521 | + // add new block just before its the original next sibling in the block map |
| 522 | + // TODO(T33894878): Remove the map reordering code & fix converter after launch |
| 523 | + invariant(nextSiblingKey != null, 'block must have a next sibling here'); |
| 524 | + const blocks = blockMap.toSeq(); |
| 525 | + blockMap = blocks |
| 526 | + .takeUntil(block => block.getKey() === nextSiblingKey) |
| 527 | + .concat( |
| 528 | + [[newBlock.getKey(), newBlock]], |
| 529 | + blocks.skipUntil(block => block.getKey() === nextSiblingKey), |
| 530 | + ) |
| 531 | + .toOrderedMap(); |
| 532 | + |
| 533 | + // set the nextChildren's parent to the new block |
| 534 | + blockMap = blockMap.map( |
| 535 | + block => |
| 536 | + nextChildren.includes(block.getKey()) |
| 537 | + ? block.merge({parent: newBlock.getKey()}) |
| 538 | + : block, |
| 539 | + ); |
| 540 | + // update the next/previous pointers for the children at the split |
| 541 | + blockMap = blockMap |
| 542 | + .set(key, block.merge({nextSibling: null})) |
| 543 | + .set( |
| 544 | + nextSiblingKey, |
| 545 | + blockMap.get(nextSiblingKey).merge({prevSibling: null}), |
| 546 | + ); |
| 547 | + const parentNextSiblingKey = parent.getNextSiblingKey(); |
| 548 | + if (parentNextSiblingKey != null) { |
| 549 | + blockMap = DraftTreeOperations.updateSibling( |
| 550 | + blockMap, |
| 551 | + newBlock.getKey(), |
| 552 | + parentNextSiblingKey, |
| 553 | + ); |
| 554 | + } |
| 555 | + blockMap = DraftTreeOperations.updateSibling( |
| 556 | + blockMap, |
| 557 | + parentKey, |
| 558 | + newBlock.getKey(), |
| 559 | + ); |
| 560 | + blockMap = DraftTreeOperations.moveChildUp(blockMap, key); |
| 561 | + } |
| 562 | + |
| 563 | + // on untab, we also want to unnest any sibling blocks that become two levels deep |
| 564 | + // ensure that block's old parent does not have a non-leaf as its first child. |
| 565 | + let childWasUntabbed = false; |
| 566 | + if (parentKey != null) { |
| 567 | + let parent = blockMap.get(parentKey); |
| 568 | + while (parent != null) { |
| 569 | + const children = parent.getChildKeys(); |
| 570 | + const firstChildKey = children.first(); |
| 571 | + invariant(firstChildKey != null, 'parent must have at least one child'); |
| 572 | + const firstChild = blockMap.get(firstChildKey); |
| 573 | + if (firstChild.getChildKeys().count() === 0) { |
| 574 | + break; |
| 575 | + } else { |
| 576 | + blockMap = DraftTreeOperations.moveChildUp(blockMap, firstChildKey); |
| 577 | + parent = blockMap.get(parentKey); |
| 578 | + childWasUntabbed = true; |
| 579 | + } |
| 580 | + } |
| 581 | + } |
| 582 | + |
| 583 | + // now, we may be in a state with two non-leaf blocks of the same type |
| 584 | + // next to each other |
| 585 | + if (childWasUntabbed && parentKey != null) { |
| 586 | + const parent = blockMap.get(parentKey); |
| 587 | + const prevSiblingKey = |
| 588 | + parent != null // parent may have been deleted |
| 589 | + ? parent.getPrevSiblingKey() |
| 590 | + : null; |
| 591 | + if (prevSiblingKey != null && parent.getChildKeys().count() > 0) { |
| 592 | + const prevSibling = blockMap.get(prevSiblingKey); |
| 593 | + if (prevSibling != null && prevSibling.getChildKeys().count() > 0) { |
| 594 | + blockMap = DraftTreeOperations.mergeBlocks(blockMap, prevSiblingKey); |
| 595 | + } |
| 596 | + } |
| 597 | + } |
| 598 | + return blockMap; |
| 599 | +}; |
| 600 | + |
585 | 601 | module.exports = NestedRichTextEditorUtil;
|
0 commit comments