wimui 0.55.0 → 0.56.0

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.
@@ -1,2 +1,2 @@
1
1
  "use client";
2
- const e=require("../../../_virtual/_rolldown/runtime.cjs"),t=require("../../../i18n/useWimTranslation.cjs"),n=require("../../../i18n/generated/common.cjs"),r=require("../../layout/VisuallyHidden/VisuallyHidden.cjs"),i=require("../Button/Button.cjs"),a=require("../../../i18n/generated/form.cjs"),o=require("../Slider/Slider.cjs"),s=require("../IconButton/IconButton.cjs"),c=require("../../overlay/Dialog/Dialog.cjs"),l=require("./image-cropper.module.cjs");let u=require("react");u=e.__toESM(u,1);let d=require("classnames");d=e.__toESM(d,1);let f=require("react/jsx-runtime");var p=10,m=50,h=u.default.forwardRef(({src:e,aspectRatio:h=1,circular:g=!1,onCrop:_,showRotation:v=!0,showZoom:y=!0,showApplyButton:b=!0,applyLabel:x,onApply:S,className:C,...w},T)=>{let{t:E}=t.useWimTranslation([a.formNs,n.commonNs]),[D,O]=(0,u.useState)(1),[k,A]=(0,u.useState)(0),[j,M]=(0,u.useState)({x:0,y:0}),[N,P]=(0,u.useState)(!1),[F,I]=(0,u.useState)({x:0,y:0}),[L,R]=(0,u.useState)(!1),z=(0,u.useRef)(null),B=(0,u.useRef)(null),V=(0,u.useId)();(0,u.useEffect)(()=>{M({x:0,y:0}),O(1),A(0)},[e]);let H=e=>{P(!0),I({x:e.clientX-j.x,y:e.clientY-j.y})},U=(0,u.useCallback)(e=>{N&&M({x:e.clientX-F.x,y:e.clientY-F.y})},[N,F]),W=(0,u.useCallback)(()=>{P(!1)},[]);(0,u.useEffect)(()=>(N?(window.addEventListener(`mousemove`,U),window.addEventListener(`mouseup`,W)):(window.removeEventListener(`mousemove`,U),window.removeEventListener(`mouseup`,W)),()=>{window.removeEventListener(`mousemove`,U),window.removeEventListener(`mouseup`,W)}),[N,U,W]);let G=e=>{let t=e.shiftKey?m:p,n={ArrowLeft:[-t,0],ArrowRight:[t,0],ArrowUp:[0,-t],ArrowDown:[0,t]}[e.key];n?(e.preventDefault(),M(e=>({x:e.x+n[0],y:e.y+n[1]}))):e.key===`Home`&&(e.preventDefault(),M({x:0,y:0}))},K=()=>{A(e=>(e+90)%360)},q=()=>{R(!0)},J=()=>{let t=e||``;_?.(t),S?.(t),R(!1)};return e?(0,f.jsxs)(`div`,{ref:T,className:(0,d.default)(`wim-image-cropper`,l.default.root,C),...w,children:[(0,f.jsxs)(`div`,{ref:B,className:l.default.viewer,onMouseDown:H,onKeyDown:G,role:`application`,tabIndex:0,"aria-label":E(`image_cropper.position_label`),"aria-describedby":V,children:[(0,f.jsx)(r.VisuallyHidden,{id:V,children:E(`image_cropper.position_hint`)}),(0,f.jsx)(`div`,{className:l.default.imageContainer,children:(0,f.jsx)(`img`,{ref:z,src:e,alt:E(`a11y.crop_target`),className:l.default.image,style:{transform:`translate(${j.x}px, ${j.y}px) rotate(${k}deg) scale(${D})`},draggable:!1})}),(0,f.jsx)(`div`,{className:l.default.overlay,children:(0,f.jsx)(`div`,{className:(0,d.default)(l.default.cropArea,{[l.default.circular]:g}),style:{aspectRatio:h}})})]}),(0,f.jsxs)(`div`,{className:l.default.controls,children:[y&&(0,f.jsx)(`div`,{className:l.default.zoomControl,children:(0,f.jsx)(o.Slider,{min:1,max:3,step:.1,value:D,onChange:e=>O(e),label:E(`image_cropper.zoom`),styles:{root:l.default.zoomSlider}})}),v&&(0,f.jsx)(`div`,{className:l.default.rotationControl,children:(0,f.jsx)(s.IconButton,{iconName:`RefreshIcon`,onClick:K,"aria-label":E(`image_cropper.rotate`),variant:`outline`,size:`sm`})}),b&&(0,f.jsx)(`div`,{className:l.default.applyControl,children:(0,f.jsx)(i.Button,{onClick:q,size:`sm`,variant:`solid`,children:x||E(`image_cropper.apply`)})})]}),(0,f.jsx)(c.Dialog,{open:L,onOpenChange:R,children:(0,f.jsxs)(c.DialogContent,{children:[(0,f.jsx)(c.DialogHeader,{children:(0,f.jsx)(c.DialogTitle,{children:E(`image_cropper.confirm_title`)})}),(0,f.jsx)(c.DialogDescription,{children:E(`image_cropper.confirm_message`)}),(0,f.jsxs)(c.DialogFooter,{children:[(0,f.jsx)(c.DialogClose,{asChild:!0,children:(0,f.jsx)(i.Button,{variant:`ghost`,children:E(`image_cropper.cancel`)})}),(0,f.jsx)(i.Button,{onClick:J,variant:`solid`,children:E(`image_cropper.confirm`)})]})]})})]}):(0,f.jsx)(`div`,{ref:T,className:(0,d.default)(`wim-image-cropper`,l.default.root,l.default.empty,C),...w,children:(0,f.jsx)(`div`,{className:l.default.emptyContent,children:E(`image_cropper.no_image`)})})});h.displayName=`ImageCropper`,exports.ImageCropper=h;
2
+ const e=require("../../../_virtual/_rolldown/runtime.cjs"),t=require("../../../i18n/useWimTranslation.cjs"),n=require("../../../i18n/generated/common.cjs"),r=require("../../layout/VisuallyHidden/VisuallyHidden.cjs"),i=require("../Button/Button.cjs"),a=require("../../../i18n/generated/form.cjs"),o=require("../Slider/Slider.cjs"),s=require("../IconButton/IconButton.cjs"),c=require("../../overlay/Dialog/Dialog.cjs"),l=require("./cropImage.cjs"),u=require("./image-cropper.module.cjs");let d=require("react");d=e.__toESM(d,1);let f=require("classnames");f=e.__toESM(f,1);let p=require("react/jsx-runtime");var m=10,h=50,g=d.default.forwardRef(({src:e,aspectRatio:g=1,circular:_=!1,onCrop:v,showRotation:y=!0,showZoom:b=!0,showApplyButton:x=!0,applyLabel:S,onApply:C,onCropError:w,maxOutputSize:T,outputType:E=`image/png`,outputQuality:D=.92,crossOrigin:O,className:k,...A},j)=>{let{t:M}=t.useWimTranslation([a.formNs,n.commonNs]),[N,P]=(0,d.useState)(1),[F,I]=(0,d.useState)(0),[L,R]=(0,d.useState)({x:0,y:0}),[z,B]=(0,d.useState)(!1),[V,H]=(0,d.useState)({x:0,y:0}),[U,W]=(0,d.useState)(!1),G=(0,d.useRef)(null),K=(0,d.useRef)(null),q=(0,d.useRef)(null),J=(0,d.useId)();(0,d.useEffect)(()=>{R({x:0,y:0}),P(1),I(0)},[e]);let Y=e=>{B(!0),H({x:e.clientX-L.x,y:e.clientY-L.y})},X=(0,d.useCallback)(e=>{z&&R({x:e.clientX-V.x,y:e.clientY-V.y})},[z,V]),Z=(0,d.useCallback)(()=>{B(!1)},[]);(0,d.useEffect)(()=>(z?(window.addEventListener(`mousemove`,X),window.addEventListener(`mouseup`,Z)):(window.removeEventListener(`mousemove`,X),window.removeEventListener(`mouseup`,Z)),()=>{window.removeEventListener(`mousemove`,X),window.removeEventListener(`mouseup`,Z)}),[z,X,Z]);let Q=e=>{let t=e.shiftKey?h:m,n={ArrowLeft:[-t,0],ArrowRight:[t,0],ArrowUp:[0,-t],ArrowDown:[0,t]}[e.key];n?(e.preventDefault(),R(e=>({x:e.x+n[0],y:e.y+n[1]}))):e.key===`Home`&&(e.preventDefault(),R({x:0,y:0}))},$=()=>{I(e=>(e+90)%360)},ee=()=>{W(!0)},te=()=>{let e=G.current,t=q.current;if(W(!1),!e||!t)return;let n={x:L.x,y:L.y,zoom:N,rotation:F,frameWidth:t.clientWidth,frameHeight:t.clientHeight,layoutWidth:e.offsetWidth,layoutHeight:e.offsetHeight,naturalWidth:e.naturalWidth,naturalHeight:e.naturalHeight,maxOutputSize:T};try{let{dataUrl:t,detail:r}=l.cropImage(e,n,E,D);v?.(t,r),C?.(t,r)}catch(e){w?.(e instanceof Error?e:Error(String(e)),l.resolveCropDetail(n).detail)}};return e?(0,p.jsxs)(`div`,{ref:j,className:(0,f.default)(`wim-image-cropper`,u.default.root,k),...A,children:[(0,p.jsxs)(`div`,{ref:K,className:u.default.viewer,onMouseDown:Y,onKeyDown:Q,role:`application`,tabIndex:0,"aria-label":M(`image_cropper.position_label`),"aria-describedby":J,children:[(0,p.jsx)(r.VisuallyHidden,{id:J,children:M(`image_cropper.position_hint`)}),(0,p.jsx)(`div`,{className:u.default.imageContainer,children:(0,p.jsx)(`img`,{ref:G,src:e,alt:M(`a11y.crop_target`),crossOrigin:O,className:u.default.image,style:{transform:`translate(${L.x}px, ${L.y}px) rotate(${F}deg) scale(${N})`},draggable:!1})}),(0,p.jsx)(`div`,{className:u.default.overlay,children:(0,p.jsx)(`div`,{ref:q,className:(0,f.default)(u.default.cropArea,{[u.default.circular]:_}),style:{aspectRatio:g}})})]}),(0,p.jsxs)(`div`,{className:u.default.controls,children:[b&&(0,p.jsx)(`div`,{className:u.default.zoomControl,children:(0,p.jsx)(o.Slider,{min:1,max:3,step:.1,value:N,onChange:e=>P(e),label:M(`image_cropper.zoom`),styles:{root:u.default.zoomSlider}})}),y&&(0,p.jsx)(`div`,{className:u.default.rotationControl,children:(0,p.jsx)(s.IconButton,{iconName:`RefreshIcon`,onClick:$,"aria-label":M(`image_cropper.rotate`),variant:`outline`,size:`sm`})}),x&&(0,p.jsx)(`div`,{className:u.default.applyControl,children:(0,p.jsx)(i.Button,{onClick:ee,size:`sm`,variant:`solid`,children:S||M(`image_cropper.apply`)})})]}),(0,p.jsx)(c.Dialog,{open:U,onOpenChange:W,children:(0,p.jsxs)(c.DialogContent,{children:[(0,p.jsx)(c.DialogHeader,{children:(0,p.jsx)(c.DialogTitle,{children:M(`image_cropper.confirm_title`)})}),(0,p.jsx)(c.DialogDescription,{children:M(`image_cropper.confirm_message`)}),(0,p.jsxs)(c.DialogFooter,{children:[(0,p.jsx)(c.DialogClose,{asChild:!0,children:(0,p.jsx)(i.Button,{variant:`ghost`,children:M(`image_cropper.cancel`)})}),(0,p.jsx)(i.Button,{onClick:te,variant:`solid`,children:M(`image_cropper.confirm`)})]})]})})]}):(0,p.jsx)(`div`,{ref:j,className:(0,f.default)(`wim-image-cropper`,u.default.root,u.default.empty,k),...A,children:(0,p.jsx)(`div`,{className:u.default.emptyContent,children:M(`image_cropper.no_image`)})})});g.displayName=`ImageCropper`,exports.ImageCropper=g;
@@ -1,4 +1,6 @@
1
1
  import { default as React } from 'react';
