Skip to content

Commit 4195c69

Browse files
Fritsch-TechSakuk3gremo
authored
Add all DirectusImage component props (#358)
Co-authored-by: Sakuk <[email protected]> Co-authored-by: Marco Polichetti <[email protected]>
1 parent 2d69c53 commit 4195c69

File tree

4 files changed

+147
-14
lines changed

4 files changed

+147
-14
lines changed

README.md

+9-2
Original file line numberDiff line numberDiff line change
@@ -127,11 +127,18 @@ export const TodoItem = ({ item }) => {
127127
Computes the URL of the given resource `asset`, rendering it using the `render` prop:
128128

129129
- `asset`: the asset representing the resource (`string` or `object` with an `id` property)
130+
- `render`: a function (which receives an object with the `url` property and all propertys provided to `DirectusImage`) that provides the component to render
131+
- `presetKey`: the key of the [Storage Asset Preset](https://docs.directus.io/user-guide/cloud/project-settings.html#files-storage), a shortcut for the below parameters
132+
133+
> **Note**: the following parameters are ignored if `presetKey` is provided
134+
130135
- `fit`: fit of the thumbnail while always preserving the aspect ratio, can be any of the following options: `cover`, `contain`, `inside` or `outside`
131136
- `height`: height of the thumbnail in pixels
132-
- `quality`: quality of the thumbnail (`1` to `100`)
133137
- `width`: width of the thumbnail in pixels
134-
- `render`: a function (which receives an object with the `url` property) that provides the component to render
138+
- `quality`: quality of the thumbnail (`1` to `100`)
139+
- `format`: the return file format
140+
- `withoutEnlargement`: if `true`, the thumbnail will not be larger than the original image
141+
- `transforms`: an array of [Sharp transforms](https://sharp.pixelplumbing.com/api-operation) to apply to the image
135142

136143
```jsx
137144
import React from 'react';

src/components/DirectusImage.spec.tsx

+7
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@ describe('Component', () => {
3434
fit: 'cover',
3535
},
3636
],
37+
[
38+
{
39+
apiUrl: 'http://example.com',
40+
asset: '11263b49-13b9-4ed8-ba39-01fe7cbeb284',
41+
presetKey: 'true',
42+
},
43+
],
3744
])('pass the props to the renderer', props => {
3845
render(<DirectusImage {...(props as DirectusImageProps)} render={dummyRenderer} />);
3946

src/components/DirectusImage.tsx

+83-6
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,98 @@ import * as React from 'react';
33
import { DirectusContext } from '@/DirectusProvider';
44
import { DirectusImageProps } from '@/types';
55

6-
export const DirectusImage = ({ apiUrl: propsApiUrl, asset, render, ...params }: DirectusImageProps): JSX.Element => {
6+
export const DirectusImage = ({
7+
apiUrl: propsApiUrl,
8+
asset,
9+
render,
10+
presetKey,
11+
width,
12+
height,
13+
quality,
14+
fit,
15+
format,
16+
withoutEnlargement,
17+
transforms,
18+
}: DirectusImageProps): JSX.Element => {
719
const directusContext = React.useContext(DirectusContext);
820

921
if (!directusContext && !propsApiUrl) {
10-
throw new Error('DirectusAsset requires either a DirectusProvider or an apiUrl prop');
22+
throw new Error('DirectusImage requires either a DirectusProvider or an apiUrl prop');
1123
}
1224

1325
const apiUrl = propsApiUrl || directusContext?.apiUrl;
1426

27+
const imageUrl = React.useMemo((): string | undefined => {
28+
const assetId = asset && 'object' === typeof asset ? asset.id : asset;
29+
30+
if (!assetId) {
31+
return undefined;
32+
}
33+
34+
const params = new URLSearchParams();
35+
36+
// test if props is DirectusImagePropsKeyed or DirectusImagePropsDynamic
37+
if ('string' === typeof presetKey) {
38+
params.append('key', presetKey);
39+
} else {
40+
if (width) {
41+
params.append('width', width.toString());
42+
}
43+
if (height) {
44+
params.append('height', height.toString());
45+
}
46+
if (quality) {
47+
params.append('quality', quality.toString());
48+
}
49+
if (fit) {
50+
params.append('fit', fit);
51+
}
52+
if (format) {
53+
params.append('format', format);
54+
}
55+
if (withoutEnlargement) {
56+
params.append('withoutEnlargement', 'true');
57+
}
58+
if (transforms) {
59+
params.append('transforms', JSON.stringify(transforms));
60+
}
61+
}
62+
63+
return `${apiUrl}/assets/${assetId}?${params.toString()}`;
64+
}, [
65+
directusContext,
66+
asset,
67+
propsApiUrl,
68+
presetKey,
69+
width,
70+
height,
71+
quality,
72+
fit,
73+
format,
74+
withoutEnlargement,
75+
transforms,
76+
]);
77+
78+
// test if props is DirectusImagePropsKeyed or DirectusImagePropsDynamic
79+
if ('string' === typeof presetKey) {
80+
return render({
81+
apiUrl,
82+
asset,
83+
url: imageUrl,
84+
presetKey,
85+
});
86+
}
87+
1588
return render({
1689
apiUrl,
1790
asset,
18-
...params,
19-
url: `${apiUrl}/assets/${'object' === typeof asset ? asset.id : asset}?${new URLSearchParams(
20-
params as Record<string, string>
21-
).toString()}`,
91+
url: imageUrl,
92+
width,
93+
height,
94+
quality,
95+
fit,
96+
format,
97+
withoutEnlargement,
98+
transforms,
2299
});
23100
};

src/types.ts

+48-6
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,32 @@ export interface DirectusAssetProps {
3131
}
3232

3333
/**
34-
* Shape of the `DirectusImage` component `render` prop.
34+
* Shape of the `DirectusImage` component `render` prop, with `presetKey` prop.
3535
*/
36-
export type DirectusImageRenderer = Omit<DirectusImageProps, 'render'> & {
36+
export type DirectusImageRendererKeyed = Omit<DirectusImagePropsKeyed, 'render'> & {
3737
url?: string;
3838
};
3939

4040
/**
41-
* Shape of `DirectusImage` component props.
41+
* Shape of the `DirectusImage` component `render` prop, with dynamic props.
42+
*/
43+
export type DirectusImageRendererDynamic = Omit<DirectusImagePropsDynamic, 'render'> & {
44+
url?: string;
45+
};
46+
47+
/**
48+
* Shape of the `DirectusImage` component `render` prop.
4249
*/
43-
export interface DirectusImageProps extends Omit<DirectusAssetProps, 'download' | 'render'> {
50+
export type DirectusImageRenderer = DirectusImageRendererKeyed | DirectusImageRendererDynamic;
51+
52+
/**
53+
* Shape of a generic image component props.
54+
*/
55+
export type DirectusImagePropsBase = Omit<DirectusAssetProps, 'download' | 'render'> & {
56+
render: (args: DirectusImageRenderer) => JSX.Element;
57+
};
58+
59+
export interface DirectusImageCustomProps {
4460
/** The width of the thumbnail in pixels. */
4561
width?: number;
4662
/** The height of the thumbnail in pixels. */
@@ -49,10 +65,36 @@ export interface DirectusImageProps extends Omit<DirectusAssetProps, 'download'
4965
quality?: number;
5066
/** The fit of the thumbnail while always preserving the aspect ratio. */
5167
fit?: 'cover' | 'contain' | 'inside' | 'outside';
52-
/** A function that returns the React element to be rendered. It will receive an object with the `url` key and all the passed props. */
53-
render: (args: DirectusImageRenderer) => JSX.Element;
68+
/** What file format to return the image in. */
69+
format?: 'auto' | 'jpg' | 'png' | 'webp' | 'tiff';
70+
/** Disable image up-scaling. */
71+
withoutEnlargement?: boolean;
72+
/** An array of sharp operations to apply to the image. */
73+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
74+
transforms?: [string, ...any[]][];
5475
}
5576

77+
/**
78+
* Shape of the `DirectusImage` component props, with dynamic props.
79+
*/
80+
export type DirectusImagePropsDynamic = {
81+
presetKey?: never;
82+
} & DirectusImageCustomProps &
83+
DirectusImagePropsBase;
84+
85+
/**
86+
* Shape of the `DirectusImage` component props, with `presetKey` prop.
87+
*/
88+
export type DirectusImagePropsKeyed = {
89+
/** Key for Storage Asset Preset ( https://docs.directus.io/user-guide/cloud/project-settings.html#files-thumbnails ). */
90+
presetKey: string;
91+
} & { [p in keyof DirectusImageCustomProps]: never } & DirectusImagePropsBase;
92+
93+
/**
94+
* Shape of the `DirectusImage` component props.
95+
*/
96+
export type DirectusImageProps = DirectusImagePropsDynamic | DirectusImagePropsKeyed;
97+
5698
/**
5799
* Shape of the context provider props.
58100
*/

0 commit comments

Comments
 (0)