feat: init

This commit is contained in:
montaghanmy
2023-03-23 11:03:16 +01:00
commit 10fe6f78d1
11518 changed files with 509786 additions and 0 deletions
+97
View File
@@ -0,0 +1,97 @@
// eslint-disable-next-line @typescript-eslint/no-use-before-define
import React, { useEffect, useState } from 'react';
import { Router } from 'react-router';
import { Switch, Route } from 'react-router-dom';
import { RecoilRoot } from 'recoil';
import MobileRedirect from './views/mobile-redirect';
import Integration from 'app/views/integration';
import RouterServices, { RouteType } from './features/router/services/router-service';
import ErrorBoundary from 'app/views/error/error-boundary';
import InitService from './features/global/services/init-service';
import useTimeout from 'app/features/global/hooks/use-timeout';
import ApplicationLoader from './components/loader/application-loader';
import DebugState from './components/debug/debug-state';
import 'app/styles/index.less';
const delayMessage = 5000;
export default () => {
const [displayDelayLoader, setDisplayDelayLoader] = useState(false);
const [firstRenderDate] = useState(Date.now());
useEffect(() => {
InitService.init();
}, []);
const server_infos_loaded = InitService.useWatcher(() => InitService.server_infos_loaded);
useTimeout(() => {
if (!server_infos_loaded && Date.now() >= firstRenderDate + delayMessage) {
setDisplayDelayLoader(true);
}
}, delayMessage);
useEffect(() => {
if (server_infos_loaded) {
setDisplayDelayLoader(false);
try {
window.document.getElementById('app_loader')?.remove();
} catch (err) {
//Null
}
}
}, [server_infos_loaded]);
if (displayDelayLoader && !server_infos_loaded) {
return <ApplicationLoader></ApplicationLoader>;
}
if (!server_infos_loaded) {
return <></>;
}
return (
<RecoilRoot>
<DebugState />
<MobileRedirect>
<Integration>
<Router history={RouterServices.history}>
<Switch>
{RouterServices.routes.map((route: RouteType, index: number) => (
<Route
key={`${route.key}_${index}`}
exact={route.exact ? route.exact : false}
path={route.path}
component={() =>
route.options?.withErrorBoundary ? (
<ErrorBoundary key={route.key}>
<route.component />
</ErrorBoundary>
) : (
<route.component key={route.key} />
)
}
/>
))}
{
<Route
path="/"
component={() => {
RouterServices.replace(
`${
RouterServices.pathnames.LOGIN
}?auto&${RouterServices.history.location.search.substr(1)}`,
);
return <div />;
}}
/>
}
</Switch>
</Router>
</Integration>
</MobileRedirect>
</RecoilRoot>
);
};
@@ -0,0 +1,44 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import _ from 'lodash';
export default function Avatar(
props: any & {
avatar: string;
size: 28 | 14 | 48;
},
) {
const size = props.size || 14;
const className =
' inline-block h-' +
size +
' w-' +
size +
' rounded-full overflow-hidden bg-zinc-200 ' +
(props.className || '');
if (props.avatar || props.src) {
return (
<div
className={className}
{..._.omit(props, 'avatar', 'className', 'src')}
style={{
backgroundPosition: 'center',
backgroundSize: 'cover',
backgroundImage: 'url(' + (props.avatar || props.src) + ')',
}}
/>
);
}
return (
<span className={className} {..._.omit(props, 'avatar', 'className', 'src')}>
<svg
className="h-full w-full text-zinc-400 bg-zinc-200"
fill="currentColor"
viewBox="0 0 24 24"
>
<path d="M24 20.993V24H0v-2.996A14.977 14.977 0 0112.004 15c4.904 0 9.26 2.354 11.996 5.993zM16.002 8.999a4 4 0 11-8 0 4 4 0 018 0z" />
</svg>
</span>
);
}
@@ -0,0 +1,51 @@
export const Alert = (props: {
theme: 'success' | 'danger' | 'warning' | 'gray' | 'primary';
title: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
icon: any;
className?: string;
bullets?: string[];
children?: React.ReactNode;
}) => {
let color = 'blue';
let textColor = 'white';
if (props.theme === 'success') color = 'green-500';
if (props.theme === 'danger') color = 'red-500';
if (props.theme === 'warning') color = 'orange-500';
if (props.theme === 'gray') {
color = 'zinc-50';
textColor = 'zinc-900';
}
if (props.theme === 'primary') {
color = 'blue-100';
textColor = 'zinc-900';
}
return (
<div
className={
`my-4 text-${textColor} bg-${color} p-4 flex items-center rounded-md ` +
(props.className || '')
}
>
<div className="flex items-center h-full" style={{ minHeight: 32 }}>
<div className="flex-shrink-0">
<props.icon className={`h-6 w-6`} aria-hidden="true" />
</div>
<div className="ml-3">
<h3 className={`text-sm font-medium`}>{props.title}</h3>
{(props.bullets || []).length > 0 && (
<div className={`mt-2 text-sm text-${color}-800`}>
<ul role="list" className="list-disc pl-5 space-y-1">
{(props.bullets || []).map(bullet => (
<li key={bullet}>{bullet}</li>
))}
</ul>
</div>
)}
{props.children}
</div>
</div>
</div>
);
};
@@ -0,0 +1,56 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { ComponentStory } from '@storybook/react';
import Avatar from '.';
import { UsersIcon } from '../icons-agnostic';
export default {
title: '@atoms/avatar',
};
type sizeType = 'lg' | 'md' | 'sm';
type shapeType = 'circle' | 'square';
const Template: ComponentStory<any> = (args: { title: string }) => {
const types = ['circle', 'square'] as shapeType[];
const sizes = ['lg', 'md', 'sm'] as sizeType[];
const values = [
{ title: args.title },
{
title: args.title,
avatar:
'https://images.freeimages.com/images/small-previews/d67/experimenting-with-nature-1547377.jpg',
},
{
icon: <UsersIcon />,
className: '',
},
];
return (
<>
<div className="flex flex-col gap-2">
{sizes.map(size => (
<div key={size} className="flex gap-2">
{types.map(tp =>
values.map(val => (
<Avatar
key={tp}
size={size}
title={val.title}
type={tp}
avatar={val.avatar}
icon={val.icon}
className={val.className}
/>
)),
)}
</div>
))}
</div>
</>
);
};
export const Default = Template.bind({});
Default.args = {
title: 'User Name',
};
@@ -0,0 +1,101 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
/* eslint-disable @typescript-eslint/ban-ts-comment */
import React from 'react';
import _ from 'lodash';
import CryptoJS from 'crypto-js';
// @ts-ignore
interface AvatarProps extends React.InputHTMLAttributes<HTMLInputElement> {
type?: 'circle' | 'square';
size?: 'xl' | 'lg' | 'md' | 'sm' | 'xs';
avatar?: string;
icon?: JSX.Element | false;
title?: string;
nogradient?: boolean;
}
const sizes = { xl: 24, lg: 14, md: 11, sm: 9, xs: 6 };
const fontSizes = { xl: '2xl', lg: '2xl', md: 'lg', sm: 'md', xs: 'sm' };
export const getGradient = (name: string) => {
const seed = parseInt(CryptoJS.MD5(name).toString().slice(0, 8), 16);
const canvas: HTMLCanvasElement = document.createElement('canvas');
canvas.width = 254;
canvas.height = 254;
const ctx = canvas.getContext('2d');
const gradient = ctx!.createLinearGradient(254, 254, 0, 0);
gradient.addColorStop(0, 'hsl(' + seed + ', 90%, 70%)');
gradient.addColorStop(1, 'hsl(' + Math.abs(seed - 60) + ', 90%, 70%)');
ctx!.fillStyle = gradient;
ctx!.fillRect(0, 0, 254, 254);
const b64 = canvas.toDataURL('image/jpeg');
return b64;
};
export default function Avatar(props: AvatarProps) {
const avatarType = props.type || 'circle';
const avatarSize = sizes[props.size || 'md'];
const fontSize = fontSizes[props.size || 'md'];
const addedClassName = props.className || '';
const avatarTitle = props.title || '';
const restProps = _.omit(props, 'size', 'type', 'avatar', 'title', 'className', 'icon');
let className = `w-${avatarSize} h-${avatarSize} ${
avatarType === 'circle' ? 'rounded-full' : 'rounded-sm'
} `;
className +=
' border border-gray flex items-center justify-center bg-center bg-cover ' +
(props.nogradient ? ' bg-zinc-100 dark:bg-zinc-800 text-zinc-900 dark:text-white ' : '');
const spl_title = avatarTitle.split(' ');
let letters = avatarTitle.slice(0, 1);
if (spl_title.length > 1) {
letters = spl_title[0].slice(0, 1) + spl_title[1].slice(0, 1);
}
const lettersClass =
`font-medium bg-gray text-${fontSize}` +
(props.nogradient ? ' text-zinc-900 dark:text-white ' : ' text-white');
const style = props.nogradient
? {}
: { backgroundImage: `url('${getGradient(props.title || '')}')` };
if (props.icon) {
className += ' ';
return (
<div
{...restProps}
style={style}
className={
className +
' text-white overflow-hidden flex items-center justify-center ' +
addedClassName
}
>
{props.icon}
</div>
);
}
if (props.avatar) {
return (
<div {...restProps}>
<img
alt={props.title}
src={props.avatar}
className={className + ' object-cover ' + addedClassName}
/>
</div>
);
}
return (
<div className={className + ' ' + addedClassName} {...restProps} style={style}>
<div className={lettersClass}>{letters.toUpperCase()}</div>
</div>
);
}
@@ -0,0 +1,108 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { ComponentStory } from '@storybook/react';
import { Badge } from '.';
import { PlusIcon, SearchIcon } from '@heroicons/react/solid';
import { TrashIcon } from '@heroicons/react/outline';
export default {
title: '@atoms/button',
};
const Template: ComponentStory<any> = (props: {
text: string;
disabled: boolean;
loading: boolean;
}) => {
return (
<>
<Badge className="my-4 mx-2" loading={props.loading}>
{props.text}
</Badge>
<Badge className="my-4 mx-2" theme="secondary" loading={props.loading}>
{props.text}
</Badge>
<Badge className="my-4 mx-2" theme="danger" loading={props.loading}>
{props.text}
</Badge>
<Badge className="my-4 mx-2" theme="default" loading={props.loading}>
{props.text}
</Badge>
<Badge className="my-4 mx-2" theme="outline" loading={props.loading}>
{props.text}
</Badge>
<br />
<Badge className="my-4 mx-2" loading={props.loading} icon={SearchIcon}>
Search
</Badge>
<Badge className="my-4 mx-2" theme="outline" loading={props.loading} icon={PlusIcon}>
Add
</Badge>
<Badge className="my-4 mx-2" theme="danger" loading={props.loading} icon={TrashIcon} />
<br />
<Badge size="lg" className="my-4 mx-2" theme="outline" loading={props.loading}>
{props.text}
</Badge>
<Badge size="md" className="my-4 mx-2" theme="outline" loading={props.loading}>
{props.text}
</Badge>
<Badge size="sm" className="my-4 mx-2" theme="outline" loading={props.loading}>
{props.text}
</Badge>
<br />
<Badge
size="lg"
className="my-4 mx-2 rounded-full"
theme="primary"
loading={props.loading}
icon={TrashIcon}
/>
<Badge
className="my-4 mx-2 rounded-full"
theme="primary"
loading={props.loading}
icon={TrashIcon}
/>
<Badge
size="sm"
className="my-4 mx-2 rounded-full"
theme="primary"
loading={props.loading}
icon={TrashIcon}
/>
<br />
<Badge
size="md"
className="my-4 mx-2 w-full justify-center"
theme="outline"
loading={props.loading}
icon={PlusIcon}
>
Add
</Badge>
</>
);
};
export const Default = Template.bind({});
Default.args = {
text: 'Text',
loading: false,
};
@@ -0,0 +1,76 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import React from 'react';
import _ from 'lodash';
interface BadgeProps extends Omit<React.AllHTMLAttributes<HTMLDivElement>, 'size'> {
theme?: 'primary' | 'secondary' | 'danger' | 'default' | 'outline';
size?: 'md' | 'lg' | 'sm';
icon?: (props: any) => JSX.Element;
loading?: boolean;
children?: React.ReactNode;
}
export const Badge = (props: BadgeProps) => {
let className = 'text-white bg-blue-500 border-transparent ';
if (props.theme === 'secondary') className = 'text-blue-500 bg-blue-100 border-transparent ';
if (props.theme === 'danger') className = 'text-white bg-rose-500 border-transparent ';
if (props.theme === 'default')
className = 'text-black dark:text-white bg-white dark:bg-zinc-800 border-gray-300';
if (props.theme === 'outline')
className = 'text-blue-500 bg-white dark:bg-zinc-800 border-blue-500';
if (props.size === 'lg') className = className + ' text-lg h-11';
else if (props.size === 'sm') className = className + ' text-sm h-7 px-3';
else className = className + ' text-base h-9';
if (!props.children) {
if (props.size === 'lg') className = className + ' w-11 !p-0 justify-center';
else if (props.size === 'sm') className = className + ' w-7 !p-0 justify-center';
else className = className + ' w-9 !p-0 justify-center';
}
return (
<div
className={
' inline-flex items-center px-4 py-2 border font-medium rounded-md focus:outline-none ' +
className +
' ' +
props.className
}
{..._.omit(props, 'loading', 'children', 'className')}
>
{props.loading && (
<>
<svg
className={'animate-spin w-4 h-4 ' + (props.children ? 'mr-2 -ml-1' : '-mx-1')}
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle
className="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="4"
></circle>
<path
className="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
></path>
</svg>{' '}
</>
)}
{props.icon && !(props.loading && !props.children) && (
<props.icon className={'w-4 h-4 ' + (props.children ? 'mr-1 -ml-1' : '-mx-1')} />
)}
{props.children}
</div>
);
};
@@ -0,0 +1,181 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import React from 'react';
import { ComponentStory } from '@storybook/react';
import { Button } from './button';
import { PlusIcon, SearchIcon } from '@heroicons/react/solid';
import { TrashIcon } from '@heroicons/react/outline';
import { UserAddIcon } from '@atoms/icons-agnostic/index';
export default {
title: '@atoms/button',
};
const Template: ComponentStory<any> = (props: {
text: string;
disabled: boolean;
loading: boolean;
}) => {
return (
<>
<Button className="my-4 mx-2" disabled={props.disabled} loading={props.loading}>
{props.text}
</Button>
<Button
className="my-4 mx-2"
theme="secondary"
disabled={props.disabled}
loading={props.loading}
>
{props.text}
</Button>
<Button
className="my-4 mx-2"
theme="danger"
disabled={props.disabled}
loading={props.loading}
>
{props.text}
</Button>
<Button
className="my-4 mx-2"
theme="default"
disabled={props.disabled}
loading={props.loading}
>
{props.text}
</Button>
<Button
className="my-4 mx-2"
theme="outline"
disabled={props.disabled}
loading={props.loading}
>
{props.text}
</Button>
<br />
<Button
className="my-4 mx-2"
disabled={props.disabled}
loading={props.loading}
icon={SearchIcon}
>
Search
</Button>
<Button
className="my-4 mx-2"
theme="outline"
disabled={props.disabled}
loading={props.loading}
icon={PlusIcon}
>
Add
</Button>
<Button
className="my-4 mx-2"
theme="danger"
disabled={props.disabled}
loading={props.loading}
icon={TrashIcon}
/>
<br />
<Button
size="lg"
className="my-4 mx-2"
theme="outline"
disabled={props.disabled}
loading={props.loading}
>
{props.text}
</Button>
<Button
size="md"
className="my-4 mx-2"
theme="outline"
disabled={props.disabled}
loading={props.loading}
>
{props.text}
</Button>
<Button
size="sm"
className="my-4 mx-2"
theme="outline"
disabled={props.disabled}
loading={props.loading}
>
{props.text}
</Button>
<br />
<Button
size="lg"
className="my-4 mx-2 rounded-full"
theme="primary"
disabled={props.disabled}
loading={props.loading}
icon={TrashIcon}
/>
<Button
className="my-4 mx-2 rounded-full"
theme="primary"
disabled={props.disabled}
loading={props.loading}
icon={TrashIcon}
/>
<Button
size="sm"
className="my-4 mx-2 rounded-full"
theme="primary"
disabled={props.disabled}
loading={props.loading}
icon={TrashIcon}
/>
<br />
<Button
size="md"
className="my-4 mx-2 w-full justify-center"
theme="outline"
disabled={props.disabled}
loading={props.loading}
icon={PlusIcon}
>
Add
</Button>
<Button
size="md"
className="my-4 mx-2 w-full justify-center"
theme="outline"
disabled={props.disabled}
loading={props.loading}
>
<UserAddIcon className="w-6 h-6" fill="currentColor" />
Add user
</Button>
</>
);
};
export const Default = Template.bind({});
Default.args = {
text: 'Text',
loading: false,
disabled: false,
};
@@ -0,0 +1,98 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import React from 'react';
import _ from 'lodash';
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
theme?: 'primary' | 'secondary' | 'danger' | 'default' | 'outline' | 'dark';
size?: 'md' | 'lg' | 'sm';
icon?: (props: any) => JSX.Element;
iconSize?: 'md' | 'lg';
loading?: boolean;
disabled?: boolean;
children?: React.ReactNode;
}
export const Button = (props: ButtonProps) => {
const disabled = props.disabled || props.loading;
let className = 'text-white bg-blue-500 hover:bg-blue-700 active:bg-blue-800 border-transparent ';
if (props.theme === 'secondary')
className =
'text-blue-500 bg-blue-100 hover:bg-blue-200 active:bg-blue-300 border-transparent ';
if (props.theme === 'danger')
className = 'text-white bg-rose-500 hover:bg-rose-600 active:bg-rose-700 border-transparent ';
if (props.theme === 'default')
className =
'text-black dark:text-white bg-white dark:bg-zinc-800 dark:hover:bg-zinc-700 dark:active:bg-zinc-900 hover:bg-gray-50 active:bg-gray-200 border-gray-300';
if (props.theme === 'outline')
className =
'text-blue-500 bg-white dark:bg-zinc-800 dark:hover:bg-zinc-700 dark:active:bg-zinc-900 hover:bg-gray-50 active:bg-gray-200 border-blue-500';
if (props.theme === 'dark')
className =
'text-zinc-300 border-0 bg-zinc-900 hover:bg-zinc-800 hover:text-white active:bg-zinc-900';
if (disabled) className += ' opacity-50 pointer-events-none';
if (props.size === 'lg') className = className + ' text-lg h-11';
else if (props.size === 'sm') className = className + ' text-sm h-7 px-3';
else className = className + ' text-base h-9';
if (!props.children) {
if (props.size === 'lg') className = className + ' w-11 !p-0 justify-center';
else if (props.size === 'sm') className = className + ' w-7 !p-0 justify-center';
else className = className + ' w-9 !p-0 justify-center';
}
return (
<button
type="button"
className={
' inline-flex items-center px-4 py-2 border font-medium rounded-md focus:outline-none ' +
className +
' ' +
props.className
}
disabled={disabled}
{..._.omit(props, 'loading', 'children', 'className')}
>
{props.loading && (
<>
<svg
className={'animate-spin w-4 h-4 ' + (props.children ? 'mr-2 -ml-1' : '-mx-1')}
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle
className="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="4"
></circle>
<path
className="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
></path>
</svg>{' '}
</>
)}
{props.icon && !(props.loading && !props.children) && (
<props.icon
className={
(props.iconSize === 'lg' ? 'w-6 h-6 ' : 'w-4 h-4 ') +
(props.children ? 'mr-1 -ml-1' : '-mx-1')
}
/>
)}
{props.children}
</button>
);
};
@@ -0,0 +1,57 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import React from 'react';
import { ComponentStory } from '@storybook/react';
import { ButtonConfirm } from './confirm';
import { RecoilRoot } from 'recoil';
export default {
title: '@atoms/button-confirm',
};
const Template: ComponentStory<any> = (props: {
text: string;
disabled: boolean;
loading: boolean;
}) => {
return (
<RecoilRoot>
<ButtonConfirm className="m-4" disabled={props.disabled} loading={props.loading}>
{props.text}
</ButtonConfirm>
<br />
<ButtonConfirm
className="m-4"
theme="secondary"
disabled={props.disabled}
loading={props.loading}
>
{props.text}
</ButtonConfirm>
<br />
<ButtonConfirm
className="m-4"
theme="danger"
disabled={props.disabled}
loading={props.loading}
>
{props.text}
</ButtonConfirm>
<br />
<ButtonConfirm
className="m-4"
theme="default"
disabled={props.disabled}
loading={props.loading}
>
{props.text}
</ButtonConfirm>
</RecoilRoot>
);
};
export const Default = Template.bind({});
Default.args = {
text: 'Text',
loading: false,
disabled: false,
};
@@ -0,0 +1,82 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import React from 'react';
import { ExclamationCircleIcon } from '@heroicons/react/outline';
import _ from 'lodash';
import { useState } from 'react';
import { Button } from './button';
import { Modal, ModalContent } from '../modal';
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
theme?: 'primary' | 'secondary' | 'danger' | 'default' | 'outline';
size?: 'md' | 'lg' | 'sm';
icon?: (props: any) => JSX.Element;
loading?: boolean;
disabled?: boolean;
confirmTitle?: string;
confirmMessage?: string;
confirmIcon?: React.ReactNode;
confirmButtonTheme?: 'primary' | 'secondary' | 'danger' | 'default';
confirmButtonText?: string;
cancelButtonText?: string;
children?: React.ReactNode;
}
export const ButtonConfirm = (props: ButtonProps) => {
const [inConfirm, setInConfirm] = useState(false);
return (
<>
<Button
{..._.omit(
props,
'onClick',
'confirmButtonTheme',
'confirmButtonText',
'cancelButtonText',
'confirmTitle',
'confirmMessage',
'confirmIcon',
)}
onClick={() => {
setInConfirm(true);
}}
/>
<Modal
open={inConfirm}
onClose={() => {
setInConfirm(false);
}}
>
<ModalContent
title={props.confirmTitle || 'Confirm action ?'}
text={props.confirmMessage || 'Confirm action by clicking Confirm.'}
icon={props.confirmIcon || ExclamationCircleIcon}
buttons={
<>
<Button
theme={props.confirmButtonTheme || 'primary'}
onClick={e => {
setInConfirm(false);
setTimeout(() => {
props.onClick && props.onClick(e);
}, 500);
}}
className="mr-4 my-2"
>
{props.confirmButtonText || 'Confirm'}
</Button>
<Button
onClick={() => {
setInConfirm(false);
}}
theme="default"
className={'mr-4 my-2 shadow-none'}
>
{props.cancelButtonText || 'Cancel'}
</Button>
</>
}
/>
</Modal>
</>
);
};
@@ -0,0 +1,52 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import React from 'react';
import { ComponentStory } from '@storybook/react';
export default {
title: '@atoms/colors',
};
const Template: ComponentStory<any> = () => {
return (
<>
<div className="grid grid-cols-1 gap-8">
{['zink', 'red', 'orange', 'green', 'blue'].map(color => (
<div key={color}>
<div className="flex flex-col space-y-3 sm:flex-row text-xs sm:space-y-0 sm:space-x-4">
<div className="w-16 shrink-0">
<div className="h-10 flex flex-col justify-center">
<div className="text-sm font-semibold text-zinc-900 dark:text-zinc-200">
{color}
</div>
</div>
</div>
<div className="min-w-0 flex-1 grid grid-cols-5 2xl:grid-cols-10 gap-x-4 gap-y-3 2xl:gap-x-2">
{[50, 100, 200, 300, 400, 500, 600, 700, 800, 900].map(shade => (
<div className="space-y-1.5" key={shade}>
<div
className={
'h-10 w-full rounded dark:ring-1 dark:ring-inset dark:ring-white/10 ' +
`bg-${color}-${shade}`
}
></div>
<div className="px-0.5 md:flex md:justify-between md:space-x-2 2xl:space-x-0 2xl:block">
<div className="w-6 font-medium text-zinc-900 2xl:w-full dark:text-white">
{shade}
</div>
<div className="text-zinc-500 font-mono lowercase dark:text-zinc-400">
#...
</div>
</div>
</div>
))}
</div>
</div>
</div>
))}
</div>
</>
);
};
export const Default = Template.bind({});
Default.args = {};
@@ -0,0 +1,3 @@
<svg width="14" height="10" viewBox="0 0 14 10" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M13.6118 0.366845C14.0023 0.757369 14.0023 1.39053 13.6118 1.78106L6.11177 9.28106C5.72125 9.67158 5.08808 9.67158 4.69756 9.28106L1.19756 5.78106C0.807032 5.39053 0.807032 4.75737 1.19756 4.36685C1.58808 3.97632 2.22125 3.97632 2.61177 4.36685L5.40466 7.15974L12.1976 0.366845C12.5881 -0.0236794 13.2212 -0.0236794 13.6118 0.366845Z" />
</svg>

After

Width:  |  Height:  |  Size: 510 B

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M12 24C18.6274 24 24 18.6274 24 12C24 5.37258 18.6274 0 12 0C5.37258 0 0 5.37258 0 12C0 18.6274 5.37258 24 12 24ZM18.2071 9.20711C18.5976 8.81658 18.5976 8.18342 18.2071 7.79289C17.8166 7.40237 17.1834 7.40237 16.7929 7.79289L10 14.5858L7.20711 11.7929C6.81658 11.4024 6.18342 11.4024 5.79289 11.7929C5.40237 12.1834 5.40237 12.8166 5.79289 13.2071L9.29289 16.7071C9.68342 17.0976 10.3166 17.0976 10.7071 16.7071L18.2071 9.20711Z"/>
</svg>

After

Width:  |  Height:  |  Size: 597 B

@@ -0,0 +1,3 @@
<svg width="28" height="28" viewBox="0 0 28 28" xmlns="http://www.w3.org/2000/svg">
<path style="fill:currentColor" d="M17 2C18.3063 2 19.4175 2.83485 19.8293 4.00009L10.1278 4C8.34473 4 7.69816 4.18565 7.04631 4.53427C6.39446 4.88288 5.88288 5.39446 5.53427 6.04631C5.18565 6.69816 5 7.34473 5 9.12777L5.00009 19.8293C3.83485 19.4175 3 18.3063 3 17V8C3 4.68629 5.68629 2 9 2H17ZM21 6C22.6569 6 24 7.34315 24 9V22C24 23.6569 22.6569 25 21 25H10C8.34315 25 7 23.6569 7 22V9C7 7.34315 8.34315 6 10 6H21ZM21 8H10C9.44772 8 9 8.44772 9 9V22C9 22.5523 9.44772 23 10 23H21C21.5523 23 22 22.5523 22 22V9C22 8.44772 21.5523 8 21 8Z"/>
</svg>

After

Width:  |  Height:  |  Size: 638 B

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path style="fill:currentColor" d="M12 0.5C14.3764 0.5 15.9252 1.73358 16.4217 3.99953L22.5 4C23.0523 4 23.5 4.44772 23.5 5C23.5 5.55228 23.0523 6 22.5 6H21.395L19.7827 21.3141C19.622 22.8409 18.3345 24 16.7992 24H7.20079C5.66551 24 4.37799 22.8409 4.21727 21.3141L2.605 6H1.5C0.947715 6 0.5 5.55228 0.5 5C0.5 4.44772 0.947715 4 1.5 4L7.57826 3.99953C8.07482 1.73358 9.62365 0.5 12 0.5ZM19.383 6H4.616L6.20628 21.1047C6.25985 21.6136 6.68903 22 7.20079 22H16.7992C17.311 22 17.7401 21.6136 17.7937 21.1047L19.383 6ZM12 8C12.5523 8 13 8.44772 13 9V19C13 19.5523 12.5523 20 12 20C11.4477 20 11 19.5523 11 19V9C11 8.44772 11.4477 8 12 8ZM16.0499 8.00125C16.6015 8.02883 17.0263 8.49834 16.9988 9.04994L16.4988 19.0499C16.4712 19.6015 16.0017 20.0263 15.4501 19.9988C14.8985 19.9712 14.4737 19.5017 14.5012 18.9501L15.0012 8.95006C15.0288 8.39847 15.4983 7.97367 16.0499 8.00125ZM7.95006 8.00125C8.50166 7.97367 8.97117 8.39847 8.99875 8.95006L9.49875 18.9501C9.52633 19.5017 9.10153 19.9712 8.54994 19.9988C7.99834 20.0263 7.52883 19.6015 7.50125 19.0499L7.00125 9.04994C6.97367 8.49834 7.39847 8.02883 7.95006 8.00125ZM12 2.5C10.7333 2.5 10.0003 2.95411 9.64783 3.99956H14.3522C13.9997 2.95411 13.2667 2.5 12 2.5Z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="28px" height="28px" viewBox="0 0 28 28" version="1.1">
<g id="surface1">
<path style="fill:currentColor;stroke:none;fill-rule:nonzero;" d="M 21 22 C 21.550781 22 22 22.449219 22 23 C 22 23.550781 21.550781 24 21 24 L 7 24 C 6.449219 24 6 23.550781 6 23 C 6 22.449219 6.449219 22 7 22 Z M 14 2 C 14.550781 2 15 2.449219 15 3 L 15 16.585938 L 20.292969 11.292969 C 20.683594 10.902344 21.316406 10.902344 21.707031 11.292969 C 22.097656 11.683594 22.097656 12.316406 21.707031 12.707031 L 14.707031 19.707031 C 14.316406 20.097656 13.683594 20.097656 13.292969 19.707031 L 6.292969 12.707031 C 5.902344 12.316406 5.902344 11.683594 6.292969 11.292969 C 6.683594 10.902344 7.316406 10.902344 7.707031 11.292969 L 13 16.585938 L 13 3 C 13 2.449219 13.449219 2 14 2 Z M 14 2 "/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 892 B

@@ -0,0 +1,3 @@
<svg width="28" height="28" viewBox="0 0 28 28" xmlns="http://www.w3.org/2000/svg">
<path style="fill:currentColor" d="M14 5.36664C20.0664 5.36664 26.1334 10.3101 26.1334 14C26.1334 17.6898 20.0664 22.6333 14 22.6333C7.9337 22.6333 1.8667 17.6898 1.8667 14C1.8667 10.3101 7.9337 5.36664 14 5.36664ZM14 7.46664C9.00955 7.46664 3.9667 11.5756 3.9667 14C3.9667 16.4243 9.00955 20.5333 14 20.5333C18.9905 20.5333 24.0334 16.4243 24.0334 14C24.0334 11.5756 18.9905 7.46664 14 7.46664ZM14 10.0333C16.1908 10.0333 17.9667 11.8092 17.9667 14C17.9667 16.1907 16.1908 17.9666 14 17.9666C11.8093 17.9666 10.0334 16.1907 10.0334 14C10.0334 11.8092 11.8093 10.0333 14 10.0333ZM14 12.1333C12.9691 12.1333 12.1334 12.969 12.1334 14C12.1334 15.0309 12.9691 15.8666 14 15.8666C15.031 15.8666 15.8667 15.0309 15.8667 14C15.8667 12.969 15.031 12.1333 14 12.1333Z"/>
</svg>

