design-zystem 1.0.213 → 1.0.214
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +28 -1
- package/dist/index.d.ts +28 -1
- package/dist/index.js +151 -5
- package/dist/index.mjs +150 -5
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -776,6 +776,33 @@ declare const formatDuration: (minutes: number | null | undefined, language?: st
|
|
|
776
776
|
declare const truncateText: (text: string | null | undefined, maxLength?: number) => string;
|
|
777
777
|
declare const truncateFileName: (fileName: string | null | undefined, maxLength?: number) => string;
|
|
778
778
|
|
|
779
|
+
/**
|
|
780
|
+
* Optimiser image:
|
|
781
|
+
* - Converti en webp (ou png/jpeg selon support navigateur)
|
|
782
|
+
* - Resize l'image pour ne pas dépasser maxDimension
|
|
783
|
+
* - Compression à la taille cible (meilleur effort)
|
|
784
|
+
*
|
|
785
|
+
* Retourne { file, error } — jamais de throw.
|
|
786
|
+
* Si error !== null, l'appelant peut afficher un message à l'utilisateur.
|
|
787
|
+
* Si l'environnement ne supporte pas les APIs nécessaires, passthrough silencieux.
|
|
788
|
+
*/
|
|
789
|
+
interface OptimizeImageResult {
|
|
790
|
+
file: File | null | undefined;
|
|
791
|
+
error: string | null;
|
|
792
|
+
}
|
|
793
|
+
interface OptimizeImageOptions {
|
|
794
|
+
maxDimension?: number;
|
|
795
|
+
targetSizeKB?: number;
|
|
796
|
+
qualityStart?: number;
|
|
797
|
+
qualityMin?: number;
|
|
798
|
+
qualityStep?: number;
|
|
799
|
+
/** Timeout en ms pour le chargement de l'image (défaut : 15 000) */
|
|
800
|
+
imageLoadTimeoutMs?: number;
|
|
801
|
+
/** Timeout en ms pour toBlob (défaut : 15 000) */
|
|
802
|
+
toBlobTimeoutMs?: number;
|
|
803
|
+
}
|
|
804
|
+
declare const optimizeImage: (file: File | null | undefined, desiredBaseName: string, opts?: OptimizeImageOptions) => Promise<OptimizeImageResult>;
|
|
805
|
+
|
|
779
806
|
declare const colors: {
|
|
780
807
|
readonly primary: "#52bdc4";
|
|
781
808
|
readonly secondary: "#f8af2d";
|
|
@@ -851,4 +878,4 @@ declare const colors: {
|
|
|
851
878
|
readonly light_blue: "#F1F5F9";
|
|
852
879
|
};
|
|
853
880
|
|
|
854
|
-
export { Accordion, type AccordionItem, Box, Bubble, Bulk, Button, CardSkeleton, Checkbox, Col, ColorPicker, DatePicker, Divider, Drawer, FileUploadZone, Grid, Icon, IconTabs, Image, Input, Link, List, ListItem, MetricCard, Modal, ModalConfirmation, MultiSelect, NewModal, Options, PageContainer, Pagination, Popover, Radio, Row, Select, type SelectOption, type SelectProps, SkeletonRow, SliderInput, Spinner, Stepper, Switch, Table, TableBody, TableCell, TableFooter, TableFooterCell, TableFooterRow, TableHeader, TableHeaderCell, TableHeaderRow, TableRow, Tabs, TagBubble, Text, Tooltip, colors, formatDate, formatDistance, formatDuration, truncateFileName, truncateText };
|
|
881
|
+
export { Accordion, type AccordionItem, Box, Bubble, Bulk, Button, CardSkeleton, Checkbox, Col, ColorPicker, DatePicker, Divider, Drawer, FileUploadZone, Grid, Icon, IconTabs, Image, Input, Link, List, ListItem, MetricCard, Modal, ModalConfirmation, MultiSelect, NewModal, Options, PageContainer, Pagination, Popover, Radio, Row, Select, type SelectOption, type SelectProps, SkeletonRow, SliderInput, Spinner, Stepper, Switch, Table, TableBody, TableCell, TableFooter, TableFooterCell, TableFooterRow, TableHeader, TableHeaderCell, TableHeaderRow, TableRow, Tabs, TagBubble, Text, Tooltip, colors, formatDate, formatDistance, formatDuration, optimizeImage, truncateFileName, truncateText };
|
package/dist/index.d.ts
CHANGED
|
@@ -776,6 +776,33 @@ declare const formatDuration: (minutes: number | null | undefined, language?: st
|
|
|
776
776
|
declare const truncateText: (text: string | null | undefined, maxLength?: number) => string;
|
|
777
777
|
declare const truncateFileName: (fileName: string | null | undefined, maxLength?: number) => string;
|
|
778
778
|
|
|
779
|
+
/**
|
|
780
|
+
* Optimiser image:
|
|
781
|
+
* - Converti en webp (ou png/jpeg selon support navigateur)
|
|
782
|
+
* - Resize l'image pour ne pas dépasser maxDimension
|
|
783
|
+
* - Compression à la taille cible (meilleur effort)
|
|
784
|
+
*
|
|
785
|
+
* Retourne { file, error } — jamais de throw.
|
|
786
|
+
* Si error !== null, l'appelant peut afficher un message à l'utilisateur.
|
|
787
|
+
* Si l'environnement ne supporte pas les APIs nécessaires, passthrough silencieux.
|
|
788
|
+
*/
|
|
789
|
+
interface OptimizeImageResult {
|
|
790
|
+
file: File | null | undefined;
|
|
791
|
+
error: string | null;
|
|
792
|
+
}
|
|
793
|
+
interface OptimizeImageOptions {
|
|
794
|
+
maxDimension?: number;
|
|
795
|
+
targetSizeKB?: number;
|
|
796
|
+
qualityStart?: number;
|
|
797
|
+
qualityMin?: number;
|
|
798
|
+
qualityStep?: number;
|
|
799
|
+
/** Timeout en ms pour le chargement de l'image (défaut : 15 000) */
|
|
800
|
+
imageLoadTimeoutMs?: number;
|
|
801
|
+
/** Timeout en ms pour toBlob (défaut : 15 000) */
|
|
802
|
+
toBlobTimeoutMs?: number;
|
|
803
|
+
}
|
|
804
|
+
declare const optimizeImage: (file: File | null | undefined, desiredBaseName: string, opts?: OptimizeImageOptions) => Promise<OptimizeImageResult>;
|
|
805
|
+
|
|
779
806
|
declare const colors: {
|
|
780
807
|
readonly primary: "#52bdc4";
|
|
781
808
|
readonly secondary: "#f8af2d";
|
|
@@ -851,4 +878,4 @@ declare const colors: {
|
|
|
851
878
|
readonly light_blue: "#F1F5F9";
|
|
852
879
|
};
|
|
853
880
|
|
|
854
|
-
export { Accordion, type AccordionItem, Box, Bubble, Bulk, Button, CardSkeleton, Checkbox, Col, ColorPicker, DatePicker, Divider, Drawer, FileUploadZone, Grid, Icon, IconTabs, Image, Input, Link, List, ListItem, MetricCard, Modal, ModalConfirmation, MultiSelect, NewModal, Options, PageContainer, Pagination, Popover, Radio, Row, Select, type SelectOption, type SelectProps, SkeletonRow, SliderInput, Spinner, Stepper, Switch, Table, TableBody, TableCell, TableFooter, TableFooterCell, TableFooterRow, TableHeader, TableHeaderCell, TableHeaderRow, TableRow, Tabs, TagBubble, Text, Tooltip, colors, formatDate, formatDistance, formatDuration, truncateFileName, truncateText };
|
|
881
|
+
export { Accordion, type AccordionItem, Box, Bubble, Bulk, Button, CardSkeleton, Checkbox, Col, ColorPicker, DatePicker, Divider, Drawer, FileUploadZone, Grid, Icon, IconTabs, Image, Input, Link, List, ListItem, MetricCard, Modal, ModalConfirmation, MultiSelect, NewModal, Options, PageContainer, Pagination, Popover, Radio, Row, Select, type SelectOption, type SelectProps, SkeletonRow, SliderInput, Spinner, Stepper, Switch, Table, TableBody, TableCell, TableFooter, TableFooterCell, TableFooterRow, TableHeader, TableHeaderCell, TableHeaderRow, TableRow, Tabs, TagBubble, Text, Tooltip, colors, formatDate, formatDistance, formatDuration, optimizeImage, truncateFileName, truncateText };
|
package/dist/index.js
CHANGED
|
@@ -75,7 +75,7 @@ __export(index_exports, {
|
|
|
75
75
|
Grid: () => Grid,
|
|
76
76
|
Icon: () => Icon,
|
|
77
77
|
IconTabs: () => IconTabs,
|
|
78
|
-
Image: () =>
|
|
78
|
+
Image: () => Image2,
|
|
79
79
|
Input: () => Input,
|
|
80
80
|
Link: () => Link,
|
|
81
81
|
List: () => List,
|
|
@@ -115,6 +115,7 @@ __export(index_exports, {
|
|
|
115
115
|
formatDate: () => formatDate,
|
|
116
116
|
formatDistance: () => formatDistance,
|
|
117
117
|
formatDuration: () => formatDuration,
|
|
118
|
+
optimizeImage: () => optimizeImage_default,
|
|
118
119
|
truncateFileName: () => truncateFileName,
|
|
119
120
|
truncateText: () => truncateText
|
|
120
121
|
});
|
|
@@ -3799,7 +3800,7 @@ var StyledImage = import_styled_components36.default.img`
|
|
|
3799
3800
|
height: ${(props) => props.height || "auto"};
|
|
3800
3801
|
display: block;
|
|
3801
3802
|
`;
|
|
3802
|
-
var
|
|
3803
|
+
var Image2 = (_a) => {
|
|
3803
3804
|
var _b = _a, {
|
|
3804
3805
|
src,
|
|
3805
3806
|
alt = "",
|
|
@@ -4305,15 +4306,25 @@ var Popover = ({ items = [], children }) => {
|
|
|
4305
4306
|
const menuRef = (0, import_react21.useRef)(null);
|
|
4306
4307
|
const [menuPosition, setMenuPosition] = (0, import_react21.useState)({ top: 0, left: 0 });
|
|
4307
4308
|
const updatePosition = (0, import_react21.useCallback)(() => {
|
|
4309
|
+
var _a;
|
|
4308
4310
|
if (!ref.current) {
|
|
4309
4311
|
return;
|
|
4310
4312
|
}
|
|
4311
4313
|
const rect = ref.current.getBoundingClientRect();
|
|
4314
|
+
const estimatedMenuHeight = items.length * 42 + 8;
|
|
4315
|
+
const menuHeight = ((_a = menuRef.current) == null ? void 0 : _a.getBoundingClientRect().height) ? menuRef.current.getBoundingClientRect().height : estimatedMenuHeight;
|
|
4316
|
+
const viewportPadding = 8;
|
|
4317
|
+
const spaceBelow = window.innerHeight - rect.bottom;
|
|
4318
|
+
const shouldOpenAbove = spaceBelow < menuHeight + 6;
|
|
4319
|
+
const top = shouldOpenAbove ? rect.top - menuHeight - 6 : rect.bottom + 6;
|
|
4312
4320
|
setMenuPosition({
|
|
4313
|
-
top:
|
|
4321
|
+
top: Math.max(
|
|
4322
|
+
viewportPadding,
|
|
4323
|
+
Math.min(top, window.innerHeight - menuHeight - viewportPadding)
|
|
4324
|
+
),
|
|
4314
4325
|
left: rect.right
|
|
4315
4326
|
});
|
|
4316
|
-
}, []);
|
|
4327
|
+
}, [items.length]);
|
|
4317
4328
|
(0, import_react21.useEffect)(() => {
|
|
4318
4329
|
const handleClickOutside = (event) => {
|
|
4319
4330
|
var _a, _b;
|
|
@@ -4324,7 +4335,7 @@ var Popover = ({ items = [], children }) => {
|
|
|
4324
4335
|
}
|
|
4325
4336
|
};
|
|
4326
4337
|
if (open) {
|
|
4327
|
-
updatePosition
|
|
4338
|
+
requestAnimationFrame(updatePosition);
|
|
4328
4339
|
window.addEventListener("scroll", updatePosition, true);
|
|
4329
4340
|
window.addEventListener("resize", updatePosition);
|
|
4330
4341
|
document.addEventListener("mousedown", handleClickOutside);
|
|
@@ -5035,6 +5046,140 @@ var truncateFileName = (fileName, maxLength = 20) => {
|
|
|
5035
5046
|
}
|
|
5036
5047
|
return fileName;
|
|
5037
5048
|
};
|
|
5049
|
+
|
|
5050
|
+
// src/services/optimizeImage/optimizeImage.ts
|
|
5051
|
+
var hasDocument = () => typeof document !== "undefined";
|
|
5052
|
+
var hasObjectURL = () => typeof URL !== "undefined" && typeof URL.createObjectURL === "function" && typeof URL.revokeObjectURL === "function";
|
|
5053
|
+
var readArrayBuffer = (file) => {
|
|
5054
|
+
if (typeof file.arrayBuffer === "function") {
|
|
5055
|
+
return file.arrayBuffer();
|
|
5056
|
+
}
|
|
5057
|
+
return new Promise((resolve, reject) => {
|
|
5058
|
+
const reader = new FileReader();
|
|
5059
|
+
reader.onload = () => resolve(reader.result);
|
|
5060
|
+
reader.onerror = () => {
|
|
5061
|
+
var _a;
|
|
5062
|
+
return reject((_a = reader.error) != null ? _a : new Error("FileReader error"));
|
|
5063
|
+
};
|
|
5064
|
+
reader.readAsArrayBuffer(file);
|
|
5065
|
+
});
|
|
5066
|
+
};
|
|
5067
|
+
var loadImage = (blob, timeoutMs) => new Promise((resolve, reject) => {
|
|
5068
|
+
const url = URL.createObjectURL(blob);
|
|
5069
|
+
let settled = false;
|
|
5070
|
+
const cleanup = () => {
|
|
5071
|
+
URL.revokeObjectURL(url);
|
|
5072
|
+
};
|
|
5073
|
+
const timer = setTimeout(() => {
|
|
5074
|
+
if (settled) return;
|
|
5075
|
+
settled = true;
|
|
5076
|
+
cleanup();
|
|
5077
|
+
reject(new Error("Image load timeout"));
|
|
5078
|
+
}, timeoutMs);
|
|
5079
|
+
const img = new Image();
|
|
5080
|
+
img.onload = () => {
|
|
5081
|
+
if (settled) return;
|
|
5082
|
+
settled = true;
|
|
5083
|
+
clearTimeout(timer);
|
|
5084
|
+
cleanup();
|
|
5085
|
+
resolve(img);
|
|
5086
|
+
};
|
|
5087
|
+
img.onerror = (e) => {
|
|
5088
|
+
if (settled) return;
|
|
5089
|
+
settled = true;
|
|
5090
|
+
clearTimeout(timer);
|
|
5091
|
+
cleanup();
|
|
5092
|
+
reject(e instanceof Error ? e : new Error("Image load error"));
|
|
5093
|
+
};
|
|
5094
|
+
img.src = url;
|
|
5095
|
+
});
|
|
5096
|
+
var canvasToBlob = (canvas, mime, quality, timeoutMs) => new Promise((resolve, reject) => {
|
|
5097
|
+
const timer = setTimeout(
|
|
5098
|
+
() => reject(new Error("toBlob timeout")),
|
|
5099
|
+
timeoutMs
|
|
5100
|
+
);
|
|
5101
|
+
canvas.toBlob(
|
|
5102
|
+
(blob) => {
|
|
5103
|
+
clearTimeout(timer);
|
|
5104
|
+
if (blob) {
|
|
5105
|
+
resolve(blob);
|
|
5106
|
+
} else {
|
|
5107
|
+
reject(new Error("toBlob returned null"));
|
|
5108
|
+
}
|
|
5109
|
+
},
|
|
5110
|
+
mime,
|
|
5111
|
+
quality
|
|
5112
|
+
);
|
|
5113
|
+
});
|
|
5114
|
+
var optimizeImage = async (file, desiredBaseName, opts = {}) => {
|
|
5115
|
+
var _a;
|
|
5116
|
+
if (!file || !((_a = file.type) == null ? void 0 : _a.startsWith("image/"))) {
|
|
5117
|
+
return { file, error: null };
|
|
5118
|
+
}
|
|
5119
|
+
if (!hasDocument() || !hasObjectURL()) {
|
|
5120
|
+
return { file, error: null };
|
|
5121
|
+
}
|
|
5122
|
+
const {
|
|
5123
|
+
maxDimension = 1920,
|
|
5124
|
+
targetSizeKB = 1024,
|
|
5125
|
+
qualityStart = 0.86,
|
|
5126
|
+
qualityMin = 0.5,
|
|
5127
|
+
qualityStep = 0.08,
|
|
5128
|
+
imageLoadTimeoutMs = 15e3,
|
|
5129
|
+
toBlobTimeoutMs = 15e3
|
|
5130
|
+
} = opts;
|
|
5131
|
+
try {
|
|
5132
|
+
const arrayBuf = await readArrayBuffer(file);
|
|
5133
|
+
const blob = new Blob([arrayBuf], { type: file.type });
|
|
5134
|
+
const img = await loadImage(blob, imageLoadTimeoutMs);
|
|
5135
|
+
const scale = Math.min(1, maxDimension / Math.max(img.width, img.height));
|
|
5136
|
+
const outW = Math.max(1, Math.round(img.width * scale));
|
|
5137
|
+
const outH = Math.max(1, Math.round(img.height * scale));
|
|
5138
|
+
const canvas = document.createElement("canvas");
|
|
5139
|
+
canvas.width = outW;
|
|
5140
|
+
canvas.height = outH;
|
|
5141
|
+
const ctx = canvas.getContext("2d");
|
|
5142
|
+
if (!ctx) throw new Error("Canvas 2D context unavailable");
|
|
5143
|
+
if ("imageSmoothingQuality" in ctx) {
|
|
5144
|
+
ctx.imageSmoothingQuality = "high";
|
|
5145
|
+
}
|
|
5146
|
+
ctx.drawImage(img, 0, 0, outW, outH);
|
|
5147
|
+
const supportsWebP = (() => {
|
|
5148
|
+
try {
|
|
5149
|
+
const probe = document.createElement("canvas");
|
|
5150
|
+
return probe.toDataURL("image/webp").startsWith("data:image/webp");
|
|
5151
|
+
} catch (e) {
|
|
5152
|
+
return false;
|
|
5153
|
+
}
|
|
5154
|
+
})();
|
|
5155
|
+
const sourceHasAlphaHint = /png|webp|gif|avif/i.test(file.type);
|
|
5156
|
+
const preferPNG = !supportsWebP && sourceHasAlphaHint;
|
|
5157
|
+
const targetMime = supportsWebP ? "image/webp" : preferPNG ? "image/png" : "image/jpeg";
|
|
5158
|
+
let q = qualityStart;
|
|
5159
|
+
let outBlob = await canvasToBlob(canvas, targetMime, q, toBlobTimeoutMs);
|
|
5160
|
+
let actualMime = outBlob.type || targetMime;
|
|
5161
|
+
if (/webp|jpeg/.test(actualMime)) {
|
|
5162
|
+
while (outBlob.size / 1024 > targetSizeKB && q > qualityMin) {
|
|
5163
|
+
q = Math.max(qualityMin, q - qualityStep);
|
|
5164
|
+
outBlob = await canvasToBlob(canvas, actualMime, q, toBlobTimeoutMs);
|
|
5165
|
+
actualMime = outBlob.type || actualMime;
|
|
5166
|
+
}
|
|
5167
|
+
}
|
|
5168
|
+
const ext = actualMime.includes("webp") ? "webp" : actualMime.includes("png") ? "png" : "jpg";
|
|
5169
|
+
const optimizedFile = new File([outBlob], `${desiredBaseName}.${ext}`, {
|
|
5170
|
+
type: actualMime,
|
|
5171
|
+
lastModified: Date.now()
|
|
5172
|
+
});
|
|
5173
|
+
return { file: optimizedFile, error: null };
|
|
5174
|
+
} catch (error) {
|
|
5175
|
+
console.error("Image optimization error:", error);
|
|
5176
|
+
return {
|
|
5177
|
+
file,
|
|
5178
|
+
error: "Une erreur est survenue lors de l'optimisation de l'image."
|
|
5179
|
+
};
|
|
5180
|
+
}
|
|
5181
|
+
};
|
|
5182
|
+
var optimizeImage_default = optimizeImage;
|
|
5038
5183
|
// Annotate the CommonJS export names for ESM import in node:
|
|
5039
5184
|
0 && (module.exports = {
|
|
5040
5185
|
Accordion,
|
|
@@ -5093,6 +5238,7 @@ var truncateFileName = (fileName, maxLength = 20) => {
|
|
|
5093
5238
|
formatDate,
|
|
5094
5239
|
formatDistance,
|
|
5095
5240
|
formatDuration,
|
|
5241
|
+
optimizeImage,
|
|
5096
5242
|
truncateFileName,
|
|
5097
5243
|
truncateText
|
|
5098
5244
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -3729,7 +3729,7 @@ var StyledImage = styled36.img`
|
|
|
3729
3729
|
height: ${(props) => props.height || "auto"};
|
|
3730
3730
|
display: block;
|
|
3731
3731
|
`;
|
|
3732
|
-
var
|
|
3732
|
+
var Image2 = (_a) => {
|
|
3733
3733
|
var _b = _a, {
|
|
3734
3734
|
src,
|
|
3735
3735
|
alt = "",
|
|
@@ -4235,15 +4235,25 @@ var Popover = ({ items = [], children }) => {
|
|
|
4235
4235
|
const menuRef = useRef12(null);
|
|
4236
4236
|
const [menuPosition, setMenuPosition] = useState12({ top: 0, left: 0 });
|
|
4237
4237
|
const updatePosition = useCallback3(() => {
|
|
4238
|
+
var _a;
|
|
4238
4239
|
if (!ref.current) {
|
|
4239
4240
|
return;
|
|
4240
4241
|
}
|
|
4241
4242
|
const rect = ref.current.getBoundingClientRect();
|
|
4243
|
+
const estimatedMenuHeight = items.length * 42 + 8;
|
|
4244
|
+
const menuHeight = ((_a = menuRef.current) == null ? void 0 : _a.getBoundingClientRect().height) ? menuRef.current.getBoundingClientRect().height : estimatedMenuHeight;
|
|
4245
|
+
const viewportPadding = 8;
|
|
4246
|
+
const spaceBelow = window.innerHeight - rect.bottom;
|
|
4247
|
+
const shouldOpenAbove = spaceBelow < menuHeight + 6;
|
|
4248
|
+
const top = shouldOpenAbove ? rect.top - menuHeight - 6 : rect.bottom + 6;
|
|
4242
4249
|
setMenuPosition({
|
|
4243
|
-
top:
|
|
4250
|
+
top: Math.max(
|
|
4251
|
+
viewportPadding,
|
|
4252
|
+
Math.min(top, window.innerHeight - menuHeight - viewportPadding)
|
|
4253
|
+
),
|
|
4244
4254
|
left: rect.right
|
|
4245
4255
|
});
|
|
4246
|
-
}, []);
|
|
4256
|
+
}, [items.length]);
|
|
4247
4257
|
useEffect13(() => {
|
|
4248
4258
|
const handleClickOutside = (event) => {
|
|
4249
4259
|
var _a, _b;
|
|
@@ -4254,7 +4264,7 @@ var Popover = ({ items = [], children }) => {
|
|
|
4254
4264
|
}
|
|
4255
4265
|
};
|
|
4256
4266
|
if (open) {
|
|
4257
|
-
updatePosition
|
|
4267
|
+
requestAnimationFrame(updatePosition);
|
|
4258
4268
|
window.addEventListener("scroll", updatePosition, true);
|
|
4259
4269
|
window.addEventListener("resize", updatePosition);
|
|
4260
4270
|
document.addEventListener("mousedown", handleClickOutside);
|
|
@@ -4965,6 +4975,140 @@ var truncateFileName = (fileName, maxLength = 20) => {
|
|
|
4965
4975
|
}
|
|
4966
4976
|
return fileName;
|
|
4967
4977
|
};
|
|
4978
|
+
|
|
4979
|
+
// src/services/optimizeImage/optimizeImage.ts
|
|
4980
|
+
var hasDocument = () => typeof document !== "undefined";
|
|
4981
|
+
var hasObjectURL = () => typeof URL !== "undefined" && typeof URL.createObjectURL === "function" && typeof URL.revokeObjectURL === "function";
|
|
4982
|
+
var readArrayBuffer = (file) => {
|
|
4983
|
+
if (typeof file.arrayBuffer === "function") {
|
|
4984
|
+
return file.arrayBuffer();
|
|
4985
|
+
}
|
|
4986
|
+
return new Promise((resolve, reject) => {
|
|
4987
|
+
const reader = new FileReader();
|
|
4988
|
+
reader.onload = () => resolve(reader.result);
|
|
4989
|
+
reader.onerror = () => {
|
|
4990
|
+
var _a;
|
|
4991
|
+
return reject((_a = reader.error) != null ? _a : new Error("FileReader error"));
|
|
4992
|
+
};
|
|
4993
|
+
reader.readAsArrayBuffer(file);
|
|
4994
|
+
});
|
|
4995
|
+
};
|
|
4996
|
+
var loadImage = (blob, timeoutMs) => new Promise((resolve, reject) => {
|
|
4997
|
+
const url = URL.createObjectURL(blob);
|
|
4998
|
+
let settled = false;
|
|
4999
|
+
const cleanup = () => {
|
|
5000
|
+
URL.revokeObjectURL(url);
|
|
5001
|
+
};
|
|
5002
|
+
const timer = setTimeout(() => {
|
|
5003
|
+
if (settled) return;
|
|
5004
|
+
settled = true;
|
|
5005
|
+
cleanup();
|
|
5006
|
+
reject(new Error("Image load timeout"));
|
|
5007
|
+
}, timeoutMs);
|
|
5008
|
+
const img = new Image();
|
|
5009
|
+
img.onload = () => {
|
|
5010
|
+
if (settled) return;
|
|
5011
|
+
settled = true;
|
|
5012
|
+
clearTimeout(timer);
|
|
5013
|
+
cleanup();
|
|
5014
|
+
resolve(img);
|
|
5015
|
+
};
|
|
5016
|
+
img.onerror = (e) => {
|
|
5017
|
+
if (settled) return;
|
|
5018
|
+
settled = true;
|
|
5019
|
+
clearTimeout(timer);
|
|
5020
|
+
cleanup();
|
|
5021
|
+
reject(e instanceof Error ? e : new Error("Image load error"));
|
|
5022
|
+
};
|
|
5023
|
+
img.src = url;
|
|
5024
|
+
});
|
|
5025
|
+
var canvasToBlob = (canvas, mime, quality, timeoutMs) => new Promise((resolve, reject) => {
|
|
5026
|
+
const timer = setTimeout(
|
|
5027
|
+
() => reject(new Error("toBlob timeout")),
|
|
5028
|
+
timeoutMs
|
|
5029
|
+
);
|
|
5030
|
+
canvas.toBlob(
|
|
5031
|
+
(blob) => {
|
|
5032
|
+
clearTimeout(timer);
|
|
5033
|
+
if (blob) {
|
|
5034
|
+
resolve(blob);
|
|
5035
|
+
} else {
|
|
5036
|
+
reject(new Error("toBlob returned null"));
|
|
5037
|
+
}
|
|
5038
|
+
},
|
|
5039
|
+
mime,
|
|
5040
|
+
quality
|
|
5041
|
+
);
|
|
5042
|
+
});
|
|
5043
|
+
var optimizeImage = async (file, desiredBaseName, opts = {}) => {
|
|
5044
|
+
var _a;
|
|
5045
|
+
if (!file || !((_a = file.type) == null ? void 0 : _a.startsWith("image/"))) {
|
|
5046
|
+
return { file, error: null };
|
|
5047
|
+
}
|
|
5048
|
+
if (!hasDocument() || !hasObjectURL()) {
|
|
5049
|
+
return { file, error: null };
|
|
5050
|
+
}
|
|
5051
|
+
const {
|
|
5052
|
+
maxDimension = 1920,
|
|
5053
|
+
targetSizeKB = 1024,
|
|
5054
|
+
qualityStart = 0.86,
|
|
5055
|
+
qualityMin = 0.5,
|
|
5056
|
+
qualityStep = 0.08,
|
|
5057
|
+
imageLoadTimeoutMs = 15e3,
|
|
5058
|
+
toBlobTimeoutMs = 15e3
|
|
5059
|
+
} = opts;
|
|
5060
|
+
try {
|
|
5061
|
+
const arrayBuf = await readArrayBuffer(file);
|
|
5062
|
+
const blob = new Blob([arrayBuf], { type: file.type });
|
|
5063
|
+
const img = await loadImage(blob, imageLoadTimeoutMs);
|
|
5064
|
+
const scale = Math.min(1, maxDimension / Math.max(img.width, img.height));
|
|
5065
|
+
const outW = Math.max(1, Math.round(img.width * scale));
|
|
5066
|
+
const outH = Math.max(1, Math.round(img.height * scale));
|
|
5067
|
+
const canvas = document.createElement("canvas");
|
|
5068
|
+
canvas.width = outW;
|
|
5069
|
+
canvas.height = outH;
|
|
5070
|
+
const ctx = canvas.getContext("2d");
|
|
5071
|
+
if (!ctx) throw new Error("Canvas 2D context unavailable");
|
|
5072
|
+
if ("imageSmoothingQuality" in ctx) {
|
|
5073
|
+
ctx.imageSmoothingQuality = "high";
|
|
5074
|
+
}
|
|
5075
|
+
ctx.drawImage(img, 0, 0, outW, outH);
|
|
5076
|
+
const supportsWebP = (() => {
|
|
5077
|
+
try {
|
|
5078
|
+
const probe = document.createElement("canvas");
|
|
5079
|
+
return probe.toDataURL("image/webp").startsWith("data:image/webp");
|
|
5080
|
+
} catch (e) {
|
|
5081
|
+
return false;
|
|
5082
|
+
}
|
|
5083
|
+
})();
|
|
5084
|
+
const sourceHasAlphaHint = /png|webp|gif|avif/i.test(file.type);
|
|
5085
|
+
const preferPNG = !supportsWebP && sourceHasAlphaHint;
|
|
5086
|
+
const targetMime = supportsWebP ? "image/webp" : preferPNG ? "image/png" : "image/jpeg";
|
|
5087
|
+
let q = qualityStart;
|
|
5088
|
+
let outBlob = await canvasToBlob(canvas, targetMime, q, toBlobTimeoutMs);
|
|
5089
|
+
let actualMime = outBlob.type || targetMime;
|
|
5090
|
+
if (/webp|jpeg/.test(actualMime)) {
|
|
5091
|
+
while (outBlob.size / 1024 > targetSizeKB && q > qualityMin) {
|
|
5092
|
+
q = Math.max(qualityMin, q - qualityStep);
|
|
5093
|
+
outBlob = await canvasToBlob(canvas, actualMime, q, toBlobTimeoutMs);
|
|
5094
|
+
actualMime = outBlob.type || actualMime;
|
|
5095
|
+
}
|
|
5096
|
+
}
|
|
5097
|
+
const ext = actualMime.includes("webp") ? "webp" : actualMime.includes("png") ? "png" : "jpg";
|
|
5098
|
+
const optimizedFile = new File([outBlob], `${desiredBaseName}.${ext}`, {
|
|
5099
|
+
type: actualMime,
|
|
5100
|
+
lastModified: Date.now()
|
|
5101
|
+
});
|
|
5102
|
+
return { file: optimizedFile, error: null };
|
|
5103
|
+
} catch (error) {
|
|
5104
|
+
console.error("Image optimization error:", error);
|
|
5105
|
+
return {
|
|
5106
|
+
file,
|
|
5107
|
+
error: "Une erreur est survenue lors de l'optimisation de l'image."
|
|
5108
|
+
};
|
|
5109
|
+
}
|
|
5110
|
+
};
|
|
5111
|
+
var optimizeImage_default = optimizeImage;
|
|
4968
5112
|
export {
|
|
4969
5113
|
Accordion,
|
|
4970
5114
|
Box,
|
|
@@ -4982,7 +5126,7 @@ export {
|
|
|
4982
5126
|
Grid,
|
|
4983
5127
|
Icon,
|
|
4984
5128
|
IconTabs,
|
|
4985
|
-
Image,
|
|
5129
|
+
Image2 as Image,
|
|
4986
5130
|
Input,
|
|
4987
5131
|
Link,
|
|
4988
5132
|
List,
|
|
@@ -5022,6 +5166,7 @@ export {
|
|
|
5022
5166
|
formatDate,
|
|
5023
5167
|
formatDistance,
|
|
5024
5168
|
formatDuration,
|
|
5169
|
+
optimizeImage_default as optimizeImage,
|
|
5025
5170
|
truncateFileName,
|
|
5026
5171
|
truncateText
|
|
5027
5172
|
};
|