@veltra/desktop 1.7.8 → 1.7.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.
Files changed (41) hide show
  1. package/dist/components/image-cropper/draw-crop.js +63 -0
  2. package/dist/components/image-cropper/draw-crop.js.map +1 -0
  3. package/dist/components/image-cropper/image-cropper.js +344 -0
  4. package/dist/components/image-cropper/image-cropper.js.map +1 -0
  5. package/dist/components/image-cropper/style.d.ts +1 -0
  6. package/dist/components/image-cropper/style.js +1 -0
  7. package/dist/components/image-cropper/style2.css +205 -0
  8. package/dist/components/image-cropper/use-image-loader.js +59 -0
  9. package/dist/components/image-cropper/use-image-loader.js.map +1 -0
  10. package/dist/components/image-cropper/use-preview.js +44 -0
  11. package/dist/components/image-cropper/use-preview.js.map +1 -0
  12. package/dist/components/image-cropper/use-selection.js +183 -0
  13. package/dist/components/image-cropper/use-selection.js.map +1 -0
  14. package/dist/components/image-cropper/use-transform.js +182 -0
  15. package/dist/components/image-cropper/use-transform.js.map +1 -0
  16. package/dist/components/index.d.ts +2 -1
  17. package/dist/components/index.js +3 -1
  18. package/dist/components/index.js.map +1 -1
  19. package/dist/index.d.ts +47 -45
  20. package/dist/index.js +46 -45
  21. package/dist/types/image-cropper.d.ts +68 -0
  22. package/dist/types/index.d.ts +2 -1
  23. package/package.json +6 -6
  24. package/src/components/auto-complete/style.scss +3 -15
  25. package/src/components/contextmenu/style.scss +4 -20
  26. package/src/components/image-cropper/__test__/draw-crop.test.ts +131 -0
  27. package/src/components/image-cropper/__test__/image-cropper.test.ts +744 -0
  28. package/src/components/image-cropper/__test__/use-transform.test.ts +229 -0
  29. package/src/components/image-cropper/draw-crop.ts +112 -0
  30. package/src/components/image-cropper/image-cropper.vue +302 -0
  31. package/src/components/image-cropper/index.ts +1 -0
  32. package/src/components/image-cropper/style.scss +232 -0
  33. package/src/components/image-cropper/style.ts +1 -0
  34. package/src/components/image-cropper/use-image-loader.ts +79 -0
  35. package/src/components/image-cropper/use-preview.ts +58 -0
  36. package/src/components/image-cropper/use-selection.ts +255 -0
  37. package/src/components/image-cropper/use-transform.ts +256 -0
  38. package/src/components/index.ts +2 -0
  39. package/src/components/select/style.scss +3 -15
  40. package/src/types/image-cropper.ts +73 -0
  41. package/src/types/index.ts +1 -0