After

Width:  |  Height:  |  Size: 858 B

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<path style="fill:currentColor" d="M8 0C12.4183 0 16 3.58172 16 8C16 12.4183 12.4183 16 8 16C3.58172 16 0 12.4183 0 8C0 3.58172 3.58172 0 8 0ZM10.4657 4.26022L8 6.727L5.5357 4.26022C5.18423 3.90875 4.61438 3.90875 4.26291 4.26022C3.91144 4.6117 3.91144 5.18154 4.26291 5.53302L6.728 8.001L4.26291 10.47C3.91144 10.8215 3.91144 11.3913 4.26291 11.7428C4.61438 12.0943 5.18423 12.0943 5.5357 11.7428L8 9.275L10.4657 11.7428C10.8172 12.0943 11.387 12.0943 11.7385 11.7428C12.09 11.3913 12.09 10.8215 11.7385 10.47L9.272 8.001L11.7385 5.53302C12.09 5.18154 12.09 4.6117 11.7385 4.26022C11.387 3.90875 10.8172 3.90875 10.4657 4.26022Z"/>
</svg>

After

Width:  |  Height:  |  Size: 728 B

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path style="fill:currentColor" d="M12 0C18.6274 0 24 5.37258 24 12C24 18.6274 18.6274 24 12 24C5.37258 24 0 18.6274 0 12C0 5.37258 5.37258 0 12 0ZM12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2ZM12 6C12.5523 6 13 6.44772 13 7V11H17C17.5523 11 18 11.4477 18 12C18 12.5523 17.5523 13 17 13H13V17C13 17.5523 12.5523 18 12 18C11.4477 18 11 17.5523 11 17V13H7C6.44772 13 6 12.5523 6 12C6 11.4477 6.44772 11 7 11H11V7C11 6.44772 11.4477 6 12 6Z"/>
</svg>

After

Width:  |  Height:  |  Size: 610 B

@@ -0,0 +1,4 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path style="fill:currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M17.2798 15.2H21.1198C22.5035 15.2 23.397 15.2019 24.077 15.2575C24.729 15.3107 24.9707 15.4012 25.0894 15.4616C25.541 15.6917 25.9081 16.0589 26.1382 16.5105C26.1987 16.6291 26.2891 16.8709 26.3424 17.5229C26.3979 18.2029 26.3998 19.0963 26.3998 20.48V21.12C26.3998 22.5038 26.3979 23.3972 26.3424 24.0772C26.2891 24.7292 26.1987 24.971 26.1382 25.0896C25.9081 25.5412 25.541 25.9084 25.0894 26.1385C24.9707 26.1989 24.729 26.2894 24.077 26.3426C23.397 26.3982 22.5035 26.4 21.1198 26.4H17.2798C15.8961 26.4 15.0026 26.3982 14.3226 26.3426C13.6706 26.2894 13.4289 26.1989 13.3102 26.1385C12.8586 25.9084 12.4915 25.5412 12.2614 25.0896C12.2009 24.971 12.1105 24.7292 12.0572 24.0772C12.0017 23.3972 11.9998 22.5038 11.9998 21.12V20.48C11.9998 19.0963 12.0017 18.2029 12.0572 17.5229C12.1105 16.8709 12.2009 16.6291 12.2614 16.5105C12.4915 16.0589 12.8586 15.6917 13.3102 15.4616C13.4289 15.4012 13.6706 15.3107 14.3226 15.2575C15.0026 15.2019 15.8961 15.2 17.2798 15.2ZM28.2766 15.4209C28.7998 16.4477 28.7998 17.7918 28.7998 20.48V21.12C28.7998 23.8083 28.7998 25.1524 28.2766 26.1792C27.8164 27.0824 27.0821 27.8167 26.179 28.2769C25.1522 28.8 23.8081 28.8 21.1198 28.8H17.2798C14.5916 28.8 13.2474 28.8 12.2206 28.2769C11.3175 27.8167 10.5832 27.0824 10.123 26.1792C9.5998 25.1524 9.5998 23.8083 9.5998 21.12V20.48C9.5998 17.7918 9.5998 16.4477 10.123 15.4209C10.5832 14.5177 11.3175 13.7834 12.2206 13.3232C13.2474 12.8 14.5916 12.8 17.2798 12.8H21.1198C23.8081 12.8 25.1522 12.8 26.179 13.3232C27.0821 13.7834 27.8164 14.5177 28.2766 15.4209Z" />
<path style="fill:currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M10.3513 3.15152C9.88265 3.62015 9.88265 4.37995 10.3513 4.84858L11.5027 6.00005H11.1998C7.00244 6.00005 3.5998 9.40268 3.5998 13.6V17.6C3.5998 18.2628 4.13706 18.8 4.7998 18.8C5.46255 18.8 5.9998 18.2628 5.9998 17.6V13.6C5.9998 10.7282 8.32792 8.40005 11.1998 8.40005H11.5027L10.3513 9.55152C9.88265 10.0202 9.88265 10.7799 10.3513 11.2486C10.8199 11.7172 11.5797 11.7172 12.0483 11.2486L15.2483 8.04858C15.717 7.57995 15.717 6.82015 15.2483 6.35152L12.0483 3.15152C11.5797 2.68289 10.8199 2.68289 10.3513 3.15152Z" />
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

@@ -0,0 +1,3 @@
<svg width="28" height="28" viewBox="0 0 28 28" xmlns="http://www.w3.org/2000/svg">
<path style="fill:currentColor" d="M13.8616 4.00652C12.5243 4.10158 11.4687 5.22458 11.4687 6.59595L11.4675 9.15215L11.4431 9.15807C6.01078 10.2918 2 15.141 2 20.8621C2 21.2778 2.0211 21.6921 2.06284 22.1029C2.16235 23.0823 3.40005 23.4343 3.99325 22.6518L4.21715 22.3663C5.96626 20.2093 8.45653 18.7944 11.2117 18.4237L11.4675 18.3925L11.4687 21.4042C11.4687 21.9845 11.662 22.5484 12.0175 23.0053C12.8954 24.1337 14.5152 24.3315 15.6353 23.4471L25.0126 16.0431C25.1759 15.9142 25.3232 15.7658 25.4511 15.6013C26.3291 14.4729 26.1328 12.8412 25.0126 11.9568L15.6353 4.55279C15.1817 4.19463 14.622 4 14.0457 4L13.8616 4.00652ZM14.0457 6.17336C14.1395 6.17336 14.2306 6.20504 14.3045 6.26335L23.6817 13.6673C23.8641 13.8113 23.896 14.0769 23.7531 14.2606C23.7323 14.2874 23.7083 14.3116 23.6817 14.3326L14.3045 21.7365C14.1221 21.8805 13.8584 21.8483 13.7155 21.6646C13.6576 21.5902 13.6262 21.4984 13.6262 21.4039L13.625 17.2416C13.6248 16.6345 13.131 16.1454 12.5285 16.1553L12.2491 16.16L11.8389 16.1799C9.11324 16.3552 6.56909 17.3718 4.50538 19.0365L4.29647 19.21L4.325 19.0465C5.09546 14.9226 8.44201 11.6748 12.6771 11.1545C13.2182 11.088 13.6251 10.6253 13.6252 10.0761L13.6262 6.59625C13.6262 6.36256 13.814 6.17336 14.0457 6.17336Z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path style="fill:currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M10.7818 4.72113C11.0747 5.01402 11.0747 5.4889 10.7818 5.78179L4.78182 11.7818C4.48893 12.0747 4.01405 12.0747 3.72116 11.7818L1.21967 9.2803C0.926777 8.98741 0.926777 8.51253 1.21967 8.21964C1.51256 7.92675 1.98744 7.92675 2.28033 8.21964L4.25149 10.1908L9.72116 4.72113C10.0141 4.42824 10.4889 4.42824 10.7818 4.72113Z"/>
<path style="fill:currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M15.0303 4.72113C15.3232 5.01403 15.3232 5.4889 15.0303 5.78179L9.03033 11.7818C8.73744 12.0747 8.26256 12.0747 7.96967 11.7818C7.67678 11.4889 7.67678 11.014 7.96967 10.7211L13.9697 4.72113C14.2626 4.42824 14.7374 4.42824 15.0303 4.72113Z"/>
</svg>

After

Width:  |  Height:  |  Size: 821 B

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path style="fill:currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M12.7818 4.21967C13.0747 4.51256 13.0747 4.98744 12.7818 5.28033L6.78182 11.2803C6.48893 11.5732 6.01405 11.5732 5.72116 11.2803L3.21967 8.77884C2.92678 8.48595 2.92678 8.01108 3.21967 7.71818C3.51256 7.42529 3.98744 7.42529 4.28033 7.71818L6.25149 9.68934L11.7212 4.21967C12.0141 3.92678 12.4889 3.92678 12.7818 4.21967Z"/>
</svg>

After

Width:  |  Height:  |  Size: 503 B

@@ -0,0 +1,3 @@
<svg width="34" height="34" viewBox="0 0 34 34" fill="none" xmlns="http://www.w3.org/2000/svg">
<path style="fill:currentColor" d="M17 0C26.3888 0 34 7.61116 34 17C34 26.3888 26.3888 34 17 34C7.61116 34 0 26.3888 0 17C0 7.61116 7.61116 0 17 0ZM17 10L16.9816 10.0002C16.958 10.0006 16.9343 10.0019 16.9107 10.004L17 10C16.9494 10 16.8996 10.0038 16.851 10.011C16.8341 10.0136 16.8166 10.0167 16.7993 10.0202C16.7773 10.0246 16.7556 10.0298 16.7342 10.0357C16.7185 10.0401 16.7031 10.0447 16.6879 10.0497C16.6682 10.0561 16.6484 10.0633 16.6289 10.0711C16.6112 10.0782 16.5939 10.0857 16.5768 10.0937C16.5561 10.1034 16.5357 10.1138 16.5157 10.1249C16.5036 10.1315 16.4914 10.1386 16.4793 10.146C16.4537 10.1617 16.4289 10.1785 16.4048 10.1963C16.3974 10.2018 16.3902 10.2073 16.383 10.2129L16.3746 10.2197L16.2929 10.2929L10.7929 15.7929C10.4024 16.1834 10.4024 16.8166 10.7929 17.2071C11.1834 17.5976 11.8166 17.5976 12.2071 17.2071L16 13.414V23.5C16 24.0523 16.4477 24.5 17 24.5C17.5523 24.5 18 24.0523 18 23.5V13.414L21.7929 17.2071C22.1834 17.5976 22.8166 17.5976 23.2071 17.2071C23.5976 16.8166 23.5976 16.1834 23.2071 15.7929L17.7071 10.2929L17.6254 10.2197C17.6226 10.2174 17.6197 10.2151 17.6168 10.2128L17.7071 10.2929C17.6717 10.2575 17.6343 10.2253 17.5953 10.1963C17.5711 10.1785 17.5463 10.1617 17.5207 10.1461C17.5086 10.1386 17.4964 10.1315 17.4841 10.1247C17.4643 10.1138 17.4439 10.1034 17.4231 10.0937C17.4061 10.0857 17.3888 10.0782 17.3713 10.0712C17.3516 10.0633 17.3318 10.0561 17.3117 10.0495C17.2969 10.0447 17.2815 10.0401 17.266 10.0358C17.2444 10.0298 17.2227 10.0246 17.2008 10.0202C17.1834 10.0167 17.1659 10.0136 17.1485 10.011C17.1294 10.0081 17.1097 10.0057 17.0898 10.004C17.066 10.0019 17.0427 10.0006 17.0194 10.0002C17.0129 10.0001 17.0065 10 17 10Z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

@@ -0,0 +1,3 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path style="fill:currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M23.0261 10.0208C23.4352 10.4299 23.4352 11.0932 23.0261 11.5023C22.617 11.9115 21.9537 11.9115 21.5446 11.5023L17.0472 7.00502L17.0472 19.1425C17.0472 19.7211 16.5782 20.1901 15.9996 20.1901C15.421 20.1901 14.952 19.7211 14.952 19.1425L14.952 7.00502L10.4547 11.5023C10.0456 11.9115 9.38226 11.9115 8.97314 11.5023C8.56401 11.0932 8.56401 10.4299 8.97314 10.0208L15.2587 3.7352C15.4625 3.53144 15.7294 3.42902 15.9965 3.42823C15.9975 3.42822 15.9986 3.42822 15.9996 3.42822C16.0007 3.42822 16.0017 3.42822 16.0028 3.42823C16.1437 3.42864 16.278 3.45687 16.4006 3.50771C16.5224 3.55808 16.6365 3.63237 16.7359 3.7306M23.0261 10.0208L16.741 3.73562L23.0261 10.0208ZM5.52344 19.8159V18.0949C5.52344 17.5163 5.99247 17.0473 6.57106 17.0473C7.14964 17.0473 7.61868 17.5163 7.61868 18.0949L7.61868 19.7711C7.61868 20.9618 7.61949 21.7918 7.67229 22.4381C7.72409 23.072 7.82065 23.4363 7.96123 23.7122C8.26254 24.3036 8.74334 24.7844 9.33471 25.0857C9.6106 25.2262 9.97485 25.3228 10.6088 25.3746C11.255 25.4274 12.0851 25.4282 13.2758 25.4282H18.7234C19.9142 25.4282 20.7442 25.4274 21.3904 25.3746C22.0244 25.3228 22.3887 25.2262 22.6646 25.0857C23.2559 24.7844 23.7367 24.3036 24.038 23.7122C24.1786 23.4363 24.2752 23.072 24.327 22.4381C24.3798 21.7918 24.3806 20.9618 24.3806 19.7711V18.0949C24.3806 17.5163 24.8496 17.0473 25.4282 17.0473C26.0068 17.0473 26.4758 17.5163 26.4758 18.0949V19.8159C26.4758 20.9513 26.4758 21.8671 26.4152 22.6087C26.3529 23.3722 26.2211 24.0429 25.9049 24.6634C25.4027 25.649 24.6014 26.4503 23.6158 26.9525C22.9953 27.2687 22.3246 27.4005 21.561 27.4629C20.8195 27.5235 19.9037 27.5235 18.7684 27.5235H13.231C12.0956 27.5235 11.1798 27.5235 10.4382 27.4629C9.67466 27.4005 9.00398 27.2687 8.38349 26.9525C7.39788 26.4503 6.59655 25.649 6.09436 24.6634C5.7782 24.0429 5.64639 23.3722 5.58401 22.6087C5.52342 21.8671 5.52343 20.9513 5.52344 19.8159Z" />
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

@@ -0,0 +1,3 @@
<svg width="28" height="28" viewBox="0 0 28 28" xmlns="http://www.w3.org/2000/svg">
<path d="M17.75 15C22.7011 15 27 16.7626 27 20.6429C27 22.6593 26.2189 23.5 24.5441 23.5H10.9559C9.28111 23.5 8.5 22.6593 8.5 20.6429C8.5 16.7626 12.7989 15 17.75 15ZM17.75 17C13.804 17 10.5 18.3547 10.5 20.6429C10.5 21.5338 10.4285 21.5064 10.8721 21.5006L24.5441 21.5C25.0801 21.5 25 21.5862 25 20.6429C25 18.3547 21.696 17 17.75 17ZM5 7C5.55228 7 6 7.44772 6 8V11H9C9.55228 11 10 11.4477 10 12C10 12.5523 9.55228 13 9 13H6V16C6 16.5523 5.55228 17 5 17C4.44772 17 4 16.5523 4 16V13H1C0.447715 13 0 12.5523 0 12C0 11.4477 0.447715 11 1 11H4V8C4 7.44772 4.44772 7 5 7ZM17.5 3C20.5385 3 23 5.46147 23 8.5C23 11.5385 20.5385 14 17.5 14C14.4615 14 12 11.5385 12 8.5C12 5.46147 14.4615 3 17.5 3ZM17.5 5C15.566 5 14 6.56603 14 8.5C14 10.434 15.566 12 17.5 12C19.434 12 21 10.434 21 8.5C21 6.56603 19.434 5 17.5 5Z"/>
</svg>

After

Width:  |  Height:  |  Size: 904 B

@@ -0,0 +1,3 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M22.6668 18.0002C27.5847 18.0002 31.8668 19.7458 31.8668 23.6764C31.8668 25.7485 31.0085 26.6669 29.255 26.6669L21.7939 26.6669C21.0696 26.6669 20.5579 25.9323 20.701 25.2223C20.8013 24.7241 21.2194 24.2669 21.7276 24.267C24.74 24.2674 29.4375 24.2696 29.4375 24.2696C29.4375 24.2696 29.4668 23.8738 29.4668 23.6764C29.4668 21.6601 26.3763 20.4002 22.6668 20.4002C22.0374 20.4002 21.4258 20.4365 20.8441 20.5066C20.3971 20.5604 19.9644 20.348 19.7112 19.9758C19.2432 19.288 19.4898 18.2476 20.315 18.143C21.0709 18.0472 21.8603 18.0002 22.6668 18.0002ZM9.33346 18.0002C14.2514 18.0002 18.5335 19.7458 18.5335 23.6764C18.5335 25.7485 17.6752 26.6669 15.9217 26.6669H2.74523C0.991743 26.6669 0.133464 25.7485 0.133464 23.6764C0.133464 19.7458 4.41553 18.0002 9.33346 18.0002ZM9.33346 20.4002C5.62395 20.4002 2.53346 21.6601 2.53346 23.6764C2.53346 23.8748 2.56013 24.2696 2.56013 24.2696H16.1041C16.1041 24.2696 16.1335 23.8738 16.1335 23.6764C16.1335 21.6601 13.043 20.4002 9.33346 20.4002ZM22.6668 5.60025C25.7237 5.60025 28.2001 8.07668 28.2001 11.1336C28.2001 14.1905 25.7237 16.6669 22.6668 16.6669C19.6099 16.6669 17.1335 14.1905 17.1335 11.1336C17.1335 8.07668 19.6099 5.60025 22.6668 5.60025ZM9.33346 5.60025C12.3904 5.60025 14.8668 8.07668 14.8668 11.1336C14.8668 14.1905 12.3904 16.6669 9.33346 16.6669C6.27656 16.6669 3.80013 14.1905 3.80013 11.1336C3.80013 8.07668 6.27656 5.60025 9.33346 5.60025ZM22.6668 8.00025C20.9354 8.00025 19.5335 9.40216 19.5335 11.1336C19.5335 12.865 20.9354 14.2669 22.6668 14.2669C24.3982 14.2669 25.8001 12.865 25.8001 11.1336C25.8001 9.40216 24.3982 8.00025 22.6668 8.00025ZM9.33346 8.00025C7.60204 8.00025 6.20013 9.40216 6.20013 11.1336C6.20013 12.865 7.60204 14.2669 9.33346 14.2669C11.0649 14.2669 12.4668 12.865 12.4668 11.1336C12.4668 9.40216 11.0649 8.00025 9.33346 8.00025Z" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

@@ -0,0 +1,3 @@
<svg width="11" height="21" viewBox="0 0 11 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path style="fill:currentColor" d="M5.5 6.125C6.53125 6.125 7.375 5.28125 7.375 4.25C7.375 3.21875 6.53125 2.375 5.5 2.375C4.46875 2.375 3.625 3.21875 3.625 4.25C3.625 5.28125 4.46875 6.125 5.5 6.125ZM5.5 8.625C4.46875 8.625 3.625 9.46875 3.625 10.5C3.625 11.5312 4.46875 12.375 5.5 12.375C6.53125 12.375 7.375 11.5312 7.375 10.5C7.375 9.46875 6.53125 8.625 5.5 8.625ZM5.5 14.875C4.46875 14.875 3.625 15.7188 3.625 16.75C3.625 17.7812 4.46875 18.625 5.5 18.625C6.53125 18.625 7.375 17.7812 7.375 16.75C7.375 15.7188 6.53125 14.875 5.5 14.875Z" />
</svg>

After

Width:  |  Height:  |  Size: 650 B

@@ -0,0 +1,3 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path style="fill:currentColor" d="M0.494915 0.494923C0.773861 0.215977 1.22612 0.215977 1.50507 0.494923L5.99999 4.98928L10.4949 0.494923C10.7524 0.237434 11.1576 0.217627 11.4378 0.435502L11.5051 0.494923C11.784 0.773869 11.784 1.22613 11.5051 1.50508L7.01071 6L11.5051 10.4949C11.7626 10.7524 11.7824 11.1576 11.5645 11.4378L11.5051 11.5051C11.2261 11.784 10.7739 11.784 10.4949 11.5051L5.99999 7.01071L1.50507 11.5051C1.24758 11.7626 0.842414 11.7824 0.562206 11.5645L0.494915 11.5051C0.215969 11.2261 0.215969 10.7739 0.494915 10.4949L4.98928 6L0.494915 1.50508C0.237427 1.24759 0.21762 0.842422 0.435494 0.562214L0.494915 0.494923Z"/>
</svg>

After

Width:  |  Height:  |  Size: 744 B

@@ -0,0 +1,4 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path style="fill:currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M16.3641 11.6883C16.3641 11.0428 15.8408 10.5194 15.1953 10.5194C14.5497 10.5194 14.0264 11.0428 14.0264 11.6883V14.0259H11.6888C11.0432 14.0259 10.5199 14.5492 10.5199 15.1948C10.5199 15.8403 11.0432 16.3636 11.6888 16.3636H14.0264V18.7013C14.0264 19.3468 14.5497 19.8701 15.1953 19.8701C15.8408 19.8701 16.3641 19.3468 16.3641 18.7013V16.3636H18.7018C19.3473 16.3636 19.8706 15.8403 19.8706 15.1948C19.8706 14.5492 19.3473 14.0259 18.7018 14.0259H16.3641V11.6883Z" />
<path style="fill:currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M15.1953 4.67529C9.38551 4.67529 4.67578 9.38502 4.67578 15.1948C4.67578 21.0045 9.38551 25.7143 15.1953 25.7143C17.679 25.7143 19.9618 24.8534 21.7614 23.4139L27.2259 28.8784C27.6824 29.3349 28.4224 29.3349 28.8789 28.8784C29.3354 28.4219 29.3354 27.6819 28.8789 27.2254L23.4144 21.7609C24.8539 19.9613 25.7147 17.6786 25.7147 15.1948C25.7147 9.38502 21.005 4.67529 15.1953 4.67529ZM15.1953 7.01296C10.6766 7.01296 7.01344 10.6761 7.01344 15.1948C7.01344 19.7135 10.6766 23.3766 15.1953 23.3766C19.714 23.3766 23.3771 19.7135 23.3771 15.1948C23.3771 10.6761 19.714 7.01296 15.1953 7.01296Z" />
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -0,0 +1,4 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path style="fill:currentColor" d="M11.0204 13.2245H13.2245H15.4286H17.6327C18.2413 13.2245 18.7347 13.7179 18.7347 14.3266C18.7347 14.9352 18.2413 15.4286 17.6327 15.4286H15.4286H13.2245H11.0204C10.4118 15.4286 9.91841 14.9352 9.91841 14.3266C9.91841 13.7179 10.4118 13.2245 11.0204 13.2245Z" />
<path style="fill:currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M14.3266 4.4082C8.84881 4.4082 4.4082 8.84881 4.4082 14.3266C4.4082 19.8043 8.84881 24.2449 14.3266 24.2449C16.6684 24.2449 18.8207 23.4333 20.5175 22.076L25.6698 27.2283C26.1001 27.6587 26.7979 27.6587 27.2283 27.2283C27.6587 26.7979 27.6587 26.1001 27.2283 25.6698L22.076 20.5175C23.4333 18.8207 24.2449 16.6684 24.2449 14.3266C24.2449 8.84881 19.8043 4.4082 14.3266 4.4082ZM14.3266 6.61228C10.0661 6.61228 6.61228 10.0661 6.61228 14.3266C6.61228 18.5871 10.0661 22.0409 14.3266 22.0409C18.5871 22.0409 22.0409 18.5871 22.0409 14.3266C22.0409 10.0661 18.5871 6.61228 14.3266 6.61228Z" />
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

@@ -0,0 +1,79 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import React, { cloneElement } from 'react';
import { ComponentStory } from '@storybook/react';
import {
CopyIcon,
DeleteIcon,
DownloadIcon,
EyeIcon,
InputClearIcon,
ShareIcon,
UserAddIcon,
CheckIcon,
ZoomInIcon,
ZoomOutIcon,
VerticalDotsIcon,
RotateCwIcon,
UploadCwIcon,
PlusIcon,
UpIcon,
XIcon,
StatusCheckDoubleIcon,
StatusCheckIcon,
UsersIcon,
CheckOutlineIcon,
} from '@atoms/icons-agnostic/index';
export default {
title: '@atoms/icons-agnostic',
};
type PropsType = {
icon: JSX.Element;
title: string;
};
const Icon = ({ icon, title }: PropsType): JSX.Element => {
const comp = cloneElement(icon, { className: 'w-8 h-8' });
return (
<div className="flex flex-col place-items-center w-[90px] my-3">
{comp}
<div className="m-2 text-xs text-zinc-500 break-words max-w-[68px] text-center">{title}</div>
</div>
);
};
// More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args
const Template: ComponentStory<any> = () => {
return (
<>
<div className="flex flex-wrap mb-2">
<Icon icon={<CopyIcon />} title="Copy" />
<Icon icon={<DeleteIcon />} title="Delete" />
<Icon icon={<DownloadIcon />} title="Download" />
<Icon icon={<EyeIcon />} title="Eye" />
<Icon icon={<ShareIcon />} title="Share" />
<Icon icon={<InputClearIcon />} title="InputClear" />
<Icon icon={<UserAddIcon />} title="User" />
<Icon icon={<CheckIcon />} title="Check" />
<Icon icon={<CheckOutlineIcon />} title="CheckOutline" />
<Icon icon={<ZoomInIcon />} title="ZoomIn" />
<Icon icon={<ZoomOutIcon />} title="ZoomOut" />
<Icon icon={<VerticalDotsIcon />} title="VerticalDots" />
<Icon icon={<RotateCwIcon />} title="RotateCw" />
<Icon icon={<UploadCwIcon />} title="Upload" />
<Icon icon={<PlusIcon />} title="Plus" />
<Icon icon={<UpIcon />} title="Up" />
<Icon icon={<XIcon />} title="X" />
<Icon icon={<StatusCheckDoubleIcon />} title="StatusCheckDouble" />
<Icon icon={<StatusCheckIcon />} title="StatusCheck" />
<Icon icon={<UsersIcon />} title="Users" />
</div>
</>
);
};
export const Default = Template.bind({});
// More on args: https://storybook.js.org/docs/react/writing-stories/args
Default.args = {};
@@ -0,0 +1,45 @@
import React, { ComponentProps } from 'react';
import { ReactComponent as CopySvg } from './assets/copy.svg';
import { ReactComponent as DeleteSvg } from './assets/delete.svg';
import { ReactComponent as DownloadSvg } from './assets/download.svg';
import { ReactComponent as UploadSvg } from './assets/upload.svg';
import { ReactComponent as InputClearSvg } from './assets/input-clear.svg';
import { ReactComponent as ShareSvg } from './assets/share.svg';
import { ReactComponent as EyeSvg } from './assets/eye.svg';
import { ReactComponent as UserAddSvg } from './assets/user-add.svg';
import { ReactComponent as CheckSvg } from './assets/check.svg';
import { ReactComponent as CheckOutlineSvg } from './assets/check-outline.svg';
import { ReactComponent as ZommInSvg } from './assets/zoom-in.svg';
import { ReactComponent as ZoomOutSvg } from './assets/zoom-out.svg';
import { ReactComponent as VerticalDotsSvg } from './assets/vertical-dots.svg';
import { ReactComponent as RotateCwSvg } from './assets/rotate-cw.svg';
import { ReactComponent as Plus } from './assets/plus.svg';
import { ReactComponent as Up } from './assets/up.svg';
import { ReactComponent as X } from './assets/x.svg';
import { ReactComponent as StatusCheckDouble } from './assets/status-check-double.svg';
import { ReactComponent as StatusCheck } from './assets/status-check.svg';
import { ReactComponent as Users } from './assets/users.svg';
export const CopyIcon = (props: ComponentProps<'svg'>) => <CopySvg {...props} />;
export const DeleteIcon = (props: ComponentProps<'svg'>) => <DeleteSvg {...props} />;
export const DownloadIcon = (props: ComponentProps<'svg'>) => <DownloadSvg {...props} />;
export const EyeIcon = (props: ComponentProps<'svg'>) => <EyeSvg {...props} />;
export const InputClearIcon = (props: ComponentProps<'svg'>) => <InputClearSvg {...props} />;
export const ShareIcon = (props: ComponentProps<'svg'>) => <ShareSvg {...props} />;
export const UserAddIcon = (props: ComponentProps<'svg'>) => <UserAddSvg {...props} />;
export const CheckIcon = (props: ComponentProps<'svg'>) => <CheckSvg {...props} />;
export const CheckOutlineIcon = (props: ComponentProps<'svg'>) => <CheckOutlineSvg {...props} />;
export const ZoomInIcon = (props: ComponentProps<'svg'>) => <ZommInSvg {...props} />;
export const ZoomOutIcon = (props: ComponentProps<'svg'>) => <ZoomOutSvg {...props} />;
export const VerticalDotsIcon = (props: ComponentProps<'svg'>) => <VerticalDotsSvg {...props} />;
export const RotateCwIcon = (props: ComponentProps<'svg'>) => <RotateCwSvg {...props} />;
export const UploadCwIcon = (props: ComponentProps<'svg'>) => <UploadSvg {...props} />;
export const PlusIcon = (props: ComponentProps<'svg'>) => <Plus {...props} />;
export const UpIcon = (props: ComponentProps<'svg'>) => <Up {...props} />;
export const XIcon = (props: ComponentProps<'svg'>) => <X {...props} />;
export const StatusCheckDoubleIcon = (props: ComponentProps<'svg'>) => (
<StatusCheckDouble {...props} />
);
export const StatusCheckIcon = (props: ComponentProps<'svg'>) => <StatusCheck {...props} />;
export const UsersIcon = (props: ComponentProps<'svg'>) => <Users {...props} />;
@@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.12" d="M12 24C18.6274 24 24 18.6274 24 12C24 5.37258 18.6274 0 12 0C5.37258 0 0 5.37258 0 12C0 18.6274 5.37258 24 12 24Z" fill="#818C99"/>
<path d="M16.7364 7.2636C17.0879 7.61508 17.0879 8.18492 16.7364 8.5364L13.273 12L16.7364 15.4636C17.0586 15.7858 17.0854 16.2915 16.817 16.6442L16.7364 16.7364C16.3849 17.0879 15.8151 17.0879 15.4636 16.7364L12 13.273L8.5364 16.7364C8.18492 17.0879 7.61508 17.0879 7.2636 16.7364C6.91213 16.3849 6.91213 15.8151 7.2636 15.4636L10.727 12L7.2636 8.5364C6.94142 8.21421 6.91457 7.70853 7.18306 7.35577L7.2636 7.2636C7.61508 6.91213 8.18492 6.91213 8.5364 7.2636L12 10.727L15.4636 7.2636C15.8151 6.91213 16.3849 6.91213 16.7364 7.2636Z" fill="#818C99"/>
</svg>

