Utils API
Helper components and hooks for building custom side panels
When building a custom side panel feel free to choose any React components you prefer. You can build your own one from scratch as well.
Here's a common use case however: display a grid of media file previews in the side panel (templates, images, assets, etc.). For this purpose you can leverage <ImagesGrid />
component.
How to use <ImagesGrid />
component?
<ImagesGrid />
is a React component that:
- Displays an array of any items as images
- Supports infinite scrolling API
- Supports drag&drop from side panel into canvas
import { ImagesGrid } from 'polotno/side-panel/images-grid';
<ImagesGrid
// pass an array of items that have any image information
images={[{ url: 'example.png' }, { url: 'example.png' }]}
// a function to get image URL from an item of the array
getPreview={(item) => item.url}
// this function will be called when user is clicked on image or dragged it into canvas
onSelect={async (image, pos, element, event) => {
// image - an item from your array
// pos - relative mouse position on drop. undefined if user just clicked on image
// element - model from your store if images was dropped on an element.
// Can be useful if you want to change some props on existing element instead of creating a new one
// event - will have additional data such as
// elements - list of all elements under the mouse
// page - page where user dropped the image
const width = 100;
const height = 100;
const x = (pos?.x || store.width / 2) - width / 2;
const y = (pos?.y || store.height / 2) - height / 2;
store.activePage?.addElement({
type: 'image',
src: image.url,
width,
height,
x,
y,
});
}}
// should we show a loading indicator at the end of the grid?
isLoading={false}
// load more will be called when user scrolled to the bottom of the list
// you can request new data from your API there.
// pass false if more data is not available
loadMore={() => {}}
// optional show special component at the bottom of every image element
getCredit={(image) => <span>Photo by Anton</span>}
// how many columns do we need? It actually should be called "columnsNumber"
// we will rename it later
rowsNumber={2}
// optionally pass crossOrigin param for images
crossOrigin="anonymous"
// optionally pass height size of the image, by default it is "auto"
itemHeight={'100px'}
// if "error" is not falsy, grid will display an error message
error={true}
/>
How to use useInfiniteAPI
hook?
In scenarios where an API is called to display a list of images within a side panel grid, various React tools are at your disposal. This includes utilizing fetch methods within hooks or leveraging any suitable library for the task. Additionally, Polotno provides the useInfiniteAPI
hook as a convenient option. The useInfiniteAPI
hook serves as a wrapper around the swr library, simplifying the process of fetching data.
import { useInfiniteAPI } from 'polotno/utils/use-api';
export const SidePanel = () => {
const { data, isLoading, loadMore, isReachingEnd, hasMore, reset, error } = useInfiniteAPI({
// a function that will return a URL to request
getAPI: ({ page, query }) => `https://example.com/api?page=${page}&query=${query}`,
// default search query for the first call call
defaultQuery: '',
// timeout before making a new call when you change search query, useful for debouncing
timeout: 500,
// a function that should return number of pages available from the first API response
// usually API response has a "totalPages", "size" or other field that tells you how many pages are available
getSize: (firstResult) => firstResult.total_pages,
// a function to make an API request
// here is example of default fetch function
// you can customize it to add for example headers
fetchFunc: (url) => fetch(url).then((r) => r.json()),
});
// data - is an array of responses from the API
// each item in the array corresponds to a page
// loadMore - function to be called when you want to request for more data
// ImagesGrid will use it when you scrape the bottom of the list
// isReachingEnd - true if you are at the end of the list
// hasMore - true if you can request for more data
// reset - function to be called when you want to reset the list
// error - error object if something went wrong
};
How to drop elements from side panel into workspace?
If you prefer not to use <ImagesGrid />
component, you will have to handle drag&drop of DOM elements implementation. However, Polotno has some tools to listen to drop events on the workspace. You can use this:
import { unstable_registerNextDomDrop } from 'polotno/config';
// then in your components inside side panel you can do something like this:
<img
draggable
src={url}
onDragStart={() => {
registerNextDomDrop((pos, element) => {
// "pos" - is relative mouse position of drop
// "element" - is element from your store in case when DOM object is dropped on another element
// you can just create new element on drop position
// or you can update existing element
// for example we can drop image from side panel into existing 'image' element in the workspace
if (element && element.type === 'image') {
// you can update any property you want, src, clipSrc, border, etc
element.set({ src: url });
return;
}
// or we can just create a new element
store.activePage.addElement({
type: 'image',
src: url,
x: pos.x,
y: pos.y,
width: 100,
height: 100,
});
});
}}
onDragEnd={() => {
registerNextDomDrop(null);
}}
/>;