@sqrzro/ui 4.0.0-alpha.71 → 4.0.0-alpha.72

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,3 +1,4 @@
1
+ import type { ImageTypeKey } from '../../../forms/utility/preprocess-image';
1
2
  import type { FormResponse, InputProps } from '../../../forms/interfaces';
2
3
  import type { ClassNameProps, HighlightableClassName } from '../../../styles/classnames/interfaces';
3
4
  export interface ImageInputClassNames {
@@ -8,10 +9,12 @@ export interface ImageInputClassNames {
8
9
  title: string;
9
10
  }
10
11
  export interface ImageInputComponentProps {
12
+ accept?: ImageTypeKey[];
13
+ format?: ImageTypeKey;
11
14
  maxSize?: [number?, number?];
12
15
  minSize?: [number?, number?];
13
16
  onUpload?: (file: File) => FormResponse<string>;
14
17
  }
15
18
  export type ImageInputProps = ClassNameProps<ImageInputClassNames> & InputProps<string | null> & ImageInputComponentProps;
16
- declare function ImageInput({ classNames, classNameProps, id, isDisabled, maxSize, minSize, name, onChange, onUpload, value, }: Readonly<ImageInputProps>): React.ReactElement;
19
+ declare function ImageInput({ accept, classNames, classNameProps, format, id, isDisabled, maxSize, minSize, name, onChange, onUpload, value, }: Readonly<ImageInputProps>): React.ReactElement;
17
20
  export default ImageInput;
@@ -5,7 +5,7 @@ import preprocessImage from '../../../forms/utility/preprocess-image';
5
5
  import { useClassNames } from '../../../styles/context';
6
6
  import tw from '../../../styles/classnames/utility/tw';
7
7
  import ImageInputPanel from '../ImageInputPanel';
8
- function ImageInput({ classNames, classNameProps, id, isDisabled, maxSize, minSize, name, onChange, onUpload, value, }) {
8
+ function ImageInput({ accept, classNames, classNameProps, format, id, isDisabled, maxSize, minSize, name, onChange, onUpload, value, }) {
9
9
  const [error, setError] = useState(null);
10
10
  const [isLoading, setIsLoading] = useState(false);
11
11
  const componentClassNames = useClassNames('imageInput', {
@@ -23,7 +23,12 @@ function ImageInput({ classNames, classNameProps, id, isDisabled, maxSize, minSi
23
23
  return;
24
24
  }
25
25
  try {
26
- const preprocessed = await preprocessImage(imageFiles[0], { maxSize, minSize });
26
+ const preprocessed = await preprocessImage(imageFiles[0], {
27
+ accept,
28
+ format,
29
+ maxSize,
30
+ minSize,
31
+ });
27
32
  const fnReturn = await onUpload(preprocessed);
28
33
  setIsLoading(false);
29
34
  if (fnReturn.error) {
@@ -41,6 +46,6 @@ function ImageInput({ classNames, classNameProps, id, isDisabled, maxSize, minSi
41
46
  setError(err instanceof Error ? err.message : 'An unexpected error occurred');
42
47
  }
43
48
  }
44
- return (_jsxs("div", { className: tw('relative flex items-center justify-center overflow-hidden text-center', componentClassNames?.root, isDisabled || isLoading ? 'pointer-events-none opacity-20' : null), children: [_jsx("input", { name: name, type: "hidden", value: value || '' }), _jsx("input", { accept: "image/*", className: "absolute left-0 top-0 h-full w-full cursor-pointer opacity-0", disabled: isDisabled || isLoading, id: id || name, name: name, onChange: handleChange, type: "file" }), _jsx(ImageInputPanel, { classNames: classNames, classNameProps: { ...classNameProps, isError: Boolean(error) }, error: error, isLoading: isLoading, minSize: minSize, value: value })] }));
49
+ return (_jsxs("div", { className: tw('relative flex items-center justify-center overflow-hidden text-center', componentClassNames?.root, isDisabled || isLoading ? 'pointer-events-none opacity-20' : null), children: [_jsx("input", { name: name, type: "hidden", value: value || '' }), _jsx("input", { accept: accept?.map((type) => `.${type}`).join(',') || 'image/*', className: "absolute left-0 top-0 h-full w-full cursor-pointer opacity-0", disabled: isDisabled || isLoading, id: id || name, name: name, onChange: handleChange, type: "file" }), _jsx(ImageInputPanel, { classNames: classNames, classNameProps: { ...classNameProps, isError: Boolean(error) }, error: error, isLoading: isLoading, minSize: minSize, value: value })] }));
45
50
  }
46
51
  export default ImageInput;
@@ -1,4 +1,7 @@
1
+ export type ImageTypeKey = 'jpg' | 'png' | 'gif' | 'svg' | 'webp';
1
2
  interface PreprocessImageOptions {
3
+ accept?: ImageTypeKey[];
4
+ format?: ImageTypeKey;
2
5
  maxSize?: [number?, number?];
3
6
  minSize?: [number?, number?];
4
7
  }
@@ -1,19 +1,21 @@
1
- const ALLOWED_TYPES = [
2
- 'image/jpeg',
3
- 'image/png',
4
- 'image/gif',
5
- 'image/svg+xml',
6
- 'image/webp',
7
- ];
8
- function isSupportedImage(file) {
9
- return ALLOWED_TYPES.includes(file.type);
1
+ const DEFAULT_FORMAT = 'webp';
2
+ const DEFAULT_ACCEPT = ['jpg', 'png', 'gif', 'svg', 'webp'];
3
+ const imageTypeMap = {
4
+ jpg: 'image/jpeg',
5
+ png: 'image/png',
6
+ gif: 'image/gif',
7
+ svg: 'image/svg+xml',
8
+ webp: 'image/webp',
9
+ };
10
+ function isAccepted(file, accept = DEFAULT_ACCEPT) {
11
+ return accept.map((key) => imageTypeMap[key]).includes(file.type);
10
12
  }
11
13
  function fitWithinMaxSize(width, height, maxWidth, maxHeight) {
12
14
  const scale = Math.min(maxWidth / width, maxHeight / height, 1);
13
15
  return [Math.round(width * scale), Math.round(height * scale)];
14
16
  }
15
17
  export async function preprocessImage(file, options = {}) {
16
- if (!isSupportedImage(file)) {
18
+ if (!isAccepted(file, options.accept)) {
17
19
  throw new Error('Unsupported file type');
18
20
  }
19
21
  if (file.type === 'image/svg+xml') {
@@ -38,20 +40,21 @@ export async function preprocessImage(file, options = {}) {
38
40
  throw new Error('Could not get canvas context');
39
41
  }
40
42
  ctx.drawImage(bitmap, 0, 0, newWidth, newHeight);
43
+ const format = options.format || DEFAULT_FORMAT;
41
44
  const blob = await new Promise((resolve, reject) => {
42
45
  canvas.toBlob((data) => {
43
46
  if (data) {
44
47
  resolve(data);
45
48
  }
46
49
  else {
47
- reject(new Error('Failed to create WebP blob'));
50
+ reject(new Error(`Failed to create ${format.toUpperCase()} blob`));
48
51
  }
49
- }, 'image/webp', 1);
52
+ }, imageTypeMap[format], 1);
50
53
  });
51
54
  const baseName = file.name.replace(/\.\w+$/, '');
52
- const newName = `${baseName}.webp`;
55
+ const newName = `${baseName}.${format}`;
53
56
  return new File([blob], newName, {
54
- type: 'image/webp',
57
+ type: imageTypeMap[format],
55
58
  lastModified: Date.now(),
56
59
  });
57
60
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sqrzro/ui",
3
3
  "type": "module",
4
- "version": "4.0.0-alpha.71",
4
+ "version": "4.0.0-alpha.72",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "license": "ISC",