After

Width:  |  Height:  |  Size: 818 B

@@ -0,0 +1,8 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 8C0 3.58172 3.58172 0 8 0H32C36.4183 0 40 3.58172 40 8V32C40 36.4183 36.4183 40 32 40H8C3.58172 40 0 36.4183 0 32V8Z" fill="#F9E165"/>
<path d="M20.438 30.2475C22.302 30.2426 23.8244 28.7569 23.875 26.8936L23.0235 19.5108C22.9248 18.551 22.1293 17.8133 21.1648 17.7871H19.7007C18.7571 17.8091 17.9693 18.5133 17.842 19.4485L17.001 26.7171C16.9786 27.6439 17.3305 28.5406 17.9772 29.2049C18.6239 29.8692 19.5109 30.245 20.438 30.2475ZM18.6104 26.6548L18.6935 25.9071C18.7638 24.9986 19.5215 24.2972 20.4328 24.2972C21.344 24.2972 22.1018 24.9986 22.172 25.9071L22.2551 26.6548C22.3164 27.0785 22.1878 27.5076 21.9036 27.8278C21.6195 28.148 21.2086 28.3267 20.7806 28.3162H20.0849C19.6475 28.3257 19.2288 28.1391 18.9434 27.8074C18.658 27.4758 18.5359 27.034 18.6104 26.6029V26.6548ZM19.3996 20.7153V20.2896C19.4511 19.7921 19.8703 19.4141 20.3705 19.4141C20.8706 19.4141 21.2898 19.7921 21.3413 20.2896L21.3933 20.7153C21.4272 20.9528 21.3542 21.1931 21.1938 21.3715C21.0335 21.5499 20.8023 21.648 20.5626 21.6395H20.1784C19.9477 21.6329 19.7307 21.5282 19.582 21.3518C19.4333 21.1753 19.3669 20.9438 19.3996 20.7153Z" fill="white"/>
<path d="M20.0277 9.9998H18.5428C18.0984 9.9998 17.7381 10.3601 17.7381 10.8045C17.7381 11.249 18.0984 11.6093 18.5428 11.6093H20.0277C20.4721 11.6093 20.8324 11.249 20.8324 10.8045C20.8324 10.3601 20.4721 9.9998 20.0277 9.9998Z" fill="white"/>
<path d="M21.6787 11.6094H20.1938C19.7494 11.6094 19.3891 11.9697 19.3891 12.4141C19.3891 12.8586 19.7494 13.2189 20.1938 13.2189H21.6787C22.1231 13.2189 22.4834 12.8586 22.4834 12.4141C22.4834 11.9697 22.1231 11.6094 21.6787 11.6094Z" fill="white"/>
<path d="M20.0277 13.2082H18.5428C18.0984 13.2082 17.7381 13.5685 17.7381 14.013C17.7381 14.4574 18.0984 14.8177 18.5428 14.8177H20.0277C20.4721 14.8177 20.8324 14.4574 20.8324 14.013C20.8324 13.5685 20.4721 13.2082 20.0277 13.2082Z" fill="white"/>
<path d="M21.6787 14.8174H20.1938C19.7494 14.8174 19.3891 15.1777 19.3891 15.6221C19.3891 16.0666 19.7494 16.4269 20.1938 16.4269H21.6787C22.1231 16.4269 22.4834 16.0666 22.4834 15.6221C22.4834 15.1777 22.1231 14.8174 21.6787 14.8174Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

@@ -0,0 +1,5 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 0C3.58172 0 0 3.58172 0 8V32C0 36.4183 3.58172 40 8 40H32C36.4183 40 40 36.4183 40 32V9L31 0H8Z" fill="#A3D3FF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M31 0L40 9H33.5C32.1193 9 31 7.88075 31 6.5V0Z" fill="#007AFF"/>
<path d="M14.2241 12.0005H10.9999L14.37 27.9979H17.6763L19.8663 18.6836L21.9696 28H25.0787L28.9999 12H23.5508V14.6643H25.4081L23.5241 22.3148L21.5774 12H18.4618L16.044 22.4625L14.2241 12.0005Z" fill="#007AFF"/>
</svg>

After

Width:  |  Height:  |  Size: 594 B

@@ -0,0 +1,4 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 8C0 3.58172 3.58172 0 8 0H32C36.4183 0 40 3.58172 40 8V32C40 36.4183 36.4183 40 32 40H8C3.58172 40 0 36.4183 0 32V8Z" fill="#E7DEFF"/>
<path d="M25.8004 16.0406C26.9497 16.0406 27.8824 15.1269 27.8824 14C27.8824 12.8734 26.9499 11.9594 25.8004 11.9594C24.651 11.9594 23.7186 12.8731 23.7186 14C23.7186 15.1289 24.6581 16.0406 25.8022 16.0406H25.8004ZM32.7813 26.542L24.9389 19.7121C24.526 19.2386 24.1531 19.3787 23.7186 19.8867L21.8055 21.9171C21.4121 22.3634 20.8377 22.5045 20.5306 22.1406L16.4471 16.7382C16.0035 16.291 15.431 16.3918 15.1735 16.9627L8.09088 26.807C7.83345 27.38 8.14289 28.0052 8.76695 28L32.3661 27.9914C33.0151 27.9955 33.1914 27.0057 32.7754 26.542H32.7813Z" fill="#8400EC"/>
</svg>

After

Width:  |  Height:  |  Size: 816 B

@@ -0,0 +1,7 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 0C3.58172 0 0 3.58172 0 8V32C0 36.4183 3.58172 40 8 40H32C36.4183 40 40 36.4183 40 32V9L31 0H8Z" fill="#D52E26"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M31 0L40 9H33.5C32.1193 9 31 7.88075 31 6.5V0Z" fill="#9D0700"/>
<path d="M10 23.9996V15.9996H13.4531C14.0469 15.9996 14.5664 16.1168 15.0117 16.3512C15.457 16.5856 15.8034 16.915 16.0508 17.3395C16.2982 17.764 16.4219 18.2601 16.4219 18.8278C16.4219 19.4007 16.2943 19.8968 16.0391 20.3161C15.7865 20.7353 15.431 21.0582 14.9727 21.2848C14.5169 21.5114 13.9844 21.6246 13.375 21.6246H11.3125V19.9371H12.9375C13.1927 19.9371 13.4102 19.8929 13.5898 19.8043C13.7721 19.7132 13.9115 19.5843 14.0078 19.4176C14.1068 19.2509 14.1563 19.0543 14.1563 18.8278C14.1563 18.5986 14.1068 18.4033 14.0078 18.2418C13.9115 18.0778 13.7721 17.9528 13.5898 17.8668C13.4102 17.7783 13.1927 17.734 12.9375 17.734H12.1719V23.9996H10Z" fill="white"/>
<path d="M20.2969 23.9996H17.2188V15.9996H20.2656C21.0885 15.9996 21.7995 16.1598 22.3984 16.4801C23 16.7978 23.4635 17.2562 23.7891 17.8551C24.1172 18.4515 24.2812 19.1663 24.2812 19.9996C24.2812 20.833 24.1185 21.5491 23.793 22.1481C23.4674 22.7444 23.0065 23.2028 22.4102 23.5231C21.8138 23.8408 21.1094 23.9996 20.2969 23.9996ZM19.3906 22.1559H20.2188C20.6146 22.1559 20.9518 22.0921 21.2305 21.9645C21.5117 21.8369 21.7253 21.6168 21.8711 21.3043C22.0195 20.9918 22.0938 20.5569 22.0938 19.9996C22.0938 19.4424 22.0182 19.0075 21.8672 18.695C21.7188 18.3825 21.5 18.1624 21.2109 18.0348C20.9245 17.9072 20.5729 17.8434 20.1562 17.8434H19.3906V22.1559Z" fill="white"/>
<path d="M25.2861 23.9996V15.9996H30.9111V17.7496H27.458V19.1246H30.5674V20.8746H27.458V23.9996H25.2861Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

@@ -0,0 +1,5 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 0C3.58172 0 0 3.58172 0 8V32C0 36.4183 3.58172 40 8 40H32C36.4183 40 40 36.4183 40 32V9L31 0H8Z" fill="#FFA7CC"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M31 0L40 9H33.5C32.1193 9 31 7.88075 31 6.5V0Z" fill="#FF007A"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M15 12V29H18.2308V22.8647H22.0962C24.8045 22.8647 27 20.4325 27 17.4323C27 14.4321 24.8045 12 22.0962 12H15ZM18.2308 20.3083V14.5564H21.4038C22.8377 14.5564 24 15.844 24 17.4323C24 19.0207 22.8377 20.3083 21.4038 20.3083H18.2308Z" fill="#FF007A"/>
</svg>

After

Width:  |  Height:  |  Size: 680 B

@@ -0,0 +1,5 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 0C3.58172 0 0 3.58172 0 8V32C0 36.4183 3.58172 40 8 40H32C36.4183 40 40 36.4183 40 32V9L31 0H8Z" fill="#82EB8C"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M31 0L40 9H33.5C32.1193 9 31 7.88075 31 6.5V0Z" fill="#26A44D"/>
<path d="M17.2539 12H12.2984L18.0049 19.8878L12 28.4981H20.2561V26.2953L18.061 26.2926L20.2266 23.0687L24.0134 28.4807H28.5143L22.4781 19.8375L28.2569 12.0181H23.3718L20.2769 16.6437L17.2539 12Z" fill="#26A44D"/>
</svg>

After

Width:  |  Height:  |  Size: 596 B

@@ -0,0 +1,4 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 8C0 3.58172 3.58172 0 8 0H32C36.4183 0 40 3.58172 40 8V32C40 36.4183 36.4183 40 32 40H8C3.58172 40 0 36.4183 0 32V8Z" fill="#F3F3F7"/>
<path d="M22.4808 10.0965C22.7777 10.1649 23.0405 10.2738 23.2989 10.4354C23.5573 10.597 23.7666 10.7666 24.3144 11.3144L27.6856 14.6856C28.2334 15.2334 28.403 15.4427 28.5646 15.7011C28.7262 15.9595 28.8351 16.2223 28.9035 16.5192C28.972 16.8162 29 17.0842 29 17.8588V25.1542C29 26.4915 28.8608 26.9764 28.5993 27.4653C28.3378 27.9542 27.9542 28.3378 27.4653 28.5993C26.9764 28.8608 26.4915 29 25.1542 29H15.8458C14.5085 29 14.0236 28.8608 13.5347 28.5993C13.0458 28.3378 12.6622 27.9542 12.4007 27.4653C12.1392 26.9764 12 26.4915 12 25.1542V13.8458C12 12.5085 12.1392 12.0236 12.4007 11.5347C12.6622 11.0458 13.0458 10.6622 13.5347 10.4007C14.0236 10.1392 14.5085 10 15.8458 10H21.1412C21.9158 10 22.1838 10.028 22.4808 10.0965ZM21.8 11.9243C21.6343 11.9243 21.5 12.0586 21.5 12.2243V16C21.5 16.2762 21.7239 16.5 22 16.5H25.7757C25.8553 16.5 25.9316 16.4684 25.9879 16.4122C26.105 16.295 26.105 16.105 25.9879 15.9879L22.0121 12.0121C21.9559 11.9559 21.8796 11.9243 21.8 11.9243Z" fill="#989CB1"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

