Skip to content

Instantly share code, notes, and snippets.

@Kishan033
Last active August 1, 2023 17:39
Show Gist options
  • Save Kishan033/fdfa7e449f8f8b088e4ebefdbec5fbd6 to your computer and use it in GitHub Desktop.
Save Kishan033/fdfa7e449f8f8b088e4ebefdbec5fbd6 to your computer and use it in GitHub Desktop.
import React, { useState, useEffect, useRef, } from "react";
import { Modal, Button, Upload, Row, Col, Slider, Typography, Input, Popover, } from 'antd';
import Icon from '@ant-design/icons';
import { InboxOutlined, CloseCircleOutlined } from '@ant-design/icons';
import { useDispatch } from "react-redux";
import { setPostImage, setPostImageAlternativeText, toggleModals, searchConnections, } from "../../../store/actions";
import { useTranslation } from "react-i18next";
import { image } from "../../../api";
// import RotateSvg from "@assets/images/rotate.svg";
// import ReverseRotateSvg from "@assets/images/reverse-rotate.svg";
// console.log("🚀 ~ file: post-photo-v2.js:9 ~ rotateimage:", rotateimage)
import { ADMIN_API_URL } from "config";
import { IMAGE_UPLOAD } from "api/routes";
import SelectUser from "./select-user";
const { Dragger } = Upload;
const { Title, } = Typography;
const { TextArea } = Input;
const defaultFilters = {
"brightnessSlider": 100,
"contrastSlider": 100,
"grayscaleSlider": 0,
"saturationSlider": 100,
"sepiaSlider": 0,
"hueRotateSlider": 0,
};
const filtersList = [
{
name: "Studio",
id: "studio",
filters: {
...defaultFilters,
brightnessSlider: 100,
grayscaleSlider: 100,
}
},
{
name: "Fever",
id: "fever",
filters: {
...defaultFilters,
contrastSlider: 97,
hueRotateSlider: 330,
saturationSlider: 111,
}
},
{
name: "Old Wood",
id: "old-wood",
filters: {
...defaultFilters,
brightnessSlider: 105,
contrastSlider: 105,
grayscaleSlider: 105,
saturationSlider: 140,
}
},
{
name: "Black&White",
id: "filter-black-and-white",
filters: {
...defaultFilters,
grayscaleSlider: 100,
brightnessSlider: 120,
contrastSlider: 100,
}
},
{
name: "Funky",
id: "filter-funky",
filters: {
...defaultFilters,
hueRotateSlider:
Math.floor(Math.random() * 360) + 1,
contrastSlider: 120,
}
},
{
name: "Vintage",
id: "filter-vintage",
filters: {
...defaultFilters,
brightnessSlider: 120,
saturateSlider: 120,
sepiaSlider: 150,
}
},
];
export const PhotoPostV2 = ({
show
}) => {
const [lang] = useTranslation("language");
const dispatch = useDispatch();
const [filter, setFilter] = useState({
"brightnessSlider": 100,
"contrastSlider": 100,
"grayscaleSlider": 0,
"saturationSlider": 100,
"sepiaSlider": 0,
"hueRotateSlider": 0,
});
const [alternativeText, setAlternativeText] = useState("");
const [zoomScale, setZoomScale] = useState(1);
const [rotationAngle, setRotateAngle] = useState(0);
const [canvasClickPosition, setCanvasClickPosition] = useState({
top: 0,
left: 0,
});
const [uploading, setUploading] = useState(false);
const [taggedUserData, setTaggedUserData] = useState([]);
const [file, setFile] = useState(null);
const filterListRef = useRef(null);
const canvasRef = useRef(null);
const [imgSrc, setImgSrc] = useState(null);
const [flippedHorizontal, setFlippedHorizontal] = useState(false);
const [flippedVertical, setFlippedVertical] = useState(false);
const [selectedAspectRatio, setSelectedAspectRatio] = useState('original'); // Default is 'original'
const [originalWidth, setOriginalWidth] = useState(null);
const [originalHeight, setOriginalHeight] = useState(null);
const applyAspectRatios = () => {
let newWidth = originalWidth;
let newHeight = originalHeight;
// Apply each aspect ratio consecutively
if (selectedAspectRatio === '3:4') {
newWidth = originalHeight * (3 / 4);
} else if (selectedAspectRatio === '16:9') {
newWidth = originalHeight * (16 / 9);
}
// Calculate the scale factor based on the new dimensions
const scaleFactor = newWidth / originalWidth;
// Update the canvas size and redraw the image
const canvas = canvasRef.current;
canvas.width = newWidth;
canvas.height = newHeight;
drawImage(scaleFactor);
};
useEffect(() => {
if (file) {
applyAspectRatios();
}
}, [selectedAspectRatio]);
const flipHorizontal = () => {
setFlippedHorizontal((prev) => !prev); // Toggle the flip state
setFlippedVertical(false); // Reset vertical flip
};
const flipVertical = () => {
setFlippedVertical((prev) => !prev); // Toggle the flip state
setFlippedHorizontal(false); // Reset horizontal flip
};
useEffect(() => {
if (file) {
updateFilters();
}
}, [file]);
const adjustRotateAngle = (angle) => {
if (angle > 360) setRotateAngle(angle % 360)
else if (angle < 0) setRotateAngle(360 + angle)
else setRotateAngle(angle);
}
const onImageUploaded = (url) => {
dispatch(setPostImage(url));
dispatch(setPostImageAlternativeText(alternativeText));
dispatch(toggleModals({ photopostv2: false }));
dispatch(toggleModals({ addpost: true }));
};
const onCancel = () => {
dispatch(toggleModals({ photopostv2: false }));
};
const updateFilter = (value, filterType) => {
setFilter({
...filter,
[filterType]: parseInt(value),
});
}
/*******************
@Purpose : Used for file upload
@Parameter : {}
@Author : INIC
******************/
const onUpload = async () => {
setUploading(true);
// const canvas = document.getElementById('canvas');
const dataURL = exportImage()
if (!dataURL) return;
const formData = new FormData();
const type = "image/png";
const byteString = atob(dataURL.split(",")[1]);
const ab = new ArrayBuffer(byteString.length);
const ia = new Uint8Array(ab);
for (let i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
const bb = new Blob([ab], { type: type });
formData.append("file", bb);
try {
const response = await image(
{ serviceURL: ADMIN_API_URL },
IMAGE_UPLOAD,
false,
formData,
true
);
if (response.status === 1) {
onImageUploaded(response.data.fileUrl);
}
} catch (error) {
console.log(error);
}
setUploading(false);
};
const exportImage = () => {
const canvas = canvasRef.current;
const tempCanvas = document.createElement('canvas');
const ctx = tempCanvas.getContext('2d');
// Adjust the size of the temporary canvas to match the visible content
const visibleRect = getVisibleRect(canvas);
tempCanvas.width = visibleRect.width;
tempCanvas.height = visibleRect.height;
// Copy the visible content to the temporary canvas
ctx.drawImage(canvas, visibleRect.x, visibleRect.y, visibleRect.width, visibleRect.height, 0, 0, visibleRect.width, visibleRect.height);
// Export the image from the temporary canvas
return tempCanvas.toDataURL();
};
const getVisibleRect = (canvas) => {
const ctx = canvas.getContext('2d');
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
let xMin = canvas.width;
let xMax = 0;
let yMin = canvas.height;
let yMax = 0;
for (let y = 0; y < canvas.height; y++) {
for (let x = 0; x < canvas.width; x++) {
const index = (y * canvas.width + x) * 4;
const alpha = data[index + 3];
if (alpha > 0) {
xMin = Math.min(xMin, x);
xMax = Math.max(xMax, x);
yMin = Math.min(yMin, y);
yMax = Math.max(yMax, y);
}
}
}
const width = xMax - xMin;
const height = yMax - yMin;
return { x: xMin, y: yMin, width, height };
};
const drawImage = (scaleFactor = 1) => {
const canvas = canvasRef.current;
const ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
const img = new Image();
img.src = imgSrc;
img.onload = () => {
// const imageAspectRatio = img.width / img.height;
// Calculate the maximum size that fits the canvas while preserving the image's aspect ratio
const maxWidth = canvas.width;
const maxHeight = canvas.height;
// const widthToFit = maxHeight * imageAspectRatio;
// const heightToFit = maxWidth / imageAspectRatio;
// Calculate the scale factor based on the best fit (scale is minimum to fit the image entirely inside the canvas)
const finalScaleFactor = Math.min(maxWidth / img.width, maxHeight / img.height) * zoomScale * scaleFactor;
// Calculate the positioning to center the image after applying rotation and scaling
// const offsetX = (canvas.width - img.width * finalScaleFactor) / 2;
// const offsetY = (canvas.height - img.height * finalScaleFactor) / 2;
// Clear the canvas and draw the image with transformations
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.save();
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.rotate((rotationAngle * Math.PI) / 180);
if (flippedHorizontal) {
ctx.scale(-1, 1); // Flip horizontally
}
if (flippedVertical) {
ctx.scale(1, -1); // Flip vertically
}
ctx.scale(finalScaleFactor, finalScaleFactor);
ctx.filter = getFilterString();
ctx.drawImage(img, -img.width / 2, -img.height / 2, img.width, img.height);
ctx.restore();
};
};
useEffect(() => {
if (imgSrc) {
drawImage();
}
}, [imgSrc, rotationAngle, zoomScale, flippedHorizontal, flippedVertical, filter]);
const onImageLoad = (e) => {
setFile(e.dataTransfer.files[0]);
const reader = new FileReader();
reader.onload = (event) => {
setImgSrc(event.target.result);
const img = new Image();
img.src = event.target.result;
img.onload = () => {
setOriginalHeight(img.height);
setOriginalWidth(img.width);
};
};
reader.readAsDataURL(e.dataTransfer.files[0]);
};
const updateFilters = () => {
filtersList.forEach(filter => {
const image = document.createElement('img');
const canvas = document.getElementById(filter.id);
const context = canvas.getContext('2d');
image.src = URL.createObjectURL(file);
image.width = 80;
image.height = 80;
image.onload = function () {
canvas.width = 100;
canvas.height = 100;
canvas.crossOrigin = "anonymous";
const filterString =
"brightness(" + filter.filters.brightnessSlider + "%" +
") contrast(" + filter.filters.contrastSlider + "%" +
") grayscale(" + filter.filters.grayscaleSlider + "%" +
") saturate(" + filter.filters.saturationSlider + "%" +
") sepia(" + filter.filters.sepiaSlider + "%" +
") hue-rotate(" + filter.filters.hueRotateSlider + "deg" + ")";
context.filter = filterString;
context.globalCompositeOperation = "copy";
context.drawImage(image, 0, 0, 100, 100);
};
});
};
const onReset = () => {
setFlippedHorizontal(false);
setFlippedVertical(false);
setSelectedAspectRatio('original');
setZoomScale(1);
setRotateAngle(0);
setFilter(defaultFilters);
};
const getFilterString = () => {
return ("brightness(" + filter.brightnessSlider + "%" +
") contrast(" + filter.contrastSlider + "%" +
") grayscale(" + filter.grayscaleSlider + "%" +
") saturate(" + filter.saturationSlider + "%" +
") sepia(" + filter.sepiaSlider + "%" +
") hue-rotate(" + filter.hueRotateSlider + "deg" + ")");
}
const onFilterScroll = (scrollSize) => {
filterListRef.current.scrollLeft += scrollSize;
}
const onCanvasClick = (event) => {
const canvas = document.getElementById('canvas');
const bb = canvas.getBoundingClientRect();
const x = Math.floor((event.clientX - bb.left) / bb.width * canvas.width);
const y = Math.floor((event.clientY - bb.top) / bb.height * canvas.height);
setCanvasClickPosition({
left: x,
top: y,
});
console.log({ x, y });
};
const onUserSelect = (selectedUser) => {
if (taggedUserData.indexOf(taggerUser => taggerUser.user.id == selectedUser.id) == -1) {
setTaggedUserData([...taggedUserData, { user: selectedUser, position: canvasClickPosition }]);
} else {
setTaggedUserData(taggedUserData.map((taggedUser) => {
if (taggedUser.user.id != selectedUser.id) return taggedUserData;
return { ...taggedUser, position: canvasClickPosition }
}));
}
};
const showTooltip = () => {
taggedUserData.forEach((taggedUser) => {
const tooltip = document.getElementById("tooltip" + taggedUser.user.id);
tooltip.innerHTML = taggedUser.user.firstName;
tooltip.style.left = taggedUser.position.left + 'px';
tooltip.style.top = taggedUser.position.top + 'px';
tooltip.style.display = 'block';
});
}
function hideTooltip() {
console.log("🚀 ~ file: post-photo-v2.js:480 ~ hideTooltip ~ hideTooltip:")
taggedUserData.forEach((taggedUser) => {
const tooltip = document.getElementById("tooltip" + taggedUser.user.id);
tooltip.style.display = 'none';
});
}
const onCanvasMouseMove = (event) => {
const canvas = document.getElementById('canvas');
const rect = canvas.getBoundingClientRect();
const x = event.clientX - rect.left;
// console.log("🚀 ~ file: post-photo-v2.js:486 ~ onCanvasMouseMove ~ x:", x)
const y = event.clientY - rect.top;
// console.log("🚀 ~ file: post-photo-v2.js:488 ~ onCanvasMouseMove ~ y:", y)
// Check if the mouse position falls within the area you want to show the tooltip
// You can adjust these conditions based on your specific use case
if (x > 1 && x < canvas.width - 3 && y > 1 && y < canvas.height - 3) {
showTooltip();
} else {
hideTooltip();
}
}
const props = {
name: 'file',
multiple: false,
onChange(info) {
const { status } = info.file;
},
onDrop(e) {
onImageLoad(e)
console.log('Dropped files', e.dataTransfer.files[0]);
},
};
return (
<div className="image-editor-wrapper">
<Modal
className="image-editor-modal"
closeIcon={null}
visible={show}
onCancel={onCancel}
cancelText={null}
closable={false}
onOk={onUpload}
okText="Upload"
confirmLoading={uploading}
title={null}
>
<Row className="p-12 d-flex align-items-center justify-content-between image-editor-modal-header">
<Title className="m-0" level={4}>Optimise the image</Title>
<CloseCircleOutlined onClick={onCancel} />
</Row>
<Row>
<Col span={file ? 12 : 24} className="image-preview p-12">
{
!file &&
<Dragger
height={200}
width="100%"
className="w-100"
{...props}
showUploadList={false}
>
<div>
<p>
<InboxOutlined />
</p>
<p className="ant-upload-text">Click or drag file to this area to upload</p>
</div>
</Dragger>
}
<img id="sourceImage" crossorigin="anonymous" />
<img id="editedImage" crossorigin="anonymous" />
<div className="preview-canvas-wrapper">
<div className="canvas-tooltip-wrapper">
<Popover
// style={{ top: `${canvasClickPosition.top}px`, left: `${canvasClickPosition.left}px` }}
placement="right"
content={
<SelectUser
onSelect={onUserSelect}
onSearchUser={searchConnections}
/>
}
trigger="click"
>
<canvas
ref={canvasRef}
onMouseMove={onCanvasMouseMove}
onClick={onCanvasClick}
id="canvas"
width="400"
height="300"
/>
{
taggedUserData.map((taggedUser) => (
<div className="canvas-tooltip" id={"tooltip" + taggedUser.user.id} ></div>
// <span>{taggedUser.user.firstName}</span>
))
}
</Popover>
</div>
</div>
{
file && <TextArea
className="rounded"
rows={4}
placeholder="Add alternative text here...."
maxLength={1000}
onChange={(event) => setAlternativeText(event.target.value)}
/>
}
</Col>
{
file && <Col span={12} className="p-12">
<Row className="mb-3 pb-3 image-editor-crop-wrapper">
<Col span={24}>
<span className="title" level={5}>Crop</span>
</Col>
<Col span={24}>
<Button onClick={() => setSelectedAspectRatio("original")} size="small">Original</Button> &nbsp;
<Button onClick={() => setSelectedAspectRatio(1)} size="small">Square</Button> &nbsp;
<Button onClick={() => setSelectedAspectRatio(4 / 1)} size="small">4:1</Button> &nbsp;
<Button onClick={() => setSelectedAspectRatio("3:4")} size="small">3:4</Button> &nbsp;
<Button onClick={() => setSelectedAspectRatio("16:9")} size="small">16:9</Button>
</Col>
</Row>
<Row className="mb-3 pb-3 d-flex image-editor-position-wrapper">
<Col span={24}>
<span className="title">Position</span>
</Col>
<Col span={8}>
<span className="slider-title">Zoom</span>
<Slider value={zoomScale} step={0.1} onChange={(value) => setZoomScale(value)} min={0} max={2} />
</Col>
<Col span={8}>
<span className="slider-title">Straighten</span>
<Slider value={rotationAngle} step={1} onChange={(value) => adjustRotateAngle(value)} min={0} max={360} />
</Col>
<Col span={8} className="d-flex align-items-center">
<Button type="link" onClick={() => adjustRotateAngle(rotationAngle + 90)} size="small">
<Icon className="optimization-icon" component={RotateSvg} />
</Button>
<Button type="link" onClick={() => adjustRotateAngle(rotationAngle - 90)} size="small">
<Icon className="optimization-icon" component={ReverseRotateSvg} />
</Button>
<Button type="link" onClick={flipVertical} size="small">
<Icon className="optimization-icon" component={VerticalFlip} />
</Button>
<Button type="link" onClick={flipHorizontal} size="small">
<Icon className="optimization-icon" component={HorizontalFlip} />
</Button>
</Col>
</Row>
<Row className="mb-3 pb-3 image-editor-optimization-wrapper">
<Col span={24}>
<span className="title">Optimization</span>
</Col>
<Col span={8}>
<span className="slider-title">Brightness</span>
<Slider value={filter.brightnessSlider} onChange={(value) => updateFilter(value, "brightnessSlider")} min={0} max={300} />
</Col>
<Col span={8}>
<span className="slider-title">Contrast</span>
<Slider value={filter.contrastSlider} onChange={(value) => updateFilter(value, "contrastSlider")} min={0} max={200} />
</Col>
<Col span={8}>
<span className="slider-title">Saturation</span>
<Slider value={filter.saturationSlider} onChange={(value) => updateFilter(value, "saturationSlider")} min={0} max={300} />
</Col>
</Row>
<div className="mb-3 image-editor-filters-wrapper">
<Col span={24}>
<span className="title">Filters</span>
</Col>
<div className="filter-image-box-list-wrapper">
<Icon onClick={() => { onFilterScroll(-120) }} className="filter-sroll-icon-left" component={ScrollLeft} />
<ul ref={filterListRef} className="filter-image-box-list">
{filtersList.map((filter, index) => {
return (
<div className={`filter-image-box${index != filtersList.length - 1 ? " mr-3" : ""}`}>
<canvas
onClick={() => {
setFilter(filter.filters);
}}
className="filter-canvas"
id={filter.id}
height="0"
>
</canvas>
<span className="font-12 text-gray-darker">
{filter.name}
</span>
</div>
);
})}
</ul>
<Icon onClick={() => { onFilterScroll(120) }} className="filter-sroll-icon-right" component={ScrollRight} />
</div>
</div>
<Row className="mb-3 pb-3">
<Button onClick={onReset} size="small">Reset</Button>
</Row>
</Col>
}
</Row>
<div>
</div>
</Modal>
</div>
);
};
const RotateSvg = () => (
<svg width="15" height="14" viewBox="0 0 15 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.66797 12.8336C8.39714 12.8336 9.08012 12.6975 9.71693 12.4252C10.3537 12.153 10.9103 11.7787 11.3867 11.3023C11.8631 10.8259 12.2374 10.2693 12.5096 9.63252C12.7819 8.99571 12.918 8.31273 12.918 7.58356H12.043C12.043 8.79884 11.6176 9.83183 10.7669 10.6825C9.91623 11.5332 8.88325 11.9586 7.66797 11.9586C6.45269 11.9586 5.4197 11.5332 4.56901 10.6825C3.71832 9.83183 3.29297 8.79884 3.29297 7.58356C3.29297 6.36828 3.70616 5.3353 4.53255 4.4846C5.35894 3.63391 6.37977 3.20856 7.59505 3.20856H7.93047L6.86589 4.27314L7.4638 4.88564L9.60755 2.74189L7.4638 0.598145L6.86589 1.19606L8.00339 2.33356H7.66797C6.9388 2.33356 6.25582 2.46967 5.61901 2.74189C4.9822 3.01412 4.42561 3.38842 3.94922 3.86481C3.47283 4.3412 3.09852 4.8978 2.8263 5.5346C2.55408 6.17141 2.41797 6.85439 2.41797 7.58356C2.41797 8.31273 2.55408 8.99571 2.8263 9.63252C3.09852 10.2693 3.47283 10.8259 3.94922 11.3023C4.42561 11.7787 4.9822 12.153 5.61901 12.4252C6.25582 12.6975 6.9388 12.8336 7.66797 12.8336Z" fill="#0F6BBF" />
</svg>
);
const ReverseRotateSvg = () => (
<svg width="15" height="14" viewBox="0 0 15 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.66797 12.8336C6.9388 12.8336 6.25582 12.6975 5.61901 12.4252C4.9822 12.153 4.42561 11.7787 3.94922 11.3023C3.47283 10.8259 3.09852 10.2693 2.8263 9.63252C2.55408 8.99571 2.41797 8.31273 2.41797 7.58356H3.29297C3.29297 8.79884 3.71832 9.83183 4.56901 10.6825C5.4197 11.5332 6.45269 11.9586 7.66797 11.9586C8.88325 11.9586 9.91623 11.5332 10.7669 10.6825C11.6176 9.83183 12.043 8.79884 12.043 7.58356C12.043 6.36828 11.6298 5.3353 10.8034 4.4846C9.977 3.63391 8.95616 3.20856 7.74089 3.20856H7.40547L8.47005 4.27314L7.87214 4.88564L5.72839 2.74189L7.87214 0.598145L8.47005 1.19606L7.33255 2.33356H7.66797C8.39714 2.33356 9.08012 2.46967 9.71693 2.74189C10.3537 3.01412 10.9103 3.38842 11.3867 3.86481C11.8631 4.3412 12.2374 4.8978 12.5096 5.5346C12.7819 6.17141 12.918 6.85439 12.918 7.58356C12.918 8.31273 12.7819 8.99571 12.5096 9.63252C12.2374 10.2693 11.8631 10.8259 11.3867 11.3023C10.9103 11.7787 10.3537 12.153 9.71693 12.4252C9.08012 12.6975 8.39714 12.8336 7.66797 12.8336Z" fill="#303439" />
</svg>
);
const VerticalFlip = () => (
<svg width="15" height="14" viewBox="0 0 15 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_12776_228063)">
<path d="M9.41797 12.2502H10.5846V11.0835H9.41797V12.2502ZM11.7513 5.25016H12.918V4.0835H11.7513V5.25016ZM2.41797 2.91683V11.0835C2.41797 11.7281 2.94005 12.2502 3.58464 12.2502H5.91797V11.0835H3.58464V2.91683H5.91797V1.75016H3.58464C2.94005 1.75016 2.41797 2.27225 2.41797 2.91683ZM11.7513 1.75016V2.91683H12.918C12.918 2.27225 12.3959 1.75016 11.7513 1.75016ZM7.08464 13.4168H8.2513V0.583496H7.08464V13.4168ZM11.7513 9.91683H12.918V8.75016H11.7513V9.91683ZM9.41797 2.91683H10.5846V1.75016H9.41797V2.91683ZM11.7513 7.5835H12.918V6.41683H11.7513V7.5835ZM11.7513 12.2502C12.3959 12.2502 12.918 11.7281 12.918 11.0835H11.7513V12.2502Z" fill="#303439" />
</g>
<defs>
<clipPath id="clip0_12776_228063">
<rect width="14" height="14" fill="white" transform="translate(0.667969)" />
</clipPath>
</defs>
</svg>
);
const HorizontalFlip = () => (
<svg width="15" height="14" viewBox="0 0 15 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_12776_228067)">
<path d="M5.91797 12.2502H4.7513V11.0835H5.91797V12.2502ZM3.58464 5.25016H2.41797V4.0835H3.58464V5.25016ZM12.918 2.91683V11.0835C12.918 11.7281 12.3959 12.2502 11.7513 12.2502H9.41797V11.0835H11.7513V2.91683H9.41797V1.75016H11.7513C12.3959 1.75016 12.918 2.27225 12.918 2.91683ZM3.58464 1.75016V2.91683H2.41797C2.41797 2.27225 2.94005 1.75016 3.58464 1.75016ZM8.2513 13.4168H7.08464V0.583496H8.2513V13.4168ZM3.58464 9.91683H2.41797V8.75016H3.58464V9.91683ZM5.91797 2.91683H4.7513V1.75016H5.91797V2.91683ZM3.58464 7.5835H2.41797V6.41683H3.58464V7.5835ZM3.58464 12.2502C2.94005 12.2502 2.41797 11.7281 2.41797 11.0835H3.58464V12.2502Z" fill="#303439" />
</g>
<defs>
<clipPath id="clip0_12776_228067">
<rect width="14" height="14" fill="white" transform="matrix(-1 0 0 1 14.668 0)" />
</clipPath>
</defs>
</svg>
);
const ScrollLeft = () => (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.5" y="0.5" width="23" height="23" rx="11.5" fill="white" />
<g clip-path="url(#clip0_12777_228112)">
<path d="M14.843 15.825L11.0263 12L14.843 8.175L13.668 7L8.66797 12L13.668 17L14.843 15.825Z" fill="#051F4E" />
</g>
<rect x="0.5" y="0.5" width="23" height="23" rx="11.5" stroke="#D1D5DB" />
<defs>
<clipPath id="clip0_12777_228112">
<rect width="20" height="20" fill="white" transform="translate(2 2)" />
</clipPath>
</defs>
</svg>
);
const ScrollRight = () => (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.5" y="0.5" width="23" height="23" rx="11.5" fill="white" />
<g clip-path="url(#clip0_12777_228113)">
<path d="M9.16016 15.825L12.9768 12L9.16016 8.175L10.3352 7L15.3352 12L10.3352 17L9.16016 15.825Z" fill="#051F4E" />
</g>
<rect x="0.5" y="0.5" width="23" height="23" rx="11.5" stroke="#D1D5DB" />
<defs>
<clipPath id="clip0_12777_228113">
<rect width="20" height="20" fill="white" transform="translate(2 2)" />
</clipPath>
</defs>
</svg>
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment