@peteai/presentation-editor 0.0.7 → 0.0.9
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 +24 -9
- 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 +25 -21
- package/dist/components/editor/hotkeys.svelte +7 -0
- package/dist/components/editor/layers/active-layer-border.svelte +1 -1
- package/dist/components/editor/layers/buttons/border-button/border-button-colors.svelte +1 -1
- package/dist/components/editor/layers/buttons/opacity-button/opacity-button.svelte +1 -6
- package/dist/components/editor/layers/controls/corner-scale-control/corner-scale-control.svelte +1 -1
- 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 +12 -6
- 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/controls/side-scale-control/side-scale-control.svelte +1 -1
- package/dist/components/editor/layers/index.d.ts +2 -2
- package/dist/components/editor/layers/index.js +2 -2
- package/dist/components/editor/layers/layer-button.svelte +1 -1
- package/dist/components/editor/layers/types/background/background-content-image.svelte +15 -20
- package/dist/components/editor/layers/types/background/background-layer-buttons.svelte +1 -1
- 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/color-sidebar/color-sidebar-color.svelte +2 -2
- package/dist/components/editor/sidebar/color-sidebar/color-sidebar-gradient-picker.svelte +5 -5
- package/dist/components/editor/sidebar/color-sidebar/color-sidebar.svelte +4 -4
- package/dist/components/editor/sidebar/font-sidebar/font-sidebar.svelte +1 -1
- 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-uploads-tab.svelte +3 -3
- package/dist/components/editor/sidebar/sidebar.svelte +7 -4
- package/dist/components/editor/snapping-guides.svelte +3 -3
- package/dist/components/editor/types.d.ts +2 -1
- package/dist/components/editor/utils.js +1 -0
- package/dist/components/ui/color-picker/color-picker-alpha-grid.svelte +2 -1
- package/dist/components/ui/color-picker/color-picker.svelte +6 -6
- package/dist/components/ui/context-menu/context-menu-checkbox-item.svelte +1 -1
- package/dist/components/ui/context-menu/context-menu-content.svelte +3 -1
- package/dist/components/ui/context-menu/context-menu-group-heading.svelte +1 -1
- package/dist/components/ui/context-menu/context-menu-item.svelte +1 -1
- package/dist/components/ui/context-menu/context-menu-radio-item.svelte +1 -1
- package/dist/components/ui/context-menu/context-menu-separator.svelte +1 -1
- package/dist/components/ui/context-menu/context-menu-shortcut.svelte +1 -1
- package/dist/components/ui/context-menu/context-menu-sub-content.svelte +1 -1
- package/dist/components/ui/context-menu/context-menu-sub-trigger.svelte +1 -1
- package/dist/components/ui/dialog/dialog-content.svelte +4 -2
- package/dist/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte +1 -1
- package/dist/components/ui/dropdown-menu/dropdown-menu-content.svelte +2 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-item.svelte +1 -1
- package/dist/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte +1 -1
- package/dist/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte +1 -1
- package/dist/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte +1 -1
- package/dist/components/ui/input/input.svelte +1 -1
- package/dist/components/ui/slider/slider.svelte +3 -3
- package/dist/components/ui/tabs/index.d.ts +4 -4
- package/dist/components/ui/tabs/index.js +4 -4
- package/dist/components/ui/tabs/tabs-content.svelte +4 -4
- package/dist/components/ui/tabs/tabs-content.svelte.d.ts +1 -1
- package/dist/components/ui/tabs/tabs-list.svelte +5 -9
- package/dist/components/ui/tabs/tabs-list.svelte.d.ts +1 -1
- package/dist/components/ui/tabs/tabs-trigger.svelte +4 -4
- package/dist/components/ui/tabs/tabs-trigger.svelte.d.ts +1 -1
- package/package.json +19 -19
|
@@ -10,12 +10,19 @@
|
|
|
10
10
|
import { GroupResizeControl } from './layers/controls/group-resize-control/index.js';
|
|
11
11
|
import { CornerScaleControl } from './layers/controls/corner-scale-control/index.js';
|
|
12
12
|
import { RotateControl } from './layers/controls/rotate-control/index.js';
|
|
13
|
-
import { TextLayerEdit } from './layers/index.js';
|
|
14
|
-
import type { GroupLayer, Layer, TextLayer } from './types.js';
|
|
13
|
+
import { ImageLayerCrop, TextLayerEdit } from './layers/index.js';
|
|
14
|
+
import type { GroupLayer, ImageLayer, Layer, TextLayer } from './types.js';
|
|
15
|
+
|
|
16
|
+
interface Props {
|
|
17
|
+
viewportRef: HTMLDivElement;
|
|
18
|
+
wrapperRef: HTMLDivElement;
|
|
19
|
+
pageRef: HTMLDivElement;
|
|
20
|
+
}
|
|
21
|
+
const { viewportRef, wrapperRef, pageRef }: Props = $props();
|
|
15
22
|
|
|
16
23
|
const editor = getEditorContext();
|
|
17
24
|
|
|
18
|
-
let
|
|
25
|
+
let textLayerEditData: { layer: TextLayer; groupLayer?: GroupLayer } | undefined = $derived.by(
|
|
19
26
|
() => {
|
|
20
27
|
if (editor.activeAction?.type === 'edit') {
|
|
21
28
|
if (editor.activeSelection.type === 'root-layers') {
|
|
@@ -37,6 +44,12 @@
|
|
|
37
44
|
},
|
|
38
45
|
);
|
|
39
46
|
|
|
47
|
+
let imageLayerCropData: { layer: ImageLayer } | undefined = $derived.by(() => {
|
|
48
|
+
if (editor.imageCropLayer) {
|
|
49
|
+
return { layer: editor.imageCropLayer };
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
40
53
|
const group = $derived.by(() => {
|
|
41
54
|
if (editor.activeSelection.type === 'background') {
|
|
42
55
|
return {
|
|
@@ -119,11 +132,13 @@
|
|
|
119
132
|
};
|
|
120
133
|
</script>
|
|
121
134
|
|
|
122
|
-
{#if
|
|
123
|
-
<TextLayerEdit {...
|
|
135
|
+
{#if textLayerEditData}
|
|
136
|
+
<TextLayerEdit {...textLayerEditData} />
|
|
124
137
|
{/if}
|
|
125
138
|
|
|
126
|
-
{#if
|
|
139
|
+
{#if imageLayerCropData}
|
|
140
|
+
<ImageLayerCrop {...imageLayerCropData} {viewportRef} {wrapperRef} {pageRef} />
|
|
141
|
+
{:else if group}
|
|
127
142
|
<div
|
|
128
143
|
class="pointer-events-none absolute left-0 top-0 h-full w-full"
|
|
129
144
|
style:width={`${group.bbox.width * group.bbox.scale * editor.zoom + group.padding * 2}px`}
|
|
@@ -132,14 +147,14 @@
|
|
|
132
147
|
>
|
|
133
148
|
{#if !group.items.length}
|
|
134
149
|
<div
|
|
135
|
-
class="
|
|
150
|
+
class="absolute -inset-px border-2 border-primary"
|
|
136
151
|
style:box-shadow="0 0 0 1px hsla(0, 0%, 100%, .07), inset 0 0 0 1px hsla(0, 0%, 100%, .07)"
|
|
137
152
|
></div>
|
|
138
153
|
{:else}
|
|
139
154
|
<div class="group-border absolute -inset-px"></div>
|
|
140
155
|
{#each group.items as item}
|
|
141
156
|
<div
|
|
142
|
-
class="
|
|
157
|
+
class="absolute -left-px -top-px border-2 border-primary"
|
|
143
158
|
style:box-shadow="0 0 0 1px hsla(0, 0%, 100%, .07), inset 0 0 0 1px hsla(0, 0%, 100%, .07)"
|
|
144
159
|
style:width={`${item.width * item.scale * editor.zoom + 2}px`}
|
|
145
160
|
style:height={`${item.height * item.scale * editor.zoom + 2}px`}
|
|
@@ -179,7 +194,7 @@
|
|
|
179
194
|
<CornerScaleControl origin="bottom-left" />
|
|
180
195
|
<CornerScaleControl origin="top-right" />
|
|
181
196
|
<CornerScaleControl origin="top-left" />
|
|
182
|
-
<RotateControl />
|
|
197
|
+
<RotateControl {pageRef} />
|
|
183
198
|
</div>
|
|
184
199
|
</div>
|
|
185
200
|
{/if}
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
interface Props {
|
|
2
|
+
viewportRef: HTMLDivElement;
|
|
3
|
+
wrapperRef: HTMLDivElement;
|
|
4
|
+
pageRef: HTMLDivElement;
|
|
5
|
+
}
|
|
6
|
+
declare const ActiveLayers: import("svelte").Component<Props, {}, "">;
|
|
2
7
|
type ActiveLayers = ReturnType<typeof ActiveLayers>;
|
|
3
8
|
export default ActiveLayers;
|
|
@@ -118,24 +118,8 @@
|
|
|
118
118
|
}
|
|
119
119
|
};
|
|
120
120
|
|
|
121
|
-
const onResize = () => {
|
|
122
|
-
tick().then(() => {
|
|
123
|
-
if (editor.zoomSnapping === 'fit') {
|
|
124
|
-
fitToScreen();
|
|
125
|
-
} else if (editor.zoomSnapping === 'fill') {
|
|
126
|
-
fillScreen();
|
|
127
|
-
}
|
|
128
|
-
});
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
$effect(() => {
|
|
132
|
-
editor.activeSidebarPopup;
|
|
133
|
-
editor.activeSidebarTab;
|
|
134
|
-
onResize();
|
|
135
|
-
});
|
|
136
|
-
|
|
137
121
|
const onDragOver = (e: DragEvent) => {
|
|
138
|
-
// console.log('dragover
|
|
122
|
+
// console.log('dragover editor', e);
|
|
139
123
|
e.preventDefault();
|
|
140
124
|
if (e.dataTransfer?.items.length) {
|
|
141
125
|
editor.fileDragged = true;
|
|
@@ -201,10 +185,24 @@
|
|
|
201
185
|
}
|
|
202
186
|
};
|
|
203
187
|
|
|
188
|
+
const onResize = () => {
|
|
189
|
+
if (editor.zoomSnapping === 'fit') {
|
|
190
|
+
tick().then(fitToScreen);
|
|
191
|
+
} else if (editor.zoomSnapping === 'fill') {
|
|
192
|
+
tick().then(fillScreen);
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
|
|
204
196
|
onMount(() => {
|
|
205
197
|
tick().then(() => {
|
|
206
198
|
fitToScreen();
|
|
207
199
|
});
|
|
200
|
+
|
|
201
|
+
const observer = new ResizeObserver(onResize);
|
|
202
|
+
|
|
203
|
+
if (viewportRef) observer.observe(viewportRef);
|
|
204
|
+
|
|
205
|
+
return () => observer.disconnect();
|
|
208
206
|
});
|
|
209
207
|
|
|
210
208
|
onDestroy(() => {
|
|
@@ -42,6 +42,7 @@ export class Editor {
|
|
|
42
42
|
onUpdate;
|
|
43
43
|
activeSidebarTab = $state(null);
|
|
44
44
|
activeSidebarPopup = $state(null);
|
|
45
|
+
imageCropLayer = $state(null);
|
|
45
46
|
toggleActiveSidebarTab(tab) {
|
|
46
47
|
this.activeSidebarTab = !this.activeSidebarPopup && this.activeSidebarTab === tab ? null : tab;
|
|
47
48
|
if (this.activeSidebarTab) {
|
|
@@ -360,6 +361,7 @@ export class Editor {
|
|
|
360
361
|
rotate: 0,
|
|
361
362
|
opacity: 1,
|
|
362
363
|
scale: 1,
|
|
364
|
+
imageRotate: 0,
|
|
363
365
|
offsetX: 0,
|
|
364
366
|
offsetY: 0,
|
|
365
367
|
cornerRadius: 0,
|
|
@@ -458,7 +460,7 @@ export class Editor {
|
|
|
458
460
|
tryToDuplicate();
|
|
459
461
|
}
|
|
460
462
|
getAbsoluteGroupLayers(groupLayer, children) {
|
|
461
|
-
const
|
|
463
|
+
const rad = degToRad(groupLayer.rotate);
|
|
462
464
|
const groupCenter = {
|
|
463
465
|
x: groupLayer.x + (groupLayer.width * groupLayer.scale) / 2,
|
|
464
466
|
y: groupLayer.y + (groupLayer.height * groupLayer.scale) / 2,
|
|
@@ -468,7 +470,7 @@ export class Editor {
|
|
|
468
470
|
x: groupLayer.x + (l.x + (l.width * l.scale) / 2) * groupLayer.scale,
|
|
469
471
|
y: groupLayer.y + (l.y + (l.height * l.scale) / 2) * groupLayer.scale,
|
|
470
472
|
};
|
|
471
|
-
const center = rotatePoint(relativeCenter, groupCenter
|
|
473
|
+
const center = rotatePoint(rad, relativeCenter, groupCenter);
|
|
472
474
|
return {
|
|
473
475
|
...l,
|
|
474
476
|
x: center.x - (l.width * l.scale * groupLayer.scale) / 2,
|
|
@@ -773,14 +775,14 @@ export class Editor {
|
|
|
773
775
|
return false;
|
|
774
776
|
if (layer.type !== 'image')
|
|
775
777
|
return false;
|
|
776
|
-
const { width, height, rotate, image, opacity, flipX, flipY, offsetX, offsetY } = layer;
|
|
777
|
-
const cover = calculateImageCover({ width, height, rotate }, { width: this.width, height: this.height });
|
|
778
|
+
const { width, height, rotate, image, opacity, flipX, flipY, imageRotate, offsetX, offsetY } = $state.snapshot(layer);
|
|
779
|
+
const cover = calculateImageCover({ width, height, rotate, imageRotate }, { width: this.width, height: this.height });
|
|
778
780
|
const backgroundImage = {
|
|
779
|
-
image
|
|
781
|
+
image,
|
|
780
782
|
scale: cover.scale,
|
|
781
|
-
offsetX:
|
|
782
|
-
offsetY:
|
|
783
|
-
rotate,
|
|
783
|
+
offsetX: offsetX + cover.offsetX,
|
|
784
|
+
offsetY: offsetY + cover.offsetY,
|
|
785
|
+
imageRotate: rotate + imageRotate,
|
|
784
786
|
opacity,
|
|
785
787
|
flipX,
|
|
786
788
|
flipY,
|
|
@@ -12,15 +12,17 @@
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
const { editor }: Props = $props();
|
|
15
|
+
|
|
16
|
+
let disabled = $derived(!!editor.imageCropLayer);
|
|
15
17
|
</script>
|
|
16
18
|
|
|
17
|
-
<div class="
|
|
19
|
+
<div class="h-12 w-full border-b border-gray-200 bg-background px-2">
|
|
18
20
|
<div class="flex h-full items-center justify-between">
|
|
19
21
|
<div class="grid grid-flow-col items-center gap-2">
|
|
20
22
|
<Button
|
|
21
23
|
variant="ghost"
|
|
22
24
|
size="icon-xs"
|
|
23
|
-
disabled={editor.historyIndex === -1}
|
|
25
|
+
disabled={editor.historyIndex === -1 || disabled}
|
|
24
26
|
onclick={() => editor.historyUndo()}
|
|
25
27
|
>
|
|
26
28
|
<UndoIcon class="h-6 w-6" />
|
|
@@ -28,31 +30,33 @@
|
|
|
28
30
|
<Button
|
|
29
31
|
variant="ghost"
|
|
30
32
|
size="icon-xs"
|
|
31
|
-
disabled={editor.historyIndex === editor.historyActions.length - 1}
|
|
33
|
+
disabled={editor.historyIndex === editor.historyActions.length - 1 || disabled}
|
|
32
34
|
onclick={() => editor.historyRedo()}
|
|
33
35
|
>
|
|
34
36
|
<RedoIcon class="h-6 w-6" />
|
|
35
37
|
</Button>
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
{#if editor.
|
|
39
|
-
{#if
|
|
40
|
-
|
|
38
|
+
{#if !disabled}
|
|
39
|
+
<div class="grid grid-flow-col items-center gap-2 overflow-x-auto">
|
|
40
|
+
{#if !editor.activePage.locked}
|
|
41
|
+
{#if editor.activeSelection.type === 'background'}
|
|
42
|
+
{#if !editor.activePage.backgroundLocked}
|
|
43
|
+
<BackgroundLayerButtons />
|
|
44
|
+
{/if}
|
|
45
|
+
{:else if editor.activeLayers.length}
|
|
46
|
+
<ActiveLayersButtons />
|
|
41
47
|
{/if}
|
|
42
|
-
{:else if editor.activeLayers.length}
|
|
43
|
-
<ActiveLayersButtons />
|
|
44
48
|
{/if}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
49
|
+
</div>
|
|
50
|
+
<Separator orientation="vertical" />
|
|
51
|
+
<Button
|
|
52
|
+
variant="ghost"
|
|
53
|
+
size="xs"
|
|
54
|
+
active={editor.activeSidebarPopup === 'position'}
|
|
55
|
+
onclick={() => editor.toggleActiveSidebarPopup('position')}
|
|
56
|
+
>
|
|
57
|
+
Position
|
|
58
|
+
</Button>
|
|
59
|
+
{/if}
|
|
56
60
|
</div>
|
|
57
61
|
</div>
|
|
58
62
|
</div>
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
style:transform={`translate(${layer.x * editor.zoom}px, ${layer.y * editor.zoom}px) rotate(${layer.rotate}deg)`}
|
|
19
19
|
>
|
|
20
20
|
<div
|
|
21
|
-
class="
|
|
21
|
+
class="absolute -inset-px border-2 border-primary"
|
|
22
22
|
style:box-shadow="0 0 0 1px hsla(0, 0%, 100%, .07), inset 0 0 0 1px hsla(0, 0%, 100%, .07)"
|
|
23
23
|
></div>
|
|
24
24
|
</div>
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
{/each}
|
|
51
51
|
</svg>
|
|
52
52
|
<span
|
|
53
|
-
class="
|
|
53
|
+
class="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 rounded-full bg-background"
|
|
54
54
|
style:width="{width / 2}px"
|
|
55
55
|
style:height="{height / 2}px"
|
|
56
56
|
></span>
|
|
@@ -86,11 +86,6 @@
|
|
|
86
86
|
initial = null;
|
|
87
87
|
}
|
|
88
88
|
};
|
|
89
|
-
|
|
90
|
-
const onChange = (e: Event) => {
|
|
91
|
-
const target = e.target as HTMLInputElement;
|
|
92
|
-
setTransparency(target.value);
|
|
93
|
-
};
|
|
94
89
|
</script>
|
|
95
90
|
|
|
96
91
|
<Popover.Root>
|
|
@@ -121,7 +116,7 @@
|
|
|
121
116
|
inputmode="decimal"
|
|
122
117
|
placeholder="--"
|
|
123
118
|
value={trasparency}
|
|
124
|
-
onchange={
|
|
119
|
+
onchange={(e) => setTransparency(e.currentTarget.value)}
|
|
125
120
|
/>
|
|
126
121
|
</div>
|
|
127
122
|
</div>
|
package/dist/components/editor/layers/controls/corner-scale-control/corner-scale-control.svelte
CHANGED
|
@@ -198,7 +198,7 @@
|
|
|
198
198
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
199
199
|
<div class={cn(handler(), cursor())} onmousedown={onMouseDown}></div>
|
|
200
200
|
<div
|
|
201
|
-
class="
|
|
201
|
+
class="h-3 w-3 rounded-full bg-white transition-colors group-hover:bg-primary group-active:bg-primary"
|
|
202
202
|
style:box-shadow="0 0 4px 1px rgba(57,76,96,.15), 0 0 0 1px rgba(43,59,74,.3)"
|
|
203
203
|
></div>
|
|
204
204
|
</div>
|
package/dist/components/editor/layers/controls/group-resize-control/group-resize-control.svelte
CHANGED
|
@@ -236,13 +236,13 @@
|
|
|
236
236
|
};
|
|
237
237
|
const groupNewCenter = { x: groupNewX + groupNewWidth / 2, y: groupNewY + groupNewHeight / 2 };
|
|
238
238
|
// calculate rotated points relative to new group center
|
|
239
|
-
const groupCenterRotated = rotatePoint(groupCenter, groupNewCenter
|
|
239
|
+
const groupCenterRotated = rotatePoint(-groupAngle, groupCenter, groupNewCenter);
|
|
240
240
|
|
|
241
241
|
const buildLayerDiffs = async ({ layer, state }: LayerStateWithoutGroup) => {
|
|
242
242
|
// do calculation with absolute values, but update state with relative values
|
|
243
243
|
const { width, height, x, y, rotate, scale } = layer;
|
|
244
244
|
const center = { x: x + (width * scale) / 2, y: y + (height * scale) / 2 };
|
|
245
|
-
const centerRotated = rotatePoint(center, groupNewCenter
|
|
245
|
+
const centerRotated = rotatePoint(-groupAngle, center, groupNewCenter);
|
|
246
246
|
const newCenterRotated = {
|
|
247
247
|
x: groupNewCenter.x + (centerRotated.x - groupCenterRotated.x) * widthScale,
|
|
248
248
|
y: groupNewCenter.y + (centerRotated.y - groupCenterRotated.y) * heightScale,
|
|
@@ -300,7 +300,7 @@
|
|
|
300
300
|
: Math.min(Math.max(newCenterRotated.y, minY), maxY);
|
|
301
301
|
}
|
|
302
302
|
|
|
303
|
-
let newCenter = rotatePoint(
|
|
303
|
+
let newCenter = rotatePoint(degToRad(bbox.rotate), newCenterRotated, groupNewCenter);
|
|
304
304
|
|
|
305
305
|
if (layer.type === 'image') {
|
|
306
306
|
const { offsetX, offsetY, image } = layer;
|
|
@@ -309,24 +309,24 @@
|
|
|
309
309
|
let newOffsetY = offsetY;
|
|
310
310
|
|
|
311
311
|
if (newSize.scaled === 'width') {
|
|
312
|
-
const maxOffsetDiff = Math.min(offsetX, image.width
|
|
312
|
+
const maxOffsetDiff = Math.min(offsetX, image.width + offsetX - width);
|
|
313
313
|
const maxWidth = (width + Math.max(maxOffsetDiff, 0) * 2) * scale;
|
|
314
314
|
if (newWidth > maxWidth) {
|
|
315
315
|
newScale *= newWidth / maxWidth;
|
|
316
|
-
newOffsetX = offsetX
|
|
317
|
-
newOffsetY
|
|
316
|
+
newOffsetX = offsetX + maxOffsetDiff;
|
|
317
|
+
newOffsetY -= ((layer.image.height / 2 + offsetY) * (newScale - scale)) / newScale;
|
|
318
318
|
} else {
|
|
319
|
-
newOffsetX += (
|
|
319
|
+
newOffsetX += (newWidth / newScale - width) / 2;
|
|
320
320
|
}
|
|
321
321
|
} else {
|
|
322
|
-
const maxOffsetDiff = Math.min(offsetY, image.height
|
|
322
|
+
const maxOffsetDiff = Math.min(offsetY, image.height + offsetY - height);
|
|
323
323
|
const maxHeight = (height + Math.max(maxOffsetDiff, 0) * 2) * scale;
|
|
324
324
|
if (newHeight > maxHeight) {
|
|
325
325
|
newScale *= newHeight / maxHeight;
|
|
326
|
-
newOffsetY = offsetY
|
|
327
|
-
newOffsetX
|
|
326
|
+
newOffsetY = offsetY + maxOffsetDiff;
|
|
327
|
+
newOffsetX -= ((layer.image.width / 2 + offsetX) * (newScale - scale)) / newScale;
|
|
328
328
|
} else {
|
|
329
|
-
newOffsetY += (
|
|
329
|
+
newOffsetY += (newHeight / newScale - height) / 2;
|
|
330
330
|
}
|
|
331
331
|
}
|
|
332
332
|
|
|
@@ -4,6 +4,12 @@
|
|
|
4
4
|
import { calculateGroupRotatedBoundingBox, type Transform } from '../../utils.js';
|
|
5
5
|
import type { Layer } from '../../../types.js';
|
|
6
6
|
|
|
7
|
+
interface Props {
|
|
8
|
+
pageRef: HTMLDivElement;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const { pageRef }: Props = $props();
|
|
12
|
+
|
|
7
13
|
const type = 'rotate';
|
|
8
14
|
|
|
9
15
|
const editor = getEditorContext();
|
|
@@ -88,16 +94,16 @@
|
|
|
88
94
|
const onMouseMove = (e: MouseEvent) => {
|
|
89
95
|
if (!initial || editor.activeAction?.id !== actionId || !element) return;
|
|
90
96
|
|
|
91
|
-
const
|
|
92
|
-
if (!
|
|
97
|
+
const pageRect = pageRef.getBoundingClientRect();
|
|
98
|
+
if (!pageRect) return;
|
|
93
99
|
|
|
94
|
-
const { x: pageX, y: pageY } =
|
|
100
|
+
const { x: pageX, y: pageY } = pageRect;
|
|
95
101
|
|
|
96
102
|
const { width, height, x, y, rotate, scale } = initial.bbox;
|
|
97
103
|
|
|
98
104
|
// Calculate the center of the layer
|
|
99
|
-
const clientX = pageX + x
|
|
100
|
-
const clientY = pageY + y
|
|
105
|
+
const clientX = pageX + (x + (width * scale) / 2) * editor.zoom;
|
|
106
|
+
const clientY = pageY + (y + (height * scale) / 2) * editor.zoom;
|
|
101
107
|
|
|
102
108
|
// Calculate the current angle based on the current mouse position relative to the center of the layer
|
|
103
109
|
const currentAngle = Math.atan2(
|
|
@@ -174,7 +180,7 @@
|
|
|
174
180
|
onmousedown={onMouseDown}
|
|
175
181
|
></div>
|
|
176
182
|
<div
|
|
177
|
-
class="
|
|
183
|
+
class="flex h-6 w-6 items-center justify-center rounded-full bg-white transition-colors group-hover:bg-primary group-hover:text-primary-foreground group-active:bg-primary group-active:text-primary-foreground"
|
|
178
184
|
style:box-shadow="0 0 4px 1px rgba(57,76,96,.15), 0 0 0 1px rgba(43,59,74,.3)"
|
|
179
185
|
>
|
|
180
186
|
<RotateIcon class="h-4 w-4" />
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
interface Props {
|
|
2
|
+
pageRef: HTMLDivElement;
|
|
3
|
+
}
|
|
4
|
+
declare const RotateControl: import("svelte").Component<Props, {}, "">;
|
|
2
5
|
type RotateControl = ReturnType<typeof RotateControl>;
|
|
3
6
|
export default RotateControl;
|
package/dist/components/editor/layers/controls/side-resize-control/side-resize-control.svelte
CHANGED
|
@@ -146,13 +146,13 @@
|
|
|
146
146
|
const snapping = 10;
|
|
147
147
|
if ((origin === 'left' && !flipX) || (origin === 'right' && flipX)) {
|
|
148
148
|
newWidth = Math.max(newWidth + adjustedXDiff, 10);
|
|
149
|
-
const maxWidth = layer.image.width
|
|
149
|
+
const maxWidth = layer.image.width + offsetX;
|
|
150
150
|
const maxWidthDiff = maxWidth * scale - newWidth;
|
|
151
151
|
if (Math.abs(maxWidthDiff) < snapping) {
|
|
152
152
|
newWidth = maxWidth * scale;
|
|
153
153
|
} else if (maxWidthDiff < 0) {
|
|
154
154
|
newScale = newWidth / maxWidth;
|
|
155
|
-
newOffsetY
|
|
155
|
+
newOffsetY -= ((layer.image.height / 2 + offsetY) * (newScale - scale)) / newScale;
|
|
156
156
|
}
|
|
157
157
|
} else if ((origin === 'top' && !flipY) || (origin === 'bottom' && flipY)) {
|
|
158
158
|
newHeight = Math.max(newHeight + adjustedYDiff, 10);
|
|
@@ -162,31 +162,31 @@
|
|
|
162
162
|
newHeight = maxHeight * scale;
|
|
163
163
|
} else if (maxHeightDiff < 0) {
|
|
164
164
|
newScale = newHeight / maxHeight;
|
|
165
|
-
newOffsetX
|
|
165
|
+
newOffsetX -= ((layer.image.width / 2 + offsetX) * (newScale - scale)) / newScale;
|
|
166
166
|
}
|
|
167
167
|
} else if ((origin === 'right' && !flipX) || (origin === 'left' && flipX)) {
|
|
168
168
|
const adjustedDiff = Math.min(adjustedXDiff, newWidth - 10);
|
|
169
169
|
newWidth -= adjustedDiff;
|
|
170
|
-
newOffsetX
|
|
170
|
+
newOffsetX -= adjustedDiff / scale;
|
|
171
171
|
if (Math.abs(newOffsetX * scale) < snapping) {
|
|
172
172
|
newOffsetX = 0;
|
|
173
|
-
newWidth = (width
|
|
174
|
-
} else if (newOffsetX
|
|
173
|
+
newWidth = (width - offsetX) * scale;
|
|
174
|
+
} else if (newOffsetX > 0) {
|
|
175
175
|
newOffsetX = 0;
|
|
176
|
-
newScale = newWidth / (width
|
|
177
|
-
newOffsetY
|
|
176
|
+
newScale = newWidth / (width - offsetX);
|
|
177
|
+
newOffsetY -= ((layer.image.height / 2 + offsetY) * (newScale - scale)) / newScale;
|
|
178
178
|
}
|
|
179
179
|
} else if ((origin === 'bottom' && !flipY) || (origin === 'top' && flipY)) {
|
|
180
180
|
const adjustedDiff = Math.min(adjustedYDiff, newHeight - 10);
|
|
181
181
|
newHeight -= adjustedDiff;
|
|
182
|
-
newOffsetY
|
|
182
|
+
newOffsetY -= adjustedDiff / scale;
|
|
183
183
|
if (Math.abs(newOffsetY * scale) < snapping) {
|
|
184
184
|
newOffsetY = 0;
|
|
185
|
-
newHeight = (height
|
|
186
|
-
} else if (newOffsetY
|
|
185
|
+
newHeight = (height - offsetY) * scale;
|
|
186
|
+
} else if (newOffsetY > 0) {
|
|
187
187
|
newOffsetY = 0;
|
|
188
|
-
newScale = newHeight / (height
|
|
189
|
-
newOffsetX
|
|
188
|
+
newScale = newHeight / (height - offsetY);
|
|
189
|
+
newOffsetX -= ((layer.image.width / 2 + offsetX) * (newScale - scale)) / newScale;
|
|
190
190
|
}
|
|
191
191
|
}
|
|
192
192
|
|
|
@@ -146,7 +146,7 @@
|
|
|
146
146
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
147
147
|
<div class={cn(handler(), cursor())} onmousedown={onMouseDown}></div>
|
|
148
148
|
<div
|
|
149
|
-
class="
|
|
149
|
+
class="h-4 w-1.5 rounded-sm bg-white transition-colors group-hover:bg-primary group-active:bg-primary"
|
|
150
150
|
style:box-shadow="0 0 4px 1px rgba(57,76,96,.15), 0 0 0 1px rgba(43,59,74,.3)"
|
|
151
151
|
></div>
|
|
152
152
|
</div>
|
|
@@ -5,6 +5,6 @@ import ActiveBackgroundBorder from './active-background-border.svelte';
|
|
|
5
5
|
import ActiveLayerBorder from './active-layer-border.svelte';
|
|
6
6
|
import { BackgroundLayer, BackgroundLayerContent, BackgroundLayerButtons } from './types/background/index.js';
|
|
7
7
|
import { TextLayer, TextLayerContent, HtmlContent, TextLayerEdit } from './types/text/index.js';
|
|
8
|
-
import { ImageLayer, ImageLayerContent } from './types/image/index.js';
|
|
8
|
+
import { ImageLayer, ImageLayerContent, ImageLayerCrop } from './types/image/index.js';
|
|
9
9
|
import { GroupLayer, GroupLayerContent } from './types/group/index.js';
|
|
10
|
-
export { LayerButton, LayerThumbWrapper, LayerWrapper, BackgroundLayer, BackgroundLayerContent, BackgroundLayerButtons, TextLayer, TextLayerContent, HtmlContent, TextLayerEdit, ImageLayer, ImageLayerContent, GroupLayer, GroupLayerContent, ActiveBackgroundBorder, ActiveLayerBorder, };
|
|
10
|
+
export { LayerButton, LayerThumbWrapper, LayerWrapper, BackgroundLayer, BackgroundLayerContent, BackgroundLayerButtons, TextLayer, TextLayerContent, HtmlContent, TextLayerEdit, ImageLayer, ImageLayerContent, ImageLayerCrop, GroupLayer, GroupLayerContent, ActiveBackgroundBorder, ActiveLayerBorder, };
|
|
@@ -5,7 +5,7 @@ import ActiveBackgroundBorder from './active-background-border.svelte';
|
|
|
5
5
|
import ActiveLayerBorder from './active-layer-border.svelte';
|
|
6
6
|
import { BackgroundLayer, BackgroundLayerContent, BackgroundLayerButtons, } from './types/background/index.js';
|
|
7
7
|
import { TextLayer, TextLayerContent, HtmlContent, TextLayerEdit } from './types/text/index.js';
|
|
8
|
-
import { ImageLayer, ImageLayerContent } from './types/image/index.js';
|
|
8
|
+
import { ImageLayer, ImageLayerContent, ImageLayerCrop } from './types/image/index.js';
|
|
9
9
|
import { GroupLayer, GroupLayerContent } from './types/group/index.js';
|
|
10
10
|
export {
|
|
11
11
|
// layer buttons
|
|
@@ -17,7 +17,7 @@ BackgroundLayer, BackgroundLayerContent, BackgroundLayerButtons,
|
|
|
17
17
|
// text layer
|
|
18
18
|
TextLayer, TextLayerContent, HtmlContent, TextLayerEdit,
|
|
19
19
|
//image layer
|
|
20
|
-
ImageLayer, ImageLayerContent,
|
|
20
|
+
ImageLayer, ImageLayerContent, ImageLayerCrop,
|
|
21
21
|
// group layer
|
|
22
22
|
GroupLayer, GroupLayerContent,
|
|
23
23
|
// layers state
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
<button
|
|
34
34
|
{onclick}
|
|
35
35
|
class={cn(
|
|
36
|
-
'
|
|
36
|
+
'flex w-full items-center justify-between rounded-md border-2 border-transparent bg-muted p-2 outline-none',
|
|
37
37
|
{
|
|
38
38
|
'border-blue-500': layer
|
|
39
39
|
? editor.activeLayers.find((l) => l.id === layer.id)
|
|
@@ -17,25 +17,20 @@
|
|
|
17
17
|
);
|
|
18
18
|
</script>
|
|
19
19
|
|
|
20
|
-
<div
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
crossorigin="anonymous"
|
|
36
|
-
class="pointer-events-none absolute h-full w-full object-fill"
|
|
37
|
-
draggable={false}
|
|
38
|
-
/>
|
|
39
|
-
</div>
|
|
20
|
+
<div class="h-full w-full transition-transform" style:transform={flipTransform}>
|
|
21
|
+
<div
|
|
22
|
+
class="relative"
|
|
23
|
+
style:width="{backgroundImage.image.width * scale}px"
|
|
24
|
+
style:height="{backgroundImage.image.height * scale}px"
|
|
25
|
+
style:transform={`translate(${backgroundImage.offsetX * scale}px, ${backgroundImage.offsetY * scale}px) rotate(${backgroundImage.imageRotate}deg)`}
|
|
26
|
+
style:opacity={backgroundImage.opacity}
|
|
27
|
+
>
|
|
28
|
+
<img
|
|
29
|
+
src={backgroundImage.image.src}
|
|
30
|
+
alt=""
|
|
31
|
+
crossorigin="anonymous"
|
|
32
|
+
class="pointer-events-none absolute h-full w-full object-fill"
|
|
33
|
+
draggable={false}
|
|
34
|
+
/>
|
|
40
35
|
</div>
|
|
41
36
|
</div>
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
active={editor.activeSidebarPopup === 'backgroundColor'}
|
|
21
21
|
>
|
|
22
22
|
<div
|
|
23
|
-
class="
|
|
23
|
+
class="relative h-6 w-6 overflow-hidden rounded-full after:absolute after:inset-0 after:rounded-full after:shadow-inner-1"
|
|
24
24
|
>
|
|
25
25
|
<ColorPickerAlphaGrid size={12}>
|
|
26
26
|
<ColorIndicator color={backgroundColor} />
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
hidden: false,
|
|
45
45
|
customScale: scale,
|
|
46
46
|
sticky: false,
|
|
47
|
-
x: rect.left + editor.dragged.offsetX
|
|
48
|
-
y: rect.top + editor.dragged.offsetY
|
|
47
|
+
x: rect.left + editor.dragged.offsetX + offsetX * scale,
|
|
48
|
+
y: rect.top + editor.dragged.offsetY + offsetY * scale,
|
|
49
49
|
};
|
|
50
50
|
} else {
|
|
51
51
|
editor.dragged = { ...editor.dragged, inside: true };
|