|
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