2
+ import { ImageCropDetail, ImageCropOutputType } from './cropImage';
3
+ export type { ImageCropDetail, ImageCropOutputType };
2
4
  export interface ImageCropperProps extends React.ComponentPropsWithoutRef<"div"> {
3
5
  /** URL or data URL of the image to crop */
4
6
  src?: string;
@@ -6,8 +8,11 @@ export interface ImageCropperProps extends React.ComponentPropsWithoutRef<"div">
6
8
  aspectRatio?: number;
7
9
  /** Whether to display a circular crop area (for profile images) */
8
10
  circular?: boolean;
9
- /** Callback when the crop result is finalized */
10
- onCrop?: (dataUrl: string) => void;
11
+ /**
12
+ * Called with the cropped image as a data URL once the crop is confirmed. The second argument
13
+ * describes the crop (offset, zoom, rotation, frame and output size) so it can be redone elsewhere.
14
+ */
15
+ onCrop?: (dataUrl: string, detail: ImageCropDetail) => void;
11
16
  /** Whether to show the rotation button */
12
17
  showRotation?: boolean;
13
18
  /** Whether to show the zoom slider */
@@ -16,8 +21,35 @@ export interface ImageCropperProps extends React.ComponentPropsWithoutRef<"div">
16
21
  showApplyButton?: boolean;
17
22
  /** Label of the apply button */
18
23
  applyLabel?: string;
19
- /** Callback when the crop is applied (after confirming in the dialog) */
20
- onApply?: (dataUrl: string) => void;
24
+ /** Called with the cropped image and the crop detail when the crop is applied (after confirming in the dialog) */
25
+ onApply?: (dataUrl: string, detail: ImageCropDetail) => void;
26
+ /**
27
+ * Called instead of onCrop and onApply when the image cannot be produced, with the crop detail.
28
+ * The usual cause is an image from another origin drawn without CORS approval: the browser then
29
+ * refuses to read the canvas. Set crossOrigin and serve the image with CORS headers, or crop on
30
+ * the server from the detail.
31
+ */
32
+ onCropError?: (error: Error, detail: ImageCropDetail) => void;
33
+ /**
34
+ * Upper bound, in px, for the longer side of the output image. The crop is taken at the
35
+ * resolution of the source image; without a bound a large photo produces a large data URL.
36
+ */
37
+ maxOutputSize?: number;
38
+ /**
39
+ * Image format of the output. "image/png" keeps transparency where the image does not cover the frame.
40
+ * @default "image/png"
41
+ */
42
+ outputType?: ImageCropOutputType;
43
+ /**
44
+ * Quality from 0 to 1 for "image/jpeg" and "image/webp". Ignored for "image/png".
45
+ * @default 0.92
46
+ */
47
+ outputQuality?: number;
48
+ /**
49
+ * CORS mode used to load the image. Needed to crop an image served from another origin;
50
+ * the server must send matching CORS headers or the image will not load at all.
51
+ */
52
+ crossOrigin?: "anonymous" | "use-credentials";
21
53
  }