@@ -0,0 +1,59 @@
1
+ import { onBeforeUnmount, shallowRef, watch } from "vue";
2
+ //#region src/components/image-cropper/use-image-loader.ts
3
+ /** 加载 File / Blob / URL 图片源,File / Blob 走 objectURL 并在替换与卸载时释放 */
4
+ function useImageLoader(options) {
5
+ const { src } = options;
6
+ const imageUrl = shallowRef("");
7
+ const loaded = shallowRef(false);
8
+ const error = shallowRef(false);
9
+ const naturalWidth = shallowRef(0);
10
+ const naturalHeight = shallowRef(0);
11
+ const imageEl = shallowRef();
12
+ let objectUrl;
13
+ function revokeObjectUrl() {
14
+ if (!objectUrl) return;
15
+ URL.revokeObjectURL(objectUrl);
16
+ objectUrl = void 0;
17
+ }
18
+ function load(source) {
19
+ revokeObjectUrl();
20
+ imageUrl.value = "";
21
+ loaded.value = false;
22
+ error.value = false;
23
+ naturalWidth.value = 0;
24
+ naturalHeight.value = 0;
25
+ imageEl.value = void 0;
26
+ if (!source) return;
27
+ const url = typeof source === "string" ? source : objectUrl = URL.createObjectURL(source);
28
+ const el = new Image();
29
+ if (typeof source === "string") el.crossOrigin = "anonymous";
30
+ el.onload = () => {
31
+ naturalWidth.value = el.naturalWidth;
32
+ naturalHeight.value = el.naturalHeight;
33
+ imageEl.value = el;
34
+ loaded.value = true;
35
+ };
36
+ el.onerror = () => {
37
+ error.value = true;
38
+ imageUrl.value = "";
39
+ };
40
+ el.src = url;
41
+ imageUrl.value = url;
42
+ }
43
+ watch(src, load, { immediate: true });
44
+ onBeforeUnmount(() => {
45
+ revokeObjectUrl();
46
+ });
47
+ return {
48
+ imageUrl,
49
+ loaded,
50
+ error,
51
+ naturalWidth,
52
+ naturalHeight,
53
+ imageEl
54
+ };
55
+ }
56
+ //#endregion
57
+ export { useImageLoader };
58
+
59
+ //# sourceMappingURL=use-image-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-image-loader.js","names":[],"sources":["../../../src/components/image-cropper/use-image-loader.ts"],"sourcesContent":["import { onBeforeUnmount, shallowRef, watch, type ShallowRef } from 'vue'\n\ninterface UseImageLoaderOptions {\n /** 图片源(File / Blob / URL) */\n src: () => File | Blob | string | undefined\n}\n\ninterface UseImageLoaderReturn {\n /** 用于渲染的图片地址 */\n imageUrl: ShallowRef<string>\n /** 是否加载完成 */\n loaded: ShallowRef<boolean>\n /** 是否加载失败 */\n error: ShallowRef<boolean>\n /** 图片原始宽度 */\n naturalWidth: ShallowRef<number>\n /** 图片原始高度 */\n naturalHeight: ShallowRef<number>\n /** 已加载完成的图片元素,供画布绘制(预览 / getResult 输出) */\n imageEl: ShallowRef<HTMLImageElement | undefined>\n}\n\n/** 加载 File / Blob / URL 图片源,File / Blob 走 objectURL 并在替换与卸载时释放 */\nexport function useImageLoader(options: UseImageLoaderOptions): UseImageLoaderReturn {\n const { src } = options\n\n const imageUrl = shallowRef('')\n const loaded = shallowRef(false)\n const error = shallowRef(false)\n const naturalWidth = shallowRef(0)\n const naturalHeight = shallowRef(0)\n const imageEl = shallowRef<HTMLImageElement>()\n\n let objectUrl: string | undefined\n\n function revokeObjectUrl() {\n if (!objectUrl) return\n URL.revokeObjectURL(objectUrl)\n objectUrl = undefined\n }\n\n function load(source: File | Blob | string | undefined) {\n revokeObjectUrl()\n imageUrl.value = ''\n loaded.value = false\n error.value = false\n naturalWidth.value = 0\n naturalHeight.value = 0\n imageEl.value = undefined\n\n if (!source) return\n\n const url = typeof source === 'string' ? source : (objectUrl = URL.createObjectURL(source))\n const el = new Image()\n // URL 源尽量走跨域匿名加载,保证后续画布导出不被污染\n if (typeof source === 'string') el.crossOrigin = 'anonymous'\n\n el.onload = () => {\n naturalWidth.value = el.naturalWidth\n naturalHeight.value = el.naturalHeight\n imageEl.value = el\n loaded.value = true\n }\n el.onerror = () => {\n error.value = true\n imageUrl.value = ''\n }\n el.src = url\n imageUrl.value = url\n }\n\n watch(src, load, { immediate: true })\n\n onBeforeUnmount(() => {\n revokeObjectUrl()\n })\n\n return { imageUrl, loaded, error, naturalWidth, naturalHeight, imageEl }\n}\n"],"mappings":";;;AAuBA,SAAgB,eAAe,SAAsD;CACnF,MAAM,EAAE,QAAQ;CAEhB,MAAM,WAAW,WAAW,EAAE;CAC9B,MAAM,SAAS,WAAW,KAAK;CAC/B,MAAM,QAAQ,WAAW,KAAK;CAC9B,MAAM,eAAe,WAAW,CAAC;CACjC,MAAM,gBAAgB,WAAW,CAAC;CAClC,MAAM,UAAU,WAA6B;CAE7C,IAAI;CAEJ,SAAS,kBAAkB;EACzB,IAAI,CAAC,WAAW;EAChB,IAAI,gBAAgB,SAAS;EAC7B,YAAY,KAAA;CACd;CAEA,SAAS,KAAK,QAA0C;EACtD,gBAAgB;EAChB,SAAS,QAAQ;EACjB,OAAO,QAAQ;EACf,MAAM,QAAQ;EACd,aAAa,QAAQ;EACrB,cAAc,QAAQ;EACtB,QAAQ,QAAQ,KAAA;EAEhB,IAAI,CAAC,QAAQ;EAEb,MAAM,MAAM,OAAO,WAAW,WAAW,SAAU,YAAY,IAAI,gBAAgB,MAAM;EACzF,MAAM,KAAK,IAAI,MAAM;EAErB,IAAI,OAAO,WAAW,UAAU,GAAG,cAAc;EAEjD,GAAG,eAAe;GAChB,aAAa,QAAQ,GAAG;GACxB,cAAc,QAAQ,GAAG;GACzB,QAAQ,QAAQ;GAChB,OAAO,QAAQ;EACjB;EACA,GAAG,gBAAgB;GACjB,MAAM,QAAQ;GACd,SAAS,QAAQ;EACnB;EACA,GAAG,MAAM;EACT,SAAS,QAAQ;CACnB;CAEA,MAAM,KAAK,MAAM,EAAE,WAAW,KAAK,CAAC;CAEpC,sBAAsB;EACpB,gBAAgB;CAClB,CAAC;CAED,OAAO;EAAE;EAAU;EAAQ;EAAO;EAAc;EAAe;CAAQ;AACzE"}
@@ -0,0 +1,44 @@
1
+ import { drawCropToCanvas } from "./draw-crop.js";
2
+ import { watch } from "vue";
3
+ //#region src/components/image-cropper/use-preview.ts
4
+ /**
5
+ * 实时预览:选区或旋转 / 翻转变换变化时把当前裁剪结果重绘到预览画布。
6
+ * 画布按显示尺寸(等比缩放到画布区 30% 以内)绘制,不能靠 CSS max-width/max-height 约束
7
+ * ——父级为 shrink-to-fit 宽度时百分比 max 约束失效,画布会按原尺寸溢出被裁剪
8
+ */
9
+ function usePreview(options) {
10
+ const { canvas, image, selection, transform, canvasSize } = options;
11
+ watch([
12
+ canvas,
13
+ image,
14
+ selection,
15
+ () => transform.rotation,
16
+ () => transform.flipX,
17
+ () => transform.flipY,
18
+ () => {
19
+ const size = canvasSize();
20
+ return [size.width, size.height];
21
+ }
22
+ ], () => {
23
+ const el = canvas.value;
24
+ const img = image.value;
25
+ const sel = selection.value;
26
+ if (!el || !img || !sel) return;
27
+ const swapped = transform.rotation % 180 !== 0;
28
+ const baseW = swapped ? sel.height : sel.width;
29
+ const baseH = swapped ? sel.width : sel.height;
30
+ const size = canvasSize();
31
+ const scale = Math.min(size.width * .3 / baseW, size.height * .3 / baseH, 1);
32
+ drawCropToCanvas(img, sel, transform, el, {
33
+ width: baseW * scale,
34
+ height: baseH * scale
35
+ });
36
+ }, {
37
+ flush: "post",
38
+ immediate: true
39
+ });
40
+ }
41
+ //#endregion
42
+ export { usePreview };
43
+
44
+ //# sourceMappingURL=use-preview.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-preview.js","names":[],"sources":["../../../src/components/image-cropper/use-preview.ts"],"sourcesContent":["import { watch, type ShallowRef } from 'vue'\n\nimport { drawCropToCanvas, type CropTransform } from './draw-crop'\nimport type { SelectionRect } from './use-selection'\n\ninterface UsePreviewOptions {\n /** 预览画布元素 */\n canvas: ShallowRef<HTMLCanvasElement | undefined>\n /** 舞台内的图片元素(已加载完成) */\n image: ShallowRef<HTMLImageElement | undefined>\n /** 当前选区(图片像素坐标) */\n selection: ShallowRef<SelectionRect | null>\n /** 影响裁剪输出的变换(缩放 / 平移不影响输出,仅旋转 / 翻转触发重绘) */\n transform: CropTransform\n /** 裁剪画布区尺寸,预览显示尺寸以它的 30% 为上限 */\n canvasSize: () => { width: number; height: number }\n}\n\n/**\n * 实时预览:选区或旋转 / 翻转变换变化时把当前裁剪结果重绘到预览画布。\n * 画布按显示尺寸(等比缩放到画布区 30% 以内)绘制,不能靠 CSS max-width/max-height 约束\n * ——父级为 shrink-to-fit 宽度时百分比 max 约束失效,画布会按原尺寸溢出被裁剪\n */\nexport function usePreview(options: UsePreviewOptions): void {\n const { canvas, image, selection, transform, canvasSize } = options\n\n watch(\n [\n canvas,\n image,\n selection,\n () => transform.rotation,\n () => transform.flipX,\n () => transform.flipY,\n () => {\n const size = canvasSize()\n return [size.width, size.height]\n }\n ],\n () => {\n const el = canvas.value\n const img = image.value\n const sel = selection.value\n if (!el || !img || !sel) return\n\n // 与 drawCropToCanvas 相同的宽高交换规则,先求旋转后的基准尺寸\n const swapped = transform.rotation % 180 !== 0\n const baseW = swapped ? sel.height : sel.width\n const baseH = swapped ? sel.width : sel.height\n\n const size = canvasSize()\n const scale = Math.min((size.width * 0.3) / baseW, (size.height * 0.3) / baseH, 1)\n drawCropToCanvas(img, sel, transform, el, { width: baseW * scale, height: baseH * scale })\n },\n // post:等待 v-if 切换后画布元素完成挂载\n { flush: 'post', immediate: true }\n )\n}\n"],"mappings":";;;;;;;;AAuBA,SAAgB,WAAW,SAAkC;CAC3D,MAAM,EAAE,QAAQ,OAAO,WAAW,WAAW,eAAe;CAE5D,MACE;EACE;EACA;EACA;QACM,UAAU;QACV,UAAU;QACV,UAAU;QACV;GACJ,MAAM,OAAO,WAAW;GACxB,OAAO,CAAC,KAAK,OAAO,KAAK,MAAM;EACjC;CACF,SACM;EACJ,MAAM,KAAK,OAAO;EAClB,MAAM,MAAM,MAAM;EAClB,MAAM,MAAM,UAAU;EACtB,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK;EAGzB,MAAM,UAAU,UAAU,WAAW,QAAQ;EAC7C,MAAM,QAAQ,UAAU,IAAI,SAAS,IAAI;EACzC,MAAM,QAAQ,UAAU,IAAI,QAAQ,IAAI;EAExC,MAAM,OAAO,WAAW;EACxB,MAAM,QAAQ,KAAK,IAAK,KAAK,QAAQ,KAAO,OAAQ,KAAK,SAAS,KAAO,OAAO,CAAC;EACjF,iBAAiB,KAAK,KAAK,WAAW,IAAI;GAAE,OAAO,QAAQ;GAAO,QAAQ,QAAQ;EAAM,CAAC;CAC3F,GAEA;EAAE,OAAO;EAAQ,WAAW;CAAK,CACnC;AACF"}
@@ -0,0 +1,183 @@
1
+ import { onBeforeUnmount, shallowRef, watch } from "vue";
2
+ //#region src/components/image-cropper/use-selection.ts
3
+ /** 全部手柄方位 */
4
+ const SELECTION_HANDLES = [
5
+ "nw",
6
+ "n",
7
+ "ne",
8
+ "e",
9
+ "se",
10
+ "s",
11
+ "sw",
12
+ "w"
13
+ ];
14
+ /** 选区最小边长(图片像素) */
15
+ const MIN_SIZE = 20;
16
+ function clamp(value, min, max) {
17
+ return Math.min(Math.max(value, min), Math.max(min, max));
18
+ }
19
+ /** 裁剪选区交互:整体拖动 + 8 个手柄调整大小,支持宽高比约束与图片边界钳制 */
20
+ function useSelection(options) {
21
+ const { target, imageWidth, imageHeight, toImageDelta, aspectRatio } = options;
22
+ const selection = shallowRef(null);
23
+ /** 有效宽高比,非法值视为自由比例 */
24
+ function validRatio() {
25
+ const ratio = aspectRatio();
26
+ return ratio && ratio > 0 ? ratio : void 0;
27
+ }
28
+ /** 尺寸与位置钳制在图片像素范围内 */
29
+ function clampRect(rect) {
30
+ const W = imageWidth();
31
+ const H = imageHeight();
32
+ const width = clamp(rect.width, Math.min(MIN_SIZE, W), W);
33
+ const height = clamp(rect.height, Math.min(MIN_SIZE, H), H);
34
+ return {
35
+ width,
36
+ height,
37
+ x: clamp(rect.x, 0, W - width),
38
+ y: clamp(rect.y, 0, H - height)
39
+ };
40
+ }
41
+ /** 以选区中心为锚按比例重算选区 */
42
+ function applyRatio(sel, ratio) {
43
+ const W = imageWidth();
44
+ const H = imageHeight();
45
+ const cx = sel.x + sel.width / 2;
46
+ const cy = sel.y + sel.height / 2;
47
+ let width = sel.width;
48
+ let height = width / ratio;
49
+ if (height > H) {
50
+ height = H;
51
+ width = height * ratio;
52
+ }
53
+ if (width > W) {
54
+ width = W;
55
+ height = width / ratio;
56
+ }
57
+ return clampRect({
58
+ x: cx - width / 2,
59
+ y: cy - height / 2,
60
+ width,
61
+ height
62
+ });
63
+ }
64
+ function initSelection() {
65
+ const W = imageWidth();
66
+ const H = imageHeight();
67
+ if (!W || !H) {
68
+ selection.value = null;
69
+ return;
70
+ }
71
+ let width = W * .8;
72
+ let height = H * .8;
73
+ const ratio = validRatio();
74
+ if (ratio) {
75
+ if (width / height > ratio) width = height * ratio;
76
+ else height = width / ratio;
77
+ }
78
+ selection.value = {
79
+ x: (W - width) / 2,
80
+ y: (H - height) / 2,
81
+ width,
82
+ height
83
+ };
84
+ }
85
+ function clearSelection() {
86
+ selection.value = null;
87
+ }
88
+ watch(() => aspectRatio(), () => {
89
+ const ratio = validRatio();
90
+ if (!ratio || !selection.value) return;
91
+ selection.value = applyRatio(selection.value, ratio);
92
+ });
93
+ let session = null;
94
+ function handleMousedown(e) {
95
+ const sel = selection.value;
96
+ if (e.button !== 0 || !sel) return;
97
+ e.stopPropagation();
98
+ e.preventDefault();
99
+ session = {
100
+ mode: e.target?.closest("[data-handle]")?.getAttribute("data-handle") ?? "move",
101
+ startX: e.clientX,
102
+ startY: e.clientY,
103
+ origin: { ...sel }
104
+ };
105
+ document.addEventListener("mousemove", handleMousemove, { passive: true });
106
+ document.addEventListener("mouseup", handleMouseup);
107
+ }
108
+ function handleMousemove(e) {
109
+ if (!session) return;
110
+ const { x: dx, y: dy } = toImageDelta(e.clientX - session.startX, e.clientY - session.startY);
111
+ selection.value = session.mode === "move" ? moveSelection(session.origin, dx, dy) : resizeSelection(session.origin, session.mode, dx, dy);
112
+ }
113
+ function handleMouseup() {
114
+ session = null;
115
+ cleanup();
116
+ }
117
+ function cleanup() {
118
+ document.removeEventListener("mousemove", handleMousemove);
119
+ document.removeEventListener("mouseup", handleMouseup);
120
+ }
121
+ function moveSelection(origin, dx, dy) {
122
+ return clampRect({
123
+ ...origin,
124
+ x: origin.x + dx,
125
+ y: origin.y + dy
126
+ });
127
+ }
128
+ function resizeSelection(origin, handle, dx, dy) {
129
+ const W = imageWidth();
130
+ const H = imageHeight();
131
+ const right = origin.x + origin.width;
132
+ const bottom = origin.y + origin.height;
133
+ const cx = origin.x + origin.width / 2;
134
+ const cy = origin.y + origin.height / 2;
135
+ const ratio = validRatio();
136
+ const west = handle.includes("w");
137
+ const east = handle.includes("e");
138
+ const north = handle.includes("n");
139
+ const south = handle.includes("s");
140
+ let width = origin.width + (east ? dx : west ? -dx : 0);
141
+ let height = origin.height + (south ? dy : north ? -dy : 0);
142
+ const maxWidth = east ? W - origin.x : west ? right : 2 * Math.min(cx, W - cx);
143
+ const maxHeight = south ? H - origin.y : north ? bottom : 2 * Math.min(cy, H - cy);
144
+ if (ratio) {
145
+ if ((east || west) && !north && !south) height = width / ratio;
146
+ else if ((north || south) && !east && !west) width = height * ratio;
147
+ else {
148
+ const widthFromHeight = height * ratio;
149
+ width = Math.abs(width - origin.width) >= Math.abs(widthFromHeight - origin.width) ? width : widthFromHeight;
150
+ height = width / ratio;
151
+ }
152
+ const maxCoupledWidth = Math.min(maxWidth, maxHeight * ratio);
153
+ width = clamp(width, Math.min(MIN_SIZE, maxCoupledWidth), maxCoupledWidth);
154
+ height = width / ratio;
155
+ } else {
156
+ width = clamp(width, Math.min(MIN_SIZE, maxWidth), maxWidth);
157
+ height = clamp(height, Math.min(MIN_SIZE, maxHeight), maxHeight);
158
+ }
159
+ return clampRect({
160
+ x: west ? right - width : east ? origin.x : cx - width / 2,
161
+ y: north ? bottom - height : south ? origin.y : cy - height / 2,
162
+ width,
163
+ height
164
+ });
165
+ }
166
+ watch(target, (el, oldEl) => {
167
+ oldEl?.removeEventListener("mousedown", handleMousedown);
168
+ el?.addEventListener("mousedown", handleMousedown);
169
+ }, { immediate: true });
170
+ onBeforeUnmount(() => {
171
+ target.value?.removeEventListener("mousedown", handleMousedown);
172
+ cleanup();
173
+ });
174
+ return {
175
+ selection,
176
+ initSelection,
177
+ clearSelection
178
+ };
179
+ }
180
+ //#endregion
181
+ export { SELECTION_HANDLES, useSelection };
182
+
183
+ //# sourceMappingURL=use-selection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-selection.js","names":[],"sources":["../../../src/components/image-cropper/use-selection.ts"],"sourcesContent":["import { onBeforeUnmount, shallowRef, watch, type Ref, type ShallowRef } from 'vue'\n\n/** 裁剪选区(图片像素坐标) */\nexport interface SelectionRect {\n x: number\n y: number\n width: number\n height: number\n}\n\n/** 手柄方位:四角 + 四边 */\nexport type SelectionHandle = 'nw' | 'n' | 'ne' | 'e' | 'se' | 's' | 'sw' | 'w'\n\n/** 全部手柄方位 */\nexport const SELECTION_HANDLES: SelectionHandle[] = ['nw', 'n', 'ne', 'e', 'se', 's', 'sw', 'w']\n\n/** 选区最小边长(图片像素) */\nconst MIN_SIZE = 20\n\nfunction clamp(value: number, min: number, max: number) {\n return Math.min(Math.max(value, min), Math.max(min, max))\n}\n\ninterface UseSelectionOptions {\n /** 选区元素(mousedown 交互目标,手柄通过 data-handle 标识) */\n target: ShallowRef<HTMLElement | undefined | null> | Ref<HTMLElement | undefined | null>\n /** 图片原始宽(图片像素) */\n imageWidth: () => number\n /** 图片原始高(图片像素) */\n imageHeight: () => number\n /** 屏幕位移换算为图片像素位移(由 use-transform 提供,考虑缩放 / 旋转 / 翻转) */\n toImageDelta: (dx: number, dy: number) => { x: number; y: number }\n /** 宽高比约束(宽 / 高),不传或 <= 0 为自由比例 */\n aspectRatio: () => number | undefined\n}\n\ninterface UseSelectionReturn {\n /** 当前选区(图片像素坐标) */\n selection: ShallowRef<SelectionRect | null>\n /** 以图片居中约 80% 区域初始化选区(受宽高比约束) */\n initSelection: () => void\n /** 清空选区 */\n clearSelection: () => void\n}\n\n/** 裁剪选区交互:整体拖动 + 8 个手柄调整大小,支持宽高比约束与图片边界钳制 */\nexport function useSelection(options: UseSelectionOptions): UseSelectionReturn {\n const { target, imageWidth, imageHeight, toImageDelta, aspectRatio } = options\n\n const selection = shallowRef<SelectionRect | null>(null)\n\n /** 有效宽高比,非法值视为自由比例 */\n function validRatio() {\n const ratio = aspectRatio()\n return ratio && ratio > 0 ? ratio : undefined\n }\n\n /** 尺寸与位置钳制在图片像素范围内 */\n function clampRect(rect: SelectionRect): SelectionRect {\n const W = imageWidth()\n const H = imageHeight()\n const width = clamp(rect.width, Math.min(MIN_SIZE, W), W)\n const height = clamp(rect.height, Math.min(MIN_SIZE, H), H)\n return { width, height, x: clamp(rect.x, 0, W - width), y: clamp(rect.y, 0, H - height) }\n }\n\n /** 以选区中心为锚按比例重算选区 */\n function applyRatio(sel: SelectionRect, ratio: number): SelectionRect {\n const W = imageWidth()\n const H = imageHeight()\n const cx = sel.x + sel.width / 2\n const cy = sel.y + sel.height / 2\n\n let width = sel.width\n let height = width / ratio\n if (height > H) {\n height = H\n width = height * ratio\n }\n if (width > W) {\n width = W\n height = width / ratio\n }\n\n return clampRect({ x: cx - width / 2, y: cy - height / 2, width, height })\n }\n\n function initSelection() {\n const W = imageWidth()\n const H = imageHeight()\n if (!W || !H) {\n selection.value = null\n return\n }\n\n let width = W * 0.8\n let height = H * 0.8\n const ratio = validRatio()\n // 在居中 80% 框内内接指定比例\n if (ratio) {\n if (width / height > ratio) width = height * ratio\n else height = width / ratio\n }\n\n selection.value = { x: (W - width) / 2, y: (H - height) / 2, width, height }\n }\n\n function clearSelection() {\n selection.value = null\n }\n\n // 宽高比变化时以选区中心为锚按比例重算\n watch(\n () => aspectRatio(),\n () => {\n const ratio = validRatio()\n if (!ratio || !selection.value) return\n selection.value = applyRatio(selection.value, ratio)\n }\n )\n\n // ---------- 拖动交互(参照 useDrag 模式:mousedown 起,document 级 move/up 收尾) ----------\n\n interface DragSession {\n /** 'move' 为整体拖动,其余为手柄方位 */\n mode: 'move' | SelectionHandle\n startX: number\n startY: number\n origin: SelectionRect\n }\n\n let session: DragSession | null = null\n\n function handleMousedown(e: MouseEvent) {\n const sel = selection.value\n if (e.button !== 0 || !sel) return\n // 阻止冒泡与默认行为:避免触发画布层交互(P3 平移)与原生选中\n e.stopPropagation()\n e.preventDefault()\n\n const handle = (e.target as HTMLElement | null)\n ?.closest('[data-handle]')\n ?.getAttribute('data-handle')\n\n session = {\n mode: (handle as SelectionHandle | null) ?? 'move',\n startX: e.clientX,\n startY: e.clientY,\n origin: { ...sel }\n }\n\n document.addEventListener('mousemove', handleMousemove, { passive: true })\n document.addEventListener('mouseup', handleMouseup)\n }\n\n function handleMousemove(e: MouseEvent) {\n if (!session) return\n\n // 屏幕位移换算为图片像素位移(含缩放 / 旋转 / 翻转)\n const { x: dx, y: dy } = toImageDelta(e.clientX - session.startX, e.clientY - session.startY)\n selection.value =\n session.mode === 'move'\n ? moveSelection(session.origin, dx, dy)\n : resizeSelection(session.origin, session.mode, dx, dy)\n }\n\n function handleMouseup() {\n session = null\n cleanup()\n }\n\n function cleanup() {\n document.removeEventListener('mousemove', handleMousemove)\n document.removeEventListener('mouseup', handleMouseup)\n }\n\n function moveSelection(origin: SelectionRect, dx: number, dy: number): SelectionRect {\n return clampRect({ ...origin, x: origin.x + dx, y: origin.y + dy })\n }\n\n function resizeSelection(\n origin: SelectionRect,\n handle: SelectionHandle,\n dx: number,\n dy: number\n ): SelectionRect {\n const W = imageWidth()\n const H = imageHeight()\n const right = origin.x + origin.width\n const bottom = origin.y + origin.height\n const cx = origin.x + origin.width / 2\n const cy = origin.y + origin.height / 2\n const ratio = validRatio()\n\n const west = handle.includes('w')\n const east = handle.includes('e')\n const north = handle.includes('n')\n const south = handle.includes('s')\n\n // 候选尺寸:被拖动的边随指针位移,对侧保持不动\n let width = origin.width + (east ? dx : west ? -dx : 0)\n let height = origin.height + (south ? dy : north ? -dy : 0)\n\n // 各轴最大尺寸:拖动边受图片边界限制,未拖动的轴绕中心扩展\n const maxWidth = east ? W - origin.x : west ? right : 2 * Math.min(cx, W - cx)\n const maxHeight = south ? H - origin.y : north ? bottom : 2 * Math.min(cy, H - cy)\n\n if (ratio) {\n if ((east || west) && !north && !south) {\n // 横向边中手柄:宽度驱动,高度按比例绕中心联动\n height = width / ratio\n } else if ((north || south) && !east && !west) {\n // 纵向边中手柄:高度驱动,宽度按比例绕中心联动\n width = height * ratio\n } else {\n // 角手柄:取位移更大的一侧驱动,单方向拖动也能生效\n const widthFromHeight = height * ratio\n width =\n Math.abs(width - origin.width) >= Math.abs(widthFromHeight - origin.width)\n ? width\n : widthFromHeight\n height = width / ratio\n }\n const maxCoupledWidth = Math.min(maxWidth, maxHeight * ratio)\n width = clamp(width, Math.min(MIN_SIZE, maxCoupledWidth), maxCoupledWidth)\n height = width / ratio\n } else {\n width = clamp(width, Math.min(MIN_SIZE, maxWidth), maxWidth)\n height = clamp(height, Math.min(MIN_SIZE, maxHeight), maxHeight)\n }\n\n return clampRect({\n x: west ? right - width : east ? origin.x : cx - width / 2,\n y: north ? bottom - height : south ? origin.y : cy - height / 2,\n width,\n height\n })\n }\n\n watch(\n target,\n (el, oldEl) => {\n oldEl?.removeEventListener('mousedown', handleMousedown)\n el?.addEventListener('mousedown', handleMousedown)\n },\n { immediate: true }\n )\n\n onBeforeUnmount(() => {\n target.value?.removeEventListener('mousedown', handleMousedown)\n cleanup()\n })\n\n return { selection, initSelection, clearSelection }\n}\n"],"mappings":"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"}
@@ -0,0 +1,182 @@
1
+ import { onBeforeUnmount, reactive, watch } from "vue";
2
+ //#region src/components/image-cropper/use-transform.ts
3
+ /** 滚轮 / 按钮单次缩放步进系数 */
4
+ const ZOOM_STEP = 1.2;
5
+ /** 最大缩放倍数(相对适应容器的缩放) */
6
+ const MAX_ZOOM_RATIO = 10;
7
+ /**
8
+ * 图片变换:滚轮以指针为锚缩放、选区外拖动平移、90° 步进旋转、翻转与重置。
9
+ * 舞台 transform-origin 为中心,旋转 / 翻转不改变图片中心位置,无需补偿平移
10
+ */
11
+ function useTransform(options) {
12
+ const { target, canvasSize, imageWidth, imageHeight, onReset } = options;
13
+ const transform = reactive({
14
+ scale: 1,
15
+ translateX: 0,
16
+ translateY: 0,
17
+ rotation: 0,
18
+ flipX: false,
19
+ flipY: false
20
+ });
21
+ /** 当前旋转下的显示尺寸(90° / 270° 交换宽高,图片像素) */
22
+ function displaySize() {
23
+ const width = imageWidth();
24
+ const height = imageHeight();
25
+ return transform.rotation % 180 === 0 ? {
26
+ width,
27
+ height
28
+ } : {
29
+ width: height,
30
+ height: width
31
+ };
32
+ }
33
+ /** 适应容器的缩放(同时是缩放范围下限) */
34
+ function fitScale() {
35
+ const { width: cw, height: ch } = canvasSize();
36
+ const { width: dw, height: dh } = displaySize();
37
+ if (!cw || !ch || !dw || !dh) return 0;
38
+ return Math.min(cw / dw, ch / dh);
39
+ }
40
+ function fit() {
41
+ const scale = fitScale();
42
+ if (!scale) return;
43
+ const { width: cw, height: ch } = canvasSize();
44
+ transform.scale = scale;
45
+ transform.translateX = (cw - imageWidth()) / 2;
46
+ transform.translateY = (ch - imageHeight()) / 2;
47
+ }
48
+ function reset() {
49
+ transform.scale = 1;
50
+ transform.translateX = 0;
51
+ transform.translateY = 0;
52
+ transform.rotation = 0;
53
+ transform.flipX = false;
54
+ transform.flipY = false;
55
+ }
56
+ function resetTransform() {
57
+ reset();
58
+ fit();
59
+ onReset();
60
+ }
61
+ function zoomTo(scale, anchor) {
62
+ const base = fitScale();
63
+ if (!base) return;
64
+ const next = Math.min(Math.max(scale, base), base * MAX_ZOOM_RATIO);
65
+ const { width: cw, height: ch } = canvasSize();
66
+ const { x: ax, y: ay } = anchor ?? {
67
+ x: cw / 2,
68
+ y: ch / 2
69
+ };
70
+ const cx = imageWidth() / 2;
71
+ const cy = imageHeight() / 2;
72
+ const k = next / transform.scale;
73
+ transform.translateX = ax - cx - k * (ax - transform.translateX - cx);
74
+ transform.translateY = ay - cy - k * (ay - transform.translateY - cy);
75
+ transform.scale = next;
76
+ }
77
+ function zoomIn(anchor) {
78
+ zoomTo(transform.scale * ZOOM_STEP, anchor);
79
+ }
80
+ function zoomOut(anchor) {
81
+ zoomTo(transform.scale / ZOOM_STEP, anchor);
82
+ }
83
+ function rotate(direction) {
84
+ transform.rotation = (transform.rotation + direction * 90 + 360) % 360;
85
+ fit();
86
+ }
87
+ function flip(axis) {
88
+ if (axis === "horizontal") transform.flipX = !transform.flipX;
89
+ else transform.flipY = !transform.flipY;
90
+ }
91
+ function toImageDelta(dx, dy) {
92
+ let x = dx;
93
+ let y = dy;
94
+ if (transform.rotation === 90) {
95
+ x = dy;
96
+ y = -dx;
97
+ } else if (transform.rotation === 180) {
98
+ x = -dx;
99
+ y = -dy;
100
+ } else if (transform.rotation === 270) {
101
+ x = -dy;
102
+ y = dx;
103
+ }
104
+ return {
105
+ x: x / ((transform.flipX ? -1 : 1) * transform.scale),
106
+ y: y / ((transform.flipY ? -1 : 1) * transform.scale)
107
+ };
108
+ }
109
+ function handleWheel(e) {
110
+ e.preventDefault();
111
+ const el = target.value;
112
+ if (!el) return;
113
+ const rect = el.getBoundingClientRect();
114
+ const anchor = {
115
+ x: e.clientX - rect.left,
116
+ y: e.clientY - rect.top
117
+ };
118
+ if (e.deltaY < 0) zoomIn(anchor);
119
+ else zoomOut(anchor);
120
+ }
121
+ /** 图片显示尺寸超出容器才允许平移 */
122
+ function isOverflowing() {
123
+ const { width: cw, height: ch } = canvasSize();
124
+ const { width: dw, height: dh } = displaySize();
125
+ return dw * transform.scale > cw || dh * transform.scale > ch;
126
+ }
127
+ let panStart = null;
128
+ function handleMousedown(e) {
129
+ if (e.button !== 0 || !isOverflowing()) return;
130
+ e.preventDefault();
131
+ panStart = {
132
+ x: e.clientX,
133
+ y: e.clientY
134
+ };
135
+ document.addEventListener("mousemove", handleMousemove, { passive: true });
136
+ document.addEventListener("mouseup", handleMouseup);
137
+ }
138
+ function handleMousemove(e) {
139
+ if (!panStart) return;
140
+ transform.translateX += e.clientX - panStart.x;
141
+ transform.translateY += e.clientY - panStart.y;
142
+ panStart = {
143
+ x: e.clientX,
144
+ y: e.clientY
145
+ };
146
+ }
147
+ function handleMouseup() {
148
+ panStart = null;
149
+ cleanup();
150
+ }
151
+ function cleanup() {
152
+ document.removeEventListener("mousemove", handleMousemove);
153
+ document.removeEventListener("mouseup", handleMouseup);
154
+ }
155
+ watch(target, (el, oldEl) => {
156
+ oldEl?.removeEventListener("mousedown", handleMousedown);
157
+ oldEl?.removeEventListener("wheel", handleWheel);
158
+ el?.addEventListener("mousedown", handleMousedown);
159
+ el?.addEventListener("wheel", handleWheel, { passive: false });
160
+ }, { immediate: true });
161
+ onBeforeUnmount(() => {
162
+ target.value?.removeEventListener("mousedown", handleMousedown);
163
+ target.value?.removeEventListener("wheel", handleWheel);
164
+ cleanup();
165
+ });
166
+ return {
167
+ transform,
168
+ fit,
169
+ reset,
170
+ resetTransform,
171
+ zoomIn,
172
+ zoomOut,
173
+ zoomTo,
174
+ rotate,
175
+ flip,
176
+ toImageDelta
177
+ };
178
+ }
179
+ //#endregion
180
+ export { useTransform };
181
+
182
+ //# sourceMappingURL=use-transform.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-transform.js","names":[],"sources":["../../../src/components/image-cropper/use-transform.ts"],"sourcesContent":["import { onBeforeUnmount, reactive, watch, type Ref, type ShallowRef } from 'vue'\n\n/** 图片变换状态:缩放 / 平移 / 90° 步进旋转 / 翻转 */\nexport interface TransformState {\n scale: number\n translateX: number\n translateY: number\n /** 90° 步进角度:0 / 90 / 180 / 270 */\n rotation: number\n flipX: boolean\n flipY: boolean\n}\n\ninterface Size {\n width: number\n height: number\n}\n\n/** 画布内的屏幕坐标点(相对画布左上角) */\ninterface Point {\n x: number\n y: number\n}\n\n/** 滚轮 / 按钮单次缩放步进系数 */\nconst ZOOM_STEP = 1.2\n/** 最大缩放倍数(相对适应容器的缩放) */\nconst MAX_ZOOM_RATIO = 10\n\ninterface UseTransformOptions {\n /** 画布元素(wheel 缩放与选区外平移的交互目标) */\n target: ShallowRef<HTMLElement | undefined | null> | Ref<HTMLElement | undefined | null>\n /** 画布可视尺寸(屏幕像素) */\n canvasSize: () => Size\n /** 图片原始宽(图片像素) */\n imageWidth: () => number\n /** 图片原始高(图片像素) */\n imageHeight: () => number\n /** 重置变换时复位选区 */\n onReset: () => void\n}\n\ninterface UseTransformReturn {\n /** 当前变换状态 */\n transform: TransformState\n /** 图片按比例适应容器并居中(按当前旋转交换宽高计算) */\n fit: () => void\n /** 复位变换状态(不重新布局、不复位选区) */\n reset: () => void\n /** 重置:恢复初始变换,重新适应容器并复位选区 */\n resetTransform: () => void\n /** 放大一级,anchor 为缩放锚点(默认画布中心) */\n zoomIn: (anchor?: Point) => void\n /** 缩小一级,anchor 为缩放锚点(默认画布中心) */\n zoomOut: (anchor?: Point) => void\n /** 缩放到指定值(按适应缩放钳制范围),anchor 为缩放锚点(默认画布中心) */\n zoomTo: (scale: number, anchor?: Point) => void\n /** 90° 步进旋转,direction 为 1 顺时针、-1 逆时针 */\n rotate: (direction: 1 | -1) => void\n /** 水平 / 垂直翻转 */\n flip: (axis: 'horizontal' | 'vertical') => void\n /** 屏幕位移换算为图片像素位移(考虑旋转 / 翻转 / 缩放) */\n toImageDelta: (dx: number, dy: number) => Point\n}\n\n/**\n * 图片变换:滚轮以指针为锚缩放、选区外拖动平移、90° 步进旋转、翻转与重置。\n * 舞台 transform-origin 为中心,旋转 / 翻转不改变图片中心位置,无需补偿平移\n */\nexport function useTransform(options: UseTransformOptions): UseTransformReturn {\n const { target, canvasSize, imageWidth, imageHeight, onReset } = options\n\n const transform = reactive<TransformState>({\n scale: 1,\n translateX: 0,\n translateY: 0,\n rotation: 0,\n flipX: false,\n flipY: false\n })\n\n /** 当前旋转下的显示尺寸(90° / 270° 交换宽高,图片像素) */\n function displaySize(): Size {\n const width = imageWidth()\n const height = imageHeight()\n return transform.rotation % 180 === 0 ? { width, height } : { width: height, height: width }\n }\n\n /** 适应容器的缩放(同时是缩放范围下限) */\n function fitScale() {\n const { width: cw, height: ch } = canvasSize()\n const { width: dw, height: dh } = displaySize()\n if (!cw || !ch || !dw || !dh) return 0\n return Math.min(cw / dw, ch / dh)\n }\n\n function fit() {\n const scale = fitScale()\n if (!scale) return\n const { width: cw, height: ch } = canvasSize()\n transform.scale = scale\n // transform-origin 为舞台中心,居中平移只取决于舞台原始尺寸,与缩放 / 旋转无关\n transform.translateX = (cw - imageWidth()) / 2\n transform.translateY = (ch - imageHeight()) / 2\n }\n\n function reset() {\n transform.scale = 1\n transform.translateX = 0\n transform.translateY = 0\n transform.rotation = 0\n transform.flipX = false\n transform.flipY = false\n }\n\n function resetTransform() {\n reset()\n fit()\n onReset()\n }\n\n function zoomTo(scale: number, anchor?: Point) {\n const base = fitScale()\n if (!base) return\n const next = Math.min(Math.max(scale, base), base * MAX_ZOOM_RATIO)\n const { width: cw, height: ch } = canvasSize()\n const { x: ax, y: ay } = anchor ?? { x: cw / 2, y: ch / 2 }\n // 舞台中心(transform-origin)局部坐标\n const cx = imageWidth() / 2\n const cy = imageHeight() / 2\n const k = next / transform.scale\n // 缩放前后锚点下的图片点保持不动:t' = A - C - k·(A - t - C)\n transform.translateX = ax - cx - k * (ax - transform.translateX - cx)\n transform.translateY = ay - cy - k * (ay - transform.translateY - cy)\n transform.scale = next\n }\n\n function zoomIn(anchor?: Point) {\n zoomTo(transform.scale * ZOOM_STEP, anchor)\n }\n\n function zoomOut(anchor?: Point) {\n zoomTo(transform.scale / ZOOM_STEP, anchor)\n }\n\n function rotate(direction: 1 | -1) {\n transform.rotation = (transform.rotation + direction * 90 + 360) % 360\n // 90° / 270° 后按交换宽高重新适应布局\n fit()\n }\n\n function flip(axis: 'horizontal' | 'vertical') {\n if (axis === 'horizontal') transform.flipX = !transform.flipX\n else transform.flipY = !transform.flipY\n }\n\n function toImageDelta(dx: number, dy: number): Point {\n // 先按旋转逆变换,再除掉带翻转符号的缩放\n let x = dx\n let y = dy\n if (transform.rotation === 90) {\n x = dy\n y = -dx\n } else if (transform.rotation === 180) {\n x = -dx\n y = -dy\n } else if (transform.rotation === 270) {\n x = -dy\n y = dx\n }\n return {\n x: x / ((transform.flipX ? -1 : 1) * transform.scale),\n y: y / ((transform.flipY ? -1 : 1) * transform.scale)\n }\n }\n\n // ---------- wheel 缩放(以指针位置为锚点) ----------\n\n function handleWheel(e: WheelEvent) {\n e.preventDefault()\n const el = target.value\n if (!el) return\n const rect = el.getBoundingClientRect()\n const anchor = { x: e.clientX - rect.left, y: e.clientY - rect.top }\n if (e.deltaY < 0) zoomIn(anchor)\n else zoomOut(anchor)\n }\n\n // ---------- 选区外拖动平移(选区内拖动由 use-selection 拦截,不冒泡到这里) ----------\n\n /** 图片显示尺寸超出容器才允许平移 */\n function isOverflowing() {\n const { width: cw, height: ch } = canvasSize()\n const { width: dw, height: dh } = displaySize()\n return dw * transform.scale > cw || dh * transform.scale > ch\n }\n\n let panStart: Point | null = null\n\n function handleMousedown(e: MouseEvent) {\n if (e.button !== 0 || !isOverflowing()) return\n e.preventDefault()\n\n panStart = { x: e.clientX, y: e.clientY }\n document.addEventListener('mousemove', handleMousemove, { passive: true })\n document.addEventListener('mouseup', handleMouseup)\n }\n\n function handleMousemove(e: MouseEvent) {\n if (!panStart) return\n transform.translateX += e.clientX - panStart.x\n transform.translateY += e.clientY - panStart.y\n panStart = { x: e.clientX, y: e.clientY }\n }\n\n function handleMouseup() {\n panStart = null\n cleanup()\n }\n\n function cleanup() {\n document.removeEventListener('mousemove', handleMousemove)\n document.removeEventListener('mouseup', handleMouseup)\n }\n\n watch(\n target,\n (el, oldEl) => {\n oldEl?.removeEventListener('mousedown', handleMousedown)\n oldEl?.removeEventListener('wheel', handleWheel)\n el?.addEventListener('mousedown', handleMousedown)\n // wheel 需要 preventDefault 阻止页面滚动,必须非 passive\n el?.addEventListener('wheel', handleWheel, { passive: false })\n },\n { immediate: true }\n )\n\n onBeforeUnmount(() => {\n target.value?.removeEventListener('mousedown', handleMousedown)\n target.value?.removeEventListener('wheel', handleWheel)\n cleanup()\n })\n\n return {\n transform,\n fit,\n reset,\n resetTransform,\n zoomIn,\n zoomOut,\n zoomTo,\n rotate,\n flip,\n toImageDelta\n }\n}\n"],"mappings":";;;AAyBA,MAAM,YAAY;;AAElB,MAAM,iBAAiB;;;;;AA0CvB,SAAgB,aAAa,SAAkD;CAC7E,MAAM,EAAE,QAAQ,YAAY,YAAY,aAAa,YAAY;CAEjE,MAAM,YAAY,SAAyB;EACzC,OAAO;EACP,YAAY;EACZ,YAAY;EACZ,UAAU;EACV,OAAO;EACP,OAAO;CACT,CAAC;;CAGD,SAAS,cAAoB;EAC3B,MAAM,QAAQ,WAAW;EACzB,MAAM,SAAS,YAAY;EAC3B,OAAO,UAAU,WAAW,QAAQ,IAAI;GAAE;GAAO;EAAO,IAAI;GAAE,OAAO;GAAQ,QAAQ;EAAM;CAC7F;;CAGA,SAAS,WAAW;EAClB,MAAM,EAAE,OAAO,IAAI,QAAQ,OAAO,WAAW;EAC7C,MAAM,EAAE,OAAO,IAAI,QAAQ,OAAO,YAAY;EAC9C,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,OAAO;EACrC,OAAO,KAAK,IAAI,KAAK,IAAI,KAAK,EAAE;CAClC;CAEA,SAAS,MAAM;EACb,MAAM,QAAQ,SAAS;EACvB,IAAI,CAAC,OAAO;EACZ,MAAM,EAAE,OAAO,IAAI,QAAQ,OAAO,WAAW;EAC7C,UAAU,QAAQ;EAElB,UAAU,cAAc,KAAK,WAAW,KAAK;EAC7C,UAAU,cAAc,KAAK,YAAY,KAAK;CAChD;CAEA,SAAS,QAAQ;EACf,UAAU,QAAQ;EAClB,UAAU,aAAa;EACvB,UAAU,aAAa;EACvB,UAAU,WAAW;EACrB,UAAU,QAAQ;EAClB,UAAU,QAAQ;CACpB;CAEA,SAAS,iBAAiB;EACxB,MAAM;EACN,IAAI;EACJ,QAAQ;CACV;CAEA,SAAS,OAAO,OAAe,QAAgB;EAC7C,MAAM,OAAO,SAAS;EACtB,IAAI,CAAC,MAAM;EACX,MAAM,OAAO,KAAK,IAAI,KAAK,IAAI,OAAO,IAAI,GAAG,OAAO,cAAc;EAClE,MAAM,EAAE,OAAO,IAAI,QAAQ,OAAO,WAAW;EAC7C,MAAM,EAAE,GAAG,IAAI,GAAG,OAAO,UAAU;GAAE,GAAG,KAAK;GAAG,GAAG,KAAK;EAAE;EAE1D,MAAM,KAAK,WAAW,IAAI;EAC1B,MAAM,KAAK,YAAY,IAAI;EAC3B,MAAM,IAAI,OAAO,UAAU;EAE3B,UAAU,aAAa,KAAK,KAAK,KAAK,KAAK,UAAU,aAAa;EAClE,UAAU,aAAa,KAAK,KAAK,KAAK,KAAK,UAAU,aAAa;EAClE,UAAU,QAAQ;CACpB;CAEA,SAAS,OAAO,QAAgB;EAC9B,OAAO,UAAU,QAAQ,WAAW,MAAM;CAC5C;CAEA,SAAS,QAAQ,QAAgB;EAC/B,OAAO,UAAU,QAAQ,WAAW,MAAM;CAC5C;CAEA,SAAS,OAAO,WAAmB;EACjC,UAAU,YAAY,UAAU,WAAW,YAAY,KAAK,OAAO;EAEnE,IAAI;CACN;CAEA,SAAS,KAAK,MAAiC;EAC7C,IAAI,SAAS,cAAc,UAAU,QAAQ,CAAC,UAAU;OACnD,UAAU,QAAQ,CAAC,UAAU;CACpC;CAEA,SAAS,aAAa,IAAY,IAAmB;EAEnD,IAAI,IAAI;EACR,IAAI,IAAI;EACR,IAAI,UAAU,aAAa,IAAI;GAC7B,IAAI;GACJ,IAAI,CAAC;EACP,OAAO,IAAI,UAAU,aAAa,KAAK;GACrC,IAAI,CAAC;GACL,IAAI,CAAC;EACP,OAAO,IAAI,UAAU,aAAa,KAAK;GACrC,IAAI,CAAC;GACL,IAAI;EACN;EACA,OAAO;GACL,GAAG,MAAM,UAAU,QAAQ,KAAK,KAAK,UAAU;GAC/C,GAAG,MAAM,UAAU,QAAQ,KAAK,KAAK,UAAU;EACjD;CACF;CAIA,SAAS,YAAY,GAAe;EAClC,EAAE,eAAe;EACjB,MAAM,KAAK,OAAO;EAClB,IAAI,CAAC,IAAI;EACT,MAAM,OAAO,GAAG,sBAAsB;EACtC,MAAM,SAAS;GAAE,GAAG,EAAE,UAAU,KAAK;GAAM,GAAG,EAAE,UAAU,KAAK;EAAI;EACnE,IAAI,EAAE,SAAS,GAAG,OAAO,MAAM;OAC1B,QAAQ,MAAM;CACrB;;CAKA,SAAS,gBAAgB;EACvB,MAAM,EAAE,OAAO,IAAI,QAAQ,OAAO,WAAW;EAC7C,MAAM,EAAE,OAAO,IAAI,QAAQ,OAAO,YAAY;EAC9C,OAAO,KAAK,UAAU,QAAQ,MAAM,KAAK,UAAU,QAAQ;CAC7D;CAEA,IAAI,WAAyB;CAE7B,SAAS,gBAAgB,GAAe;EACtC,IAAI,EAAE,WAAW,KAAK,CAAC,cAAc,GAAG;EACxC,EAAE,eAAe;EAEjB,WAAW;GAAE,GAAG,EAAE;GAAS,GAAG,EAAE;EAAQ;EACxC,SAAS,iBAAiB,aAAa,iBAAiB,EAAE,SAAS,KAAK,CAAC;EACzE,SAAS,iBAAiB,WAAW,aAAa;CACpD;CAEA,SAAS,gBAAgB,GAAe;EACtC,IAAI,CAAC,UAAU;EACf,UAAU,cAAc,EAAE,UAAU,SAAS;EAC7C,UAAU,cAAc,EAAE,UAAU,SAAS;EAC7C,WAAW;GAAE,GAAG,EAAE;GAAS,GAAG,EAAE;EAAQ;CAC1C;CAEA,SAAS,gBAAgB;EACvB,WAAW;EACX,QAAQ;CACV;CAEA,SAAS,UAAU;EACjB,SAAS,oBAAoB,aAAa,eAAe;EACzD,SAAS,oBAAoB,WAAW,aAAa;CACvD;CAEA,MACE,SACC,IAAI,UAAU;EACb,OAAO,oBAAoB,aAAa,eAAe;EACvD,OAAO,oBAAoB,SAAS,WAAW;EAC/C,IAAI,iBAAiB,aAAa,eAAe;EAEjD,IAAI,iBAAiB,SAAS,aAAa,EAAE,SAAS,MAAM,CAAC;CAC/D,GACA,EAAE,WAAW,KAAK,CACpB;CAEA,sBAAsB;EACpB,OAAO,OAAO,oBAAoB,aAAa,eAAe;EAC9D,OAAO,OAAO,oBAAoB,SAAS,WAAW;EACtD,QAAQ;CACV,CAAC;CAED,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;AACF"}
@@ -88,6 +88,7 @@ import _sfc_main$82 from "./paginator/paginator.js";
88
88
  import _sfc_main$83 from "./float-button/float-button.js";
