@peteai/presentation-editor 0.0.2 → 0.0.4
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/README.md +2 -2
- package/dist/components/presentation-editor/active-layers-buttons.svelte +53 -0
- package/dist/components/presentation-editor/active-layers-buttons.svelte.d.ts +3 -0
- package/dist/components/presentation-editor/active-layers.svelte +181 -0
- package/dist/components/presentation-editor/active-layers.svelte.d.ts +3 -0
- package/dist/components/presentation-editor/color-indicator/color-indicator-gradient-def.svelte +81 -0
- package/dist/components/presentation-editor/color-indicator/color-indicator-gradient-def.svelte.d.ts +9 -0
- package/dist/components/presentation-editor/color-indicator/color-indicator-gradient.svelte +21 -0
- package/dist/components/presentation-editor/color-indicator/color-indicator-gradient.svelte.d.ts +8 -0
- package/dist/components/presentation-editor/color-indicator/color-indicator.svelte +23 -0
- package/dist/components/presentation-editor/color-indicator/color-indicator.svelte.d.ts +6 -0
- package/dist/components/presentation-editor/color-indicator/index.d.ts +4 -0
- package/dist/components/presentation-editor/color-indicator/index.js +6 -0
- package/dist/components/presentation-editor/cursor-tooltip.svelte +1 -1
- package/dist/components/presentation-editor/dragged.svelte +21 -11
- package/dist/components/presentation-editor/fonts.d.ts +3 -0
- package/dist/components/presentation-editor/fonts.js +1278 -0
- package/dist/components/presentation-editor/header.svelte +21 -33
- package/dist/components/presentation-editor/header.svelte.d.ts +16 -6
- package/dist/components/presentation-editor/hotkeys.svelte +85 -0
- package/dist/components/presentation-editor/{sidebar/layers.svelte.d.ts → hotkeys.svelte.d.ts} +3 -3
- package/dist/components/presentation-editor/layers/active-background-border.svelte +3 -7
- package/dist/components/presentation-editor/layers/active-layer-border.svelte +2 -5
- package/dist/components/presentation-editor/layers/active-layer-border.svelte.d.ts +0 -1
- package/dist/components/presentation-editor/layers/buttons/border-button/border-button.svelte +113 -129
- package/dist/components/presentation-editor/layers/buttons/border-button/border-button.svelte.d.ts +2 -2
- package/dist/components/presentation-editor/layers/buttons/corner-radius-button/corner-radius-button.svelte +51 -32
- package/dist/components/presentation-editor/layers/buttons/corner-radius-button/corner-radius-button.svelte.d.ts +2 -2
- package/dist/components/presentation-editor/layers/buttons/flip-button/flip-button.svelte +30 -7
- package/dist/components/presentation-editor/layers/buttons/flip-button/flip-button.svelte.d.ts +3 -3
- package/dist/components/presentation-editor/layers/buttons/opacity-button/opacity-button.svelte +76 -33
- package/dist/components/presentation-editor/layers/buttons/opacity-button/opacity-button.svelte.d.ts +3 -3
- package/dist/components/presentation-editor/layers/controls/corner-scale-control/corner-scale-control.svelte +89 -59
- package/dist/components/presentation-editor/layers/controls/corner-scale-control/corner-scale-control.svelte.d.ts +5 -100
- package/dist/components/presentation-editor/layers/controls/group-resize-control/group-resize-control.svelte +337 -0
- package/dist/components/presentation-editor/layers/controls/group-resize-control/group-resize-control.svelte.d.ts +104 -0
- package/dist/components/presentation-editor/layers/controls/group-resize-control/index.d.ts +2 -0
- package/dist/components/presentation-editor/layers/controls/group-resize-control/index.js +4 -0
- package/dist/components/presentation-editor/layers/controls/rotate-control/rotate-control.svelte +128 -43
- package/dist/components/presentation-editor/layers/controls/rotate-control/rotate-control.svelte.d.ts +1 -5
- package/dist/components/presentation-editor/layers/controls/side-resize-control/side-resize-control.svelte +68 -57
- package/dist/components/presentation-editor/layers/controls/side-resize-control/side-resize-control.svelte.d.ts +2 -110
- package/dist/components/presentation-editor/layers/controls/side-scale-control/side-scale-control.svelte +45 -32
- package/dist/components/presentation-editor/layers/controls/side-scale-control/side-scale-control.svelte.d.ts +2 -54
- package/dist/components/presentation-editor/layers/index.d.ts +4 -5
- package/dist/components/presentation-editor/layers/index.js +7 -8
- package/dist/components/presentation-editor/layers/layer-button.svelte +25 -7
- package/dist/components/presentation-editor/layers/layer-wrapper.svelte +212 -162
- package/dist/components/presentation-editor/layers/layer-wrapper.svelte.d.ts +2 -2
- package/dist/components/presentation-editor/layers/types/background/background-content-image.svelte +41 -0
- package/dist/components/presentation-editor/layers/types/background/background-content-image.svelte.d.ts +8 -0
- package/dist/components/presentation-editor/layers/types/background/background-layer-buttons.svelte +28 -74
- package/dist/components/presentation-editor/layers/types/background/background-layer-buttons.svelte.d.ts +2 -17
- package/dist/components/presentation-editor/layers/types/background/background-layer-content.svelte +19 -0
- package/dist/components/presentation-editor/layers/types/background/background-layer-content.svelte.d.ts +8 -0
- package/dist/components/presentation-editor/layers/types/background/background-layer.svelte +69 -61
- package/dist/components/presentation-editor/layers/types/background/background-layer.svelte.d.ts +2 -3
- package/dist/components/presentation-editor/layers/types/background/index.d.ts +2 -3
- package/dist/components/presentation-editor/layers/types/background/index.js +2 -3
- package/dist/components/presentation-editor/layers/types/html/buttons/alignment-button/alignment-button.svelte +55 -12
- package/dist/components/presentation-editor/layers/types/html/buttons/alignment-button/alignment-button.svelte.d.ts +3 -3
- package/dist/components/presentation-editor/layers/types/html/buttons/bold-button/bold-button.svelte +60 -8
- package/dist/components/presentation-editor/layers/types/html/buttons/bold-button/bold-button.svelte.d.ts +3 -3
- package/dist/components/presentation-editor/layers/types/html/buttons/case-button/case-button.svelte +59 -24
- package/dist/components/presentation-editor/layers/types/html/buttons/case-button/case-button.svelte.d.ts +3 -3
- package/dist/components/presentation-editor/layers/types/html/buttons/color-button/color-button.svelte +27 -76
- package/dist/components/presentation-editor/layers/types/html/buttons/color-button/color-button.svelte.d.ts +3 -3
- package/dist/components/presentation-editor/layers/types/html/buttons/font-family-button/font-family-button.svelte +36 -0
- package/dist/components/presentation-editor/layers/types/html/buttons/font-family-button/font-family-button.svelte.d.ts +7 -0
- package/dist/components/presentation-editor/layers/types/html/buttons/font-family-button/index.d.ts +2 -0
- package/dist/components/presentation-editor/layers/types/html/buttons/font-family-button/index.js +2 -0
- package/dist/components/presentation-editor/layers/types/html/buttons/font-size-button/font-size-button.svelte +72 -29
- package/dist/components/presentation-editor/layers/types/html/buttons/font-size-button/font-size-button.svelte.d.ts +3 -5
- package/dist/components/presentation-editor/layers/types/html/buttons/italic-button/italic-button.svelte +60 -8
- package/dist/components/presentation-editor/layers/types/html/buttons/italic-button/italic-button.svelte.d.ts +3 -3
- package/dist/components/presentation-editor/layers/types/html/buttons/list-button/list-button.svelte +71 -18
- package/dist/components/presentation-editor/layers/types/html/buttons/list-button/list-button.svelte.d.ts +3 -3
- package/dist/components/presentation-editor/layers/types/html/buttons/strikethrough-button/strikethrough-button.svelte +54 -8
- package/dist/components/presentation-editor/layers/types/html/buttons/strikethrough-button/strikethrough-button.svelte.d.ts +3 -3
- package/dist/components/presentation-editor/layers/types/html/buttons/underline-button/underline-button.svelte +54 -9
- package/dist/components/presentation-editor/layers/types/html/buttons/underline-button/underline-button.svelte.d.ts +3 -3
- package/dist/components/presentation-editor/layers/types/html/editor/createEditor.js +2 -2
- package/dist/components/presentation-editor/layers/types/html/editor/utils.d.ts +11 -0
- package/dist/components/presentation-editor/layers/types/html/editor/utils.js +88 -0
- package/dist/components/presentation-editor/layers/types/html/extensions/font-family/font-family.d.ts +27 -0
- package/dist/components/presentation-editor/layers/types/html/extensions/font-family/font-family.js +40 -0
- package/dist/components/presentation-editor/layers/types/html/extensions/font-family/index.d.ts +3 -0
- package/dist/components/presentation-editor/layers/types/html/extensions/font-family/index.js +3 -0
- package/dist/components/presentation-editor/layers/types/html/extensions/font-size/font-size.d.ts +5 -1
- package/dist/components/presentation-editor/layers/types/html/extensions/font-size/font-size.js +3 -7
- package/dist/components/presentation-editor/layers/types/html/extensions.d.ts +1 -0
- package/dist/components/presentation-editor/layers/types/html/extensions.js +56 -0
- package/dist/components/presentation-editor/layers/types/html/html-content.svelte +26 -5
- package/dist/components/presentation-editor/layers/types/html/html-layer-content.svelte +26 -0
- package/dist/components/presentation-editor/layers/types/html/html-layer-content.svelte.d.ts +9 -0
- package/dist/components/presentation-editor/layers/types/html/html-layer-edit.svelte +103 -0
- package/dist/components/presentation-editor/layers/types/html/html-layer-edit.svelte.d.ts +8 -0
- package/dist/components/presentation-editor/layers/types/html/html-layer.svelte +61 -53
- package/dist/components/presentation-editor/layers/types/html/index.d.ts +3 -5
- package/dist/components/presentation-editor/layers/types/html/index.js +3 -56
- package/dist/components/presentation-editor/layers/types/image/{image-content.svelte → image-layer-content.svelte} +11 -3
- package/dist/components/presentation-editor/layers/types/image/image-layer-content.svelte.d.ts +8 -0
- package/dist/components/presentation-editor/layers/types/image/image-layer.svelte +51 -21
- package/dist/components/presentation-editor/layers/types/image/index.d.ts +2 -4
- package/dist/components/presentation-editor/layers/types/image/index.js +2 -4
- package/dist/components/presentation-editor/layers/utils.d.ts +68 -9
- package/dist/components/presentation-editor/layers/utils.js +260 -25
- package/dist/components/presentation-editor/menu/background-menu-content.svelte +80 -0
- package/dist/components/presentation-editor/menu/background-menu-content.svelte.d.ts +9 -0
- package/dist/components/presentation-editor/menu/layer-menu-content.svelte +183 -0
- package/dist/components/presentation-editor/menu/layer-menu-content.svelte.d.ts +3 -0
- package/dist/components/presentation-editor/menu/slide-menu-content.svelte +67 -0
- package/dist/components/presentation-editor/menu/slide-menu-content.svelte.d.ts +9 -0
- package/dist/components/presentation-editor/presentation-editor.svelte +119 -175
- package/dist/components/presentation-editor/presentation-editor.svelte.js +597 -136
- package/dist/components/presentation-editor/sidebar/color-sidebar/color-sidebar-color.svelte +58 -0
- package/dist/components/presentation-editor/sidebar/color-sidebar/color-sidebar-color.svelte.d.ts +10 -0
- package/dist/components/presentation-editor/sidebar/color-sidebar/color-sidebar-gradient-picker.svelte +144 -0
- package/dist/components/presentation-editor/sidebar/color-sidebar/color-sidebar-gradient-picker.svelte.d.ts +7 -0
- package/dist/components/presentation-editor/sidebar/color-sidebar/color-sidebar.svelte +404 -0
- package/dist/components/presentation-editor/sidebar/color-sidebar/color-sidebar.svelte.d.ts +3 -0
- package/dist/components/presentation-editor/sidebar/color-sidebar/index.d.ts +2 -0
- package/dist/components/presentation-editor/sidebar/color-sidebar/index.js +2 -0
- package/dist/components/presentation-editor/sidebar/font-sidebar/font-sidebar-button.svelte +26 -0
- package/dist/components/presentation-editor/sidebar/font-sidebar/font-sidebar-button.svelte.d.ts +8 -0
- package/dist/components/presentation-editor/sidebar/font-sidebar/font-sidebar.svelte +216 -0
- package/dist/components/presentation-editor/sidebar/font-sidebar/font-sidebar.svelte.d.ts +3 -0
- package/dist/components/presentation-editor/sidebar/font-sidebar/index.d.ts +2 -0
- package/dist/components/presentation-editor/sidebar/font-sidebar/index.js +2 -0
- package/dist/components/presentation-editor/sidebar/position-slidebar.svelte +130 -0
- package/dist/components/presentation-editor/sidebar/position-slidebar.svelte.d.ts +18 -0
- package/dist/components/presentation-editor/sidebar/sidebar-text-tab-button.svelte +90 -0
- package/dist/components/presentation-editor/sidebar/sidebar-text-tab-button.svelte.d.ts +7 -0
- package/dist/components/presentation-editor/sidebar/sidebar-text-tab.svelte +82 -0
- package/dist/components/presentation-editor/sidebar/sidebar-text-tab.svelte.d.ts +18 -0
- package/dist/components/presentation-editor/sidebar/{images-library.svelte → sidebar-uploads-tab.svelte} +0 -1
- package/dist/components/presentation-editor/sidebar/sidebar-uploads-tab.svelte.d.ts +3 -0
- package/dist/components/presentation-editor/sidebar/sidebar-wrapper.svelte +25 -0
- package/dist/components/presentation-editor/sidebar/sidebar-wrapper.svelte.d.ts +7 -0
- package/dist/components/presentation-editor/sidebar/sidebar.svelte +71 -15
- package/dist/components/presentation-editor/sidebar/sidebar.svelte.d.ts +16 -5
- package/dist/components/presentation-editor/sidebar/uploads-image.svelte +28 -11
- package/dist/components/presentation-editor/slide-editor.svelte +20 -22
- package/dist/components/presentation-editor/slide-inner.svelte +19 -18
- package/dist/components/presentation-editor/slides-navigation/slide-preview.svelte +61 -52
- package/dist/components/presentation-editor/slides-navigation/slides-navigation.svelte +6 -8
- package/dist/components/presentation-editor/snapping-guides.svelte +3 -3
- package/dist/components/presentation-editor/types.d.ts +67 -27
- package/dist/components/presentation-editor/utils.d.ts +50 -1
- package/dist/components/presentation-editor/utils.js +101 -6
- package/dist/components/ui/button/button.svelte +3 -2
- package/dist/components/ui/button/button.svelte.d.ts +5 -82
- package/dist/components/ui/color-picker/color-picker-alpha-grid.svelte +43 -0
- package/dist/components/ui/color-picker/color-picker-alpha-grid.svelte.d.ts +8 -0
- package/dist/components/ui/color-picker/color-picker.svelte +344 -0
- package/dist/components/ui/color-picker/color-picker.svelte.d.ts +13 -0
- package/dist/components/ui/color-picker/index.d.ts +3 -0
- package/dist/components/ui/color-picker/index.js +5 -0
- package/dist/components/ui/context-menu/context-menu-shortcut.svelte +6 -3
- package/dist/components/ui/context-menu/context-menu-sub-trigger.svelte +1 -1
- package/dist/components/ui/dropdown-menu/dropdown-menu-item.svelte +1 -1
- package/dist/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte +6 -3
- package/dist/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte +1 -1
- package/dist/components/ui/infinite-loader/index.d.ts +4 -0
- package/dist/components/ui/infinite-loader/index.js +4 -0
- package/dist/components/ui/infinite-loader/infinite-loader-loop-tracker.d.ts +13 -0
- package/dist/components/ui/infinite-loader/infinite-loader-loop-tracker.js +44 -0
- package/dist/components/ui/infinite-loader/infinite-loader-state.svelte.d.ts +15 -0
- package/dist/components/ui/infinite-loader/infinite-loader-state.svelte.js +28 -0
- package/dist/components/ui/infinite-loader/infinite-loader.svelte +149 -0
- package/dist/components/ui/infinite-loader/infinite-loader.svelte.d.ts +17 -0
- package/dist/components/ui/input/input.svelte +1 -1
- package/dist/components/ui/slider/slider.svelte +20 -18
- package/dist/components/ui/tabs/index.d.ts +6 -0
- package/dist/components/ui/tabs/index.js +8 -0
- package/dist/components/ui/tabs/tabs-content.svelte +19 -0
- package/dist/components/ui/tabs/tabs-content.svelte.d.ts +4 -0
- package/dist/components/ui/tabs/tabs-list.svelte +19 -0
- package/dist/components/ui/tabs/tabs-list.svelte.d.ts +4 -0
- package/dist/components/ui/tabs/tabs-trigger.svelte +19 -0
- package/dist/components/ui/tabs/tabs-trigger.svelte.d.ts +4 -0
- package/dist/components/ui/toggle/toggle.svelte +3 -2
- package/dist/components/ui/toggle/toggle.svelte.d.ts +2 -46
- package/dist/plugin.js +3 -2
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +1 -0
- package/package.json +28 -25
- package/dist/components/presentation-editor/app.css +0 -12
- package/dist/components/presentation-editor/layers/hovered-layer.svelte +0 -34
- package/dist/components/presentation-editor/layers/hovered-layer.svelte.d.ts +0 -7
- package/dist/components/presentation-editor/layers/types/background/background-content.svelte +0 -11
- package/dist/components/presentation-editor/layers/types/background/background-content.svelte.d.ts +0 -7
- package/dist/components/presentation-editor/layers/types/background/background-layer-thumb.svelte +0 -12
- package/dist/components/presentation-editor/layers/types/background/background-layer-thumb.svelte.d.ts +0 -7
- package/dist/components/presentation-editor/layers/types/html/html-layer-active.svelte +0 -159
- package/dist/components/presentation-editor/layers/types/html/html-layer-active.svelte.d.ts +0 -8
- package/dist/components/presentation-editor/layers/types/html/html-layer-buttons.svelte +0 -42
- package/dist/components/presentation-editor/layers/types/html/html-layer-buttons.svelte.d.ts +0 -10
- package/dist/components/presentation-editor/layers/types/html/html-layer-thumb.svelte +0 -24
- package/dist/components/presentation-editor/layers/types/html/html-layer-thumb.svelte.d.ts +0 -8
- package/dist/components/presentation-editor/layers/types/image/image-content.svelte.d.ts +0 -8
- package/dist/components/presentation-editor/layers/types/image/image-layer-buttons.svelte +0 -21
- package/dist/components/presentation-editor/layers/types/image/image-layer-buttons.svelte.d.ts +0 -7
- package/dist/components/presentation-editor/layers/types/image/image-layer-thumb.svelte +0 -13
- package/dist/components/presentation-editor/layers/types/image/image-layer-thumb.svelte.d.ts +0 -8
- package/dist/components/presentation-editor/sidebar/images-library.svelte.d.ts +0 -3
- package/dist/components/presentation-editor/sidebar/layers.svelte +0 -94
- package/dist/components/presentation-editor/slides-navigation/buttons/slide-delete-button.svelte +0 -32
- package/dist/components/presentation-editor/slides-navigation/buttons/slide-delete-button.svelte.d.ts +0 -10
- package/dist/components/presentation-editor/slides-navigation/buttons/slide-duplicate-button.svelte +0 -34
- package/dist/components/presentation-editor/slides-navigation/buttons/slide-duplicate-button.svelte.d.ts +0 -10
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
import { tick } from 'svelte';
|
|
2
|
+
export const rotatePointOld = (point, angleRad) => {
|
|
2
3
|
const cos = Math.cos(angleRad);
|
|
3
4
|
const sin = Math.sin(angleRad);
|
|
4
5
|
return {
|
|
@@ -32,17 +33,22 @@ const getPoints = (origin, width, height) => {
|
|
|
32
33
|
throw new Error(`Unknown origin: ${origin}`);
|
|
33
34
|
}
|
|
34
35
|
};
|
|
35
|
-
export const
|
|
36
|
-
const { x, y, width, height } =
|
|
37
|
-
const
|
|
36
|
+
export const calculateLayerTransform = (layer) => {
|
|
37
|
+
const { x, y, width, height } = layer;
|
|
38
|
+
const scale = layer.scale || 1;
|
|
39
|
+
const rotate = layer.rotate || 0;
|
|
40
|
+
return { x, y, width: width * scale, height: height * scale, rotate };
|
|
41
|
+
};
|
|
42
|
+
export const calculateNewPosition = (origin, transform, newWidth, newHeight) => {
|
|
43
|
+
const { x, y, width, height, rotate } = transform;
|
|
38
44
|
const oldPoint = getPoints(origin, width, height);
|
|
39
45
|
const newPoint = getPoints(origin, newWidth, newHeight);
|
|
40
46
|
let deltaX = newPoint.x - oldPoint.x;
|
|
41
47
|
let deltaY = newPoint.y - oldPoint.y;
|
|
42
48
|
if (rotate !== 0) {
|
|
43
49
|
const rad = (rotate * Math.PI) / 180;
|
|
44
|
-
const rotatedOldCenter =
|
|
45
|
-
const rotatedNewCenter =
|
|
50
|
+
const rotatedOldCenter = rotatePointOld(oldPoint, rad);
|
|
51
|
+
const rotatedNewCenter = rotatePointOld(newPoint, rad);
|
|
46
52
|
deltaX = rotatedNewCenter.x - rotatedOldCenter.x;
|
|
47
53
|
deltaY = rotatedNewCenter.y - rotatedOldCenter.y;
|
|
48
54
|
}
|
|
@@ -51,15 +57,14 @@ export const calculateNewPosition = (origin, element, newWidth, newHeight) => {
|
|
|
51
57
|
newY: y - deltaY - (newHeight - height) / 2,
|
|
52
58
|
};
|
|
53
59
|
};
|
|
54
|
-
export function calculateBoundingBox(
|
|
55
|
-
const { x, y, width, height } =
|
|
56
|
-
const rotate = element.rotate || 0;
|
|
60
|
+
export function calculateBoundingBox(transform) {
|
|
61
|
+
const { x, y, width, height, rotate } = transform;
|
|
57
62
|
if (rotate === 0) {
|
|
58
63
|
return { x, y, width, height };
|
|
59
64
|
}
|
|
60
65
|
const radians = (rotate * Math.PI) / 180;
|
|
61
66
|
const origins = ['top-left', 'top-right'];
|
|
62
|
-
const corners = origins.map((position) =>
|
|
67
|
+
const corners = origins.map((position) => rotatePointOld(getPoints(position, width, height), radians));
|
|
63
68
|
const maxX = Math.max(...corners.map((corner) => Math.abs(corner.x)));
|
|
64
69
|
const maxY = Math.max(...corners.map((corner) => Math.abs(corner.y)));
|
|
65
70
|
return {
|
|
@@ -69,24 +74,254 @@ export function calculateBoundingBox(element) {
|
|
|
69
74
|
height: maxY * 2,
|
|
70
75
|
};
|
|
71
76
|
}
|
|
72
|
-
export function
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const
|
|
77
|
-
const
|
|
77
|
+
export function degToRad(deg) {
|
|
78
|
+
return (deg * Math.PI) / 180;
|
|
79
|
+
}
|
|
80
|
+
export function rotatePoint(p, origin, angleRad) {
|
|
81
|
+
const dx = p.x - origin.x;
|
|
82
|
+
const dy = p.y - origin.y;
|
|
83
|
+
const cos = Math.cos(angleRad);
|
|
84
|
+
const sin = Math.sin(angleRad);
|
|
85
|
+
return {
|
|
86
|
+
x: origin.x + dx * cos - dy * sin,
|
|
87
|
+
y: origin.y + dx * sin + dy * cos,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
export function getRotatedCorners(rect) {
|
|
91
|
+
const cx = rect.x + rect.width / 2;
|
|
92
|
+
const cy = rect.y + rect.height / 2;
|
|
93
|
+
const angle = degToRad(rect.rotate);
|
|
94
|
+
const hw = rect.width / 2;
|
|
95
|
+
const hh = rect.height / 2;
|
|
96
|
+
const corners = [
|
|
97
|
+
{ x: cx - hw, y: cy - hh },
|
|
98
|
+
{ x: cx + hw, y: cy - hh },
|
|
99
|
+
{ x: cx + hw, y: cy + hh },
|
|
100
|
+
{ x: cx - hw, y: cy + hh },
|
|
101
|
+
];
|
|
102
|
+
return corners.map((p) => rotatePoint(p, { x: cx, y: cy }, angle));
|
|
103
|
+
}
|
|
104
|
+
export function calculateBoundingBoxSize(width, height, rotate) {
|
|
105
|
+
const theta = degToRad(rotate);
|
|
106
|
+
const cosTheta = Math.abs(Math.cos(theta));
|
|
107
|
+
const sinTheta = Math.abs(Math.sin(theta));
|
|
108
|
+
return {
|
|
109
|
+
width: width * cosTheta + height * sinTheta,
|
|
110
|
+
height: width * sinTheta + height * cosTheta,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
export function isRotatedVertically(rotate) {
|
|
114
|
+
const rotationNormalized = ((rotate % 360) + 360) % 360;
|
|
115
|
+
const rotationShifted = (rotationNormalized + 45) % 360;
|
|
116
|
+
return Math.floor(rotationShifted / 90) % 2 === 1;
|
|
117
|
+
}
|
|
118
|
+
export function calculateNewSize(width, height, rotate, scale, isHorizontal) {
|
|
119
|
+
const angle = degToRad(rotate);
|
|
120
|
+
const cosTheta = Math.abs(Math.cos(angle));
|
|
121
|
+
const sinTheta = Math.abs(Math.sin(angle));
|
|
122
|
+
// Bounding box dimensions
|
|
123
|
+
const bboxWidth = width * cosTheta + height * sinTheta;
|
|
124
|
+
const bboxHeight = width * sinTheta + height * cosTheta;
|
|
125
|
+
// Determine if sides are flipped by rotation
|
|
126
|
+
const rotatedVertically = isRotatedVertically(rotate);
|
|
127
|
+
const bboxSize = (isHorizontal ? bboxWidth : bboxHeight) * scale;
|
|
128
|
+
const scalingRectangleSide = isHorizontal !== rotatedVertically ? 'width' : 'height';
|
|
129
|
+
const scalingWidth = scalingRectangleSide === 'width';
|
|
130
|
+
const dimensionFactor = !rotatedVertically ? cosTheta : sinTheta;
|
|
131
|
+
const crossFactor = rotatedVertically ? cosTheta : sinTheta;
|
|
132
|
+
const newMainSize = (bboxSize - (scalingWidth ? height : width) * crossFactor) / dimensionFactor;
|
|
133
|
+
const newWidth = scalingWidth ? newMainSize : width;
|
|
134
|
+
const newHeight = !scalingWidth ? newMainSize : height;
|
|
135
|
+
return { width: newWidth, height: newHeight, scaled: scalingRectangleSide };
|
|
136
|
+
}
|
|
137
|
+
export function getRotatedBoundingBox(center, width, height, rotate) {
|
|
138
|
+
const angle = degToRad(rotate);
|
|
139
|
+
// Half dimensions
|
|
140
|
+
const hw = width / 2;
|
|
141
|
+
const hh = height / 2;
|
|
142
|
+
// Rotation matrix components
|
|
143
|
+
const cos = Math.cos(angle);
|
|
144
|
+
const sin = Math.sin(angle);
|
|
145
|
+
// Corners of the unrotated rectangle relative to center
|
|
146
|
+
const corners = [
|
|
147
|
+
{ x: -hw, y: -hh },
|
|
148
|
+
{ x: hw, y: -hh },
|
|
149
|
+
{ x: hw, y: hh },
|
|
150
|
+
{ x: -hw, y: hh },
|
|
151
|
+
];
|
|
152
|
+
// Rotate corners and translate back to original position
|
|
153
|
+
const rotatedCorners = corners.map(({ x, y }) => {
|
|
154
|
+
return {
|
|
155
|
+
x: center.x + x * cos - y * sin,
|
|
156
|
+
y: center.y + x * sin + y * cos,
|
|
157
|
+
};
|
|
158
|
+
});
|
|
159
|
+
// Find min/max bounds
|
|
160
|
+
const xs = rotatedCorners.map((p) => p.x);
|
|
161
|
+
const ys = rotatedCorners.map((p) => p.y);
|
|
162
|
+
const minX = Math.min(...xs);
|
|
163
|
+
const maxX = Math.max(...xs);
|
|
164
|
+
const minY = Math.min(...ys);
|
|
165
|
+
const maxY = Math.max(...ys);
|
|
78
166
|
return {
|
|
167
|
+
x: minX,
|
|
168
|
+
y: minY,
|
|
169
|
+
width: maxX - minX,
|
|
170
|
+
height: maxY - minY,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
export function calculateGroupRotatedBoundingBox(rects, groupRotate = 0) {
|
|
174
|
+
if (!groupRotate)
|
|
175
|
+
return { ...calculateGroupBoundingBox(rects), rotate: 0 };
|
|
176
|
+
const allCorners = rects.flatMap(getRotatedCorners);
|
|
177
|
+
const theta = degToRad(-groupRotate); // inverse to align bbox with axes
|
|
178
|
+
// Rotate all points to align with desired bbox angle
|
|
179
|
+
const rotatedPoints = allCorners.map((p) => rotatePoint(p, { x: 0, y: 0 }, theta));
|
|
180
|
+
// Compute AABB of rotated points
|
|
181
|
+
const xs = rotatedPoints.map((p) => p.x);
|
|
182
|
+
const ys = rotatedPoints.map((p) => p.y);
|
|
183
|
+
const minX = Math.min(...xs);
|
|
184
|
+
const maxX = Math.max(...xs);
|
|
185
|
+
const minY = Math.min(...ys);
|
|
186
|
+
const maxY = Math.max(...ys);
|
|
187
|
+
const width = maxX - minX;
|
|
188
|
+
const height = maxY - minY;
|
|
189
|
+
const centerRotated = {
|
|
190
|
+
x: (minX + maxX) / 2,
|
|
191
|
+
y: (minY + maxY) / 2,
|
|
192
|
+
};
|
|
193
|
+
// Rotate center back to original space
|
|
194
|
+
const center = rotatePoint(centerRotated, { x: 0, y: 0 }, degToRad(groupRotate));
|
|
195
|
+
const bbox = {
|
|
196
|
+
x: center.x - width / 2,
|
|
197
|
+
y: center.y - height / 2,
|
|
79
198
|
width,
|
|
80
199
|
height,
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
200
|
+
rotate: groupRotate,
|
|
201
|
+
};
|
|
202
|
+
return bbox;
|
|
203
|
+
}
|
|
204
|
+
export function calculateRelativeRects(bbox, rects) {
|
|
205
|
+
const theta = degToRad(-bbox.rotate); // inverse to align bbox with axes
|
|
206
|
+
const bboxCenter = { x: bbox.x + bbox.width / 2, y: bbox.y + bbox.height / 2 };
|
|
207
|
+
return rects.map((rect) => {
|
|
208
|
+
// Get center of original rect
|
|
209
|
+
const center = {
|
|
210
|
+
x: rect.x + rect.width / 2,
|
|
211
|
+
y: rect.y + rect.height / 2,
|
|
212
|
+
};
|
|
213
|
+
// Rotate center ralatively to the bbox center
|
|
214
|
+
const relativeCenter = rotatePoint(center, bboxCenter, theta);
|
|
215
|
+
return {
|
|
216
|
+
x: relativeCenter.x - rect.width / 2 - bbox.x,
|
|
217
|
+
y: relativeCenter.y - rect.height / 2 - bbox.y,
|
|
218
|
+
width: rect.width,
|
|
219
|
+
height: rect.height,
|
|
220
|
+
rotate: rect.rotate - bbox.rotate,
|
|
221
|
+
};
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
export const checkPolygonsIntersect = (a, b) => {
|
|
225
|
+
const polygons = [a, b];
|
|
226
|
+
for (const polygon of polygons) {
|
|
227
|
+
for (let i1 = 0; i1 < polygon.length; i1++) {
|
|
228
|
+
const i2 = (i1 + 1) % polygon.length;
|
|
229
|
+
const p1 = polygon[i1];
|
|
230
|
+
const p2 = polygon[i2];
|
|
231
|
+
const normal = { x: p2.y - p1.y, y: p1.x - p2.x };
|
|
232
|
+
let minA = Infinity, maxA = -Infinity;
|
|
233
|
+
for (const point of a) {
|
|
234
|
+
const projected = normal.x * point.x + normal.y * point.y;
|
|
235
|
+
minA = Math.min(minA, projected);
|
|
236
|
+
maxA = Math.max(maxA, projected);
|
|
86
237
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
238
|
+
let minB = Infinity, maxB = -Infinity;
|
|
239
|
+
for (const point of b) {
|
|
240
|
+
const projected = normal.x * point.x + normal.y * point.y;
|
|
241
|
+
minB = Math.min(minB, projected);
|
|
242
|
+
maxB = Math.max(maxB, projected);
|
|
243
|
+
}
|
|
244
|
+
if (maxA < minB || maxB < minA)
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return true;
|
|
249
|
+
};
|
|
250
|
+
export function calculateGroupBoundingBox(transforms) {
|
|
251
|
+
let minX = Number.MAX_VALUE;
|
|
252
|
+
let maxX = Number.MIN_VALUE;
|
|
253
|
+
let minY = Number.MAX_VALUE;
|
|
254
|
+
let maxY = Number.MIN_VALUE;
|
|
255
|
+
transforms.forEach((transform) => {
|
|
256
|
+
const bbox = calculateBoundingBox(transform);
|
|
257
|
+
minX = Math.min(minX, bbox.x);
|
|
258
|
+
maxX = Math.max(maxX, bbox.x + bbox.width);
|
|
259
|
+
minY = Math.min(minY, bbox.y);
|
|
260
|
+
maxY = Math.max(maxY, bbox.y + bbox.height);
|
|
261
|
+
});
|
|
262
|
+
return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
|
|
263
|
+
}
|
|
264
|
+
const minScaleToContain = (rotatedRect, rect) => {
|
|
265
|
+
const { width: rw, height: rh, rotate } = rotatedRect;
|
|
266
|
+
const { width: w, height: h } = rect;
|
|
267
|
+
// Convert angle to radians
|
|
268
|
+
const angleRad = (rotate || 0) * (Math.PI / 180);
|
|
269
|
+
// Calculate the width and height of the bounding box of the rotated rectangle
|
|
270
|
+
const boundingWidth = Math.abs(w * Math.cos(angleRad)) + Math.abs(h * Math.sin(angleRad));
|
|
271
|
+
const boundingHeight = Math.abs(w * Math.sin(angleRad)) + Math.abs(h * Math.cos(angleRad));
|
|
272
|
+
// Calculate the scale factors needed to fit the rectangle into the bounding box
|
|
273
|
+
const scaleX = boundingWidth / rw;
|
|
274
|
+
const scaleY = boundingHeight / rh;
|
|
275
|
+
// The minimum scale factor is the maximum of the two scale factors
|
|
276
|
+
const minScale = Math.max(scaleX, scaleY);
|
|
277
|
+
return minScale;
|
|
278
|
+
};
|
|
279
|
+
export function calculateImageCover(image, bbox) {
|
|
280
|
+
const scale = minScaleToContain(image, bbox);
|
|
281
|
+
return {
|
|
282
|
+
width: bbox.width / scale,
|
|
283
|
+
height: bbox.height / scale,
|
|
284
|
+
scale,
|
|
285
|
+
offsetX: (image.width - bbox.width / scale) / 2,
|
|
286
|
+
offsetY: (image.height - bbox.height / scale) / 2,
|
|
91
287
|
};
|
|
92
288
|
}
|
|
289
|
+
export const defaultColor = '#000000';
|
|
290
|
+
export function extractBorderColors(layers) {
|
|
291
|
+
const layersWithBorder = layers.filter((layer) => layer.borderWidth);
|
|
292
|
+
if (!layersWithBorder.length)
|
|
293
|
+
return;
|
|
294
|
+
const set = new Set();
|
|
295
|
+
layersWithBorder.forEach((layer) => {
|
|
296
|
+
set.add(layer.borderColor || defaultColor);
|
|
297
|
+
});
|
|
298
|
+
return Array.from(set);
|
|
299
|
+
}
|
|
300
|
+
export function buildBackgroundCircleStyle(colors) {
|
|
301
|
+
if (colors.length < 2) {
|
|
302
|
+
return `background-color: ${colors[0] || defaultColor}`;
|
|
303
|
+
}
|
|
304
|
+
const step = 360 / colors.length;
|
|
305
|
+
const gradientColors = colors
|
|
306
|
+
.map((c, i) => `${c} ${Math.round(i * step)}deg ${Math.round((i + 1) * step)}deg`)
|
|
307
|
+
.join(', ');
|
|
308
|
+
return `background-image: conic-gradient(${gradientColors})`;
|
|
309
|
+
}
|
|
310
|
+
export async function applyHtmlToLayer(layer, html) {
|
|
311
|
+
layer.html = html;
|
|
312
|
+
await tick();
|
|
313
|
+
const { x, y, width, height } = layer;
|
|
314
|
+
const redo = { html, x, y, height };
|
|
315
|
+
const layerContent = document.getElementById(`layer-content-${layer.id}`);
|
|
316
|
+
if (layerContent && layerContent.offsetHeight !== layer.height) {
|
|
317
|
+
const scale = layer.scale || 1;
|
|
318
|
+
const rotate = layer.rotate || 0;
|
|
319
|
+
const newHeight = layerContent.offsetHeight * scale;
|
|
320
|
+
const { newX, newY } = calculateNewPosition(`top-left`, { x, y, width: width * scale, height: height * scale, rotate }, width * scale, newHeight);
|
|
321
|
+
redo.x = newX;
|
|
322
|
+
redo.y = newY;
|
|
323
|
+
redo.height = newHeight / scale;
|
|
324
|
+
}
|
|
325
|
+
Object.assign(layer, redo);
|
|
326
|
+
return redo;
|
|
327
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import CopyIcon from 'lucide-svelte/icons/copy';
|
|
3
|
+
import PasteIcon from 'lucide-svelte/icons/clipboard';
|
|
4
|
+
import DuplicateIcon from 'lucide-svelte/icons/copy-plus';
|
|
5
|
+
import AddIcon from 'lucide-svelte/icons/file-plus-2';
|
|
6
|
+
import TrashIcon from 'lucide-svelte/icons/trash-2';
|
|
7
|
+
import LockIcon from 'lucide-svelte/icons/lock-keyhole';
|
|
8
|
+
import UnlockIcon from 'lucide-svelte/icons/lock-keyhole-open';
|
|
9
|
+
import ExtractImageIcon from 'lucide-svelte/icons/square-arrow-out-up-right';
|
|
10
|
+
import * as DropdownMenu from '../../ui/dropdown-menu/index.js';
|
|
11
|
+
import type { PresentationEditor } from '../presentation-editor.svelte.js';
|
|
12
|
+
import type { Slide } from '../types.js';
|
|
13
|
+
import { checkIfMac } from '../../../utils.js';
|
|
14
|
+
|
|
15
|
+
interface Props {
|
|
16
|
+
editor: PresentationEditor;
|
|
17
|
+
slide: Slide;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let { editor, slide }: Props = $props();
|
|
21
|
+
|
|
22
|
+
const locked = $derived(slide.locked || slide.backgroundLocked);
|
|
23
|
+
|
|
24
|
+
const isMac = checkIfMac();
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<DropdownMenu.Item onclick={() => editor.copySlide(slide.id)}>
|
|
28
|
+
<CopyIcon />
|
|
29
|
+
Copy
|
|
30
|
+
<DropdownMenu.Shortcut>{isMac ? '⌘C' : 'Ctrl+C'}</DropdownMenu.Shortcut>
|
|
31
|
+
</DropdownMenu.Item>
|
|
32
|
+
<DropdownMenu.Item
|
|
33
|
+
onclick={() => editor.clipboardPaste()}
|
|
34
|
+
disabled={!editor.clipboard || (slide.locked && !('layers' in editor.clipboard))}
|
|
35
|
+
>
|
|
36
|
+
<PasteIcon />
|
|
37
|
+
Paste
|
|
38
|
+
<DropdownMenu.Shortcut>{isMac ? '⌘V' : 'Ctrl+V'}</DropdownMenu.Shortcut>
|
|
39
|
+
</DropdownMenu.Item>
|
|
40
|
+
<DropdownMenu.Item onclick={() => editor.addSlide()}>
|
|
41
|
+
<AddIcon />
|
|
42
|
+
Add page
|
|
43
|
+
<DropdownMenu.Shortcut>{isMac ? '⌘⏎' : 'Ctrl+⏎'}</DropdownMenu.Shortcut>
|
|
44
|
+
</DropdownMenu.Item>
|
|
45
|
+
<DropdownMenu.Item onclick={() => editor.duplicateSlide(slide)}>
|
|
46
|
+
<DuplicateIcon />
|
|
47
|
+
Duplicate page
|
|
48
|
+
<DropdownMenu.Shortcut>{isMac ? '⌘D' : 'Ctrl+D'}</DropdownMenu.Shortcut>
|
|
49
|
+
</DropdownMenu.Item>
|
|
50
|
+
<DropdownMenu.Item onclick={() => editor.removeBackground(slide.id)} disabled={locked}>
|
|
51
|
+
<TrashIcon />
|
|
52
|
+
{#if slide.backgroundImage}
|
|
53
|
+
Delete background image
|
|
54
|
+
{:else}
|
|
55
|
+
Delete background
|
|
56
|
+
{/if}
|
|
57
|
+
<DropdownMenu.Shortcut>DELETE</DropdownMenu.Shortcut>
|
|
58
|
+
</DropdownMenu.Item>
|
|
59
|
+
|
|
60
|
+
<DropdownMenu.Separator />
|
|
61
|
+
|
|
62
|
+
<DropdownMenu.Item onclick={() => editor.backgroundToggleLocked(slide.id)} disabled={slide.locked}>
|
|
63
|
+
{#if slide.backgroundLocked}
|
|
64
|
+
<UnlockIcon />
|
|
65
|
+
Unlock background
|
|
66
|
+
{:else}
|
|
67
|
+
<LockIcon />
|
|
68
|
+
Lock background
|
|
69
|
+
{/if}
|
|
70
|
+
<DropdownMenu.Shortcut>{isMac ? '⌥⇧L' : 'Alt+Shift+L'}</DropdownMenu.Shortcut>
|
|
71
|
+
</DropdownMenu.Item>
|
|
72
|
+
|
|
73
|
+
{#if slide.backgroundImage}
|
|
74
|
+
<DropdownMenu.Separator />
|
|
75
|
+
|
|
76
|
+
<DropdownMenu.Item onclick={() => editor.backgroundImageToLayer(slide.id)} disabled={locked}>
|
|
77
|
+
<ExtractImageIcon />
|
|
78
|
+
Detach image from background
|
|
79
|
+
</DropdownMenu.Item>
|
|
80
|
+
{/if}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { PresentationEditor } from '../presentation-editor.svelte.js';
|
|
2
|
+
import type { Slide } from '../types.js';
|
|
3
|
+
interface Props {
|
|
4
|
+
editor: PresentationEditor;
|
|
5
|
+
slide: Slide;
|
|
6
|
+
}
|
|
7
|
+
declare const BackgroundMenuContent: import("svelte").Component<Props, {}, "">;
|
|
8
|
+
type BackgroundMenuContent = ReturnType<typeof BackgroundMenuContent>;
|
|
9
|
+
export default BackgroundMenuContent;
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import CopyIcon from 'lucide-svelte/icons/copy';
|
|
3
|
+
import PasteIcon from 'lucide-svelte/icons/clipboard';
|
|
4
|
+
import DuplicateIcon from 'lucide-svelte/icons/copy-plus';
|
|
5
|
+
import TrashIcon from 'lucide-svelte/icons/trash-2';
|
|
6
|
+
import LayerIcon from 'lucide-svelte/icons/layers';
|
|
7
|
+
import LayerBringForwardIcon from 'lucide-svelte/icons/arrow-up';
|
|
8
|
+
import LayerBringToFrontIcon from 'lucide-svelte/icons/arrow-up-to-line';
|
|
9
|
+
import LayerSendBackwardIcon from 'lucide-svelte/icons/arrow-down';
|
|
10
|
+
import LayerSendToBackIcon from 'lucide-svelte/icons/arrow-down-to-line';
|
|
11
|
+
import AlignLeftIcon from 'lucide-svelte/icons/align-start-vertical';
|
|
12
|
+
import AlignCenterIcon from 'lucide-svelte/icons/align-center-vertical';
|
|
13
|
+
import AlignRightIcon from 'lucide-svelte/icons/align-end-vertical';
|
|
14
|
+
import AlignTopIcon from 'lucide-svelte/icons/align-start-horizontal';
|
|
15
|
+
import AlignMiddleIcon from 'lucide-svelte/icons/align-center-horizontal';
|
|
16
|
+
import AlignBottomIcon from 'lucide-svelte/icons/align-end-horizontal';
|
|
17
|
+
import LockIcon from 'lucide-svelte/icons/lock-keyhole';
|
|
18
|
+
import UnlockIcon from 'lucide-svelte/icons/lock-keyhole-open';
|
|
19
|
+
import SetAsBackgroundIcon from 'lucide-svelte/icons/image-upscale';
|
|
20
|
+
import * as DropdownMenu from '../../ui/dropdown-menu/index.js';
|
|
21
|
+
import { getPresentationEditorContext } from '../presentation-editor.svelte.js';
|
|
22
|
+
import { checkIfMac } from '../../../utils.js';
|
|
23
|
+
|
|
24
|
+
const editor = getPresentationEditorContext();
|
|
25
|
+
|
|
26
|
+
const someLayersLocked = $derived(editor.activeLayers.some((layer) => layer.locked));
|
|
27
|
+
const locked = $derived(editor.activeSlide.locked || someLayersLocked);
|
|
28
|
+
|
|
29
|
+
const isMac = checkIfMac();
|
|
30
|
+
|
|
31
|
+
const activeLayerIds = $derived(editor.activeLayers.map((l) => l.id));
|
|
32
|
+
|
|
33
|
+
const singleImageLayer = $derived(
|
|
34
|
+
editor.activeLayers.length === 1 && editor.activeLayers[0].type === 'image'
|
|
35
|
+
? editor.activeLayers[0]
|
|
36
|
+
: null,
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
let overlaps = editor.getLayerOverlaps(editor.activeSlide, editor.activeLayers);
|
|
40
|
+
</script>
|
|
41
|
+
|
|
42
|
+
<DropdownMenu.Item onclick={() => editor.copyLayers(editor.activeSlide.id, activeLayerIds)}>
|
|
43
|
+
<CopyIcon />
|
|
44
|
+
Copy
|
|
45
|
+
<DropdownMenu.Shortcut>{isMac ? '⌘C' : 'Ctrl+C'}</DropdownMenu.Shortcut>
|
|
46
|
+
</DropdownMenu.Item>
|
|
47
|
+
<DropdownMenu.Item onclick={() => editor.clipboardPaste()} disabled={!editor.clipboard}>
|
|
48
|
+
<PasteIcon />
|
|
49
|
+
Paste
|
|
50
|
+
<DropdownMenu.Shortcut>{isMac ? '⌘V' : 'Ctrl+V'}</DropdownMenu.Shortcut>
|
|
51
|
+
</DropdownMenu.Item>
|
|
52
|
+
<DropdownMenu.Item onclick={() => editor.duplicateLayers(editor.activeLayers)}>
|
|
53
|
+
<DuplicateIcon />
|
|
54
|
+
Duplicate
|
|
55
|
+
<DropdownMenu.Shortcut>{isMac ? '⌘D' : 'Ctrl+D'}</DropdownMenu.Shortcut>
|
|
56
|
+
</DropdownMenu.Item>
|
|
57
|
+
<DropdownMenu.Item
|
|
58
|
+
onclick={() => editor.removeLayers(editor.activeSlide.id, activeLayerIds)}
|
|
59
|
+
disabled={locked}
|
|
60
|
+
>
|
|
61
|
+
<TrashIcon />
|
|
62
|
+
Delete
|
|
63
|
+
<DropdownMenu.Shortcut>DELETE</DropdownMenu.Shortcut>
|
|
64
|
+
</DropdownMenu.Item>
|
|
65
|
+
|
|
66
|
+
<DropdownMenu.Separator />
|
|
67
|
+
|
|
68
|
+
{#if overlaps}
|
|
69
|
+
<DropdownMenu.Sub>
|
|
70
|
+
<DropdownMenu.SubTrigger disabled={locked}>
|
|
71
|
+
<LayerIcon />
|
|
72
|
+
Layers
|
|
73
|
+
</DropdownMenu.SubTrigger>
|
|
74
|
+
<DropdownMenu.SubContent class="w-56" align="start">
|
|
75
|
+
<DropdownMenu.Item
|
|
76
|
+
disabled={overlaps.forward === undefined}
|
|
77
|
+
onclick={() => editor.moveLayers(editor.activeSlide.id, activeLayerIds, 'forward')}
|
|
78
|
+
>
|
|
79
|
+
<LayerBringForwardIcon />
|
|
80
|
+
Bring forward
|
|
81
|
+
<DropdownMenu.Shortcut>{isMac ? '⌘]' : 'Ctrl+]'}</DropdownMenu.Shortcut>
|
|
82
|
+
</DropdownMenu.Item>
|
|
83
|
+
<DropdownMenu.Item
|
|
84
|
+
disabled={overlaps.front === undefined}
|
|
85
|
+
onclick={() => editor.moveLayers(editor.activeSlide.id, activeLayerIds, 'front')}
|
|
86
|
+
>
|
|
87
|
+
<LayerBringToFrontIcon />
|
|
88
|
+
Bring to front
|
|
89
|
+
<DropdownMenu.Shortcut>{isMac ? '⌥⌘]' : 'Ctrl+]'}</DropdownMenu.Shortcut>
|
|
90
|
+
</DropdownMenu.Item>
|
|
91
|
+
<DropdownMenu.Item
|
|
92
|
+
disabled={overlaps.backward === undefined}
|
|
93
|
+
onclick={() => editor.moveLayers(editor.activeSlide.id, activeLayerIds, 'backward')}
|
|
94
|
+
>
|
|
95
|
+
<LayerSendBackwardIcon />
|
|
96
|
+
Send backward
|
|
97
|
+
<DropdownMenu.Shortcut>{isMac ? '⌘[' : 'Ctrl+['}</DropdownMenu.Shortcut>
|
|
98
|
+
</DropdownMenu.Item>
|
|
99
|
+
<DropdownMenu.Item
|
|
100
|
+
disabled={overlaps.back === undefined}
|
|
101
|
+
onclick={() => editor.moveLayers(editor.activeSlide.id, activeLayerIds, 'back')}
|
|
102
|
+
>
|
|
103
|
+
<LayerSendToBackIcon />
|
|
104
|
+
Send to back
|
|
105
|
+
<DropdownMenu.Shortcut>{isMac ? '⌥⌘[' : 'Ctrl+['}</DropdownMenu.Shortcut>
|
|
106
|
+
</DropdownMenu.Item>
|
|
107
|
+
</DropdownMenu.SubContent>
|
|
108
|
+
</DropdownMenu.Sub>
|
|
109
|
+
{/if}
|
|
110
|
+
|
|
111
|
+
<DropdownMenu.Sub>
|
|
112
|
+
<DropdownMenu.SubTrigger disabled={locked}>
|
|
113
|
+
<AlignLeftIcon />
|
|
114
|
+
Align to page
|
|
115
|
+
</DropdownMenu.SubTrigger>
|
|
116
|
+
<DropdownMenu.SubContent class="w-32" align="start">
|
|
117
|
+
<DropdownMenu.Item
|
|
118
|
+
onclick={() => editor.alignLayers(editor.activeSlide.id, activeLayerIds, 'left')}
|
|
119
|
+
>
|
|
120
|
+
<AlignLeftIcon />
|
|
121
|
+
Left
|
|
122
|
+
</DropdownMenu.Item>
|
|
123
|
+
<DropdownMenu.Item
|
|
124
|
+
onclick={() => editor.alignLayers(editor.activeSlide.id, activeLayerIds, 'center')}
|
|
125
|
+
>
|
|
126
|
+
<AlignCenterIcon />
|
|
127
|
+
Center
|
|
128
|
+
</DropdownMenu.Item>
|
|
129
|
+
<DropdownMenu.Item
|
|
130
|
+
onclick={() => editor.alignLayers(editor.activeSlide.id, activeLayerIds, 'right')}
|
|
131
|
+
>
|
|
132
|
+
<AlignRightIcon />
|
|
133
|
+
Right
|
|
134
|
+
</DropdownMenu.Item>
|
|
135
|
+
<DropdownMenu.Separator />
|
|
136
|
+
<DropdownMenu.Item
|
|
137
|
+
onclick={() => editor.alignLayers(editor.activeSlide.id, activeLayerIds, 'top')}
|
|
138
|
+
>
|
|
139
|
+
<AlignTopIcon />
|
|
140
|
+
Top
|
|
141
|
+
</DropdownMenu.Item>
|
|
142
|
+
<DropdownMenu.Item
|
|
143
|
+
onclick={() => editor.alignLayers(editor.activeSlide.id, activeLayerIds, 'middle')}
|
|
144
|
+
>
|
|
145
|
+
<AlignMiddleIcon />
|
|
146
|
+
Middle
|
|
147
|
+
</DropdownMenu.Item>
|
|
148
|
+
<DropdownMenu.Item
|
|
149
|
+
onclick={() => editor.alignLayers(editor.activeSlide.id, activeLayerIds, 'bottom')}
|
|
150
|
+
>
|
|
151
|
+
<AlignBottomIcon />
|
|
152
|
+
Bottom
|
|
153
|
+
</DropdownMenu.Item>
|
|
154
|
+
</DropdownMenu.SubContent>
|
|
155
|
+
</DropdownMenu.Sub>
|
|
156
|
+
|
|
157
|
+
<DropdownMenu.Separator />
|
|
158
|
+
|
|
159
|
+
<DropdownMenu.Item
|
|
160
|
+
onclick={() =>
|
|
161
|
+
editor.toggleLayersLocked(editor.activeSlide.id, activeLayerIds, !someLayersLocked)}
|
|
162
|
+
disabled={editor.activeSlide.locked}
|
|
163
|
+
>
|
|
164
|
+
{#if someLayersLocked}
|
|
165
|
+
<UnlockIcon />
|
|
166
|
+
Unlock
|
|
167
|
+
{:else}
|
|
168
|
+
<LockIcon />
|
|
169
|
+
Lock
|
|
170
|
+
{/if}
|
|
171
|
+
<DropdownMenu.Shortcut>{isMac ? '⌥⇧L' : 'Alt+Shift+L'}</DropdownMenu.Shortcut>
|
|
172
|
+
</DropdownMenu.Item>
|
|
173
|
+
|
|
174
|
+
{#if singleImageLayer}
|
|
175
|
+
<DropdownMenu.Separator />
|
|
176
|
+
<DropdownMenu.Item
|
|
177
|
+
onclick={() => editor.imageLayerToBackground(editor.activeSlide.id, singleImageLayer.id)}
|
|
178
|
+
disabled={locked}
|
|
179
|
+
>
|
|
180
|
+
<SetAsBackgroundIcon />
|
|
181
|
+
Set image as background
|
|
182
|
+
</DropdownMenu.Item>
|
|
183
|
+
{/if}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import CopyIcon from 'lucide-svelte/icons/copy';
|
|
3
|
+
import PasteIcon from 'lucide-svelte/icons/clipboard';
|
|
4
|
+
import DuplicateIcon from 'lucide-svelte/icons/copy-plus';
|
|
5
|
+
import AddIcon from 'lucide-svelte/icons/file-plus-2';
|
|
6
|
+
import TrashIcon from 'lucide-svelte/icons/trash-2';
|
|
7
|
+
import LockIcon from 'lucide-svelte/icons/lock-keyhole';
|
|
8
|
+
import UnlockIcon from 'lucide-svelte/icons/lock-keyhole-open';
|
|
9
|
+
import * as DropdownMenu from '../../ui/dropdown-menu/index.js';
|
|
10
|
+
import type { PresentationEditor } from '../presentation-editor.svelte.js';
|
|
11
|
+
import type { Slide } from '../types.js';
|
|
12
|
+
import { checkIfMac } from '../../../utils.js';
|
|
13
|
+
|
|
14
|
+
interface Props {
|
|
15
|
+
editor: PresentationEditor;
|
|
16
|
+
slide: Slide;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let { editor, slide }: Props = $props();
|
|
20
|
+
|
|
21
|
+
const locked = $derived(slide.locked);
|
|
22
|
+
|
|
23
|
+
const isMac = checkIfMac();
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<DropdownMenu.Item onclick={() => editor.copySlide(slide.id)}>
|
|
27
|
+
<CopyIcon />
|
|
28
|
+
Copy
|
|
29
|
+
<DropdownMenu.Shortcut>{isMac ? '⌘C' : 'Ctrl+C'}</DropdownMenu.Shortcut>
|
|
30
|
+
</DropdownMenu.Item>
|
|
31
|
+
|
|
32
|
+
<DropdownMenu.Item onclick={() => editor.clipboardPaste()} disabled={!editor.clipboard}>
|
|
33
|
+
<PasteIcon />
|
|
34
|
+
Paste
|
|
35
|
+
<DropdownMenu.Shortcut>{isMac ? '⌘V' : 'Ctrl+V'}</DropdownMenu.Shortcut>
|
|
36
|
+
</DropdownMenu.Item>
|
|
37
|
+
|
|
38
|
+
<DropdownMenu.Item onclick={() => editor.addSlide()}>
|
|
39
|
+
<AddIcon />
|
|
40
|
+
Add page
|
|
41
|
+
<DropdownMenu.Shortcut>{isMac ? '⌘⏎' : 'Ctrl+⏎'}</DropdownMenu.Shortcut>
|
|
42
|
+
</DropdownMenu.Item>
|
|
43
|
+
|
|
44
|
+
<DropdownMenu.Item onclick={() => editor.duplicateSlide(slide)}>
|
|
45
|
+
<DuplicateIcon />
|
|
46
|
+
Duplicate page
|
|
47
|
+
<DropdownMenu.Shortcut>{isMac ? '⌘D' : 'Ctrl+D'}</DropdownMenu.Shortcut>
|
|
48
|
+
</DropdownMenu.Item>
|
|
49
|
+
|
|
50
|
+
<DropdownMenu.Item onclick={() => editor.removeSlide(slide.id)} disabled={locked}>
|
|
51
|
+
<TrashIcon />
|
|
52
|
+
Delete page
|
|
53
|
+
<DropdownMenu.Shortcut>DELETE</DropdownMenu.Shortcut>
|
|
54
|
+
</DropdownMenu.Item>
|
|
55
|
+
|
|
56
|
+
<DropdownMenu.Separator />
|
|
57
|
+
|
|
58
|
+
<DropdownMenu.Item onclick={() => editor.toggleSlideLocked(slide.id)}>
|
|
59
|
+
{#if slide.locked}
|
|
60
|
+
<UnlockIcon />
|
|
61
|
+
Unlock page
|
|
62
|
+
{:else}
|
|
63
|
+
<LockIcon />
|
|
64
|
+
Lock page
|
|
65
|
+
{/if}
|
|
66
|
+
<DropdownMenu.Shortcut>{isMac ? '⌥⇧L' : 'Alt+Shift+L'}</DropdownMenu.Shortcut>
|
|
67
|
+
</DropdownMenu.Item>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { PresentationEditor } from '../presentation-editor.svelte.js';
|
|
2
|
+
import type { Slide } from '../types.js';
|
|
3
|
+
interface Props {
|
|
4
|
+
editor: PresentationEditor;
|
|
5
|
+
slide: Slide;
|
|
6
|
+
}
|
|
7
|
+
declare const SlideMenuContent: import("svelte").Component<Props, {}, "">;
|
|
8
|
+
type SlideMenuContent = ReturnType<typeof SlideMenuContent>;
|
|
9
|
+
export default SlideMenuContent;
|