@@ -0,0 +1,229 @@
<svg width="105" height="100" viewBox="0 0 105 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M54.353 100C81.9673 100 104.353 77.6142 104.353 50C104.353 22.3858 81.9673 0 54.353 0C26.7388 0 4.35303 22.3858 4.35303 50C4.35303 77.6142 26.7388 100 54.353 100Z" fill="url(#paint0_radial_1954_107668)"/>
<g style="mix-blend-mode:soft-light">
<path d="M54.353 100C81.9673 100 104.353 77.6142 104.353 50C104.353 22.3858 81.9673 0 54.353 0C26.7388 0 4.35303 22.3858 4.35303 50C4.35303 77.6142 26.7388 100 54.353 100Z" fill="url(#paint1_radial_1954_107668)"/>
</g>
<g style="mix-blend-mode:soft-light">
<path d="M54.353 100C81.9673 100 104.353 77.6142 104.353 50C104.353 22.3858 81.9673 0 54.353 0C26.7388 0 4.35303 22.3858 4.35303 50C4.35303 77.6142 26.7388 100 54.353 100Z" fill="url(#paint2_radial_1954_107668)"/>
</g>
<g style="mix-blend-mode:overlay">
<path d="M54.353 100C81.9673 100 104.353 77.6142 104.353 50C104.353 22.3858 81.9673 0 54.353 0C26.7388 0 4.35303 22.3858 4.35303 50C4.35303 77.6142 26.7388 100 54.353 100Z" fill="url(#paint3_radial_1954_107668)" fill-opacity="0.7"/>
</g>
<mask id="mask0_1954_107668" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="4" y="0" width="101" height="100">
<path d="M54.147 100C81.7612 100 104.147 77.6142 104.147 50C104.147 22.3858 81.7612 0 54.147 0C26.5327 0 4.14697 22.3858 4.14697 50C4.14697 77.6142 26.5327 100 54.147 100Z" fill="white"/>
</mask>
<g mask="url(#mask0_1954_107668)">
<g filter="url(#filter0_f_1954_107668)">
<path d="M3.86202 48.5733C3.91631 47.8629 4.1501 47.7917 4.21829 48.5131C4.29405 49.3146 4.14678 50.9258 4.18223 51.8977C4.27612 54.4712 4.67061 56.7088 5.07728 58.5289C6.3878 64.3941 8.20267 68.1736 9.89542 71.3757C15.4596 81.9015 21.5547 87.2315 27.5007 91.2485C36.2165 97.1367 45.0032 99.0097 53.7969 99.407C54.0188 99.417 54.0557 100.862 53.8375 101.05C53.444 101.389 52.9565 101.052 52.5539 101.038C51.3972 100.998 50.2404 100.918 49.0838 100.798C45.6541 100.442 42.2247 99.716 38.8022 98.5483C29.4965 95.3734 19.4629 89.937 10.7003 74.625C10.149 73.6618 9.60218 72.6476 9.06305 71.5579C8.63189 70.6863 8.20493 69.7728 7.78548 68.7957C6.40585 65.5819 3.19087 57.355 3.86202 48.5733ZM53.8031 101.065C53.5915 101.072 53.5379 99.725 53.7396 99.4454C54.0445 99.0227 54.5014 99.3708 54.8228 99.3507C56.1576 99.2673 57.4922 99.1435 58.8263 98.976C62.4844 98.5166 66.1399 97.7323 69.7872 96.5245C78.8336 93.5287 88.5593 88.9615 97.0872 74.9296C97.6299 74.0365 98.168 73.0891 98.6988 72.0707C100.312 68.9758 104.355 60.7603 103.887 50.5474C103.848 49.6864 104.11 49.1707 104.228 49.9093C104.377 50.8497 104.316 52.8102 104.275 53.8534C104.143 57.1943 103.63 60.0197 103.089 62.3014C101.375 69.5327 98.9678 73.7255 96.7635 77.2263C89.5988 88.6053 81.8171 93.1925 74.2516 96.4888C67.466 99.4454 60.6287 100.855 53.8031 101.065Z" fill="#401C02"/>
</g>
<g filter="url(#filter1_f_1954_107668)">
<path d="M3.86202 48.5733C3.91631 47.8629 4.1501 47.7917 4.21829 48.5131C4.29405 49.3146 4.14678 50.9258 4.18223 51.8977C4.27612 54.4712 4.67061 56.7088 5.07728 58.5289C6.3878 64.3941 8.20267 68.1736 9.89542 71.3757C15.4596 81.9015 21.5547 87.2315 27.5007 91.2485C36.2165 97.1367 45.0032 99.0097 53.7969 99.407C54.0188 99.417 54.0557 100.862 53.8375 101.05C53.444 101.389 52.9565 101.052 52.5539 101.038C51.3972 100.998 50.2404 100.918 49.0838 100.798C45.6541 100.442 42.2247 99.716 38.8022 98.5483C29.4965 95.3734 19.4629 89.937 10.7003 74.625C10.149 73.6618 9.60218 72.6476 9.06305 71.5579C8.63189 70.6863 8.20493 69.7728 7.78548 68.7957C6.40585 65.5819 3.19087 57.355 3.86202 48.5733ZM53.8031 101.065C53.5915 101.072 53.5379 99.725 53.7396 99.4454C54.0445 99.0227 54.5014 99.3708 54.8228 99.3507C56.1576 99.2673 57.4922 99.1435 58.8263 98.976C62.4844 98.5166 66.1399 97.7323 69.7872 96.5245C78.8336 93.5287 88.5593 88.9615 97.0872 74.9296C97.6299 74.0365 98.168 73.0891 98.6988 72.0707C100.312 68.9758 104.355 60.7603 103.887 50.5474C103.848 49.6864 104.11 49.1707 104.228 49.9093C104.377 50.8497 104.316 52.8102 104.275 53.8534C104.143 57.1943 103.63 60.0197 103.089 62.3014C101.375 69.5327 98.9678 73.7255 96.7635 77.2263C89.5988 88.6053 81.8171 93.1925 74.2516 96.4888C67.466 99.4454 60.6287 100.855 53.8031 101.065Z" fill="#401C02"/>
</g>
</g>
<path d="M64.4585 70.7832V78.7938C64.458 81.3425 63.4451 83.7866 61.6428 85.5885C59.8404 87.3905 57.3961 88.4028 54.8475 88.4028H53.4108C50.8623 88.4028 48.4182 87.3904 46.6162 85.5884C44.8141 83.7864 43.8018 81.3423 43.8018 78.7938V70.7853C46.7622 71.7762 50.3135 72.3526 54.1301 72.3526C57.9468 72.3526 61.4981 71.7804 64.4585 70.7832Z" fill="url(#paint4_radial_1954_107668)"/>
<path d="M54.8659 59.7064H53.3918C47.9487 59.7064 43.5361 64.119 43.5361 69.5621V78.9141C43.5361 84.3572 47.9487 88.7698 53.3918 88.7698H54.8659C60.309 88.7698 64.7216 84.3572 64.7216 78.9141V69.5621C64.7216 64.119 60.309 59.7064 54.8659 59.7064Z" stroke="url(#paint5_linear_1954_107668)" stroke-width="0.841488" stroke-miterlimit="10"/>
<path d="M64.4585 69.676V70.7831C61.4981 71.7761 57.9468 72.3524 54.1301 72.3524C50.3135 72.3524 46.7622 71.7761 43.8018 70.7852V69.676C43.8018 67.1276 44.8141 64.6835 46.6162 62.8814C48.4182 61.0794 50.8623 60.067 53.4108 60.067H54.8475C57.3961 60.067 59.8404 61.0793 61.6428 62.8813C63.4451 64.6833 64.458 67.1274 64.4585 69.676V69.676Z" fill="url(#paint6_linear_1954_107668)"/>
<path d="M54.845 60.0692H53.4104C48.1023 60.0692 43.7993 64.3722 43.7993 69.6803V78.7938C43.7993 84.1019 48.1023 88.4049 53.4104 88.4049H54.845C60.1531 88.4049 64.4561 84.1019 64.4561 78.7938V69.6803C64.4561 64.3722 60.1531 60.0692 54.845 60.0692Z" stroke="url(#paint7_linear_1954_107668)" stroke-width="0.820964" stroke-miterlimit="10"/>
<mask id="mask1_1954_107668" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="43" y="60" width="22" height="29">
<path d="M54.845 60.0692H53.4104C48.1023 60.0692 43.7993 64.3722 43.7993 69.6803V78.7938C43.7993 84.1019 48.1023 88.4049 53.4104 88.4049H54.845C60.1531 88.4049 64.4561 84.1019 64.4561 78.7938V69.6803C64.4561 64.3722 60.1531 60.0692 54.845 60.0692Z" fill="url(#paint8_linear_1954_107668)"/>
</mask>
<g mask="url(#mask1_1954_107668)">
<g filter="url(#filter2_f_1954_107668)">
<path d="M54.845 60.0692H53.4104C48.1023 60.0692 43.7993 64.3722 43.7993 69.6803V78.7938C43.7993 84.1019 48.1023 88.4049 53.4104 88.4049H54.845C60.1531 88.4049 64.4561 84.1019 64.4561 78.7938V69.6803C64.4561 64.3722 60.1531 60.0692 54.845 60.0692Z" stroke="#42210B" stroke-width="1.23145" stroke-miterlimit="10"/>
</g>
</g>
<path d="M60.6798 40.2086C62.4358 38.0899 70.9688 29.3205 85.8084 38.2868C86.3012 38.5761 86.6727 39.034 86.8541 39.576C87.0355 40.1179 87.0146 40.7072 86.7952 41.2348C86.4987 41.9065 85.883 42.3585 84.6453 41.8879C81.7575 40.787 72.6211 37.327 62.527 41.4546C62.527 41.4546 61.1442 42.3108 60.5741 41.465C60.4551 41.2717 60.4012 41.0455 60.4202 40.8194C60.4393 40.5933 60.5303 40.3792 60.6798 40.2086Z" fill="url(#paint9_radial_1954_107668)"/>
<path d="M60.6798 40.2086C62.4358 38.0899 70.9688 29.3205 85.8084 38.2868C86.3012 38.5761 86.6727 39.034 86.8541 39.576C87.0355 40.1179 87.0146 40.7072 86.7952 41.2348C86.4987 41.9065 85.883 42.3585 84.6453 41.8879C81.7575 40.787 72.6211 37.327 62.527 41.4546C62.527 41.4546 61.1442 42.3108 60.5741 41.465C60.4551 41.2717 60.4012 41.0455 60.4202 40.8194C60.4393 40.5933 60.5303 40.3792 60.6798 40.2086Z" stroke="url(#paint10_linear_1954_107668)" stroke-width="0.410482" stroke-miterlimit="10"/>
<path d="M29.2837 54.9862L9.82519 88.844C9.52499 89.3664 9.02957 89.7481 8.44792 89.9051C7.86628 90.0622 7.24605 89.9818 6.72369 89.6816L2.87655 87.4706C2.35418 87.1704 1.97247 86.675 1.81539 86.0933C1.6583 85.5117 1.7387 84.8915 2.03891 84.3691L21.4974 50.5113L29.2837 54.9862Z" fill="black"/>
<mask id="mask2_1954_107668" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="4" y="0" width="101" height="100">
<path d="M54.353 100C81.9673 100 104.353 77.6142 104.353 50C104.353 22.3858 81.9673 0 54.353 0C26.7388 0 4.35303 22.3858 4.35303 50C4.35303 77.6142 26.7388 100 54.353 100Z" fill="url(#paint11_radial_1954_107668)"/>
</mask>
<g mask="url(#mask2_1954_107668)">
<g filter="url(#filter3_f_1954_107668)">
<path d="M23.9208 56.4309C22.822 55.726 21.6818 55.0626 20.5001 54.5236C18.8416 53.7565 17.2453 53.3626 15.4209 53.2175C13.9075 53.1139 12.2076 53.1553 10.9015 54.0468C9.76125 54.8346 9.07712 56.0992 8.74541 57.4053C8.70395 57.5504 8.51737 59.4369 8.4759 59.4577C5.07595 60.7223 4.12231 62.1942 3.31379 63.6454C1.63454 66.7551 3.41744 69.6368 3.41744 69.6368C-2.20077 74.5087 1.53088 79.0903 1.53088 79.0903C-1.72395 82.3037 1.21991 85.0817 1.21991 85.0817L10.7356 91.384C12.2905 92.4206 14.4258 91.9852 15.4624 90.4304C16.499 88.8755 16.0636 86.7402 14.5087 85.7036L16.1465 86.8024C17.8672 87.9426 20.2099 87.4658 21.3501 85.7451L21.9099 84.8951C23.0501 83.1744 22.5733 80.8318 20.8526 79.6915L20.583 79.5049C22.4489 80.7488 24.9988 80.2305 26.2427 78.3647L26.4086 78.0952C26.4086 78.0952 25.6 79.6915 27.4037 80.8939C30.4305 82.9049 31.3012 80.9769 31.3012 80.9769C31.3012 80.9769 36.5255 72.2697 37.9352 70.1136C39.345 67.9576 37.4377 63.8527 37.4377 63.8527C32.5865 55.8504 29.9329 60.2869 23.9208 56.4309Z" fill="#8D3300"/>
</g>
</g>
<path d="M23.9218 54.3578C22.823 53.6529 21.6828 52.9895 20.5011 52.4505C18.8426 51.6834 17.2463 51.2895 15.4219 51.1444C13.9085 51.0408 12.2085 51.0822 10.9025 51.9737C9.76223 52.7615 9.07809 54.0261 8.74639 55.3322C8.70493 55.4773 8.51834 57.3638 8.47688 57.3846C5.07693 58.6492 4.12329 60.1211 3.31476 61.5723C1.63552 64.682 3.41842 67.5637 3.41842 67.5637C-2.19979 72.4356 1.53186 77.0172 1.53186 77.0172C-1.72297 80.2306 1.22089 83.0086 1.22089 83.0086L10.7366 89.3109C12.2915 90.3475 14.4268 89.9121 15.4634 88.3573C16.4999 86.8024 16.0646 84.6671 14.5097 83.6305L16.1475 84.7293C17.8682 85.8695 20.2109 85.3927 21.3511 83.672L21.9108 82.822C23.0511 81.1013 22.5742 78.7586 20.8535 77.6184L20.584 77.4318C22.4498 78.6757 24.9998 78.1574 26.2437 76.2916L26.4095 76.0221C26.4095 76.0221 25.601 77.6184 27.4047 78.8208C30.4314 80.8318 31.3022 78.9038 31.3022 78.9038C31.3022 78.9038 36.5265 70.1966 37.9362 68.0405C39.3459 65.8844 37.4387 61.7796 37.4387 61.7796C32.5875 53.7773 29.9339 58.2138 23.9218 54.3578Z" fill="url(#paint12_radial_1954_107668)"/>
<path d="M29.2084 55.0626L27.6742 57.7162L20.2109 52.7614L21.4548 50.5846L29.2084 55.0626Z" fill="black"/>
<mask id="mask3_1954_107668" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="51" width="39" height="39">
<path d="M23.9218 54.3578C22.823 53.6529 21.6828 52.9895 20.5011 52.4505C18.8426 51.6834 17.2463 51.2895 15.4219 51.1444C13.9085 51.0408 12.2085 51.0822 10.9025 51.9737C9.76223 52.7615 9.07809 54.0261 8.74639 55.3322C8.70493 55.4773 8.51834 57.3638 8.47688 57.3846C5.07693 58.6492 4.12329 60.1211 3.31476 61.5723C1.63552 64.682 3.41842 67.5637 3.41842 67.5637C-2.19979 72.4356 1.53186 77.0172 1.53186 77.0172C-1.72297 80.2306 1.22089 83.0086 1.22089 83.0086L10.7366 89.3109C12.2915 90.3475 14.4268 89.9121 15.4634 88.3573C16.4999 86.8024 16.0646 84.6671 14.5097 83.6305L16.1475 84.7293C17.8682 85.8695 20.2109 85.3927 21.3511 83.672L21.9108 82.822C23.0511 81.1013 22.5742 78.7586 20.8535 77.6184L20.584 77.4318C22.4498 78.6757 24.9998 78.1574 26.2437 76.2916L26.4095 76.0221C26.4095 76.0221 25.601 77.6184 27.4047 78.8208C30.4314 80.8318 31.3022 78.9038 31.3022 78.9038C31.3022 78.9038 36.5265 70.1966 37.9362 68.0405C39.3459 65.8844 37.4387 61.7796 37.4387 61.7796C32.5875 53.7773 29.9339 58.2138 23.9218 54.3578Z" fill="url(#paint13_radial_1954_107668)"/>
</mask>
<g mask="url(#mask3_1954_107668)">
<g opacity="0.4" filter="url(#filter4_f_1954_107668)">
<path opacity="0.4" d="M20.9158 52.4504C19.7963 51.9736 18.6147 51.6626 17.433 51.476C16.8318 51.4138 16.2305 51.3516 15.6293 51.3309C15.0281 51.3102 14.4269 51.3102 13.8464 51.3931C13.266 51.476 12.6855 51.6004 12.1672 51.8492C11.6489 52.0772 11.1928 52.4297 10.7989 52.8443C10.4258 53.2589 10.1148 53.7565 9.88674 54.2955C9.78308 54.565 9.67942 54.8345 9.5965 55.104C9.57577 55.1662 9.55503 55.2491 9.5343 55.3113L9.51357 55.415L9.49284 55.4979L9.45138 55.9333L9.34772 56.8454C9.32699 56.9906 9.30626 57.1564 9.28553 57.3015L9.2648 57.3637C9.2648 57.3845 9.2648 57.4052 9.24406 57.4674V57.4881C9.22333 57.5088 9.26479 57.4674 9.22333 57.5503C9.18187 57.6332 9.09894 57.6954 9.01602 57.7162L8.99529 57.7576L8.95382 57.7784L8.74651 57.8613C8.18676 58.0893 7.66848 58.3381 7.15019 58.6283C6.13435 59.1881 5.2429 59.9552 4.62096 60.9295C4.45511 61.1783 4.30999 61.4271 4.16487 61.6759C4.08194 61.8002 4.04048 61.9246 3.97829 62.0698C3.91609 62.1941 3.8539 62.3185 3.81244 62.4636L3.66732 62.8575L3.56366 63.2722C3.5222 63.4173 3.5222 63.5417 3.48073 63.6868C3.48073 63.749 3.46 63.8319 3.46 63.8941L3.43927 64.1014C3.39781 64.6612 3.46 65.2417 3.58439 65.8014C3.64658 66.0709 3.72951 66.3612 3.83317 66.6307C3.93682 66.9002 4.06121 67.1697 4.1856 67.3977L4.35145 67.688L4.10268 67.8953C3.43927 68.455 2.83806 69.077 2.34051 69.7818C2.09173 70.1343 1.88442 70.4867 1.6771 70.8806C1.49052 71.2538 1.3454 71.6684 1.22101 72.0623C1.0137 72.8708 0.972233 73.7415 1.13808 74.5708C1.22101 74.9854 1.3454 75.4 1.51125 75.7939C1.6771 76.1878 1.88442 76.5817 2.11246 76.9134L2.23685 77.0793L2.09173 77.2037C1.69783 77.5768 1.3454 77.9915 1.05516 78.4683C0.785651 78.9244 0.578337 79.4634 0.536874 80.0024C0.47468 80.5414 0.557605 81.1012 0.764919 81.6195C0.951502 82.1378 1.24174 82.6353 1.61491 83.0499C1.20028 82.656 0.868576 82.1999 0.619799 81.6817C0.371023 81.1634 0.246634 80.5829 0.267366 80.0024C0.288097 79.4219 0.453948 78.8415 0.723456 78.3232C0.992965 77.8049 1.3454 77.3281 1.76003 76.9134L1.7393 77.2244C1.42832 76.8512 1.22101 76.4573 1.0137 76.022C0.806382 75.6074 0.661262 75.1513 0.557605 74.7159C0.350291 73.8037 0.350291 72.8501 0.557605 71.9172C0.661262 71.4611 0.827113 71.005 1.0137 70.5904C1.22101 70.1757 1.44906 69.7611 1.71856 69.3879C2.23685 68.6209 2.87952 67.9367 3.56366 67.3355L3.48073 67.8331C3.29415 67.5221 3.16976 67.2526 3.04537 66.9416C2.92099 66.6514 2.83806 66.3404 2.75513 66.0295C2.61001 65.4075 2.54782 64.7441 2.56855 64.1014L2.58928 63.8527C2.58928 63.7697 2.61001 63.6868 2.63075 63.6039C2.65148 63.438 2.67221 63.2722 2.71367 63.1271L2.83806 62.6502L3.00391 62.1941C3.04537 62.0283 3.1283 61.9039 3.21123 61.7588C3.27342 61.6137 3.35634 61.4685 3.43927 61.3234C3.58439 61.0539 3.75024 60.7844 3.93682 60.5149C4.28926 59.9759 4.70389 59.4783 5.20144 59.0637C5.67826 58.6283 6.21728 58.2759 6.7563 57.9857C7.31604 57.6747 7.87579 57.4259 8.45627 57.2186L8.68432 57.1357L8.74651 57.1149H8.76724C8.70505 57.1357 8.62212 57.1979 8.58066 57.2808C8.5392 57.3637 8.55993 57.3015 8.55993 57.3223V57.343C8.55993 57.3637 8.55993 57.343 8.55993 57.3223V57.2808C8.58066 57.1357 8.60139 56.9906 8.62212 56.8454L8.74651 55.954L8.8087 55.4979C8.8087 55.4564 8.8087 55.4357 8.82944 55.3528L8.8709 55.2284C8.89163 55.1455 8.91236 55.0833 8.93309 55.0003C9.03675 54.6894 9.14041 54.3991 9.28553 54.1089C9.55504 53.5284 9.90747 52.9894 10.3636 52.5126C10.8197 52.0358 11.3587 51.6626 11.9599 51.4345C12.5611 51.1858 13.183 51.0821 13.805 51.0199C14.4269 50.9785 15.0489 50.9992 15.6708 51.0406C16.2927 51.1028 16.894 51.1858 17.4952 51.3102C18.6769 51.476 19.8378 51.8906 20.9158 52.4504Z" fill="#B04B02"/>
</g>
</g>
<mask id="mask4_1954_107668" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="51" width="39" height="39">
<path d="M23.9218 54.3578C22.823 53.6529 21.6828 52.9895 20.5011 52.4505C18.8426 51.6834 17.2463 51.2895 15.4219 51.1444C13.9085 51.0408 12.2085 51.0822 10.9025 51.9737C9.76223 52.7615 9.07809 54.0261 8.74639 55.3322C8.70493 55.4773 8.51834 57.3638 8.47688 57.3846C5.07693 58.6492 4.12329 60.1211 3.31476 61.5723C1.63552 64.682 3.41842 67.5637 3.41842 67.5637C-2.19979 72.4356 1.53186 77.0172 1.53186 77.0172C-1.72297 80.2306 1.22089 83.0086 1.22089 83.0086L10.7366 89.3109C12.2915 90.3475 14.4268 89.9121 15.4634 88.3573C16.4999 86.8024 16.0646 84.6671 14.5097 83.6305L16.1475 84.7293C17.8682 85.8695 20.2109 85.3927 21.3511 83.672L21.9108 82.822C23.0511 81.1013 22.5742 78.7586 20.8535 77.6184L20.584 77.4318C22.4498 78.6757 24.9998 78.1574 26.2437 76.2916L26.4095 76.0221C26.4095 76.0221 25.601 77.6184 27.4047 78.8208C30.4314 80.8318 31.3022 78.9038 31.3022 78.9038C31.3022 78.9038 36.5265 70.1966 37.9362 68.0405C39.3459 65.8844 37.4387 61.7796 37.4387 61.7796C32.5875 53.7773 29.9339 58.2138 23.9218 54.3578Z" fill="url(#paint14_radial_1954_107668)"/>
</mask>
<g mask="url(#mask4_1954_107668)">
<g style="mix-blend-mode:multiply" filter="url(#filter5_f_1954_107668)">
<path d="M14.4885 83.6511C16.0641 84.6877 16.4787 86.823 15.4422 88.3779C14.9239 89.1657 14.1361 89.6632 13.2654 89.8291C12.4154 89.9949 11.4825 89.8498 10.6947 89.3315L10.5288 89.2279C11.9385 88.5852 14.219 87.0096 14.4885 83.6511Z" fill="#E7834A"/>
<path d="M20.8322 77.5975L20.853 77.6183C22.5737 78.7585 23.0505 81.1011 21.9103 82.8218L21.3505 83.6718C20.2103 85.3925 17.8677 85.8694 16.5408 84.9779L15.8774 84.5425C17.2664 84.0657 20.0237 82.4694 20.8322 77.5975Z" fill="#E7834A"/>
<path d="M26.1603 69.9062C27.7151 71.2123 28.0883 73.5342 26.9273 75.2757L26.2432 76.2915C25.1029 78.0122 22.8847 78.572 21.0811 77.722C22.553 76.9757 25.2688 74.944 26.1603 69.9062Z" fill="#E7834A"/>
<path d="M31.8194 60.6187C33.4572 62.3186 33.7475 64.993 32.3999 67.0247L32.0268 67.5844C30.6999 69.5746 28.1914 70.3624 26.0146 69.5954C27.6939 68.4551 30.4719 65.8637 31.8194 60.6187Z" fill="#E7834A"/>
</g>
</g>
<mask id="mask5_1954_107668" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="51" width="39" height="39">
<path d="M23.9218 54.3578C22.823 53.6529 21.6828 52.9895 20.5011 52.4505C18.8426 51.6834 17.2463 51.2895 15.4219 51.1444C13.9085 51.0408 12.2085 51.0822 10.9025 51.9737C9.76223 52.7615 9.07809 54.0261 8.74639 55.3322C8.70493 55.4773 8.51834 57.3638 8.47688 57.3846C5.07693 58.6492 4.12329 60.1211 3.31476 61.5723C1.63552 64.682 3.41842 67.5637 3.41842 67.5637C-2.19979 72.4356 1.53186 77.0172 1.53186 77.0172C-1.72297 80.2306 1.22089 83.0086 1.22089 83.0086L10.7366 89.3109C12.2915 90.3475 14.4268 89.9121 15.4634 88.3573C16.4999 86.8024 16.0646 84.6671 14.5097 83.6305L16.1475 84.7293C17.8682 85.8695 20.2109 85.3927 21.3511 83.672L21.9108 82.822C23.0511 81.1013 22.5742 78.7586 20.8535 77.6184L20.584 77.4318C22.4498 78.6757 24.9998 78.1574 26.2437 76.2916L26.4095 76.0221C26.4095 76.0221 25.601 77.6184 27.4047 78.8208C30.4314 80.8318 31.3022 78.9038 31.3022 78.9038C31.3022 78.9038 36.5265 70.1966 37.9362 68.0405C39.3459 65.8844 37.4387 61.7796 37.4387 61.7796C32.5875 53.7773 29.9339 58.2138 23.9218 54.3578Z" fill="url(#paint15_radial_1954_107668)"/>
</mask>
<g mask="url(#mask5_1954_107668)">
<g filter="url(#filter6_f_1954_107668)">
<path d="M18.4688 51.6005C19.3602 51.8492 20.2517 52.1395 21.0809 52.5541C21.9309 52.948 22.7394 53.4041 23.5479 53.8602C23.9418 54.0882 24.3357 54.337 24.7504 54.5651C24.9577 54.6687 25.1443 54.7724 25.3723 54.876L25.6833 55.0211L25.9943 55.1455C26.2016 55.2285 26.4296 55.3114 26.6369 55.3736C26.865 55.4358 27.0723 55.5187 27.3003 55.5602C27.7357 55.6846 28.1918 55.7675 28.6479 55.8297C29.5601 55.9748 30.4722 56.037 31.4051 56.2028C31.882 56.2858 32.3381 56.4101 32.7942 56.5967C33.2502 56.7833 33.6649 57.0114 34.0795 57.2809C34.4941 57.5504 34.8466 57.8613 35.199 58.1931C35.5307 58.5248 35.8624 58.8772 36.1734 59.2504C36.7746 59.976 37.2929 60.7637 37.7904 61.5723V61.593V61.6137C38.0392 62.1527 38.2258 62.6918 38.4124 63.2515C38.5782 63.8113 38.7233 64.371 38.8063 64.9515C38.8892 65.532 38.9306 66.1332 38.8685 66.7344C38.827 67.0454 38.7648 67.3356 38.6611 67.6258C38.6197 67.771 38.5368 67.9161 38.4746 68.0612L38.3502 68.2685C38.3087 68.3307 38.2672 68.3929 38.2258 68.4551C37.0026 70.3624 35.8002 72.3111 34.5978 74.2599L32.7942 77.1623L31.9027 78.6135L31.6747 78.9866C31.6125 79.0903 31.5088 79.2562 31.4051 79.3598C31.1978 79.5879 30.9283 79.7744 30.6381 79.8988C30.0576 80.1269 29.4149 80.0854 28.8552 79.9196C28.2954 79.733 27.7772 79.4427 27.3003 79.1318C26.8028 78.8208 26.3881 78.344 26.2016 77.7842C26.015 77.2245 26.0357 76.6025 26.2845 76.0635L26.865 75.2343L26.326 76.0843C26.1394 76.6025 26.1186 77.2037 26.326 77.722C26.5333 78.2403 26.9272 78.6549 27.404 78.9245C27.8808 79.2147 28.3991 79.4635 28.9174 79.6086C29.4357 79.7537 30.0161 79.7744 30.493 79.5464C30.721 79.4427 30.9491 79.2769 31.1149 79.0903C31.1978 78.9866 31.26 78.9037 31.3222 78.7586L31.5295 78.3854L32.4003 76.9135L34.1417 73.9696C35.3234 72.0209 36.4636 70.0514 37.7075 68.1234C38.3294 67.2734 38.3294 66.1124 38.1843 65.0344C38.0185 63.9564 37.666 62.8576 37.2307 61.8418L37.2514 61.8625C36.7746 61.0747 36.2563 60.3077 35.6965 59.6028C35.4063 59.2504 35.1161 58.9186 34.7844 58.5869C34.4527 58.276 34.121 57.9857 33.7478 57.737C33.3746 57.4882 32.9807 57.2601 32.5868 57.0943C32.1722 56.9284 31.7369 56.804 31.3015 56.7211C30.41 56.5345 29.4771 56.4516 28.5649 56.2858C27.632 56.1199 26.7199 55.8711 25.8491 55.4772L25.5174 55.3321L25.2065 55.1663C24.9991 55.0626 24.7918 54.9382 24.5845 54.8346C24.1699 54.6065 23.7967 54.337 23.4028 54.0882C22.6358 53.5907 21.8272 53.1139 21.0187 52.6785C20.2102 52.2431 19.3395 51.9114 18.4688 51.6005Z" fill="#923D00"/>
<path d="M5.80159 77.8669C8.16497 79.2352 10.4662 80.6449 12.7673 82.0961C13.3478 82.4486 13.9076 82.8217 14.488 83.1949L14.6954 83.34L14.9234 83.5059C15.0063 83.5681 15.0685 83.6303 15.1515 83.6925C15.2136 83.7547 15.2966 83.8169 15.3588 83.879C15.6283 84.1486 15.8356 84.4595 16.0222 84.7912C16.2088 85.1229 16.3124 85.4961 16.3746 85.8693C16.4368 86.2424 16.4575 86.6156 16.3953 87.0095C16.3746 87.1961 16.3331 87.3827 16.2917 87.5692C16.2295 87.7558 16.188 87.9424 16.0844 88.1083C16.0222 88.2948 15.9185 88.44 15.8149 88.6058C15.7112 88.7509 15.6075 88.9168 15.4624 89.0619C15.2136 89.3521 14.9027 89.5802 14.5917 89.7875C14.26 89.9948 13.9076 90.1192 13.5344 90.2229C13.1612 90.3058 12.7881 90.3472 12.4149 90.3265C12.0417 90.3058 11.6686 90.2436 11.3161 90.0985C11.2332 90.0777 11.1503 90.0363 11.0466 89.9948C10.9637 89.9533 10.8808 89.9119 10.7979 89.8704L10.5491 89.7253C10.4662 89.6838 10.404 89.6216 10.3418 89.5802L9.49178 88.9997C8.93203 88.6265 8.37228 88.2326 7.81254 87.8387C5.59428 86.2839 3.37602 84.7083 1.19922 83.0705C3.5626 84.4181 5.86378 85.8485 8.16497 87.2997C8.74545 87.6522 9.3052 88.0253 9.88568 88.3985L10.7357 88.9582C10.7979 88.9997 10.8808 89.0619 10.943 89.0826L11.1296 89.1863C11.1918 89.2277 11.2747 89.2485 11.3369 89.2899C11.3991 89.3314 11.482 89.3521 11.5442 89.3729C11.8344 89.4765 12.1247 89.518 12.4149 89.5387C12.7051 89.5594 13.0161 89.518 13.3064 89.4558C13.5966 89.3936 13.8868 89.2692 14.1356 89.1034C14.3844 88.9582 14.6332 88.7509 14.8197 88.5229C14.9234 88.4192 15.0063 88.2948 15.0893 88.1704C15.1722 88.0461 15.2551 87.9217 15.2966 87.7765C15.3588 87.6522 15.4002 87.507 15.4624 87.3619C15.5039 87.2168 15.5453 87.0717 15.5453 86.9266C15.6283 86.3254 15.5246 85.7241 15.2551 85.1851C15.1307 84.9156 14.9649 84.6668 14.7576 84.4388C14.7161 84.3766 14.6539 84.3351 14.5917 84.2729C14.5295 84.2108 14.488 84.1693 14.4259 84.1278L14.2393 83.9827L14.032 83.8376C13.4722 83.4644 12.9125 83.0705 12.3527 82.6766C10.1759 81.0803 7.95766 79.5255 5.80159 77.8669Z" fill="#923D00"/>
<path d="M10.5287 70.7562C13.2238 72.3525 15.8982 74.0111 18.5311 75.6903C19.1945 76.1049 19.8579 76.5196 20.5213 76.9549L21.0188 77.2659C21.1018 77.3073 21.1847 77.3903 21.2676 77.4525L21.392 77.5561L21.5164 77.6598C21.8481 77.9293 22.1176 78.2817 22.3456 78.6549C22.8017 79.4012 22.9883 80.2927 22.9054 81.1427C22.8639 81.578 22.7603 81.9926 22.5944 82.4073C22.553 82.5109 22.5115 82.6146 22.4493 82.6975L22.2834 82.9878C22.1798 83.1743 22.0761 83.3195 21.9517 83.4853C21.8481 83.6512 21.7444 83.7963 21.62 83.9829L21.4335 84.2524C21.3713 84.3353 21.2883 84.4182 21.2054 84.5011C20.8944 84.8121 20.5627 85.0816 20.1688 85.2889C19.4018 85.6828 18.5103 85.8694 17.6603 85.745C17.225 85.6828 16.8104 85.5584 16.4165 85.3719L16.2713 85.3097L16.1262 85.2267C16.0433 85.1645 15.9396 85.1231 15.8567 85.0609L15.3799 84.7292C14.7372 84.2938 14.0945 83.8377 13.4519 83.4024C10.8812 81.6195 8.31048 79.8158 5.78125 77.9293C8.49706 79.5256 11.1507 81.1841 13.8043 82.8634C14.4677 83.278 15.1311 83.7134 15.7945 84.128L16.2921 84.4389C16.375 84.5011 16.4579 84.5219 16.5201 84.5841L16.6445 84.6463L16.7689 84.7085C17.0799 84.8536 17.4323 84.9572 17.7847 84.9987C18.4896 85.0816 19.1945 84.9365 19.8164 84.6255C20.1274 84.4597 20.3969 84.2316 20.6457 83.9829L20.8115 83.7963L20.9566 83.589C21.0603 83.4438 21.1639 83.2573 21.2883 83.1121C21.392 82.9463 21.5164 82.7804 21.5993 82.6353L21.7237 82.4073L21.8274 82.1792C21.9725 81.8683 22.0554 81.5158 22.0969 81.1634C22.1591 80.4585 22.0139 79.7537 21.6615 79.1317C21.4957 78.8207 21.2676 78.5512 20.9981 78.3025L20.8944 78.2195L20.7908 78.1366C20.7079 78.0744 20.6664 78.0329 20.5835 77.9708L20.1066 77.639C19.464 77.2037 18.8213 76.7476 18.1786 76.3122C15.6079 74.4464 13.0372 72.6428 10.5287 70.7562Z" fill="#923D00"/>
<path d="M15.7117 62.9404C18.4689 64.5575 21.164 66.2367 23.8591 67.9574C24.5225 68.3928 25.2066 68.8074 25.87 69.2428L25.9944 69.3257L26.1188 69.4294C26.2017 69.4916 26.3054 69.5537 26.3883 69.6159C26.5542 69.7611 26.72 69.9062 26.8652 70.072C27.0103 70.2171 27.1347 70.4037 27.2798 70.5903C27.3834 70.7769 27.5078 70.9635 27.5908 71.1501C27.7773 71.544 27.9017 71.9586 27.9639 72.3939C28.0261 72.8293 28.0261 73.2647 27.9639 73.7C27.9017 74.1354 27.7773 74.55 27.5908 74.9439C27.5078 75.1512 27.3834 75.3378 27.2798 75.5244L26.9481 76.0219L26.6164 76.5195C26.492 76.6853 26.3676 76.8719 26.2018 77.0378C25.9115 77.3695 25.5591 77.639 25.1859 77.867C24.8127 78.0951 24.3981 78.2609 23.9835 78.3646C23.5689 78.4682 23.1128 78.5304 22.6774 78.5097C22.4494 78.5097 22.2421 78.4682 22.0347 78.4268C21.8274 78.3646 21.5994 78.3231 21.4128 78.2609C21.2055 78.1987 21.0189 78.0951 20.8116 78.0122C20.7079 77.9707 20.625 77.9085 20.5213 77.8463L20.397 77.7426L20.2726 77.6597C19.6092 77.2036 18.9665 76.7683 18.3031 76.3122C15.6909 74.5085 13.0788 72.6635 10.5288 70.7562C13.2861 72.3732 15.9812 74.0525 18.6762 75.7732C19.3397 76.2085 20.0238 76.6232 20.6872 77.0585L20.8116 77.1414L20.936 77.2036C21.0189 77.2451 21.0811 77.2866 21.164 77.328C21.3299 77.4109 21.4957 77.4731 21.6616 77.5353C21.8274 77.5975 22.014 77.6183 22.1799 77.6597C22.3664 77.6805 22.5323 77.7012 22.7189 77.7012C23.0713 77.7012 23.4445 77.6805 23.7762 77.5768C24.1286 77.4731 24.4603 77.3487 24.7713 77.1622C25.0823 76.9756 25.3518 76.7475 25.5798 76.478C25.7042 76.3536 25.8079 76.1878 25.9115 76.0427L26.2432 75.5451L26.5749 75.0476C26.6578 74.8817 26.7615 74.7366 26.8444 74.5707C27.0103 74.2598 27.1139 73.9073 27.1554 73.5549C27.2176 73.2025 27.2176 72.8293 27.1554 72.4769C27.1139 72.1244 27.0103 71.772 26.8652 71.4403C26.803 71.2745 26.6993 71.1293 26.6164 70.9635C26.5127 70.8184 26.4091 70.6732 26.2847 70.5281C26.181 70.383 26.0359 70.2586 25.9115 70.1342C25.8493 70.072 25.7664 70.0306 25.7042 69.9684L25.6005 69.8854L25.4762 69.8025C24.8127 69.3464 24.1701 68.9111 23.5067 68.455C20.8738 66.6721 18.2616 64.8477 15.7117 62.9404Z" fill="#923D00"/>
<path d="M21.7655 53.7979C24.5642 55.4356 27.3008 57.1563 30.0373 58.8978L31.0532 59.5405C31.219 59.6441 31.4056 59.7892 31.5714 59.9136C31.7373 60.0587 31.9031 60.2039 32.0483 60.349C32.6495 60.9709 33.1056 61.738 33.3751 62.5672C33.6238 63.3965 33.686 64.2879 33.5409 65.1587C33.4995 65.366 33.458 65.594 33.3958 65.8013C33.3129 66.0086 33.2714 66.216 33.1678 66.4233L33.0226 66.7135C32.9812 66.8172 32.919 66.9001 32.8568 67.0038C32.7946 67.0867 32.7531 67.1903 32.6909 67.2733L32.5251 67.522L32.3592 67.7708C32.297 67.8537 32.2348 67.9367 32.1726 68.0403C32.1105 68.1233 32.0483 68.2269 31.9653 68.3098L31.7373 68.5586C31.5922 68.7245 31.4263 68.8489 31.2605 69.0147C31.0946 69.1598 30.908 69.2842 30.7422 69.4086C29.9959 69.8854 29.1666 70.1757 28.2959 70.2586C27.4252 70.3415 26.5544 70.2171 25.7459 69.9062C25.5386 69.8232 25.352 69.7196 25.1654 69.6366C24.9789 69.533 24.7923 69.4086 24.6264 69.3049L23.6313 68.6208C20.957 66.7757 18.3241 64.9099 15.7119 62.9819C18.5107 64.6196 21.2472 66.3404 23.9837 68.0818L24.9996 68.7245C25.1654 68.8281 25.3313 68.911 25.4971 69.0147C25.663 69.0976 25.8288 69.1806 26.0154 69.2427C26.7203 69.4915 27.4874 69.5952 28.2337 69.4915C28.98 69.3879 29.7056 69.1391 30.3276 68.7452C30.4727 68.6415 30.6385 68.5379 30.7836 68.4135C30.908 68.2891 31.0739 68.1647 31.1775 68.0196L31.3641 67.8123C31.4263 67.7501 31.4678 67.6672 31.53 67.5842C31.5922 67.5013 31.6336 67.4391 31.6958 67.3562L31.8617 67.1074L32.0275 66.8586C32.0897 66.7757 32.1312 66.6928 32.1726 66.6099C32.2141 66.5269 32.2763 66.444 32.3178 66.3611L32.4422 66.1123C32.5251 65.9465 32.5665 65.7599 32.6495 65.594C32.7117 65.4074 32.7531 65.2416 32.7739 65.055C32.8982 64.3294 32.8568 63.5623 32.6495 62.8367C32.4422 62.1111 32.069 61.4477 31.5507 60.888C31.4263 60.7429 31.2812 60.6185 31.1361 60.4941C30.991 60.3904 30.8458 60.266 30.68 60.1417L29.6849 59.4575C27.0313 57.5917 24.3776 55.7259 21.7655 53.7979Z" fill="#923D00"/>
</g>
</g>
<path d="M37.8511 51.4408C41.8952 51.4408 45.1735 46.5799 45.1735 40.5837C45.1735 34.5875 41.8952 29.7267 37.8511 29.7267C33.8071 29.7267 30.5288 34.5875 30.5288 40.5837C30.5288 46.5799 33.8071 51.4408 37.8511 51.4408Z" fill="url(#paint16_radial_1954_107668)"/>
<path d="M37.8511 51.646C39.962 51.646 41.8489 50.3773 43.199 48.3756C44.5498 46.3726 45.3787 43.6172 45.3787 40.5837C45.3787 37.5503 44.5498 34.7948 43.199 32.7919C41.8489 30.7901 39.962 29.5214 37.8511 29.5214C35.7403 29.5214 33.8533 30.7901 32.5033 32.7919C31.1524 34.7948 30.3236 37.5503 30.3236 40.5837C30.3236 43.6172 31.1524 46.3726 32.5033 48.3756C33.8533 50.3773 35.7403 51.646 37.8511 51.646Z" stroke="url(#paint17_linear_1954_107668)" stroke-width="0.410482" stroke-miterlimit="10"/>
<path style="mix-blend-mode:screen" d="M35.3008 53.6529C45.0318 53.6529 52.9204 45.7644 52.9204 36.0333C52.9204 26.3023 45.0318 18.4137 35.3008 18.4137C25.5697 18.4137 17.6812 26.3023 17.6812 36.0333C17.6812 45.7644 25.5697 53.6529 35.3008 53.6529Z" fill="url(#paint18_linear_1954_107668)"/>
<path d="M30.7879 54.4822L31.074 53.9888C34.8822 54.8821 38.8756 54.5435 42.479 53.022C46.0824 51.5004 49.1099 48.8744 51.1254 45.5221C56.3435 36.8149 53.4805 25.4002 44.765 20.1945C40.9564 17.9257 36.4583 17.104 32.0935 17.8797C27.7288 18.6554 23.7892 20.9767 20.9956 24.4189C18.202 27.861 16.7411 32.194 16.8802 36.625C17.0193 41.0559 18.7491 45.2887 21.7532 48.5489L21.4339 49.0837C21.2431 49.4142 21.1914 49.8069 21.2902 50.1755C21.3889 50.544 21.63 50.8583 21.9605 51.0491L28.8184 55.0088C28.9821 55.1042 29.1631 55.1662 29.3509 55.1914C29.5388 55.2165 29.7297 55.2043 29.9127 55.1553C30.0958 55.1064 30.2674 55.0217 30.4176 54.9062C30.5678 54.7907 30.6936 54.6466 30.7879 54.4822V54.4822ZM20.7601 27.6371C22.5176 24.5926 25.1861 22.1764 28.3897 20.7291C31.5933 19.2818 35.1703 18.8763 38.6165 19.5699C42.0627 20.2634 45.2044 22.021 47.5987 24.5949C49.993 27.1688 51.5192 30.4292 51.9621 33.9165C52.405 37.4038 51.7423 40.9422 50.0674 44.0329C48.3926 47.1236 45.79 49.6107 42.6265 51.1437C39.4631 52.6768 35.8983 53.1783 32.4347 52.5778C28.971 51.9772 25.7832 50.3048 23.3205 47.7963C20.7516 45.1763 19.1108 41.7875 18.6485 38.1475C18.1862 34.5075 18.9278 30.8161 20.7601 27.6371V27.6371Z" fill="black"/>
<defs>
<filter id="filter0_f_1954_107668" x="-1.36003" y="42.8741" width="110.814" height="63.4577" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="2.56551" result="effect1_foregroundBlur_1954_107668"/>
</filter>
<filter id="filter1_f_1954_107668" x="-4.43864" y="39.7955" width="116.971" height="69.615" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="4.10482" result="effect1_foregroundBlur_1954_107668"/>
</filter>
<filter id="filter2_f_1954_107668" x="41.9521" y="58.222" width="24.3511" height="32.03" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.615723" result="effect1_foregroundBlur_1954_107668"/>
</filter>
<filter id="filter3_f_1954_107668" x="-6.1582" y="47.0204" width="50.77" height="51.0848" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="3.07861" result="effect1_foregroundBlur_1954_107668"/>
</filter>
<filter id="filter4_f_1954_107668" x="-0.966309" y="49.7659" width="23.1138" height="34.5155" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.615723" result="effect1_foregroundBlur_1954_107668"/>
</filter>
<filter id="filter5_f_1954_107668" x="6.42399" y="56.5138" width="30.9338" height="37.4853" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="2.05241" result="effect1_foregroundBlur_1954_107668"/>
</filter>
<filter id="filter6_f_1954_107668" x="-0.85319" y="49.5481" width="41.804" height="42.8361" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="1.0262" result="effect1_foregroundBlur_1954_107668"/>
</filter>
<radialGradient id="paint0_radial_1954_107668" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(54.3157) rotate(90) scale(99.9254)">
<stop stop-color="#FFDE43"/>
<stop offset="1" stop-color="#FFBC00"/>
</radialGradient>
<radialGradient id="paint1_radial_1954_107668" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(54.3157 13.3718) rotate(90) scale(86.5536)">
<stop stop-color="white" stop-opacity="0.5"/>
<stop offset="0.782278"/>
</radialGradient>
<radialGradient id="paint2_radial_1954_107668" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(54.3157 13.3718) rotate(90) scale(86.5536)">
<stop stop-color="white" stop-opacity="0.5"/>
<stop offset="0.782278"/>
</radialGradient>
<radialGradient id="paint3_radial_1954_107668" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(54.3157 -10.884) rotate(90) scale(110.809 149.854)">
<stop offset="0.0922635" stop-color="white"/>
<stop offset="0.670207" stop-color="white" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint4_radial_1954_107668" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(54.1301 79.5574) scale(7.79611 17.7968)">
<stop stop-color="#743C1C"/>
<stop offset="1" stop-color="#401C02"/>
</radialGradient>
<linearGradient id="paint5_linear_1954_107668" x1="54.1288" y1="89.5876" x2="54.1288" y2="58.7712" gradientUnits="userSpaceOnUse">
<stop offset="0.265625" stop-color="#FFDE43"/>
<stop offset="1" stop-color="#F79D14"/>
</linearGradient>
<linearGradient id="paint6_linear_1954_107668" x1="43.7938" y1="66.212" x2="64.4585" y2="66.212" gradientUnits="userSpaceOnUse">
<stop stop-color="#CCCCCC"/>
<stop offset="0.05" stop-color="#D5D5D5"/>
<stop offset="0.2" stop-color="#E9E9E9"/>
<stop offset="0.35" stop-color="#F6F6F6"/>
<stop offset="0.5" stop-color="#FAFAFA"/>
<stop offset="0.65" stop-color="#F6F6F6"/>
<stop offset="0.8" stop-color="#E9E9E9"/>
<stop offset="0.95" stop-color="#D5D5D5"/>
<stop offset="1" stop-color="#CCCCCC"/>
</linearGradient>
<linearGradient id="paint7_linear_1954_107668" x1="54.1298" y1="88.8195" x2="54.1298" y2="59.6546" gradientUnits="userSpaceOnUse">
<stop stop-color="#401C02"/>
<stop offset="1" stop-color="#401C02"/>
</linearGradient>
<linearGradient id="paint8_linear_1954_107668" x1="54.1298" y1="88.8195" x2="54.1298" y2="59.6546" gradientUnits="userSpaceOnUse">
<stop stop-color="#401C02"/>
<stop offset="1" stop-color="#401C02"/>
</linearGradient>
<radialGradient id="paint9_radial_1954_107668" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(73.6781 38.202) scale(16.9287 3.88383)">
<stop stop-color="#743C1C"/>
<stop offset="1" stop-color="#401C02"/>
</radialGradient>
<linearGradient id="paint10_linear_1954_107668" x1="73.6965" y1="42.2999" x2="73.6965" y2="34.078" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFDE43"/>
<stop offset="0.22" stop-color="#FED93F"/>
<stop offset="0.51" stop-color="#FCC934"/>
<stop offset="0.82" stop-color="#F9AF21"/>
<stop offset="1" stop-color="#F79D14"/>
</linearGradient>
<radialGradient id="paint11_radial_1954_107668" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(54.3157) rotate(90) scale(99.9254)">
<stop stop-color="#FFDE43"/>
<stop offset="1" stop-color="#FFBC00"/>
</radialGradient>
<radialGradient id="paint12_radial_1954_107668" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(17.4208 70.2557) rotate(-56.4402) scale(18.1122)">
<stop offset="0.0139674" stop-color="#FFDE43"/>
<stop offset="0.3975" stop-color="#FFDC41"/>
<stop offset="0.6038" stop-color="#FFD43B"/>
<stop offset="0.7679" stop-color="#FFC631"/>
<stop offset="0.9089" stop-color="#FFB323"/>
<stop offset="1" stop-color="#FFA216"/>
</radialGradient>
<radialGradient id="paint13_radial_1954_107668" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(17.4208 70.2557) rotate(-56.4402) scale(18.1122)">
<stop offset="0.0139674" stop-color="#FFDE43"/>
<stop offset="0.3975" stop-color="#FFDC41"/>
<stop offset="0.6038" stop-color="#FFD43B"/>
<stop offset="0.7679" stop-color="#FFC631"/>
<stop offset="0.9089" stop-color="#FFB323"/>
<stop offset="1" stop-color="#FFA216"/>
</radialGradient>
<radialGradient id="paint14_radial_1954_107668" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(17.4208 70.2557) rotate(-56.4402) scale(18.1122)">
<stop offset="0.0139674" stop-color="#FFDE43"/>
<stop offset="0.3975" stop-color="#FFDC41"/>
<stop offset="0.6038" stop-color="#FFD43B"/>
<stop offset="0.7679" stop-color="#FFC631"/>
<stop offset="0.9089" stop-color="#FFB323"/>
<stop offset="1" stop-color="#FFA216"/>
</radialGradient>
<radialGradient id="paint15_radial_1954_107668" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(17.4208 70.2557) rotate(-56.4402) scale(18.1122)">
<stop offset="0.0139674" stop-color="#FFDE43"/>
<stop offset="0.3975" stop-color="#FFDC41"/>
<stop offset="0.6038" stop-color="#FFD43B"/>
<stop offset="0.7679" stop-color="#FFC631"/>
<stop offset="0.9089" stop-color="#FFB323"/>
<stop offset="1" stop-color="#FFA216"/>
</radialGradient>
<radialGradient id="paint16_radial_1954_107668" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(37.841 40.5765) scale(9.33419 10.8759)">
<stop stop-color="#743C1C"/>
<stop offset="1" stop-color="#401C02"/>
</radialGradient>
<linearGradient id="paint17_linear_1954_107668" x1="37.8511" y1="52.0517" x2="37.8511" y2="29.028" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFDE43"/>
<stop offset="0.22" stop-color="#FED93F"/>
<stop offset="0.51" stop-color="#FCC934"/>
<stop offset="0.82" stop-color="#F9AF21"/>
<stop offset="1" stop-color="#F79D14"/>
</linearGradient>
<linearGradient id="paint18_linear_1954_107668" x1="13.4758" y1="21.8879" x2="31.2217" y2="52.4812" gradientUnits="userSpaceOnUse">
<stop offset="0.03" stop-color="white" stop-opacity="0"/>
<stop offset="0.15" stop-color="white" stop-opacity="0.12"/>
<stop offset="0.45" stop-color="white" stop-opacity="0.41"/>
<stop offset="0.71" stop-color="white" stop-opacity="0.62"/>
<stop offset="0.9" stop-color="white" stop-opacity="0.75"/>
<stop offset="1" stop-color="white" stop-opacity="0.8"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 42 KiB

