@streamscloud/kit 0.1.9 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/core/utils/color-helper.d.ts +13 -0
  2. package/dist/core/utils/color-helper.js +39 -0
  3. package/dist/core/utils/index.d.ts +1 -0
  4. package/dist/core/utils/index.js +1 -0
  5. package/dist/ui/color-picker/cmp.color-picker.svelte +150 -0
  6. package/dist/ui/color-picker/cmp.color-picker.svelte.d.ts +33 -0
  7. package/dist/ui/color-picker/cmp.input-stub.svelte +98 -0
  8. package/dist/ui/color-picker/cmp.input-stub.svelte.d.ts +40 -0
  9. package/dist/ui/color-picker/color-picker-localization.d.ts +3 -0
  10. package/dist/ui/color-picker/color-picker-localization.js +12 -0
  11. package/dist/ui/color-picker/index.d.ts +1 -0
  12. package/dist/ui/color-picker/index.js +1 -0
  13. package/dist/ui/cropper/image-editor-dialog/cmp.image-editor-dialog.svelte +109 -0
  14. package/dist/ui/cropper/image-editor-dialog/cmp.image-editor-dialog.svelte.d.ts +9 -0
  15. package/dist/ui/cropper/image-editor-dialog/image-editor-dialog-localization.d.ts +6 -0
  16. package/dist/ui/cropper/image-editor-dialog/image-editor-dialog-localization.js +33 -0
  17. package/dist/ui/cropper/image-editor-dialog/index.d.ts +21 -0
  18. package/dist/ui/cropper/image-editor-dialog/index.js +25 -0
  19. package/dist/ui/cropper/image-editor-dialog/types.d.ts +25 -0
  20. package/dist/ui/cropper/image-editor-dialog/types.js +1 -0
  21. package/dist/ui/cropper/img-cropper/cmp.img-cropper-controls.svelte +67 -0
  22. package/dist/ui/cropper/img-cropper/cmp.img-cropper-controls.svelte.d.ts +21 -0
  23. package/dist/ui/cropper/img-cropper/cmp.img-cropper-toolbar.svelte +228 -0
  24. package/dist/ui/cropper/img-cropper/cmp.img-cropper-toolbar.svelte.d.ts +28 -0
  25. package/dist/ui/cropper/img-cropper/cmp.img-cropper.svelte +198 -0
  26. package/dist/ui/cropper/img-cropper/cmp.img-cropper.svelte.d.ts +58 -0
  27. package/dist/ui/cropper/img-cropper/cropperjs-elements.d.ts +33 -0
  28. package/dist/ui/cropper/img-cropper/img-cropper-contain-worker.svelte.d.ts +40 -0
  29. package/dist/ui/cropper/img-cropper/img-cropper-contain-worker.svelte.js +159 -0
  30. package/dist/ui/cropper/img-cropper/img-cropper-cover-worker.svelte.d.ts +40 -0
  31. package/dist/ui/cropper/img-cropper/img-cropper-cover-worker.svelte.js +163 -0
  32. package/dist/ui/cropper/img-cropper/img-cropper-localization.d.ts +6 -0
  33. package/dist/ui/cropper/img-cropper/img-cropper-localization.js +33 -0
  34. package/dist/ui/cropper/img-cropper/img-cropper-toolbar-localization.d.ts +11 -0
  35. package/dist/ui/cropper/img-cropper/img-cropper-toolbar-localization.js +68 -0
  36. package/dist/ui/cropper/img-cropper/img-cropper-utils.d.ts +32 -0
  37. package/dist/ui/cropper/img-cropper/img-cropper-utils.js +138 -0
  38. package/dist/ui/cropper/img-cropper/img-cropper-worker.svelte.d.ts +39 -0
  39. package/dist/ui/cropper/img-cropper/img-cropper-worker.svelte.js +1 -0
  40. package/dist/ui/cropper/img-cropper/img-cropper.svelte.d.ts +81 -0
  41. package/dist/ui/cropper/img-cropper/img-cropper.svelte.js +160 -0
  42. package/dist/ui/cropper/img-cropper/index.d.ts +4 -0
  43. package/dist/ui/cropper/img-cropper/index.js +4 -0
  44. package/dist/ui/icon-text/cmp.icon-text.svelte +90 -0
  45. package/dist/ui/icon-text/cmp.icon-text.svelte.d.ts +39 -0
  46. package/dist/ui/icon-text/index.d.ts +1 -0
  47. package/dist/ui/icon-text/index.js +1 -0
  48. package/package.json +27 -5
