@plait/core 0.0.13 → 0.0.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/board/board.component.d.ts +3 -1
- package/esm2020/board/board.component.mjs +14 -7
- package/esm2020/interfaces/board.mjs +1 -1
- package/esm2020/interfaces/history.mjs +2 -0
- package/esm2020/interfaces/index.mjs +2 -1
- package/esm2020/interfaces/operation.mjs +79 -2
- package/esm2020/plugins/create-board.mjs +8 -1
- package/esm2020/plugins/with-history.mjs +123 -0
- package/esm2020/transfroms/node.mjs +3 -2
- package/esm2020/utils/history.mjs +28 -0
- package/esm2020/utils/index.mjs +2 -1
- package/fesm2015/plait-core.mjs +389 -167
- package/fesm2015/plait-core.mjs.map +1 -1
- package/fesm2020/plait-core.mjs +408 -170
- package/fesm2020/plait-core.mjs.map +1 -1
- package/interfaces/board.d.ts +6 -0
- package/interfaces/history.d.ts +5 -0
- package/interfaces/index.d.ts +1 -0
- package/interfaces/operation.d.ts +7 -3
- package/package.json +1 -1
- package/plugins/with-history.d.ts +24 -0
- package/utils/history.d.ts +13 -0
- package/utils/index.d.ts +1 -0
- package/styles/styles.scss +0 -62
- package/styles/theme.scss +0 -14
package/fesm2015/plait-core.mjs
CHANGED
|
@@ -4,9 +4,9 @@ import produce, { createDraft, finishDraft, isDraft } from 'immer';
|
|
|
4
4
|
import { Subject, fromEvent } from 'rxjs';
|
|
5
5
|
import { takeUntil, filter } from 'rxjs/operators';
|
|
6
6
|
import rough from 'roughjs/bin/rough';
|
|
7
|
+
import { isKeyHotkey, isHotkey } from 'is-hotkey';
|
|
7
8
|
import * as i2 from '@angular/common';
|
|
8
9
|
import { BrowserModule } from '@angular/platform-browser';
|
|
9
|
-
import { isKeyHotkey } from 'is-hotkey';
|
|
10
10
|
|
|
11
11
|
// record richtext type status
|
|
12
12
|
const FLUSHING = new WeakMap();
|
|
@@ -341,7 +341,8 @@ function setNode(board, props, path) {
|
|
|
341
341
|
board.apply(operation);
|
|
342
342
|
}
|
|
343
343
|
function removeNode(board, path) {
|
|
344
|
-
const
|
|
344
|
+
const node = PlaitNode.get(board, path);
|
|
345
|
+
const operation = { type: 'remove_node', path, node };
|
|
345
346
|
board.apply(operation);
|
|
346
347
|
}
|
|
347
348
|
function moveNode(board, path, newPath) {
|
|
@@ -390,9 +391,16 @@ function createBoard(host, children, options) {
|
|
|
390
391
|
},
|
|
391
392
|
children,
|
|
392
393
|
operations: [],
|
|
394
|
+
history: {
|
|
395
|
+
redos: [],
|
|
396
|
+
undos: []
|
|
397
|
+
},
|
|
393
398
|
selection: null,
|
|
394
399
|
cursor: BaseCursorStatus.select,
|
|
395
400
|
readonly: options.readonly,
|
|
401
|
+
allowClearBoard: options.allowClearBoard,
|
|
402
|
+
undo: () => { },
|
|
403
|
+
redo: () => { },
|
|
396
404
|
apply: (operation) => {
|
|
397
405
|
board.operations.push(operation);
|
|
398
406
|
Transforms.transform(board, operation);
|
|
@@ -533,8 +541,372 @@ function withSelection(board) {
|
|
|
533
541
|
const isSetViewportOperation = (value) => {
|
|
534
542
|
return value.type === 'set_viewport';
|
|
535
543
|
};
|
|
544
|
+
const inverse = (op) => {
|
|
545
|
+
switch (op.type) {
|
|
546
|
+
case 'insert_node': {
|
|
547
|
+
return Object.assign(Object.assign({}, op), { type: 'remove_node' });
|
|
548
|
+
}
|
|
549
|
+
case 'remove_node': {
|
|
550
|
+
return Object.assign(Object.assign({}, op), { type: 'insert_node' });
|
|
551
|
+
}
|
|
552
|
+
case 'move_node': {
|
|
553
|
+
const { newPath, path } = op;
|
|
554
|
+
// PERF: in this case the move operation is a no-op anyways.
|
|
555
|
+
if (Path.equals(newPath, path)) {
|
|
556
|
+
return op;
|
|
557
|
+
}
|
|
558
|
+
// If the move happens completely within a single parent the path and
|
|
559
|
+
// newPath are stable with respect to each other.
|
|
560
|
+
if (Path.isSibling(path, newPath)) {
|
|
561
|
+
return Object.assign(Object.assign({}, op), { path: newPath, newPath: path });
|
|
562
|
+
}
|
|
563
|
+
// If the move does not happen within a single parent it is possible
|
|
564
|
+
// for the move to impact the true path to the location where the node
|
|
565
|
+
// was removed from and where it was inserted. We have to adjust for this
|
|
566
|
+
// and find the original path. We can accomplish this (only in non-sibling)
|
|
567
|
+
// moves by looking at the impact of the move operation on the node
|
|
568
|
+
// after the original move path.
|
|
569
|
+
const inversePath = Path.transform(path, op);
|
|
570
|
+
const inverseNewPath = Path.transform(Path.next(path), op);
|
|
571
|
+
return Object.assign(Object.assign({}, op), { path: inversePath, newPath: inverseNewPath });
|
|
572
|
+
}
|
|
573
|
+
case 'set_node': {
|
|
574
|
+
const { properties, newProperties } = op;
|
|
575
|
+
return Object.assign(Object.assign({}, op), { properties: newProperties, newProperties: properties });
|
|
576
|
+
}
|
|
577
|
+
case 'set_selection': {
|
|
578
|
+
const { properties, newProperties } = op;
|
|
579
|
+
if (properties == null) {
|
|
580
|
+
return Object.assign(Object.assign({}, op), { properties: newProperties, newProperties: null });
|
|
581
|
+
}
|
|
582
|
+
else if (newProperties == null) {
|
|
583
|
+
return Object.assign(Object.assign({}, op), { properties: null, newProperties: properties });
|
|
584
|
+
}
|
|
585
|
+
else {
|
|
586
|
+
return Object.assign(Object.assign({}, op), { properties: newProperties, newProperties: properties });
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
case 'set_viewport': {
|
|
590
|
+
const { properties, newProperties } = op;
|
|
591
|
+
if (properties == null) {
|
|
592
|
+
return Object.assign(Object.assign({}, op), { properties: newProperties, newProperties: newProperties });
|
|
593
|
+
}
|
|
594
|
+
else if (newProperties == null) {
|
|
595
|
+
return Object.assign(Object.assign({}, op), { properties: properties, newProperties: properties });
|
|
596
|
+
}
|
|
597
|
+
else {
|
|
598
|
+
return Object.assign(Object.assign({}, op), { properties: newProperties, newProperties: properties });
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
};
|
|
536
603
|
const PlaitOperation = {
|
|
537
|
-
isSetViewportOperation
|
|
604
|
+
isSetViewportOperation,
|
|
605
|
+
inverse
|
|
606
|
+
};
|
|
607
|
+
|
|
608
|
+
const IS_IOS = typeof navigator !== 'undefined' &&
|
|
609
|
+
typeof window !== 'undefined' &&
|
|
610
|
+
/iPad|iPhone|iPod/.test(navigator.userAgent) &&
|
|
611
|
+
!window.MSStream;
|
|
612
|
+
const IS_APPLE = typeof navigator !== 'undefined' && /Mac OS X/.test(navigator.userAgent);
|
|
613
|
+
const IS_FIREFOX = typeof navigator !== 'undefined' && /^(?!.*Seamonkey)(?=.*Firefox).*/i.test(navigator.userAgent);
|
|
614
|
+
const IS_SAFARI = typeof navigator !== 'undefined' && /Version\/[\d\.]+.*Safari/.test(navigator.userAgent);
|
|
615
|
+
// "modern" Edge was released at 79.x
|
|
616
|
+
const IS_EDGE_LEGACY = typeof navigator !== 'undefined' && /Edge?\/(?:[0-6][0-9]|[0-7][0-8])/i.test(navigator.userAgent);
|
|
617
|
+
const IS_CHROME = typeof navigator !== 'undefined' && /Chrome/i.test(navigator.userAgent);
|
|
618
|
+
// Native beforeInput events don't work well with react on Chrome 75 and older, Chrome 76+ can use beforeInput
|
|
619
|
+
const IS_CHROME_LEGACY = typeof navigator !== 'undefined' && /Chrome?\/(?:[0-7][0-5]|[0-6][0-9])/i.test(navigator.userAgent);
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* Hotkey mappings for each platform.
|
|
623
|
+
*/
|
|
624
|
+
const HOTKEYS = {
|
|
625
|
+
bold: 'mod+b',
|
|
626
|
+
compose: ['down', 'left', 'right', 'up', 'backspace', 'enter'],
|
|
627
|
+
moveBackward: 'left',
|
|
628
|
+
moveForward: 'right',
|
|
629
|
+
moveUp: 'up',
|
|
630
|
+
moveDown: 'down',
|
|
631
|
+
moveWordBackward: 'ctrl+left',
|
|
632
|
+
moveWordForward: 'ctrl+right',
|
|
633
|
+
deleteBackward: 'shift?+backspace',
|
|
634
|
+
deleteForward: 'shift?+delete',
|
|
635
|
+
extendBackward: 'shift+left',
|
|
636
|
+
extendForward: 'shift+right',
|
|
637
|
+
italic: 'mod+i',
|
|
638
|
+
splitBlock: 'shift?+enter',
|
|
639
|
+
undo: 'mod+z'
|
|
640
|
+
};
|
|
641
|
+
const APPLE_HOTKEYS = {
|
|
642
|
+
moveLineBackward: 'opt+up',
|
|
643
|
+
moveLineForward: 'opt+down',
|
|
644
|
+
moveWordBackward: 'opt+left',
|
|
645
|
+
moveWordForward: 'opt+right',
|
|
646
|
+
deleteBackward: ['ctrl+backspace', 'ctrl+h'],
|
|
647
|
+
deleteForward: ['ctrl+delete', 'ctrl+d'],
|
|
648
|
+
deleteLineBackward: 'cmd+shift?+backspace',
|
|
649
|
+
deleteLineForward: ['cmd+shift?+delete', 'ctrl+k'],
|
|
650
|
+
deleteWordBackward: 'opt+shift?+backspace',
|
|
651
|
+
deleteWordForward: 'opt+shift?+delete',
|
|
652
|
+
extendLineBackward: 'opt+shift+up',
|
|
653
|
+
extendLineForward: 'opt+shift+down',
|
|
654
|
+
redo: 'cmd+shift+z',
|
|
655
|
+
transposeCharacter: 'ctrl+t'
|
|
656
|
+
};
|
|
657
|
+
const WINDOWS_HOTKEYS = {
|
|
658
|
+
deleteWordBackward: 'ctrl+shift?+backspace',
|
|
659
|
+
deleteWordForward: 'ctrl+shift?+delete',
|
|
660
|
+
redo: ['ctrl+y', 'ctrl+shift+z']
|
|
661
|
+
};
|
|
662
|
+
/**
|
|
663
|
+
* Create a platform-aware hotkey checker.
|
|
664
|
+
*/
|
|
665
|
+
const create = (key) => {
|
|
666
|
+
const generic = HOTKEYS[key];
|
|
667
|
+
const apple = APPLE_HOTKEYS[key];
|
|
668
|
+
const windows = WINDOWS_HOTKEYS[key];
|
|
669
|
+
const isGeneric = generic && isKeyHotkey(generic);
|
|
670
|
+
const isApple = apple && isKeyHotkey(apple);
|
|
671
|
+
const isWindows = windows && isKeyHotkey(windows);
|
|
672
|
+
return (event) => {
|
|
673
|
+
if (isGeneric && isGeneric(event)) {
|
|
674
|
+
return true;
|
|
675
|
+
}
|
|
676
|
+
if (IS_APPLE && isApple && isApple(event)) {
|
|
677
|
+
return true;
|
|
678
|
+
}
|
|
679
|
+
if (!IS_APPLE && isWindows && isWindows(event)) {
|
|
680
|
+
return true;
|
|
681
|
+
}
|
|
682
|
+
return false;
|
|
683
|
+
};
|
|
684
|
+
};
|
|
685
|
+
/**
|
|
686
|
+
* Hotkeys.
|
|
687
|
+
*/
|
|
688
|
+
const hotkeys = {
|
|
689
|
+
isBold: create('bold'),
|
|
690
|
+
isCompose: create('compose'),
|
|
691
|
+
isMoveBackward: create('moveBackward'),
|
|
692
|
+
isMoveForward: create('moveForward'),
|
|
693
|
+
isMoveUp: create('moveUp'),
|
|
694
|
+
isMoveDown: create('moveDown'),
|
|
695
|
+
isDeleteBackward: create('deleteBackward'),
|
|
696
|
+
isDeleteForward: create('deleteForward'),
|
|
697
|
+
isDeleteLineBackward: create('deleteLineBackward'),
|
|
698
|
+
isDeleteLineForward: create('deleteLineForward'),
|
|
699
|
+
isDeleteWordBackward: create('deleteWordBackward'),
|
|
700
|
+
isDeleteWordForward: create('deleteWordForward'),
|
|
701
|
+
isExtendBackward: create('extendBackward'),
|
|
702
|
+
isExtendForward: create('extendForward'),
|
|
703
|
+
isExtendLineBackward: create('extendLineBackward'),
|
|
704
|
+
isExtendLineForward: create('extendLineForward'),
|
|
705
|
+
isItalic: create('italic'),
|
|
706
|
+
isMoveLineBackward: create('moveLineBackward'),
|
|
707
|
+
isMoveLineForward: create('moveLineForward'),
|
|
708
|
+
isMoveWordBackward: create('moveWordBackward'),
|
|
709
|
+
isMoveWordForward: create('moveWordForward'),
|
|
710
|
+
isRedo: create('redo'),
|
|
711
|
+
isSplitBlock: create('splitBlock'),
|
|
712
|
+
isTransposeCharacter: create('transposeCharacter'),
|
|
713
|
+
isUndo: create('undo')
|
|
714
|
+
};
|
|
715
|
+
|
|
716
|
+
function idCreator(length = 5) {
|
|
717
|
+
// remove numeral
|
|
718
|
+
const $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz'; /****默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1****/
|
|
719
|
+
const maxPosition = $chars.length;
|
|
720
|
+
let key = '';
|
|
721
|
+
for (let i = 0; i < length; i++) {
|
|
722
|
+
key += $chars.charAt(Math.floor(Math.random() * maxPosition));
|
|
723
|
+
}
|
|
724
|
+
return key;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
// https://stackoverflow.com/a/6853926/232122
|
|
728
|
+
function distanceBetweenPointAndSegment(x, y, x1, y1, x2, y2) {
|
|
729
|
+
const A = x - x1;
|
|
730
|
+
const B = y - y1;
|
|
731
|
+
const C = x2 - x1;
|
|
732
|
+
const D = y2 - y1;
|
|
733
|
+
const dot = A * C + B * D;
|
|
734
|
+
const lenSquare = C * C + D * D;
|
|
735
|
+
let param = -1;
|
|
736
|
+
if (lenSquare !== 0) {
|
|
737
|
+
// in case of 0 length line
|
|
738
|
+
param = dot / lenSquare;
|
|
739
|
+
}
|
|
740
|
+
let xx, yy;
|
|
741
|
+
if (param < 0) {
|
|
742
|
+
xx = x1;
|
|
743
|
+
yy = y1;
|
|
744
|
+
}
|
|
745
|
+
else if (param > 1) {
|
|
746
|
+
xx = x2;
|
|
747
|
+
yy = y2;
|
|
748
|
+
}
|
|
749
|
+
else {
|
|
750
|
+
xx = x1 + param * C;
|
|
751
|
+
yy = y1 + param * D;
|
|
752
|
+
}
|
|
753
|
+
const dx = x - xx;
|
|
754
|
+
const dy = y - yy;
|
|
755
|
+
return Math.hypot(dx, dy);
|
|
756
|
+
}
|
|
757
|
+
function rotate(x1, y1, x2, y2, angle) {
|
|
758
|
+
// 𝑎′𝑥=(𝑎𝑥−𝑐𝑥)cos𝜃−(𝑎𝑦−𝑐𝑦)sin𝜃+𝑐𝑥
|
|
759
|
+
// 𝑎′𝑦=(𝑎𝑥−𝑐𝑥)sin𝜃+(𝑎𝑦−𝑐𝑦)cos𝜃+𝑐𝑦.
|
|
760
|
+
// https://math.stackexchange.com/questions/2204520/how-do-i-rotate-a-line-segment-in-a-specific-point-on-the-line
|
|
761
|
+
return [(x1 - x2) * Math.cos(angle) - (y1 - y2) * Math.sin(angle) + x2, (x1 - x2) * Math.sin(angle) + (y1 - y2) * Math.cos(angle) + y2];
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
/**
|
|
765
|
+
* Check whether to merge an operation into the previous operation.
|
|
766
|
+
*/
|
|
767
|
+
const shouldMerge = (op, prev) => {
|
|
768
|
+
if (op.type === 'set_viewport') {
|
|
769
|
+
return true;
|
|
770
|
+
}
|
|
771
|
+
return false;
|
|
772
|
+
};
|
|
773
|
+
/**
|
|
774
|
+
* Check whether an operation needs to be saved to the history.
|
|
775
|
+
*/
|
|
776
|
+
const shouldSave = (op, prev) => {
|
|
777
|
+
if (op.type === 'set_selection') {
|
|
778
|
+
return false;
|
|
779
|
+
}
|
|
780
|
+
return true;
|
|
781
|
+
};
|
|
782
|
+
/**
|
|
783
|
+
* Check whether an operation should clear the redos stack.
|
|
784
|
+
*/
|
|
785
|
+
const shouldClear = (op) => {
|
|
786
|
+
if (op.type === 'set_selection') {
|
|
787
|
+
return false;
|
|
788
|
+
}
|
|
789
|
+
return true;
|
|
790
|
+
};
|
|
791
|
+
|
|
792
|
+
function withHistroy(board) {
|
|
793
|
+
const { apply, keydown } = board;
|
|
794
|
+
board.history = { undos: [], redos: [] };
|
|
795
|
+
board.redo = () => {
|
|
796
|
+
const { history } = board;
|
|
797
|
+
const { redos } = history;
|
|
798
|
+
if (redos.length > 0) {
|
|
799
|
+
const batch = redos[redos.length - 1];
|
|
800
|
+
PlaitHistoryBoard.withoutSaving(board, () => {
|
|
801
|
+
for (const op of batch) {
|
|
802
|
+
board.apply(op);
|
|
803
|
+
}
|
|
804
|
+
});
|
|
805
|
+
history.redos.pop();
|
|
806
|
+
history.undos.push(batch);
|
|
807
|
+
}
|
|
808
|
+
};
|
|
809
|
+
board.undo = () => {
|
|
810
|
+
const { history } = board;
|
|
811
|
+
const { undos } = history;
|
|
812
|
+
if (undos.length > 0) {
|
|
813
|
+
const batch = undos[undos.length - 1];
|
|
814
|
+
PlaitHistoryBoard.withoutSaving(board, () => {
|
|
815
|
+
const inverseOps = batch.map(PlaitOperation.inverse).reverse();
|
|
816
|
+
for (const op of inverseOps) {
|
|
817
|
+
board.apply(op);
|
|
818
|
+
}
|
|
819
|
+
});
|
|
820
|
+
history.redos.push(batch);
|
|
821
|
+
history.undos.pop();
|
|
822
|
+
}
|
|
823
|
+
};
|
|
824
|
+
board.apply = (op) => {
|
|
825
|
+
const { operations, history } = board;
|
|
826
|
+
const { undos } = history;
|
|
827
|
+
const lastBatch = undos[undos.length - 1];
|
|
828
|
+
const lastOp = lastBatch && lastBatch[lastBatch.length - 1];
|
|
829
|
+
let save = PlaitHistoryBoard.isSaving(board);
|
|
830
|
+
let merge = PlaitHistoryBoard.isMerging(board);
|
|
831
|
+
if (save == null) {
|
|
832
|
+
save = shouldSave(op, lastOp);
|
|
833
|
+
}
|
|
834
|
+
if (save) {
|
|
835
|
+
if (merge == null) {
|
|
836
|
+
if (lastBatch == null) {
|
|
837
|
+
merge = false;
|
|
838
|
+
}
|
|
839
|
+
else if (operations.length !== 0) {
|
|
840
|
+
merge = true;
|
|
841
|
+
}
|
|
842
|
+
else {
|
|
843
|
+
merge = shouldMerge(op, lastOp);
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
if (lastBatch && merge) {
|
|
847
|
+
lastBatch.push(op);
|
|
848
|
+
}
|
|
849
|
+
else {
|
|
850
|
+
const batch = [op];
|
|
851
|
+
undos.push(batch);
|
|
852
|
+
}
|
|
853
|
+
while (undos.length > 100) {
|
|
854
|
+
undos.shift();
|
|
855
|
+
}
|
|
856
|
+
if (shouldClear(op)) {
|
|
857
|
+
history.redos = [];
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
apply(op);
|
|
861
|
+
};
|
|
862
|
+
board.keydown = (event) => {
|
|
863
|
+
if (isHotkey('mod+z', event)) {
|
|
864
|
+
board.undo();
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
867
|
+
if (isHotkey('mod+shift+z', event)) {
|
|
868
|
+
board.redo();
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
871
|
+
keydown(event);
|
|
872
|
+
};
|
|
873
|
+
return board;
|
|
874
|
+
}
|
|
875
|
+
const SAVING = new WeakMap();
|
|
876
|
+
const MERGING = new WeakMap();
|
|
877
|
+
const PlaitHistoryBoard = {
|
|
878
|
+
/**
|
|
879
|
+
* Get the saving flag's current value.
|
|
880
|
+
*/
|
|
881
|
+
isSaving(board) {
|
|
882
|
+
return SAVING.get(board);
|
|
883
|
+
},
|
|
884
|
+
/**
|
|
885
|
+
* Get the merge flag's current value.
|
|
886
|
+
*/
|
|
887
|
+
isMerging(board) {
|
|
888
|
+
return MERGING.get(board);
|
|
889
|
+
},
|
|
890
|
+
/**
|
|
891
|
+
* Apply a series of changes inside a synchronous `fn`, without merging any of
|
|
892
|
+
* the new operations into previous save point in the history.
|
|
893
|
+
*/
|
|
894
|
+
withoutMerging(editor, fn) {
|
|
895
|
+
const prev = PlaitHistoryBoard.isMerging(editor);
|
|
896
|
+
MERGING.set(editor, false);
|
|
897
|
+
fn();
|
|
898
|
+
MERGING.set(editor, prev);
|
|
899
|
+
},
|
|
900
|
+
/**
|
|
901
|
+
* Apply a series of changes inside a synchronous `fn`, without saving any of
|
|
902
|
+
* their operations into the history.
|
|
903
|
+
*/
|
|
904
|
+
withoutSaving(editor, fn) {
|
|
905
|
+
const prev = PlaitHistoryBoard.isSaving(editor);
|
|
906
|
+
SAVING.set(editor, false);
|
|
907
|
+
fn();
|
|
908
|
+
SAVING.set(editor, prev);
|
|
909
|
+
}
|
|
538
910
|
};
|
|
539
911
|
|
|
540
912
|
class PlaitElementComponent {
|
|
@@ -614,6 +986,7 @@ class PlaitBoardComponent {
|
|
|
614
986
|
this.plaitValue = [];
|
|
615
987
|
this.plaitPlugins = [];
|
|
616
988
|
this.plaitReadonly = false;
|
|
989
|
+
this.plaitAllowClearBoard = false;
|
|
617
990
|
this.plaitChange = new EventEmitter();
|
|
618
991
|
this.plaitBoardInitialized = new EventEmitter();
|
|
619
992
|
this.trackBy = (index, element) => {
|
|
@@ -652,8 +1025,8 @@ class PlaitBoardComponent {
|
|
|
652
1025
|
this.plaitBoardInitialized.emit(this.board);
|
|
653
1026
|
}
|
|
654
1027
|
initializePlugins() {
|
|
655
|
-
const options = { readonly: this.plaitReadonly };
|
|
656
|
-
let board = withSelection(withBoard(createBoard(this.host, this.plaitValue, options)));
|
|
1028
|
+
const options = { readonly: this.plaitReadonly, allowClearBoard: this.plaitAllowClearBoard };
|
|
1029
|
+
let board = withHistroy(withSelection(withBoard(createBoard(this.host, this.plaitValue, options))));
|
|
657
1030
|
this.plaitPlugins.forEach(plugin => {
|
|
658
1031
|
board = plugin(board);
|
|
659
1032
|
});
|
|
@@ -709,12 +1082,15 @@ class PlaitBoardComponent {
|
|
|
709
1082
|
(_a = this.board) === null || _a === void 0 ? void 0 : _a.keyup(event);
|
|
710
1083
|
});
|
|
711
1084
|
window.onresize = () => {
|
|
712
|
-
|
|
713
|
-
const viewBoxModel = getViewBox(this.board);
|
|
714
|
-
const viewBoxValues = (_a = this.host.getAttribute('viewBox')) === null || _a === void 0 ? void 0 : _a.split(',');
|
|
715
|
-
this.renderer2.setAttribute(this.host, 'viewBox', `${viewBoxValues[0].trim()}, ${viewBoxValues[1].trim()}, ${viewBoxModel.width}, ${viewBoxModel.height}`);
|
|
1085
|
+
this.refreshViewport();
|
|
716
1086
|
};
|
|
717
1087
|
}
|
|
1088
|
+
refreshViewport() {
|
|
1089
|
+
var _a;
|
|
1090
|
+
const viewBoxModel = getViewBox(this.board);
|
|
1091
|
+
const viewBoxValues = (_a = this.host.getAttribute('viewBox')) === null || _a === void 0 ? void 0 : _a.split(',');
|
|
1092
|
+
this.renderer2.setAttribute(this.host, 'viewBox', `${viewBoxValues[0].trim()}, ${viewBoxValues[1].trim()}, ${viewBoxModel.width}, ${viewBoxModel.height}`);
|
|
1093
|
+
}
|
|
718
1094
|
updateViewport() {
|
|
719
1095
|
this.zoom = Math.floor(this.board.viewport.zoom * 100);
|
|
720
1096
|
const viewBox = getViewBox(this.board);
|
|
@@ -744,7 +1120,7 @@ class PlaitBoardComponent {
|
|
|
744
1120
|
}
|
|
745
1121
|
}
|
|
746
1122
|
PlaitBoardComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: PlaitBoardComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Component });
|
|
747
|
-
PlaitBoardComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.11", type: PlaitBoardComponent, selector: "plait-board", inputs: { plaitValue: "plaitValue", plaitViewport: "plaitViewport", plaitPlugins: "plaitPlugins", plaitReadonly: "plaitReadonly" }, outputs: { plaitChange: "plaitChange", plaitBoardInitialized: "plaitBoardInitialized" }, host: { properties: { "class": "this.hostClass" } }, viewQueries: [{ propertyName: "svg", first: true, predicate: ["svg"], descendants: true, static: true }], ngImport: i0, template: `
|
|
1123
|
+
PlaitBoardComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.11", type: PlaitBoardComponent, selector: "plait-board", inputs: { plaitValue: "plaitValue", plaitViewport: "plaitViewport", plaitPlugins: "plaitPlugins", plaitReadonly: "plaitReadonly", plaitAllowClearBoard: "plaitAllowClearBoard" }, outputs: { plaitChange: "plaitChange", plaitBoardInitialized: "plaitBoardInitialized" }, host: { properties: { "class": "this.hostClass" } }, viewQueries: [{ propertyName: "svg", first: true, predicate: ["svg"], descendants: true, static: true }], ngImport: i0, template: `
|
|
748
1124
|
<svg #svg width="100%" height="100%" style="position: relative"></svg>
|
|
749
1125
|
<div *ngIf="isFocused" class="plait-toolbar island zoom-toolbar plait-board-attached">
|
|
750
1126
|
<button class="item" (mousedown)="zoomOut($event)">-</button>
|
|
@@ -800,6 +1176,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
|
|
|
800
1176
|
type: Input
|
|
801
1177
|
}], plaitReadonly: [{
|
|
802
1178
|
type: Input
|
|
1179
|
+
}], plaitAllowClearBoard: [{
|
|
1180
|
+
type: Input
|
|
803
1181
|
}], plaitChange: [{
|
|
804
1182
|
type: Output
|
|
805
1183
|
}], plaitBoardInitialized: [{
|
|
@@ -820,162 +1198,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
|
|
|
820
1198
|
}]
|
|
821
1199
|
}] });
|
|
822
1200
|
|
|
823
|
-
const IS_IOS = typeof navigator !== 'undefined' &&
|
|
824
|
-
typeof window !== 'undefined' &&
|
|
825
|
-
/iPad|iPhone|iPod/.test(navigator.userAgent) &&
|
|
826
|
-
!window.MSStream;
|
|
827
|
-
const IS_APPLE = typeof navigator !== 'undefined' && /Mac OS X/.test(navigator.userAgent);
|
|
828
|
-
const IS_FIREFOX = typeof navigator !== 'undefined' && /^(?!.*Seamonkey)(?=.*Firefox).*/i.test(navigator.userAgent);
|
|
829
|
-
const IS_SAFARI = typeof navigator !== 'undefined' && /Version\/[\d\.]+.*Safari/.test(navigator.userAgent);
|
|
830
|
-
// "modern" Edge was released at 79.x
|
|
831
|
-
const IS_EDGE_LEGACY = typeof navigator !== 'undefined' && /Edge?\/(?:[0-6][0-9]|[0-7][0-8])/i.test(navigator.userAgent);
|
|
832
|
-
const IS_CHROME = typeof navigator !== 'undefined' && /Chrome/i.test(navigator.userAgent);
|
|
833
|
-
// Native beforeInput events don't work well with react on Chrome 75 and older, Chrome 76+ can use beforeInput
|
|
834
|
-
const IS_CHROME_LEGACY = typeof navigator !== 'undefined' && /Chrome?\/(?:[0-7][0-5]|[0-6][0-9])/i.test(navigator.userAgent);
|
|
835
|
-
|
|
836
|
-
/**
|
|
837
|
-
* Hotkey mappings for each platform.
|
|
838
|
-
*/
|
|
839
|
-
const HOTKEYS = {
|
|
840
|
-
bold: 'mod+b',
|
|
841
|
-
compose: ['down', 'left', 'right', 'up', 'backspace', 'enter'],
|
|
842
|
-
moveBackward: 'left',
|
|
843
|
-
moveForward: 'right',
|
|
844
|
-
moveUp: 'up',
|
|
845
|
-
moveDown: 'down',
|
|
846
|
-
moveWordBackward: 'ctrl+left',
|
|
847
|
-
moveWordForward: 'ctrl+right',
|
|
848
|
-
deleteBackward: 'shift?+backspace',
|
|
849
|
-
deleteForward: 'shift?+delete',
|
|
850
|
-
extendBackward: 'shift+left',
|
|
851
|
-
extendForward: 'shift+right',
|
|
852
|
-
italic: 'mod+i',
|
|
853
|
-
splitBlock: 'shift?+enter',
|
|
854
|
-
undo: 'mod+z'
|
|
855
|
-
};
|
|
856
|
-
const APPLE_HOTKEYS = {
|
|
857
|
-
moveLineBackward: 'opt+up',
|
|
858
|
-
moveLineForward: 'opt+down',
|
|
859
|
-
moveWordBackward: 'opt+left',
|
|
860
|
-
moveWordForward: 'opt+right',
|
|
861
|
-
deleteBackward: ['ctrl+backspace', 'ctrl+h'],
|
|
862
|
-
deleteForward: ['ctrl+delete', 'ctrl+d'],
|
|
863
|
-
deleteLineBackward: 'cmd+shift?+backspace',
|
|
864
|
-
deleteLineForward: ['cmd+shift?+delete', 'ctrl+k'],
|
|
865
|
-
deleteWordBackward: 'opt+shift?+backspace',
|
|
866
|
-
deleteWordForward: 'opt+shift?+delete',
|
|
867
|
-
extendLineBackward: 'opt+shift+up',
|
|
868
|
-
extendLineForward: 'opt+shift+down',
|
|
869
|
-
redo: 'cmd+shift+z',
|
|
870
|
-
transposeCharacter: 'ctrl+t'
|
|
871
|
-
};
|
|
872
|
-
const WINDOWS_HOTKEYS = {
|
|
873
|
-
deleteWordBackward: 'ctrl+shift?+backspace',
|
|
874
|
-
deleteWordForward: 'ctrl+shift?+delete',
|
|
875
|
-
redo: ['ctrl+y', 'ctrl+shift+z']
|
|
876
|
-
};
|
|
877
|
-
/**
|
|
878
|
-
* Create a platform-aware hotkey checker.
|
|
879
|
-
*/
|
|
880
|
-
const create = (key) => {
|
|
881
|
-
const generic = HOTKEYS[key];
|
|
882
|
-
const apple = APPLE_HOTKEYS[key];
|
|
883
|
-
const windows = WINDOWS_HOTKEYS[key];
|
|
884
|
-
const isGeneric = generic && isKeyHotkey(generic);
|
|
885
|
-
const isApple = apple && isKeyHotkey(apple);
|
|
886
|
-
const isWindows = windows && isKeyHotkey(windows);
|
|
887
|
-
return (event) => {
|
|
888
|
-
if (isGeneric && isGeneric(event)) {
|
|
889
|
-
return true;
|
|
890
|
-
}
|
|
891
|
-
if (IS_APPLE && isApple && isApple(event)) {
|
|
892
|
-
return true;
|
|
893
|
-
}
|
|
894
|
-
if (!IS_APPLE && isWindows && isWindows(event)) {
|
|
895
|
-
return true;
|
|
896
|
-
}
|
|
897
|
-
return false;
|
|
898
|
-
};
|
|
899
|
-
};
|
|
900
|
-
/**
|
|
901
|
-
* Hotkeys.
|
|
902
|
-
*/
|
|
903
|
-
const hotkeys = {
|
|
904
|
-
isBold: create('bold'),
|
|
905
|
-
isCompose: create('compose'),
|
|
906
|
-
isMoveBackward: create('moveBackward'),
|
|
907
|
-
isMoveForward: create('moveForward'),
|
|
908
|
-
isMoveUp: create('moveUp'),
|
|
909
|
-
isMoveDown: create('moveDown'),
|
|
910
|
-
isDeleteBackward: create('deleteBackward'),
|
|
911
|
-
isDeleteForward: create('deleteForward'),
|
|
912
|
-
isDeleteLineBackward: create('deleteLineBackward'),
|
|
913
|
-
isDeleteLineForward: create('deleteLineForward'),
|
|
914
|
-
isDeleteWordBackward: create('deleteWordBackward'),
|
|
915
|
-
isDeleteWordForward: create('deleteWordForward'),
|
|
916
|
-
isExtendBackward: create('extendBackward'),
|
|
917
|
-
isExtendForward: create('extendForward'),
|
|
918
|
-
isExtendLineBackward: create('extendLineBackward'),
|
|
919
|
-
isExtendLineForward: create('extendLineForward'),
|
|
920
|
-
isItalic: create('italic'),
|
|
921
|
-
isMoveLineBackward: create('moveLineBackward'),
|
|
922
|
-
isMoveLineForward: create('moveLineForward'),
|
|
923
|
-
isMoveWordBackward: create('moveWordBackward'),
|
|
924
|
-
isMoveWordForward: create('moveWordForward'),
|
|
925
|
-
isRedo: create('redo'),
|
|
926
|
-
isSplitBlock: create('splitBlock'),
|
|
927
|
-
isTransposeCharacter: create('transposeCharacter'),
|
|
928
|
-
isUndo: create('undo')
|
|
929
|
-
};
|
|
930
|
-
|
|
931
|
-
function idCreator(length = 5) {
|
|
932
|
-
// remove numeral
|
|
933
|
-
const $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz'; /****默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1****/
|
|
934
|
-
const maxPosition = $chars.length;
|
|
935
|
-
let key = '';
|
|
936
|
-
for (let i = 0; i < length; i++) {
|
|
937
|
-
key += $chars.charAt(Math.floor(Math.random() * maxPosition));
|
|
938
|
-
}
|
|
939
|
-
return key;
|
|
940
|
-
}
|
|
941
|
-
|
|
942
|
-
// https://stackoverflow.com/a/6853926/232122
|
|
943
|
-
function distanceBetweenPointAndSegment(x, y, x1, y1, x2, y2) {
|
|
944
|
-
const A = x - x1;
|
|
945
|
-
const B = y - y1;
|
|
946
|
-
const C = x2 - x1;
|
|
947
|
-
const D = y2 - y1;
|
|
948
|
-
const dot = A * C + B * D;
|
|
949
|
-
const lenSquare = C * C + D * D;
|
|
950
|
-
let param = -1;
|
|
951
|
-
if (lenSquare !== 0) {
|
|
952
|
-
// in case of 0 length line
|
|
953
|
-
param = dot / lenSquare;
|
|
954
|
-
}
|
|
955
|
-
let xx, yy;
|
|
956
|
-
if (param < 0) {
|
|
957
|
-
xx = x1;
|
|
958
|
-
yy = y1;
|
|
959
|
-
}
|
|
960
|
-
else if (param > 1) {
|
|
961
|
-
xx = x2;
|
|
962
|
-
yy = y2;
|
|
963
|
-
}
|
|
964
|
-
else {
|
|
965
|
-
xx = x1 + param * C;
|
|
966
|
-
yy = y1 + param * D;
|
|
967
|
-
}
|
|
968
|
-
const dx = x - xx;
|
|
969
|
-
const dy = y - yy;
|
|
970
|
-
return Math.hypot(dx, dy);
|
|
971
|
-
}
|
|
972
|
-
function rotate(x1, y1, x2, y2, angle) {
|
|
973
|
-
// 𝑎′𝑥=(𝑎𝑥−𝑐𝑥)cos𝜃−(𝑎𝑦−𝑐𝑦)sin𝜃+𝑐𝑥
|
|
974
|
-
// 𝑎′𝑦=(𝑎𝑥−𝑐𝑥)sin𝜃+(𝑎𝑦−𝑐𝑦)cos𝜃+𝑐𝑦.
|
|
975
|
-
// https://math.stackexchange.com/questions/2204520/how-do-i-rotate-a-line-segment-in-a-specific-point-on-the-line
|
|
976
|
-
return [(x1 - x2) * Math.cos(angle) - (y1 - y2) * Math.sin(angle) + x2, (x1 - x2) * Math.sin(angle) + (y1 - y2) * Math.cos(angle) + y2];
|
|
977
|
-
}
|
|
978
|
-
|
|
979
1201
|
/*
|
|
980
1202
|
* Public API Surface of plait
|
|
981
1203
|
*/
|
|
@@ -984,5 +1206,5 @@ function rotate(x1, y1, x2, y2, angle) {
|
|
|
984
1206
|
* Generated bundle index. Do not edit.
|
|
985
1207
|
*/
|
|
986
1208
|
|
|
987
|
-
export { BOARD_TO_ON_CHANGE, BaseCursorStatus, FLUSHING, HOST_TO_ROUGH_SVG, IS_APPLE, IS_CHROME, IS_CHROME_LEGACY, IS_EDGE_LEGACY, IS_FIREFOX, IS_IOS, IS_SAFARI, IS_TEXT_EDITABLE, NS, Path, PlaitBoardComponent, PlaitElementComponent, PlaitModule, PlaitNode, PlaitOperation, Transforms, Viewport, createG, createSVG, createText, distanceBetweenPointAndSegment, getViewBox, hotkeys, idCreator, isNoSelectionElement, isNullOrUndefined, isSetViewportOperation, rotate, toPoint, toRectangleClient, transformPoint, transformPoints };
|
|
1209
|
+
export { BOARD_TO_ON_CHANGE, BaseCursorStatus, FLUSHING, HOST_TO_ROUGH_SVG, IS_APPLE, IS_CHROME, IS_CHROME_LEGACY, IS_EDGE_LEGACY, IS_FIREFOX, IS_IOS, IS_SAFARI, IS_TEXT_EDITABLE, NS, Path, PlaitBoardComponent, PlaitElementComponent, PlaitModule, PlaitNode, PlaitOperation, Transforms, Viewport, createG, createSVG, createText, distanceBetweenPointAndSegment, getViewBox, hotkeys, idCreator, inverse, isNoSelectionElement, isNullOrUndefined, isSetViewportOperation, rotate, shouldClear, shouldMerge, shouldSave, toPoint, toRectangleClient, transformPoint, transformPoints };
|
|
988
1210
|
//# sourceMappingURL=plait-core.mjs.map
|