@peteai/presentation-editor 0.0.7 → 0.0.8
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/dist/components/editor/active-layers.svelte +22 -7
- package/dist/components/editor/active-layers.svelte.d.ts +6 -1
- package/dist/components/editor/editor.svelte +15 -17
- package/dist/components/editor/editor.svelte.js +10 -8
- package/dist/components/editor/header.svelte +24 -20
- package/dist/components/editor/hotkeys.svelte +7 -0
- package/dist/components/editor/layers/buttons/opacity-button/opacity-button.svelte +1 -6
- package/dist/components/editor/layers/controls/group-resize-control/group-resize-control.svelte +11 -11
- package/dist/components/editor/layers/controls/rotate-control/rotate-control.svelte +11 -5
- package/dist/components/editor/layers/controls/rotate-control/rotate-control.svelte.d.ts +4 -1
- package/dist/components/editor/layers/controls/side-resize-control/side-resize-control.svelte +13 -13
- package/dist/components/editor/layers/index.d.ts +2 -2
- package/dist/components/editor/layers/index.js +2 -2
- package/dist/components/editor/layers/types/background/background-content-image.svelte +15 -20
- package/dist/components/editor/layers/types/background/background-layer.svelte +2 -2
- package/dist/components/editor/layers/types/image/controls/image-rotate-control/image-rotate-control.svelte +120 -0
- package/dist/components/editor/layers/types/image/controls/image-rotate-control/image-rotate-control.svelte.d.ts +8 -0
- package/dist/components/editor/layers/types/image/controls/image-rotate-control/index.d.ts +2 -0
- package/dist/components/editor/layers/types/image/controls/image-rotate-control/index.js +4 -0
- package/dist/components/editor/layers/types/image/controls/image-scale-control/image-scale-control.svelte +154 -0
- package/dist/components/editor/layers/types/image/controls/image-scale-control/image-scale-control.svelte.d.ts +91 -0
- package/dist/components/editor/layers/types/image/controls/image-scale-control/index.d.ts +2 -0
- package/dist/components/editor/layers/types/image/controls/image-scale-control/index.js +4 -0
- package/dist/components/editor/layers/types/image/image-layer-content.svelte +3 -3
- package/dist/components/editor/layers/types/image/image-layer-crop.svelte +182 -0
- package/dist/components/editor/layers/types/image/image-layer-crop.svelte.d.ts +10 -0
- package/dist/components/editor/layers/types/image/image-layer.svelte +16 -0
- package/dist/components/editor/layers/types/image/index.d.ts +2 -1
- package/dist/components/editor/layers/types/image/index.js +2 -1
- package/dist/components/editor/layers/types/text/extensions/list-item/list-item.js +0 -2
- package/dist/components/editor/layers/utils.d.ts +24 -9
- package/dist/components/editor/layers/utils.js +107 -54
- package/dist/components/editor/page-editor.svelte +6 -2
- package/dist/components/editor/sidebar/image-crop-sidebar.svelte +112 -0
- package/dist/components/editor/sidebar/image-crop-sidebar.svelte.d.ts +7 -0
- package/dist/components/editor/sidebar/position-sidebar.svelte +0 -2
- package/dist/components/editor/sidebar/sidebar.svelte +6 -3
- package/dist/components/editor/types.d.ts +2 -1
- package/dist/components/editor/utils.js +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import { tv, type VariantProps } from 'tailwind-variants';
|
|
3
|
+
import type { ImageLayer } from '../../../../../types.js';
|
|
4
|
+
|
|
5
|
+
const cornerScaleControlVariants = tv({
|
|
6
|
+
slots: {
|
|
7
|
+
base: 'group absolute w-8 h-8 flex items-center justify-center pointer-events-none',
|
|
8
|
+
handler: 'absolute w-4 h-4 pointer-events-auto',
|
|
9
|
+
cursor: '',
|
|
10
|
+
},
|
|
11
|
+
variants: {
|
|
12
|
+
origin: {
|
|
13
|
+
'bottom-right': {
|
|
14
|
+
base: '-top-4 -left-4',
|
|
15
|
+
handler: '-translate-x-0.5 -translate-y-0.5 rounded-tl-full',
|
|
16
|
+
cursor: 'cursor-nwse-resize',
|
|
17
|
+
},
|
|
18
|
+
'bottom-left': {
|
|
19
|
+
base: '-top-4 -right-4',
|
|
20
|
+
handler: 'translate-x-0.5 -translate-y-0.5 rounded-tr-full',
|
|
21
|
+
cursor: 'cursor-nesw-resize',
|
|
22
|
+
},
|
|
23
|
+
'top-right': {
|
|
24
|
+
base: '-bottom-4 -left-4',
|
|
25
|
+
handler: '-translate-x-0.5 translate-y-0.5 rounded-bl-full',
|
|
26
|
+
cursor: 'cursor-nesw-resize',
|
|
27
|
+
},
|
|
28
|
+
'top-left': {
|
|
29
|
+
base: '-bottom-4 -right-4',
|
|
30
|
+
handler: 'translate-x-0.5 translate-y-0.5 rounded-br-full',
|
|
31
|
+
cursor: 'cursor-nwse-resize',
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
type Variants = VariantProps<typeof cornerScaleControlVariants>;
|
|
38
|
+
|
|
39
|
+
interface Props extends Omit<Variants, 'origin'>, Required<Pick<Variants, 'origin'>> {
|
|
40
|
+
layer: ImageLayer;
|
|
41
|
+
}
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
<script lang="ts">
|
|
45
|
+
import { cn } from '../../../../../../../utils.js';
|
|
46
|
+
import {
|
|
47
|
+
getImageLayerBboxRelativeToImageCenter,
|
|
48
|
+
calculateNewPosition,
|
|
49
|
+
degToRad,
|
|
50
|
+
rotatePoint,
|
|
51
|
+
} from '../../../../utils.js';
|
|
52
|
+
import { getEditorContext } from '../../../../../editor.svelte.js';
|
|
53
|
+
|
|
54
|
+
let { origin, layer }: Props = $props();
|
|
55
|
+
|
|
56
|
+
const type = `${origin}-origin-scale`;
|
|
57
|
+
|
|
58
|
+
const editor = getEditorContext();
|
|
59
|
+
|
|
60
|
+
let initial: {
|
|
61
|
+
clientX: number;
|
|
62
|
+
clientY: number;
|
|
63
|
+
layer: ImageLayer;
|
|
64
|
+
minScaleFactor: number;
|
|
65
|
+
} | null = null;
|
|
66
|
+
|
|
67
|
+
const { base, handler, cursor } = cornerScaleControlVariants({ origin });
|
|
68
|
+
|
|
69
|
+
const onMouseDown = (e: MouseEvent) => {
|
|
70
|
+
if (e.button !== 0) return;
|
|
71
|
+
e.preventDefault();
|
|
72
|
+
e.stopPropagation();
|
|
73
|
+
console.log(type, 'mousedown', e);
|
|
74
|
+
|
|
75
|
+
const { minX, minY, maxX, maxY } = getImageLayerBboxRelativeToImageCenter(layer);
|
|
76
|
+
|
|
77
|
+
const minScaleFactor = Math.max(
|
|
78
|
+
~origin.indexOf('left') ? 0.5 + maxX / layer.image.width : 0.5 - minX / layer.image.width,
|
|
79
|
+
~origin.indexOf('top') ? 0.5 + maxY / layer.image.height : 0.5 - minY / layer.image.height,
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
initial = {
|
|
83
|
+
clientX: e.clientX,
|
|
84
|
+
clientY: e.clientY,
|
|
85
|
+
layer: $state.snapshot(layer),
|
|
86
|
+
minScaleFactor,
|
|
87
|
+
};
|
|
88
|
+
addEventListener('mousemove', onMouseMove);
|
|
89
|
+
addEventListener('mouseup', onMouseUp);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const onMouseUp = (e: MouseEvent) => {
|
|
93
|
+
console.log(type, 'mouseup', e);
|
|
94
|
+
removeEventListener('mousemove', onMouseMove);
|
|
95
|
+
removeEventListener('mouseup', onMouseUp);
|
|
96
|
+
initial = null;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const onMouseMove = (e: MouseEvent) => {
|
|
100
|
+
if (!initial) return;
|
|
101
|
+
|
|
102
|
+
const { x, y, width, height, scale, rotate, image, imageRotate, offsetX, offsetY } =
|
|
103
|
+
initial.layer;
|
|
104
|
+
const imageX = x + offsetX * scale;
|
|
105
|
+
const imageY = y + offsetY * scale;
|
|
106
|
+
const rad = degToRad(rotate + imageRotate);
|
|
107
|
+
const xDiff = (e.clientX - initial.clientX) / editor.zoom;
|
|
108
|
+
const yDiff = (e.clientY - initial.clientY) / editor.zoom;
|
|
109
|
+
const adjustedXDiff = xDiff * Math.cos(rad) + yDiff * Math.sin(rad);
|
|
110
|
+
const adjustedYDiff = -xDiff * Math.sin(rad) + yDiff * Math.cos(rad);
|
|
111
|
+
|
|
112
|
+
// Define the new x2 and y2 coordinates based on the variant (corner being dragged)
|
|
113
|
+
const x2 = imageX + image.width * scale + adjustedXDiff * (~origin.indexOf('left') ? 1 : -1);
|
|
114
|
+
const y2 = imageY + image.height * scale + adjustedYDiff * (~origin.indexOf('top') ? 1 : -1);
|
|
115
|
+
|
|
116
|
+
// Calculate the slope of the rectangle
|
|
117
|
+
const slope = image.height / image.width;
|
|
118
|
+
|
|
119
|
+
// Calculate the x-coordinate of the intersection point
|
|
120
|
+
const xIntersect = (y2 + x2 / slope - imageY + imageX * slope) / (slope + 1 / slope);
|
|
121
|
+
|
|
122
|
+
let scaleFactor = Math.max(
|
|
123
|
+
(xIntersect - imageX) / (image.width * scale),
|
|
124
|
+
initial.minScaleFactor,
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
if (scaleFactor > 0) {
|
|
128
|
+
const { newX, newY } = calculateNewPosition(
|
|
129
|
+
origin,
|
|
130
|
+
{ ...image, x: offsetX, y: offsetY, rotate: imageRotate, scale: 1 },
|
|
131
|
+
image.width * scaleFactor,
|
|
132
|
+
image.height * scaleFactor,
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
const newProps = {
|
|
136
|
+
scale: scale * scaleFactor,
|
|
137
|
+
width: width / scaleFactor,
|
|
138
|
+
height: height / scaleFactor,
|
|
139
|
+
offsetX: newX / scaleFactor,
|
|
140
|
+
offsetY: newY / scaleFactor,
|
|
141
|
+
};
|
|
142
|
+
Object.assign(layer, newProps);
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
</script>
|
|
146
|
+
|
|
147
|
+
<div class={base()}>
|
|
148
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
149
|
+
<div class={cn(handler(), cursor())} onmousedown={onMouseDown}></div>
|
|
150
|
+
<div
|
|
151
|
+
class="group-hover:bg-primary group-active:bg-primary h-3 w-3 rounded-full bg-white transition-colors"
|
|
152
|
+
style:box-shadow="0 0 4px 1px rgba(57,76,96,.15), 0 0 0 1px rgba(43,59,74,.3)"
|
|
153
|
+
></div>
|
|
154
|
+
</div>
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { type VariantProps } from 'tailwind-variants';
|
|
2
|
+
import type { ImageLayer } from '../../../../../types.js';
|
|
3
|
+
declare const cornerScaleControlVariants: import("tailwind-variants").TVReturnType<{
|
|
4
|
+
origin: {
|
|
5
|
+
'bottom-right': {
|
|
6
|
+
base: string;
|
|
7
|
+
handler: string;
|
|
8
|
+
cursor: string;
|
|
9
|
+
};
|
|
10
|
+
'bottom-left': {
|
|
11
|
+
base: string;
|
|
12
|
+
handler: string;
|
|
13
|
+
cursor: string;
|
|
14
|
+
};
|
|
15
|
+
'top-right': {
|
|
16
|
+
base: string;
|
|
17
|
+
handler: string;
|
|
18
|
+
cursor: string;
|
|
19
|
+
};
|
|
20
|
+
'top-left': {
|
|
21
|
+
base: string;
|
|
22
|
+
handler: string;
|
|
23
|
+
cursor: string;
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
}, {
|
|
27
|
+
base: string;
|
|
28
|
+
handler: string;
|
|
29
|
+
cursor: string;
|
|
30
|
+
}, undefined, {
|
|
31
|
+
origin: {
|
|
32
|
+
'bottom-right': {
|
|
33
|
+
base: string;
|
|
34
|
+
handler: string;
|
|
35
|
+
cursor: string;
|
|
36
|
+
};
|
|
37
|
+
'bottom-left': {
|
|
38
|
+
base: string;
|
|
39
|
+
handler: string;
|
|
40
|
+
cursor: string;
|
|
41
|
+
};
|
|
42
|
+
'top-right': {
|
|
43
|
+
base: string;
|
|
44
|
+
handler: string;
|
|
45
|
+
cursor: string;
|
|
46
|
+
};
|
|
47
|
+
'top-left': {
|
|
48
|
+
base: string;
|
|
49
|
+
handler: string;
|
|
50
|
+
cursor: string;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
}, {
|
|
54
|
+
base: string;
|
|
55
|
+
handler: string;
|
|
56
|
+
cursor: string;
|
|
57
|
+
}, import("tailwind-variants").TVReturnType<{
|
|
58
|
+
origin: {
|
|
59
|
+
'bottom-right': {
|
|
60
|
+
base: string;
|
|
61
|
+
handler: string;
|
|
62
|
+
cursor: string;
|
|
63
|
+
};
|
|
64
|
+
'bottom-left': {
|
|
65
|
+
base: string;
|
|
66
|
+
handler: string;
|
|
67
|
+
cursor: string;
|
|
68
|
+
};
|
|
69
|
+
'top-right': {
|
|
70
|
+
base: string;
|
|
71
|
+
handler: string;
|
|
72
|
+
cursor: string;
|
|
73
|
+
};
|
|
74
|
+
'top-left': {
|
|
75
|
+
base: string;
|
|
76
|
+
handler: string;
|
|
77
|
+
cursor: string;
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
}, {
|
|
81
|
+
base: string;
|
|
82
|
+
handler: string;
|
|
83
|
+
cursor: string;
|
|
84
|
+
}, undefined, unknown, unknown, undefined>>;
|
|
85
|
+
type Variants = VariantProps<typeof cornerScaleControlVariants>;
|
|
86
|
+
interface Props extends Omit<Variants, 'origin'>, Required<Pick<Variants, 'origin'>> {
|
|
87
|
+
layer: ImageLayer;
|
|
88
|
+
}
|
|
89
|
+
declare const ImageScaleControl: import("svelte").Component<Props, {}, "">;
|
|
90
|
+
type ImageScaleControl = ReturnType<typeof ImageScaleControl>;
|
|
91
|
+
export default ImageScaleControl;
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
layer.flipX || layer.flipY ? `scale(${layer.flipX ? -1 : 1}, ${layer.flipY ? -1 : 1})` : null,
|
|
33
33
|
);
|
|
34
34
|
|
|
35
|
-
let cornderRadius = $derived(layer.cornerRadius * thumbScale);
|
|
35
|
+
let cornderRadius = $derived(Math.min(layer.cornerRadius * thumbScale, width / 2, height / 2));
|
|
36
36
|
|
|
37
37
|
const bezier = 0.447715;
|
|
38
38
|
let roundedClipPath = $derived(
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
class="relative"
|
|
52
52
|
style:width="{layer.image.width * scale}px"
|
|
53
53
|
style:height="{layer.image.height * scale}px"
|
|
54
|
-
style:transform={`translate(${
|
|
54
|
+
style:transform={`translate(${layer.offsetX * scale}px, ${layer.offsetY * scale}px) rotate(${layer.imageRotate}deg)`}
|
|
55
55
|
>
|
|
56
56
|
{#if imageLoaded}
|
|
57
57
|
<img
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
<ColorIndicatorGradientDef id={gradientId} color={borderColor} {width} {height} />
|
|
86
86
|
{/if}
|
|
87
87
|
<clipPath id={layerBorderId}>
|
|
88
|
-
<path d={path}></path>
|
|
88
|
+
<path d={roundedClipPath || path}></path>
|
|
89
89
|
</clipPath>
|
|
90
90
|
</defs>
|
|
91
91
|
<path
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { onMount } from 'svelte';
|
|
3
|
+
import type { ImageLayer } from '../../../types.js';
|
|
4
|
+
import { getEditorContext } from '../../../editor.svelte.js';
|
|
5
|
+
import ImageLayerContent from './image-layer-content.svelte';
|
|
6
|
+
import { ImageScaleControl } from './controls/image-scale-control/index.js';
|
|
7
|
+
import { ImageRotateControl } from './controls/image-rotate-control/index.js';
|
|
8
|
+
import { getImageLayerBboxRelativeToImageCenter, degToRad, rotatePoint } from '../../utils.js';
|
|
9
|
+
|
|
10
|
+
interface Props {
|
|
11
|
+
layer: ImageLayer;
|
|
12
|
+
viewportRef: HTMLDivElement;
|
|
13
|
+
wrapperRef: HTMLDivElement;
|
|
14
|
+
pageRef: HTMLDivElement;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const { layer, viewportRef, wrapperRef, pageRef }: Props = $props();
|
|
18
|
+
|
|
19
|
+
const editor = getEditorContext();
|
|
20
|
+
|
|
21
|
+
let overlayRef: HTMLDivElement | null = $state(null);
|
|
22
|
+
|
|
23
|
+
let imageTransform = $derived.by(() => {
|
|
24
|
+
const rad = degToRad(layer.rotate);
|
|
25
|
+
|
|
26
|
+
const layerCenter = {
|
|
27
|
+
x: layer.x + (layer.width * layer.scale) / 2,
|
|
28
|
+
y: layer.y + (layer.height * layer.scale) / 2,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const relativeCenter = {
|
|
32
|
+
x: layer.x + (layer.offsetX + layer.image.width / 2) * layer.scale,
|
|
33
|
+
y: layer.y + (layer.offsetY + layer.image.height / 2) * layer.scale,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const center = rotatePoint(rad, relativeCenter, layerCenter);
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
x: center.x - (layer.image.width * layer.scale) / 2,
|
|
40
|
+
y: center.y - (layer.image.height * layer.scale) / 2,
|
|
41
|
+
width: layer.image.width,
|
|
42
|
+
height: layer.image.height,
|
|
43
|
+
rotate: layer.imageRotate + layer.rotate,
|
|
44
|
+
scale: layer.scale,
|
|
45
|
+
};
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const updateOverlay = () => {
|
|
49
|
+
if (overlayRef) {
|
|
50
|
+
const viewportRect = viewportRef.getBoundingClientRect();
|
|
51
|
+
const wrapperRect = wrapperRef.getBoundingClientRect();
|
|
52
|
+
const pageRect = pageRef.getBoundingClientRect();
|
|
53
|
+
|
|
54
|
+
overlayRef.style.left = `${viewportRect.left - viewportRef.scrollLeft - pageRect.left}px`;
|
|
55
|
+
overlayRef.style.top = `${viewportRect.top - viewportRef.scrollTop - pageRect.top}px`;
|
|
56
|
+
overlayRef.style.width = `${wrapperRect.width > viewportRect.width ? wrapperRect.width : viewportRect.width}px`;
|
|
57
|
+
overlayRef.style.height = `${wrapperRect.height > viewportRect.height ? wrapperRect.height : viewportRect.height}px`;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
$effect(() => {
|
|
62
|
+
editor.zoom;
|
|
63
|
+
updateOverlay();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
onMount(() => {
|
|
67
|
+
const observer = new ResizeObserver(updateOverlay);
|
|
68
|
+
|
|
69
|
+
if (viewportRef) observer.observe(viewportRef);
|
|
70
|
+
|
|
71
|
+
return () => observer.disconnect();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
let initial: {
|
|
75
|
+
clientX: number;
|
|
76
|
+
clientY: number;
|
|
77
|
+
layer: ImageLayer;
|
|
78
|
+
minImageXDiff: number;
|
|
79
|
+
minImageYDiff: number;
|
|
80
|
+
maxImageXDiff: number;
|
|
81
|
+
maxImageYDiff: number;
|
|
82
|
+
} | null = null;
|
|
83
|
+
|
|
84
|
+
const onMouseDown = (e: MouseEvent) => {
|
|
85
|
+
e.preventDefault();
|
|
86
|
+
e.stopPropagation();
|
|
87
|
+
|
|
88
|
+
const { minX, minY, maxX, maxY } = getImageLayerBboxRelativeToImageCenter(layer);
|
|
89
|
+
|
|
90
|
+
initial = {
|
|
91
|
+
clientX: e.clientX,
|
|
92
|
+
clientY: e.clientY,
|
|
93
|
+
layer: $state.snapshot(layer),
|
|
94
|
+
minImageXDiff: maxX - layer.image.width / 2,
|
|
95
|
+
minImageYDiff: maxY - layer.image.height / 2,
|
|
96
|
+
maxImageXDiff: layer.image.width / 2 + minX,
|
|
97
|
+
maxImageYDiff: layer.image.height / 2 + minY,
|
|
98
|
+
};
|
|
99
|
+
addEventListener('mousemove', onMouseMove);
|
|
100
|
+
addEventListener('mouseup', onMouseUp);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const onMouseUp = (e: MouseEvent) => {
|
|
104
|
+
removeEventListener('mousemove', onMouseMove);
|
|
105
|
+
removeEventListener('mouseup', onMouseUp);
|
|
106
|
+
initial = null;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const onMouseMove = (e: MouseEvent) => {
|
|
110
|
+
if (!initial) return;
|
|
111
|
+
const { scale, rotate, imageRotate, offsetX, offsetY } = initial.layer;
|
|
112
|
+
|
|
113
|
+
const xDiff = (e.clientX - initial.clientX) / scale / editor.zoom;
|
|
114
|
+
const yDiff = (e.clientY - initial.clientY) / scale / editor.zoom;
|
|
115
|
+
|
|
116
|
+
const imageRad = degToRad(rotate + imageRotate);
|
|
117
|
+
|
|
118
|
+
const imageXDiff2 = Math.min(
|
|
119
|
+
Math.max(xDiff * Math.cos(imageRad) + yDiff * Math.sin(imageRad), initial.minImageXDiff),
|
|
120
|
+
initial.maxImageXDiff,
|
|
121
|
+
);
|
|
122
|
+
const imageYDiff2 = Math.min(
|
|
123
|
+
Math.max(-xDiff * Math.sin(imageRad) + yDiff * Math.cos(imageRad), initial.minImageYDiff),
|
|
124
|
+
initial.maxImageYDiff,
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
const rad = degToRad(-imageRotate);
|
|
128
|
+
|
|
129
|
+
const adjustedXDiff = imageXDiff2 * Math.cos(rad) + imageYDiff2 * Math.sin(rad);
|
|
130
|
+
const adjustedYDiff = -imageXDiff2 * Math.sin(rad) + imageYDiff2 * Math.cos(rad);
|
|
131
|
+
|
|
132
|
+
Object.assign(layer, {
|
|
133
|
+
offsetX: offsetX + adjustedXDiff,
|
|
134
|
+
offsetY: offsetY + adjustedYDiff,
|
|
135
|
+
});
|
|
136
|
+
};
|
|
137
|
+
</script>
|
|
138
|
+
|
|
139
|
+
<div class="select-none">
|
|
140
|
+
<div bind:this={overlayRef} class="absolute bg-slate-500/50"></div>
|
|
141
|
+
<div
|
|
142
|
+
class="absolute opacity-50"
|
|
143
|
+
style:width={`${layer.image.width * layer.scale * editor.zoom}px`}
|
|
144
|
+
style:height={`${layer.image.height * layer.scale * editor.zoom}px`}
|
|
145
|
+
style:transform={`translate(${imageTransform.x * editor.zoom}px, ${imageTransform.y * editor.zoom}px) rotate(${imageTransform.rotate}deg)`}
|
|
146
|
+
>
|
|
147
|
+
<img class="pointer-events-none h-full w-full" src={layer.image.src} alt="" />
|
|
148
|
+
</div>
|
|
149
|
+
<div
|
|
150
|
+
class="absolute overflow-hidden"
|
|
151
|
+
style:width={`${layer.width * layer.scale * editor.zoom}px`}
|
|
152
|
+
style:height={`${layer.height * layer.scale * editor.zoom}px`}
|
|
153
|
+
style:transform={`translate(${layer.x * editor.zoom}px, ${layer.y * editor.zoom}px) rotate(${layer.rotate}deg)`}
|
|
154
|
+
>
|
|
155
|
+
<ImageLayerContent {layer} thumbScale={editor.zoom} />
|
|
156
|
+
</div>
|
|
157
|
+
<div class="pointer-events-auto">
|
|
158
|
+
<div
|
|
159
|
+
class="shadow-inner-1 outline-primary/50 pointer-events-none absolute outline outline-2"
|
|
160
|
+
style:width={`${layer.image.width * layer.scale * editor.zoom}px`}
|
|
161
|
+
style:height={`${layer.image.height * layer.scale * editor.zoom}px`}
|
|
162
|
+
style:transform={`translate(${imageTransform.x * editor.zoom}px, ${imageTransform.y * editor.zoom}px) rotate(${imageTransform.rotate}deg)`}
|
|
163
|
+
>
|
|
164
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
165
|
+
<div
|
|
166
|
+
class="pointer-events-auto absolute -inset-3 cursor-move"
|
|
167
|
+
onmousedown={onMouseDown}
|
|
168
|
+
></div>
|
|
169
|
+
<ImageScaleControl origin="bottom-right" {layer} />
|
|
170
|
+
<ImageScaleControl origin="bottom-left" {layer} />
|
|
171
|
+
<ImageScaleControl origin="top-right" {layer} />
|
|
172
|
+
<ImageScaleControl origin="top-left" {layer} />
|
|
173
|
+
<ImageRotateControl {pageRef} {layer} />
|
|
174
|
+
</div>
|
|
175
|
+
<div
|
|
176
|
+
class="outline-primary pointer-events-none absolute outline outline-2"
|
|
177
|
+
style:width={`${layer.width * layer.scale * editor.zoom}px`}
|
|
178
|
+
style:height={`${layer.height * layer.scale * editor.zoom}px`}
|
|
179
|
+
style:transform={`translate(${layer.x * editor.zoom}px, ${layer.y * editor.zoom}px) rotate(${layer.rotate}deg)`}
|
|
180
|
+
></div>
|
|
181
|
+
</div>
|
|
182
|
+
</div>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ImageLayer } from '../../../types.js';
|
|
2
|
+
interface Props {
|
|
3
|
+
layer: ImageLayer;
|
|
4
|
+
viewportRef: HTMLDivElement;
|
|
5
|
+
wrapperRef: HTMLDivElement;
|
|
6
|
+
pageRef: HTMLDivElement;
|
|
7
|
+
}
|
|
8
|
+
declare const ImageLayerCrop: import("svelte").Component<Props, {}, "">;
|
|
9
|
+
type ImageLayerCrop = ReturnType<typeof ImageLayerCrop>;
|
|
10
|
+
export default ImageLayerCrop;
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
|
|
13
13
|
const editor = getEditorContext();
|
|
14
14
|
|
|
15
|
+
let isCropping = $derived(editor.imageCropLayer?.id === layer.id);
|
|
16
|
+
|
|
15
17
|
let layerSubstitute: ImageLayer | null = $state(null);
|
|
16
18
|
|
|
17
19
|
let ref: HTMLDivElement;
|
|
@@ -19,6 +21,7 @@
|
|
|
19
21
|
const buildImageDropChanges = (image: Image) => {
|
|
20
22
|
return {
|
|
21
23
|
image,
|
|
24
|
+
imageRotate: 0,
|
|
22
25
|
...calculateImageCover(image, {
|
|
23
26
|
width: layer.width * layer.scale,
|
|
24
27
|
height: layer.height * layer.scale,
|
|
@@ -89,12 +92,25 @@
|
|
|
89
92
|
}
|
|
90
93
|
layerSubstitute = null;
|
|
91
94
|
};
|
|
95
|
+
|
|
96
|
+
const onDoubleClick = () => {
|
|
97
|
+
if (editor.activeLayers.length > 1) return;
|
|
98
|
+
editor.imageCropLayer = $state.snapshot(
|
|
99
|
+
editor.activeGroupAndChild
|
|
100
|
+
? (editor.getAbsoluteGroupLayers(editor.activeGroupAndChild.groupLayer, [
|
|
101
|
+
layer,
|
|
102
|
+
])[0] as ImageLayer)
|
|
103
|
+
: layer,
|
|
104
|
+
);
|
|
105
|
+
};
|
|
92
106
|
</script>
|
|
93
107
|
|
|
94
108
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
95
109
|
<div
|
|
96
110
|
bind:this={ref}
|
|
97
111
|
class="h-full w-full"
|
|
112
|
+
style:opacity={isCropping ? 0 : null}
|
|
113
|
+
ondblclick={onDoubleClick}
|
|
98
114
|
ondragenter={onDragEnter}
|
|
99
115
|
ondragover={onDragOver}
|
|
100
116
|
ondragleave={onDragLeave}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import Root from './image-layer.svelte';
|
|
2
2
|
import ImageLayerContent from './image-layer-content.svelte';
|
|
3
|
-
|
|
3
|
+
import ImageLayerCrop from './image-layer-crop.svelte';
|
|
4
|
+
export { Root, Root as ImageLayer, ImageLayerContent, ImageLayerCrop };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import Root from './image-layer.svelte';
|
|
2
2
|
import ImageLayerContent from './image-layer-content.svelte';
|
|
3
|
-
|
|
3
|
+
import ImageLayerCrop from './image-layer-crop.svelte';
|
|
4
|
+
export { Root, Root as ImageLayer, ImageLayerContent, ImageLayerCrop };
|
|
@@ -28,7 +28,6 @@ export const ListItem = TiptapNode.create({
|
|
|
28
28
|
addCommands() {
|
|
29
29
|
return {
|
|
30
30
|
splitListItem: (typeOrName, overrideAttrs) => ({ editor, tr, state, dispatch }) => {
|
|
31
|
-
console.log('splitListItem overwritten');
|
|
32
31
|
const type = getNodeType(typeOrName, state.schema);
|
|
33
32
|
const { $from, $to } = state.selection;
|
|
34
33
|
// @ts-ignore
|
|
@@ -160,7 +159,6 @@ export const ListItem = TiptapNode.create({
|
|
|
160
159
|
// Check if selection is in first list item
|
|
161
160
|
const firstListItem = findParentNodeClosestToPos(tr.selection.$from, (node) => node.type.name === this.name);
|
|
162
161
|
if (firstListItem && newParentList.node.firstChild === firstListItem.node) {
|
|
163
|
-
console.log('firstListItem', firstListItem);
|
|
164
162
|
const attrs = {
|
|
165
163
|
...newParentList.node.attrs,
|
|
166
164
|
...oldParentList.node.attrs,
|
|
@@ -12,13 +12,6 @@ export type Transform = {
|
|
|
12
12
|
rotate: number;
|
|
13
13
|
scale: number;
|
|
14
14
|
};
|
|
15
|
-
export declare const rotatePointOld: (point: {
|
|
16
|
-
x: number;
|
|
17
|
-
y: number;
|
|
18
|
-
}, angleRad: number) => {
|
|
19
|
-
x: number;
|
|
20
|
-
y: number;
|
|
21
|
-
};
|
|
22
15
|
export declare const calculateLayerTransform: (layer: Layer, groupScale?: number) => Transform;
|
|
23
16
|
export declare const calculateNewPosition: (origin: Origin, transform: Transform, newWidth: number, newHeight: number) => {
|
|
24
17
|
newX: number;
|
|
@@ -31,7 +24,7 @@ export declare function calculateBoundingBox(transform: Transform): {
|
|
|
31
24
|
height: number;
|
|
32
25
|
};
|
|
33
26
|
export declare function degToRad(deg: number): number;
|
|
34
|
-
export declare function rotatePoint(
|
|
27
|
+
export declare function rotatePoint(rad: number, p: Point, center?: Point): Point;
|
|
35
28
|
export declare function getRotatedCorners(rect: Transform): Point[];
|
|
36
29
|
export declare function isRotatedVertically(rotate: number): boolean;
|
|
37
30
|
interface ScaleResult {
|
|
@@ -51,9 +44,9 @@ export declare function calculateRelativeRects(bbox: Transform, absoluteRects: T
|
|
|
51
44
|
x: number;
|
|
52
45
|
y: number;
|
|
53
46
|
rotate: number;
|
|
47
|
+
scale: number;
|
|
54
48
|
width: number;
|
|
55
49
|
height: number;
|
|
56
|
-
scale: number;
|
|
57
50
|
}[];
|
|
58
51
|
export declare function calculateAbsoluteRects(bbox: Transform, rects: Transform[]): Transform[];
|
|
59
52
|
export declare const checkPolygonsIntersect: (a: {
|
|
@@ -68,6 +61,7 @@ export declare function calculateImageCover(image: {
|
|
|
68
61
|
width: number;
|
|
69
62
|
height: number;
|
|
70
63
|
rotate?: number | null;
|
|
64
|
+
imageRotate?: number | null;
|
|
71
65
|
}, bbox: {
|
|
72
66
|
width: number;
|
|
73
67
|
height: number;
|
|
@@ -81,4 +75,25 @@ export declare function calculateImageCover(image: {
|
|
|
81
75
|
export declare const defaultColor = "#000000";
|
|
82
76
|
export declare function extractBorderColors(layers: ImageLayer[]): string[] | undefined;
|
|
83
77
|
export declare function buildBackgroundCircleStyle(colors: string[]): string;
|
|
78
|
+
export declare function getImageLayerBboxRelativeToImageCenter(layer: ImageLayer): {
|
|
79
|
+
minX: number;
|
|
80
|
+
minY: number;
|
|
81
|
+
maxX: number;
|
|
82
|
+
maxY: number;
|
|
83
|
+
};
|
|
84
|
+
export declare function calculateImageLayerPropsForImageRotate(layer: ImageLayer, imageRotate: number): {
|
|
85
|
+
imageRotate: number;
|
|
86
|
+
scale?: undefined;
|
|
87
|
+
width?: undefined;
|
|
88
|
+
height?: undefined;
|
|
89
|
+
offsetX?: undefined;
|
|
90
|
+
offsetY?: undefined;
|
|
91
|
+
} | {
|
|
92
|
+
imageRotate: number;
|
|
93
|
+
scale: number;
|
|
94
|
+
width: number;
|
|
95
|
+
height: number;
|
|
96
|
+
offsetX: number;
|
|
97
|
+
offsetY: number;
|
|
98
|
+
};
|
|
84
99
|
export {};
|