@@ -0,0 +1,67 @@
1
+ <script lang="ts">import { ImgCropperLocalization } from './img-cropper-localization';
2
+ const { cropper } = $props();
3
+ const loc = new ImgCropperLocalization();
4
+ </script>
5
+
6
+ {#if cropper.cropBoxVisible}
7
+ <div class="img-cropper-controls">
8
+ <button type="button" class="img-cropper-controls__button img-cropper-controls__button--apply" onclick={() => cropper.crop()}>{loc.apply}</button>
9
+ <button type="button" class="img-cropper-controls__button img-cropper-controls__button--cancel" onclick={() => cropper.clearSelection()}>
10
+ {loc.cancel}
11
+ </button>
12
+ </div>
13
+ {/if}
14
+
15
+ <!--
16
+ @component
17
+ Apply/Cancel controls for the image cropper. Renders conditionally when a crop box is active.
18
+ Can be placed anywhere in the layout.
19
+
20
+ ### Props
21
+ | Prop | Type | Default | Description |
22
+ |---|---|---|---|
23
+ | `cropper` | `ImgCropper` | — | Cropper controller instance |
24
+
25
+ ### CSS Custom Properties
26
+ | Property | Description | Default |
27
+ |---|---|---|
28
+ | `--sc-kit--img-cropper-controls--background` | Panel background | `transparent` |
29
+ -->
30
+
31
+ <style>.img-cropper-controls {
32
+ --_img-cropper-controls--background: var(--sc-kit--img-cropper-controls--background, transparent);
33
+ width: 15rem;
34
+ padding: 0.3125rem;
35
+ display: flex;
36
+ gap: 0.625rem;
37
+ background: var(--_img-cropper-controls--background);
38
+ }
39
+ .img-cropper-controls__button {
40
+ font-size: 0.75rem;
41
+ padding: 0.25rem 1rem;
42
+ border-radius: 0.25rem;
43
+ max-width: 8rem;
44
+ height: 2rem;
45
+ display: inline-flex;
46
+ flex: 1;
47
+ justify-content: center;
48
+ align-items: center;
49
+ cursor: pointer;
50
+ transition: background-color linear 0.2s, color linear 0.2s;
51
+ }
52
+ .img-cropper-controls__button--apply {
53
+ color: #ffffff;
54
+ background: #0cce6b;
55
+ }
56
+ .img-cropper-controls__button--apply:hover {
57
+ background: #0bbc60;
58
+ }
59
+ .img-cropper-controls__button--cancel {
60
+ color: light-dark(#2e2e2e, #ffffff);
61
+ background: light-dark(#ffffff, #222222);
62
+ border: 1px solid light-dark(#e5e7eb, #242424);
63
+ box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
64
+ }
65
+ .img-cropper-controls__button--cancel:hover {
66
+ background: light-dark(#f9fafb, #242424);
67
+ }</style>
@@ -0,0 +1,21 @@
1
+ import type { ImgCropper } from './img-cropper.svelte';
2
+ type Props = {
3
+ cropper: ImgCropper;
4
+ };
5
+ /**
6
+ * Apply/Cancel controls for the image cropper. Renders conditionally when a crop box is active.
7
+ * Can be placed anywhere in the layout.
8
+ *
9
+ * ### Props
10
+ * | Prop | Type | Default | Description |
11
+ * |---|---|---|---|
12
+ * | `cropper` | `ImgCropper` | — | Cropper controller instance |
13
+ *
14
+ * ### CSS Custom Properties
15
+ * | Property | Description | Default |
16
+ * |---|---|---|
17
+ * | `--sc-kit--img-cropper-controls--background` | Panel background | `transparent` |
18
+ */
19
+ declare const Cmp: import("svelte").Component<Props, {}, "">;
20
+ type Cmp = ReturnType<typeof Cmp>;
21
+ export default Cmp;
@@ -0,0 +1,228 @@
1
+ <script lang="ts">import { ColorPicker } from '../../color-picker';
2
+ import { Dropdown } from '../../dropdown';
3
+ import { Icon } from '../../icon';
4
+ import { IconText } from '../../icon-text';
5
+ import { ImgCropperToolbarLocalization } from './img-cropper-toolbar-localization';
6
+ import IconArrowMove from '@fluentui/svg-icons/icons/arrow_move_20_regular.svg?raw';
7
+ import IconReset from '@fluentui/svg-icons/icons/arrow_reset_20_regular.svg?raw';
8
+ import IconArrowRotateClockwise from '@fluentui/svg-icons/icons/arrow_rotate_clockwise_20_regular.svg?raw';
9
+ import IconCrop from '@fluentui/svg-icons/icons/crop_20_regular.svg?raw';
10
+ import IconDismiss from '@fluentui/svg-icons/icons/dismiss_20_regular.svg?raw';
11
+ import IconZoomIn from '@fluentui/svg-icons/icons/zoom_in_20_regular.svg?raw';
12
+ import IconZoomOut from '@fluentui/svg-icons/icons/zoom_out_20_regular.svg?raw';
13
+ const { cropper, children } = $props();
14
+ const loc = new ImgCropperToolbarLocalization();
15
+ const ratioLabel = (option) => (option.value === null ? loc.free : option.label);
16
+ const selectedRatioLabel = $derived.by(() => {
17
+ const found = cropper.ratioOptions?.find((r) => r.value === cropper.aspectRatio);
18
+ return found ? ratioLabel(found) : loc.free;
19
+ });
20
+ </script>
21
+
22
+ <div class="img-cropper-toolbar">
23
+ <button
24
+ type="button"
25
+ class="img-cropper-toolbar__button"
26
+ class:img-cropper-toolbar__button--active={cropper.dragMode === 'move'}
27
+ onclick={() => cropper.enableMoveMode()}>
28
+ <IconText icon={IconArrowMove}>{loc.move}</IconText>
29
+ </button>
30
+ <button
31
+ type="button"
32
+ class="img-cropper-toolbar__button"
33
+ class:img-cropper-toolbar__button--active={cropper.dragMode === 'crop'}
34
+ onclick={() => cropper.enableCropMode()}>
35
+ <IconText icon={IconCrop}>{loc.crop}</IconText>
36
+ </button>
37
+ <button type="button" class="img-cropper-toolbar__button" onclick={() => cropper.zoomIn()}>
38
+ <IconText icon={IconZoomIn}>{loc.zoomIn}</IconText>
39
+ </button>
40
+ <button type="button" class="img-cropper-toolbar__button" onclick={() => cropper.zoomOut()}>
41
+ <IconText icon={IconZoomOut}>{loc.zoomOut}</IconText>
42
+ </button>
43
+ <button type="button" class="img-cropper-toolbar__button" onclick={() => cropper.rotate()}>
44
+ <IconText icon={IconArrowRotateClockwise}>{loc.rotate}</IconText>
45
+ </button>
46
+ <button type="button" class="img-cropper-toolbar__button img-cropper-toolbar__button--reset" onclick={() => cropper.reset()}>
47
+ <IconText icon={IconReset}>{loc.reset}</IconText>
48
+ </button>
49
+
50
+ {#if cropper.ratioOptions}
51
+ <div class="img-cropper-toolbar__ratio">
52
+ <span class="img-cropper-toolbar__label">{loc.ratio}</span>
53
+ <Dropdown position="top">
54
+ {#snippet trigger()}
55
+ <div class="img-cropper-toolbar__ratio-trigger">{selectedRatioLabel}</div>
56
+ {/snippet}
57
+ <div class="img-cropper-toolbar__ratio-menu">
58
+ {#each cropper.ratioOptions as option (String(option.value))}
59
+ <button type="button" class="img-cropper-toolbar__ratio-item" onclick={() => cropper.changeAspectRatio(option.value)}>
60
+ {ratioLabel(option)}
61
+ </button>
62
+ {/each}
63
+ </div>
64
+ </Dropdown>
65
+ </div>
66
+ {/if}
67
+
68
+ {#if cropper.showFillColor}
69
+ <div class="img-cropper-toolbar__fill">
70
+ <span class="img-cropper-toolbar__label">{loc.fill}</span>
71
+ <ColorPicker value={cropper.fillColor} on={{ change: (v) => (cropper.fillColor = v) }}>
72
+ <div
73
+ class="img-cropper-toolbar__fill-swatch"
74
+ class:img-cropper-toolbar__fill-swatch--empty={cropper.isTransparentFill}
75
+ style:background-color={cropper.isTransparentFill ? undefined : cropper.fillColor}>
76
+ </div>
77
+ </ColorPicker>
78
+ {#if !cropper.isTransparentFill}
79
+ <button type="button" class="img-cropper-toolbar__fill-reset" onclick={() => (cropper.fillColor = '')}>
80
+ <Icon src={IconDismiss} />
81
+ </button>
82
+ {/if}
83
+ </div>
84
+ {/if}
85
+
86
+ {#if children}
87
+ {@render children()}
88
+ {/if}
89
+ </div>
90
+
91
+ <!--
92
+ @component
93
+ Toolbar with image manipulation buttons: Move, Crop, Zoom In/Out, Rotate, Reset.
94
+ Shows aspect ratio dropdown when `cropper.ratioOptions` is set, and fill color
95
+ picker when `cropper.showFillColor` is true (derived from `mode === 'contain'`).
96
+
97
+ ### Props
98
+ | Prop | Type | Default | Description |
99
+ |---|---|---|---|
100
+ | `cropper` | `ImgCropper` | — | Cropper controller instance |
101
+ | `children` | `Snippet` | — | Extra controls rendered at the end |
102
+
103
+ ### CSS Custom Properties
104
+ | Property | Description | Default |
105
+ |---|---|---|
106
+ | `--sc-kit--img-cropper-toolbar--color` | Button text/icon color | `light-dark(gray-800, white)` |
107
+ | `--sc-kit--img-cropper-toolbar--background` | Button background | `light-dark(white, dark-700)` |
108
+ | `--sc-kit--img-cropper-toolbar--border-color` | Button border color | `light-dark(neutral-200, dark-600)` |
109
+ | `--sc-kit--img-cropper-toolbar--active-color` | Active button accent color | `light-dark(primary-500, primary-400)` |
110
+ -->
111
+
112
+ <style>.img-cropper-toolbar {
113
+ --_img-cropper-toolbar--color: var(--sc-kit--img-cropper-toolbar--color, light-dark(#2e2e2e, #ffffff));
114
+ --_img-cropper-toolbar--background: var(--sc-kit--img-cropper-toolbar--background, light-dark(#ffffff, #222222));
115
+ --_img-cropper-toolbar--border-color: var(--sc-kit--img-cropper-toolbar--border-color, light-dark(#e5e7eb, #242424));
116
+ --_img-cropper-toolbar--active-color: var(
117
+ --sc-kit--img-cropper-toolbar--active-color,
118
+ light-dark(#144ab0, #5a8dec)
119
+ );
120
+ container-type: inline-size;
121
+ display: flex;
122
+ align-items: center;
123
+ min-width: 0;
124
+ gap: 0.5rem;
125
+ --sc-kit--icon-text--icon--size: 1rem;
126
+ }
127
+ .img-cropper-toolbar__button {
128
+ font-size: 0.75rem;
129
+ border-radius: 0.25rem;
130
+ white-space: nowrap;
131
+ border: 1px solid var(--_img-cropper-toolbar--border-color);
132
+ color: var(--_img-cropper-toolbar--color);
133
+ background: var(--_img-cropper-toolbar--background);
134
+ min-width: 5rem;
135
+ padding: 0.25rem 0.5rem;
136
+ display: flex;
137
+ justify-content: center;
138
+ cursor: pointer;
139
+ transition: background-color linear 0.2s, color linear 0.2s, border-color linear 0.2s;
140
+ }
141
+ .img-cropper-toolbar__button:hover, .img-cropper-toolbar__button--active {
142
+ color: var(--_img-cropper-toolbar--active-color);
143
+ border-color: var(--_img-cropper-toolbar--active-color);
144
+ }
145
+ .img-cropper-toolbar__button--reset {
146
+ margin-left: 0.5rem;
147
+ }
148
+ .img-cropper-toolbar__button {
149
+ /* Set 'container-type: inline-size;' to reference container*/
150
+ }
151
+ @container (width < 53.125rem) {
152
+ .img-cropper-toolbar__button {
153
+ min-width: auto;
154
+ padding: 0.25rem;
155
+ --sc-kit--icon-text--text--display: none;
156
+ }
157
+ }
158
+ .img-cropper-toolbar__label {
159
+ font-size: 0.625rem;
160
+ white-space: nowrap;
161
+ margin-right: 0.5rem;
162
+ }
163
+ .img-cropper-toolbar__ratio {
164
+ display: flex;
165
+ align-items: center;
166
+ margin-left: 1rem;
167
+ }
168
+ .img-cropper-toolbar__ratio-trigger {
169
+ padding: 0.25rem;
170
+ border-radius: 0.125rem;
171
+ border: 1px solid var(--_img-cropper-toolbar--border-color);
172
+ cursor: pointer;
173
+ font-size: 0.75rem;
174
+ white-space: nowrap;
175
+ }
176
+ .img-cropper-toolbar__ratio-menu {
177
+ background: var(--_img-cropper-toolbar--background);
178
+ border: 1px solid var(--_img-cropper-toolbar--border-color);
179
+ border-radius: 0.25rem;
180
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
181
+ padding: 0.25rem 0;
182
+ }
183
+ .img-cropper-toolbar__ratio-item {
184
+ display: block;
185
+ width: 100%;
186
+ padding: 0.375rem 0.75rem;
187
+ border: none;
188
+ background: none;
189
+ text-align: left;
190
+ cursor: pointer;
191
+ font-size: 0.75rem;
192
+ color: var(--_img-cropper-toolbar--color);
193
+ }
194
+ .img-cropper-toolbar__ratio-item:hover {
195
+ background: light-dark(#f9fafb, #242424);
196
+ }
197
+ .img-cropper-toolbar__fill {
198
+ display: flex;
199
+ align-items: center;
200
+ gap: 0.5rem;
201
+ margin-left: 1rem;
202
+ }
203
+ .img-cropper-toolbar__fill-swatch {
204
+ width: 3.125rem;
205
+ height: 1.625rem;
206
+ border-radius: 0.25rem;
207
+ border: 1px solid var(--_img-cropper-toolbar--border-color);
208
+ cursor: pointer;
209
+ }
210
+ .img-cropper-toolbar__fill-swatch--empty {
211
+ background-image: linear-gradient(45deg, #ccc 25%, transparent 25%), linear-gradient(-45deg, #ccc 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #ccc 75%), linear-gradient(-45deg, transparent 75%, #ccc 75%);
212
+ background-size: 8px 8px;
213
+ background-position: 0 0, 0 4px, 4px -4px, -4px 0;
214
+ }
215
+ .img-cropper-toolbar__fill-reset {
216
+ display: flex;
217
+ align-items: center;
218
+ justify-content: center;
219
+ cursor: pointer;
220
+ color: var(--_img-cropper-toolbar--color);
221
+ background: none;
222
+ border: none;
223
+ padding: 0;
224
+ --sc-kit--icon--size: 1rem;
225
+ }
226
+ .img-cropper-toolbar__fill-reset:hover {
227
+ color: var(--_img-cropper-toolbar--active-color);
228
+ }</style>
@@ -0,0 +1,28 @@
1
+ import type { ImgCropper } from './img-cropper.svelte';
2
+ import type { Snippet } from 'svelte';
3
+ type Props = {
4
+ cropper: ImgCropper;
5
+ children?: Snippet;
6
+ };
7
+ /**
8
+ * Toolbar with image manipulation buttons: Move, Crop, Zoom In/Out, Rotate, Reset.
9
+ * Shows aspect ratio dropdown when `cropper.ratioOptions` is set, and fill color
10
+ * picker when `cropper.showFillColor` is true (derived from `mode === 'contain'`).
11
+ *
12
+ * ### Props
13
+ * | Prop | Type | Default | Description |
14
+ * |---|---|---|---|
15
+ * | `cropper` | `ImgCropper` | — | Cropper controller instance |
16
+ * | `children` | `Snippet` | — | Extra controls rendered at the end |
17
+ *
18
+ * ### CSS Custom Properties
19
+ * | Property | Description | Default |
20
+ * |---|---|---|
21
+ * | `--sc-kit--img-cropper-toolbar--color` | Button text/icon color | `light-dark(gray-800, white)` |
22
+ * | `--sc-kit--img-cropper-toolbar--background` | Button background | `light-dark(white, dark-700)` |
23
+ * | `--sc-kit--img-cropper-toolbar--border-color` | Button border color | `light-dark(neutral-200, dark-600)` |
24
+ * | `--sc-kit--img-cropper-toolbar--active-color` | Active button accent color | `light-dark(primary-500, primary-400)` |
25
+ */
26
+ declare const Cmp: import("svelte").Component<Props, {}, "">;
27
+ type Cmp = ReturnType<typeof Cmp>;
28
+ export default Cmp;
@@ -0,0 +1,198 @@
1
+ <script lang="ts">import { Toastr } from '../../../core/toastr';
2
+ import { Loading } from '../../loading';
3
+ import { default as ImgCropperControls } from './cmp.img-cropper-controls.svelte';
4
+ import { ImgCropperLocalization } from './img-cropper-localization';
5
+ const { src, cropper, showControls = true } = $props();
6
+ const loc = new ImgCropperLocalization();
7
+ let cropperLoaded = $state(false);
8
+ $effect(() => {
9
+ const loadCropper = async () => {
10
+ try {
11
+ await import('cropperjs');
12
+ cropperLoaded = true;
13
+ }
14
+ catch {
15
+ Toastr.error(loc.initCropperError);
16
+ }
17
+ };
18
+ void loadCropper();
19
+ });
20
+ const initCanvas = (canvasEl) => {
21
+ const imageEl = canvasEl.querySelector('cropper-image');
22
+ const selectionEl = canvasEl.querySelector('cropper-selection');
23
+ const selectHandle = canvasEl.querySelector('cropper-handle[action="select"]');
24
+ if (imageEl && selectionEl) {
25
+ const initCropper = async () => {
26
+ try {
27
+ await cropper.init({
28
+ src,
29
+ cropperImage: imageEl,
30
+ cropperSelection: selectionEl,
31
+ cropperCanvas: canvasEl,
32
+ selectHandle
33
+ });
34
+ }
35
+ catch {
36
+ Toastr.error(loc.imageLoadError);
37
+ }
38
+ };
39
+ void initCropper();
40
+ }
41
+ return { destroy: () => cropper.destroy() };
42
+ };
43
+ </script>
44
+
45
+ <div
46
+ class="img-cropper"
47
+ style:--img-cropper--fill-color={cropper.fillColor || undefined}
48
+ style:--img-cropper--ratio={cropper.effectiveCanvasRatio ?? undefined}
49
+ style:--img-cropper--natural-w={cropper.naturalWidth ? `${cropper.naturalWidth}px` : undefined}
50
+ style:--img-cropper--natural-h={cropper.naturalHeight ? `${cropper.naturalHeight}px` : undefined}>
51
+ {#if cropper.loading || !cropper.ready}
52
+ <Loading positionAbsoluteCenter={true} />
53
+ {/if}
54
+
55
+ {#if cropperLoaded}
56
+ <cropper-canvas
57
+ class="img-cropper__canvas"
58
+ class:img-cropper__canvas--visible={cropper.ready}
59
+ class:img-cropper__canvas--fitted={cropper.effectiveCanvasRatio !== null && cropper.naturalWidth > 0}
60
+ class:img-cropper__canvas--shadow={cropper.showImageShadow}
61
+ use:initCanvas
62
+ background={(cropper.showFillColor && cropper.isTransparentFill) || undefined}>
63
+ <cropper-image crossorigin={cropper.corsMode === 'native' ? 'anonymous' : null} initial-center-size={cropper.mode} scalable translatable rotatable>
64
+ </cropper-image>
65
+ <cropper-shade hidden></cropper-shade>
66
+ <cropper-handle action="select" plain></cropper-handle>
67
+ <cropper-selection movable resizable aspect-ratio={cropper.aspectRatio ?? undefined}>
68
+ <cropper-grid role="grid" bordered covered></cropper-grid>
69
+ <cropper-crosshair centered></cropper-crosshair>
70
+ <cropper-handle action="move" theme-color="rgba(255,255,255,0.35)"></cropper-handle>
71
+ <cropper-handle action="n-resize"></cropper-handle>
72
+ <cropper-handle action="e-resize"></cropper-handle>
73
+ <cropper-handle action="s-resize"></cropper-handle>
74
+ <cropper-handle action="w-resize"></cropper-handle>
75
+ <cropper-handle action="ne-resize"></cropper-handle>
76
+ <cropper-handle action="nw-resize"></cropper-handle>
77
+ <cropper-handle action="se-resize"></cropper-handle>
78
+ <cropper-handle action="sw-resize"></cropper-handle>
79
+ </cropper-selection>
80
+ </cropper-canvas>
81
+
82
+ {#if cropper.showImageShadow && cropper.ready}
83
+ <div class="img-cropper__shadow" class:img-cropper__shadow--full={cropper.effectiveCanvasRatio === null}></div>
84
+ {/if}
85
+ {/if}
86
+
87
+ {#if showControls}
88
+ <div class="img-cropper__controls">
89
+ <ImgCropperControls cropper={cropper} />
90
+ </div>
91
+ {/if}
92
+ </div>
93
+
94
+ <!--
95
+ @component
96
+ Image cropper view based on CropperJS v2 web components.
97
+
98
+ Renders a canvas with crop selection, rotation, zoom, and reset capabilities.
99
+ The container fills 100% width and height of its parent; the image is centered inside.
100
+ The component itself uses `overflow: visible` — the parent element should set
101
+ `overflow: hidden` to clip the shadow overlay at its boundaries.
102
+
103
+ #### Modes
104
+
105
+ The behavior is determined by the `ImgCropper` instance passed via the `cropper` prop:
106
+
107
+ - **`cover`** — the image always fills the canvas completely. The crop selection
108
+ defines the visible area. After save, the result is the selected region at
109
+ natural image resolution.
110
+ - **`contain`** — the image is fitted inside the canvas with optional fill color
111
+ for empty space. After crop, the canvas shrinks to match the cropped area's
112
+ aspect ratio. After save, the result includes the fill color background.
113
+
114
+ #### Aspect ratio
115
+
116
+ Aspect ratio is configured on the `ImgCropper` instance via the `aspectRatio` option:
117
+
118
+ - **Fixed number** (e.g. `16/9`) — locks the crop selection to this ratio.
119
+ - **Dynamic object** — provides a list of `supported` ratios and an `initial` value.
120
+ The toolbar renders a ratio dropdown when multiple options are available.
121
+ - In `contain` mode, `allowFreeCrop: true` adds a free-ratio option that lets
122
+ the user draw an unconstrained selection. After crop, the canvas dynamically
123
+ adopts the cropped area's proportions.
124
+ - In `cover` mode, `initial` is required and free crop is not available.
125
+ - **Omitted** — defaults to free crop in `contain` mode, or fills the canvas in `cover` mode.
126
+
127
+ #### Shadow overlay
128
+
129
+ When `showImageShadow` is enabled on the cropper instance, a dark semi-transparent
130
+ overlay (`box-shadow: 0 0 0 9999px`) extends beyond the canvas bounds so the full
131
+ image remains visible outside the crop area. In free-ratio mode (no fixed aspect
132
+ ratio), the shadow covers the entire container instead.
133
+
134
+ ### Props
135
+ | Prop | Type | Default | Description |
136
+ |---|---|---|---|
137
+ | `src` | `string` | — | Image source URL |
138
+ | `cropper` | `ImgCropper` | — | Cropper controller instance |
139
+ | `showControls` | `boolean` | `true` | Show built-in Apply/Cancel crop controls |
140
+
141
+ ### CSS Custom Properties
142
+ None — canvas background, shadow, and sizing are controlled by the `ImgCropper` instance.
143
+ -->
144
+
145
+ <style>.img-cropper {
146
+ --_fill-color: var(--img-cropper--fill-color);
147
+ --_ratio: var(--img-cropper--ratio);
148
+ --_natural-w: var(--img-cropper--natural-w, 100cqw);
149
+ --_natural-h: var(--img-cropper--natural-h, 100cqh);
150
+ container-type: size;
151
+ position: relative;
152
+ width: 100%;
153
+ height: 100%;
154
+ display: flex;
155
+ justify-content: center;
156
+ align-items: center;
157
+ overflow: visible;
158
+ }
159
+ .img-cropper__canvas {
160
+ width: 100%;
161
+ height: 100%;
162
+ background-color: var(--_fill-color);
163
+ visibility: hidden;
164
+ }
165
+ .img-cropper__canvas--fitted {
166
+ width: min(100cqw, 100cqh * var(--_ratio), max(var(--_natural-w), var(--_natural-h) * var(--_ratio)));
167
+ height: auto;
168
+ aspect-ratio: var(--_ratio);
169
+ }
170
+ .img-cropper__canvas--visible {
171
+ visibility: visible;
172
+ }
173
+ .img-cropper__canvas--shadow {
174
+ overflow: visible;
175
+ }
176
+ .img-cropper__shadow {
177
+ position: absolute;
178
+ top: 50%;
179
+ left: 50%;
180
+ transform: translate(-50%, -50%);
181
+ width: min(100cqw, 100cqh * var(--_ratio), max(var(--_natural-w), var(--_natural-h) * var(--_ratio)));
182
+ aspect-ratio: var(--_ratio);
183
+ box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.8);
184
+ pointer-events: none;
185
+ z-index: 2;
186
+ }
187
+ .img-cropper__shadow--full {
188
+ width: 100cqw;
189
+ height: 100cqh;
190
+ aspect-ratio: auto;
191
+ }
192
+ .img-cropper__controls {
193
+ position: absolute;
194
+ bottom: 0;
195
+ left: 50%;
196
+ transform: translateX(-50%);
197
+ z-index: 100;
198
+ }</style>
@@ -0,0 +1,58 @@
1
+ import type { ImgCropper } from './img-cropper.svelte';
2
+ type Props = {
3
+ src: string;
4
+ cropper: ImgCropper;
5
+ showControls?: boolean;
6
+ };
7
+ /**
8
+ * Image cropper view based on CropperJS v2 web components.
9
+ *
10
+ * Renders a canvas with crop selection, rotation, zoom, and reset capabilities.
11
+ * The container fills 100% width and height of its parent; the image is centered inside.
12
+ * The component itself uses `overflow: visible` — the parent element should set
13
+ * `overflow: hidden` to clip the shadow overlay at its boundaries.
14
+ *
15
+ * #### Modes
16
+ *
17
+ * The behavior is determined by the `ImgCropper` instance passed via the `cropper` prop:
18
+ *
19
+ * - **`cover`** — the image always fills the canvas completely. The crop selection
20
+ * defines the visible area. After save, the result is the selected region at
21
+ * natural image resolution.
22
+ * - **`contain`** — the image is fitted inside the canvas with optional fill color
23
+ * for empty space. After crop, the canvas shrinks to match the cropped area's
24
+ * aspect ratio. After save, the result includes the fill color background.
25
+ *
26
+ * #### Aspect ratio
27
+ *
28
+ * Aspect ratio is configured on the `ImgCropper` instance via the `aspectRatio` option:
29
+ *
30
+ * - **Fixed number** (e.g. `16/9`) — locks the crop selection to this ratio.
31
+ * - **Dynamic object** — provides a list of `supported` ratios and an `initial` value.
32
+ * The toolbar renders a ratio dropdown when multiple options are available.
33
+ * - In `contain` mode, `allowFreeCrop: true` adds a free-ratio option that lets
34
+ * the user draw an unconstrained selection. After crop, the canvas dynamically
35
+ * adopts the cropped area's proportions.
36
+ * - In `cover` mode, `initial` is required and free crop is not available.
37
+ * - **Omitted** — defaults to free crop in `contain` mode, or fills the canvas in `cover` mode.
38
+ *
39
+ * #### Shadow overlay
40
+ *
41
+ * When `showImageShadow` is enabled on the cropper instance, a dark semi-transparent
42
+ * overlay (`box-shadow: 0 0 0 9999px`) extends beyond the canvas bounds so the full
43
+ * image remains visible outside the crop area. In free-ratio mode (no fixed aspect
44
+ * ratio), the shadow covers the entire container instead.
45
+ *
46
+ * ### Props
47
+ * | Prop | Type | Default | Description |
48
+ * |---|---|---|---|
49
+ * | `src` | `string` | — | Image source URL |
50
+ * | `cropper` | `ImgCropper` | — | Cropper controller instance |
51
+ * | `showControls` | `boolean` | `true` | Show built-in Apply/Cancel crop controls |
52
+ *
53
+ * ### CSS Custom Properties
54
+ * None — canvas background, shadow, and sizing are controlled by the `ImgCropper` instance.
55
+ */
56
+ declare const Cmp: import("svelte").Component<Props, {}, "">;
57
+ type Cmp = ReturnType<typeof Cmp>;
58
+ export default Cmp;
@@ -0,0 +1,33 @@
1
+ // Type declarations for CropperJS v2 web components.
2
+ // Extends svelteHTML.IntrinsicElements so the Svelte language server
3
+ // recognises <cropper-*> tags and their attributes in templates.
4
+
5
+ type CamelToKebab<S extends string> = S extends `${infer F}${infer R}`
6
+ ? R extends Uncapitalize<R>
7
+ ? `${Lowercase<F>}${CamelToKebab<R>}`
8
+ : `${Lowercase<F>}-${CamelToKebab<R>}`
9
+ : S;
10
+
11
+ type CropperElementProps<T extends HTMLElement> = {
12
+ [K in keyof T as K extends keyof HTMLElement
13
+ ? never
14
+ : K extends `$${string}`
15
+ ? never
16
+ : T[K] extends (...args: never[]) => unknown
17
+ ? never
18
+ : K extends string
19
+ ? CamelToKebab<K>
20
+ : never]?: (T[K] extends boolean ? boolean : T[K] extends number ? number | string : string) | null;
21
+ } & import('svelte/elements').HTMLAttributes<HTMLElement>;
22
+
23
+ declare namespace svelteHTML {
24
+ interface IntrinsicElements {
25
+ 'cropper-canvas': CropperElementProps<import('cropperjs').CropperCanvas>;
26
+ 'cropper-image': CropperElementProps<import('cropperjs').CropperImage>;
27
+ 'cropper-shade': CropperElementProps<import('cropperjs').CropperShade>;
28
+ 'cropper-handle': CropperElementProps<import('cropperjs').CropperHandle>;
29
+ 'cropper-selection': CropperElementProps<import('cropperjs').CropperSelection>;
30
+ 'cropper-grid': CropperElementProps<import('cropperjs').CropperGrid>;
31
+ 'cropper-crosshair': CropperElementProps<import('cropperjs').CropperCrosshair>;
32
+ }
33
+ }
@@ -0,0 +1,40 @@
1
+ import type { DragMode, ImgCropperWorker, ImgCropperWorkerParams } from './img-cropper-worker.svelte';
2
+ export declare class ImgCropperContainWorker implements ImgCropperWorker {
3
+ cropBoxVisible: boolean;
4
+ dragMode: DragMode;
5
+ naturalWidth: number;
6
+ naturalHeight: number;
7
+ canvasRatio: number | null;
8
+ destroy: () => void;
9
+ private _image;
10
+ private _selection;
11
+ private _canvas;
12
+ private _selectHandle;
13
+ private _originalSrc;
14
+ constructor(params: ImgCropperWorkerParams);
15
+ ready: () => Promise<void>;
16
+ rotate: () => Promise<void>;
17
+ zoomIn: () => void;
18
+ zoomOut: () => void;
19
+ reset: () => Promise<void>;
20
+ crop: (options?: {
21
+ fillColor?: string;
22
+ freeRatio?: boolean;
23
+ }) => Promise<{
24
+ dataUrl: string;
25
+ width: number;
26
+ height: number;
27
+ }>;
28
+ save: (options?: {
29
+ fillColor?: string;
30
+ }) => Promise<{
31
+ dataUrl: string;
32
+ width: number;
33
+ height: number;
34
+ }>;
35
+ clearSelection: () => void;
36
+ enableCropMode: () => void;
37
+ private _applyMoveMode;
38
+ private _fitImage;
39
+ private _centerForCurrentRotation;
40
+ }