@peteai/presentation-editor 0.0.1 → 0.0.3

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.
Files changed (214) hide show
  1. package/README.md +172 -38
  2. package/dist/components/presentation-editor/active-layers-buttons.svelte +53 -0
  3. package/dist/components/presentation-editor/active-layers-buttons.svelte.d.ts +3 -0
  4. package/dist/components/presentation-editor/active-layers.svelte +181 -0
  5. package/dist/components/presentation-editor/active-layers.svelte.d.ts +3 -0
  6. package/dist/components/presentation-editor/color-indicator/color-indicator-gradient-def.svelte +81 -0
  7. package/dist/components/presentation-editor/color-indicator/color-indicator-gradient-def.svelte.d.ts +9 -0
  8. package/dist/components/presentation-editor/color-indicator/color-indicator-gradient.svelte +21 -0
  9. package/dist/components/presentation-editor/color-indicator/color-indicator-gradient.svelte.d.ts +8 -0
  10. package/dist/components/presentation-editor/color-indicator/color-indicator.svelte +23 -0
  11. package/dist/components/presentation-editor/color-indicator/color-indicator.svelte.d.ts +6 -0
  12. package/dist/components/presentation-editor/color-indicator/index.d.ts +4 -0
  13. package/dist/components/presentation-editor/color-indicator/index.js +6 -0
  14. package/dist/components/presentation-editor/cursor-tooltip.svelte +1 -1
  15. package/dist/components/presentation-editor/dragged.svelte +21 -11
  16. package/dist/components/presentation-editor/fonts.d.ts +3 -0
  17. package/dist/components/presentation-editor/fonts.js +1278 -0
  18. package/dist/components/presentation-editor/header.svelte +21 -33
  19. package/dist/components/presentation-editor/header.svelte.d.ts +16 -6
  20. package/dist/components/presentation-editor/hotkeys.svelte +85 -0
  21. package/dist/components/presentation-editor/{sidebar/layers.svelte.d.ts → hotkeys.svelte.d.ts} +3 -3
  22. package/dist/components/presentation-editor/layers/active-background-border.svelte +3 -7
  23. package/dist/components/presentation-editor/layers/active-layer-border.svelte +2 -5
  24. package/dist/components/presentation-editor/layers/active-layer-border.svelte.d.ts +0 -1
  25. package/dist/components/presentation-editor/layers/buttons/border-button/border-button.svelte +113 -129
  26. package/dist/components/presentation-editor/layers/buttons/border-button/border-button.svelte.d.ts +2 -2
  27. package/dist/components/presentation-editor/layers/buttons/corner-radius-button/corner-radius-button.svelte +51 -32
  28. package/dist/components/presentation-editor/layers/buttons/corner-radius-button/corner-radius-button.svelte.d.ts +2 -2
  29. package/dist/components/presentation-editor/layers/buttons/flip-button/flip-button.svelte +30 -7
  30. package/dist/components/presentation-editor/layers/buttons/flip-button/flip-button.svelte.d.ts +3 -3
  31. package/dist/components/presentation-editor/layers/buttons/opacity-button/opacity-button.svelte +76 -33
  32. package/dist/components/presentation-editor/layers/buttons/opacity-button/opacity-button.svelte.d.ts +3 -3
  33. package/dist/components/presentation-editor/layers/controls/corner-scale-control/corner-scale-control.svelte +89 -59
  34. package/dist/components/presentation-editor/layers/controls/corner-scale-control/corner-scale-control.svelte.d.ts +5 -100
  35. package/dist/components/presentation-editor/layers/controls/group-resize-control/group-resize-control.svelte +337 -0
  36. package/dist/components/presentation-editor/layers/controls/group-resize-control/group-resize-control.svelte.d.ts +104 -0
  37. package/dist/components/presentation-editor/layers/controls/group-resize-control/index.d.ts +2 -0
  38. package/dist/components/presentation-editor/layers/controls/group-resize-control/index.js +4 -0
  39. package/dist/components/presentation-editor/layers/controls/rotate-control/rotate-control.svelte +128 -43
  40. package/dist/components/presentation-editor/layers/controls/rotate-control/rotate-control.svelte.d.ts +1 -5
  41. package/dist/components/presentation-editor/layers/controls/side-resize-control/side-resize-control.svelte +68 -57
  42. package/dist/components/presentation-editor/layers/controls/side-resize-control/side-resize-control.svelte.d.ts +2 -110
  43. package/dist/components/presentation-editor/layers/controls/side-scale-control/side-scale-control.svelte +45 -32
  44. package/dist/components/presentation-editor/layers/controls/side-scale-control/side-scale-control.svelte.d.ts +2 -54
  45. package/dist/components/presentation-editor/layers/index.d.ts +4 -5
  46. package/dist/components/presentation-editor/layers/index.js +7 -8
  47. package/dist/components/presentation-editor/layers/layer-button.svelte +25 -7
  48. package/dist/components/presentation-editor/layers/layer-wrapper.svelte +212 -162
  49. package/dist/components/presentation-editor/layers/layer-wrapper.svelte.d.ts +2 -2
  50. package/dist/components/presentation-editor/layers/types/background/background-content-image.svelte +41 -0
  51. package/dist/components/presentation-editor/layers/types/background/background-content-image.svelte.d.ts +8 -0
  52. package/dist/components/presentation-editor/layers/types/background/background-layer-buttons.svelte +28 -74
  53. package/dist/components/presentation-editor/layers/types/background/background-layer-buttons.svelte.d.ts +2 -17
  54. package/dist/components/presentation-editor/layers/types/background/background-layer-content.svelte +19 -0
  55. package/dist/components/presentation-editor/layers/types/background/background-layer-content.svelte.d.ts +8 -0
  56. package/dist/components/presentation-editor/layers/types/background/background-layer.svelte +69 -61
  57. package/dist/components/presentation-editor/layers/types/background/background-layer.svelte.d.ts +2 -3
  58. package/dist/components/presentation-editor/layers/types/background/index.d.ts +2 -3
  59. package/dist/components/presentation-editor/layers/types/background/index.js +2 -3
  60. package/dist/components/presentation-editor/layers/types/html/buttons/alignment-button/alignment-button.svelte +55 -12
  61. package/dist/components/presentation-editor/layers/types/html/buttons/alignment-button/alignment-button.svelte.d.ts +3 -3
  62. package/dist/components/presentation-editor/layers/types/html/buttons/bold-button/bold-button.svelte +60 -8
  63. package/dist/components/presentation-editor/layers/types/html/buttons/bold-button/bold-button.svelte.d.ts +3 -3
  64. package/dist/components/presentation-editor/layers/types/html/buttons/case-button/case-button.svelte +59 -24
  65. package/dist/components/presentation-editor/layers/types/html/buttons/case-button/case-button.svelte.d.ts +3 -3
  66. package/dist/components/presentation-editor/layers/types/html/buttons/color-button/color-button.svelte +27 -76
  67. package/dist/components/presentation-editor/layers/types/html/buttons/color-button/color-button.svelte.d.ts +3 -3
  68. package/dist/components/presentation-editor/layers/types/html/buttons/font-family-button/font-family-button.svelte +36 -0
  69. package/dist/components/presentation-editor/layers/types/html/buttons/font-family-button/font-family-button.svelte.d.ts +7 -0
  70. package/dist/components/presentation-editor/layers/types/html/buttons/font-family-button/index.d.ts +2 -0
  71. package/dist/components/presentation-editor/layers/types/html/buttons/font-family-button/index.js +2 -0
  72. package/dist/components/presentation-editor/layers/types/html/buttons/font-size-button/font-size-button.svelte +72 -29
  73. package/dist/components/presentation-editor/layers/types/html/buttons/font-size-button/font-size-button.svelte.d.ts +3 -5
  74. package/dist/components/presentation-editor/layers/types/html/buttons/italic-button/italic-button.svelte +60 -8
  75. package/dist/components/presentation-editor/layers/types/html/buttons/italic-button/italic-button.svelte.d.ts +3 -3
  76. package/dist/components/presentation-editor/layers/types/html/buttons/list-button/list-button.svelte +71 -18
  77. package/dist/components/presentation-editor/layers/types/html/buttons/list-button/list-button.svelte.d.ts +3 -3
  78. package/dist/components/presentation-editor/layers/types/html/buttons/strikethrough-button/strikethrough-button.svelte +54 -8
  79. package/dist/components/presentation-editor/layers/types/html/buttons/strikethrough-button/strikethrough-button.svelte.d.ts +3 -3
  80. package/dist/components/presentation-editor/layers/types/html/buttons/underline-button/underline-button.svelte +54 -9
  81. package/dist/components/presentation-editor/layers/types/html/buttons/underline-button/underline-button.svelte.d.ts +3 -3
  82. package/dist/components/presentation-editor/layers/types/html/editor/createEditor.js +2 -2
  83. package/dist/components/presentation-editor/layers/types/html/editor/utils.d.ts +11 -0
  84. package/dist/components/presentation-editor/layers/types/html/editor/utils.js +88 -0
  85. package/dist/components/presentation-editor/layers/types/html/extensions/font-family/font-family.d.ts +27 -0
  86. package/dist/components/presentation-editor/layers/types/html/extensions/font-family/font-family.js +40 -0
  87. package/dist/components/presentation-editor/layers/types/html/extensions/font-family/index.d.ts +3 -0
  88. package/dist/components/presentation-editor/layers/types/html/extensions/font-family/index.js +3 -0
  89. package/dist/components/presentation-editor/layers/types/html/extensions/font-size/font-size.d.ts +5 -1
  90. package/dist/components/presentation-editor/layers/types/html/extensions/font-size/font-size.js +3 -7
  91. package/dist/components/presentation-editor/layers/types/html/extensions.d.ts +1 -0
  92. package/dist/components/presentation-editor/layers/types/html/extensions.js +56 -0
  93. package/dist/components/presentation-editor/layers/types/html/html-content.svelte +26 -5
  94. package/dist/components/presentation-editor/layers/types/html/html-layer-content.svelte +26 -0
  95. package/dist/components/presentation-editor/layers/types/html/html-layer-content.svelte.d.ts +9 -0
  96. package/dist/components/presentation-editor/layers/types/html/html-layer-edit.svelte +103 -0
  97. package/dist/components/presentation-editor/layers/types/html/html-layer-edit.svelte.d.ts +8 -0
  98. package/dist/components/presentation-editor/layers/types/html/html-layer.svelte +61 -53
  99. package/dist/components/presentation-editor/layers/types/html/index.d.ts +3 -5
  100. package/dist/components/presentation-editor/layers/types/html/index.js +3 -56
  101. package/dist/components/presentation-editor/layers/types/image/{image-content.svelte → image-layer-content.svelte} +11 -3
  102. package/dist/components/presentation-editor/layers/types/image/image-layer-content.svelte.d.ts +8 -0
  103. package/dist/components/presentation-editor/layers/types/image/image-layer.svelte +51 -21
  104. package/dist/components/presentation-editor/layers/types/image/index.d.ts +2 -4
  105. package/dist/components/presentation-editor/layers/types/image/index.js +2 -4
  106. package/dist/components/presentation-editor/layers/utils.d.ts +68 -9
  107. package/dist/components/presentation-editor/layers/utils.js +260 -25
  108. package/dist/components/presentation-editor/menu/background-menu-content.svelte +80 -0
  109. package/dist/components/presentation-editor/menu/background-menu-content.svelte.d.ts +9 -0
  110. package/dist/components/presentation-editor/menu/layer-menu-content.svelte +183 -0
  111. package/dist/components/presentation-editor/menu/layer-menu-content.svelte.d.ts +3 -0
  112. package/dist/components/presentation-editor/menu/slide-menu-content.svelte +67 -0
  113. package/dist/components/presentation-editor/menu/slide-menu-content.svelte.d.ts +9 -0
  114. package/dist/components/presentation-editor/presentation-editor.svelte +120 -176
  115. package/dist/components/presentation-editor/presentation-editor.svelte.d.ts +1 -4
  116. package/dist/components/presentation-editor/presentation-editor.svelte.js +597 -136
  117. package/dist/components/presentation-editor/sidebar/color-sidebar/color-sidebar-color.svelte +58 -0
  118. package/dist/components/presentation-editor/sidebar/color-sidebar/color-sidebar-color.svelte.d.ts +10 -0
  119. package/dist/components/presentation-editor/sidebar/color-sidebar/color-sidebar-gradient-picker.svelte +144 -0
  120. package/dist/components/presentation-editor/sidebar/color-sidebar/color-sidebar-gradient-picker.svelte.d.ts +7 -0
  121. package/dist/components/presentation-editor/sidebar/color-sidebar/color-sidebar.svelte +404 -0
  122. package/dist/components/presentation-editor/sidebar/color-sidebar/color-sidebar.svelte.d.ts +3 -0
  123. package/dist/components/presentation-editor/sidebar/color-sidebar/index.d.ts +2 -0
  124. package/dist/components/presentation-editor/sidebar/color-sidebar/index.js +2 -0
  125. package/dist/components/presentation-editor/sidebar/font-sidebar/font-sidebar-button.svelte +26 -0
  126. package/dist/components/presentation-editor/sidebar/font-sidebar/font-sidebar-button.svelte.d.ts +8 -0
  127. package/dist/components/presentation-editor/sidebar/font-sidebar/font-sidebar.svelte +216 -0
  128. package/dist/components/presentation-editor/sidebar/font-sidebar/font-sidebar.svelte.d.ts +3 -0
  129. package/dist/components/presentation-editor/sidebar/font-sidebar/index.d.ts +2 -0
  130. package/dist/components/presentation-editor/sidebar/font-sidebar/index.js +2 -0
  131. package/dist/components/presentation-editor/sidebar/position-slidebar.svelte +130 -0
  132. package/dist/components/presentation-editor/sidebar/position-slidebar.svelte.d.ts +18 -0
  133. package/dist/components/presentation-editor/sidebar/sidebar-text-tab-button.svelte +90 -0
  134. package/dist/components/presentation-editor/sidebar/sidebar-text-tab-button.svelte.d.ts +7 -0
  135. package/dist/components/presentation-editor/sidebar/sidebar-text-tab.svelte +82 -0
  136. package/dist/components/presentation-editor/sidebar/sidebar-text-tab.svelte.d.ts +18 -0
  137. package/dist/components/presentation-editor/sidebar/{images-library.svelte → sidebar-uploads-tab.svelte} +0 -1
  138. package/dist/components/presentation-editor/sidebar/sidebar-uploads-tab.svelte.d.ts +3 -0
  139. package/dist/components/presentation-editor/sidebar/sidebar-wrapper.svelte +25 -0
  140. package/dist/components/presentation-editor/sidebar/sidebar-wrapper.svelte.d.ts +7 -0
  141. package/dist/components/presentation-editor/sidebar/sidebar.svelte +71 -15
  142. package/dist/components/presentation-editor/sidebar/sidebar.svelte.d.ts +16 -5
  143. package/dist/components/presentation-editor/sidebar/uploads-image.svelte +28 -11
  144. package/dist/components/presentation-editor/slide-editor.svelte +20 -22
  145. package/dist/components/presentation-editor/slide-inner.svelte +19 -18
  146. package/dist/components/presentation-editor/slides-navigation/slide-preview.svelte +61 -52
  147. package/dist/components/presentation-editor/slides-navigation/slides-navigation.svelte +6 -8
  148. package/dist/components/presentation-editor/snapping-guides.svelte +3 -3
  149. package/dist/components/presentation-editor/types.d.ts +67 -27
  150. package/dist/components/presentation-editor/utils.d.ts +50 -1
  151. package/dist/components/presentation-editor/utils.js +101 -6
  152. package/dist/components/ui/button/button.svelte +3 -2
  153. package/dist/components/ui/button/button.svelte.d.ts +5 -82
  154. package/dist/components/ui/color-picker/color-picker-alpha-grid.svelte +43 -0
  155. package/dist/components/ui/color-picker/color-picker-alpha-grid.svelte.d.ts +8 -0
  156. package/dist/components/ui/color-picker/color-picker.svelte +344 -0
  157. package/dist/components/ui/color-picker/color-picker.svelte.d.ts +13 -0
  158. package/dist/components/ui/color-picker/index.d.ts +3 -0
  159. package/dist/components/ui/color-picker/index.js +5 -0
  160. package/dist/components/ui/context-menu/context-menu-shortcut.svelte +6 -3
  161. package/dist/components/ui/context-menu/context-menu-sub-trigger.svelte +1 -1
  162. package/dist/components/ui/dropdown-menu/dropdown-menu-item.svelte +1 -1
  163. package/dist/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte +6 -3
  164. package/dist/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte +1 -1
  165. package/dist/components/ui/infinite-loader/index.d.ts +4 -0
  166. package/dist/components/ui/infinite-loader/index.js +4 -0
  167. package/dist/components/ui/infinite-loader/infinite-loader-loop-tracker.d.ts +13 -0
  168. package/dist/components/ui/infinite-loader/infinite-loader-loop-tracker.js +44 -0
  169. package/dist/components/ui/infinite-loader/infinite-loader-state.svelte.d.ts +15 -0
  170. package/dist/components/ui/infinite-loader/infinite-loader-state.svelte.js +28 -0
  171. package/dist/components/ui/infinite-loader/infinite-loader.svelte +149 -0
  172. package/dist/components/ui/infinite-loader/infinite-loader.svelte.d.ts +17 -0
  173. package/dist/components/ui/input/input.svelte +1 -1
  174. package/dist/components/ui/slider/slider.svelte +20 -18
  175. package/dist/components/ui/tabs/index.d.ts +6 -0
  176. package/dist/components/ui/tabs/index.js +8 -0
  177. package/dist/components/ui/tabs/tabs-content.svelte +19 -0
  178. package/dist/components/ui/tabs/tabs-content.svelte.d.ts +4 -0
  179. package/dist/components/ui/tabs/tabs-list.svelte +19 -0
  180. package/dist/components/ui/tabs/tabs-list.svelte.d.ts +4 -0
  181. package/dist/components/ui/tabs/tabs-trigger.svelte +19 -0
  182. package/dist/components/ui/tabs/tabs-trigger.svelte.d.ts +4 -0
  183. package/dist/components/ui/toggle/toggle.svelte +3 -2
  184. package/dist/components/ui/toggle/toggle.svelte.d.ts +2 -46
  185. package/dist/index.d.ts +5 -6
  186. package/dist/index.js +1 -4
  187. package/dist/plugin.js +3 -2
  188. package/dist/utils.d.ts +1 -0
  189. package/dist/utils.js +1 -0
  190. package/package.json +28 -25
  191. package/dist/components/presentation-editor/app.css +0 -12
  192. package/dist/components/presentation-editor/layers/hovered-layer.svelte +0 -34
  193. package/dist/components/presentation-editor/layers/hovered-layer.svelte.d.ts +0 -7
  194. package/dist/components/presentation-editor/layers/types/background/background-content.svelte +0 -11
  195. package/dist/components/presentation-editor/layers/types/background/background-content.svelte.d.ts +0 -7
  196. package/dist/components/presentation-editor/layers/types/background/background-layer-thumb.svelte +0 -12
  197. package/dist/components/presentation-editor/layers/types/background/background-layer-thumb.svelte.d.ts +0 -7
  198. package/dist/components/presentation-editor/layers/types/html/html-layer-active.svelte +0 -159
  199. package/dist/components/presentation-editor/layers/types/html/html-layer-active.svelte.d.ts +0 -8
  200. package/dist/components/presentation-editor/layers/types/html/html-layer-buttons.svelte +0 -42
  201. package/dist/components/presentation-editor/layers/types/html/html-layer-buttons.svelte.d.ts +0 -10
  202. package/dist/components/presentation-editor/layers/types/html/html-layer-thumb.svelte +0 -24
  203. package/dist/components/presentation-editor/layers/types/html/html-layer-thumb.svelte.d.ts +0 -8
  204. package/dist/components/presentation-editor/layers/types/image/image-content.svelte.d.ts +0 -8
  205. package/dist/components/presentation-editor/layers/types/image/image-layer-buttons.svelte +0 -21
  206. package/dist/components/presentation-editor/layers/types/image/image-layer-buttons.svelte.d.ts +0 -7
  207. package/dist/components/presentation-editor/layers/types/image/image-layer-thumb.svelte +0 -13
  208. package/dist/components/presentation-editor/layers/types/image/image-layer-thumb.svelte.d.ts +0 -8
  209. package/dist/components/presentation-editor/sidebar/images-library.svelte.d.ts +0 -3
  210. package/dist/components/presentation-editor/sidebar/layers.svelte +0 -94
  211. package/dist/components/presentation-editor/slides-navigation/buttons/slide-delete-button.svelte +0 -32
  212. package/dist/components/presentation-editor/slides-navigation/buttons/slide-delete-button.svelte.d.ts +0 -10
  213. package/dist/components/presentation-editor/slides-navigation/buttons/slide-duplicate-button.svelte +0 -34
  214. package/dist/components/presentation-editor/slides-navigation/buttons/slide-duplicate-button.svelte.d.ts +0 -10