@@ -0,0 +1,3 @@
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11 0C17.0751 0 22 4.92487 22 11C22 17.0751 17.0751 22 11 22C4.92487 22 0 17.0751 0 11C0 4.92487 4.92487 0 11 0ZM16 10H6C5.44772 10 5 10.4477 5 11C5 11.5523 5.44772 12 6 12H16C16.5523 12 17 11.5523 17 11C17 10.4477 16.5523 10 16 10Z" fill="#E64646"/>
</svg>

After

Width:  |  Height:  |  Size: 363 B

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<svg viewBox="0 0 300 200" fill="none" xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="filter0_dd_3240_167960" x="0" y="0" width="848" height="648" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="2"/>
<feGaussianBlur stdDeviation="12"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.08 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_3240_167960"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.08 0"/>
<feBlend mode="normal" in2="effect1_dropShadow_3240_167960" result="effect2_dropShadow_3240_167960"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_3240_167960" result="shape"/>
</filter>
<clipPath id="clip0_3240_167960">
<rect x="24" y="22" width="800" height="600" rx="20" fill="white"/>
</clipPath>
<clipPath id="clip1_3240_167960">
<rect width="800" height="52" fill="white" transform="translate(24 22)"/>
</clipPath>
<clipPath id="clip2_3240_167960">
<rect x="24" y="22" width="800" height="52" fill="white"/>
</clipPath>
</defs>
<g filter="url(#filter0_dd_3240_167960)" transform="matrix(1, 0, 0, 1, -298.468353, -50.344063)">
<g clip-path="url(#clip0_3240_167960)">
<g clip-path="url(#clip1_3240_167960)">
<g clip-path="url(#clip2_3240_167960)"/>
</g>
<path d="M431.053 79.5945L468.196 66.5968C473.692 64.6718 479.687 67.5566 481.612 73.0527L491.403 101.032C493.328 106.528 490.443 112.523 484.947 114.448L447.804 127.451C442.308 129.376 436.313 126.486 434.388 120.99L424.597 93.0105C422.672 87.5143 425.557 81.5195 431.053 79.5945Z" fill="#DEC3E7"/>
<path d="M424.742 87.3424L459.135 100.619L477.811 68.7627" stroke="#270033" stroke-width="2.14484" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M453.561 98.9307L438.07 125.398" stroke="#270033" stroke-width="2.14484" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M491.136 106.823L463.156 96.0454" stroke="#270033" stroke-width="2.14484" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M541.91 180.64L559.11 184.72L544.51 194.62L549.52 211.57L533.62 203.9L521.49 216.7L520.19 199.07L503 194.99L517.6 185.03L512.58 168.14L528.48 175.81L540.61 163L541.91 180.64Z" fill="#53DFC6"/>
<path d="M453.45 153.284L468.387 189.409L432.758 165.869L453.45 153.284Z" fill="#97FCEA"/>
<path opacity="0.2" d="M453.45 153.284L468.387 189.409L432.758 165.869L453.45 153.284Z" fill="black"/>
<path d="M453.453 153.283L421.093 198.426L365.037 96.5137C364.914 96.3234 365.118 96.1128 365.336 96.2419L453.453 153.283Z" fill="#53DFC6"/>
<path d="M503.006 130.9L453.46 153.284L365.268 96.2288C365.2 96.1337 365.234 95.9706 365.39 96.0046L503.006 130.9Z" fill="#53DFC6"/>
<path d="M453.454 153.284L439.891 172.216" stroke="black" stroke-width="2.71821" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M365.336 96.2432L453.453 153.285" stroke="black" stroke-width="2.71821" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M308 168.84L322.915 158.199L315.056 147.799L327.995 150.608L334.303 137L337.43 151.356L352 151.837L339.383 158.599L347.803 170.441L335.078 167.1L330.266 179.531L327.22 166.085L308 168.84Z" fill="#DEC3E7"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

