@plait/core 0.0.24 → 0.0.29

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.
@@ -1,32 +1,13 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { Component, ChangeDetectionStrategy, Input, EventEmitter, HostBinding, ViewChild, Output, NgModule } from '@angular/core';
3
- import produce, { createDraft, finishDraft, isDraft } from 'immer';
3
+ import rough from 'roughjs/bin/rough';
4
4
  import { Subject, fromEvent } from 'rxjs';
5
5
  import { takeUntil, filter } from 'rxjs/operators';
6
- import rough from 'roughjs/bin/rough';
6
+ import produce, { createDraft, finishDraft, isDraft } from 'immer';
7
7
  import { isKeyHotkey, isHotkey } from 'is-hotkey';
8
8
  import * as i2 from '@angular/common';
9
9
  import { BrowserModule } from '@angular/platform-browser';
10
10
 
11
- // record richtext type status
12
- const FLUSHING = new WeakMap();
13
- const IS_TEXT_EDITABLE = new WeakMap();
14
- const BOARD_TO_ON_CHANGE = new WeakMap();
15
- const HOST_TO_ROUGH_SVG = new WeakMap();
16
-
17
- function isNullOrUndefined(value) {
18
- return value === null || value === undefined;
19
- }
20
-
21
- const Viewport = {
22
- isViewport: (value) => {
23
- return (!isNullOrUndefined(value.offsetX) &&
24
- !isNullOrUndefined(value.offsetY) &&
25
- !isNullOrUndefined(value.zoom) &&
26
- !isNullOrUndefined(value.viewBackgroundColor));
27
- }
28
- };
29
-
30
11
  const Path = {
31
12
  parent(path) {
32
13
  if (path.length === 0) {
@@ -165,6 +146,108 @@ const Path = {
165
146
  }
166
147
  };
167
148
 
149
+ const isSetViewportOperation = (value) => {
150
+ return value.type === 'set_viewport';
151
+ };
152
+ const inverse = (op) => {
153
+ switch (op.type) {
154
+ case 'insert_node': {
155
+ return { ...op, type: 'remove_node' };
156
+ }
157
+ case 'remove_node': {
158
+ return { ...op, type: 'insert_node' };
159
+ }
160
+ case 'move_node': {
161
+ const { newPath, path } = op;
162
+ // PERF: in this case the move operation is a no-op anyways.
163
+ if (Path.equals(newPath, path)) {
164
+ return op;
165
+ }
166
+ // If the move happens completely within a single parent the path and
167
+ // newPath are stable with respect to each other.
168
+ if (Path.isSibling(path, newPath)) {
169
+ return { ...op, path: newPath, newPath: path };
170
+ }
171
+ // If the move does not happen within a single parent it is possible
172
+ // for the move to impact the true path to the location where the node
173
+ // was removed from and where it was inserted. We have to adjust for this
174
+ // and find the original path. We can accomplish this (only in non-sibling)
175
+ // moves by looking at the impact of the move operation on the node
176
+ // after the original move path.
177
+ const inversePath = Path.transform(path, op);
178
+ const inverseNewPath = Path.transform(Path.next(path), op);
179
+ return { ...op, path: inversePath, newPath: inverseNewPath };
180
+ }
181
+ case 'set_node': {
182
+ const { properties, newProperties } = op;
183
+ return { ...op, properties: newProperties, newProperties: properties };
184
+ }
185
+ case 'set_selection': {
186
+ const { properties, newProperties } = op;
187
+ if (properties == null) {
188
+ return {
189
+ ...op,
190
+ properties: newProperties,
191
+ newProperties: null
192
+ };
193
+ }
194
+ else if (newProperties == null) {
195
+ return {
196
+ ...op,
197
+ properties: null,
198
+ newProperties: properties
199
+ };
200
+ }
201
+ else {
202
+ return { ...op, properties: newProperties, newProperties: properties };
203
+ }
204
+ }
205
+ case 'set_viewport': {
206
+ const { properties, newProperties } = op;
207
+ if (properties == null) {
208
+ return {
209
+ ...op,
210
+ properties: newProperties,
211
+ newProperties: newProperties
212
+ };
213
+ }
214
+ else if (newProperties == null) {
215
+ return {
216
+ ...op,
217
+ properties: properties,
218
+ newProperties: properties
219
+ };
220
+ }
221
+ else {
222
+ return { ...op, properties: newProperties, newProperties: properties };
223
+ }
224
+ }
225
+ }
226
+ };
227
+ const PlaitOperation = {
228
+ isSetViewportOperation,
229
+ inverse
230
+ };
231
+
232
+ // record richtext type status
233
+ const FLUSHING = new WeakMap();
234
+ const IS_TEXT_EDITABLE = new WeakMap();
235
+ const BOARD_TO_ON_CHANGE = new WeakMap();
236
+ const HOST_TO_ROUGH_SVG = new WeakMap();
237
+
238
+ function isNullOrUndefined(value) {
239
+ return value === null || value === undefined;
240
+ }
241
+
242
+ const Viewport = {
243
+ isViewport: (value) => {
244
+ return (!isNullOrUndefined(value.offsetX) &&
245
+ !isNullOrUndefined(value.offsetY) &&
246
+ !isNullOrUndefined(value.zoom) &&
247
+ !isNullOrUndefined(value.viewBackgroundColor));
248
+ }
249
+ };
250
+
168
251
  const PlaitNode = {
169
252
  parent: (board, path) => {
170
253
  const parentPath = Path.parent(path);
@@ -446,6 +529,13 @@ function withBoard(board) {
446
529
  return board;
447
530
  }
448
531
 
532
+ /**
533
+ * Extendable Custom Types Interface
534
+ */
535
+
536
+ const SAVING = new WeakMap();
537
+ const MERGING = new WeakMap();
538
+
449
539
  const NS = 'http://www.w3.org/2000/svg';
450
540
  function toPoint(x, y, container) {
451
541
  const rect = container.getBoundingClientRect();
@@ -468,174 +558,6 @@ function createText(x, y, fill, textContent) {
468
558
  return text;
469
559
  }
470
560
 
471
- function toRectangleClient(points) {
472
- const xArray = points.map(ele => ele[0]);
473
- const yArray = points.map(ele => ele[1]);
474
- const xMin = Math.min(...xArray);
475
- const xMax = Math.max(...xArray);
476
- const yMin = Math.min(...yArray);
477
- const yMax = Math.max(...yArray);
478
- const rect = { x: xMin, y: yMin, width: xMax - xMin, height: yMax - yMin };
479
- return rect;
480
- }
481
-
482
- function transformPoints(board, points) {
483
- const newPoints = points.map(point => {
484
- return transformPoint(board, point);
485
- });
486
- return newPoints;
487
- }
488
- function transformPoint(board, point) {
489
- const { width, height } = board.host.getBoundingClientRect();
490
- const viewBox = getViewBox(board);
491
- let x = (point[0] / width) * viewBox.width + viewBox.minX;
492
- let y = (point[1] / height) * viewBox.height + viewBox.minY;
493
- const newPoint = [x - board.viewport.offsetX, y - board.viewport.offsetY];
494
- return newPoint;
495
- }
496
- function getViewBox(board) {
497
- const { width, height } = board.host.getBoundingClientRect();
498
- const scaleWidth = (board.viewport.zoom - 1) * width;
499
- const scaleHeight = (board.viewport.zoom - 1) * height;
500
- const viewBoxWidth = width - scaleWidth;
501
- const viewBoxHeight = height - scaleHeight;
502
- const minX = scaleWidth / 2;
503
- const minY = scaleHeight / 2;
504
- return { minX, minY: minY, width: viewBoxWidth, height: viewBoxHeight };
505
- }
506
- function isNoSelectionElement(e) {
507
- return e.target?.closest('.plait-board-attached');
508
- }
509
-
510
- function withSelection(board) {
511
- const { mousedown, mousemove, globalMouseup } = board;
512
- let start = null;
513
- let end = null;
514
- board.mousedown = (event) => {
515
- mousedown(event);
516
- };
517
- board.mousemove = (event) => {
518
- const movedTarget = toPoint(event.x, event.y, board.host);
519
- if (start) {
520
- const rectangleClient = toRectangleClient([start, movedTarget]);
521
- if (start && Math.hypot(rectangleClient.width, rectangleClient.height) > 5) {
522
- end = movedTarget;
523
- }
524
- }
525
- mousemove(event);
526
- };
527
- board.globalMouseup = (event) => {
528
- if (isNoSelectionElement(event)) {
529
- return globalMouseup(event);
530
- }
531
- else {
532
- if (!start && event.target instanceof Node && board.host.contains(event.target)) {
533
- start = toPoint(event.x, event.y, board.host);
534
- }
535
- }
536
- if (start) {
537
- Transforms.setSelection(board, { anchor: start, focus: start });
538
- }
539
- else {
540
- Transforms.setSelection(board, null);
541
- }
542
- start = null;
543
- end = null;
544
- globalMouseup(event);
545
- };
546
- return board;
547
- }
548
-
549
- const isSetViewportOperation = (value) => {
550
- return value.type === 'set_viewport';
551
- };
552
- const inverse = (op) => {
553
- switch (op.type) {
554
- case 'insert_node': {
555
- return { ...op, type: 'remove_node' };
556
- }
557
- case 'remove_node': {
558
- return { ...op, type: 'insert_node' };
559
- }
560
- case 'move_node': {
561
- const { newPath, path } = op;
562
- // PERF: in this case the move operation is a no-op anyways.
563
- if (Path.equals(newPath, path)) {
564
- return op;
565
- }
566
- // If the move happens completely within a single parent the path and
567
- // newPath are stable with respect to each other.
568
- if (Path.isSibling(path, newPath)) {
569
- return { ...op, path: newPath, newPath: path };
570
- }
571
- // If the move does not happen within a single parent it is possible
572
- // for the move to impact the true path to the location where the node
573
- // was removed from and where it was inserted. We have to adjust for this
574
- // and find the original path. We can accomplish this (only in non-sibling)
575
- // moves by looking at the impact of the move operation on the node
576
- // after the original move path.
577
- const inversePath = Path.transform(path, op);
578
- const inverseNewPath = Path.transform(Path.next(path), op);
579
- return { ...op, path: inversePath, newPath: inverseNewPath };
580
- }
581
- case 'set_node': {
582
- const { properties, newProperties } = op;
583
- return { ...op, properties: newProperties, newProperties: properties };
584
- }
585
- case 'set_selection': {
586
- const { properties, newProperties } = op;
587
- if (properties == null) {
588
- return {
589
- ...op,
590
- properties: newProperties,
591
- newProperties: null
592
- };
593
- }
594
- else if (newProperties == null) {
595
- return {
596
- ...op,
597
- properties: null,
598
- newProperties: properties
599
- };
600
- }
601
- else {
602
- return { ...op, properties: newProperties, newProperties: properties };
603
- }
604
- }
605
- case 'set_viewport': {
606
- const { properties, newProperties } = op;
607
- if (properties == null) {
608
- return {
609
- ...op,
610
- properties: newProperties,
611
- newProperties: newProperties
612
- };
613
- }
614
- else if (newProperties == null) {
615
- return {
616
- ...op,
617
- properties: properties,
618
- newProperties: properties
619
- };
620
- }
621
- else {
622
- return { ...op, properties: newProperties, newProperties: properties };
623
- }
624
- }
625
- }
626
- };
627
- const PlaitOperation = {
628
- isSetViewportOperation,
629
- inverse
630
- };
631
-
632
- /**
633
- * Extendable Custom Types Interface
634
- */
635
-
636
- const SAVING = new WeakMap();
637
- const MERGING = new WeakMap();
638
-
639
561
  const IS_IOS = typeof navigator !== 'undefined' &&
640
562
  typeof window !== 'undefined' &&
641
563
  /iPad|iPhone|iPod/.test(navigator.userAgent) &&
@@ -649,6 +571,17 @@ const IS_CHROME = typeof navigator !== 'undefined' && /Chrome/i.test(navigator.u
649
571
  // Native beforeInput events don't work well with react on Chrome 75 and older, Chrome 76+ can use beforeInput
650
572
  const IS_CHROME_LEGACY = typeof navigator !== 'undefined' && /Chrome?\/(?:[0-7][0-5]|[0-6][0-9])/i.test(navigator.userAgent);
651
573
 
574
+ function toRectangleClient(points) {
575
+ const xArray = points.map(ele => ele[0]);
576
+ const yArray = points.map(ele => ele[1]);
577
+ const xMin = Math.min(...xArray);
578
+ const xMax = Math.max(...xArray);
579
+ const yMin = Math.min(...yArray);
580
+ const yMax = Math.max(...yArray);
581
+ const rect = { x: xMin, y: yMin, width: xMax - xMin, height: yMax - yMin };
582
+ return rect;
583
+ }
584
+
652
585
  /**
653
586
  * Hotkey mappings for each platform.
654
587
  */
@@ -791,6 +724,39 @@ function rotate(x1, y1, x2, y2, angle) {
791
724
  // https://math.stackexchange.com/questions/2204520/how-do-i-rotate-a-line-segment-in-a-specific-point-on-the-line
792
725
  return [(x1 - x2) * Math.cos(angle) - (y1 - y2) * Math.sin(angle) + x2, (x1 - x2) * Math.sin(angle) + (y1 - y2) * Math.cos(angle) + y2];
793
726
  }
727
+ function distanceBetweenPointAndPoint(x1, y1, x2, y2) {
728
+ const dx = x1 - x2;
729
+ const dy = y1 - y2;
730
+ return Math.hypot(dx, dy);
731
+ }
732
+
733
+ function transformPoints(board, points) {
734
+ const newPoints = points.map(point => {
735
+ return transformPoint(board, point);
736
+ });
737
+ return newPoints;
738
+ }
739
+ function transformPoint(board, point) {
740
+ const { width, height } = board.host.getBoundingClientRect();
741
+ const viewBox = getViewBox(board);
742
+ let x = (point[0] / width) * viewBox.width + viewBox.minX;
743
+ let y = (point[1] / height) * viewBox.height + viewBox.minY;
744
+ const newPoint = [x - board.viewport.offsetX, y - board.viewport.offsetY];
745
+ return newPoint;
746
+ }
747
+ function getViewBox(board) {
748
+ const { width, height } = board.host.getBoundingClientRect();
749
+ const scaleWidth = (board.viewport.zoom - 1) * width;
750
+ const scaleHeight = (board.viewport.zoom - 1) * height;
751
+ const viewBoxWidth = width - scaleWidth;
752
+ const viewBoxHeight = height - scaleHeight;
753
+ const minX = scaleWidth / 2;
754
+ const minY = scaleHeight / 2;
755
+ return { minX, minY: minY, width: viewBoxWidth, height: viewBoxHeight };
756
+ }
757
+ function isNoSelectionElement(e) {
758
+ return e.target?.closest('.plait-board-attached');
759
+ }
794
760
 
795
761
  /**
796
762
  * Check whether to merge an operation into the previous operation.
@@ -805,7 +771,7 @@ const shouldMerge = (op, prev) => {
805
771
  * Check whether an operation needs to be saved to the history.
806
772
  */
807
773
  const shouldSave = (op, prev) => {
808
- if (op.type === 'set_selection') {
774
+ if (op.type === 'set_selection' || op.type === 'set_viewport') {
809
775
  return false;
810
776
  }
811
777
  return true;
@@ -938,6 +904,45 @@ function withHistroy(board) {
938
904
  return board;
939
905
  }
940
906
 
907
+ function withSelection(board) {
908
+ const { mousedown, mousemove, globalMouseup } = board;
909
+ let start = null;
910
+ let end = null;
911
+ board.mousedown = (event) => {
912
+ mousedown(event);
913
+ };
914
+ board.mousemove = (event) => {
915
+ const movedTarget = toPoint(event.x, event.y, board.host);
916
+ if (start) {
917
+ const rectangleClient = toRectangleClient([start, movedTarget]);
918
+ if (start && Math.hypot(rectangleClient.width, rectangleClient.height) > 5) {
919
+ end = movedTarget;
920
+ }
921
+ }
922
+ mousemove(event);
923
+ };
924
+ board.globalMouseup = (event) => {
925
+ if (isNoSelectionElement(event)) {
926
+ return globalMouseup(event);
927
+ }
928
+ else {
929
+ if (!start && event.target instanceof Node && board.host.contains(event.target)) {
930
+ start = toPoint(event.x, event.y, board.host);
931
+ }
932
+ }
933
+ if (start) {
934
+ Transforms.setSelection(board, { anchor: start, focus: start });
935
+ }
936
+ else {
937
+ Transforms.setSelection(board, null);
938
+ }
939
+ start = null;
940
+ end = null;
941
+ globalMouseup(event);
942
+ };
943
+ return board;
944
+ }
945
+
941
946
  class PlaitElementComponent {
942
947
  constructor(renderer2, viewContainerRef) {
943
948
  this.renderer2 = renderer2;
@@ -1028,6 +1033,9 @@ class PlaitBoardComponent {
1028
1033
  get isFocused() {
1029
1034
  return this.board?.selection;
1030
1035
  }
1036
+ get focused() {
1037
+ return this.isFocused;
1038
+ }
1031
1039
  ngOnInit() {
1032
1040
  const roughSVG = rough.svg(this.host, { options: { roughness: 0, strokeWidth: 1 } });
1033
1041
  HOST_TO_ROUGH_SVG.set(this.host, roughSVG);
@@ -1111,7 +1119,7 @@ class PlaitBoardComponent {
1111
1119
  .subscribe((event) => {
1112
1120
  this.board?.keyup(event);
1113
1121
  });
1114
- fromEvent(this.host, 'copy')
1122
+ fromEvent(document, 'copy')
1115
1123
  .pipe(takeUntil(this.destroy$), filter(() => {
1116
1124
  return !IS_TEXT_EDITABLE.get(this.board) && !!this.board.selection;
1117
1125
  }))
@@ -1119,14 +1127,14 @@ class PlaitBoardComponent {
1119
1127
  event.preventDefault();
1120
1128
  this.board?.setFragment(event.clipboardData);
1121
1129
  });
1122
- fromEvent(this.host, 'paste')
1130
+ fromEvent(document, 'paste')
1123
1131
  .pipe(takeUntil(this.destroy$), filter(() => {
1124
1132
  return !IS_TEXT_EDITABLE.get(this.board) && !!this.board.selection;
1125
1133
  }))
1126
1134
  .subscribe((event) => {
1127
1135
  this.board?.insertFragment(event.clipboardData);
1128
1136
  });
1129
- fromEvent(this.host, 'cut')
1137
+ fromEvent(document, 'cut')
1130
1138
  .pipe(takeUntil(this.destroy$), filter(() => {
1131
1139
  return !IS_TEXT_EDITABLE.get(this.board) && !!this.board.selection;
1132
1140
  }))
@@ -1179,7 +1187,7 @@ class PlaitBoardComponent {
1179
1187
  }
1180
1188
  }
1181
1189
  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 });
1182
- 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: `
1190
+ 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", "class.focused": "this.focused" } }, viewQueries: [{ propertyName: "svg", first: true, predicate: ["svg"], descendants: true, static: true }], ngImport: i0, template: `
1183
1191
  <svg #svg width="100%" height="100%" style="position: relative"></svg>
1184
1192
  <div *ngIf="isFocused" class="plait-toolbar island zoom-toolbar plait-board-attached">
1185
1193
  <button class="item" (mousedown)="zoomOut($event)">-</button>
@@ -1241,6 +1249,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
1241
1249
  type: Output
1242
1250
  }], plaitBoardInitialized: [{
1243
1251
  type: Output
1252
+ }], focused: [{
1253
+ type: HostBinding,
1254
+ args: ['class.focused']
1244
1255
  }] } });
1245
1256
 
1246
1257
  class PlaitModule {
@@ -1267,5 +1278,5 @@ const CLIP_BOARD_FORMAT_KEY = 'x-plait-fragment';
1267
1278
  * Generated bundle index. Do not edit.
1268
1279
  */
1269
1280
 
1270
- export { BOARD_TO_ON_CHANGE, BaseCursorStatus, CLIP_BOARD_FORMAT_KEY, FLUSHING, HOST_TO_ROUGH_SVG, IS_APPLE, IS_CHROME, IS_CHROME_LEGACY, IS_EDGE_LEGACY, IS_FIREFOX, IS_IOS, IS_SAFARI, IS_TEXT_EDITABLE, MERGING, NS, Path, PlaitBoardComponent, PlaitElementComponent, PlaitHistoryBoard, PlaitModule, PlaitNode, PlaitOperation, SAVING, Transforms, Viewport, createG, createSVG, createText, distanceBetweenPointAndSegment, getViewBox, hotkeys, idCreator, inverse, isNoSelectionElement, isNullOrUndefined, isSetViewportOperation, rotate, shouldClear, shouldMerge, shouldSave, toPoint, toRectangleClient, transformPoint, transformPoints };
1281
+ export { BOARD_TO_ON_CHANGE, BaseCursorStatus, CLIP_BOARD_FORMAT_KEY, FLUSHING, HOST_TO_ROUGH_SVG, IS_APPLE, IS_CHROME, IS_CHROME_LEGACY, IS_EDGE_LEGACY, IS_FIREFOX, IS_IOS, IS_SAFARI, IS_TEXT_EDITABLE, MERGING, NS, Path, PlaitBoardComponent, PlaitElementComponent, PlaitHistoryBoard, PlaitModule, PlaitNode, PlaitOperation, SAVING, Transforms, Viewport, createG, createSVG, createText, distanceBetweenPointAndPoint, distanceBetweenPointAndSegment, getViewBox, hotkeys, idCreator, inverse, isNoSelectionElement, isNullOrUndefined, isSetViewportOperation, rotate, shouldClear, shouldMerge, shouldSave, toPoint, toRectangleClient, transformPoint, transformPoints };
1271
1282
  //# sourceMappingURL=plait-core.mjs.map