@@ -1,29 +1,98 @@
1
1
  import uniqid from 'uniqid';
2
2
  import { getContext, setContext } from 'svelte';
3
+ import { defaultFonts } from './fonts.js';
3
4
  import { cognitionImages, cognitionSlides } from './cognition-slides.js';
4
5
  import { cognitionSlideToJson } from './utils.js';
5
- import { calculateBoundingBox } from './layers/utils.js';
6
+ import { calculateLayerTransform, calculateBoundingBox, calculateImageCover, checkPolygonsIntersect, getRotatedCorners, calculateGroupBoundingBox, calculateGroupRotatedBoundingBox, } from './layers/utils.js';
7
+ import createEditor from './layers/types/html/editor/createEditor.js';
8
+ import { extensions } from './layers/types/html/extensions.js';
9
+ import { SvelteSet } from 'svelte/reactivity';
6
10
  export class PresentationEditor {
7
11
  mode = 'multiple';
8
- slides = $state(cognitionSlides.map(cognitionSlideToJson)); // getSlides()
12
+ fonts = defaultFonts;
9
13
  images = $state(cognitionImages);
14
+ slides = $state(cognitionSlides.map(cognitionSlideToJson)); // getSlides()
10
15
  width = 1920;
11
16
  height = 1080;
12
17
  zoom = $state(0.4);
13
18
  generateId = () => uniqid();
19
+ loadFont = async (family) => {
20
+ const font = this.fonts[family];
21
+ if (!font)
22
+ return;
23
+ const WebFont = await import('webfontloader');
24
+ return new Promise((resolve, reject) => {
25
+ WebFont.load({
26
+ [font.type]: { families: [family] },
27
+ active: () => resolve(null),
28
+ inactive: reject,
29
+ });
30
+ });
31
+ };
32
+ searchFonts = (q, limit) => {
33
+ const terms = q
34
+ .trim()
35
+ .split(/\W/g)
36
+ .map((term) => term.toLowerCase());
37
+ return Object.keys(this.fonts)
38
+ .filter((family) => terms.some((term) => ~family.toLowerCase().indexOf(term)))
39
+ .slice(0, limit);
40
+ };
14
41
  onImageUpload;
15
42
  onSlideAdd;
16
43
  onSlideUpdate;
17
44
  onSlideRemove;
45
+ onBackgroundImageAdd;
46
+ onBackgroundImageUpdate;
47
+ onBackgroundImageRemove;
18
48
  onLayerAdd;
19
49
  onLayerUpdate;
20
50
  onLayerRemove;
51
+ activeSidebarTab = $state(null);
52
+ activeSidebarPopup = $state(null);
53
+ toggleActiveSidebarTab(tab) {
54
+ this.activeSidebarTab = !this.activeSidebarPopup && this.activeSidebarTab === tab ? null : tab;
55
+ if (this.activeSidebarTab) {
56
+ this.activeSidebarPopup = null;
57
+ }
58
+ }
59
+ toggleActiveSidebarPopup(popup) {
60
+ this.activeSidebarPopup = this.activeSidebarPopup === popup ? null : popup;
61
+ }
62
+ zoomSnapping = $state(null);
63
+ findSlide(slideId) {
64
+ return this.slides.find(({ id }) => id === slideId);
65
+ }
66
+ findLayer(slide, layerId) {
67
+ return slide.layers.find(({ id }) => id === layerId);
68
+ }
69
+ /**
70
+ * Finds layers in a slide based on their IDs.
71
+ *
72
+ * @param slide The slide to search for layers.
73
+ * @param layerIds The IDs of the layers to find. Can be a single ID or an array of IDs.
74
+ * @returns An array of layers that match the provided IDs.
75
+ */
76
+ findLayers(slide, layerIds) {
77
+ // Normalize the input to ensure it's always an array of IDs
78
+ const ids = Array.isArray(layerIds) ? layerIds : [layerIds];
79
+ // Filter the slide's layers to only include those with IDs in the provided list
80
+ return slide.layers.filter(({ id }) => ids.includes(id));
81
+ }
21
82
  activeSlide = $state();
22
- clipboard = null;
23
- slideSelected = $state(false);
24
- activeLayer = $state();
25
83
  activeBackground = $state(false);
26
- htmlEditor = $state.raw(null);
84
+ activeLayerIds = new SvelteSet();
85
+ activeLayers = $derived(this.activeBackground
86
+ ? []
87
+ : this.activeSlide.layers.filter(({ id }) => this.activeLayerIds.has(id)));
88
+ activeGroupRotate = $state(0);
89
+ slideSelected = $state(false);
90
+ clipboard = null;
91
+ htmlEditor = createEditor({
92
+ extensions,
93
+ injectCSS: false,
94
+ editorProps: { attributes: { class: 'whitespace-pre-wrap focus:outline-none' } },
95
+ });
27
96
  activeAction = $state(null);
28
97
  historyActions = $state([]);
29
98
  historyIndex = $state(-1);
@@ -41,7 +110,7 @@ export class PresentationEditor {
41
110
  Object.assign(this, options);
42
111
  }
43
112
  if (!this.slides.length) {
44
- this.addSlide({ index: 0 });
113
+ this.addSlide();
45
114
  }
46
115
  // initial sorting
47
116
  this.sortSlides();
@@ -62,53 +131,184 @@ export class PresentationEditor {
62
131
  return {
63
132
  id: this.generateId(),
64
133
  sortOrder,
134
+ locked: false,
65
135
  backgroundColor: null,
136
+ backgroundImage: null,
137
+ backgroundLocked: false,
66
138
  layers: [],
67
139
  };
68
140
  }
69
- addSlide({ slide = this.buildEmptySlide(), index = this.findSlideIndex(this.activeSlide.id) + 1, } = {}) {
141
+ async addSlide({ slide, index = this.findSlideIndex(this.activeSlide.id) + 1, } = {}) {
70
142
  if (this.mode !== 'multiple')
71
- return;
143
+ return false;
144
+ if (!slide) {
145
+ slide = this.buildEmptySlide();
146
+ const inheritedSlide = this.slides[index > 0 ? index - 1 : 0];
147
+ if (inheritedSlide) {
148
+ Object.assign(slide, { backgroundColor: inheritedSlide.backgroundColor });
149
+ }
150
+ }
72
151
  const prevSlide = this.slides[index - 1];
73
152
  const nextSlide = this.slides[index];
74
153
  if (!prevSlide && !nextSlide)
75
- return;
154
+ return false;
76
155
  const sortOrder = nextSlide && prevSlide
77
156
  ? (prevSlide.sortOrder + nextSlide.sortOrder) / 2
78
157
  : prevSlide
79
158
  ? prevSlide.sortOrder + 1
80
159
  : nextSlide.sortOrder - 1;
81
- this.historyPush({ type: 'slideAdd', slide: { ...slide, sortOrder } });
160
+ return this.historyPush({ type: 'slideAdd', slide: { ...slide, sortOrder } });
82
161
  }
83
- removeSlide(slide) {
162
+ removeSlide(slideId) {
84
163
  if (this.mode !== 'multiple')
85
- return;
164
+ return false;
165
+ const slide = this.findSlide(slideId);
166
+ if (!slide || slide.locked)
167
+ return false;
86
168
  this.historyPush({ type: 'slideRemove', slide });
87
169
  }
88
- duplicateSlide(slide) {
170
+ copySlide(slideId) {
171
+ const slide = this.findSlide(slideId);
172
+ if (!slide)
173
+ return;
174
+ this.clipboard = $state.snapshot(slide);
175
+ }
176
+ async toggleSlideLocked(slideId) {
89
177
  if (this.mode !== 'multiple')
178
+ return false;
179
+ const slide = this.findSlide(slideId);
180
+ if (!slide)
181
+ return false;
182
+ return this.historyPush({
183
+ type: 'slide',
184
+ slideId,
185
+ undo: { locked: slide.locked },
186
+ redo: { locked: !slide.locked },
187
+ });
188
+ }
189
+ async removeBackground(slideId) {
190
+ const slide = this.findSlide(slideId);
191
+ if (!slide || slide.locked)
90
192
  return;
193
+ await this.historyPush({
194
+ slideId: slide.id,
195
+ type: 'slide',
196
+ ...(slide.backgroundImage
197
+ ? { undo: { backgroundImage: slide.backgroundImage }, redo: { backgroundImage: null } }
198
+ : { undo: { backgroundColor: slide.backgroundColor }, redo: { backgroundColor: null } }),
199
+ });
200
+ }
201
+ async backgroundImageToLayer(slideId) {
202
+ const slide = this.findSlide(slideId);
203
+ if (!slide || slide.locked)
204
+ return;
205
+ if (!slide.backgroundImage)
206
+ return;
207
+ await this.historyPush({
208
+ type: 'layerToSlideBackground',
209
+ slideId: slide.id,
210
+ imageLayer: this.buildImageLayer({
211
+ ...slide.backgroundImage,
212
+ sortOrder: this.generateLayersSortOrder()[0],
213
+ // clean some background props
214
+ scale: null,
215
+ opacity: null,
216
+ // clean offset to avoid wrong positioning
217
+ offsetX: null,
218
+ offsetY: null,
219
+ }),
220
+ backgroundImage: null,
221
+ opposite: true,
222
+ });
223
+ }
224
+ async backgroundToggleLocked(slideId) {
225
+ const slide = this.findSlide(slideId);
226
+ if (!slide || slide.locked)
227
+ return false;
228
+ return this.historyPush({
229
+ type: 'slide',
230
+ slideId,
231
+ undo: { backgroundLocked: slide.backgroundLocked },
232
+ redo: { backgroundLocked: !slide.backgroundLocked },
233
+ });
234
+ }
235
+ async duplicateSlide(slide) {
236
+ if (this.mode !== 'multiple')
237
+ return false;
91
238
  const { layers, ...rest } = slide;
92
239
  const duplicate = {
93
240
  ...rest,
94
241
  id: this.generateId(),
95
242
  layers: layers.map((layer) => ({ ...layer, id: this.generateId() })),
96
243
  };
97
- this.addSlide({ slide: duplicate });
244
+ return this.addSlide({ slide: duplicate });
98
245
  }
99
- async addImageLayer({ image, x, y, width, height, sortOrder, ...rest }) {
100
- const imageLayer = {
246
+ generateLayersSortOrder(amount = 1, index = this.activeSlide.layers.length) {
247
+ index = Math.min(index, this.activeSlide.layers.length);
248
+ const prev = this.activeSlide.layers[index - 1];
249
+ const next = this.activeSlide.layers[index];
250
+ let startingOrder = 0;
251
+ let increment = 1;
252
+ if (next && prev) {
253
+ increment = (next.sortOrder - prev.sortOrder) / (amount + 1);
254
+ startingOrder = prev.sortOrder;
255
+ }
256
+ else if (prev) {
257
+ startingOrder = prev.sortOrder;
258
+ }
259
+ else if (next) {
260
+ startingOrder = next.sortOrder - (amount + 1);
261
+ }
262
+ return Array.from({ length: amount }, (_, i) => startingOrder + increment * (i + 1));
263
+ }
264
+ buildHtmlLayer({ html, width, height, ...rest }) {
265
+ const scale = rest.scale ?? 1;
266
+ const x = rest.x ?? (this.width - width * scale) / 2;
267
+ const y = rest.y ?? (this.height - height * scale) / 2;
268
+ const layer = {
101
269
  id: this.generateId(),
102
- type: 'image',
270
+ type: 'html',
103
271
  x,
104
272
  y,
105
273
  width,
106
274
  height,
107
- image,
108
- sortOrder: sortOrder ??
109
- (this.activeSlide.layers.length
110
- ? this.activeSlide.layers[this.activeSlide.layers.length - 1].sortOrder + 1
111
- : 1),
275
+ html,
276
+ sortOrder: 1,
277
+ locked: false,
278
+ rotate: null,
279
+ opacity: null,
280
+ scale,
281
+ ...rest,
282
+ };
283
+ console.log('htmlLayer', layer);
284
+ return layer;
285
+ }
286
+ getMaxScale({ width, height }) {
287
+ return Math.min(this.width / 2 / width, this.height / 2 / height);
288
+ }
289
+ buildImageLayer({ image, ...rest }) {
290
+ const width = rest.width ?? image.width;
291
+ const height = rest.height ?? image.height;
292
+ const inputScale = rest.scale ?? 1;
293
+ const scale = rest.scale ?? Math.min(this.getMaxScale({ width, height }), 1);
294
+ if (scale !== 1) {
295
+ rest.scale = scale;
296
+ }
297
+ // adjust x and y according to scale if set or center image
298
+ const x = rest.x == null
299
+ ? (this.width - width * scale) / 2
300
+ : rest.x + (width * inputScale - width * scale) / 2;
301
+ const y = rest.y == null
302
+ ? (this.height - height * scale) / 2
303
+ : rest.y + (height * inputScale - height * scale) / 2;
304
+ const layer = {
305
+ id: this.generateId(),
306
+ type: 'image',
307
+ width,
308
+ height,
309
+ image: { ...image },
310
+ sortOrder: 1,
311
+ locked: false,
112
312
  rotate: null,
113
313
  opacity: null,
114
314
  scale: null,
@@ -121,51 +321,66 @@ export class PresentationEditor {
121
321
  borderWidth: null,
122
322
  borderColor: null,
123
323
  ...rest,
324
+ x,
325
+ y,
124
326
  };
125
- console.log('imageLayer', imageLayer);
126
- this.historyPush({ type: 'layerAdd', layer: { ...imageLayer } });
327
+ console.log('imageLayer', layer);
328
+ return layer;
127
329
  }
128
- addLayer({ layer, index = this.activeSlide.layers.length }) {
129
- const prev = this.activeSlide.layers[index - 1];
130
- const next = this.activeSlide.layers[index];
131
- if (!prev && !next)
330
+ addLayers(slideId, layers, index) {
331
+ const slide = this.findSlide(slideId);
332
+ if (!slide || slide.locked)
132
333
  return;
133
- const sortOrder = next && prev
134
- ? (prev.sortOrder + next.sortOrder) / 2
135
- : prev
136
- ? prev.sortOrder + 1
137
- : next.sortOrder - 1;
138
- this.historyPush({ type: 'layerAdd', layer: { ...layer, sortOrder } });
139
- }
140
- duplicateLayer(layer) {
334
+ layers = Array.isArray(layers) ? layers : [layers];
335
+ const layersSortOrder = this.generateLayersSortOrder(layers.length, index);
336
+ this.historyPush({
337
+ type: 'layersAdd',
338
+ slideId: slide.id,
339
+ layers: layers.map((layer, i) => ({ ...layer, sortOrder: layersSortOrder[i] })),
340
+ });
341
+ }
342
+ copyLayers(slideId, layerIds) {
343
+ const slide = this.findSlide(slideId);
344
+ if (!slide || slide.locked)
345
+ return;
346
+ const layers = this.findLayers(slide, layerIds);
347
+ if (!layers.length)
348
+ return;
349
+ this.clipboard = $state.snapshot(layers);
350
+ }
351
+ duplicateLayers(layers) {
352
+ const duplicates = layers.map((layer) => ({
353
+ ...layer,
354
+ id: this.generateId(),
355
+ }));
141
356
  const increment = 50;
142
- const scale = layer.scale || 1;
143
- const xIncrement = layer.x + (layer.width * scale) / 2 > this.width / 2 ? -increment : increment;
144
- const yIncrement = layer.y + (layer.height * scale) / 2 > this.height / 2 ? -increment : increment;
357
+ const groupBbox = calculateGroupBoundingBox(layers.map(calculateLayerTransform));
358
+ const xIncrement = groupBbox.x + groupBbox.width / 2 > this.width / 2 ? -increment : increment;
359
+ const yIncrement = groupBbox.y + groupBbox.height / 2 > this.height / 2 ? -increment : increment;
145
360
  let xAdjust = 0;
146
- let iteration = 1;
361
+ let iteration = 0;
147
362
  const tryToDuplicate = () => {
148
- const duplicate = {
149
- ...layer,
150
- id: this.generateId(),
363
+ layers.forEach((layer, i) => Object.assign(duplicates[i], {
151
364
  x: layer.x + xAdjust + xIncrement * iteration,
152
365
  y: layer.y + yIncrement * iteration,
153
- };
154
- const occupied = this.activeSlide.layers.some((l) => l.x === duplicate.x &&
366
+ }));
367
+ const occupied = duplicates.every((duplicate) => this.activeSlide.layers.some((l) => l.x === duplicate.x &&
155
368
  l.y === duplicate.y &&
156
369
  l.width === duplicate.width &&
157
- l.height === duplicate.height);
370
+ l.height === duplicate.height &&
371
+ l.scale === duplicate.scale &&
372
+ l.rotate === duplicate.rotate));
158
373
  if (occupied) {
159
374
  iteration++;
160
375
  tryToDuplicate();
161
376
  }
162
- else if (this.checkIfLayerInBounds(duplicate, true)) {
163
- this.addLayer({ layer: duplicate });
377
+ else if (!duplicates.some((duplicate) => this.isLayerOutOfBounds(duplicate))) {
378
+ this.addLayers(this.activeSlide.id, duplicates);
164
379
  }
165
380
  else {
166
381
  xAdjust += xIncrement;
167
382
  iteration = 0;
168
- const newX = layer.x + xAdjust;
383
+ const newX = groupBbox.x + xAdjust;
169
384
  if (newX >= 0 && newX < this.width) {
170
385
  tryToDuplicate();
171
386
  }
@@ -173,41 +388,297 @@ export class PresentationEditor {
173
388
  };
174
389
  tryToDuplicate();
175
390
  }
391
+ async removeLayers(slideId, layerIds) {
392
+ const slide = this.findSlide(slideId);
393
+ if (!slide || slide.locked)
394
+ return;
395
+ const layers = this.findLayers(slide, layerIds).filter(({ locked }) => !locked);
396
+ if (!layers.length)
397
+ return;
398
+ this.historyPush({
399
+ type: 'layersRemove',
400
+ slideId: slide.id,
401
+ layers,
402
+ });
403
+ }
404
+ checkOverlap(corners, layer) {
405
+ const layerCorners = getRotatedCorners(calculateLayerTransform(layer));
406
+ return checkPolygonsIntersect(corners, layerCorners);
407
+ }
408
+ getLayerOverlaps(slide, layers) {
409
+ const indexes = layers.map((layer) => slide.layers.findIndex(({ id }) => id === layer.id));
410
+ const minIndex = Math.min(...indexes);
411
+ const maxIndex = Math.max(...indexes);
412
+ const corners = getRotatedCorners(layers.length === 1
413
+ ? calculateLayerTransform(layers[0])
414
+ : { ...calculateGroupBoundingBox(layers.map(calculateLayerTransform)), rotate: 0 });
415
+ const overlapsIndexes = slide.layers
416
+ .map((l, i) => ({ l, i }))
417
+ .filter(({ l }) => !layers.includes(l) && this.checkOverlap(corners, l))
418
+ .map(({ i }) => i);
419
+ const overlaps = {
420
+ forward: overlapsIndexes.find((i) => i > maxIndex),
421
+ front: overlapsIndexes.findLast((i) => i > maxIndex),
422
+ backward: overlapsIndexes.findLast((i) => i < minIndex),
423
+ back: overlapsIndexes.find((i) => i < minIndex),
424
+ };
425
+ if (overlaps.forward === undefined) {
426
+ const forwardIndex = overlapsIndexes.findLast((i) => i > minIndex);
427
+ overlaps.forward = forwardIndex;
428
+ overlaps.front = forwardIndex;
429
+ }
430
+ if (overlaps.backward === undefined) {
431
+ const backwardIndex = overlapsIndexes.find((i) => i < maxIndex);
432
+ overlaps.backward = backwardIndex;
433
+ overlaps.back = backwardIndex;
434
+ }
435
+ return overlaps.backward !== undefined || overlaps.forward !== undefined ? overlaps : null;
436
+ }
437
+ async moveLayers(slideId, layerIds, direction) {
438
+ const slide = this.findSlide(slideId);
439
+ if (!slide || slide.locked)
440
+ return;
441
+ const layers = this.findLayers(slide, layerIds);
442
+ if (!layers.length || layers.some(({ locked }) => locked))
443
+ return;
444
+ const overlaps = this.getLayerOverlaps(slide, layers);
445
+ if (!overlaps)
446
+ return;
447
+ const newIndex = overlaps[direction];
448
+ if (newIndex === undefined)
449
+ return;
450
+ const sortOrders = this.generateLayersSortOrder(layers.length, ['forward', 'front'].includes(direction) ? newIndex + 1 : newIndex);
451
+ await this.historyPush({
452
+ type: 'layersGroup',
453
+ actions: layers.map((layer, i) => {
454
+ return {
455
+ type: 'layer',
456
+ slideId,
457
+ layer: { id: layer.id, type: layer.type },
458
+ undo: { sortOrder: layer.sortOrder },
459
+ redo: { sortOrder: sortOrders[i] },
460
+ };
461
+ }),
462
+ });
463
+ }
464
+ async alignLayers(slideId, layerIds, origin) {
465
+ const slide = this.findSlide(slideId);
466
+ if (!slide || slide.locked)
467
+ return;
468
+ const layers = this.findLayers(slide, layerIds);
469
+ if (!layers.length || layers.some(({ locked }) => locked))
470
+ return;
471
+ const bbox = layers.length === 1
472
+ ? { x: 0, y: 0, width: this.width, height: this.height }
473
+ : calculateBoundingBox(calculateGroupRotatedBoundingBox(layers.map(calculateLayerTransform), this.activeGroupRotate));
474
+ await this.historyPush({
475
+ type: 'layersGroup',
476
+ actions: layers.map((layer) => {
477
+ const layerTransform = calculateLayerTransform(layer);
478
+ const layerBbox = calculateBoundingBox(layerTransform);
479
+ const { x, y, width, height } = layerTransform;
480
+ const alignment = {
481
+ left: {
482
+ undo: { x },
483
+ redo: { x: bbox.x + (layerBbox.width - width) / 2 },
484
+ },
485
+ center: {
486
+ undo: { x },
487
+ redo: { x: bbox.x + (bbox.width - width) / 2 },
488
+ },
489
+ right: {
490
+ undo: { x },
491
+ redo: { x: bbox.x + bbox.width - width - (layerBbox.width - width) / 2 },
492
+ },
493
+ top: {
494
+ undo: { y },
495
+ redo: { y: bbox.y + (layerBbox.height - height) / 2 },
496
+ },
497
+ middle: {
498
+ undo: { y },
499
+ redo: { y: bbox.y + (bbox.height - height) / 2 },
500
+ },
501
+ bottom: {
502
+ undo: { y },
503
+ redo: { y: bbox.y + bbox.height - height - (layerBbox.height - height) / 2 },
504
+ },
505
+ }[origin];
506
+ return {
507
+ type: 'layer',
508
+ slideId,
509
+ layer: { id: layer.id, type: layer.type },
510
+ ...alignment,
511
+ };
512
+ }),
513
+ });
514
+ }
515
+ async imageLayerToBackground(slideId, layerId) {
516
+ const slide = this.findSlide(slideId);
517
+ if (!slide || slide.locked)
518
+ return false;
519
+ if (slide.backgroundLocked)
520
+ return false;
521
+ const layer = this.findLayer(slide, layerId);
522
+ if (!layer || layer.locked)
523
+ return false;
524
+ if (layer.type !== 'image')
525
+ return false;
526
+ await this.historyPush({
527
+ type: 'layerToSlideBackground',
528
+ slideId: slide.id,
529
+ imageLayer: layer,
530
+ backgroundImage: slide.backgroundImage,
531
+ });
532
+ }
533
+ async toggleLayersLocked(slideId, layerIds, locked) {
534
+ const slide = this.findSlide(slideId);
535
+ if (!slide || slide.locked)
536
+ return false;
537
+ const layers = this.findLayers(slide, layerIds);
538
+ if (!layers.length)
539
+ return false;
540
+ return this.historyPush({
541
+ type: 'layersGroup',
542
+ actions: layers.map((layer) => ({
543
+ type: 'layer',
544
+ slideId,
545
+ layer: { id: layer.id, type: layer.type },
546
+ undo: { locked: layer.locked },
547
+ redo: { locked },
548
+ })),
549
+ });
550
+ }
551
+ clipboardPaste() {
552
+ if (this.clipboard) {
553
+ if (Array.isArray(this.clipboard)) {
554
+ this.duplicateLayers(this.clipboard);
555
+ }
556
+ else if ('layers' in this.clipboard) {
557
+ this.duplicateSlide(this.clipboard);
558
+ }
559
+ }
560
+ }
561
+ dropDragged(x, y, index) {
562
+ if (this.dragged) {
563
+ if ('image' in this.dragged) {
564
+ const { image, scale } = this.dragged;
565
+ const { width, height } = image;
566
+ const layer = this.buildImageLayer({ x, y, width, height, image, scale });
567
+ this.addLayers(this.activeSlide.id, layer, index);
568
+ }
569
+ else if (this.dragged?.ref) {
570
+ const { ref, html, scale } = this.dragged;
571
+ const rect = ref.getBoundingClientRect();
572
+ const width = rect.width / scale / this.zoom;
573
+ const height = rect.height / scale / this.zoom;
574
+ const layer = this.buildHtmlLayer({ x, y, width, height, scale, html });
575
+ this.addLayers(this.activeSlide.id, layer, index);
576
+ }
577
+ }
578
+ }
176
579
  setActiveSlide(slideId) {
177
580
  if (this.mode !== 'multiple')
178
581
  return;
179
- const slide = this.slides.find(({ id }) => id === slideId);
180
- if (slide) {
181
- this.setActiveLayer(null);
182
- // this.historyReset();
183
- this.activeSlide = slide;
184
- this.slideSelected = true;
185
- return slide;
582
+ const slide = this.findSlide(slideId);
583
+ if (!slide)
584
+ return;
585
+ this.clearActiveLayer();
586
+ // this.historyReset();
587
+ this.activeSlide = slide;
588
+ this.slideSelected = true;
589
+ return slide;
590
+ }
591
+ setActiveLayers(layerIds, action) {
592
+ layerIds = Array.isArray(layerIds) ? layerIds : [layerIds];
593
+ // Reset the active action, background, and slide selection
594
+ this.activeAction = null;
595
+ this.activeBackground = false;
596
+ this.slideSelected = false;
597
+ // If the action is 'delete', remove the layer from the active layer ids
598
+ if (action === 'delete') {
599
+ layerIds.forEach((layerId) => this.activeLayerIds.delete(layerId));
600
+ }
601
+ else {
602
+ // If no action is provided, clear the active layer ids
603
+ if (!action) {
604
+ this.activeLayerIds.clear();
605
+ this.activeGroupRotate = 0;
606
+ }
607
+ // Add the layers to the active layer ids
608
+ layerIds.forEach((layerId) => this.activeLayerIds.add(layerId));
186
609
  }
187
610
  }
188
- setActiveLayer(layer) {
611
+ clearActiveLayer() {
189
612
  this.activeAction = null;
190
- this.activeLayer = layer;
191
613
  this.activeBackground = false;
192
614
  this.slideSelected = false;
615
+ this.activeLayerIds.clear();
616
+ this.activeGroupRotate = 0;
193
617
  }
194
618
  setActiveBackground(value) {
195
619
  this.activeAction = null;
196
- this.activeLayer = null;
197
620
  this.activeBackground = value;
198
621
  this.slideSelected = false;
622
+ this.activeLayerIds.clear();
623
+ this.activeGroupRotate = 0;
624
+ }
625
+ doSlideUpdate(slide, changes) {
626
+ Object.assign(slide, changes);
627
+ if ('sortOrder' in changes) {
628
+ this.sortSlides();
629
+ }
630
+ this.onSlideUpdate?.(slide.id, changes);
631
+ }
632
+ doBackgroundImageAdd(slide, backgroundImage) {
633
+ Object.assign(slide, { backgroundImage });
634
+ if (this.onBackgroundImageAdd) {
635
+ this.onBackgroundImageAdd(slide.id, backgroundImage);
636
+ }
637
+ }
638
+ doBackgroundImageUpdate(slide, changes) {
639
+ if (!slide.backgroundImage)
640
+ return;
641
+ Object.assign(slide.backgroundImage, changes);
642
+ this.onBackgroundImageUpdate?.(slide.id, changes);
643
+ }
644
+ doBackgroundImageRemove(slide) {
645
+ Object.assign(slide, { backgroundImage: null });
646
+ if (this.onBackgroundImageRemove) {
647
+ this.onBackgroundImageRemove(slide.id);
648
+ }
649
+ }
650
+ async doLayersAdd(slide, layers) {
651
+ if (this.onLayerAdd) {
652
+ const func = this.onLayerAdd;
653
+ layers = await Promise.all(layers.map((layer) => func(slide.id, { ...layer })));
654
+ }
655
+ slide.layers.push(...layers);
656
+ this.sortLayers(slide.layers);
657
+ this.setActiveLayers(layers.map(({ id }) => id));
658
+ return layers;
659
+ }
660
+ doLayersRemove(slide, layers) {
661
+ layers.forEach((layer) => {
662
+ const index = slide.layers.findIndex(({ id }) => id === layer.id);
663
+ if (!~index)
664
+ return;
665
+ this.setActiveLayers(layer.id, 'delete');
666
+ slide.layers.splice(index, 1);
667
+ this.onLayerRemove?.(slide.id, layer.id, layer.type);
668
+ });
199
669
  }
200
670
  async applyChanges(action, type) {
201
671
  if (this.mode === 'multiple' && (action.type === 'slideAdd' || action.type === 'slideRemove')) {
202
672
  if ((action.type === 'slideAdd' && type === 'redo') ||
203
673
  (action.type === 'slideRemove' && type === 'undo')) {
204
- let newSlide = { ...action.slide };
674
+ let slide = { ...action.slide };
205
675
  if (this.onSlideAdd) {
206
- newSlide = await this.onSlideAdd(action.slide);
676
+ slide = await this.onSlideAdd(slide);
677
+ action.slide.id = slide.id;
207
678
  }
208
- this.slides.push(newSlide);
679
+ this.slides.push(slide);
209
680
  this.sortSlides();
210
- this.setActiveSlide(newSlide.id);
681
+ this.setActiveSlide(slide.id);
211
682
  if (action.type === 'slideRemove' && action.newSlide) {
212
683
  const slideId = action.newSlide.id;
213
684
  const index = this.slides.findIndex(({ id }) => id === slideId);
@@ -223,13 +694,14 @@ export class PresentationEditor {
223
694
  this.slides.splice(index, 1);
224
695
  this.onSlideRemove?.(action.slide.id);
225
696
  if (!this.slides.length && action.type === 'slideRemove') {
226
- let newSlide = this.buildEmptySlide();
697
+ let slide = this.buildEmptySlide();
227
698
  if (this.onSlideAdd) {
228
- newSlide = await this.onSlideAdd(action.slide);
699
+ slide = await this.onSlideAdd(slide);
700
+ action.slide.id = slide.id;
229
701
  }
230
- this.slides.push(newSlide);
231
- this.setActiveSlide(newSlide.id);
232
- action.newSlide = newSlide;
702
+ this.slides.push(slide);
703
+ this.setActiveSlide(slide.id);
704
+ action.newSlide = slide;
233
705
  }
234
706
  else {
235
707
  this.setActiveSlide(this.slides[index > 0 ? index - 1 : 0].id);
@@ -237,68 +709,83 @@ export class PresentationEditor {
237
709
  }
238
710
  }
239
711
  }
240
- else {
712
+ else if (action.type === 'layersGroup') {
713
+ const results = await Promise.all(action.actions.map((a) => this.applyChanges(a, type)));
714
+ return results.some((res) => res);
715
+ }
716
+ else if ('slideId' in action) {
241
717
  const slide = this.slides.find(({ id }) => id === action.slideId);
242
718
  if (!slide)
243
- return;
244
- if (this.mode === 'multiple' && action.type === 'slide') {
245
- Object.assign(slide, action[type]);
246
- if ('sortOrder' in action[type]) {
247
- this.sortSlides();
248
- }
249
- this.onSlideUpdate?.(action.slideId, action[type]);
719
+ return false;
720
+ if (action.type === 'slide') {
721
+ this.doSlideUpdate(slide, action[type]);
250
722
  }
251
- else if (action.type === 'layerAdd' || action.type === 'layerRemove') {
252
- if ((action.type === 'layerAdd' && type === 'redo') ||
253
- (action.type === 'layerRemove' && type === 'undo')) {
254
- let layer = { ...action.layer };
255
- if (this.onLayerAdd) {
256
- layer = await this.onLayerAdd(action.slideId, action.layer);
257
- }
258
- slide.layers.push(layer);
259
- this.sortLayers(slide.layers);
260
- this.setActiveLayer(layer);
723
+ else if (action.type === 'layersAdd' || action.type === 'layersRemove') {
724
+ if ((action.type === 'layersAdd' && type === 'redo') ||
725
+ (action.type === 'layersRemove' && type === 'undo')) {
726
+ const layers = await this.doLayersAdd(slide, action.layers);
727
+ action.layers.forEach((layer, i) => (layer.id = layers[i].id));
261
728
  }
262
729
  else {
263
- const index = slide.layers.findIndex(({ id }) => id === action.layer.id);
264
- if (~index) {
265
- slide.layers.splice(index, 1);
266
- this.onLayerRemove?.(action.slideId, action.layer.id, action.layer.type);
267
- }
730
+ this.doLayersRemove(slide, action.layers);
268
731
  }
269
732
  }
270
- else if (!action.type || action.type === 'layer') {
733
+ else if (action.type === 'layer') {
271
734
  const layer = slide.layers.find(({ id }) => id === action.layer.id);
272
735
  if (!layer)
273
- return;
736
+ return false;
274
737
  Object.assign(layer, action[type]);
275
738
  if ('sortOrder' in action[type]) {
276
739
  this.sortLayers(slide.layers);
277
740
  }
278
741
  this.onLayerUpdate?.(action.slideId, action.layer.id, action.layer.type, action[type]);
279
742
  }
743
+ else if (action.type === 'layerToSlideBackground') {
744
+ if ((type === 'redo' && !action.opposite) || (type === 'undo' && action.opposite)) {
745
+ const { width, height, rotate, image, opacity, flipX, flipY, offsetX, offsetY } = action.imageLayer;
746
+ const cover = calculateImageCover({ width, height, rotate }, { width: this.width, height: this.height });
747
+ const backgroundImage = {
748
+ image: { ...image },
749
+ scale: cover.scale,
750
+ offsetX: (offsetX || 0) + cover.offsetX,
751
+ offsetY: (offsetY || 0) + cover.offsetY,
752
+ rotate,
753
+ opacity,
754
+ flipX,
755
+ flipY,
756
+ };
757
+ this.doBackgroundImageAdd(slide, backgroundImage);
758
+ this.doLayersRemove(slide, [action.imageLayer]);
759
+ }
760
+ else {
761
+ this.doBackgroundImageRemove(slide);
762
+ const [layer] = await this.doLayersAdd(slide, [action.imageLayer]);
763
+ action.imageLayer.id = layer.id;
764
+ }
765
+ }
766
+ else if (action.type === 'backgroundImage') {
767
+ this.doBackgroundImageUpdate(slide, action[type]);
768
+ }
280
769
  }
770
+ return true;
281
771
  }
282
772
  historyReset() {
283
773
  this.historyActions = [];
284
774
  this.historyIndex = -1;
285
775
  }
286
- async historyPush(newAction, slideId) {
287
- const action = { ...newAction, slideId: slideId ?? this.activeSlide.id };
288
- this.historyActions = [...this.historyActions.slice(0, this.historyIndex + 1), action];
289
- this.historyIndex = this.historyActions.length - 1;
290
- await this.applyChanges(action, 'redo');
776
+ async historyPush(action) {
777
+ const done = await this.applyChanges(action, 'redo');
778
+ if (done) {
779
+ this.historyActions = [...this.historyActions.slice(0, this.historyIndex + 1), action];
780
+ this.historyIndex = this.historyActions.length - 1;
781
+ }
782
+ return done;
291
783
  }
292
784
  async historyUndo() {
293
785
  if (this.historyIndex >= 0) {
294
786
  const action = this.historyActions[this.historyIndex];
295
787
  this.historyIndex--;
296
788
  await this.applyChanges(action, 'undo');
297
- if (this.activeLayer) {
298
- if (action.type === 'layerAdd' && action.layer.id === this.activeLayer.id) {
299
- this.setActiveLayer(null);
300
- }
301
- }
302
789
  }
303
790
  }
304
791
  async historyRedo() {
@@ -306,41 +793,15 @@ export class PresentationEditor {
306
793
  this.historyIndex++;
307
794
  const action = this.historyActions[this.historyIndex];
308
795
  await this.applyChanges(action, 'redo');
309
- if (this.activeLayer) {
310
- if (action.type === 'layerRemove' && action.layer.id === this.activeLayer.id) {
311
- this.setActiveLayer(null);
312
- }
313
- }
314
796
  }
315
797
  }
316
- async removeBackground() {
317
- await this.historyPush({
318
- type: 'slide',
319
- undo: { backgroundColor: this.activeSlide.backgroundColor },
320
- redo: { backgroundColor: null },
321
- });
322
- }
323
- async removeLayer(layer) {
324
- await this.historyPush({
325
- type: 'layerRemove',
326
- layer: this.activeAction?.layer ?? layer,
327
- });
328
- this.setActiveLayer(null);
329
- return true;
330
- }
331
- checkIfLayerInBounds(layer, check = false) {
332
- const bBox = calculateBoundingBox(layer);
798
+ isLayerOutOfBounds(layer) {
799
+ const bbox = calculateBoundingBox(calculateLayerTransform(layer));
333
800
  const scale = layer.scale || 1;
334
- if (bBox.x > this.width ||
335
- bBox.y > this.height ||
336
- bBox.x + bBox.width * scale < 0 ||
337
- bBox.y * scale + bBox.height < 0) {
338
- if (!check) {
339
- this.removeLayer(layer);
340
- }
341
- return false;
342
- }
343
- return true;
801
+ return (bbox.x > this.width ||
802
+ bbox.y > this.height ||
803
+ bbox.x + bbox.width * scale < 0 ||
804
+ bbox.y * scale + bbox.height < 0);
344
805
  }
345
806
  }
346
807
  const presentationEditorKey = Symbol('presentationEditor');