@dosgato/dialog 0.0.33 → 0.0.35
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/Button.svelte.d.ts +7 -7
- package/ButtonGroup.svelte.d.ts +5 -5
- package/Checkbox.svelte.d.ts +4 -4
- package/Container.svelte.d.ts +2 -2
- package/Dialog.svelte +3 -2
- package/Dialog.svelte.d.ts +14 -14
- package/FieldAutocomplete.svelte +7 -2
- package/FieldAutocomplete.svelte.d.ts +9 -9
- package/FieldCheckbox.svelte.d.ts +4 -4
- package/FieldChoices.svelte.d.ts +6 -6
- package/FieldChooserLink.svelte +7 -2
- package/FieldChooserLink.svelte.d.ts +8 -8
- package/FieldCodeEditor.svelte +2 -2
- package/FieldCodeEditor.svelte.d.ts +7 -7
- package/FieldDate.svelte.d.ts +4 -4
- package/FieldDateTime.svelte.d.ts +3 -3
- package/FieldDualListbox.svelte.d.ts +6 -6
- package/FieldHidden.svelte +1 -1
- package/FieldHidden.svelte.d.ts +2 -2
- package/FieldMultiple.svelte.d.ts +7 -7
- package/FieldMultiselect.svelte +5 -1
- package/FieldMultiselect.svelte.d.ts +5 -5
- package/FieldNumber.svelte.d.ts +5 -5
- package/FieldRadio.svelte.d.ts +13 -13
- package/FieldSelect.svelte.d.ts +15 -15
- package/FieldStandard.svelte +2 -1
- package/FieldStandard.svelte.d.ts +11 -10
- package/FieldText.svelte +1 -1
- package/FieldText.svelte.d.ts +8 -8
- package/FieldTextArea.svelte +1 -1
- package/FieldTextArea.svelte.d.ts +6 -6
- package/FileIcon.svelte.d.ts +5 -5
- package/Form.svelte +3 -3
- package/Form.svelte.d.ts +5 -5
- package/FormDialog.svelte +2 -2
- package/FormDialog.svelte.d.ts +24 -21
- package/Icon.svelte.d.ts +5 -5
- package/Input.svelte.d.ts +8 -8
- package/Listbox.svelte +2 -2
- package/Listbox.svelte.d.ts +6 -6
- package/Radio.svelte.d.ts +4 -4
- package/Switcher.svelte.d.ts +11 -11
- package/TabStore.d.ts +3 -3
- package/TabStore.js +7 -6
- package/Tabs.svelte +6 -6
- package/Tabs.svelte.d.ts +2 -2
- package/chooser/Chooser.svelte +4 -3
- package/chooser/Chooser.svelte.d.ts +12 -12
- package/chooser/ChooserStore.d.ts +4 -4
- package/chooser/ChooserStore.js +3 -3
- package/colorpicker/FieldColorPicker.svelte.d.ts +5 -5
- package/cropper/FieldCropper.svelte +219 -0
- package/cropper/FieldCropper.svelte.d.ts +24 -0
- package/cropper/cropper.d.ts +78 -0
- package/cropper/cropper.js +220 -0
- package/cropper/index.d.ts +2 -0
- package/cropper/index.js +2 -0
- package/helpers.d.ts +1 -1
- package/iconpicker/FieldIconPicker.svelte +5 -5
- package/iconpicker/FieldIconPicker.svelte.d.ts +3 -3
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/package.json +13 -14
- package/tree/LoadIcon.svelte.d.ts +2 -2
- package/tree/Tree.svelte.d.ts +11 -11
- package/tree/TreeNode.svelte +4 -1
- package/tree/TreeNode.svelte.d.ts +4 -4
- package/tree/treestore.d.ts +16 -16
- package/tree/treestore.js +3 -3
- package/imagecropper/FieldImageCropper.svelte +0 -380
- package/imagecropper/FieldImageCropper.svelte.d.ts +0 -25
- package/imagecropper/ImageCropperStore.d.ts +0 -16
- package/imagecropper/ImageCropperStore.js +0 -104
- package/imagecropper/imagecropper.d.ts +0 -21
- package/imagecropper/imagecropper.js +0 -1
- package/imagecropper/index.d.ts +0 -1
- package/imagecropper/index.js +0 -1
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
<script>import { resize, ScreenReaderOnly } from '@txstate-mws/svelte-components';
|
|
2
|
+
import { isNotBlank, randomid } from 'txstate-utils';
|
|
3
|
+
import FieldStandard from '../FieldStandard.svelte';
|
|
4
|
+
import { CropperStore } from './cropper';
|
|
5
|
+
export let id = undefined;
|
|
6
|
+
export let path;
|
|
7
|
+
export let imageSrc;
|
|
8
|
+
export let selectionAspectRatio = 1;
|
|
9
|
+
export let minSelection = 0; // percentage of image, a value 0-1
|
|
10
|
+
export let label = '';
|
|
11
|
+
export let required = false;
|
|
12
|
+
export let conditional = undefined;
|
|
13
|
+
export let helptext = undefined;
|
|
14
|
+
const store = new CropperStore({ width: 0, height: 0, minSelection, targetAspect: selectionAspectRatio });
|
|
15
|
+
const { output, selection } = store;
|
|
16
|
+
let setVal;
|
|
17
|
+
let value;
|
|
18
|
+
function init(spValue, spSetVal) {
|
|
19
|
+
setVal = spSetVal;
|
|
20
|
+
value = spValue;
|
|
21
|
+
}
|
|
22
|
+
$: store.setOutput(value);
|
|
23
|
+
$: setVal?.($output);
|
|
24
|
+
let rect;
|
|
25
|
+
function updateRect(..._) {
|
|
26
|
+
if (!container)
|
|
27
|
+
return false;
|
|
28
|
+
rect = container.getBoundingClientRect();
|
|
29
|
+
store.updateDimensions(rect.width, rect.height);
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
function isInside(clientX, clientY) {
|
|
33
|
+
return !(clientX < rect.left || clientX > rect.right || clientY > rect.bottom || clientY < rect.top);
|
|
34
|
+
}
|
|
35
|
+
function relativeToRect(clientX, clientY) {
|
|
36
|
+
return [Math.min(Math.max(0, clientX - rect.left), rect.width), Math.min(Math.max(0, clientY - rect.top), rect.height)];
|
|
37
|
+
}
|
|
38
|
+
let container;
|
|
39
|
+
function onMouseDown(e) {
|
|
40
|
+
if (!updateRect())
|
|
41
|
+
return;
|
|
42
|
+
if (e instanceof TouchEvent && e.touches.length > 1)
|
|
43
|
+
return;
|
|
44
|
+
const clientX = e instanceof MouseEvent ? e.clientX : e.touches[0].clientX;
|
|
45
|
+
const clientY = e instanceof MouseEvent ? e.clientY : e.touches[0].clientY;
|
|
46
|
+
if (isInside(clientX, clientY)) {
|
|
47
|
+
e.preventDefault();
|
|
48
|
+
store.startDrag(clientX - rect.left, clientY - rect.top);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function onMouseMove(e) {
|
|
52
|
+
if (!updateRect())
|
|
53
|
+
return;
|
|
54
|
+
if (e instanceof TouchEvent && e.touches.length > 1)
|
|
55
|
+
return;
|
|
56
|
+
const clientX = e instanceof MouseEvent ? e.clientX : e.touches[0].clientX;
|
|
57
|
+
const clientY = e instanceof MouseEvent ? e.clientY : e.touches[0].clientY;
|
|
58
|
+
if (e instanceof MouseEvent && !e.buttons && $store.drag)
|
|
59
|
+
store.endDrag();
|
|
60
|
+
if (isInside(clientX, clientY) || $store.drag)
|
|
61
|
+
store.mouseMove(...relativeToRect(clientX, clientY));
|
|
62
|
+
}
|
|
63
|
+
function onMouseUp(e) {
|
|
64
|
+
if (!updateRect())
|
|
65
|
+
return;
|
|
66
|
+
store.endDrag();
|
|
67
|
+
const clientX = e instanceof MouseEvent ? e.clientX : e.changedTouches[0].clientX;
|
|
68
|
+
const clientY = e instanceof MouseEvent ? e.clientY : e.changedTouches[0].clientY;
|
|
69
|
+
if (isInside(clientX, clientY)) {
|
|
70
|
+
store.mouseMove(...relativeToRect(clientX, clientY));
|
|
71
|
+
container?.querySelector('.selectionHilite')?.focus();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function onMaximize() {
|
|
75
|
+
if (!updateRect())
|
|
76
|
+
return;
|
|
77
|
+
store.maximize();
|
|
78
|
+
}
|
|
79
|
+
function onKeyDown(type) {
|
|
80
|
+
return (e) => {
|
|
81
|
+
const tl = type === 'tl';
|
|
82
|
+
const tr = type === 'tr';
|
|
83
|
+
const bl = type === 'bl';
|
|
84
|
+
const br = type === 'br';
|
|
85
|
+
const left = e.key === 'ArrowLeft';
|
|
86
|
+
const right = e.key === 'ArrowRight';
|
|
87
|
+
const up = e.key === 'ArrowUp';
|
|
88
|
+
const down = e.key === 'ArrowDown';
|
|
89
|
+
if (left || right || up || down) {
|
|
90
|
+
e.preventDefault();
|
|
91
|
+
e.stopPropagation();
|
|
92
|
+
}
|
|
93
|
+
else
|
|
94
|
+
return;
|
|
95
|
+
const step = e.shiftKey ? (e.altKey || e.metaKey ? 80 : 20) : (e.altKey || e.metaKey ? 40 : 1);
|
|
96
|
+
if (type === 'move') {
|
|
97
|
+
store.move(left ? -1 * step : (right ? step : 0), up ? -1 * step : (down ? step : 0));
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
store.expand(type, ((tl || bl) && right) || ((tl || tr) && down) || ((tr || br) && left) || ((bl || br) && up) ? -1 * step : step);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
$: updateRect(container);
|
|
105
|
+
const descid = randomid();
|
|
106
|
+
const movedescid = randomid();
|
|
107
|
+
let focusWithin = false;
|
|
108
|
+
</script>
|
|
109
|
+
|
|
110
|
+
<svelte:window on:mousemove={onMouseMove} on:mouseup={onMouseUp} on:touchend={onMouseUp} on:touchcancel={onMouseUp} />
|
|
111
|
+
<FieldStandard bind:id {label} {path} {required} {conditional} {helptext} {descid} let:value let:setVal let:helptextid>
|
|
112
|
+
{@const _ = init(value, setVal)}
|
|
113
|
+
{#if isNotBlank(imageSrc)}
|
|
114
|
+
<div on:focusin={() => { focusWithin = true }} on:focusout={() => { focusWithin = false }}>
|
|
115
|
+
<div bind:this={container} use:resize on:resize={() => updateRect()} class="crop-image-container" on:mousedown={onMouseDown} on:touchstart={onMouseDown} on:touchmove={onMouseMove} style:cursor={$store.cursor}>
|
|
116
|
+
<img class="crop-image" src={imageSrc} alt="" />
|
|
117
|
+
{#if $selection && $output}
|
|
118
|
+
<div class='crop-bg'>
|
|
119
|
+
<img class='crop-image clipped' src={imageSrc} alt="" style:clip-path="polygon({$output.left}% {$output.top}%, {100 - $output.right}% {$output.top}%, {100 - $output.right}% {100 - $output.bottom}%, {$output.left}% {100 - $output.bottom}%, {$output.left}% {$output.top}%)" />
|
|
120
|
+
</div>
|
|
121
|
+
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
|
122
|
+
<div class='selectionHilite' {id} tabindex="0" on:keydown={onKeyDown('move')}
|
|
123
|
+
aria-labelledby={descid}
|
|
124
|
+
aria-describedby="{movedescid} {helptextid ?? ''}"
|
|
125
|
+
style:left="{$selection.left}px"
|
|
126
|
+
style:top="{$selection.top}px"
|
|
127
|
+
style:width="{$selection.right - $selection.left}px"
|
|
128
|
+
style:height="{$selection.bottom - $selection.top}px"
|
|
129
|
+
>
|
|
130
|
+
<ScreenReaderOnly id={movedescid}>arrows move crop selection, hold shift and/or cmd/alt for bigger steps</ScreenReaderOnly>
|
|
131
|
+
{#if focusWithin}
|
|
132
|
+
<ScreenReaderOnly arialive="polite">top left x y coordinate is ({Math.round($selection.left)}, {Math.round($selection.top)}) bottom right x y coordinate is ({Math.round($selection.right)}, {Math.round($selection.bottom)})</ScreenReaderOnly>
|
|
133
|
+
<ScreenReaderOnly arialive="polite">crop area is {Math.round($store.width)} pixels wide by {Math.round($store.height)} pixels tall</ScreenReaderOnly>
|
|
134
|
+
{/if}
|
|
135
|
+
<div class='selectionCorner tl'
|
|
136
|
+
tabindex="0"
|
|
137
|
+
on:keydown={onKeyDown('tl')}
|
|
138
|
+
>
|
|
139
|
+
<ScreenReaderOnly>arrows adjust crop size, bottom right is fixed, hold shift and/or cmd/alt for bigger steps</ScreenReaderOnly>
|
|
140
|
+
</div>
|
|
141
|
+
<div class='selectionCorner tr'
|
|
142
|
+
tabindex="0"
|
|
143
|
+
on:keydown={onKeyDown('tr')}
|
|
144
|
+
>
|
|
145
|
+
<ScreenReaderOnly>arrows adjust crop size, bottom left is fixed, hold shift and/or cmd/alt for bigger steps</ScreenReaderOnly>
|
|
146
|
+
</div>
|
|
147
|
+
<div class='selectionCorner bl'
|
|
148
|
+
tabindex="0"
|
|
149
|
+
on:keydown={onKeyDown('bl')}
|
|
150
|
+
>
|
|
151
|
+
<ScreenReaderOnly>arrows adjust crop size, top right is fixed, hold shift and/or cmd/alt for bigger steps</ScreenReaderOnly>
|
|
152
|
+
</div>
|
|
153
|
+
<div class='selectionCorner br'
|
|
154
|
+
tabindex="0"
|
|
155
|
+
on:keydown={onKeyDown('br')}
|
|
156
|
+
>
|
|
157
|
+
<ScreenReaderOnly>arrows adjust crop size, top left is fixed, hold shift and/or cmd/alt for bigger steps</ScreenReaderOnly>
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
160
|
+
{/if}
|
|
161
|
+
</div>
|
|
162
|
+
<div class="action-buttons">
|
|
163
|
+
<button type="button" class='btn-center-max' on:click={onMaximize}>Center and Maximize</button>
|
|
164
|
+
<button type="button" class='btn-clear' on:click={() => store.reset()}>Clear</button>
|
|
165
|
+
</div>
|
|
166
|
+
<div class="cropper-instructions">
|
|
167
|
+
Click and drag to select a section of your image to use.
|
|
168
|
+
</div>
|
|
169
|
+
</div>
|
|
170
|
+
{:else}
|
|
171
|
+
Image not selected
|
|
172
|
+
{/if}
|
|
173
|
+
</FieldStandard>
|
|
174
|
+
|
|
175
|
+
<style>
|
|
176
|
+
.crop-image-container {
|
|
177
|
+
position: relative;
|
|
178
|
+
user-select: none;
|
|
179
|
+
}
|
|
180
|
+
.crop-image {
|
|
181
|
+
width: 100%;
|
|
182
|
+
display: block;
|
|
183
|
+
user-select: none;
|
|
184
|
+
}
|
|
185
|
+
.crop-bg {
|
|
186
|
+
position: absolute;
|
|
187
|
+
top: 0;
|
|
188
|
+
left: 0;
|
|
189
|
+
width: 100%;
|
|
190
|
+
height: 100%;
|
|
191
|
+
background-color: rgba(0, 0, 0, 0.8);
|
|
192
|
+
}
|
|
193
|
+
.selectionHilite {
|
|
194
|
+
position: absolute;
|
|
195
|
+
border: 1px dashed white;
|
|
196
|
+
}
|
|
197
|
+
.selectionCorner {
|
|
198
|
+
position: absolute;
|
|
199
|
+
width: 10px;
|
|
200
|
+
height: 10px;
|
|
201
|
+
}
|
|
202
|
+
.selectionCorner.tl {
|
|
203
|
+
top: 0;
|
|
204
|
+
left: 0;
|
|
205
|
+
}
|
|
206
|
+
.selectionCorner.tr {
|
|
207
|
+
top: 0;
|
|
208
|
+
right: 0;
|
|
209
|
+
}
|
|
210
|
+
.selectionCorner.bl {
|
|
211
|
+
bottom: 0;
|
|
212
|
+
left: 0;
|
|
213
|
+
}
|
|
214
|
+
.selectionCorner.br {
|
|
215
|
+
bottom: 0;
|
|
216
|
+
right: 0;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
</style>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { SvelteComponentTyped } from "svelte";
|
|
2
|
+
declare const __propDef: {
|
|
3
|
+
props: {
|
|
4
|
+
id?: string | undefined;
|
|
5
|
+
path: string;
|
|
6
|
+
imageSrc: string;
|
|
7
|
+
selectionAspectRatio?: number | undefined;
|
|
8
|
+
minSelection?: number | undefined;
|
|
9
|
+
label?: string | undefined;
|
|
10
|
+
required?: boolean | undefined;
|
|
11
|
+
conditional?: boolean | undefined;
|
|
12
|
+
helptext?: string | undefined;
|
|
13
|
+
};
|
|
14
|
+
events: {
|
|
15
|
+
[evt: string]: CustomEvent<any>;
|
|
16
|
+
};
|
|
17
|
+
slots: {};
|
|
18
|
+
};
|
|
19
|
+
export type FieldCropperProps = typeof __propDef.props;
|
|
20
|
+
export type FieldCropperEvents = typeof __propDef.events;
|
|
21
|
+
export type FieldCropperSlots = typeof __propDef.slots;
|
|
22
|
+
export default class FieldCropper extends SvelteComponentTyped<FieldCropperProps, FieldCropperEvents, FieldCropperSlots> {
|
|
23
|
+
}
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Store } from '@txstate-mws/svelte-store';
|
|
2
|
+
export interface CropOutput {
|
|
3
|
+
left: number;
|
|
4
|
+
right: number;
|
|
5
|
+
top: number;
|
|
6
|
+
bottom: number;
|
|
7
|
+
}
|
|
8
|
+
export interface CropSelectionPx {
|
|
9
|
+
left: number;
|
|
10
|
+
top: number;
|
|
11
|
+
right: number;
|
|
12
|
+
bottom: number;
|
|
13
|
+
}
|
|
14
|
+
export interface ICropperStore {
|
|
15
|
+
width: number;
|
|
16
|
+
height: number;
|
|
17
|
+
targetAspect: number;
|
|
18
|
+
minSelection: number;
|
|
19
|
+
selection?: CropOutput;
|
|
20
|
+
cursor: 'crosshair' | 'move' | 'ew-resize' | 'ns-resize' | 'nesw-resize' | 'nwse-resize' | 'grabbing';
|
|
21
|
+
drag?: {
|
|
22
|
+
start: {
|
|
23
|
+
x: number;
|
|
24
|
+
y: number;
|
|
25
|
+
};
|
|
26
|
+
selection?: CropSelectionPx;
|
|
27
|
+
edge: {
|
|
28
|
+
left: boolean;
|
|
29
|
+
right: boolean;
|
|
30
|
+
top: boolean;
|
|
31
|
+
bottom: boolean;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export declare class CropperStore extends Store<ICropperStore> {
|
|
36
|
+
selection: import("@txstate-mws/svelte-store").DerivedStore<{
|
|
37
|
+
left: number;
|
|
38
|
+
right: number;
|
|
39
|
+
top: number;
|
|
40
|
+
bottom: number;
|
|
41
|
+
} | undefined, ICropperStore>;
|
|
42
|
+
output: import("@txstate-mws/svelte-store").DerivedStore<CropOutput | undefined, ICropperStore>;
|
|
43
|
+
constructor(initialValue: Omit<ICropperStore, 'cursor'>);
|
|
44
|
+
setOutput(selection?: CropOutput): void;
|
|
45
|
+
updateDimensions(width: number, height: number): void;
|
|
46
|
+
/**
|
|
47
|
+
* The svelte component is responsible for making sure x and y are relative to the drawing area,
|
|
48
|
+
* e.g. (0, 0) is the top left corner of the drawing area
|
|
49
|
+
*
|
|
50
|
+
* negative coordinates are not allowed
|
|
51
|
+
*/
|
|
52
|
+
startDrag(x: number, y: number): void;
|
|
53
|
+
endDrag(): void;
|
|
54
|
+
reset(): void;
|
|
55
|
+
maximize(): void;
|
|
56
|
+
convertToPct(sel: CropSelectionPx | undefined, width: number, height: number): {
|
|
57
|
+
left: number;
|
|
58
|
+
right: number;
|
|
59
|
+
top: number;
|
|
60
|
+
bottom: number;
|
|
61
|
+
} | undefined;
|
|
62
|
+
convertToPx(output: CropOutput | undefined, width: number, height: number): {
|
|
63
|
+
left: number;
|
|
64
|
+
right: number;
|
|
65
|
+
top: number;
|
|
66
|
+
bottom: number;
|
|
67
|
+
} | undefined;
|
|
68
|
+
determineSelection(startX: number, startY: number, x: number, y: number, v: ICropperStore): CropSelectionPx;
|
|
69
|
+
/**
|
|
70
|
+
* The svelte component is responsible for making sure x and y are relative to the drawing area,
|
|
71
|
+
* e.g. (0, 0) is the top left corner of the drawing area
|
|
72
|
+
*
|
|
73
|
+
* negative coordinates are not allowed
|
|
74
|
+
*/
|
|
75
|
+
mouseMove(x: number, y: number): void;
|
|
76
|
+
move(dx: number, dy: number): void;
|
|
77
|
+
expand(type: 'tl' | 'tr' | 'bl' | 'br', by: number): void;
|
|
78
|
+
}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { Store, derivedStore } from '@txstate-mws/svelte-store';
|
|
2
|
+
import { clone } from 'txstate-utils';
|
|
3
|
+
const edgeSensitivity = 4;
|
|
4
|
+
export class CropperStore extends Store {
|
|
5
|
+
// coordinates of the selection edges relative to the top left of the draw area, in pixels
|
|
6
|
+
// e.g. { left: 0, right: 10 } means the selection is 10 pixels wide
|
|
7
|
+
selection = derivedStore(this, v => {
|
|
8
|
+
return this.convertToPx(v.selection, v.width, v.height);
|
|
9
|
+
});
|
|
10
|
+
output = derivedStore(this, 'selection');
|
|
11
|
+
constructor(initialValue) {
|
|
12
|
+
super({ ...initialValue, cursor: 'crosshair' });
|
|
13
|
+
}
|
|
14
|
+
setOutput(selection) {
|
|
15
|
+
this.update(v => ({ ...v, selection }));
|
|
16
|
+
}
|
|
17
|
+
updateDimensions(width, height) {
|
|
18
|
+
this.update(v => ({ ...v, width, height }));
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The svelte component is responsible for making sure x and y are relative to the drawing area,
|
|
22
|
+
* e.g. (0, 0) is the top left corner of the drawing area
|
|
23
|
+
*
|
|
24
|
+
* negative coordinates are not allowed
|
|
25
|
+
*/
|
|
26
|
+
startDrag(x, y) {
|
|
27
|
+
this.update(v => {
|
|
28
|
+
const r = clone(v);
|
|
29
|
+
const selection = this.convertToPx(r.selection, r.width, r.height);
|
|
30
|
+
r.drag = { start: { x, y }, edge: { left: false, top: false, right: false, bottom: false } };
|
|
31
|
+
if (selection) {
|
|
32
|
+
r.drag.edge = {
|
|
33
|
+
left: Math.abs(x - selection.left) < edgeSensitivity,
|
|
34
|
+
top: Math.abs(y - selection.top) < edgeSensitivity,
|
|
35
|
+
right: Math.abs(selection.right - x) < edgeSensitivity,
|
|
36
|
+
bottom: Math.abs(selection.bottom - y) < edgeSensitivity
|
|
37
|
+
};
|
|
38
|
+
if ((r.drag.edge.left && r.drag.edge.top) ||
|
|
39
|
+
(r.drag.edge.top && r.drag.edge.right) ||
|
|
40
|
+
(r.drag.edge.right && r.drag.edge.bottom) ||
|
|
41
|
+
(r.drag.edge.bottom && r.drag.edge.left) ||
|
|
42
|
+
(x > selection.left && x < selection.right &&
|
|
43
|
+
y > selection.top && y < selection.bottom))
|
|
44
|
+
r.drag.selection = selection;
|
|
45
|
+
}
|
|
46
|
+
return r;
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
endDrag() {
|
|
50
|
+
this.update(v => ({ ...v, drag: undefined }));
|
|
51
|
+
}
|
|
52
|
+
reset() {
|
|
53
|
+
this.update(v => ({ ...v, selection: undefined }));
|
|
54
|
+
}
|
|
55
|
+
maximize() {
|
|
56
|
+
this.update(v => {
|
|
57
|
+
const ar = v.width / v.height;
|
|
58
|
+
const s = {};
|
|
59
|
+
if (ar > v.targetAspect) { // draw area is wider than aspect, fill height
|
|
60
|
+
s.left = 100 * (ar - v.targetAspect) / (2 * ar);
|
|
61
|
+
s.right = s.left;
|
|
62
|
+
s.top = 0;
|
|
63
|
+
s.bottom = 0;
|
|
64
|
+
}
|
|
65
|
+
else { // draw area is taller than aspect, fill width
|
|
66
|
+
s.left = 0;
|
|
67
|
+
s.right = 0;
|
|
68
|
+
s.top = 100 * (v.targetAspect - ar) / (2 * v.targetAspect);
|
|
69
|
+
s.bottom = s.top;
|
|
70
|
+
}
|
|
71
|
+
return { ...v, selection: s };
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
convertToPct(sel, width, height) {
|
|
75
|
+
if (!sel)
|
|
76
|
+
return undefined;
|
|
77
|
+
return {
|
|
78
|
+
left: 100.0 * sel.left / width,
|
|
79
|
+
right: 100.0 * (width - sel.right) / width,
|
|
80
|
+
top: 100.0 * sel.top / height,
|
|
81
|
+
bottom: 100.0 * (height - sel.bottom) / height
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
convertToPx(output, width, height) {
|
|
85
|
+
if (!output)
|
|
86
|
+
return undefined;
|
|
87
|
+
return {
|
|
88
|
+
left: output.left * width / 100.0,
|
|
89
|
+
right: width - output.right * width / 100.0,
|
|
90
|
+
top: output.top * height / 100.0,
|
|
91
|
+
bottom: height - output.bottom * height / 100.0
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
determineSelection(startX, startY, x, y, v) {
|
|
95
|
+
x = Math.min(v.width, Math.max(0, x));
|
|
96
|
+
y = Math.min(v.height, Math.max(0, y));
|
|
97
|
+
const dx = x - startX;
|
|
98
|
+
const dy = y - startY;
|
|
99
|
+
const dragar = Math.abs(dx / dy);
|
|
100
|
+
if (dragar > v.targetAspect) { // too wide, dy is dominant
|
|
101
|
+
const dragdx = Math.abs(dy * v.targetAspect);
|
|
102
|
+
return {
|
|
103
|
+
left: dx < 0 ? startX - dragdx : startX,
|
|
104
|
+
right: dx < 0 ? startX : startX + dragdx,
|
|
105
|
+
top: dy < 0 ? y : startY,
|
|
106
|
+
bottom: dy < 0 ? startY : y
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
else { // too tall, dx is dominant
|
|
110
|
+
const dragdy = Math.abs(dx / v.targetAspect);
|
|
111
|
+
return {
|
|
112
|
+
left: dx < 0 ? x : startX,
|
|
113
|
+
right: dx < 0 ? startX : x,
|
|
114
|
+
top: dy < 0 ? startY - dragdy : startY,
|
|
115
|
+
bottom: dy < 0 ? startY : startY + dragdy
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* The svelte component is responsible for making sure x and y are relative to the drawing area,
|
|
121
|
+
* e.g. (0, 0) is the top left corner of the drawing area
|
|
122
|
+
*
|
|
123
|
+
* negative coordinates are not allowed
|
|
124
|
+
*/
|
|
125
|
+
mouseMove(x, y) {
|
|
126
|
+
this.update(v => {
|
|
127
|
+
const r = { ...v };
|
|
128
|
+
if (v.drag) {
|
|
129
|
+
let s = {};
|
|
130
|
+
const dx = x - v.drag.start.x;
|
|
131
|
+
const dy = y - v.drag.start.y;
|
|
132
|
+
if (v.drag.selection) { // adjust the selection
|
|
133
|
+
r.cursor = 'grabbing';
|
|
134
|
+
const old = v.drag.selection;
|
|
135
|
+
if (v.drag.edge.left && v.drag.edge.top) { // expanding
|
|
136
|
+
s = this.determineSelection(v.drag.selection.right, v.drag.selection.bottom, x, y, v);
|
|
137
|
+
}
|
|
138
|
+
else if (v.drag.edge.left && v.drag.edge.bottom) { // expanding
|
|
139
|
+
s = this.determineSelection(v.drag.selection.right, v.drag.selection.top, x, y, v);
|
|
140
|
+
}
|
|
141
|
+
else if (v.drag.edge.right && v.drag.edge.top) { // expanding
|
|
142
|
+
s = this.determineSelection(v.drag.selection.left, v.drag.selection.bottom, x, y, v);
|
|
143
|
+
}
|
|
144
|
+
else if (v.drag.edge.right && v.drag.edge.bottom) { // expanding
|
|
145
|
+
s = this.determineSelection(v.drag.selection.left, v.drag.selection.top, x, y, v);
|
|
146
|
+
}
|
|
147
|
+
else { // moving
|
|
148
|
+
const maxdx = Math.max(Math.min(v.width - old.right, dx), -1 * old.left);
|
|
149
|
+
const maxdy = Math.max(Math.min(v.height - old.bottom, dy), -1 * old.top);
|
|
150
|
+
s.left = old.left + maxdx;
|
|
151
|
+
s.right = old.right + maxdx;
|
|
152
|
+
s.top = old.top + maxdy;
|
|
153
|
+
s.bottom = old.bottom + maxdy;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
else { // drawing a new selection
|
|
157
|
+
s = this.determineSelection(v.drag.start.x, v.drag.start.y, x, y, v);
|
|
158
|
+
}
|
|
159
|
+
return { ...r, selection: this.convertToPct(s, v.width, v.height) };
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
r.cursor = 'crosshair';
|
|
163
|
+
if (r.selection) {
|
|
164
|
+
const selection = this.convertToPx(r.selection, r.width, r.height);
|
|
165
|
+
const left = Math.abs(x - selection.left) < edgeSensitivity;
|
|
166
|
+
const top = Math.abs(y - selection.top) < edgeSensitivity;
|
|
167
|
+
const right = Math.abs(selection.right - x) < edgeSensitivity;
|
|
168
|
+
const bottom = Math.abs(selection.bottom - y) < edgeSensitivity;
|
|
169
|
+
const inside = x > selection.left && x < selection.right && y > selection.top && y < selection.bottom;
|
|
170
|
+
if ((left && top) || (right && bottom))
|
|
171
|
+
r.cursor = 'nwse-resize';
|
|
172
|
+
else if ((right && top) || (left && bottom))
|
|
173
|
+
r.cursor = 'nesw-resize';
|
|
174
|
+
else if (inside)
|
|
175
|
+
r.cursor = 'move';
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return r;
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
move(dx, dy) {
|
|
182
|
+
this.update(v => {
|
|
183
|
+
if (!v.selection)
|
|
184
|
+
return v;
|
|
185
|
+
const selection = this.convertToPx(v.selection, v.width, v.height);
|
|
186
|
+
if (dx < 0 && Math.abs(dx) > selection.left)
|
|
187
|
+
dx = -1 * selection.left;
|
|
188
|
+
if (dx > 0 && dx > v.width - selection.right)
|
|
189
|
+
dx = v.width - selection.right;
|
|
190
|
+
if (dy < 0 && Math.abs(dy) > selection.top)
|
|
191
|
+
dy = -1 * selection.top;
|
|
192
|
+
if (dy > 0 && dy > v.height - selection.bottom)
|
|
193
|
+
dy = v.height - selection.bottom;
|
|
194
|
+
selection.left += dx;
|
|
195
|
+
selection.right += dx;
|
|
196
|
+
selection.top += dy;
|
|
197
|
+
selection.bottom += dy;
|
|
198
|
+
return { ...v, selection: this.convertToPct(selection, v.width, v.height) };
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
expand(type, by) {
|
|
202
|
+
this.update(v => {
|
|
203
|
+
if (!v.selection)
|
|
204
|
+
return v;
|
|
205
|
+
const curr = this.convertToPx(v.selection, v.width, v.height);
|
|
206
|
+
const dx = by * v.targetAspect;
|
|
207
|
+
const dy = by / v.targetAspect;
|
|
208
|
+
let s;
|
|
209
|
+
if (type === 'tl')
|
|
210
|
+
s = this.determineSelection(curr.right, curr.bottom, curr.left - dx, curr.top - dy, v);
|
|
211
|
+
else if (type === 'tr')
|
|
212
|
+
s = this.determineSelection(curr.left, curr.bottom, curr.right + dx, curr.top - dy, v);
|
|
213
|
+
else if (type === 'bl')
|
|
214
|
+
s = this.determineSelection(curr.right, curr.top, curr.left - dx, curr.bottom + dy, v);
|
|
215
|
+
else if (type === 'br')
|
|
216
|
+
s = this.determineSelection(curr.left, curr.top, curr.right + dx, curr.bottom + dy, v);
|
|
217
|
+
return { ...v, selection: this.convertToPct(s, v.width, v.height) };
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
}
|
package/cropper/index.js
ADDED
package/helpers.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function getDescribedBy(ids: string[]): string;
|
|
1
|
+
export declare function getDescribedBy(ids: (string | undefined)[]): string | null;
|
|
@@ -105,7 +105,7 @@ function onKeyDown(e) {
|
|
|
105
105
|
|
|
106
106
|
<FieldStandard bind:id {path} {descid} {label} {required} {defaultValue} {conditional} {helptext} let:value let:valid let:invalid let:id let:onBlur let:setVal let:messagesid let:helptextid>
|
|
107
107
|
<Icon icon={`${value.prefix === 'fab' ? 'fa-brands' : 'fa-solid'}:${value.icon.slice(3)}`}/>
|
|
108
|
-
<button id="btnSelectIcon" on:click={() => { modalOpen = true }} aria-describedby={getDescribedBy([descid, messagesid, helptextid])}>Select New Icon</button>
|
|
108
|
+
<button type="button" id="btnSelectIcon" on:click={() => { modalOpen = true }} aria-describedby={getDescribedBy([descid, messagesid, helptextid])}>Select New Icon</button>
|
|
109
109
|
{#if modalOpen}
|
|
110
110
|
<Modal>
|
|
111
111
|
<section>
|
|
@@ -117,7 +117,7 @@ function onKeyDown(e) {
|
|
|
117
117
|
<div class="filters">
|
|
118
118
|
<div class="search-wrapper">
|
|
119
119
|
<input bind:value={searchVal} id="search_for" placeholder="Search Icons" on:keyup={onSearch}/>
|
|
120
|
-
<label for="search_for" class="fa fa-search"
|
|
120
|
+
<label for="search_for" class="fa fa-search" title="search"></label>
|
|
121
121
|
</div>
|
|
122
122
|
<select bind:value={category} class="icon-category" aria-label="Select Category" on:change={onSelectCategory}>
|
|
123
123
|
<option value="all">All</option>
|
|
@@ -143,8 +143,8 @@ function onKeyDown(e) {
|
|
|
143
143
|
</div>
|
|
144
144
|
</div>
|
|
145
145
|
<footer class="actions">
|
|
146
|
-
<button aria-describedby="{labelid} {descid}" on:click={onCancel(value)}>Cancel</button>
|
|
147
|
-
<button aria-describedby="{labelid} {descid}" on:click={onSaveIconSelection(setVal)}>Save Changes</button>
|
|
146
|
+
<button type="button" aria-describedby="{labelid} {descid}" on:click={onCancel(value)}>Cancel</button>
|
|
147
|
+
<button type="button" aria-describedby="{labelid} {descid}" on:click={onSaveIconSelection(setVal)}>Save Changes</button>
|
|
148
148
|
</footer>
|
|
149
149
|
</section>
|
|
150
150
|
</Modal>
|
|
@@ -192,7 +192,7 @@ function onKeyDown(e) {
|
|
|
192
192
|
}
|
|
193
193
|
|
|
194
194
|
.icon-modal-content fieldset {
|
|
195
|
-
max-height:
|
|
195
|
+
max-height: 75vh;
|
|
196
196
|
overflow-y: scroll;
|
|
197
197
|
}
|
|
198
198
|
|
|
@@ -3,12 +3,12 @@ declare const __propDef: {
|
|
|
3
3
|
props: {
|
|
4
4
|
id?: string | undefined;
|
|
5
5
|
path: string;
|
|
6
|
-
label?: string;
|
|
7
|
-
required?: boolean;
|
|
6
|
+
label?: string | undefined;
|
|
7
|
+
required?: boolean | undefined;
|
|
8
8
|
defaultValue?: {
|
|
9
9
|
icon: string;
|
|
10
10
|
prefix: string;
|
|
11
|
-
};
|
|
11
|
+
} | undefined;
|
|
12
12
|
conditional?: boolean | undefined;
|
|
13
13
|
helptext?: string | undefined;
|
|
14
14
|
};
|
package/index.d.ts
CHANGED
package/index.js
CHANGED