89
89
  import _sfc_main$84 from "./badge/badge.js";
90
90
  import _sfc_main$85 from "./segment/segment.js";
91
+ import _sfc_main$86 from "./image-cropper/image-cropper.js";
91
92
  import { EvaluateOptions, createEmptyGroup, createEmptyLeaf, evaluate } from "./condition-editor/core/evaluator.js";
92
93
  import "./condition-editor/index.js";
93
94
  import { messageConfirm } from "./message-confirm/message-confirm.js";
@@ -107,4 +108,4 @@ import { defineBatchEditColumns } from "./batch-edit/helper.js";
107
108
  import "./batch-edit/index.js";
108
109
  import { defineTableColumns } from "./table/use-columns.js";
109
110
  import "./table/index.js";
110
- export { type EvaluateOptions as ConditionEvaluateOptions, ContextmenuPanelDIKey, ContextmenuRootDIKey, _sfc_main$57 as UAction, action_group_default as UActionGroup, _sfc_main$31 as UAutoComplete, _sfc_main$84 as UBadge, _sfc_main$58 as UBatchEdit, _sfc_main$81 as UBreadcrumb, _sfc_main$17 as UButton, _sfc_main$18 as UButtonGroup, _sfc_main$20 as UCalendar, _sfc_main$22 as UCard, _sfc_main$26 as UCardAction, _sfc_main$25 as UCardContent, _sfc_main$24 as UCardCover, _sfc_main$23 as UCardHeader, _sfc_main$60 as UCascade, _sfc_main$48 as UCheckTag, _sfc_main$10 as UCheckbox, _sfc_main$36 as UCheckboxButton, _sfc_main$78 as UCheckboxGroup, _sfc_main$13 as UCodeEditor, _sfc_main$44 as UCollapse, _sfc_main$45 as UCollapseItem, _sfc_main$6 as UConditionEditor, _sfc_main$49 as UContextmenu, _sfc_main$27 as UDatePanel, _sfc_main$62 as UDatePicker, _sfc_main$28 as UDateRangePicker, _sfc_main$52 as UDialog, _sfc_main$14 as UDrawer, _sfc_main$4 as UDropdown, _sfc_main$73 as UDualNav, _sfc_main$1 as UEmpty, _sfc_main$53 as UExpressionEditor, _sfc_main$41 as UFilePicker, _sfc_main$79 as UFileViewer, _sfc_main$83 as UFloatButton, form_default as UForm, form_item_default as UFormItem, _sfc_main$39 as UGanttChart, _sfc_main$15 as UGrid, _sfc_main$75 as UGridInput, _sfc_main$16 as UGridItem, _sfc_main$66 as UGroupInput, _sfc_main$74 as UGroupNav, _sfc_main as UIcon, input_default as UInput, _sfc_main$55 as UKbd, _sfc_main$34 as ULayout, _sfc_main$67 as UList, _sfc_main$68 as UListItem, _sfc_main$29 as ULoading, _sfc_main$40 as UMessage, _sfc_main$19 as UMessageConfirm, _sfc_main$37 as UMultiSelect, _sfc_main$65 as UMultiTreeSelect, _sfc_main$72 as UNav, _sfc_main$70 as UNavItem, _sfc_main$71 as UNavSub, NodeRender as UNodeRender, _sfc_main$33 as UNotification, _sfc_main$51 as UNumber, _sfc_main$42 as UNumberInput, _sfc_main$43 as UNumberRangeInput, _sfc_main$82 as UPaginator, _sfc_main$63 as UPalette, _sfc_main$50 as UPasswordInput, _sfc_main$56 as UPopConfirm, _sfc_main$32 as UProgress, _sfc_main$30 as UProgressNodes, _sfc_main$21 as URadio, _sfc_main$77 as URadioGroup, _sfc_main$69 as URichTextEditor, _sfc_main$2 as UScroll, _sfc_main$85 as USegment, _sfc_main$5 as USelect, _sfc_main$38 as USlider, _sfc_main$61 as USteps, _sfc_main$76 as USwitch, _sfc_main$46 as UTable, _sfc_main$47 as UTableEditor, _sfc_main$9 as UTabs, _sfc_main$7 as UTabsHorizontal, _sfc_main$8 as UTabsVertical, _sfc_main$35 as UTag, _sfc_main$80 as UText, _sfc_main$59 as UTextarea, _sfc_main$64 as UTheme, _sfc_main$3 as UTip, _sfc_main$11 as UTree, _sfc_main$12 as UTreeSelect, _sfc_main$54 as UWatermark, contextmenu, createEmptyGroup, createEmptyLeaf, defineBatchEditColumns, defineTableColumns, evaluate as evaluateConditionExpression, message, messageConfirm, notification, vLoading };
111
+ export { type EvaluateOptions as ConditionEvaluateOptions, ContextmenuPanelDIKey, ContextmenuRootDIKey, _sfc_main$57 as UAction, action_group_default as UActionGroup, _sfc_main$31 as UAutoComplete, _sfc_main$84 as UBadge, _sfc_main$58 as UBatchEdit, _sfc_main$81 as UBreadcrumb, _sfc_main$17 as UButton, _sfc_main$18 as UButtonGroup, _sfc_main$20 as UCalendar, _sfc_main$22 as UCard, _sfc_main$26 as UCardAction, _sfc_main$25 as UCardContent, _sfc_main$24 as UCardCover, _sfc_main$23 as UCardHeader, _sfc_main$60 as UCascade, _sfc_main$48 as UCheckTag, _sfc_main$10 as UCheckbox, _sfc_main$36 as UCheckboxButton, _sfc_main$78 as UCheckboxGroup, _sfc_main$13 as UCodeEditor, _sfc_main$44 as UCollapse, _sfc_main$45 as UCollapseItem, _sfc_main$6 as UConditionEditor, _sfc_main$49 as UContextmenu, _sfc_main$27 as UDatePanel, _sfc_main$62 as UDatePicker, _sfc_main$28 as UDateRangePicker, _sfc_main$52 as UDialog, _sfc_main$14 as UDrawer, _sfc_main$4 as UDropdown, _sfc_main$73 as UDualNav, _sfc_main$1 as UEmpty, _sfc_main$53 as UExpressionEditor, _sfc_main$41 as UFilePicker, _sfc_main$79 as UFileViewer, _sfc_main$83 as UFloatButton, form_default as UForm, form_item_default as UFormItem, _sfc_main$39 as UGanttChart, _sfc_main$15 as UGrid, _sfc_main$75 as UGridInput, _sfc_main$16 as UGridItem, _sfc_main$66 as UGroupInput, _sfc_main$74 as UGroupNav, _sfc_main as UIcon, _sfc_main$86 as UImageCropper, input_default as UInput, _sfc_main$55 as UKbd, _sfc_main$34 as ULayout, _sfc_main$67 as UList, _sfc_main$68 as UListItem, _sfc_main$29 as ULoading, _sfc_main$40 as UMessage, _sfc_main$19 as UMessageConfirm, _sfc_main$37 as UMultiSelect, _sfc_main$65 as UMultiTreeSelect, _sfc_main$72 as UNav, _sfc_main$70 as UNavItem, _sfc_main$71 as UNavSub, NodeRender as UNodeRender, _sfc_main$33 as UNotification, _sfc_main$51 as UNumber, _sfc_main$42 as UNumberInput, _sfc_main$43 as UNumberRangeInput, _sfc_main$82 as UPaginator, _sfc_main$63 as UPalette, _sfc_main$50 as UPasswordInput, _sfc_main$56 as UPopConfirm, _sfc_main$32 as UProgress, _sfc_main$30 as UProgressNodes, _sfc_main$21 as URadio, _sfc_main$77 as URadioGroup, _sfc_main$69 as URichTextEditor, _sfc_main$2 as UScroll, _sfc_main$85 as USegment, _sfc_main$5 as USelect, _sfc_main$38 as USlider, _sfc_main$61 as USteps, _sfc_main$76 as USwitch, _sfc_main$46 as UTable, _sfc_main$47 as UTableEditor, _sfc_main$9 as UTabs, _sfc_main$7 as UTabsHorizontal, _sfc_main$8 as UTabsVertical, _sfc_main$35 as UTag, _sfc_main$80 as UText, _sfc_main$59 as UTextarea, _sfc_main$64 as UTheme, _sfc_main$3 as UTip, _sfc_main$11 as UTree, _sfc_main$12 as UTreeSelect, _sfc_main$54 as UWatermark, contextmenu, createEmptyGroup, createEmptyLeaf, defineBatchEditColumns, defineTableColumns, evaluate as evaluateConditionExpression, message, messageConfirm, notification, vLoading };
@@ -99,6 +99,7 @@ import _sfc_main$82 from "./paginator/paginator.js";
99
99
  import _sfc_main$83 from "./float-button/float-button.js";
