@trackunit/react-compound-components 0.0.198 → 0.0.201
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/README.md +1 -1
- package/index.cjs.js +132 -0
- package/index.esm.js +131 -4
- package/package.json +6 -2
- package/src/ImageCollection/ImageCollection.d.ts +28 -0
- package/src/ImageCollection/ImageCollection.stories.d.ts +154 -0
- package/src/ImageCollection/helpers.d.ts +13 -0
- package/src/ImageCollection/index.d.ts +2 -0
- package/src/index.d.ts +1 -0
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ This could include components like Dialogs, Toasts, Modals, and more...
|
|
|
5
5
|
|
|
6
6
|
This library is exposed publicly for use in the the Trackunit [Iris App SDK](https://www.npmjs.com/package/@trackunit/iris-app).
|
|
7
7
|
|
|
8
|
-
To browse all
|
|
8
|
+
To browse all available components visit our [Public Storybook](https://apps.iris.trackunit.com/storybook/).
|
|
9
9
|
|
|
10
10
|
For more info and a full guide on Iris App SDK Development, please visit our [Developer Hub](https://developers.trackunit.com/).
|
|
11
11
|
|
package/index.cjs.js
CHANGED
|
@@ -6,6 +6,10 @@ var reactCoreHooks = require('@trackunit/react-core-hooks');
|
|
|
6
6
|
var React = require('react');
|
|
7
7
|
var reactComponents = require('@trackunit/react-components');
|
|
8
8
|
var reactModal = require('@trackunit/react-modal');
|
|
9
|
+
var cssClassVarianceUtilities = require('@trackunit/css-class-variance-utilities');
|
|
10
|
+
var ImageGallery = require('react-image-gallery');
|
|
11
|
+
var tailwindMerge = require('tailwind-merge');
|
|
12
|
+
var reactFormComponents = require('@trackunit/react-form-components');
|
|
9
13
|
|
|
10
14
|
function _interopNamespaceDefault(e) {
|
|
11
15
|
var n = Object.create(null);
|
|
@@ -126,6 +130,129 @@ const ConfirmationDialogContextProvider = ({ children }) => {
|
|
|
126
130
|
return (jsxRuntime.jsxs(reactCoreHooks.ConfirmationDialogProvider, { value: value, children: [children, resolveAction ? jsxRuntime.jsx(ConfirmationDialogModal, { ...confirmationDialogModalProps }) : null] }));
|
|
127
131
|
};
|
|
128
132
|
|
|
133
|
+
const IMAGE_ENDPOINT = "https://images.iris.trackunit.com";
|
|
134
|
+
/**
|
|
135
|
+
* Generates an url for the original sized image from the image id
|
|
136
|
+
*/
|
|
137
|
+
const createOriginalUrl = (id) => `${IMAGE_ENDPOINT}/${btoa(JSON.stringify({
|
|
138
|
+
key: id,
|
|
139
|
+
}))}`;
|
|
140
|
+
const createResizedUrl = (id, width, height) => {
|
|
141
|
+
const request = {
|
|
142
|
+
key: id,
|
|
143
|
+
edits: {
|
|
144
|
+
resize: {
|
|
145
|
+
width,
|
|
146
|
+
height,
|
|
147
|
+
fit: "contain",
|
|
148
|
+
background: "transparent",
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
return `${IMAGE_ENDPOINT}/${btoa(JSON.stringify(request))}`;
|
|
153
|
+
};
|
|
154
|
+
/**
|
|
155
|
+
* Generates an url for the thumbnail size image
|
|
156
|
+
*/
|
|
157
|
+
const createThumbnailUrl = (id) => createResizedUrl(id, 100, 100);
|
|
158
|
+
/**
|
|
159
|
+
* Generates srcSet HTML attribute to avoid loading the full size image on small screens
|
|
160
|
+
*/
|
|
161
|
+
const createSrcSet = (id) => `${createResizedUrl(id, 480)} 480w, ${createResizedUrl(id, 800)} 800w`;
|
|
162
|
+
|
|
163
|
+
const ACCEPT_FOR_IMAGES = "image/jpeg, image/png, image/bmp, image/webp, image/gif";
|
|
164
|
+
const cvaGallery = cssClassVarianceUtilities.cvaMerge([
|
|
165
|
+
// These make the gallery grow to the height of the parent container,
|
|
166
|
+
// which avoids jumping of the thumbnail row when going through images
|
|
167
|
+
// with different ratios
|
|
168
|
+
"grow",
|
|
169
|
+
"[&_.image-gallery-content]:h-[100%]",
|
|
170
|
+
"[&_.image-gallery-content]:flex",
|
|
171
|
+
"[&_.image-gallery-content]:flex-col",
|
|
172
|
+
"[&_.image-gallery-slide-wrapper]:grow",
|
|
173
|
+
"[&_.image-gallery-slide-wrapper]:flex",
|
|
174
|
+
"[&_.image-gallery-slide-wrapper]:flex-col",
|
|
175
|
+
"[&_.image-gallery-slide-wrapper]:justify-center",
|
|
176
|
+
"[&_.image-gallery-slide]:flex",
|
|
177
|
+
// This centers thumbnails and makes the clickable area independent of image size
|
|
178
|
+
"[&_.image-gallery-thumbnail]:h-[100px]",
|
|
179
|
+
]);
|
|
180
|
+
/**
|
|
181
|
+
* Show and manage images
|
|
182
|
+
*
|
|
183
|
+
* Will show thumbnail selection on desktop.
|
|
184
|
+
*
|
|
185
|
+
* Reduces bandwidth usage by lazy loading thumbnails and loading smaller versions of images depending on screen size.
|
|
186
|
+
*/
|
|
187
|
+
const ImageCollection = (props) => {
|
|
188
|
+
var _a;
|
|
189
|
+
const { imageIds, actions, emptyPlaceholderText, emptyPlaceholderActionLabel, additionalItemClassName, uploading } = props;
|
|
190
|
+
const [openImageId, setOpenImageId] = React.useState(imageIds[0]);
|
|
191
|
+
const [isDeleting, setIsDeleting] = React.useState(false);
|
|
192
|
+
const { width } = reactComponents.useResize();
|
|
193
|
+
const fileInputRef = React.useRef(null);
|
|
194
|
+
const imageGalleryRef = React.useRef(null);
|
|
195
|
+
const uploadButton = React.useMemo(() => (actions === null || actions === void 0 ? void 0 : actions.upload) ? (jsxRuntime.jsx("div", { className: "flex justify-end", children: jsxRuntime.jsx(reactComponents.Button, { loading: uploading, onClick: () => { var _a; return (_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, children: actions.upload.label }) })) : null, [actions === null || actions === void 0 ? void 0 : actions.upload, uploading]);
|
|
196
|
+
const items = React.useMemo(() => imageIds.map(imageId => ({
|
|
197
|
+
id: imageId,
|
|
198
|
+
original: createOriginalUrl(imageId),
|
|
199
|
+
thumbnail: createThumbnailUrl(imageId),
|
|
200
|
+
srcSet: createSrcSet(imageId),
|
|
201
|
+
})), [imageIds]);
|
|
202
|
+
React.useEffect(() => {
|
|
203
|
+
if ((!openImageId || !imageIds.includes(openImageId)) && imageIds.length > 0) {
|
|
204
|
+
setOpenImageId(imageIds[0]);
|
|
205
|
+
}
|
|
206
|
+
}, [imageIds, openImageId]);
|
|
207
|
+
const onRemove = React.useCallback(async () => {
|
|
208
|
+
var _a;
|
|
209
|
+
if (!(((_a = actions === null || actions === void 0 ? void 0 : actions.remove) === null || _a === void 0 ? void 0 : _a.onRemove) && openImageId)) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
try {
|
|
213
|
+
let nextIndex;
|
|
214
|
+
const indexToDelete = imageIds.indexOf(openImageId);
|
|
215
|
+
const deletingLastImage = indexToDelete === imageIds.length - 1;
|
|
216
|
+
if (imageIds.length === 1) {
|
|
217
|
+
nextIndex = undefined;
|
|
218
|
+
}
|
|
219
|
+
else if (deletingLastImage) {
|
|
220
|
+
nextIndex = indexToDelete - 1;
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
// we set the index after the deletion, so the index will be the same
|
|
224
|
+
nextIndex = indexToDelete;
|
|
225
|
+
}
|
|
226
|
+
setIsDeleting(true);
|
|
227
|
+
await actions.remove.onRemove(openImageId);
|
|
228
|
+
if (nextIndex !== undefined) {
|
|
229
|
+
imageGalleryRef.current && nextIndex && imageGalleryRef.current.slideToIndex(nextIndex);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
finally {
|
|
233
|
+
setIsDeleting(false);
|
|
234
|
+
}
|
|
235
|
+
}, [actions === null || actions === void 0 ? void 0 : actions.remove, imageIds, openImageId]);
|
|
236
|
+
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [imageIds.length > 0 ? (jsxRuntime.jsxs("div", { className: "flex h-[100vh] max-h-[100vh] flex-col", children: [jsxRuntime.jsx(ImageGallery, { additionalClass: cvaGallery(), items: items, onBeforeSlide: index => { var _a; return setOpenImageId((_a = items[index]) === null || _a === void 0 ? void 0 : _a.id); },
|
|
237
|
+
// Library typing is wrong, this is fired when a gallery image is clicked (library typing says div)
|
|
238
|
+
onClick: (e) => window.open(e.target.src, "_blank"), ref: imageGalleryRef, renderItem: ({ original, srcSet,
|
|
239
|
+
// Typing of the library is not flexible enough to add id, which is only used for the unit tests
|
|
240
|
+
// @ts-ignore
|
|
241
|
+
id, }) => (jsxRuntime.jsx("img", { alt: "full", className: tailwindMerge.twMerge(additionalItemClassName, "w-[100%]", "object-contain"), "data-testid": `image-${id}`, loading: "lazy", sizes: "(max-width: 480px) 480px, 800px", src: original, srcSet: srcSet })), renderThumbInner: ({ thumbnail,
|
|
242
|
+
// Typing of the library is not flexible enough to add id, which is only used for the unit tests
|
|
243
|
+
// @ts-ignore
|
|
244
|
+
id, }) => jsxRuntime.jsx("img", { alt: "thumbnail", "data-testid": `thumbnail-${id}`, loading: "lazy", src: thumbnail }), showFullscreenButton: false, showPlayButton: false, showThumbnails: width > 768,
|
|
245
|
+
// reduce sliding around during deletion
|
|
246
|
+
slideDuration: isDeleting ? 0 : 200, ...props }), uploadButton, (actions === null || actions === void 0 ? void 0 : actions.remove) ? (jsxRuntime.jsx(reactComponents.Button, { className: "absolute right-2 top-2", loading: isDeleting, onClick: onRemove, prefix: jsxRuntime.jsx(reactComponents.Icon, { name: "Trash", size: "small" }), variant: "secondary-danger", children: actions.remove.label })) : null] })) : (jsxRuntime.jsx(reactComponents.EmptyState, { action: ((_a = actions === null || actions === void 0 ? void 0 : actions.upload) === null || _a === void 0 ? void 0 : _a.onUpload) ? (uploading ? (jsxRuntime.jsx(reactComponents.Spinner, {})) : (jsxRuntime.jsx(reactFormComponents.DropZone, { accept: ACCEPT_FOR_IMAGES, filesSelected: actions.upload.onUpload, label: emptyPlaceholderActionLabel }))) : null, description: emptyPlaceholderText })), jsxRuntime.jsx("input", {
|
|
247
|
+
// Users can still select other files on mobile devices
|
|
248
|
+
accept: ACCEPT_FOR_IMAGES, hidden: true, multiple: true, onChange: async (e) => {
|
|
249
|
+
if (!(e.target.files && (actions === null || actions === void 0 ? void 0 : actions.upload))) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
await actions.upload.onUpload(e.target.files);
|
|
253
|
+
}, ref: fileInputRef, type: "file" })] }));
|
|
254
|
+
};
|
|
255
|
+
|
|
129
256
|
/*
|
|
130
257
|
* ----------------------------
|
|
131
258
|
* | SETUP TRANSLATIONS START |
|
|
@@ -136,3 +263,8 @@ const ConfirmationDialogContextProvider = ({ children }) => {
|
|
|
136
263
|
setupLibraryTranslations();
|
|
137
264
|
|
|
138
265
|
exports.ConfirmationDialogContextProvider = ConfirmationDialogContextProvider;
|
|
266
|
+
exports.IMAGE_ENDPOINT = IMAGE_ENDPOINT;
|
|
267
|
+
exports.ImageCollection = ImageCollection;
|
|
268
|
+
exports.createOriginalUrl = createOriginalUrl;
|
|
269
|
+
exports.createSrcSet = createSrcSet;
|
|
270
|
+
exports.createThumbnailUrl = createThumbnailUrl;
|
package/index.esm.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
1
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import { registerTranslations, useNamespaceTranslation } from '@trackunit/i18n-library-translation';
|
|
3
3
|
import { ConfirmationDialogProvider } from '@trackunit/react-core-hooks';
|
|
4
4
|
import * as React from 'react';
|
|
5
|
-
import { useState } from 'react';
|
|
6
|
-
import { Card, CardHeader, CardBody, CardFooter, Button } from '@trackunit/react-components';
|
|
5
|
+
import { useState, useRef, useMemo, useEffect, useCallback } from 'react';
|
|
6
|
+
import { Card, CardHeader, CardBody, CardFooter, Button, useResize, Icon, EmptyState, Spinner } from '@trackunit/react-components';
|
|
7
7
|
import { Modal } from '@trackunit/react-modal';
|
|
8
|
+
import { cvaMerge } from '@trackunit/css-class-variance-utilities';
|
|
9
|
+
import ImageGallery from 'react-image-gallery';
|
|
10
|
+
import { twMerge } from 'tailwind-merge';
|
|
11
|
+
import { DropZone } from '@trackunit/react-form-components';
|
|
8
12
|
|
|
9
13
|
var defaultTranslations = {
|
|
10
14
|
"confirmationDialog.default.message": "Are you certain that you wish to proceed with this course of action?",
|
|
@@ -106,6 +110,129 @@ const ConfirmationDialogContextProvider = ({ children }) => {
|
|
|
106
110
|
return (jsxs(ConfirmationDialogProvider, { value: value, children: [children, resolveAction ? jsx(ConfirmationDialogModal, { ...confirmationDialogModalProps }) : null] }));
|
|
107
111
|
};
|
|
108
112
|
|
|
113
|
+
const IMAGE_ENDPOINT = "https://images.iris.trackunit.com";
|
|
114
|
+
/**
|
|
115
|
+
* Generates an url for the original sized image from the image id
|
|
116
|
+
*/
|
|
117
|
+
const createOriginalUrl = (id) => `${IMAGE_ENDPOINT}/${btoa(JSON.stringify({
|
|
118
|
+
key: id,
|
|
119
|
+
}))}`;
|
|
120
|
+
const createResizedUrl = (id, width, height) => {
|
|
121
|
+
const request = {
|
|
122
|
+
key: id,
|
|
123
|
+
edits: {
|
|
124
|
+
resize: {
|
|
125
|
+
width,
|
|
126
|
+
height,
|
|
127
|
+
fit: "contain",
|
|
128
|
+
background: "transparent",
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
return `${IMAGE_ENDPOINT}/${btoa(JSON.stringify(request))}`;
|
|
133
|
+
};
|
|
134
|
+
/**
|
|
135
|
+
* Generates an url for the thumbnail size image
|
|
136
|
+
*/
|
|
137
|
+
const createThumbnailUrl = (id) => createResizedUrl(id, 100, 100);
|
|
138
|
+
/**
|
|
139
|
+
* Generates srcSet HTML attribute to avoid loading the full size image on small screens
|
|
140
|
+
*/
|
|
141
|
+
const createSrcSet = (id) => `${createResizedUrl(id, 480)} 480w, ${createResizedUrl(id, 800)} 800w`;
|
|
142
|
+
|
|
143
|
+
const ACCEPT_FOR_IMAGES = "image/jpeg, image/png, image/bmp, image/webp, image/gif";
|
|
144
|
+
const cvaGallery = cvaMerge([
|
|
145
|
+
// These make the gallery grow to the height of the parent container,
|
|
146
|
+
// which avoids jumping of the thumbnail row when going through images
|
|
147
|
+
// with different ratios
|
|
148
|
+
"grow",
|
|
149
|
+
"[&_.image-gallery-content]:h-[100%]",
|
|
150
|
+
"[&_.image-gallery-content]:flex",
|
|
151
|
+
"[&_.image-gallery-content]:flex-col",
|
|
152
|
+
"[&_.image-gallery-slide-wrapper]:grow",
|
|
153
|
+
"[&_.image-gallery-slide-wrapper]:flex",
|
|
154
|
+
"[&_.image-gallery-slide-wrapper]:flex-col",
|
|
155
|
+
"[&_.image-gallery-slide-wrapper]:justify-center",
|
|
156
|
+
"[&_.image-gallery-slide]:flex",
|
|
157
|
+
// This centers thumbnails and makes the clickable area independent of image size
|
|
158
|
+
"[&_.image-gallery-thumbnail]:h-[100px]",
|
|
159
|
+
]);
|
|
160
|
+
/**
|
|
161
|
+
* Show and manage images
|
|
162
|
+
*
|
|
163
|
+
* Will show thumbnail selection on desktop.
|
|
164
|
+
*
|
|
165
|
+
* Reduces bandwidth usage by lazy loading thumbnails and loading smaller versions of images depending on screen size.
|
|
166
|
+
*/
|
|
167
|
+
const ImageCollection = (props) => {
|
|
168
|
+
var _a;
|
|
169
|
+
const { imageIds, actions, emptyPlaceholderText, emptyPlaceholderActionLabel, additionalItemClassName, uploading } = props;
|
|
170
|
+
const [openImageId, setOpenImageId] = useState(imageIds[0]);
|
|
171
|
+
const [isDeleting, setIsDeleting] = useState(false);
|
|
172
|
+
const { width } = useResize();
|
|
173
|
+
const fileInputRef = useRef(null);
|
|
174
|
+
const imageGalleryRef = useRef(null);
|
|
175
|
+
const uploadButton = useMemo(() => (actions === null || actions === void 0 ? void 0 : actions.upload) ? (jsx("div", { className: "flex justify-end", children: jsx(Button, { loading: uploading, onClick: () => { var _a; return (_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, children: actions.upload.label }) })) : null, [actions === null || actions === void 0 ? void 0 : actions.upload, uploading]);
|
|
176
|
+
const items = useMemo(() => imageIds.map(imageId => ({
|
|
177
|
+
id: imageId,
|
|
178
|
+
original: createOriginalUrl(imageId),
|
|
179
|
+
thumbnail: createThumbnailUrl(imageId),
|
|
180
|
+
srcSet: createSrcSet(imageId),
|
|
181
|
+
})), [imageIds]);
|
|
182
|
+
useEffect(() => {
|
|
183
|
+
if ((!openImageId || !imageIds.includes(openImageId)) && imageIds.length > 0) {
|
|
184
|
+
setOpenImageId(imageIds[0]);
|
|
185
|
+
}
|
|
186
|
+
}, [imageIds, openImageId]);
|
|
187
|
+
const onRemove = useCallback(async () => {
|
|
188
|
+
var _a;
|
|
189
|
+
if (!(((_a = actions === null || actions === void 0 ? void 0 : actions.remove) === null || _a === void 0 ? void 0 : _a.onRemove) && openImageId)) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
try {
|
|
193
|
+
let nextIndex;
|
|
194
|
+
const indexToDelete = imageIds.indexOf(openImageId);
|
|
195
|
+
const deletingLastImage = indexToDelete === imageIds.length - 1;
|
|
196
|
+
if (imageIds.length === 1) {
|
|
197
|
+
nextIndex = undefined;
|
|
198
|
+
}
|
|
199
|
+
else if (deletingLastImage) {
|
|
200
|
+
nextIndex = indexToDelete - 1;
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
// we set the index after the deletion, so the index will be the same
|
|
204
|
+
nextIndex = indexToDelete;
|
|
205
|
+
}
|
|
206
|
+
setIsDeleting(true);
|
|
207
|
+
await actions.remove.onRemove(openImageId);
|
|
208
|
+
if (nextIndex !== undefined) {
|
|
209
|
+
imageGalleryRef.current && nextIndex && imageGalleryRef.current.slideToIndex(nextIndex);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
finally {
|
|
213
|
+
setIsDeleting(false);
|
|
214
|
+
}
|
|
215
|
+
}, [actions === null || actions === void 0 ? void 0 : actions.remove, imageIds, openImageId]);
|
|
216
|
+
return (jsxs(Fragment, { children: [imageIds.length > 0 ? (jsxs("div", { className: "flex h-[100vh] max-h-[100vh] flex-col", children: [jsx(ImageGallery, { additionalClass: cvaGallery(), items: items, onBeforeSlide: index => { var _a; return setOpenImageId((_a = items[index]) === null || _a === void 0 ? void 0 : _a.id); },
|
|
217
|
+
// Library typing is wrong, this is fired when a gallery image is clicked (library typing says div)
|
|
218
|
+
onClick: (e) => window.open(e.target.src, "_blank"), ref: imageGalleryRef, renderItem: ({ original, srcSet,
|
|
219
|
+
// Typing of the library is not flexible enough to add id, which is only used for the unit tests
|
|
220
|
+
// @ts-ignore
|
|
221
|
+
id, }) => (jsx("img", { alt: "full", className: twMerge(additionalItemClassName, "w-[100%]", "object-contain"), "data-testid": `image-${id}`, loading: "lazy", sizes: "(max-width: 480px) 480px, 800px", src: original, srcSet: srcSet })), renderThumbInner: ({ thumbnail,
|
|
222
|
+
// Typing of the library is not flexible enough to add id, which is only used for the unit tests
|
|
223
|
+
// @ts-ignore
|
|
224
|
+
id, }) => jsx("img", { alt: "thumbnail", "data-testid": `thumbnail-${id}`, loading: "lazy", src: thumbnail }), showFullscreenButton: false, showPlayButton: false, showThumbnails: width > 768,
|
|
225
|
+
// reduce sliding around during deletion
|
|
226
|
+
slideDuration: isDeleting ? 0 : 200, ...props }), uploadButton, (actions === null || actions === void 0 ? void 0 : actions.remove) ? (jsx(Button, { className: "absolute right-2 top-2", loading: isDeleting, onClick: onRemove, prefix: jsx(Icon, { name: "Trash", size: "small" }), variant: "secondary-danger", children: actions.remove.label })) : null] })) : (jsx(EmptyState, { action: ((_a = actions === null || actions === void 0 ? void 0 : actions.upload) === null || _a === void 0 ? void 0 : _a.onUpload) ? (uploading ? (jsx(Spinner, {})) : (jsx(DropZone, { accept: ACCEPT_FOR_IMAGES, filesSelected: actions.upload.onUpload, label: emptyPlaceholderActionLabel }))) : null, description: emptyPlaceholderText })), jsx("input", {
|
|
227
|
+
// Users can still select other files on mobile devices
|
|
228
|
+
accept: ACCEPT_FOR_IMAGES, hidden: true, multiple: true, onChange: async (e) => {
|
|
229
|
+
if (!(e.target.files && (actions === null || actions === void 0 ? void 0 : actions.upload))) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
await actions.upload.onUpload(e.target.files);
|
|
233
|
+
}, ref: fileInputRef, type: "file" })] }));
|
|
234
|
+
};
|
|
235
|
+
|
|
109
236
|
/*
|
|
110
237
|
* ----------------------------
|
|
111
238
|
* | SETUP TRANSLATIONS START |
|
|
@@ -115,4 +242,4 @@ const ConfirmationDialogContextProvider = ({ children }) => {
|
|
|
115
242
|
*/
|
|
116
243
|
setupLibraryTranslations();
|
|
117
244
|
|
|
118
|
-
export { ConfirmationDialogContextProvider };
|
|
245
|
+
export { ConfirmationDialogContextProvider, IMAGE_ENDPOINT, ImageCollection, createOriginalUrl, createSrcSet, createThumbnailUrl };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trackunit/react-compound-components",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.201",
|
|
4
4
|
"repository": "https://github.com/Trackunit/manager",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
6
6
|
"dependencies": {
|
|
@@ -9,7 +9,11 @@
|
|
|
9
9
|
"react": "18.3.1",
|
|
10
10
|
"@trackunit/react-components": "*",
|
|
11
11
|
"@trackunit/i18n-library-translation": "*",
|
|
12
|
-
"@trackunit/react-modal": "*"
|
|
12
|
+
"@trackunit/react-modal": "*",
|
|
13
|
+
"@trackunit/css-class-variance-utilities": "*",
|
|
14
|
+
"@trackunit/react-form-components": "*",
|
|
15
|
+
"tailwind-merge": "^2.0.0",
|
|
16
|
+
"react-image-gallery": "1.3.0"
|
|
13
17
|
},
|
|
14
18
|
"engines": {
|
|
15
19
|
"node": ">=20.x",
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ComponentProps } from "react";
|
|
2
|
+
import ImageGallery from "react-image-gallery";
|
|
3
|
+
type Props = Omit<ComponentProps<typeof ImageGallery>, "items"> & {
|
|
4
|
+
imageIds: string[];
|
|
5
|
+
actions?: {
|
|
6
|
+
upload?: {
|
|
7
|
+
label: string;
|
|
8
|
+
onUpload: (pictures: FileList) => Promise<void>;
|
|
9
|
+
};
|
|
10
|
+
remove?: {
|
|
11
|
+
label: string;
|
|
12
|
+
onRemove?: (id: string) => Promise<void>;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
emptyPlaceholderText: string;
|
|
16
|
+
emptyPlaceholderActionLabel?: string;
|
|
17
|
+
additionalItemClassName?: string;
|
|
18
|
+
uploading?: boolean;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Show and manage images
|
|
22
|
+
*
|
|
23
|
+
* Will show thumbnail selection on desktop.
|
|
24
|
+
*
|
|
25
|
+
* Reduces bandwidth usage by lazy loading thumbnails and loading smaller versions of images depending on screen size.
|
|
26
|
+
*/
|
|
27
|
+
export declare const ImageCollection: (props: Props) => import("react/jsx-runtime").JSX.Element;
|
|
28
|
+
export {};
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { Meta } from "@storybook/react";
|
|
2
|
+
import { ComponentProps } from "react";
|
|
3
|
+
import { ImageCollection } from "./ImageCollection";
|
|
4
|
+
declare const meta: Meta<typeof ImageCollection>;
|
|
5
|
+
export default meta;
|
|
6
|
+
export declare const packageName: () => import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export declare const Default: {
|
|
8
|
+
name: string;
|
|
9
|
+
render: (props: ComponentProps<typeof ImageCollection>) => import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
args: {
|
|
11
|
+
items: {
|
|
12
|
+
id: string;
|
|
13
|
+
original: string;
|
|
14
|
+
thumbnail: string;
|
|
15
|
+
}[];
|
|
16
|
+
onClick?: import("react").MouseEventHandler<HTMLDivElement> | undefined;
|
|
17
|
+
flickThreshold?: number | undefined;
|
|
18
|
+
showNav?: boolean | undefined;
|
|
19
|
+
autoPlay?: boolean | undefined;
|
|
20
|
+
lazyLoad?: boolean | undefined;
|
|
21
|
+
infinite?: boolean | undefined;
|
|
22
|
+
showIndex?: boolean | undefined;
|
|
23
|
+
showBullets?: boolean | undefined;
|
|
24
|
+
showThumbnails?: boolean | undefined;
|
|
25
|
+
showPlayButton?: boolean | undefined;
|
|
26
|
+
showFullscreenButton?: boolean | undefined;
|
|
27
|
+
disableThumbnailScroll?: boolean | undefined;
|
|
28
|
+
disableKeyDown?: boolean | undefined;
|
|
29
|
+
disableSwipe?: boolean | undefined;
|
|
30
|
+
useBrowserFullscreen?: boolean | undefined;
|
|
31
|
+
preventDefaultTouchmoveEvent?: boolean | undefined;
|
|
32
|
+
onErrorImageURL?: string | undefined;
|
|
33
|
+
indexSeparator?: string | undefined;
|
|
34
|
+
thumbnailPosition?: "top" | "right" | "bottom" | "left" | undefined;
|
|
35
|
+
startIndex?: number | undefined;
|
|
36
|
+
slideDuration?: number | undefined;
|
|
37
|
+
slideInterval?: number | undefined;
|
|
38
|
+
slideOnThumbnailOver?: boolean | undefined;
|
|
39
|
+
swipeThreshold?: number | undefined;
|
|
40
|
+
swipingTransitionDuration?: number | undefined;
|
|
41
|
+
onSlide?: ((currentIndex: number) => void) | undefined;
|
|
42
|
+
onBeforeSlide?: ((currentIndex: number) => void) | undefined;
|
|
43
|
+
onScreenChange?: ((fullScreen: boolean) => void) | undefined;
|
|
44
|
+
onPause?: ((currentIndex: number) => void) | undefined;
|
|
45
|
+
onPlay?: ((currentIndex: number) => void) | undefined;
|
|
46
|
+
onImageLoad?: import("react").ReactEventHandler<HTMLImageElement> | undefined;
|
|
47
|
+
onImageError?: import("react").ReactEventHandler<HTMLImageElement> | undefined;
|
|
48
|
+
onTouchMove?: import("react").TouchEventHandler<HTMLDivElement> | undefined;
|
|
49
|
+
onTouchEnd?: import("react").TouchEventHandler<HTMLDivElement> | undefined;
|
|
50
|
+
onTouchStart?: import("react").TouchEventHandler<HTMLDivElement> | undefined;
|
|
51
|
+
onMouseOver?: import("react").MouseEventHandler<HTMLDivElement> | undefined;
|
|
52
|
+
onMouseLeave?: import("react").MouseEventHandler<HTMLDivElement> | undefined;
|
|
53
|
+
onThumbnailError?: import("react").ReactEventHandler<HTMLImageElement> | undefined;
|
|
54
|
+
onThumbnailClick?: ((event: import("react").MouseEvent<HTMLAnchorElement>, index: number) => void) | undefined;
|
|
55
|
+
renderCustomControls?: (() => import("react").ReactNode) | undefined;
|
|
56
|
+
renderLeftNav?: ((onClick: import("react").MouseEventHandler<HTMLElement>, disabled: boolean) => import("react").ReactNode) | undefined;
|
|
57
|
+
renderRightNav?: ((onClick: import("react").MouseEventHandler<HTMLElement>, disabled: boolean) => import("react").ReactNode) | undefined;
|
|
58
|
+
renderPlayPauseButton?: ((onClick: import("react").MouseEventHandler<HTMLElement>, isPlaying: boolean) => import("react").ReactNode) | undefined;
|
|
59
|
+
renderFullscreenButton?: ((onClick: import("react").MouseEventHandler<HTMLElement>, isFullscreen: boolean) => import("react").ReactNode) | undefined;
|
|
60
|
+
renderItem?: ((item: import("react-image-gallery").ReactImageGalleryItem) => import("react").ReactNode) | undefined;
|
|
61
|
+
renderThumbInner?: ((item: import("react-image-gallery").ReactImageGalleryItem) => import("react").ReactNode) | undefined;
|
|
62
|
+
stopPropagation?: boolean | undefined;
|
|
63
|
+
additionalClass?: string | undefined;
|
|
64
|
+
useTranslate3D?: boolean | undefined;
|
|
65
|
+
isRTL?: boolean | undefined;
|
|
66
|
+
imageIds?: string[] | undefined;
|
|
67
|
+
actions?: {
|
|
68
|
+
upload?: {
|
|
69
|
+
label: string;
|
|
70
|
+
onUpload: (pictures: FileList) => Promise<void>;
|
|
71
|
+
};
|
|
72
|
+
remove?: {
|
|
73
|
+
label: string;
|
|
74
|
+
onRemove?: (id: string) => Promise<void>;
|
|
75
|
+
};
|
|
76
|
+
} | undefined;
|
|
77
|
+
emptyPlaceholderText?: string | undefined;
|
|
78
|
+
emptyPlaceholderActionLabel?: string | undefined;
|
|
79
|
+
additionalItemClassName?: string | undefined;
|
|
80
|
+
uploading?: boolean | undefined;
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
export declare const EmptyState: {
|
|
84
|
+
name: string;
|
|
85
|
+
render: (props: ComponentProps<typeof ImageCollection>) => import("react/jsx-runtime").JSX.Element;
|
|
86
|
+
args: {
|
|
87
|
+
items: never[];
|
|
88
|
+
onClick?: import("react").MouseEventHandler<HTMLDivElement> | undefined;
|
|
89
|
+
flickThreshold?: number | undefined;
|
|
90
|
+
showNav?: boolean | undefined;
|
|
91
|
+
autoPlay?: boolean | undefined;
|
|
92
|
+
lazyLoad?: boolean | undefined;
|
|
93
|
+
infinite?: boolean | undefined;
|
|
94
|
+
showIndex?: boolean | undefined;
|
|
95
|
+
showBullets?: boolean | undefined;
|
|
96
|
+
showThumbnails?: boolean | undefined;
|
|
97
|
+
showPlayButton?: boolean | undefined;
|
|
98
|
+
showFullscreenButton?: boolean | undefined;
|
|
99
|
+
disableThumbnailScroll?: boolean | undefined;
|
|
100
|
+
disableKeyDown?: boolean | undefined;
|
|
101
|
+
disableSwipe?: boolean | undefined;
|
|
102
|
+
useBrowserFullscreen?: boolean | undefined;
|
|
103
|
+
preventDefaultTouchmoveEvent?: boolean | undefined;
|
|
104
|
+
onErrorImageURL?: string | undefined;
|
|
105
|
+
indexSeparator?: string | undefined;
|
|
106
|
+
thumbnailPosition?: "top" | "right" | "bottom" | "left" | undefined;
|
|
107
|
+
startIndex?: number | undefined;
|
|
108
|
+
slideDuration?: number | undefined;
|
|
109
|
+
slideInterval?: number | undefined;
|
|
110
|
+
slideOnThumbnailOver?: boolean | undefined;
|
|
111
|
+
swipeThreshold?: number | undefined;
|
|
112
|
+
swipingTransitionDuration?: number | undefined;
|
|
113
|
+
onSlide?: ((currentIndex: number) => void) | undefined;
|
|
114
|
+
onBeforeSlide?: ((currentIndex: number) => void) | undefined;
|
|
115
|
+
onScreenChange?: ((fullScreen: boolean) => void) | undefined;
|
|
116
|
+
onPause?: ((currentIndex: number) => void) | undefined;
|
|
117
|
+
onPlay?: ((currentIndex: number) => void) | undefined;
|
|
118
|
+
onImageLoad?: import("react").ReactEventHandler<HTMLImageElement> | undefined;
|
|
119
|
+
onImageError?: import("react").ReactEventHandler<HTMLImageElement> | undefined;
|
|
120
|
+
onTouchMove?: import("react").TouchEventHandler<HTMLDivElement> | undefined;
|
|
121
|
+
onTouchEnd?: import("react").TouchEventHandler<HTMLDivElement> | undefined;
|
|
122
|
+
onTouchStart?: import("react").TouchEventHandler<HTMLDivElement> | undefined;
|
|
123
|
+
onMouseOver?: import("react").MouseEventHandler<HTMLDivElement> | undefined;
|
|
124
|
+
onMouseLeave?: import("react").MouseEventHandler<HTMLDivElement> | undefined;
|
|
125
|
+
onThumbnailError?: import("react").ReactEventHandler<HTMLImageElement> | undefined;
|
|
126
|
+
onThumbnailClick?: ((event: import("react").MouseEvent<HTMLAnchorElement>, index: number) => void) | undefined;
|
|
127
|
+
renderCustomControls?: (() => import("react").ReactNode) | undefined;
|
|
128
|
+
renderLeftNav?: ((onClick: import("react").MouseEventHandler<HTMLElement>, disabled: boolean) => import("react").ReactNode) | undefined;
|
|
129
|
+
renderRightNav?: ((onClick: import("react").MouseEventHandler<HTMLElement>, disabled: boolean) => import("react").ReactNode) | undefined;
|
|
130
|
+
renderPlayPauseButton?: ((onClick: import("react").MouseEventHandler<HTMLElement>, isPlaying: boolean) => import("react").ReactNode) | undefined;
|
|
131
|
+
renderFullscreenButton?: ((onClick: import("react").MouseEventHandler<HTMLElement>, isFullscreen: boolean) => import("react").ReactNode) | undefined;
|
|
132
|
+
renderItem?: ((item: import("react-image-gallery").ReactImageGalleryItem) => import("react").ReactNode) | undefined;
|
|
133
|
+
renderThumbInner?: ((item: import("react-image-gallery").ReactImageGalleryItem) => import("react").ReactNode) | undefined;
|
|
134
|
+
stopPropagation?: boolean | undefined;
|
|
135
|
+
additionalClass?: string | undefined;
|
|
136
|
+
useTranslate3D?: boolean | undefined;
|
|
137
|
+
isRTL?: boolean | undefined;
|
|
138
|
+
imageIds?: string[] | undefined;
|
|
139
|
+
actions?: {
|
|
140
|
+
upload?: {
|
|
141
|
+
label: string;
|
|
142
|
+
onUpload: (pictures: FileList) => Promise<void>;
|
|
143
|
+
};
|
|
144
|
+
remove?: {
|
|
145
|
+
label: string;
|
|
146
|
+
onRemove?: (id: string) => Promise<void>;
|
|
147
|
+
};
|
|
148
|
+
} | undefined;
|
|
149
|
+
emptyPlaceholderText?: string | undefined;
|
|
150
|
+
emptyPlaceholderActionLabel?: string | undefined;
|
|
151
|
+
additionalItemClassName?: string | undefined;
|
|
152
|
+
uploading?: boolean | undefined;
|
|
153
|
+
};
|
|
154
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare const IMAGE_ENDPOINT = "https://images.iris.trackunit.com";
|
|
2
|
+
/**
|
|
3
|
+
* Generates an url for the original sized image from the image id
|
|
4
|
+
*/
|
|
5
|
+
export declare const createOriginalUrl: (id: string) => string;
|
|
6
|
+
/**
|
|
7
|
+
* Generates an url for the thumbnail size image
|
|
8
|
+
*/
|
|
9
|
+
export declare const createThumbnailUrl: (id: string) => string;
|
|
10
|
+
/**
|
|
11
|
+
* Generates srcSet HTML attribute to avoid loading the full size image on small screens
|
|
12
|
+
*/
|
|
13
|
+
export declare const createSrcSet: (id: string) => string;
|
package/src/index.d.ts
CHANGED