22
54
  /**
23
55
  * Component for cropping and rotating an image.
@@ -6,173 +6,195 @@ import { Button as r } from "../Button/Button.js";
6
6
  import { formNs as i } from "../../../i18n/generated/form.js";
7
7
  import { Slider as a } from "../Slider/Slider.js";
8
8
  import { IconButton as o } from "../IconButton/IconButton.js";
9
- import { Dialog as s, DialogClose as c, DialogContent as l, DialogDescription as u, DialogFooter as d, DialogHeader as f, DialogTitle as ee } from "../../overlay/Dialog/Dialog.js";
10
- import p from "./image-cropper.module.js";
11
- import m, { useCallback as h, useEffect as g, useId as _, useRef as v, useState as y } from "react";
12
- import b from "classnames";
13
- import { jsx as x, jsxs as S } from "react/jsx-runtime";
9
+ import { Dialog as s, DialogClose as c, DialogContent as l, DialogDescription as u, DialogFooter as ee, DialogHeader as d, DialogTitle as te } from "../../overlay/Dialog/Dialog.js";
10
+ import { cropImage as f, resolveCropDetail as p } from "./cropImage.js";
11
+ import m from "./image-cropper.module.js";
12
+ import h, { useCallback as g, useEffect as _, useId as v, useRef as y, useState as b } from "react";
13
+ import x from "classnames";
14
+ import { jsx as S, jsxs as C } from "react/jsx-runtime";
14
15
  //#region src/components/form/ImageCropper/ImageCropper.tsx
15
- var C = 10, w = 50, T = m.forwardRef(({ src: m, aspectRatio: T = 1, circular: E = !1, onCrop: D, showRotation: O = !0, showZoom: te = !0, showApplyButton: k = !0, applyLabel: A, onApply: j, className: M, ...N }, P) => {
16
- let { t: F } = e([i, t]), [I, L] = y(1), [R, z] = y(0), [B, V] = y({
16
+ var w = 10, T = 50, E = h.forwardRef(({ src: h, aspectRatio: E = 1, circular: ne = !1, onCrop: re, showRotation: ie = !0, showZoom: ae = !0, showApplyButton: oe = !0, applyLabel: D, onApply: O, onCropError: k, maxOutputSize: A, outputType: j = "image/png", outputQuality: M = .92, crossOrigin: N, className: P, ...F }, I) => {
17
+ let { t: L } = e([i, t]), [R, z] = b(1), [B, V] = b(0), [H, U] = b({
17
18
  x: 0,
18
19
  y: 0
19
- }), [H, U] = y(!1), [W, G] = y({
20
+ }), [W, G] = b(!1), [K, q] = b({
20
21
  x: 0,
21
22
  y: 0
22
- }), [K, q] = y(!1), J = v(null), Y = v(null), X = _();
23
- g(() => {
24
- V({
23
+ }), [se, J] = b(!1), Y = y(null), ce = y(null), X = y(null), Z = v();
24
+ _(() => {
25
+ U({
25
26
  x: 0,
26
27
  y: 0
27
- }), L(1), z(0);
28
- }, [m]);
29
- let Z = (e) => {
30
- U(!0), G({
31
- x: e.clientX - B.x,
32
- y: e.clientY - B.y
28
+ }), z(1), V(0);
29
+ }, [h]);
30
+ let le = (e) => {
31
+ G(!0), q({
32
+ x: e.clientX - H.x,
33
+ y: e.clientY - H.y
33
34
  });
34
- }, Q = h((e) => {
35
- H && V({
36
- x: e.clientX - W.x,
37
- y: e.clientY - W.y
35
+ }, Q = g((e) => {
36
+ W && U({
37
+ x: e.clientX - K.x,
38
+ y: e.clientY - K.y
38
39
  });
39
- }, [H, W]), $ = h(() => {
40
- U(!1);
40
+ }, [W, K]), $ = g(() => {
41
+ G(!1);
41
42
  }, []);
42
- g(() => (H ? (window.addEventListener("mousemove", Q), window.addEventListener("mouseup", $)) : (window.removeEventListener("mousemove", Q), window.removeEventListener("mouseup", $)), () => {
43
+ _(() => (W ? (window.addEventListener("mousemove", Q), window.addEventListener("mouseup", $)) : (window.removeEventListener("mousemove", Q), window.removeEventListener("mouseup", $)), () => {
43
44
  window.removeEventListener("mousemove", Q), window.removeEventListener("mouseup", $);
44
45
  }), [
45
- H,
46
+ W,
46
47
  Q,
47
48
  $
48
49
  ]);
49
- let ne = (e) => {
50
- let t = e.shiftKey ? w : C, n = {
50
+ let ue = (e) => {
51
+ let t = e.shiftKey ? T : w, n = {
51
52
  ArrowLeft: [-t, 0],
52
53
  ArrowRight: [t, 0],
53
54
  ArrowUp: [0, -t],
54
55
  ArrowDown: [0, t]
55
56
  }[e.key];
56
- n ? (e.preventDefault(), V((e) => ({
57
+ n ? (e.preventDefault(), U((e) => ({
57
58
  x: e.x + n[0],
58
59
  y: e.y + n[1]
59
- }))) : e.key === "Home" && (e.preventDefault(), V({
60
+ }))) : e.key === "Home" && (e.preventDefault(), U({
60
61
  x: 0,
61
62
  y: 0
62
63
  }));
63
- }, re = () => {
64
- z((e) => (e + 90) % 360);
65
- }, ie = () => {
66
- q(!0);
67
- }, ae = () => {
68
- let e = m || "";
69
- D?.(e), j?.(e), q(!1);
64
+ }, de = () => {
65
+ V((e) => (e + 90) % 360);
66
+ }, fe = () => {
67
+ J(!0);
68
+ }, pe = () => {
69
+ let e = Y.current, t = X.current;
70
+ if (J(!1), !e || !t) return;
71
+ let n = {
72
+ x: H.x,
73
+ y: H.y,
74
+ zoom: R,
75
+ rotation: B,
76
+ frameWidth: t.clientWidth,
77
+ frameHeight: t.clientHeight,
78
+ layoutWidth: e.offsetWidth,
79
+ layoutHeight: e.offsetHeight,
80
+ naturalWidth: e.naturalWidth,
81
+ naturalHeight: e.naturalHeight,
82
+ maxOutputSize: A
83
+ };
84
+ try {
85
+ let { dataUrl: t, detail: r } = f(e, n, j, M);
86
+ re?.(t, r), O?.(t, r);
87
+ } catch (e) {
88
+ k?.(e instanceof Error ? e : Error(String(e)), p(n).detail);
89
+ }
70
90
  };
71
- return m ? /* @__PURE__ */ S("div", {
72
- ref: P,
73
- className: b("wim-image-cropper", p.root, M),
74
- ...N,
91
+ return h ? /* @__PURE__ */ C("div", {
92
+ ref: I,
93
+ className: x("wim-image-cropper", m.root, P),
94
+ ...F,
75
95
  children: [
76
- /* @__PURE__ */ S("div", {
77
- ref: Y,
78
- className: p.viewer,
79
- onMouseDown: Z,
80
- onKeyDown: ne,
96
+ /* @__PURE__ */ C("div", {
97
+ ref: ce,
98
+ className: m.viewer,
99
+ onMouseDown: le,
100
+ onKeyDown: ue,
81
101
  role: "application",
82
102
  tabIndex: 0,
83
- "aria-label": F("image_cropper.position_label"),
84
- "aria-describedby": X,
103
+ "aria-label": L("image_cropper.position_label"),
104
+ "aria-describedby": Z,
85
105
  children: [
86
- /* @__PURE__ */ x(n, {
87
- id: X,
88
- children: F("image_cropper.position_hint")
106
+ /* @__PURE__ */ S(n, {
107
+ id: Z,
108
+ children: L("image_cropper.position_hint")
89
109
  }),
90
- /* @__PURE__ */ x("div", {
91
- className: p.imageContainer,
92
- children: /* @__PURE__ */ x("img", {
93
- ref: J,
94
- src: m,
95
- alt: F("a11y.crop_target"),
96
- className: p.image,
97
- style: { transform: `translate(${B.x}px, ${B.y}px) rotate(${R}deg) scale(${I})` },
110
+ /* @__PURE__ */ S("div", {
111
+ className: m.imageContainer,
112
+ children: /* @__PURE__ */ S("img", {
113
+ ref: Y,
114
+ src: h,
115
+ alt: L("a11y.crop_target"),
116
+ crossOrigin: N,
117
+ className: m.image,
118
+ style: { transform: `translate(${H.x}px, ${H.y}px) rotate(${B}deg) scale(${R})` },
98
119
  draggable: !1
99
120
  })
100
121
  }),
101
- /* @__PURE__ */ x("div", {
102
- className: p.overlay,
103
- children: /* @__PURE__ */ x("div", {
104
- className: b(p.cropArea, { [p.circular]: E }),
105
- style: { aspectRatio: T }
122
+ /* @__PURE__ */ S("div", {
123
+ className: m.overlay,
124
+ children: /* @__PURE__ */ S("div", {
125
+ ref: X,
126
+ className: x(m.cropArea, { [m.circular]: ne }),
127
+ style: { aspectRatio: E }
106
128
  })
107
129
  })
108
130
  ]
109
131
  }),
110
- /* @__PURE__ */ S("div", {
111
- className: p.controls,
132
+ /* @__PURE__ */ C("div", {
133
+ className: m.controls,
112
134
  children: [
113
- te && /* @__PURE__ */ x("div", {
114
- className: p.zoomControl,
115
- children: /* @__PURE__ */ x(a, {
135
+ ae && /* @__PURE__ */ S("div", {
136
+ className: m.zoomControl,
137
+ children: /* @__PURE__ */ S(a, {
116
138
  min: 1,
117
139
  max: 3,
118
140
  step: .1,
119
- value: I,
120
- onChange: (e) => L(e),
121
- label: F("image_cropper.zoom"),
122
- styles: { root: p.zoomSlider }
141
+ value: R,
142
+ onChange: (e) => z(e),
143
+ label: L("image_cropper.zoom"),
144
+ styles: { root: m.zoomSlider }
123
145
  })
124
146
  }),
125
- O && /* @__PURE__ */ x("div", {
126
- className: p.rotationControl,
127
- children: /* @__PURE__ */ x(o, {
147
+ ie && /* @__PURE__ */ S("div", {
148
+ className: m.rotationControl,
149
+ children: /* @__PURE__ */ S(o, {
128
150
  iconName: "RefreshIcon",
129
- onClick: re,
130
- "aria-label": F("image_cropper.rotate"),
151
+ onClick: de,
152
+ "aria-label": L("image_cropper.rotate"),
131
153
  variant: "outline",
132
154
  size: "sm"
133
155
  })
134
156
  }),
135
- k && /* @__PURE__ */ x("div", {
136
- className: p.applyControl,
137
- children: /* @__PURE__ */ x(r, {
138
- onClick: ie,
157
+ oe && /* @__PURE__ */ S("div", {
158
+ className: m.applyControl,
159
+ children: /* @__PURE__ */ S(r, {
160
+ onClick: fe,
139
161
  size: "sm",
140
162
  variant: "solid",
141
- children: A || F("image_cropper.apply")
163
+ children: D || L("image_cropper.apply")
142
164
  })
143
165
  })
144
166
  ]
145
167
  }),
146
- /* @__PURE__ */ x(s, {
147
- open: K,
148
- onOpenChange: q,
149
- children: /* @__PURE__ */ S(l, { children: [
150
- /* @__PURE__ */ x(f, { children: /* @__PURE__ */ x(ee, { children: F("image_cropper.confirm_title") }) }),
151
- /* @__PURE__ */ x(u, { children: F("image_cropper.confirm_message") }),
152
- /* @__PURE__ */ S(d, { children: [/* @__PURE__ */ x(c, {
168
+ /* @__PURE__ */ S(s, {
169
+ open: se,
170
+ onOpenChange: J,
171
+ children: /* @__PURE__ */ C(l, { children: [
172
+ /* @__PURE__ */ S(d, { children: /* @__PURE__ */ S(te, { children: L("image_cropper.confirm_title") }) }),
173
+ /* @__PURE__ */ S(u, { children: L("image_cropper.confirm_message") }),
174
+ /* @__PURE__ */ C(ee, { children: [/* @__PURE__ */ S(c, {
153
175
  asChild: !0,
154
- children: /* @__PURE__ */ x(r, {
176
+ children: /* @__PURE__ */ S(r, {
155
177
  variant: "ghost",
156
- children: F("image_cropper.cancel")
178
+ children: L("image_cropper.cancel")
157
179
  })
158
- }), /* @__PURE__ */ x(r, {
159
- onClick: ae,
180
+ }), /* @__PURE__ */ S(r, {
181
+ onClick: pe,
160
182
  variant: "solid",
161
- children: F("image_cropper.confirm")
183
+ children: L("image_cropper.confirm")
162
184
  })] })
163
185
  ] })
164
186
  })
165
187
  ]
166
- }) : /* @__PURE__ */ x("div", {
167
- ref: P,
168
- className: b("wim-image-cropper", p.root, p.empty, M),
169
- ...N,
170
- children: /* @__PURE__ */ x("div", {
171
- className: p.emptyContent,
172
- children: F("image_cropper.no_image")
188
+ }) : /* @__PURE__ */ S("div", {
189
+ ref: I,
190
+ className: x("wim-image-cropper", m.root, m.empty, P),
191
+ ...F,
192
+ children: /* @__PURE__ */ S("div", {
193
+ className: m.emptyContent,
194
+ children: L("image_cropper.no_image")
173
195
  })
174
196
  });
175
197
  });
176
- T.displayName = "ImageCropper";
198
+ E.displayName = "ImageCropper";
177
199
  //#endregion
178
- export { T as ImageCropper };
200
+ export { E as ImageCropper };
@@ -0,0 +1,2 @@
1
+ "use client";
2
+ function e(e){let t=(e.layoutWidth>0?e.naturalWidth/e.layoutWidth:1)/e.zoom,n=Math.max(e.frameWidth,e.frameHeight)*t;e.maxOutputSize!==void 0&&e.maxOutputSize>0&&n>e.maxOutputSize&&(t*=e.maxOutputSize/n);let r=Math.max(1,Math.round(e.frameWidth*t)),i=Math.max(1,Math.round(e.frameHeight*t));return{detail:{x:e.x,y:e.y,zoom:e.zoom,rotation:e.rotation,frameWidth:e.frameWidth,frameHeight:e.frameHeight,width:r,height:i,naturalWidth:e.naturalWidth,naturalHeight:e.naturalHeight},scale:r/e.frameWidth}}function t(t,n,r,i){let{detail:a,scale:o}=e(n);if(!t.complete||n.naturalWidth===0||n.naturalHeight===0)throw Error(`ImageCropper: the image has not loaded, so there is nothing to crop.`);let s=document.createElement(`canvas`);s.width=a.width,s.height=a.height;let c=s.getContext(`2d`);if(!c)throw Error(`ImageCropper: a 2D canvas context is not available.`);return r===`image/jpeg`&&(c.fillStyle=`#fff`,c.fillRect(0,0,a.width,a.height)),c.translate(a.width/2,a.height/2),c.scale(o,o),c.translate(n.x,n.y),c.rotate(n.rotation*Math.PI/180),c.scale(n.zoom,n.zoom),c.drawImage(t,-n.layoutWidth/2,-n.layoutHeight/2,n.layoutWidth,n.layoutHeight),{dataUrl:s.toDataURL(r,i),detail:a}}exports.cropImage=t,exports.resolveCropDetail=e;
@@ -0,0 +1,58 @@
1
+ /** What the crop was made from. Enough to redo the crop elsewhere (for example on a server). */
2
+ export type ImageCropDetail = {
3
+ /** Horizontal offset of the image center from the center of the crop frame, in CSS px. */
4
+ x: number;
5
+ /** Vertical offset of the image center from the center of the crop frame, in CSS px. */
6
+ y: number;
7
+ /** Zoom factor applied to the image (1 = the size it is laid out at). */
8
+ zoom: number;
9
+ /** Clockwise rotation in degrees. */
10
+ rotation: number;
11
+ /** Width of the crop frame on screen, in CSS px. */
12
+ frameWidth: number;
13
+ /** Height of the crop frame on screen, in CSS px. */
14
+ frameHeight: number;
15
+ /** Width of the output image in px, after `maxOutputSize` is applied. */
16
+ width: number;
17
+ /** Height of the output image in px, after `maxOutputSize` is applied. */
18
+ height: number;
19
+ /** Natural width of the source image in px. */
20
+ naturalWidth: number;
21
+ /** Natural height of the source image in px. */
22
+ naturalHeight: number;
23
+ };
24
+ export type ImageCropOutputType = "image/png" | "image/jpeg" | "image/webp";
25
+ export type CropGeometry = {
26
+ x: number;
27
+ y: number;
28
+ zoom: number;
29
+ rotation: number;
30
+ frameWidth: number;
31
+ frameHeight: number;
32
+ /** 画像が、拡大の前に画面で占めている大きさ(CSS px)。 */
33
+ layoutWidth: number;
34
+ layoutHeight: number;
35
+ naturalWidth: number;
36
+ naturalHeight: number;
37
+ maxOutputSize?: number;
38
+ };
39
+ /**
40
+ * 出力の大きさと、枠の 1 CSS px が出力の何 px になるかを決める。
41
+ *
42
+ * 画面の枠の大きさで切ると、拡大して見ている写真は粗くなる。元の画像の解像度で切る ──
43
+ * 枠が元の画像の何 px ぶんを覆っているかを、出力の大きさにする。`maxOutputSize` は長いほうの辺の上限。
44
+ */
45
+ export declare function resolveCropDetail(g: CropGeometry): {
46
+ detail: ImageCropDetail;
47
+ scale: number;
48
+ };
49
+ /**
50
+ * 画面に見えている枠の中を、Canvas に描いて data URL にする。
51
+ *
52
+ * 画面の `transform`(`translate → rotate → scale`。原点は画像の中心)を、枠の中心を原点にして
53
+ * そのまま Canvas に写す。失敗(画像が読めていない・別オリジンの画像で Canvas が汚染された)は投げる。
54
+ */
55
+ export declare function cropImage(image: HTMLImageElement, g: CropGeometry, type: ImageCropOutputType, quality: number): {
56
+ dataUrl: string;
57
+ detail: ImageCropDetail;
58
+ };
@@ -0,0 +1,36 @@
1
+ "use client";
2
+ //#region src/components/form/ImageCropper/cropImage.ts
3
+ function e(e) {
4
+ let t = (e.layoutWidth > 0 ? e.naturalWidth / e.layoutWidth : 1) / e.zoom, n = Math.max(e.frameWidth, e.frameHeight) * t;
5
+ e.maxOutputSize !== void 0 && e.maxOutputSize > 0 && n > e.maxOutputSize && (t *= e.maxOutputSize / n);
6
+ let r = Math.max(1, Math.round(e.frameWidth * t)), i = Math.max(1, Math.round(e.frameHeight * t));
7
+ return {
8
+ detail: {
9
+ x: e.x,
10
+ y: e.y,
11
+ zoom: e.zoom,
12
+ rotation: e.rotation,
13
+ frameWidth: e.frameWidth,
14
+ frameHeight: e.frameHeight,
15
+ width: r,
16
+ height: i,
17
+ naturalWidth: e.naturalWidth,
18
+ naturalHeight: e.naturalHeight
19
+ },
20
+ scale: r / e.frameWidth
21
+ };
22
+ }
23
+ function t(t, n, r, i) {
24
+ let { detail: a, scale: o } = e(n);
25
+ if (!t.complete || n.naturalWidth === 0 || n.naturalHeight === 0) throw Error("ImageCropper: the image has not loaded, so there is nothing to crop.");
26
+ let s = document.createElement("canvas");
27
+ s.width = a.width, s.height = a.height;
28
+ let c = s.getContext("2d");
29
+ if (!c) throw Error("ImageCropper: a 2D canvas context is not available.");
30
+ return r === "image/jpeg" && (c.fillStyle = "#fff", c.fillRect(0, 0, a.width, a.height)), c.translate(a.width / 2, a.height / 2), c.scale(o, o), c.translate(n.x, n.y), c.rotate(n.rotation * Math.PI / 180), c.scale(n.zoom, n.zoom), c.drawImage(t, -n.layoutWidth / 2, -n.layoutHeight / 2, n.layoutWidth, n.layoutHeight), {
31
+ dataUrl: s.toDataURL(r, i),
32
+ detail: a
33
+ };
34
+ }
35
+ //#endregion
36
+ export { t as cropImage, e as resolveCropDetail };
@@ -1,2 +1,2 @@
1
1
  "use client";
2
- const e=require("../../../_virtual/_rolldown/runtime.cjs"),t=require("./anchor.module.cjs");let n=require("react");n=e.__toESM(n,1);let r=require("classnames");r=e.__toESM(r,1);let i=require("react/jsx-runtime");var a=({items:e=[],bounds:a=5,offset:o=0,direction:s=`vertical`,className:c,style:l})=>{let[u,d]=(0,n.useState)(``),[f,p]=(0,n.useState)({}),m=(0,n.useRef)(null),h=(e,t)=>{e.preventDefault();let n=t.replace(`#`,``),r=document.getElementById(n);r&&(window.scrollTo({top:r.offsetTop-o,behavior:`smooth`}),d(t))};(0,n.useLayoutEffect)(()=>{let t=()=>{let t=(e,n)=>{let r=[];return e.forEach(e=>{r.push({href:e.href,parentHref:n}),e.children&&(r=r.concat(t(e.children,n||e.href)))}),r},n=t(e),r=``,i=window.scrollY||document.documentElement.scrollTop,c=document.documentElement.scrollHeight,l=document.documentElement.clientHeight;if(i>0&&Math.ceil(i+l)>=c&&n.length>0){let e=n[n.length-1];r=s===`horizontal`&&e.parentHref?e.parentHref:e.href}else for(let e of n){let t=e.href.replace(`#`,``),n=document.getElementById(t);if(n){if(n.getBoundingClientRect().top<=o+a)r=s===`horizontal`&&e.parentHref?e.parentHref:e.href;else break}}r&&r!==u&&d(r)};return window.addEventListener(`scroll`,t),t(),()=>window.removeEventListener(`scroll`,t)},[e,o,a,u,s]),(0,n.useLayoutEffect)(()=>{if(!u||!m.current)return;let e=m.current.querySelector(`a[href="${u}"]`);if(e){let n=m.current.getBoundingClientRect();if(s===`vertical`){let t=e.getBoundingClientRect();p({top:t.top-n.top,height:t.height,opacity:1})}else{let r=m.current.querySelector(`.${t.default.list}`);if(r){let t=e.getBoundingClientRect(),i=r.getBoundingClientRect(),a=t.left-i.left+r.scrollLeft,o=a-i.width/2+t.width/2,s=r.scrollWidth-i.width,c=Math.max(0,Math.min(o,s)),l=a-c+(i.left-n.left);p({left:l,width:t.width,opacity:1}),r.scrollTo({left:c,behavior:`smooth`})}else{let t=e.getBoundingClientRect();p({left:t.left-m.current.getBoundingClientRect().left,width:t.width,opacity:1})}}}else p({opacity:0})},[u,s]);let g=e=>(0,i.jsx)(`ul`,{className:(0,r.default)(t.default.list,t.default[s]),children:e.map(e=>(0,i.jsxs)(`li`,{className:(0,r.default)(t.default.item,u===e.href&&t.default.active),children:[(0,i.jsx)(`a`,{href:e.href,className:t.default.link,"aria-current":u===e.href?`location`:void 0,onClick:t=>h(t,e.href),title:typeof e.title==`string`?e.title:void 0,children:e.title}),e.children&&s===`vertical`&&g(e.children)]},e.key))});return(0,i.jsxs)(`div`,{className:(0,r.default)(`wim-anchor`,t.default.root,t.default[s],c),style:l,ref:m,children:[(0,i.jsx)(`div`,{className:t.default.ink,children:(0,i.jsx)(`span`,{className:t.default.inkBall,style:f})}),g(e)]})};exports.Anchor=a,exports.default=a;
2
+ const e=require("../../../_virtual/_rolldown/runtime.cjs"),t=require("./anchor.module.cjs");let n=require("react");n=e.__toESM(n,1);let r=require("classnames");r=e.__toESM(r,1);let i=require("react/jsx-runtime");var a=({items:e=[],bounds:a=5,offset:o=0,direction:s=`vertical`,className:c,style:l})=>{let[u,d]=(0,n.useState)(``),[f,p]=(0,n.useState)({}),m=(0,n.useRef)(null),h=(e,t)=>{e.preventDefault();let n=t.replace(`#`,``),r=document.getElementById(n);r&&(window.scrollTo({top:r.offsetTop-o,behavior:`smooth`}),d(t))};(0,n.useLayoutEffect)(()=>{let t=()=>{let t=(e,n)=>{let r=[];return e.forEach(e=>{r.push({href:e.href,parentHref:n}),e.children&&(r=r.concat(t(e.children,n||e.href)))}),r},n=t(e),r=``,i=window.scrollY||document.documentElement.scrollTop,c=document.documentElement.scrollHeight,l=document.documentElement.clientHeight;if(i>0&&Math.ceil(i+l)>=c&&n.length>0){let e=n[n.length-1];r=s===`horizontal`&&e.parentHref?e.parentHref:e.href}else for(let e of n){let t=e.href.replace(`#`,``),n=document.getElementById(t);if(n){if(n.getBoundingClientRect().top<=o+a)r=s===`horizontal`&&e.parentHref?e.parentHref:e.href;else break}}r&&r!==u&&d(r)};return window.addEventListener(`scroll`,t),t(),()=>window.removeEventListener(`scroll`,t)},[e,o,a,u,s]);let g=(0,n.useCallback)(()=>{if(!u||!m.current)return;let e=m.current.querySelector(`a[href="${u}"]`);if(e){let n=m.current.getBoundingClientRect();if(s===`vertical`){let t=e.getBoundingClientRect();p({top:t.top-n.top,height:t.height,opacity:1})}else{let r=m.current.querySelector(`.${t.default.list}`);if(r){let t=e.getBoundingClientRect(),i=r.getBoundingClientRect(),a=t.left-i.left+r.scrollLeft,o=a-i.width/2+t.width/2,s=r.scrollWidth-i.width,c=Math.max(0,Math.min(o,s)),l=a-c+(i.left-n.left);p({left:l,width:t.width,opacity:1}),r.scrollTo({left:c,behavior:`smooth`})}else{let t=e.getBoundingClientRect();p({left:t.left-m.current.getBoundingClientRect().left,width:t.width,opacity:1})}}}else p({opacity:0})},[u,s]);(0,n.useLayoutEffect)(()=>{g()},[g]),(0,n.useEffect)(()=>{let e=m.current;if(!e||typeof ResizeObserver>`u`)return;let t=new ResizeObserver(()=>g());return t.observe(e),e.querySelectorAll(`a`).forEach(e=>t.observe(e)),()=>t.disconnect()},[g,e]);let _=e=>(0,i.jsx)(`ul`,{className:(0,r.default)(t.default.list,t.default[s]),children:e.map(e=>(0,i.jsxs)(`li`,{className:(0,r.default)(t.default.item,u===e.href&&t.default.active),children:[(0,i.jsx)(`a`,{href:e.href,className:t.default.link,"aria-current":u===e.href?`location`:void 0,onClick:t=>h(t,e.href),title:typeof e.title==`string`?e.title:void 0,children:e.title}),e.children&&s===`vertical`&&_(e.children)]},e.key))});return(0,i.jsxs)(`div`,{className:(0,r.default)(`wim-anchor`,t.default.root,t.default[s],c),style:l,ref:m,children:[(0,i.jsx)(`div`,{className:t.default.ink,children:(0,i.jsx)(`span`,{className:t.default.inkBall,style:f})}),_(e)]})};exports.Anchor=a,exports.default=a;