@@ -0,0 +1,68 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import React, { cloneElement } from 'react';
import { ComponentStory } from '@storybook/react';
import {
DismissIcon,
FileTypeArchiveIcon,
FileTypeDocumentIcon,
FileTypePdfIcon,
FileTypeSpreadsheetIcon,
FileTypeUnknownIcon,
FileTypeMediaIcon,
FileTypeSlidesIcon,
NotFoundIcon,
RemoveIcon,
SentIcon,
} from '@atoms/icons-colored/index';
export default {
title: '@atoms/icons-colored',
};
type PropsType = {
icon: JSX.Element;
title: string;
};
const Icon = ({ icon, title }: PropsType): JSX.Element => {
const comp = cloneElement(icon, { className: 'w-8 h-8' });
return (
<div className="flex flex-col place-items-center w-[90px] my-3">
{comp}
<div className="m-2 text-xs text-zinc-500 break-words max-w-[68px] text-center">{title}</div>
</div>
);
};
// More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args
const Template: ComponentStory<any> = () => {
return (
<>
<h1>General</h1>
<div className="flex flex-wrap mb-2">
<Icon icon={<DismissIcon />} title="Dismiss" />
</div>
<h1>FileTypes</h1>
<div className="flex flex-wrap mb-2">
<Icon icon={<FileTypeArchiveIcon />} title="FileTypeArchive" />
<Icon icon={<FileTypePdfIcon />} title="FileTypePdf" />
<Icon icon={<FileTypeDocumentIcon />} title="FileTypeDocument" />
<Icon icon={<FileTypeSpreadsheetIcon />} title="FileTypeSpreadsheet" />
<Icon icon={<FileTypeUnknownIcon />} title="FileTypeUnknown" />
<Icon icon={<FileTypeMediaIcon />} title="FileTypeMedia" />
<Icon icon={<FileTypeSlidesIcon />} title="FileTypeSlides" />
</div>
<h1>Component specific</h1>
<div className="flex flex-wrap mb-2">
<Icon icon={<NotFoundIcon />} title="NotFound" />
<Icon icon={<RemoveIcon />} title="Remove" />
<Icon icon={<SentIcon />} title="Remove" />
</div>
</>
);
};
export const Default = Template.bind({});
// More on args: https://storybook.js.org/docs/react/writing-stories/args
Default.args = {};
@@ -0,0 +1,39 @@
import React, { ComponentProps } from 'react';
import { ReactComponent as DismissSvg } from './assets/dismiss.svg';
import { ReactComponent as NotFoundSvg } from './assets/not-found.svg';
import { ReactComponent as FileTypeArchiveSvg } from './assets/file-type-archive.svg';
import { ReactComponent as FileTypeDocumentSvg } from './assets/file-type-document.svg';
import { ReactComponent as FileTypePdfSvg } from './assets/file-type-pdf.svg';
import { ReactComponent as FileTypeSpreadsheetSvg } from './assets/file-type-spreadsheet.svg';
import { ReactComponent as FileTypeUnknownSvg } from './assets/file-type-unknown.svg';
import { ReactComponent as FileTypeMediaSvg } from './assets/file-type-media.svg';
import { ReactComponent as FileTypeSlidesSvg } from './assets/file-type-slides.svg';
import { ReactComponent as RemoveSvg } from './assets/remove.svg';
import { ReactComponent as SentSvg } from './assets/sent.svg';
export const DismissIcon = (props: ComponentProps<'svg'>) => <DismissSvg {...props} />;
export const NotFoundIcon = (props: ComponentProps<'svg'>) => <NotFoundSvg {...props} />;
export const FileTypeArchiveIcon = (props: ComponentProps<'svg'>) => (
<FileTypeArchiveSvg {...props} />
);
export const FileTypePdfIcon = (props: ComponentProps<'svg'>) => <FileTypePdfSvg {...props} />;
export const FileTypeDocumentIcon = (props: ComponentProps<'svg'>) => (
<FileTypeDocumentSvg {...props} />
);
export const FileTypeSpreadsheetIcon = (props: ComponentProps<'svg'>) => (
<FileTypeSpreadsheetSvg {...props} />
);
export const FileTypeUnknownIcon = (props: ComponentProps<'svg'>) => (
<FileTypeUnknownSvg {...props} />
);
export const FileTypeMediaIcon = (props: ComponentProps<'svg'>) => <FileTypeMediaSvg {...props} />;
export const FileTypeSlidesIcon = (props: ComponentProps<'svg'>) => (
<FileTypeSlidesSvg {...props} />
);
export const RemoveIcon = (props: ComponentProps<'svg'>) => <RemoveSvg {...props} />;
export const SentIcon = (props: ComponentProps<'svg'>) => <SentSvg {...props} />;
@@ -0,0 +1,54 @@
import { CheckOutlineIcon } from '../icons-agnostic';
import { BaseSmall } from '../text';
export const Checkbox = (props: {
label?: string;
onChange?: (status: boolean) => void;
value?: boolean;
className?: string;
disabled?: boolean;
}) => {
const renderSwitch = () => {
const className = props.className || '';
return (
<div
className={
' flex justify-center items-center w-6 h-6 border-2 rounded-full text-white ' +
(props.value
? 'border-blue-500 bg-blue-500 hover:border-blue-600 hover:bg-blue-600'
: 'border-zinc-300 ' + (props.disabled ? '' : 'hover:border-blue-300')) +
' ' +
(props.disabled ? 'opacity-50' : 'cursor-pointer') +
' ' +
(className || '')
}
onClick={() =>
!props.label && !props.disabled && props.onChange && props.onChange(!props.value)
}
>
{props.value && <CheckOutlineIcon className="m-icon-small" />}
</div>
);
};
if (props.label) {
return (
<div
className={'flex flex-row items-center'}
onClick={() => {
if (!props.disabled) {
props.onChange && props.onChange(!props.value);
}
}}
>
{renderSwitch()}
<BaseSmall className={'ml-2 ' + (props.disabled ? 'opacity-50' : 'cursor-pointer')}>
{props.label}
</BaseSmall>
</div>
);
} else {
return renderSwitch();
}
};
@@ -0,0 +1,23 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { ReactNode } from 'react';
interface InputLabelProps {
prefix?: (props: any) => JSX.Element;
suffix?: (props: any) => JSX.Element;
input: ({ className }: { className: string }) => ReactNode;
className?: string;
}
export const InputDecorationIcon = (props: InputLabelProps) => {
return (
<div className={'relative ' + props.className}>
{props.prefix && (
<props.prefix className="h-4 w-4 absolute m-auto top-0 bottom-0 left-3 text-zinc-500" />
)}
{props.input({ className: (props.prefix ? 'pl-9 ' : '') + (props.suffix ? 'pr-9 ' : '') })}
{props.suffix && (
<props.suffix className="h-4 w-4 absolute m-auto top-0 bottom-0 right-3 text-zinc-500" />
)}
</div>
);
};
@@ -0,0 +1,27 @@
import { Info } from '../text';
interface InputLabelProps {
label?: string;
feedback?: string;
hasError?: boolean;
input?: React.ReactNode;
className?: string;
}
export const InputLabel = (props: InputLabelProps) => {
return (
<>
{props.label && (
<div className={props.className}>
<label className="block text-sm text-zinc-400">{props.label}</label>
<div className="mt-1">{props.input}</div>
{props.feedback && (
<Info noColor className={props.hasError ? 'text-red-400' : 'text-blue-500'}>
{props.feedback}
</Info>
)}
</div>
)}
</>
);
};
@@ -0,0 +1,32 @@
import _ from 'lodash';
import { defaultInputClassName, errorInputClassName } from './input-text';
interface InputProps extends Omit<React.SelectHTMLAttributes<HTMLSelectElement>, 'size'> {
theme?: 'plain' | 'outline';
hasError?: boolean;
size?: 'md' | 'lg' | 'sm';
className?: string;
children?: React.ReactNode;
}
export function Select(props: InputProps) {
let inputClassName = props.hasError
? errorInputClassName(props.theme)
: defaultInputClassName(props.theme);
inputClassName = inputClassName + (props.disabled ? ' opacity-75' : '');
if (props.size === 'lg') inputClassName = inputClassName + ' text-lg h-11';
else if (props.size === 'sm') inputClassName = inputClassName + ' text-sm h-7 py-0 px-3';
else inputClassName = inputClassName + ' text-base h-9 py-1';
return (
<select
className={inputClassName + ' ' + props.className}
{..._.omit(props, 'label', 'className', 'size')}
>
{props.children}
</select>
);
}
export default Select;
@@ -0,0 +1,76 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import _ from 'lodash';
import React from 'react';
interface InputProps
extends Omit<
React.InputHTMLAttributes<HTMLInputElement> & React.TextareaHTMLAttributes<HTMLTextAreaElement>,
'size'
> {
theme?: 'plain' | 'outline';
label?: string;
size?: 'sm' | 'md' | 'lg';
feedback?: string;
hasError?: boolean;
multiline?: boolean;
inputComponent?: React.ReactNode;
inputClassName?: string;
className?: string;
inputRef?: React.Ref<HTMLInputElement | HTMLTextAreaElement>;
}
const baseInputClassName =
'tw-input block w-full rounded-md focus:ring-1 focus:ring-blue-500 z-0 focus:z-10 dark:text-white text-black ';
export const defaultInputClassName = (theme: 'plain' | 'outline' = 'plain') => {
return (
baseInputClassName +
(theme === 'plain'
? 'bg-zinc-200 border-zinc-200 dark:bg-zinc-800 dark:border-zinc-800'
: 'bg-zinc-50 border-zinc-300 dark:bg-zinc-800 dark:border-zinc-700')
);
};
export const errorInputClassName = (theme: 'plain' | 'outline' = 'plain') => {
return (
baseInputClassName +
(theme === 'plain'
? 'bg-red-200 border-red-200 dark:bg-red-800 dark:border-red-800'
: 'bg-red-50 border-red-300 dark:bg-red-900 dark:border-red-800')
);
};
export const Input = (props: InputProps) => {
let inputClassName = props.hasError
? errorInputClassName(props.theme)
: defaultInputClassName(props.theme);
inputClassName = inputClassName + (props.disabled ? ' opacity-75' : '');
if (!props.multiline) {
if (props.size === 'lg') inputClassName = inputClassName + ' h-11';
else if (props.size === 'sm') inputClassName = inputClassName + ' h-7';
else inputClassName = inputClassName + ' h-9';
}
return (
<>
{props.inputComponent ||
(props.multiline ? (
<textarea
ref={props.inputRef as React.Ref<HTMLTextAreaElement>}
className={inputClassName + ' ' + props.inputClassName + ' ' + props.className}
{..._.omit(props as any, 'label', 'inputClassName', 'className', 'value', 'size')}
>
{props.value}
</textarea>
) : (
<input
ref={props.inputRef as React.Ref<HTMLInputElement>}
type="text"
className={inputClassName + ' ' + props.inputClassName + ' ' + props.className}
{..._.omit(props, 'label', 'inputClassName', 'className', 'size')}
/>
))}
</>
);
};
@@ -0,0 +1,41 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { ComponentStory } from '@storybook/react';
import { useState } from 'react';
import { Checkbox } from '../input-checkbox';
export default {
title: '@atoms/checkbox',
};
const Template: ComponentStory<any> = (props: { label: string; disabled: boolean }) => {
const [checked, setChecked] = useState(false);
return (
<div className="max-w-md">
<Checkbox
onChange={e => {
setChecked(e);
}}
value={checked}
disabled={props.disabled}
/>
<br />
<Checkbox
onChange={e => {
setChecked(e);
}}
value={checked}
disabled={props.disabled}
label={props.label}
/>
</div>
);
};
export const Default = Template.bind({});
Default.args = {
label: 'Checkbox label',
disabled: false,
};
@@ -0,0 +1,107 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { ComponentStory } from '@storybook/react';
import { SearchIcon } from '@heroicons/react/solid';
import { Title } from '@atoms/text';
import { Input } from '../input-text';
import { FilterIcon, CogIcon } from '@heroicons/react/outline';
import Select from '../input-select';
import { InputDecorationIcon } from '../input-decoration-icon';
export default {
title: '@atoms/input-groups',
};
const Template: ComponentStory<any> = () => {
return (
<>
<Title>Icons</Title>
<br />
<InputDecorationIcon
prefix={SearchIcon}
input={({ className }) => <Input className={className} placeholder="Awesome text" />}
/>
<br />
<InputDecorationIcon
suffix={FilterIcon}
input={({ className }) => <Input className={className} placeholder="Awesome text" />}
/>
<br />
<InputDecorationIcon
prefix={SearchIcon}
suffix={FilterIcon}
input={({ className }) => <Input className={className} placeholder="Awesome text" />}
/>
<br />
<InputDecorationIcon
prefix={SearchIcon}
suffix={FilterIcon}
input={({ className }) => (
<Input className={className} multiline placeholder="Awesome text" />
)}
/>
<br />
<InputDecorationIcon
prefix={SearchIcon}
input={({ className }) => (
<Select className={className}>
<option>Option 1</option>
</Select>
)}
/>
<br />
<br />
<Title>Groups</Title>
<br />
<div className="relative flex">
<Input className="grow rounded-tr-none rounded-br-none mr-px" placeholder="Awesome text" />
<Select className="w-auto rounded-tl-none rounded-bl-none">
<option>Option 1</option>
</Select>
</div>
<br />
<br />
<Title>Mixed</Title>
<br />
<div className="relative flex">
<InputDecorationIcon
className="grow mr-px"
prefix={SearchIcon}
suffix={CogIcon}
input={({ className }) => (
<Input
className={className + ' rounded-tr-none rounded-br-none'}
placeholder="Awesome text"
/>
)}
/>
<InputDecorationIcon
className=""
prefix={FilterIcon}
input={({ className }) => (
<Select
className={className + 'pl-9 w-auto rounded-tl-none rounded-bl-none text-opacity-50'}
>
<option>company</option>
<option>channel</option>
</Select>
)}
/>
</div>
</>
);
};
export const Default = Template.bind({});
Default.args = {
text: 'Text',
disabled: false,
};
@@ -0,0 +1,73 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { ComponentStory } from '@storybook/react';
import { Input } from '../input-text';
import { Title } from '../../text';
import { InputLabel } from '../input-decoration-label';
export default {
title: '@atoms/input',
};
const Template: ComponentStory<any> = (props: {
text: string;
disabled: boolean;
loading: boolean;
}) => {
return (
<div className="max-w-md">
<Title>Sizes and themes plain / outline</Title>
<br />
<Input size="sm" disabled={props.disabled} placeholder="Awesome text" />
<br />
<Input size="md" disabled={props.disabled} placeholder="Awesome text" />
<br />
<Input size="lg" disabled={props.disabled} placeholder="Awesome text" />
<br />
<Input theme="outline" size="sm" disabled={props.disabled} placeholder="Awesome text" />
<br />
<Input theme="outline" size="md" disabled={props.disabled} placeholder="Awesome text" />
<br />
<Input theme="outline" size="lg" disabled={props.disabled} placeholder="Awesome text" />
<br />
<Title>Labels and errors</Title>
<br />
<Input disabled={props.disabled} placeholder="Awesome text" value={props.text} />
<br />
<InputLabel
hasError
label="Label and feedback"
feedback="The feedback"
input={
<Input
disabled={props.disabled}
placeholder="Awesome text area"
hasError
value={props.text}
/>
}
/>
<br />
<InputLabel
label="Multi-line"
feedback="Some non-error feedback"
input={
<Input
disabled={props.disabled}
placeholder="Awesome text area"
value={props.text}
multiline
/>
}
/>
</div>
);
};
export const Default = Template.bind({});
Default.args = {
text: 'Text',
disabled: false,
};
@@ -0,0 +1,58 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { ComponentStory } from '@storybook/react';
import { Title } from '@atoms/text';
import { InputLabel } from '../input-decoration-label';
import Select from '../input-select';
export default {
title: '@atoms/select',
};
const Template: ComponentStory<any> = (props: {
text: string;
disabled: boolean;
loading: boolean;
}) => {
return (
<div className="max-w-md">
<Title>Sizes and with error</Title>
<br />
<Select size="sm" disabled={props.disabled}>
<option>Option 1</option>
<option>Option 2</option>
</Select>
<br />
<Select size="md" disabled={props.disabled}>
<option>Option 1</option>
<option>Option 2</option>
</Select>
<br />
<Select size="lg" hasError disabled={props.disabled}>
<option>Option 1</option>
<option>Option 2</option>
</Select>
<br />
<Title>Labels</Title>
<br />
<InputLabel
label="Some label"
hasError
feedback="Has error"
input={
<Select size="lg" hasError disabled={props.disabled}>
<option>Option 1</option>
<option>Option 2</option>
</Select>
}
/>
</div>
);
};
export const Default = Template.bind({});
Default.args = {
disabled: false,
};
@@ -0,0 +1,48 @@
export default function SingleCenterCard(props: {
logo?: boolean;
insetLogo?: boolean;
title?: string | React.ReactNode;
subtitle?: string | React.ReactNode;
children: React.ReactNode;
}) {
return (
<div className="min-h-full flex flex-col justify-center py-0 sm:bg-transparent">
{(!!props.logo || !!props.title || !!props.subtitle) && (
<div className="sm:mx-auto sm:w-full sm:max-w-md">
<img
className="mx-auto h-6 w-auto"
src="/images/logo_colors.svg"
alt="Workflow"
/>
{!!props.title && (
<h2 className="mt-6 text-center text-3xl font-extrabold text-gray-900">
{props.title}
</h2>
)}
{!!props.subtitle && (
<p className="mt-2 text-center text-sm text-gray-600">
{props.subtitle}
</p>
)}
</div>
)}
<div
style={{ zIndex: 1 }}
className="mt-0 mb-0 h-full sm:mb-12 sm:mt-8 sm:mx-auto sm:w-full sm:max-w-md"
>
<div className="py-8 px-4 sm:shadow-lg sm:rounded-lg sm:px-10 sm:bg-white ">
{props.insetLogo && (
<img
className="mx-auto h-6 w-auto mb-4"
src="/images/logo_colors.svg"
alt="Workflow"
/>
)}
{props.children}
</div>
</div>
</div>
);
}
@@ -0,0 +1,36 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import _ from 'lodash';
import { Link } from 'react-router-dom';
export default function A(
props: any & {
to?: string;
href?: string;
children: React.ReactNode;
noColor?: boolean;
},
) {
const colors = props.noColor ? '' : 'hover:text-blue-700 active:text-blue-800 text-blue-500';
if (props.to) {
return (
<Link
to={props.to}
className={colors + ' ' + (props.className || '')}
{..._.omit(props, 'children', 'className', 'noColor')}
>
{props.children}
</Link>
);
}
return (
<a
href={(props.href || '#').replace(/^javascript:/, '')}
className={colors + ' ' + (props.className || '')}
{..._.omit(props, 'children', 'className', 'noColor')}
>
{props.children}
</a>
);
}
@@ -0,0 +1,31 @@
import React from 'react';
import { MemoryRouter } from 'react-router-dom';
import { ComponentStory, ComponentMeta } from '@storybook/react';
import A from './index';
export default {
title: '@atoms/link',
component: A,
decorators: [(Story) => (<MemoryRouter><Story/></MemoryRouter>)]
} as ComponentMeta<typeof A>;
const Template: ComponentStory<typeof A> = args => <A {...args} />;
export const Default = Template.bind({});
Default.args = {
href: 'https://www.google.com',
children: 'Link',
}
export const noColor = Template.bind({});
noColor.args = {
href: 'https://www.google.com',
children: 'Link',
noColor: true,
};
export const internal = Template.bind({});
internal.args = {
to: '/',
children: 'Link',
};
@@ -0,0 +1,24 @@
export const Loader = (props: { className?: string }) => {
return (
<svg
className={'animate-spin text-gray-400 inline ' + (props.className || '')}
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle
className="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="4"
></circle>
<path
className="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
></path>
</svg>
);
};
@@ -0,0 +1,23 @@
import _ from 'lodash';
import React from 'react';
interface PropsType extends Omit<React.HTMLAttributes<HTMLDivElement>, 'size'> {
size?: 16 | 32 | 64 | 128 | 256;
}
export const Logo = (props: PropsType): React.ReactElement => {
const { size = 128 } = props;
return (
<div
className={'flex flex-col items-center justify-center ' + props.className}
{..._.omit(props, 'className', 'size')}
>
<img
className="mx-auto w-auto"
src={`/public/img/logo/${size}x${size}.png`}
alt="Twake logo"
/>
</div>
);
};
@@ -0,0 +1,38 @@
import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';
import { Logo } from '.';
export default {
title: '@atoms/logo',
component: Logo,
} as ComponentMeta<typeof Logo>;
const Template: ComponentStory<typeof Logo> = args => <Logo {...args} />;
export const Default = Template.bind({});
Default.args = {};
export const extraSmall = Template.bind({});
extraSmall.args = {
size: 16,
};
export const small = Template.bind({});
small.args = {
size: 32,
};
export const mediumSmall = Template.bind({});
mediumSmall.args = {
size: 64,
};
export const medium = Template.bind({});
medium.args = {
size: 128,
};
export const large = Template.bind({});
large.args = {
size: 256,
};
@@ -0,0 +1,93 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import React, { useState } from 'react';
import { RecoilRoot } from 'recoil';
import { ComponentStory } from '@storybook/react';
import { Modal, ModalContent } from '.';
import { Button } from '../button/button';
import { ButtonConfirm } from '../button/confirm';
import { CheckCircleIcon, ExclamationCircleIcon } from '@heroicons/react/outline';
import { Title } from '../text';
export default {
title: '@atoms/modal',
};
const Template: ComponentStory<any> = () => {
const [openA, setOpenA] = useState(false);
const [openB, setOpenB] = useState(false);
return (
<RecoilRoot>
<Modal open={openB} onClose={() => setOpenB(false)}>
<Title>Title</Title>
Hello this is the modal B<br />
No more popup now
</Modal>
<Modal open={openA} onClose={() => setOpenA(false)}>
<Title>Title</Title>
Hello this is the modal A<br />
<ButtonConfirm onClick={() => setOpenB(true)}>Open B</ButtonConfirm>
</Modal>
<Button onClick={() => setOpenA(true)}>Open A</Button>
</RecoilRoot>
);
};
export const Default = Template.bind({});
Default.args = {};
const TemplateWithContent: ComponentStory<any> = () => {
const [openA, setOpenA] = useState(false);
const [openB, setOpenB] = useState(false);
return (
<RecoilRoot>
<Modal open={openB} onClose={() => setOpenB(false)}>
<ModalContent
title="Modal B"
text="Congratulation, this is modal B !"
theme="success"
icon={CheckCircleIcon}
buttons={
<>
<Button
className="ml-2"
theme="default"
onClick={() => {
setOpenB(false);
}}
>
Go back on modal B
</Button>
<Button
onClick={() => {
setOpenB(false);
setOpenA(false);
}}
>
Close it all
</Button>
</>
}
/>
</Modal>
<Modal open={openA} onClose={() => setOpenA(false)}>
<ModalContent
title="Modal A"
text="This is the wrong popup, open the modal B !"
theme="danger"
icon={ExclamationCircleIcon}
buttons={
<>
<ButtonConfirm onClick={() => setOpenB(true)}>Open modal B</ButtonConfirm>
</>
}
/>
</Modal>
<Button onClick={() => setOpenA(true)}>Open A</Button>
</RecoilRoot>
);
};
export const WithModalContent = TemplateWithContent.bind({});
WithModalContent.args = {};
@@ -0,0 +1,197 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Dialog, Transition } from '@headlessui/react';
import { Fragment, ReactNode, useCallback, useEffect, useState } from 'react';
import { atom, useRecoilState } from 'recoil';
import { DismissIcon } from '../icons-colored';
import { Title } from '../text';
const ModalsCountState = atom({
key: 'ModalsState',
default: 0,
});
let visibleModals = 0;
export const Modal = (props: {
open?: boolean;
onClose?: () => void;
children?: React.ReactNode;
closable?: boolean;
className?: string;
style?: React.CSSProperties;
positioned?: boolean;
}) => {
const [open, setOpen] = useState(false);
const [, setModalsCountState] = useRecoilState(ModalsCountState);
const [level, setLevel] = useState(0);
const [didOpenOnce, setDidOpenOnce] = useState(false);
const onClose = useCallback(() => {
visibleModals += -1;
setModalsCountState(visibleModals);
}, [setModalsCountState]);
const onOpen = useCallback(() => {
visibleModals += 1;
setLevel(visibleModals);
setModalsCountState(visibleModals);
}, [setModalsCountState]);
useEffect(() => {
if (props.open !== open) {
setOpen(props.open || false);
if (props.open) {
onOpen();
} else {
onClose();
}
}
if (!didOpenOnce && props.open) setDidOpenOnce(true);
}, [props.open]);
useEffect(() => {
return () => {
if (open) onClose();
};
}, []);
const zIndex = 'z-' + level + '0';
return (
<Transition.Root show={open} as={Fragment}>
<Dialog
as="div"
className={'relative ' + zIndex}
onClose={() => props.onClose && props.closable && props.onClose()}
>
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0 pointer-events-none"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0 pointer-events-none"
>
<div
className={
'fixed inset-0 bg-opacity-25 dark:bg-opacity-75 transition-opacity ' +
(level === 1 ? 'bg-black' : 'bg-transparent')
}
/>
</Transition.Child>
<div
className={
'fixed z-10 inset-0 overflow-y-auto transition-transform ' +
(level !== visibleModals && open
? '-translate-y-6 sm:scale-95 brightness-75 '
: level !== visibleModals && !open
? 'translate-y-6 sm:scale-95 brightness-75 '
: '')
}
>
<div
className={
'flex items-end justify-center min-h-screen webkit-h-full text-center sm:block '
}
>
{
/* This element is to trick the browser into centering the modal contents. */
!props.positioned && (
<span
className="hidden sm:inline-block sm:align-middle sm:h-screen"
aria-hidden="true"
>
&#8203;
</span>
)
}
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0 pointer-events-none translate-y-4 sm:translate-y-0 sm:scale-95"
enterTo="opacity-100 translate-y-0 sm:scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 pointer-events-none translate-y-4 sm:translate-y-0 sm:scale-95"
>
<Dialog.Panel
className={
'relative inline-block align-bottom bg-white dark:bg-zinc-900 rounded-tr-xl rounded-tl-xl sm:rounded-md px-4 pt-5 pb-4 text-left w-full overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-xl sm:w-full sm:p-4 ' +
(props.className || '')
}
style={props.style || {}}
>
{props.closable !== false && (
<div className="absolute top-0 right-0 pt-4 pr-4">
<button
type="button"
className="hover:opacity-75 focus:outline-none "
onClick={() => props.onClose && props.onClose()}
>
<DismissIcon />
</button>
</div>
)}
{didOpenOnce && props.children}
</Dialog.Panel>
</Transition.Child>
</div>
</div>
</Dialog>
</Transition.Root>
);
};
export const ModalContent = (props: {
title: ReactNode | string;
text?: string;
textCenter?: boolean;
buttons?: ReactNode;
children?: ReactNode;
icon?: any;
theme?: 'success' | 'danger' | 'warning' | 'gray';
}) => {
let color = 'blue';
if (props.theme === 'success') color = 'green';
if (props.theme === 'danger') color = 'red';
if (props.theme === 'warning') color = 'orange';
if (props.theme === 'gray') color = 'gray';
return (
<>
<div className="sm:flex sm:items-start">
{props.icon && (
<div
className={`mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-${color}-100 sm:mx-0 sm:h-10 sm:w-10`}
>
<props.icon className={`h-6 w-6 text-${color}-600`} aria-hidden="true" />
</div>
)}
<div
className={
'w-full ' +
(props.icon ? 'sm:ml-4 ' : '') +
(props.textCenter ? 'text-center' : 'text-left')
}
>
<Title className="pr-8 overflow-hidden text-ellipsis">{props.title}</Title>
<div className="mt-2">
<p className="text-sm text-gray-500">{props.text || ''}</p>
</div>
</div>
</div>
{props.buttons && (
<div
className={
'mt-5 sm:mt-4 sm:flex sm:flex-row-reverse sm:text-left ' +
(props.textCenter ? 'text-center' : 'text-left')
}
>
{props.buttons}
</div>
)}
{props.children}
</>
);
};
@@ -0,0 +1,32 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import React from 'react';
import { ComponentStory } from '@storybook/react';
import * as Text from '.';
export default {
title: '@atoms/text',
};
// More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args
const Template: ComponentStory<any> = (props: { text: string }) => (
<>
<Text.Title className="block">{props.text || 'Title'}</Text.Title>
<br />
<Text.Subtitle className="block">{props.text || 'Subtitle'}</Text.Subtitle>
<br />
<Text.Base className="block">{props.text || 'Base'}</Text.Base>
<br />
<Text.BaseSmall className="block">{props.text || 'BaseSmall'}</Text.BaseSmall>
<br />
<Text.Info className="block">{props.text || 'Info'}</Text.Info>
<br />
<Text.InfoSmall className="block">{props.text || 'InfoSmall'}</Text.InfoSmall>
<br />
</>
);
export const Default = Template.bind({});
Default.args = {
text: '',
};
@@ -0,0 +1,75 @@
import React from 'react';
import _ from 'lodash';
import { ReactNode } from 'react';
type TextProps = {
type: 'title' | 'subtitle' | 'base' | 'base-small' | 'info' | 'info-small';
children: ReactNode | ReactNode[];
className?: string;
noColor?: boolean;
} & Omit<
React.ButtonHTMLAttributes<HTMLSpanElement>,
'children' | 'className' | 'type' | 'noColor'
>;
type TypedTextProps = Omit<TextProps, 'type'>;
export const Title = (props: TypedTextProps) => Text({ type: 'title', ...props });
export const Subtitle = (props: TypedTextProps) => Text({ type: 'subtitle', ...props });
export const Base = (props: TypedTextProps) => Text({ type: 'base', ...props });
export const BaseSmall = (props: TypedTextProps) => Text({ type: 'base-small', ...props });
export const Info = (props: TypedTextProps) => Text({ type: 'info', ...props });
export const InfoSmall = (props: TypedTextProps) => Text({ type: 'info-small', ...props });
const Text = (props: TextProps) => {
let defaultClassName = '';
switch (props.type) {
case 'title':
defaultClassName =
'text-lg font-semibold block ' +
' ' +
(props.noColor ? '' : 'text-zinc-900 dark:text-white');
break;
case 'subtitle':
defaultClassName =
'text-base font-semibold' + ' ' + (props.noColor ? '' : 'text-zinc-800 dark:text-white');
break;
case 'base':
defaultClassName =
'text-base font-normal' + ' ' + (props.noColor ? '' : 'text-zinc-800 dark:text-white');
break;
case 'base-small':
defaultClassName =
'text-sm font-normal' + ' ' + (props.noColor ? '' : 'text-zinc-800 dark:text-white');
break;
case 'info':
defaultClassName =
'text-sm font-normal' +
' ' +
(props.noColor ? '' : 'text-zinc-400 dark:text-white dark:opacity-50');
break;
case 'info-small':
defaultClassName =
'text-xs font-normal' +
' ' +
(props.noColor ? '' : 'text-zinc-400 dark:text-white dark:opacity-50');
break;
}
return (
<span
className={defaultClassName + ' ' + (props.className || '')}
{..._.omit(props, 'type', 'className', 'children', 'noColor')}
>
{props.children}
</span>
);
};
export default Text;
@@ -0,0 +1,18 @@
.addUserButton {
color: var(--primary);
.icon {
.iconBox {
width: 18px;
height: 18px;
background-color: var(--primary-background);
border-radius: 4px;
padding-left: 2px;
.icon-unicon {
color: var(--primary) !important;
}
}
color: var(--primary) !important ;
}
}
@@ -0,0 +1,40 @@
import React from 'react';
import Icon from 'app/components/icon/icon.jsx';
import Languages from 'app/features/global/services/languages-service';
import './add-user-button.scss';
import { useRecoilState } from 'recoil';
import { invitationState } from 'app/features/invitation/state/invitation';
import { useInvitationUsers } from 'app/features/invitation/hooks/use-invitation-users';
import AccessRightsService from 'app/features/workspace-members/services/workspace-members-access-rights-service';
import { useCurrentWorkspace } from 'app/features/workspaces/hooks/use-workspaces';
export default () => {
const [, setInvitationOpen] = useRecoilState(invitationState);
const { allowed_guests, allowed_members } = useInvitationUsers();
const { workspace } = useCurrentWorkspace();
return AccessRightsService.hasLevel(workspace?.id, 'moderator') &&
(allowed_guests > 0 || allowed_members > 0) ? (
<div
className="channel addUserButton"
onClick={() => {
setInvitationOpen(true);
}}
>
<div className="icon">
<div className="iconBox">
<Icon type="plus" />
</div>
</div>
<div className="text">
{Languages.t(
'scenes.app.popup.workspaceparameter.pages.collaboraters_adding_button',
[],
'Ajouter des collaborateurs',
)}
</div>
</div>
) : (
<></>
);
};
@@ -0,0 +1,199 @@
/* eslint-disable react/prop-types */
import React, { Component } from 'react';
import File from 'components/drive/file';
import FilePicker from 'components/drive/file-picker/file-picker.jsx';
import TaskPicker from 'components/task-picker/task-picker.jsx';
import Menu from 'components/menus/menu.jsx';
import Icon from 'app/components/icon/icon.jsx';
import Button from 'components/buttons/button.jsx';
import UploadZone from 'components/uploads/upload-zone';
import MenusManager from 'app/components/menus/menus-manager.jsx';
import Workspaces from 'app/deprecated/workspaces/workspaces.jsx';
import Languages from 'app/features/global/services/languages-service';
import './attachment-picker.scss';
export default class AttachmentPicker extends Component {
/*
props : {
readOnly : bool
attachments : []
onChange
className
}
*/
getIcon(att) {
if (att.type.toLocaleLowerCase() === 'event') {
return 'calendar-alt';
}
if (att.type.toLocaleLowerCase() === 'file') {
return 'folder';
}
if (att.type.toLocaleLowerCase() === 'task') {
return 'check-square';
}
}
addAttachment(attachment) {
var attachments = this.props.attachments || [];
attachments.push(attachment);
if (this.props.onChange) {
this.props.onChange(attachments);
}
}
removeAttachment(attachment) {
var attachments = this.props.attachments || [];
var index = attachments.indexOf(attachment);
if (index >= 0) {
attachments.splice(index, 1);
if (this.props.onChange) {
this.props.onChange(attachments);
}
}
}
render() {
return (
<div className={'attachmentPicker ' + (this.props.className || '')}>
<div className="attachments">
{(Object.values(this.props.attachments || {}) || []).map(att => {
if (att.type === 'file') {
var additionalMenu = [];
if (!this.props.readOnly) {
additionalMenu = [
{
type: 'menu',
text: Languages.t(
'components.attachmentpicker.remove_attach',
[],
'Remove attachment',
),
onClick: () => {
this.removeAttachment(att);
},
},
];
}
return (
<div className="attachment attachment_file drive_view list" key={`attachment_file-${att.id}`}>
<File
data={{ id: att.id || '' }}
additionalMenu={additionalMenu}
notInDrive={true}
style={{ marginBottom: 0 }}
/>
</div>
);
}
return (
<div className="attachment" key={att.id}>
<Icon className="app-icon" type={this.getIcon(att)} />
{att.name}
{!this.props.readOnly && (
<Icon
className="remove"
type="times"
onClick={() => {
this.removeAttachment(att);
}}
/>
)}
</div>
);
})}
</div>
{!this.props.readOnly && (
<Menu
style={{ display: 'inline-block' }}
menu={[
{
type: 'menu',
text: Languages.t('components.attachmentpicker.file', [], 'File'),
icon: 'file',
submenu: [
{
type: 'menu',
icon: 'desktop',
text: Languages.t(
'components.attachmentpicker.from_computer',
[],
'From computer',
),
onClick: () => {
this.upload_zone.open();
MenusManager.closeMenu();
},
},
{
type: 'menu',
icon: 'folder',
text: Languages.t(
'components.attachmentpicker.from_twake',
[],
'From Twake Documents',
),
submenu: [
{
type: 'react-element',
reactElement: (
<FilePicker
mode={'select_file'}
onChoose={res => {
this.addAttachment({ type: 'file', id: res.id, name: res.name });
MenusManager.closeMenu();
}}
initialDirectory={{ id: '' }}
/>
),
},
],
},
],
},
{
type: 'menu',
text: Languages.t('scenes.apps.tasks.task', [], 'Task'),
icon: 'check-square',
submenu: [
{
type: 'react-element',
reactElement: (
<TaskPicker
mode={'select_task'}
onChoose={res => {
this.addAttachment({ type: 'task', id: res.id, name: res.title });
MenusManager.closeMenu();
}}
/>
),
},
],
},
]}
>
{' '}
<Button className="small secondary-text right-margin">
<Icon type="plus" className="m-icon-small" />{' '}
{Languages.t(
'components.attachmentpicker.add_attachment',
[],
'Ajouter des pièces jointes',
)}
</Button>
</Menu>
)}
<UploadZone
ref={node => (this.upload_zone = node)}
disableClick
parent={''}
driveCollectionKey={'attachment_' + Workspaces.currentWorkspaceId}
uploadOptions={{ workspace_id: Workspaces.currentWorkspaceId, detached: true }}
onUploaded={res => {
this.addAttachment({ type: 'file', id: res.id, name: res.name });
}}
multiple={false}
/>
</div>
);
}
}
@@ -0,0 +1,39 @@
.attachmentPicker {
.attachment {
display: inline-block;
font-size: 12px;
color: var(--black);
border: solid 1px var(--grey-background);
height: 32px;
box-sizing: border-box;
border-radius: var(--border-radius-large);
padding: 4px;
padding-right: 8px;
line-height: 24px;
margin-right: 8px;
margin-bottom: 8px;
&.attachment_file {
border: 0;
padding: 0;
height: auto;
}
}
i.app-icon {
margin-right: 4px;
margin-left: 4px;
font-size: 14px;
position: relative;
top: -1px;
vertical-align: top;
}
.remove {
opacity: 0.5;
cursor: pointer;
&:hover {
opacity: 1;
}
}
}
@@ -0,0 +1,61 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import React, { useState, ReactElement } from 'react';
import { AutoComplete } from 'antd';
import { AutoCompleteProps } from 'antd/lib/auto-complete';
const { Option } = AutoComplete;
export default (
props: {
maxItems?: number;
render?: (item: any) => ReactElement;
onSearch?: (query: string, callback: (res: string[]) => void) => void;
onSelect?: (id: string) => void;
align?: 'top' | 'bottom';
} & Omit<AutoCompleteProps, 'onSearch' | 'onSelect'>,
) => {
const [list, setList] = useState<string[]>([]);
const [inputValue, setInputValue] = useState<string>('');
const resetInputValue = () => setInputValue('');
return (
<AutoComplete
{...props}
dropdownAlign={
props.align === 'top'
? {
points: ['bl', 'tl'], // align dropdown bottom-left to top-left of input element
offset: [0, -4], // align offset
overflow: {
adjustX: 0,
adjustY: 0, // do not auto flip in y-axis
},
}
: {}
}
onSearch={(text: string) => {
props.onSearch &&
props.onSearch(text, (res: any[]) => {
setList(res);
});
}}
value={inputValue}
onSelect={(id: string) => {
resetInputValue();
props.onSelect && props.onSelect(id);
}}
onChange={e => setInputValue(e || '')}
>
{list.slice(0, props.maxItems || 10).map((item: any) => {
return item ? (
<Option key={item} value={item || ''}>
{props.render ? props.render(item) : item?.id}
</Option>
) : (
<></>
);
})}
</AutoComplete>
);
};
@@ -0,0 +1,23 @@
.autocomplete {
position: relative;
border-radius: 1px solid rgba(0, 0, 0, 0.2);
transition: box-shadow 0.2s;
.dropmenu {
opacity: 0;
transition: opacity 0.2s;
pointer-events: none;
}
&.focused {
.dropmenu {
opacity: 1;
pointer-events: all;
}
}
input,
textarea {
margin: 0px !important;
}
}
@@ -0,0 +1,399 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import React, { Component } from 'react';
import AutoHeight from '../auto-height/auto-height';
import Input from '../inputs/input';
import './auto-complete.scss';
type State = {
currentList: any[];
selected: number;
resultPosition: string;
selectedUser: object;
focused: any;
};
type Props = {
onResize?: any;
autoHeight?: any;
className?: string;
placeholder?: string;
search: ((text: string, cb: (arr: any) => any) => void)[];
max: number[];
onSelect?: (obj: object, other: any) => void;
renderItem: ((obj: object) => any)[];
renderItemChoosen: ((el: any) => any)[];
regexHooked: any;
onChange?: any;
value?: any;
onChangeCurrentList?: any;
hideResult?: any;
onBackspace?: any;
disableNavigationKey?: any;
showResultsOnInit?: any;
position?: any;
onHide?: any;
onEscape?: any;
keyUp?: any;
onKeyUp?: any;
onKeyDown?: any;
onKeyPress?: any;
onPaste?: any;
onFocusChange?: any;
big?: boolean;
small?: boolean;
autoFocus?: any;
};
export default class AutoComplete extends Component<Props, State> {
currentIdFromList = '';
is_open = false;
input: any;
container: any;
outsideClickListener: any;
currentRegexUsed: any = -1;
constructor(props: any) {
super(props);
this.state = {
currentList: [],
selected: -1,
resultPosition: '',
selectedUser: {},
focused: '',
};
this.keyUp = this.keyUp.bind(this);
this.keyDown = this.keyDown.bind(this);
}
keyDown(ev: any) {
const key = ev.which || window.event;
if (
this.is_open &&
key &&
this.currentRegexUsed >= 0 &&
this.state.currentList.length > 0 &&
(key === 13 || key === 9 || key === 38 || key === 40)
) {
ev.preventDefault();
ev.stopPropagation();
}
if (key === 27 && !this.is_open && this.props.onEscape) {
this.props.onEscape();
}
if (key === 27 && this.is_open && this.props.onHide) {
this.is_open = false;
this.props.onHide();
}
}
keyUp(ev: any) {
const key = ev.which;
const allText = this.getValueBeforeCaret();
if (key === 8 && this.input.value.length === 0 && this.props.onBackspace) {
this.props.onBackspace();
}
for (let i = 0; i < this.props.regexHooked.length; i++) {
let text = allText.match(this.props.regexHooked[i]);
if (text && allText) {
if (this.currentRegexUsed < 0 || this.currentRegexUsed !== i) {
this.currentRegexUsed = i;
}
text = text[1] || '';
if (
key &&
!this.props.disableNavigationKey &&
(key === 13 || key === 9 || key === 38 || key === 40) &&
this.state.currentList.length > 0
) {
ev.preventDefault();
ev.stopPropagation();
if (key === 13 || key === 9) {
//Select (enter)
this.select((this.state.currentList || {})[this.state.selected]);
} else if (key === 38 || key === 40) {
//Up // down
let nextState = 0;
if (
(key === 38 && this.state.resultPosition === 'top') ||
(key === 40 && this.state.resultPosition === 'bottom')
) {
nextState = (this.state.selected + 1) % this.state.currentList.length;
} else {
nextState =
(this.state.selected + this.state.currentList.length - 1) %
this.state.currentList.length;
}
this.setState({
selected: nextState,
});
this.currentIdFromList = this.getCurrentIdFromList(this.state.currentList, nextState);
}
} else {
this.is_open = true;
this.search(text, i);
}
if (this.props.onChangeCurrentList) {
this.props.onChangeCurrentList(this.state.currentList, this.state.selected);
}
return;
}
}
this.currentRegexUsed = -1;
this.setState({ currentList: [], resultPosition: '' });
this.props.keyUp && this.props.keyUp(ev);
}
getCurrentIdFromList(list: any[], pos: number) {
if (!list[pos]) {
return;
}
const id = list[pos].id || JSON.stringify(list[pos]);
return id;
}
componentWillUnmount() {
document.removeEventListener('click', this.outsideClickListener);
this.input.removeEventListener('keydown', this.keyDown);
this.input.removeEventListener('keyup', this.keyUp);
}
componentDidMount() {
const element = this.container;
this.outsideClickListener = (event: any) => {
if (!element.contains(event.target) && document.contains(event.target)) {
this.setState({ focused: false });
}
};
this.outsideClickListener = this.outsideClickListener.bind(this);
document.addEventListener('click', this.outsideClickListener);
this.input.addEventListener('keydown', this.keyDown);
this.input.addEventListener('keyup', this.keyUp);
if (this.props.showResultsOnInit) {
this.currentRegexUsed = 0;
this.search('', 0);
this.props.onChangeCurrentList(this.state.currentList, this.state.selected);
}
}
search(query: any, i: any) {
this.props.search[this.currentRegexUsed](query, results => {
if (!this.is_open) {
return;
}
const suggestions: any[] = [];
for (let j = 0; j < Math.min(this.props.max[this.currentRegexUsed], results.length); j++) {
results[j].autocomplete_id = j;
suggestions.push(results[j]);
}
let selection = 0;
const idsFromSuggestedList = suggestions.map(
(item, index) => (item = this.getCurrentIdFromList(suggestions, index)),
);
selection = Math.max(idsFromSuggestedList.indexOf(this.currentIdFromList), 0);
this.currentRegexUsed = i;
this.setState({
selected: selection,
currentList: suggestions,
});
this.currentIdFromList = this.getCurrentIdFromList(suggestions, selection);
if (this.props.onChangeCurrentList) {
this.props.onChangeCurrentList(suggestions, this.state.selected);
}
});
}
getValueBeforeCaret() {
return this.input.value.substr(0, this.input.selectionStart);
}
putTextAtCursor(text: string, alreadyTypedLength: any) {
alreadyTypedLength = alreadyTypedLength || 0;
const myValue = text;
if (this.input.selectionStart || this.input.selectionStart === '0') {
const startPos = this.input.selectionStart;
const endPos = this.input.selectionEnd;
this.input.value =
this.input.value.substring(0, startPos - alreadyTypedLength) +
myValue +
this.input.value.substring(endPos, this.input.value.length);
this.input.selectionStart = this.input.selectionStart + myValue.length;
this.input.selectionEnd = this.input.selectionStart + myValue.length;
} else {
this.input.value += myValue;
}
if (this.props.onChange) this.props.onChange({ target: { value: this.input.value } });
this.input.focus();
this.input.dispatchEvent(new Event('input'));
this.setState({
selected: -1,
currentList: [],
});
}
select(item: any) {
if (!item) {
return;
}
const m = this.getValueBeforeCaret().match(this.props.regexHooked[this.currentRegexUsed]);
if (m === null) {
return;
}
const alreadyTypedLength = m[0].length;
this.putTextAtCursor(
this.props.renderItemChoosen[this.currentRegexUsed](item),
alreadyTypedLength,
);
if (this.props.onSelect) {
this.props.onSelect(item, this.currentRegexUsed);
}
this.is_open = false;
}
setPositionResult() {
if (this.input && this.state.resultPosition === '' && this.state.currentList.length > 0) {
const size = this.state.currentList.length * 32 + 5;
if (
document.documentElement.clientHeight - (window as any).getBoundingClientRect(this.input).bottom <
size ||
this.props.position === 'top'
) {
this.setState({ resultPosition: 'top' });
} else {
this.setState({ resultPosition: 'bottom' });
}
}
}
focus() {
if (!this.input) {
return;
}
this.input.focus();
this.setState({ focused: true });
this.keyUp({});
}
blur() {
this.input.blur();
this.setState({ focused: false });
}
setContent(content: any) {
this.input.value = content;
}
render() {
this.setPositionResult();
return (
<div
className={
'autocomplete ' +
(this.props.className || '') +
' ' +
(this.state.focused && this.state.currentList.length ? 'focused ' : '')
}
onClick={() => {
this.focus();
this.setState({ focused: true });
}}
ref={node => (this.container = node)}
>
<div className={this.state.resultPosition}>
{this.props.autoHeight && (
<AutoHeight
className={'' + (this.props.big ? ' big' : this.props.small ? ' small' : ' medium')}
refInput={(ref: any) => {
this.input = ref;
}}
onResize={this.props.onResize}
placeholder={this.props.placeholder}
value={this.props.value}
onChange={this.props.onChange}
onKeyUp={this.props.onKeyUp}
onKeyDown={this.props.onKeyDown}
onKeyPress={this.props.onKeyPress}
onPaste={this.props.onPaste}
onFocus={() => {
if (this.props.onFocusChange) {
this.props.onFocusChange(true);
}
}}
onBlur={() => {
if (this.props.onFocusChange) {
this.props.onFocusChange(false);
}
}}
autoFocus={this.props.autoFocus}
/>
)}
{!this.props.autoHeight && (
<Input
className={
'full_width ' + (this.props.big ? ' big' : this.props.small ? ' small' : ' medium')
}
refInput={(ref: any) => {
this.input = ref;
}}
placeholder={this.props.placeholder}
value={this.props.value}
onChange={this.props.onChange}
onKeyUp={this.props.onKeyUp}
onKeyDown={this.props.onKeyDown}
onKeyPress={this.props.onKeyPress}
onPaste={this.props.onPaste}
onFocus={() => {
if (this.props.onFocusChange) {
this.props.onFocusChange(true);
}
}}
onBlur={() => {
if (this.props.onFocusChange) {
this.props.onFocusChange(false);
}
}}
autoFocus={this.props.autoFocus}
/>
)}
{!this.props.hideResult && this.state.currentList.length > 0 ? (
<div
className={
'menu-list as_frame inline ' +
(this.state.focused && this.state.currentList.length ? 'fade_in ' : '') +
this.state.resultPosition
}
>
{this.state.currentList.map((item, index) => {
return (
<div
key={index}
className={
'menu ' +
(!this.props.disableNavigationKey &&
item.autocomplete_id === this.state.selected
? 'is_selected'
: '')
}
onClick={() => this.select(item)}
>
{item && this.props.renderItem[this.currentRegexUsed](item)}
</div>
);
})}
</div>
) : (
''
)}
</div>
</div>
);
}
}
@@ -0,0 +1,95 @@
/* eslint-disable react/prop-types */
import React, { Component } from 'react';
import './auto-height.scss';
export default class AutoHeight extends Component {
constructor() {
super();
this.change = this.change.bind(this);
}
componentDidMount() {
this.textarea_offset =
parseInt(this.textarea.style.paddingTop || 0) +
parseInt(this.textarea.style.paddingBottom || 0);
window.addEventListener('resize', this.change);
this.change();
}
componentWillUnmount() {
window.removeEventListener('resize', this.change);
}
change() {
this.textarea.style.height = '1px';
var totalHeight = this.textarea.scrollHeight - this.textarea_offset;
this.textarea.style.height = totalHeight + 'px';
this.container.style.height = totalHeight + 'px';
if (this.oldHeight !== totalHeight && this.props.onResize) {
this.props.onResize();
}
this.oldHeight = totalHeight;
}
componentDidUpdate() {
this.change();
}
render() {
var className = this.props.className || '';
if (this.props.big) {
className += ' big ';
}
if (this.props.medium) {
className += ' medium ';
}
if (this.props.small) {
className += ' small ';
}
if (
className.indexOf('medium') === className.indexOf('small') &&
className.indexOf('big') === className.indexOf('small') &&
className.indexOf('big') < 0
) {
className += ' medium';
}
return (
<div
ref={node => (this.container = node)}
className={'input autoheight_container ' + className}
style={{
display: 'inline-block',
width: '100%',
minHeight: this.props.minHeight ? this.props.minHeight : '',
maxHeight: this.props.maxHeight ? this.props.maxHeight : '',
}}
onMouseEnter={() => {
this.change();
}}
>
<textarea
style={{
cursor: this.props.disabled ? 'not-allowed' : undefined,
maxHeight: this.props.maxHeight ? this.props.maxHeight : '',
minHeight: this.props.minHeight ? this.props.minHeight : '',
}}
{...this.props}
className="input full_width"
ref={node => {
if (this.props.refInput) {
this.props.refInput(node);
}
this.textarea = node;
}}
onChange={evt => {
if (this.props.onChange) {
this.props.onChange(evt);
}
}}
onKeyUp={this.props.onKeyUp}
onKeyDown={this.props.onKeyDown}
>
{this.props.children}
</textarea>
</div>
);
}
}
@@ -0,0 +1,54 @@
.input.autoheight_container {
textarea {
padding: 16px;
resize: none;
font-size: 14px;
&::-webkit-input-placeholder {
/* Chrome/Opera/Safari */
white-space: nowrap;
}
&::-moz-placeholder {
/* Firefox 19+ */
white-space: nowrap;
}
&:-ms-input-placeholder {
/* IE 10+ */
white-space: nowrap;
}
&:-moz-placeholder {
/* Firefox 18- */
white-space: nowrap;
}
}
&.small,
&.medium,
&.big {
padding: 0px;
}
&.small {
textarea {
padding: 4px 8px;
resize: none;
font-size: 14px;
line-height: 16px;
}
}
&.medium {
textarea {
padding: 13px 16px;
resize: none;
font-size: 14px;
line-height: 16px;
}
}
&.big {
textarea {
padding: 16px;
resize: none;
font-size: 16px;
line-height: 18px;
}
}
}
@@ -0,0 +1,5 @@
.avatar-component-container {
display: flex !important;
align-items: center;
justify-content: center;
}
@@ -0,0 +1,34 @@
import React from 'react';
import { Avatar, AvatarProps, Image } from 'antd';
import './avatar.scss';
type PropsType = {
url?: string;
size?: number;
shape?: AvatarProps['shape'];
borderRadius?: React.CSSProperties['borderRadius'];
fallback?: string;
onClick?: ((event: React.MouseEvent<HTMLDivElement, MouseEvent>) => void) | undefined;
};
const AvatarComponent = ({ url, shape, size, borderRadius, fallback, onClick }: PropsType) => (
<div onClick={onClick}>
<Avatar
className="avatar-component-container"
shape={shape || 'square'}
size={size}
style={{ width: size || 24, height: size || 24 }}
src={
<Image
src={url?.length ? url : fallback}
style={{ width: size || 24, borderRadius: borderRadius || 4 }}
preview={false}
/>
}
/>
</div>
);
export default AvatarComponent;
@@ -0,0 +1,43 @@
.ant-layout-header.banner-container {
padding: 0 16px;
&.primary {
background-color: var(--primary);
color: var(--white);
}
&.secondary {
background-color: var(--secondary);
color: var(--white);
border-bottom: 1px solid var(--black);
}
&.default {
background-color: var(--primary-background);
color: var(--black);
}
&.warning {
background-color: var(--warning);
color: var(--white);
}
&.important {
background-color: var(--red);
color: var(--white);
}
&.ghost {
background-color: transparent;
color: var(--black);
}
.banner-col-icon {
display: flex;
align-items: center;
.icon {
cursor: pointer;
}
}
}
@@ -0,0 +1,50 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import React, { FC } from 'react';
import { Col, Layout, Row } from 'antd';
import './banner.scss';
import { X } from 'react-feather';
import { CSSProperties } from '@material-ui/styles';
type PropsType = {
closable?: boolean;
content?: string | JSX.Element;
onClose?: () => any;
height?: string | number;
type: 'primary' | 'secondary' | 'default' | 'warning' | 'important' | 'ghost';
style?: CSSProperties;
contentColumnStyle?: CSSProperties;
className?: string;
children?: JSX.Element | JSX.Element[];
};
const Banner: FC<PropsType> = ({
closable,
content,
onClose,
height,
type,
children,
style,
contentColumnStyle,
className,
}) => {
const headerStyle = {
height: height ? height : 68,
lineHeight: height ? `${height}px` : '68px',
...style,
};
return (
<Layout.Header className={`banner-container ${type} ${className || ''}`} style={headerStyle}>
<Row align="middle" justify="space-between" gutter={[0, 0]} style={headerStyle}>
<Col /* ghost column */></Col>
<Col style={contentColumnStyle}>{content || children || ''}</Col>
<Col className="banner-col-icon">
{closable && <X size={16} className="icon" onClick={onClose} />}
</Col>
</Row>
</Layout.Header>
);
};
export default Banner;
@@ -0,0 +1,74 @@
/* eslint-disable react/prop-types */
import React from 'react';
import Loader from 'components/loader/loader.jsx';
import Button from './button.jsx';
import './buttons.scss';
export default class ButtonWithTimeout extends React.Component {
/*
props = {
loading : boolean
value : text in button
loadingTimeout : time before show loading
disabled
onClick
style
className
}
*/
constructor() {
super();
this.state = {
showLoader: false,
};
this.timeout = '';
}
componentWillUnmount() {
clearTimeout(this.timeout);
}
componentDidUpdate(prevProps) {
// eslint-disable-next-line @typescript-eslint/no-this-alias
var that = this;
if (prevProps.loading && !this.props.loading) {
if (this.state.showLoader) {
this.setState({ showLoader: false });
}
clearTimeout(this.timeout);
} else if (!prevProps.loading && this.props.loading) {
this.timeout = setTimeout(function () {
that.setState({ showLoader: true });
}, this.props.loadingTimeout || 2000);
}
}
render() {
return (
<div
className={'buttonWithTimeout_container ' + (this.state.showLoader ? 'is_loading ' : '')}
>
<Button
id={this.props.id}
refButton={node => (this.input = node)}
type="button"
style={this.props.style}
medium={this.props.medium}
small={this.props.small}
big={this.props.big}
disabled={this.props.disabled}
className={
'buttonWithTimeout ' +
(this.props.disabled ? 'buttonWithTimeoutDisabled' : '') +
' ' +
(this.props.className ? this.props.className : '')
}
onClick={this.props.onClick}
>
{this.props.value}
</Button>
<div className="loaderContainer">
{this.state.showLoader && <Loader className="loader" color="#FFF" />}
</div>
</div>
);
}
}
+40
View File
@@ -0,0 +1,40 @@
/* eslint-disable react/prop-types */
import React from 'react';
import './buttons.scss';
export default class Button extends React.Component {
render() {
var className = this.props.className || '';
if (this.props.big) {
className += ' big ';
}
if (this.props.medium) {
className += ' medium ';
}
if (this.props.small) {
className += ' small ';
}
if (
className.indexOf('medium') === className.indexOf('small') &&
className.indexOf('big') === className.indexOf('small') &&
className.indexOf('big') < 0
) {
className += ' small';
}
return (
<button
ref={this.props.refButton}
{...this.props}
className={'button no-tw ' + className}
onClick={evt => {
evt.target.blur();
this.props.onClick && this.props.onClick(evt);
}}
>
{this.props.value || this.props.children}
</button>
);
}
}
+185
View File
@@ -0,0 +1,185 @@
.buttonWithTimeout_container {
position: relative;
pointer-events: none;
& > * {
pointer-events: all;
}
.buttonWithTimeout {
padding: 0px 35px;
transition: padding 0.5s;
}
.loaderContainer {
color: #fff;
top: 0px;
right: 8px;
position: absolute;
display: inline-block;
transition: opacity 0.5s;
opacity: 0;
height: 100%;
align-items: center;
display: inline-flex;
.loader {
width: 30px;
height: 30px;
}
}
&.is_loading {
.buttonWithTimeout {
padding-right: 45px;
padding-left: 25px;
}
.loaderContainer {
opacity: 1;
}
}
}
.button, .button.no-tw {
background-color: var(--primary);
color: var(--white);
font-weight: 500;
border: 0px;
border-radius: var(--border-radius-base);
display: inline-block;
vertical-align: middle;
cursor: pointer;
&:hover {
background-color: var(--primary-hover);
}
&:active {
background-color: var(--primary);
}
&:focus {
outline: none;
}
&.disabled,
&:disabled {
background-color: var(--primary-background);
pointer-events: none;
}
&.small {
height: 32px;
line-height: 32px;
padding: 0px 8px;
font-size: 14px;
}
&.medium {
height: 40px;
line-height: 40px;
padding: 0px 12px;
font-size: 16px;
}
&.big {
height: 64px;
font-size: 18px;
line-height: 64px;
padding: 0px 24px;
}
&.secondary {
background: #807f8a;
color: var(--white);
&:hover {
background: #706f79;
}
&:active {
background: #696871;
}
&.disabled,
&:disabled {
background: #bcbcc2;
}
}
&.danger {
background: #ff1020;
color: var(--white);
&:hover {
background: #f00010;
}
&:active {
background: #d80000;
}
&.disabled,
&:disabled {
background: #ffa0a0;
}
}
&.secondary-light {
background-color: var(--grey-light);
color: var(--black);
&:hover,
&:active {
opacity: 0.9;
}
&.disabled,
&:disabled {
background-color: var(--grey-background);
color: var(--grey-dark);
}
}
&.primary-text {
background: transparent;
color: var(--primary);
font-size: 12px;
padding: 0 2px;
.m-icon-small {
font-size: 14px !important;
&:before {
margin: 0px;
}
}
&.disabled,
&:disabled {
color: var(--primary-background);
}
&:hover {
color: var(--primary-hover);
// background-color: var(--primary-background);
}
&:active {
// background-color: var(--primary-background);
}
}
&.secondary-text {
background: transparent;
color: var(--grey-dark);
font-size: 12px;
padding: 0 2px;
.m-icon-small {
font-size: 14px !important;
&:before {
margin: 0px;
}
}
&.disabled,
&:disabled {
color: var(--primary-background);
}
&:hover {
color: var(--black);
//background-color: var(--grey-background);
}
&:active {
color: var(--black);
}
}
.m-icon-small {
height: 100%;
}
}
@@ -0,0 +1,159 @@
/* eslint-disable react/prop-types */
/* eslint-disable react/jsx-key */
import React from 'react';
import './calendar-selector.scss';
import Collections from 'app/deprecated/CollectionsV1/Collections/Collections.js';
import Select from 'components/select/select.jsx';
import MenusManager from 'app/components/menus/menus-manager.jsx';
import WorkspacesService from 'app/deprecated/workspaces/workspaces.jsx';
import Languages from 'app/features/global/services/languages-service';
export default class CalendarSelector extends React.Component {
constructor(props) {
super();
this.props = props;
this.state = {
selected: [],
};
}
delete(item) {
var list = this.props.value.map(item => item.calendar_id);
const index = list.indexOf(item.id);
if (index >= 0) {
list.splice(index, 1);
this.change(list);
}
MenusManager.closeMenu();
}
openMenu(evt, item) {
if (this.props.readonly) {
return;
}
MenusManager.openMenu(
[
{
className: 'error',
icon: 'trash',
text: Languages.t('components.calendar.calendarselector.remove', [], 'Retirer'),
onClick: () => {
this.delete(item);
},
},
],
{ x: evt.clientX, y: evt.clientY, width: 150 },
'bottom',
);
}
change(list) {
if (this.props.onChange)
this.props.onChange(
list
.map(id => {
var cal = Collections.get('calendars').find(id);
if (!cal) {
return undefined;
}
return {
calendar_id: cal.id,
workspace_id: cal.workspace_id,
};
})
.filter(item => item),
);
}
render() {
return (
<div
className={
'calendar_selector_container ' +
(this.props.medium ? 'medium ' : '') +
(this.props.small ? 'small ' : '') +
(this.props.big ? 'big ' : '') +
this.props.className
}
>
<div className="selected_calendars_list calendar_selector_part">
{this.props.value.map(ws_cal_id => {
var item = Collections.get('calendars').find(ws_cal_id.calendar_id);
if (!item || item.workspace_id !== WorkspacesService.currentWorkspaceId) {
return '';
}
return (
<div
ref={node => (this.node = node)}
onClick={evt => this.openMenu(evt, item)}
className={'calendar_item ' + (this.props.readonly ? '' : 'removable')}
>
<div className="calendar_color" style={{ background: item.color }} />
{'' + item.title}
</div>
);
})}
{(!this.props.value || this.props.value.length === 0) && (
<div className="smalltext right-margin" style={{ lineHeight: '40px' }}>
{Languages.t(
'components.calendar.calendarselector.no_workspace_calendar',
[],
"Aucun calendrier d'espace de travail.",
)}
</div>
)}
</div>
{!this.props.readonly && (
<div className="list_calendar_to_select">
<Select
medium={this.props.medium}
small={this.props.small}
big={this.props.big}
options={[
{
type: 'title',
text: Languages.t('scenes.apps.calendar.left.calendars', [], 'Calendriers'),
className: 'no-background',
},
].concat(
this.props.calendarList.map(item => {
return {
text: (
<div className="calendar_selector_part calendar_item">
<div className="calendar_color" style={{ background: item.color }} />
{item.title}
</div>
),
value: item.id,
};
}),
)}
onChange={v => {
var list = this.props.value.map(item => item.calendar_id);
var existe = false;
// eslint-disable-next-line array-callback-return
list.map(id => {
if (v === id) existe = true;
});
if (!existe) {
list.push(v);
}
//Only one calendar for now
if (!this.props.allowMultiple) {
list = [v];
}
this.change(list);
}}
/>
</div>
)}
</div>
);
}
}
@@ -0,0 +1,61 @@
.calendar_selector_container {
display: inline-block;
vertical-align: middle;
.selected_calendars_list {
display: inline-block;
}
.list_calendar_to_select {
display: inline-block;
vertical-align: top;
.select {
display: inline-block;
vertical-align: top;
}
}
&.medium {
.calendar_item {
height: 40px;
padding: 4px 12px;
}
}
}
.calendar_selector_part {
.calendar_item {
height: 32px;
box-sizing: border-box;
line-height: 32px;
padding-left: 8px;
padding-right: 8px;
font-size: 14px;
display: inline-block;
background-color: var(--grey-background);
border-radius: var(--border-radius-base);
margin-right: 8px;
margin-bottom: 8px;
text-transform: capitalize;
&.removable {
cursor: pointer;
&:hover {
background: var(--primary-background);
}
}
}
.calendar_color {
width: 12px;
height: 12px;
border-radius: 6px;
display: inline-block;
margin: 10px 0;
vertical-align: top;
background: var(--green);
margin-right: 8px;
}
}
+423
View File
@@ -0,0 +1,423 @@
/* eslint-disable react/prop-types */
import React from 'react';
import Tooltip from 'components/tooltip/tooltip.jsx';
import moment from 'moment';
import DateTimeUtils from 'app/features/global/utils/datetime.js';
import UserService from 'app/features/users/services/current-user-service';
import Globals from 'app/features/global/services/globals-twake-app-service';
import DayPicker from './day-picker/day-picker.jsx';
import MenusManager from 'app/components/menus/menus-manager.jsx';
import Input from 'components/inputs/input.jsx';
import Icon from 'app/components/icon/icon.jsx';
import './date-picker.scss';
export default class DatePicker extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.display_format = 'D MMM YYYY';
this.old_props_ts = props.ts;
this.state = {
time_ts: props.ts,
time_string: moment(new Date(props.ts * 1000)).format(this.display_format),
};
this.user_language =
(UserService.getCurrentUser() || {}).language || Globals.window.navigator.language;
this.months_names = [
'jan',
'feb',
'mar',
'apr',
'may',
'jun',
'jul',
'aug',
'sep',
'oct',
'nov',
'dec',
].map(m => m.toLocaleLowerCase());
this.months_long_names = [
'january',
'february',
'march',
'april',
'may',
'june',
'july',
'august',
'september',
'october',
'november',
'december',
].map(m => m.toLocaleLowerCase());
this.months_names = this.months_names
.concat(
moment.localeData().monthsShort() || [
'jan',
'feb',
'mar',
'apr',
'may',
'jun',
'jul',
'aug',
'sep',
'oct',
'nov',
'dec',
],
)
.map(m => m.toLocaleLowerCase());
this.months_long_names = this.months_long_names
.concat(moment.localeData().months())
.map(m => m.toLocaleLowerCase());
this.months_names = this.months_names
.concat(moment.localeData(this.user_language).monthsShort())
.map(m => m.toLocaleLowerCase());
this.months_long_names = this.months_long_names
.concat(moment.localeData(this.user_language).months())
.map(m => m.toLocaleLowerCase());
if (
Globals.window.navigator &&
Globals.window.navigator.language &&
Globals.window.navigator.language !== this.user_language
) {
this.months_names = this.months_names.concat(
(moment.localeData(Globals.window.navigator.language).monthsShort() || []).map(m =>
m.toLocaleLowerCase(),
),
);
this.months_long_names = this.months_long_names.concat(
(moment.localeData(Globals.window.navigator.language).months() || []).map(m =>
m.toLocaleLowerCase(),
),
);
}
this.all_months = this.months_long_names.concat(this.months_names);
this.focused = false;
}
componentWillUnmount() {
if (this.helper_open) {
MenusManager.closeMenu();
}
}
shouldComponentUpdate(nextProps, nextStates) {
if (nextProps.ts !== this.old_props_ts) {
this.old_props_ts = nextProps.ts;
nextStates.time_ts = nextProps.ts;
if (!this.focused) {
nextStates.time_string = moment(new Date(nextProps.ts * 1000)).format(this.display_format);
}
}
return true;
}
process(string) {
var d, m, y;
var error = false;
if (string) {
var original_string = string;
//Try to find year
var year = string.match(/([0-9]{4,4})/);
if (year && year[1]) {
y = parseInt(year[1]);
string = string.replace(year[1], '');
}
//Try to find month as text
var sentences = string.match(/(^|[0-9])([^0-9]+)($|[0-9])/);
var in_text_month = false;
if (sentences && sentences[1] !== undefined) {
sentences.slice(1).forEach(s => {
// eslint-disable-next-line no-useless-escape
s = s.replace(/[0-9-'`~!@#$%^&*()_|+=?;:'",.<>\{\}\[\]\\\/]/gi, '');
s = s.trim();
s = s.toLocaleLowerCase();
if (s) {
var found = false;
this.all_months.forEach((m, i) => {
if (!found && m.indexOf(s) === 0) {
found = (i % 12) + 1;
}
});
if (found !== false) {
in_text_month = found;
string = string.replace(s, '');
}
}
});
}
if (in_text_month) {
m = in_text_month;
}
if (m && y) {
d = parseInt(string.replace(/[^0-9]/, ''));
} else {
//Get all numbers
var numbers = string.match(/[+-]?\d+(?:\.\d+)?/g);
if (numbers && numbers.length >= 1) {
if (y) {
//Only got year
if (numbers.length === 1) {
m = parseInt(numbers[0]);
} else {
//>=2
if (DateTimeUtils.isDateFirstInFormat()) {
d = parseInt(numbers[0]);
m = parseInt(numbers[1]);
} else {
d = parseInt(numbers[1]);
m = parseInt(numbers[0]);
}
if (m > 12) {
var tmp = d;
d = m;
m = tmp;
}
}
} else if (m) {
//Only got month
if (numbers.length === 1) {
d = parseInt(numbers[0]);
} else {
d = parseInt(numbers[0]);
y = parseInt(numbers[1]);
if (y < 100 && y >= 50) {
y += 1900;
} else if (y < 50) {
y += 2000;
}
}
} else {
//Got nothing
if (numbers.length === 1) {
y = parseInt(numbers[0]);
d = 1;
m = 1;
if (y < 100 && y >= 50) {
y += 1900;
} else if (y < 50) {
y += 2000;
}
} else if (numbers.length === 2) {
if (Math.max(parseInt(numbers[0]), parseInt(numbers[1])) > 12) {
y = Math.max(d, m);
m = Math.min(d, m);
} else {
d = parseInt(numbers[0]);
m = parseInt(numbers[1]);
}
if (m > 12) {
// eslint-disable-next-line no-redeclare
var tmp = m;
m = d;
d = tmp;
}
} else if (numbers.length >= 3) {
if (DateTimeUtils.isDateFirstInFormat()) {
d = parseInt(numbers[0]);
m = parseInt(numbers[1]);
y = parseInt(numbers[2]);
} else {
d = parseInt(numbers[1]);
m = parseInt(numbers[0]);
y = parseInt(numbers[2]);
}
}
if (y < 100 && y >= 50) {
y += 1900;
} else if (y < 50) {
y += 2000;
}
}
}
}
//Last check date is correct
if (!y || !m || !d || isNaN(y) || isNaN(m) || isNaN(d)) {
if ((y && !isNaN(y)) || (m && !isNaN(m)) || (d && !isNaN(d))) {
//If partial input, do not show error
if (y && !isNaN(y)) {
m = m || 1;
d = d || 1;
} else if (m && !isNaN(m)) {
y = new Date().getFullYear() + (m <= new Date().getMonth() + 1 ? 1 : 0);
d = d || 1;
} else if (d && !isNaN(d)) {
m = 1;
y = y || new Date().getFullYear() + (m <= new Date().getMonth() + 1 ? 1 : 0);
}
} else {
error = true;
}
}
if (y < 1950) {
error = true;
}
if (m < 1 || m > 12) {
error = true;
}
// eslint-disable-next-line no-redeclare
var tmp = new Date();
tmp.setMonth(m - 1);
tmp.setFullYear(y);
tmp.setDate(d);
if (d < 1 || d > 31 || tmp.getDate() !== d) {
error = true;
}
} else {
error = true;
}
this.setState({ time_string: original_string });
if (!error) {
// eslint-disable-next-line no-redeclare
var d = moment(d + '-' + m + '-' + y, 'DD-MM-YYYY').toDate();
this.changeDate(d);
} else {
this.setState({ error: true });
this.setState({});
}
}
changeDate(d, changeInput) {
var date = new Date(this.state.time_ts * 1000);
date.setDate(d.getDate());
date.setMonth(d.getMonth());
date.setFullYear(d.getFullYear());
this.setState({ time_ts: date.getTime() / 1000 });
this.setState({ error: false });
this.setState({
time_string_formatted: moment(this.state.time_ts * 1000).format(this.display_format),
});
if (changeInput) {
this.setState({ time_string: this.state.time_string_formatted });
}
this.props.onChange && this.props.onChange(this.state.time_ts);
this.setState({});
this.input.focus();
}
focus() {
this.closeMenuTimeout && clearTimeout(this.closeMenuTimeout);
this.focused = true;
if (!this.state.time_ts) {
this.setState({ time_ts: new Date().getTime() / 1000 });
}
if (!this.helper_open) {
this.helper_open = true;
var pos = window.getBoundingClientRect(this.input);
pos.x = pos.x || pos.left;
pos.y = pos.y || pos.top;
MenusManager.openMenu(
[
{
type: 'react-element',
reactElement: () => (
<div
style={{ padding: '20px 24px', margin: -16 }}
onMouseDown={() => {
this.cancelBlur = true;
}}
>
<DayPicker
value={moment(this.state.time_ts * 1000)}
onChange={value => {
var ts = value._d.getTime();
MenusManager.closeMenu();
setTimeout(() => {
this.changeDate(new Date(ts), true);
this.cancelBlur = false;
this.input.blur();
}, 100);
}}
onClick={() => this.input.focus()}
/>
</div>
),
},
],
{ x: pos.left + pos.width / 2, y: pos.bottom },
'bottom',
{ allowClickOut: false },
);
}
}
blur() {
if (this.cancelBlur) {
this.cancelBlur = false;
return;
}
this.focused = false;
this.setState({ time_string: this.state.time_string_formatted, error: false });
this.closeMenuTimeout && clearTimeout(this.closeMenuTimeout);
this.closeMenuTimeout = setTimeout(() => {
MenusManager.closeMenu();
this.helper_open = false;
}, 50);
this.props.onChangeBlur && this.props.onChangeBlur(this.state.time_ts);
this.props.onChange && this.props.onChange(this.state.time_ts);
}
render() {
return (
<div className={'date_selector ' + this.props.className} style={{ display: 'inline-block' }}>
<Tooltip position="top" tooltip="Invalide" overable={false} visible={this.state.error}>
<Input
onEchap={() => this.input.blur()}
onEnter={() => this.input.blur()}
onBlur={() => this.blur()}
onFocus={() => this.focus()}
placeholder={'No date'}
value={this.state.time_ts ? this.state.time_string : ''}
style={{ maxWidth: 108 }}
onChange={evt => this.process(evt.target.value)}
refInput={node => (this.input = node)}
big={this.props.big}
medium={this.props.medium}
small={this.props.small}
/>
{this.props.withReset && (
<div
className="reset_date"
onClick={() => {
this.setState({ time_ts: false });
this.props.onChangeBlur(this.state.time_ts);
this.setState({});
}}
>
<Icon type="trash" />
</div>
)}
</Tooltip>
</div>
);
}
}
@@ -0,0 +1,18 @@
.date_selector {
.reset_date {
width: 16px;
height: 16px;
position: absolute;
right: 8px;
top: 12px;
background: #eee;
border-radius: var(--border-radius-large);
font-size: 12px;
color: #444;
cursor: pointer;
&:hover {
background: #ddd;
}
}
}
@@ -0,0 +1,164 @@
/* eslint-disable react/prop-types */
import React, { Component } from 'react';
import Icon from 'app/components/icon/icon.jsx';
import moment from 'moment';
import './day-picker.scss';
export default class DayPicker extends Component {
/*
this.props :{
onChange(day)
value : Array of selected days
}
*/
constructor(props) {
super(props);
this.state = {
days: [],
currentDate: moment(),
};
this.openSelected = false;
window.moment = moment;
}
componentDidMount() {
this.setState(this.updateDay());
}
UNSAFE_componentWillUpdate(nextProps, nextState) {
if (moment(nextProps.value).valueOf() != this.oldProp) {
var obj = this.updateDay(nextProps.value);
nextState.days = obj.days;
nextState.currentDate = obj.currentDate;
}
this.oldProp = moment(nextProps.value).valueOf();
}
updateDay(date) {
var searchedDate = date || moment();
var days = [];
var first_day_of_month = moment(searchedDate).startOf('month').startOf('week');
for (var i = 0; i < 35; i++) {
days.push(moment(moment(first_day_of_month).add(i, 'days')));
}
return {
days: days,
currentDate: searchedDate,
};
}
isSelected(day) {
if (this.props.value) {
if (
Array.isArray(this.props.value) &&
this.props.value.length == 2 &&
this.props.value[0] &&
this.props.value[1]
) {
return day.isBetween(this.props.value[0], this.props.value[1], 'day', '[]');
} else if (
Array.isArray(this.props.value) &&
this.props.value.length >= 1 &&
this.props.value[0]
) {
return day.isSame(this.props.value[0], 'day');
} else if (!Array.isArray(this.props.value) && this.props.value) {
return day.isSame(this.props.value, 'day');
}
}
return false;
}
isLast(day) {
if (this.props.value) {
if (
Array.isArray(this.props.value) &&
this.props.value.length == 2 &&
this.props.value[0] &&
this.props.value[1]
) {
return day.isSame(this.props.value[1], 'day');
} else if (
Array.isArray(this.props.value) &&
this.props.value.length >= 1 &&
this.props.value[0]
) {
return day.isSame(this.props.value[0], 'day');
} else if (!Array.isArray(this.props.value) && this.props.value) {
return day.isSame(this.props.value, 'day');
}
}
return false;
}
render() {
var last_week = -1;
return (
<div
className="dayPicker"
onClick={() => this.props.onClick && this.props.onClick()}
onMouseDown={this.props.onMouseDown}
>
<div className="titleDayPicker">
<div className="month">{moment(this.state.currentDate).format('MMMM YYYY')}</div>
<div className="chevron">
<div
className="move_icon"
onClick={() => {
this.setState(this.updateDay(moment(this.state.currentDate).subtract(1, 'months')));
}}
>
<Icon type="angle-left" />
</div>
<div
className="move_icon"
onClick={() => {
this.setState(this.updateDay(moment(this.state.currentDate).add(1, 'months')));
}}
>
<Icon type="angle-right" />
</div>
</div>
</div>
<div className="days">
<div className="dayName" key={'wn'}>
Wk
</div>
{this.state.days.map((day, index) => {
if (index < 7) {
return (
<div className="dayName" key={day.format()}>
{day.format('ddd')}
</div>
);
}
})}
{this.state.days.map(day => {
var list = [];
if (day.week() != last_week) {
last_week = day.week();
list.push(<div className="weeknumber">{last_week}</div>);
}
list.push(
<div
key={day.format()}
onClick={() => {
if (this.props.onChange) {
this.props.onChange(day);
}
}}
className={
'day ' +
(day.month() == this.state.currentDate.month() ? '' : 'notInMonth') +
' ' +
(day.format('YYYY MM DD') == moment().format('YYYY MM DD') ? 'today' : '') +
' ' +
(this.isSelected(day) ? 'selected' : '') +
' ' +
(this.isLast(day) ? 'last' : '')
}
>
{day.date()}
</div>,
);
return list;
})}
</div>
</div>
);
}
}
@@ -0,0 +1,97 @@
.dayPicker {
width: 200px;
font-size: 14px;
font-weight: 500;
.titleDayPicker {
display: flex;
margin-top: -8px;
margin-bottom: -4px;
.month {
display: inline-block;
text-transform: capitalize;
line-height: 30px;
}
.chevron {
flex: 1;
text-align: right;
display: inline-block;
.move_icon {
display: inline-block;
font-size: 14px;
opacity: 1;
padding: 5px;
border-radius: var(--border-radius-base);
}
.move_icon:hover {
cursor: pointer;
background: rgba(55, 53, 47, 0.08);
}
}
}
.days {
.weeknumber {
background: #dfdfdf;
height: 26px;
line-height: 26px;
vertical-align: top;
display: inline-block;
font-size: 12px;
color: #000;
opacity: 0.3;
padding: 0 1px;
box-sizing: border-box;
width: 12.5%;
text-align: center;
}
.dayName {
display: inline-block;
width: 12.5%;
text-align: center;
height: 28px;
line-height: 28px;
font-size: 11px;
border-radius: var(--border-radius-base);
opacity: 0.6;
}
.dayName:first-child {
opacity: 0.3;
}
.day {
display: inline-block;
width: 12.5%;
text-align: center;
height: 24px;
line-height: 24px;
margin-bottom: 2px;
border-radius: var(--border-radius-base);
}
.day:not(.selected):hover {
background: rgba(45, 156, 219, 0.2);
}
.day:hover {
cursor: pointer;
}
.notInMonth {
opacity: 0.5;
}
.selected + .selected:not(.last) {
border-radius: 0px;
}
.selected {
background: #827dff;
border-radius: var(--border-radius-base) 0px 0px var(--border-radius-base);
color: white;
}
.selected + .last {
border-radius: 0px 4px 4px 0px;
}
:not(.selected) + .last {
border-radius: var(--border-radius-base);
}
.today:not(.selected) {
color: rgb(235, 87, 87);
border-radius: var(--border-radius-base);
}
}
}
+123
View File
@@ -0,0 +1,123 @@
/* eslint-disable react/prop-types */
import React, { Component } from 'react';
import './event.scss';
import Collections from 'app/deprecated/CollectionsV1/Collections/Collections.js';
import Icon from 'app/components/icon/icon.jsx';
import Languages from 'app/features/global/services/languages-service';
import UserListManager from 'components/user-list-manager-depreciated/user-list-manager';
import moment from 'moment';
export default class Event extends Component {
constructor(props) {
super();
this.props = props;
}
render() {
var className = '';
var icon = '';
var event_duration = '';
var classNameUser = '';
if (this.props.inEvent) {
classNameUser = 'inEvent';
}
var event_name =
this.props.event.title || Languages.t('scenes.apps.drive.navigators.new_file.untitled');
var event_start = moment(parseInt(this.props.event.from) * 1000).format('LT');
var event_end = moment(parseInt(this.props.event.to) * 1000).format('LT');
var location = this.props.event.location || '';
if (event_start) event_duration = event_start;
if (event_end)
event_duration =
event_duration + Languages.t('components.calendar.event.to', [], ' à ') + event_end;
var duration = (parseInt(this.props.event.to) - parseInt(this.props.event.from)) / 60;
if (
(duration > 0 && duration <= 15) ||
this.props.event.type == 'deadline' ||
this.props.event.type == 'remind'
) {
className += ' size_15 ';
} else if ((duration > 15 && duration <= 30) || this.props.event.all_day) {
className += ' size_30';
if (location) event_duration += ', ' + location;
} else if (duration > 30 && duration <= 45) {
className += ' size_45';
} else if (duration > 45 && duration <= 60) {
className += ' size_60';
}
className += ' ' + this.props.event.type;
if (this.props.event.type == 'remind') {
icon = 'stopwatch';
} else if (this.props.event.type == 'deadline') {
icon = 'stopwatch-slash';
} else if (this.props.event.type == 'move') {
icon = 'car-sideview';
}
var users = [];
var emails = [];
(this.props.event.participants || []).map(obj => {
if (obj) {
var regex = /^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i;
if (obj.user_id_or_mail.match(regex)) {
var user = Collections.get('users').find(obj.user_id_or_mail);
users.push(user);
} else {
var email = obj.user_id_or_mail;
emails.push(email);
}
}
});
return (
<div
className={'event_container ' + className + ' ' + classNameUser}
style={{ background: '' + this.props.getColor() }}
>
<div className={'block ' + className}>
{icon && <Icon type={'' + icon} className="icon" />}
<span className={'text title ' + className}>
{event_name}
<span style={{ fontWeight: 'normal' }}>
{(duration <= 30 || this.props.event.all_day) && ', ' + event_start}
</span>
</span>
</div>
{duration > 30 && !this.props.event.all_day && (
<div className={'time ' + className}>
<span className="text simple">{event_duration}</span>
</div>
)}
<div className={'place ' + className}>
<span className="text simple">{'' + location}</span>
</div>
<div className={'users ' + className}>
<UserListManager
noPlaceholder
users={(this.props.event.participants || []).map(u => {
return { id: u.user_id_or_mail };
})}
max={4}
readOnly
collapsed
medium
/>
</div>
</div>
);
}
}
+144
View File
@@ -0,0 +1,144 @@
.event_container {
margin: 0px;
width: auto;
height: auto;
border-radius: var(--border-radius-base);
color: var(--white);
box-sizing: border-box;
padding-left: 4px;
padding-right: 2px;
padding-top: 2px;
position: relative;
transition: box-shadow 0.2s;
cursor: pointer;
& > div {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
&:hover {
box-shadow: 0 0px 1000px 0px rgba(0, 0, 0, 0.1) inset;
}
.icon {
display: inline-block;
font-size: 14px;
font-weight: 700;
margin-left: -2px;
}
.text {
&.title {
font-size: 12px;
font-weight: 700;
}
&.simple {
font-size: 11px;
font-weight: 500;
}
}
.time,
.place,
.description,
.users {
display: flex;
flex-direction: row;
text-overflow: ellipsis;
}
.users {
position: absolute;
bottom: 2px;
right: 2px;
display: flex;
align-items: center;
height: 28px;
.emoji-container {
width: 24px;
text-align: center;
}
.more {
font-size: 12px;
margin: 0 2px;
font-weight: 700;
opacity: 0.8;
}
}
.time.size_15,
.place.size_15,
.place.size_30,
.users.size_15,
.users.size_30,
.users.size_45 {
.user_head {
width: 12px !important;
height: 12px !important;
}
}
.place.size_45 {
margin-top: 8px;
}
.users > div {
border-radius: var(--border-radius-base);
margin-left: 2px;
}
//event
min-height: 60px;
&.size_15 {
min-height: 16px;
height: 16px;
font-size: 11px;
}
&.size_30 {
min-height: 24px;
height: 24px;
}
&.size_45 {
min-height: 32px;
height: 32px;
}
&.size_60 {
min-height: 48px;
}
.inEvent {
border-style: solid;
border: 0px;
}
//rappel
&.remind {
border-radius: var(--border-radius-large);
min-height: 18px;
}
//deadline
&.deadline {
border-top-left-radius: 1px;
border-top-right-radius: 1px;
box-shadow: 0 2px 0px 0px rgba(255, 0, 0, 0.5) inset;
min-height: 18px;
}
//deplacement
&.move {
border-radius: var(--border-radius-large);
border-top-left-radius: 4px;
}
}
@@ -0,0 +1,121 @@
/* eslint-disable react/prop-types */
/* eslint-disable react/no-direct-mutation-state */
import React, { Component } from 'react';
import Input from 'components/inputs/input.jsx';
import Tooltip from 'components/tooltip/tooltip.jsx';
import moment from 'moment';
import DateTimeUtils from 'app/features/global/utils/datetime.js';
import Languages from 'app/features/global/services/languages-service';
export default class TimeSelector extends Component {
constructor(props) {
super(props);
this.state = {
time_ts: props.ts,
time_string: moment(new Date(props.ts * 1000)).format(DateTimeUtils.getDefaultTimeFormat()),
};
this.old_ts = props.ts;
this.focus = false;
}
shouldComponentUpdate(nextProps, nextStates) {
if (nextProps.ts != this.old_ts) {
this.old_ts = nextProps.ts;
nextStates.time_ts = nextProps.ts;
if (!this.focus) {
nextStates.time_string = moment(new Date(nextProps.ts * 1000)).format(
DateTimeUtils.getDefaultTimeFormat(),
);
}
}
return true;
}
process(string) {
var h, m, am_pm;
var error = false;
if (string) {
var match = string.match(/^[^0-9]*([0-9]+)([^0-9]+)?([0-9]*)?(.*?)$/);
if (match) {
h = parseInt(match[1]);
m = parseInt(match[3] || '0');
am_pm = ((match[4] ? match[4] : match[2]) || '').toLocaleLowerCase();
if (am_pm && (am_pm[0] == 'p' || am_pm.indexOf('pm') >= 0)) {
am_pm = 'PM';
} else if (am_pm && (am_pm[0] == 'a' || am_pm.indexOf('am') >= 0)) {
am_pm = 'AM';
} else {
if (h == 12) {
am_pm = 'PM';
} else {
am_pm = 'AM';
}
}
} else {
error = true;
}
if (h > 12) {
am_pm = '';
}
if (h > 23) {
error = true;
}
if (m > 59) {
error = true;
}
} else {
error = true;
}
this.state.time_string = string;
if (!error) {
var d = moment(h + ':' + m + am_pm, 'LT').toDate();
var date = new Date(this.state.time_ts * 1000);
date.setHours(d.getHours());
date.setMinutes(d.getMinutes());
this.state.time_ts = date.getTime() / 1000;
this.state.error = false;
this.state.time_string_formatted = moment(this.state.time_ts * 1000).format(
DateTimeUtils.getDefaultTimeFormat(),
);
this.props.onChange && this.props.onChange(this.state.time_ts);
} else {
this.state.error = true;
}
this.setState({});
}
blur() {
this.focus = false;
this.setState({ time_string: this.state.time_string_formatted, error: false });
this.props.onChangeBlur && this.props.onChangeBlur(this.state.time_ts);
this.props.onChange && this.props.onChange(this.state.time_ts);
}
render() {
return (
<div className={'time_selector ' + this.props.className} style={{ display: 'inline-block' }}>
<Tooltip
position="top"
tooltip={Languages.t('components.workspace.calendar.invalid', [], 'Invalide')}
overable={false}
visible={this.state.error}
>
<Input
onBlur={() => this.blur()}
onFocus={() => (this.focus = true)}
value={this.state.time_string}
style={{ maxWidth: 94 }}
onChange={evt => this.process(evt.target.value)}
/>
</Tooltip>
</div>
);
}
}
@@ -0,0 +1,44 @@
import { Modal, ModalContent } from 'app/atoms/modal';
import {
channelAttachmentListState,
activeChannelAttachementListTabState,
} from 'app/features/channels/state/channel-attachment-list';
import Languages from 'app/features/global/services/languages-service';
import Tab from 'app/molecules/tabs';
import React from 'react';
import { useRecoilState } from 'recoil';
import ChannelFiles from './parts/channel-files';
import ChannelMedias from './parts/channel-medias';
enum Tabs {
Medias = 0,
Files = 1,
}
export default (): React.ReactElement => {
const [open, setOpen] = useRecoilState(channelAttachmentListState);
const [activeTab, setActiveTab] = useRecoilState(activeChannelAttachementListTabState);
return (
<Modal open={open} onClose={() => setOpen(false)} className="sm:w-[60vw] sm:max-w-2xl">
<ModalContent textCenter title={Languages.t('components.channel_attachement_list.title')}>
<Tab
tabs={[
<div key="media">
<div className="flex">
{Languages.t('components.channel_attachement_list.medias')}
</div>
</div>,
<div key="files">
<div className="flex">{Languages.t('components.channel_attachement_list.files')}</div>
</div>,
]}
selected={activeTab}
onClick={index => setActiveTab(index)}
/>
{activeTab === Tabs.Medias && <ChannelMedias />}
{activeTab === Tabs.Files && <ChannelFiles />}
</ModalContent>
</Modal>
);
};
@@ -0,0 +1,176 @@
import { Button } from 'app/atoms/button/button';
import { DownloadIcon } from 'app/atoms/icons-agnostic';
import {
FileTypeArchiveIcon,
FileTypeDocumentIcon,
FileTypePdfIcon,
FileTypeSlidesIcon,
FileTypeSpreadsheetIcon,
FileTypeUnknownIcon,
} from 'app/atoms/icons-colored';
import { Base, Info } from 'app/atoms/text';
import { channelAttachmentListState } from 'app/features/channels/state/channel-attachment-list';
import fileUploadApiClient from 'app/features/files/api/file-upload-api-client';
import fileUploadService from 'app/features/files/services/file-upload-service';
import { formatDate } from 'app/features/global/utils/format-date';
import { formatSize } from 'app/features/global/utils/format-file-size';
import { Message, MessageFileType } from 'app/features/messages/types/message';
import routerService from 'app/features/router/services/router-service';
import { UserType } from 'app/features/users/types/user';
import { useFileViewerModal } from 'app/features/viewer/hooks/use-viewer';
import Media from 'app/molecules/media';
import React from 'react';
import { ArrowRight } from 'react-feather';
import { useRecoilState } from 'recoil';
type FileMessageType = {
message?: Message;
};
type FileUserType = {
user?: UserType;
};
type PropsType = {
file: MessageFileType & FileMessageType & FileUserType;
is_media: boolean;
};
type FilePreviewType = {
file: MessageFileType & FileMessageType & FileUserType;
};
export default ({ file, is_media }: PropsType): React.ReactElement => {
return is_media ? <ChannelMedia file={file} /> : <ChannelFile file={file} />;
};
const ChannelFile = ({ file }: FilePreviewType): React.ReactElement => {
const [, setOpen] = useRecoilState(channelAttachmentListState);
const name = file?.metadata?.name;
const extension = name?.split('.').pop();
const previewUrl = fileUploadApiClient.getFileThumbnailUrlFromMessageFile(file);
const fileType = fileUploadApiClient.mimeToType(file?.metadata?.mime || '');
const { open: openViewer } = useFileViewerModal();
const iconClassName = previewUrl
? 'absolute left-0 top-0 bottom-0 right-0 m-auto w-8 h-8'
: 'absolute bottom-1 left-1 w-6 h-6';
return (
<div
className="flex items-center p-2 hover:bg-zinc-50 rounded-md cursor-pointer"
onClick={() => openViewer(file)}
>
<div className="relative flex bg-zinc-200 rounded-md w-16 h-16 mr-3">
<Media size="md" url={previewUrl} duration={fileType === 'video' ? extension : undefined} />
{(!['image', 'video'].includes(fileType) || !previewUrl) && (
<>
{fileType === 'archive' ? (
<FileTypeArchiveIcon className={iconClassName} />
) : fileType === 'pdf' ? (
<FileTypePdfIcon className={iconClassName} />
) : fileType === 'document' ? (
<FileTypeDocumentIcon className={iconClassName} />
) : fileType === 'slides' ? (
<FileTypeSlidesIcon className={iconClassName} />
) : fileType === 'spreadsheet' ? (
<FileTypeSpreadsheetIcon className={iconClassName} />
) : (
<FileTypeUnknownIcon className={iconClassName} />
)}
</>
)}
</div>
<div className="grow mr-3 overflow-hidden">
<Base className="block whitespace-nowrap overflow-hidden text-ellipsis">{name}</Base>
<Info className="block whitespace-nowrap overflow-hidden text-ellipsis">
{extension?.toLocaleUpperCase()} {formatDate(file?.message?.created_at)} {' '}
{formatSize(file?.metadata?.size)}
</Info>
</div>
<div
className="whitespace-nowrap"
onClick={e => {
e.preventDefault();
e.stopPropagation();
}}
>
<Button
theme="outline"
className="w-9 px-1.5 ml-2 rounded-full border-none"
onClick={() => downloadFile(file)}
>
<DownloadIcon className="w-6 h-6" />
</Button>
{!!file.message && (
<Button
theme="outline"
className="w-9 px-1.5 ml-2 rounded-full border-none"
onClick={() => {
setOpen(false);
gotoMessage(file.message as Message);
}}
>
<ArrowRight className="w-6 h-6" />
</Button>
)}
</div>
</div>
);
};
const ChannelMedia = ({ file }: FilePreviewType): React.ReactElement => {
const previewUrl = fileUploadApiClient.getFileThumbnailUrlFromMessageFile(file);
const type = fileUploadApiClient.mimeToType(file?.metadata?.mime || '');
const { open: openViewer } = useFileViewerModal();
return (
<div
className="cursor-pointer hover:opacity-75 inline-block m-2"
onClick={() => openViewer(file)}
>
<Media
key={file.id}
size="lg"
url={previewUrl}
duration={
type === 'video'
? file?.metadata?.name?.split('.').slice(-1)?.[0]?.toLocaleUpperCase()
: undefined
}
/>
</div>
);
};
const getFileDownloadRoute = (file: MessageFileType): string => {
if (file?.metadata?.source !== 'internal') {
return '';
}
return fileUploadService.getDownloadRoute({
companyId: file.metadata?.external_id?.company_id,
fileId: file.metadata?.external_id?.id,
});
};
const downloadFile = (file: MessageFileType): void => {
const url = getFileDownloadRoute(file);
if (url) {
window.location.href = url;
}
};
const gotoMessage = (message: Message): void => {
routerService.push(
routerService.generateRouteFromState({
companyId: message?.cache?.company_id,
channelId: message?.cache?.channel_id,
threadId: message?.thread_id,
workspaceId: message?.cache?.workspace_id,
...(message.id !== message?.thread_id ? { messageId: message.id } : {}),
}),
);
};
@@ -0,0 +1,36 @@
import { useChannelFileList } from 'app/features/channels/hooks/use-channel-media-files';
import React, { useEffect } from 'react';
import ChannelAttachment from './channel-attachment';
import { LoadingAttachements, NoAttachements } from './commun';
import PerfectScrollbar from 'react-perfect-scrollbar';
type PropsType = {
maxItems?: number;
};
export default ({ maxItems }: PropsType): React.ReactElement => {
const { loading, result, loadMore, loadItems } = useChannelFileList();
useEffect(() => {
loadItems();
}, []);
return (
<PerfectScrollbar
className="-mb-4 py-3 overflow-hidden -mx-2 px-2"
style={{ maxHeight: 'calc(80vh - 100px)', minHeight: 'calc(80vh - 100px)' }}
options={{ suppressScrollX: true, suppressScrollY: false }}
component="div"
onYReachEnd={() => loadMore()}
>
{result.length === 0 && !loading && <NoAttachements />}
{loading ? (
<LoadingAttachements />
) : (
result
.slice(0, maxItems || result.length)
.map(file => <ChannelAttachment key={file.id} file={file} is_media={false} />)
)}
</PerfectScrollbar>
);
};
@@ -0,0 +1,44 @@
import { useChannelMediaList } from 'app/features/channels/hooks/use-channel-media-files';
import fileUploadApiClient from 'app/features/files/api/file-upload-api-client';
import React, { useEffect } from 'react';
import ChannelAttachment from './channel-attachment';
import { LoadingAttachements, NoAttachements } from './commun';
import PerfectScrollbar from 'react-perfect-scrollbar';
type PropsType = {
maxItems?: number;
};
export default ({ maxItems }: PropsType): React.ReactElement => {
const { loading, result, loadMore, loadItems } = useChannelMediaList();
useEffect(() => {
loadItems();
}, []);
return (
<>
<PerfectScrollbar
className="-mb-4 py-3 overflow-hidden -mx-2 px-2"
style={{ maxHeight: 'calc(80vh - 100px)', minHeight: 'calc(80vh - 100px)' }}
options={{ suppressScrollX: true, suppressScrollY: false }}
component="div"
onYReachEnd={() => loadMore()}
>
{result.length === 0 && !loading && <NoAttachements />}
{loading ? (
<LoadingAttachements />
) : (
result
.slice(0, maxItems || result.length)
.map(file => {
const url = fileUploadApiClient.getFileThumbnailUrlFromMessageFile(file);
return url && <ChannelAttachment key={file.id} file={file} is_media={true} />;
})
.filter(Boolean)
)}
</PerfectScrollbar>
</>
);
};
@@ -0,0 +1,22 @@
import { Loader } from 'app/atoms/loader';
import { Info } from 'app/atoms/text';
import Languages from 'app/features/global/services/languages-service';
import React from 'react';
export const LoadingAttachements = (): React.ReactElement => {
return (
<div className="flex h-full justify-center items-center px-2">
<Loader className="h-8 w-8 m-auto" />
</div>
);
};
export const NoAttachements = (): React.ReactElement => {
return (
<div className="flex items-center justify-center flex-col h-64 px-2">
<Info className="p-2">
{Languages.t('components.channel_attachement_list.nothing_found')}
</Info>
</div>
);
};
@@ -0,0 +1,178 @@
import { InformationCircleIcon } from '@heroicons/react/outline';
import { SearchIcon } from '@heroicons/react/solid';
import { Alert } from 'app/atoms/alert';
import { InputDecorationIcon } from 'app/atoms/input/input-decoration-icon';
import { Input } from 'app/atoms/input/input-text';
import Text, { Info } from 'app/atoms/text';
import { useSearchChannelMembersAll } from 'app/features/channel-members-search/hooks/use-search-all';
import Languages from 'app/features/global/services/languages-service';
import { delayRequest } from 'app/features/global/utils/managedSearchRequest';
import Strings from 'app/features/global/utils/strings';
import { useInvitationUsers } from 'app/features/invitation/hooks/use-invitation-users';
import { invitationState } from 'app/features/invitation/state/invitation';
import useRouterChannel from 'app/features/router/hooks/use-router-channel';
import { useEffect, useState } from 'react';
import PerfectScrollbar from 'react-perfect-scrollbar';
import { useRecoilState } from 'recoil';
import { EmailItem } from './email-item';
import { MemberItem } from './member-item';
import { UserItem } from './user-item';
import AccessRightsService from 'app/features/workspace-members/services/workspace-members-access-rights-service';
import { useCurrentWorkspace } from 'app/features/workspaces/hooks/use-workspaces';
export const ChannelMembersListModal = (): JSX.Element => {
const channelId = useRouterChannel();
const [query, setQuery] = useState<string>('');
const { addEmailSuggestion, pendingEmailList, channelMembersList, usersList, search, refresh } =
useSearchChannelMembersAll({
channelId,
});
useEffect(() => {
delayRequest('ChannelMembersListModal', async () => {
search(query);
});
}, [search, query]);
useEffect(() => {
if (channelId) refresh();
}, [channelId]);
return (
<div
className="flex flex-col max-w-full space-y-1"
style={{ height: '80vh', maxHeight: '400px' }}
>
<div>
<InputDecorationIcon
className="mb-2"
suffix={SearchIcon}
input={({ className }) => (
<Input
className={className}
placeholder={Languages.t('scenes.client.channelbar.channelmemberslist.search_invite')}
onChange={e => {
setQuery(e.target.value);
}}
value={query}
/>
)}
/>
{pendingEmailList?.length === 0 && channelMembersList?.length <= 1 && (
<Alert
className="mb-0"
theme="primary"
title={Languages.t('scenes.client.channelbar.channelmemberslist.search_invite_notice')}
icon={InformationCircleIcon}
/>
)}
</div>
<div className="-mx-3">
<PerfectScrollbar
options={{ suppressScrollX: true, suppressScrollY: false }}
component="div"
style={{ width: '100%', height: '100%' }}
>
<div className="mx-1">
{addEmailSuggestion &&
pendingEmailList?.length === 0 &&
channelMembersList?.length === 0 &&
usersList?.length === 0 &&
!Strings.verifyMail(query) && (
<>
<Info className="px-2 mt-2 block">
{Languages.t(
'scenes.client.channelbar.channelmemberslist.search_invite_type_email',
)}
</Info>
</>
)}
{addEmailSuggestion && <EmailSuggestion email={query} />}
{pendingEmailList?.length > 0 && (
<>
<Info className="px-2 mt-2 block">
{Languages.t('scenes.client.channelbar.channelmemberslist.pending_section')}
</Info>
</>
)}
{pendingEmailList &&
pendingEmailList.map((item, index) => {
return (
<div key={`key_${index}`} className="flex py-1 hover:bg-zinc-50 rounded-sm px-2">
<EmailItem email={item} />
</div>
);
})}
{channelMembersList?.length > 0 && (
<>
<Info className="px-2 mt-2 block">
{Languages.t('scenes.client.channelbar.channelmemberslist.members_section')}
</Info>
</>
)}
{channelMembersList &&
channelMembersList
.filter(a => a && a?.user)
.map(cMember => {
return (
<div
key={cMember.user_id}
className="flex py-1 hover:bg-zinc-50 rounded-sm px-2"
>
<MemberItem userId={cMember.user_id} member={cMember} />
</div>
);
})}
{usersList?.length > 0 && (
<>
<Info className="px-2 mt-2 block">
{Languages.t('scenes.client.channelbar.channelmemberslist.not_members_section')}
</Info>
</>
)}
{usersList &&
usersList.map(user => {
return (
<div key={user.id} className="flex py-1 hover:bg-zinc-50 rounded-sm px-2">
<UserItem userId={user.id || ''} />
</div>
);
})}
</div>
</PerfectScrollbar>
</div>
</div>
);
};
const EmailSuggestion = ({ email }: { email: string }) => {
const [, setInvitationOpen] = useRecoilState(invitationState);
const { addInvitation, allowed_guests, allowed_members } = useInvitationUsers();
const { workspace } = useCurrentWorkspace();
const invite = () => {
addInvitation(email);
setInvitationOpen(true);
};
if (!email || !Strings.verifyMail(email)) {
return <></>;
}
return AccessRightsService.hasLevel(workspace?.id, 'moderator') &&
(allowed_guests > 0 || allowed_members > 0) ? (
<div>
<Info className="px-2 mt-2 mb-2 block items-center flex">
<Text type="base" className="cursor-pointer" onClick={() => invite()}>
{Languages.t(
'scenes.client.channelbar.channelmemberslist.invite_to_workspace',
[email],
`Invite ${email} to the workspace ➡`,
)}
</Text>
</Info>
</div>
) : (
<></>
);
};
@@ -0,0 +1,106 @@
import { MailIcon } from '@heroicons/react/outline';
import { XIcon } from '@heroicons/react/solid';
import { Tooltip } from 'antd';
import { Button } from 'app/atoms/button/button';
import { Modal, ModalContent } from 'app/atoms/modal';
import { Info } from 'app/atoms/text';
import { usePendingEmail } from 'app/features/channel-members-search/hooks/use-pending-email-hook';
import Languages from 'app/features/global/services/languages-service';
import { PendingEmail } from 'app/features/pending-emails/types/pending-email';
import useRouterCompany from 'app/features/router/hooks/use-router-company';
import useRouterWorkspace from 'app/features/router/hooks/use-router-workspace';
import WorkspaceUserAPIClient from 'app/features/workspace-members/api/workspace-members-api-client';
import { WorkspacePendingUserType } from 'app/features/workspaces/types/workspace';
import { useState } from 'react';
type IProps = {
email: PendingEmail;
};
export const EmailItem = (props: IProps): JSX.Element => {
const { email } = props.email;
const { loading, cancelInvite } = usePendingEmail(email);
const workspaceId = useRouterWorkspace();
const companyId = useRouterCompany();
const [cancelWorkspaceInvitation, setCancelWorkspaceInvitation] =
useState<WorkspacePendingUserType | null>(null);
const _cancelInvite = async () => {
//Also check if the user is pending in the workspace and display something if that's the case
const pending = await WorkspaceUserAPIClient.listPending(companyId, workspaceId);
const foundWorkspacePending = pending.find(
p => p.email.toLocaleLowerCase() === email.toLocaleLowerCase(),
);
if (foundWorkspacePending) {
//Propose to also cancel workspace invitation
setCancelWorkspaceInvitation(foundWorkspacePending);
} else {
await cancelInvite();
}
};
return (
<>
<Modal open={!!cancelWorkspaceInvitation} onClose={() => setCancelWorkspaceInvitation(null)}>
<ModalContent
theme="warning"
title="Remove workspace invitation ?"
text="Do you want to cancel also the workspace invitation?"
buttons={[
<Button
key="no"
theme="default"
onClick={() => {
setCancelWorkspaceInvitation(null);
cancelInvite();
}}
>
{Languages.t('general.no')}
</Button>,
<Button
className="mr-2"
key="yes"
theme="primary"
onClick={() => {
setCancelWorkspaceInvitation(null);
cancelInvite();
WorkspaceUserAPIClient.cancelPending(companyId, workspaceId, email);
}}
>
{Languages.t('general.yes')}
</Button>,
]}
></ModalContent>
</Modal>
<div className="flex grow items-center space-x-1">
<MailIcon className="h-6 w-6 text-zinc-500" />
<div>
<span className="pl-2">
{email}{' '}
<Info>
(
{Languages.t(
'scenes.client.channels_bar.modals.parts.channel_member_row.label.pending_email',
)}
)
</Info>
</span>
</div>
</div>
<div>
<Tooltip
placement="top"
title={Languages.t('scenes.app.popup.workspaceparameter.pages.cancel_invitation')}
>
<Button
theme="default"
size="sm"
icon={XIcon}
loading={loading}
onClick={() => _cancelInvite()}
></Button>
</Tooltip>
</div>
</>
);
};
@@ -0,0 +1,81 @@
import Avatar from 'app/atoms/avatar';
import { ChannelMemberWithUser } from 'app/features/channel-members-search/types/channel-members';
import Languages from 'app/features/global/services/languages-service';
import UsersService from 'app/features/users/services/current-user-service';
import useRouterCompany from 'app/features/router/hooks/use-router-company';
import useRouterWorkspace from 'app/features/router/hooks/use-router-workspace';
import useRouterChannel from 'app/features/router/hooks/use-router-channel';
import { useChannelMember } from 'app/features/channel-members-search/hooks/member-hook';
import { LogoutIcon } from '@heroicons/react/outline';
import { Tooltip } from 'antd';
import { ButtonConfirm } from 'app/atoms/button/confirm';
import MemberGrade from 'app/views/client/popup/WorkspaceParameter/Pages/WorkspacePartnerTabs/MemberGrade';
type IMemberProps = {
member: ChannelMemberWithUser;
userId?: string;
};
export const MemberItem = (props: IMemberProps): JSX.Element => {
const { member, userId } = props;
const { first_name, email } = member.user;
const companyId = useRouterCompany();
const workspaceId = useRouterWorkspace();
const channelId = useRouterChannel();
const parameters = { companyId, workspaceId, channelId };
const { leave, loading } = useChannelMember(userId || '', parameters);
const isCurrentUser = (): boolean => {
const currentUserId: string = UsersService.getCurrentUserId();
return props.userId === currentUserId;
};
return (
<>
<div className="w-8 flex items-center ">
<Avatar className="" size="xs" avatar={UsersService.getThumbnail(member.user)} />
</div>
<div className="grow flex items-center overflow-hidden ">
<div className="whitespace-nowrap overflow-hidden text-ellipsis">
<span className="font-bold">{first_name}</span>
<span className="pl-2">{email}</span>
</div>
</div>
<div className="mr-2 flex items-center">
<MemberGrade
companyRole={member.user.companies?.find(c => c.company.id === companyId)?.role || ''}
workspaceRole={member.user.workspaces?.find(c => c.id === workspaceId)?.role || ''}
/>
</div>
<div>
{isCurrentUser() ? (
<Tooltip placement="top" title={Languages.t('scenes.app.channelsbar.channel_leaving')}>
<ButtonConfirm
theme="danger"
size="sm"
icon={LogoutIcon}
loading={loading}
onClick={() => leave(props.userId || '')}
/>
</Tooltip>
) : (
<Tooltip
placement="top"
title={Languages.t('scenes.client.channelbar.channelmemberslist.menu.option_2')}
>
<ButtonConfirm
theme="primary"
size="sm"
icon={LogoutIcon}
loading={loading}
onClick={() => leave(props.userId || '')}
/>
</Tooltip>
)}
</div>
</>
);
};
@@ -0,0 +1,100 @@
import Avatar from 'app/atoms/avatar';
import UsersService from 'app/features/users/services/current-user-service';
import { useUser } from 'app/features/users/hooks/use-user';
import { Button } from 'app/atoms/button/button';
import Languages from 'app/features/global/services/languages-service';
import { useChannelMember } from 'app/features/channel-members-search/hooks/member-hook';
import { PlusIcon } from '@heroicons/react/solid';
import useRouterWorkspace from 'app/features/router/hooks/use-router-workspace';
import React from 'react';
import { Modal, ModalContent } from 'app/atoms/modal';
import useRouterCompany from 'app/features/router/hooks/use-router-company';
import ConsoleService from 'app/features/console/services/console-service';
import MemberGrade from 'app/views/client/popup/WorkspaceParameter/Pages/WorkspacePartnerTabs/MemberGrade';
type IUserProps = {
userId: string;
};
export const UserItem = (props: IUserProps): JSX.Element => {
const { userId } = props;
const user = useUser(userId || '');
const workspaceId = useRouterWorkspace();
const companyId = useRouterCompany();
const [confirmWorkspaceInvitation, setConfirmWorkspaceInvitation] =
React.useState<boolean>(false);
if (!user) {
return <></>;
}
const { addMember, loading } = useChannelMember(userId || '');
const [full_name, avatar] = [UsersService.getFullName(user), UsersService.getThumbnail(user)];
const _addMember = () => {
if (!(user.workspaces || []).map(w => w.id).includes(workspaceId)) {
//Ask for confirmation to invite the user to the workspace first
setConfirmWorkspaceInvitation(true);
} else {
addMember(userId || '');
}
};
return (
<>
<Modal open={confirmWorkspaceInvitation} onClose={() => setConfirmWorkspaceInvitation(false)}>
<ModalContent
theme="warning"
title="Also invite to the workspace?"
text="This user is not in the current workspace, he will be invited."
buttons={[
<Button key="no" theme="default" onClick={() => setConfirmWorkspaceInvitation(false)}>
{Languages.t('general.no')}
</Button>,
<Button
className="mr-2"
key="yes"
theme="primary"
onClick={async () => {
setConfirmWorkspaceInvitation(false);
await ConsoleService.addMailsInWorkspace({
workspace_id: workspaceId || '',
company_id: companyId || '',
emails: [user.email],
});
await addMember(userId || '');
}}
>
{Languages.t('general.yes')}
</Button>,
]}
></ModalContent>
</Modal>
<div className="w-8 flex items-center ">
<Avatar size="xs" avatar={avatar} />
</div>
<div className="grow flex items-center overflow-hidden ">
<div className="whitespace-nowrap overflow-hidden text-ellipsis">
<span className="font-bold">{full_name}</span>
<span className="pl-2">{user.email}</span>
</div>
</div>
<div className="mr-2 flex items-center">
<MemberGrade
companyRole={user.companies?.find(c => c.company.id === companyId)?.role || ''}
workspaceRole={user.workspaces?.find(c => c.id === workspaceId)?.role || ''}
/>
</div>
<div>
<Button
theme="primary"
size="sm"
icon={PlusIcon}
loading={loading}
onClick={() => _addMember()}
/>
</div>
</>
);
};

Some files were not shown because too many files have changed in this diff Show More