100
100
  import _sfc_main$84 from "./badge/badge.js";
101
101
  import _sfc_main$85 from "./segment/segment.js";
102
+ import _sfc_main$86 from "./image-cropper/image-cropper.js";
102
103
  //#region src/components/index.ts
103
104
  var components_exports = /* @__PURE__ */ __exportAll({
104
105
  ContextmenuPanelDIKey: () => ContextmenuPanelDIKey,
@@ -148,6 +149,7 @@ var components_exports = /* @__PURE__ */ __exportAll({
148
149
  UGroupInput: () => _sfc_main$66,
149
150
  UGroupNav: () => _sfc_main$74,
150
151
  UIcon: () => _sfc_main,
152
+ UImageCropper: () => _sfc_main$86,
151
153
  UInput: () => input_default,
152
154
  UKbd: () => _sfc_main$55,
153
155
  ULayout: () => _sfc_main$34,
@@ -206,6 +208,6 @@ var components_exports = /* @__PURE__ */ __exportAll({
206
208
  vLoading: () => vLoading
207
209
  });
208
210
  //#endregion
209
- export { ContextmenuPanelDIKey, ContextmenuRootDIKey, _sfc_main$57 as UAction, action_group_default as UActionGroup, _sfc_main$31 as UAutoComplete, _sfc_main$84 as UBadge, _sfc_main$58 as UBatchEdit, _sfc_main$81 as UBreadcrumb, _sfc_main$17 as UButton, _sfc_main$18 as UButtonGroup, _sfc_main$20 as UCalendar, _sfc_main$22 as UCard, _sfc_main$26 as UCardAction, _sfc_main$25 as UCardContent, _sfc_main$24 as UCardCover, _sfc_main$23 as UCardHeader, _sfc_main$60 as UCascade, _sfc_main$48 as UCheckTag, _sfc_main$10 as UCheckbox, _sfc_main$36 as UCheckboxButton, _sfc_main$78 as UCheckboxGroup, _sfc_main$13 as UCodeEditor, _sfc_main$44 as UCollapse, _sfc_main$45 as UCollapseItem, _sfc_main$6 as UConditionEditor, _sfc_main$49 as UContextmenu, _sfc_main$27 as UDatePanel, _sfc_main$62 as UDatePicker, _sfc_main$28 as UDateRangePicker, _sfc_main$52 as UDialog, _sfc_main$14 as UDrawer, _sfc_main$4 as UDropdown, _sfc_main$73 as UDualNav, _sfc_main$1 as UEmpty, _sfc_main$53 as UExpressionEditor, _sfc_main$41 as UFilePicker, _sfc_main$79 as UFileViewer, _sfc_main$83 as UFloatButton, form_default as UForm, form_item_default as UFormItem, _sfc_main$39 as UGanttChart, _sfc_main$15 as UGrid, _sfc_main$75 as UGridInput, _sfc_main$16 as UGridItem, _sfc_main$66 as UGroupInput, _sfc_main$74 as UGroupNav, _sfc_main as UIcon, input_default as UInput, _sfc_main$55 as UKbd, _sfc_main$34 as ULayout, _sfc_main$67 as UList, _sfc_main$68 as UListItem, _sfc_main$29 as ULoading, _sfc_main$40 as UMessage, _sfc_main$19 as UMessageConfirm, _sfc_main$37 as UMultiSelect, _sfc_main$65 as UMultiTreeSelect, _sfc_main$72 as UNav, _sfc_main$70 as UNavItem, _sfc_main$71 as UNavSub, NodeRender as UNodeRender, _sfc_main$33 as UNotification, _sfc_main$51 as UNumber, _sfc_main$42 as UNumberInput, _sfc_main$43 as UNumberRangeInput, _sfc_main$82 as UPaginator, _sfc_main$63 as UPalette, _sfc_main$50 as UPasswordInput, _sfc_main$56 as UPopConfirm, _sfc_main$32 as UProgress, _sfc_main$30 as UProgressNodes, _sfc_main$21 as URadio, _sfc_main$77 as URadioGroup, _sfc_main$69 as URichTextEditor, _sfc_main$2 as UScroll, _sfc_main$85 as USegment, _sfc_main$5 as USelect, _sfc_main$38 as USlider, _sfc_main$61 as USteps, _sfc_main$76 as USwitch, _sfc_main$46 as UTable, _sfc_main$47 as UTableEditor, _sfc_main$9 as UTabs, _sfc_main$7 as UTabsHorizontal, _sfc_main$8 as UTabsVertical, _sfc_main$35 as UTag, _sfc_main$80 as UText, _sfc_main$59 as UTextarea, _sfc_main$64 as UTheme, _sfc_main$3 as UTip, _sfc_main$11 as UTree, _sfc_main$12 as UTreeSelect, _sfc_main$54 as UWatermark, components_exports, contextmenu, createEmptyGroup, createEmptyLeaf, defineBatchEditColumns, defineTableColumns, evaluate as evaluateConditionExpression, message, messageConfirm, notification, vLoading };
211
+ export { ContextmenuPanelDIKey, ContextmenuRootDIKey, _sfc_main$57 as UAction, action_group_default as UActionGroup, _sfc_main$31 as UAutoComplete, _sfc_main$84 as UBadge, _sfc_main$58 as UBatchEdit, _sfc_main$81 as UBreadcrumb, _sfc_main$17 as UButton, _sfc_main$18 as UButtonGroup, _sfc_main$20 as UCalendar, _sfc_main$22 as UCard, _sfc_main$26 as UCardAction, _sfc_main$25 as UCardContent, _sfc_main$24 as UCardCover, _sfc_main$23 as UCardHeader, _sfc_main$60 as UCascade, _sfc_main$48 as UCheckTag, _sfc_main$10 as UCheckbox, _sfc_main$36 as UCheckboxButton, _sfc_main$78 as UCheckboxGroup, _sfc_main$13 as UCodeEditor, _sfc_main$44 as UCollapse, _sfc_main$45 as UCollapseItem, _sfc_main$6 as UConditionEditor, _sfc_main$49 as UContextmenu, _sfc_main$27 as UDatePanel, _sfc_main$62 as UDatePicker, _sfc_main$28 as UDateRangePicker, _sfc_main$52 as UDialog, _sfc_main$14 as UDrawer, _sfc_main$4 as UDropdown, _sfc_main$73 as UDualNav, _sfc_main$1 as UEmpty, _sfc_main$53 as UExpressionEditor, _sfc_main$41 as UFilePicker, _sfc_main$79 as UFileViewer, _sfc_main$83 as UFloatButton, form_default as UForm, form_item_default as UFormItem, _sfc_main$39 as UGanttChart, _sfc_main$15 as UGrid, _sfc_main$75 as UGridInput, _sfc_main$16 as UGridItem, _sfc_main$66 as UGroupInput, _sfc_main$74 as UGroupNav, _sfc_main as UIcon, _sfc_main$86 as UImageCropper, input_default as UInput, _sfc_main$55 as UKbd, _sfc_main$34 as ULayout, _sfc_main$67 as UList, _sfc_main$68 as UListItem, _sfc_main$29 as ULoading, _sfc_main$40 as UMessage, _sfc_main$19 as UMessageConfirm, _sfc_main$37 as UMultiSelect, _sfc_main$65 as UMultiTreeSelect, _sfc_main$72 as UNav, _sfc_main$70 as UNavItem, _sfc_main$71 as UNavSub, NodeRender as UNodeRender, _sfc_main$33 as UNotification, _sfc_main$51 as UNumber, _sfc_main$42 as UNumberInput, _sfc_main$43 as UNumberRangeInput, _sfc_main$82 as UPaginator, _sfc_main$63 as UPalette, _sfc_main$50 as UPasswordInput, _sfc_main$56 as UPopConfirm, _sfc_main$32 as UProgress, _sfc_main$30 as UProgressNodes, _sfc_main$21 as URadio, _sfc_main$77 as URadioGroup, _sfc_main$69 as URichTextEditor, _sfc_main$2 as UScroll, _sfc_main$85 as USegment, _sfc_main$5 as USelect, _sfc_main$38 as USlider, _sfc_main$61 as USteps, _sfc_main$76 as USwitch, _sfc_main$46 as UTable, _sfc_main$47 as UTableEditor, _sfc_main$9 as UTabs, _sfc_main$7 as UTabsHorizontal, _sfc_main$8 as UTabsVertical, _sfc_main$35 as UTag, _sfc_main$80 as UText, _sfc_main$59 as UTextarea, _sfc_main$64 as UTheme, _sfc_main$3 as UTip, _sfc_main$11 as UTree, _sfc_main$12 as UTreeSelect, _sfc_main$54 as UWatermark, components_exports, contextmenu, createEmptyGroup, createEmptyLeaf, defineBatchEditColumns, defineTableColumns, evaluate as evaluateConditionExpression, message, messageConfirm, notification, vLoading };
210
212
 
211
213
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/components/index.ts"],"sourcesContent":["export * from './condition-editor'\n\nexport * from './tabs'\n\nexport * from './tree'\n\nexport * from './tree-select'\n\nexport * from './code-editor'\n\nexport * from './dropdown'\n\nexport * from './drawer'\n\nexport * from './form'\n\nexport * from './message-confirm'\n\nexport * from './calendar'\n\nexport * from './radio'\n\nexport * from './card'\n\nexport * from './date-range-picker'\n\nexport * from './loading'\n\nexport * from './scroll'\n\nexport * from './progress-nodes'\n\nexport * from './empty'\n\nexport * from './auto-complete'\n\nexport * from './progress'\n\nexport * from './input'\n\nexport * from './notification'\n\nexport * from './layout'\n\nexport * from './multi-select'\n\nexport * from './checkbox'\n\nexport * from './slider'\n\nexport * from './gantt-chart'\n\nexport * from './message'\n\nexport * from './file-picker'\n\nexport * from './number-range-input'\n\nexport * from './number-input'\n\nexport * from './collapse'\n\nexport * from './date-panel'\n\nexport * from './table-editor'\n\nexport * from './check-tag'\n\nexport * from './contextmenu'\n\nexport * from './password-input'\n\nexport * from './number'\n\nexport * from './dialog'\n\nexport * from './node-render'\n\nexport * from './expression-editor'\n\nexport * from './watermark'\n\nexport * from './batch-edit'\n\nexport * from './action'\n\nexport * from './tip'\n\nexport * from './textarea'\n\nexport * from './button'\n\nexport * from './cascade'\n\nexport * from './steps'\n\nexport * from './date-picker'\n\nexport * from './theme'\n\nexport * from './table'\n\nexport * from './multi-tree-select'\n\nexport * from './group-input'\n\nexport * from './list'\n\nexport * from './rich-text-editor'\n\nexport * from './nav'\n\nexport * from './dual-nav'\n\nexport * from './group-nav'\n\nexport * from './grid-input'\n\nexport * from './switch'\n\nexport * from './form-item'\n\nexport * from './radio-group'\n\nexport * from './checkbox-group'\n\nexport * from './file-viewer'\n\nexport * from './text'\n\nexport * from './select'\n\nexport * from './breadcrumb'\n\nexport * from './icon'\n\nexport * from './kbd'\n\nexport * from './pop-confirm'\n\nexport * from './paginator'\n\nexport * from './tag'\n\nexport * from './float-button'\n\nexport * from './palette'\n\nexport * from './grid'\n\nexport * from './badge'\n\nexport * from './segment'\n"],"mappings":""}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/components/index.ts"],"sourcesContent":["export * from './condition-editor'\n\nexport * from './tabs'\n\nexport * from './tree'\n\nexport * from './tree-select'\n\nexport * from './code-editor'\n\nexport * from './dropdown'\n\nexport * from './drawer'\n\nexport * from './form'\n\nexport * from './message-confirm'\n\nexport * from './calendar'\n\nexport * from './radio'\n\nexport * from './card'\n\nexport * from './date-range-picker'\n\nexport * from './loading'\n\nexport * from './scroll'\n\nexport * from './progress-nodes'\n\nexport * from './empty'\n\nexport * from './auto-complete'\n\nexport * from './progress'\n\nexport * from './input'\n\nexport * from './notification'\n\nexport * from './layout'\n\nexport * from './multi-select'\n\nexport * from './checkbox'\n\nexport * from './slider'\n\nexport * from './gantt-chart'\n\nexport * from './message'\n\nexport * from './file-picker'\n\nexport * from './number-range-input'\n\nexport * from './number-input'\n\nexport * from './collapse'\n\nexport * from './date-panel'\n\nexport * from './table-editor'\n\nexport * from './check-tag'\n\nexport * from './contextmenu'\n\nexport * from './password-input'\n\nexport * from './number'\n\nexport * from './dialog'\n\nexport * from './node-render'\n\nexport * from './expression-editor'\n\nexport * from './watermark'\n\nexport * from './batch-edit'\n\nexport * from './action'\n\nexport * from './tip'\n\nexport * from './textarea'\n\nexport * from './button'\n\nexport * from './cascade'\n\nexport * from './steps'\n\nexport * from './date-picker'\n\nexport * from './theme'\n\nexport * from './table'\n\nexport * from './multi-tree-select'\n\nexport * from './group-input'\n\nexport * from './list'\n\nexport * from './rich-text-editor'\n\nexport * from './nav'\n\nexport * from './dual-nav'\n\nexport * from './group-nav'\n\nexport * from './grid-input'\n\nexport * from './switch'\n\nexport * from './form-item'\n\nexport * from './radio-group'\n\nexport * from './checkbox-group'\n\nexport * from './file-viewer'\n\nexport * from './text'\n\nexport * from './select'\n\nexport * from './breadcrumb'\n\nexport * from './icon'\n\nexport * from './kbd'\n\nexport * from './pop-confirm'\n\nexport * from './paginator'\n\nexport * from './tag'\n\nexport * from './float-button'\n\nexport * from './palette'\n\nexport * from './grid'\n\nexport * from './badge'\n\nexport * from './segment'\n\nexport * from './image-cropper'\n"],"mappings":""}