mirror of
https://github.com/grafana/grafana.git
synced 2025-01-02 12:17:01 -06:00
Icons: Adds custom icon support ands new panel and interpolation icons (#30277)
* Icons: Adds custom icon support ands new panel and interpolation icons * Removed icon files * updated snapshot * Updates
This commit is contained in:
parent
a9338542b6
commit
d10dbc70a1
@ -5,6 +5,7 @@ export interface SelectableValue<T = any> {
|
||||
label?: string;
|
||||
value?: T;
|
||||
imgUrl?: string;
|
||||
icon?: string;
|
||||
description?: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ export interface RadioButtonProps {
|
||||
size?: RadioButtonSize;
|
||||
disabled?: boolean;
|
||||
name?: string;
|
||||
description?: string;
|
||||
active: boolean;
|
||||
id: string;
|
||||
onChange: () => void;
|
||||
@ -101,6 +102,7 @@ export const RadioButton: React.FC<RadioButtonProps> = ({
|
||||
onChange,
|
||||
id,
|
||||
name = undefined,
|
||||
description,
|
||||
fullWidth,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
@ -117,7 +119,7 @@ export const RadioButton: React.FC<RadioButtonProps> = ({
|
||||
checked={active}
|
||||
name={name}
|
||||
/>
|
||||
<label className={cx(styles.radioLabel)} htmlFor={id}>
|
||||
<label className={cx(styles.radioLabel)} htmlFor={id} title={description}>
|
||||
{children}
|
||||
</label>
|
||||
</>
|
||||
|
@ -16,33 +16,7 @@ export default {
|
||||
|
||||
const sizes: RadioButtonSize[] = ['sm', 'md'];
|
||||
|
||||
export const Simple = () => {
|
||||
const [selected, setSelected] = useState('graphite');
|
||||
const BEHAVIOUR_GROUP = 'Behaviour props';
|
||||
const disabled = boolean('Disabled', false, BEHAVIOUR_GROUP);
|
||||
const disabledItem = select('Disabled item', ['', 'graphite', 'prometheus', 'elastic'], '', BEHAVIOUR_GROUP);
|
||||
const VISUAL_GROUP = 'Visual options';
|
||||
const size = select<RadioButtonSize>('Size', sizes, 'md', VISUAL_GROUP);
|
||||
|
||||
const options = [
|
||||
{ label: 'Prometheus', value: 'prometheus' },
|
||||
{ label: 'Graphite', value: 'graphite' },
|
||||
{ label: 'Elastic', value: 'elastic' },
|
||||
];
|
||||
|
||||
return (
|
||||
<RadioButtonGroup
|
||||
options={options}
|
||||
disabled={disabled}
|
||||
disabledOptions={[disabledItem]}
|
||||
value={selected}
|
||||
onChange={v => setSelected(v!)}
|
||||
size={size}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const FullWidth = () => {
|
||||
export const RadioButtons = () => {
|
||||
const [selected, setSelected] = useState('elastic');
|
||||
const BEHAVIOUR_GROUP = 'Behaviour props';
|
||||
const disabled = boolean('Disabled', false, BEHAVIOUR_GROUP);
|
||||
@ -52,21 +26,45 @@ export const FullWidth = () => {
|
||||
|
||||
const options = [
|
||||
{ label: 'Prometheus', value: 'prometheus' },
|
||||
{ label: 'Graphite', value: 'graphite' },
|
||||
{ label: 'Graphite', value: 'graphite', icon: 'cloud' },
|
||||
{ label: 'Elastic', value: 'elastic' },
|
||||
];
|
||||
|
||||
const optionsWithOnlyIcons = [
|
||||
{ description: 'Prometheus', value: 'prometheus', icon: 'gf-interpolation-linear' },
|
||||
{ description: 'Graphite', value: 'graphite', icon: 'gf-interpolation-smooth' },
|
||||
{ description: 'Elastic', value: 'elastic', icon: 'gf-interpolation-step-after' },
|
||||
];
|
||||
|
||||
return (
|
||||
<div style={{ width: '100%' }}>
|
||||
<RadioButtonGroup
|
||||
options={options}
|
||||
disabled={disabled}
|
||||
disabledOptions={[disabledItem]}
|
||||
value={selected}
|
||||
onChange={v => setSelected(v!)}
|
||||
size={size}
|
||||
fullWidth
|
||||
/>
|
||||
<div style={{ marginBottom: '32px' }}>
|
||||
<h5>Full width</h5>
|
||||
<RadioButtonGroup
|
||||
options={options}
|
||||
disabled={disabled}
|
||||
disabledOptions={[disabledItem]}
|
||||
value={selected}
|
||||
onChange={v => setSelected(v!)}
|
||||
size={size}
|
||||
fullWidth
|
||||
/>
|
||||
</div>
|
||||
<div style={{ marginBottom: '32px' }}>
|
||||
<h5>Auto width</h5>
|
||||
<RadioButtonGroup
|
||||
options={options}
|
||||
disabled={disabled}
|
||||
disabledOptions={[disabledItem]}
|
||||
value={selected}
|
||||
onChange={v => setSelected(v!)}
|
||||
size={size}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ marginBottom: '32px' }}>
|
||||
<h5>With only icons and descriptions</h5>
|
||||
<RadioButtonGroup options={optionsWithOnlyIcons} value={selected} onChange={v => setSelected(v!)} size={size} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -4,6 +4,7 @@ import uniqueId from 'lodash/uniqueId';
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { RadioButtonSize, RadioButton } from './RadioButton';
|
||||
import { Icon } from '../../Icon/Icon';
|
||||
import { IconName } from '../../../types/icon';
|
||||
|
||||
const getRadioButtonGroupStyles = () => {
|
||||
return {
|
||||
@ -85,8 +86,9 @@ export function RadioButtonGroup<T>({
|
||||
id={`option-${o.value}-${id}`}
|
||||
name={groupName.current}
|
||||
fullWidth={fullWidth}
|
||||
description={o.description}
|
||||
>
|
||||
{o.icon && <Icon name={o.icon} className={styles.icon} />}
|
||||
{o.icon && <Icon name={o.icon as IconName} className={styles.icon} />}
|
||||
{o.label}
|
||||
</RadioButton>
|
||||
);
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { ComponentType } from 'react';
|
||||
import { css, cx } from 'emotion';
|
||||
import { GrafanaTheme, toPascalCase } from '@grafana/data';
|
||||
import { stylesFactory } from '../../themes/stylesFactory';
|
||||
@ -7,6 +7,8 @@ import { IconName, IconType, IconSize } from '../../types/icon';
|
||||
//@ts-ignore
|
||||
import * as DefaultIcon from '@iconscout/react-unicons';
|
||||
import * as MonoIcon from './assets';
|
||||
import { customIcons } from './custom';
|
||||
import { SvgProps } from './assets/types';
|
||||
|
||||
const alwaysMonoIcons = ['grafana', 'favorite', 'heart-break', 'heart'];
|
||||
|
||||
@ -34,6 +36,24 @@ const getIconStyles = stylesFactory((theme: GrafanaTheme) => {
|
||||
};
|
||||
});
|
||||
|
||||
function getIconComponent(name: string, type: string): ComponentType<SvgProps> {
|
||||
if (alwaysMonoIcons.includes(name)) {
|
||||
type = 'mono';
|
||||
}
|
||||
|
||||
if (name?.startsWith('gf-')) {
|
||||
return customIcons[name];
|
||||
}
|
||||
|
||||
const iconName = type === 'default' ? `Uil${toPascalCase(name)}` : toPascalCase(name);
|
||||
|
||||
/* Unicons don't have type definitions */
|
||||
//@ts-ignore
|
||||
const Component = type === 'default' ? DefaultIcon[iconName] : MonoIcon[iconName];
|
||||
|
||||
return Component ?? customIcons.notFoundDummy;
|
||||
}
|
||||
|
||||
export const Icon = React.forwardRef<HTMLDivElement, IconProps>(
|
||||
({ size = 'md', type = 'default', name, className, style, ...divElementProps }, ref) => {
|
||||
const theme = useTheme();
|
||||
@ -41,24 +61,11 @@ export const Icon = React.forwardRef<HTMLDivElement, IconProps>(
|
||||
const svgSize = getSvgSize(size);
|
||||
|
||||
/* Temporary solution to display also font awesome icons */
|
||||
const isFontAwesome = name?.includes('fa-');
|
||||
if (isFontAwesome) {
|
||||
if (name?.startsWith('fa-')) {
|
||||
return <i className={cx(name, className)} {...divElementProps} style={style} />;
|
||||
}
|
||||
|
||||
if (alwaysMonoIcons.includes(name)) {
|
||||
type = 'mono';
|
||||
}
|
||||
|
||||
const iconName = type === 'default' ? `Uil${toPascalCase(name)}` : toPascalCase(name);
|
||||
|
||||
/* Unicons don't have type definitions */
|
||||
//@ts-ignore
|
||||
const Component = type === 'default' ? DefaultIcon[iconName] : MonoIcon[iconName];
|
||||
|
||||
if (!Component) {
|
||||
return <div />;
|
||||
}
|
||||
const Component = getIconComponent(name, type);
|
||||
|
||||
return (
|
||||
<div className={styles.container} {...divElementProps} ref={ref}>
|
||||
|
78
packages/grafana-ui/src/components/Icon/custom/index.tsx
Normal file
78
packages/grafana-ui/src/components/Icon/custom/index.tsx
Normal file
@ -0,0 +1,78 @@
|
||||
import React, { FC, ComponentType } from 'react';
|
||||
import { SvgProps } from '../assets/types';
|
||||
|
||||
const InterpolationLinear: FC<SvgProps> = ({ size, ...rest }) => {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28.5 20" width={'30px'} height={size} {...rest}>
|
||||
<g id="Layer_2" data-name="Layer 2">
|
||||
<g id="Icons">
|
||||
<circle cx="14.17" cy="2.67" r="2.67" />
|
||||
<circle cx="25.83" cy="17.33" r="2.67" />
|
||||
<rect x="19.25" y="-1.21" width="1.5" height="22.42" transform="translate(-1.79 15.03) rotate(-39.57)" />
|
||||
<circle cx="2.67" cy="17.33" r="2.67" />
|
||||
<rect x="-2.71" y="9.25" width="22.42" height="1.5" transform="translate(-4.62 10.18) rotate(-50.44)" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
const InterpolationSmooth: FC<SvgProps> = ({ size, ...rest }) => {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28.34 20" width={'30px'} height={size} {...rest}>
|
||||
<g id="Layer_2" data-name="Layer 2">
|
||||
<g id="Icons">
|
||||
<circle cx="14.17" cy="2.67" r="2.67" />
|
||||
<circle cx="2.67" cy="17.33" r="2.67" />
|
||||
<path d="M3.42,17.33H1.92c0-6.46,4.39-15.41,12.64-15.41v1.5C7.29,3.42,3.42,11.5,3.42,17.33Z" />
|
||||
<circle cx="25.67" cy="17.33" r="2.67" />
|
||||
<path d="M26.42,17.33h-1.5c0-5.83-3.87-13.91-11.14-13.91V1.92C22,1.92,26.42,10.87,26.42,17.33Z" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
const InterpolationStepBefore: FC<SvgProps> = ({ size, ...rest }) => {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28.34 20" width={'30px'} height={size} {...rest}>
|
||||
<g id="Layer_2" data-name="Layer 2">
|
||||
<g id="Icons">
|
||||
<circle cx="14.17" cy="2.67" r="2.67" />
|
||||
<circle cx="2.67" cy="17.33" r="2.67" />
|
||||
<circle cx="25.67" cy="17.33" r="2.67" />
|
||||
<polygon points="3.42 17.33 1.92 17.33 1.92 1.92 13.78 1.92 13.78 3.42 3.42 3.42 3.42 17.33" />
|
||||
<polygon points="25.67 18.08 13.42 18.08 13.42 2.67 14.92 2.67 14.92 16.58 25.67 16.58 25.67 18.08" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
const InterpolationStepAfter: FC<SvgProps> = ({ size, ...rest }) => {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28.34 20" width={'30px'} height={size} {...rest}>
|
||||
<g id="Layer_2" data-name="Layer 2">
|
||||
<g id="Icons">
|
||||
<circle cx="14.17" cy="2.67" r="2.67" />
|
||||
<circle cx="25.67" cy="17.33" r="2.67" />
|
||||
<circle cx="2.67" cy="17.33" r="2.67" />
|
||||
<polygon points="26.42 17.33 24.92 17.33 24.92 3.42 14.56 3.42 14.56 1.92 26.42 1.92 26.42 17.33" />
|
||||
<polygon points="14.92 18.08 2.67 18.08 2.67 16.58 13.42 16.58 13.42 2.67 14.92 2.67 14.92 18.08" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
const IconNotFound: FC<SvgProps> = ({ ...rest }) => {
|
||||
return <svg {...rest} />;
|
||||
};
|
||||
|
||||
export const customIcons: Record<string, ComponentType<SvgProps>> = {
|
||||
'gf-interpolation-linear': InterpolationLinear,
|
||||
'gf-interpolation-smooth': InterpolationSmooth,
|
||||
'gf-interpolation-step-before': InterpolationStepBefore,
|
||||
'gf-interpolation-step-after': InterpolationStepAfter,
|
||||
notFoundDummy: IconNotFound,
|
||||
};
|
@ -142,10 +142,10 @@ export const graphFieldOptions = {
|
||||
] as Array<SelectableValue<DrawStyle>>,
|
||||
|
||||
lineInterpolation: [
|
||||
{ label: 'Linear', value: LineInterpolation.Linear },
|
||||
{ label: 'Smooth', value: LineInterpolation.Smooth },
|
||||
{ label: 'Step Before', value: LineInterpolation.StepBefore },
|
||||
{ label: 'Step After', value: LineInterpolation.StepAfter },
|
||||
{ description: 'Linear', value: LineInterpolation.Linear, icon: 'gf-interpolation-linear' },
|
||||
{ description: 'Smooth', value: LineInterpolation.Smooth, icon: 'gf-interpolation-smooth' },
|
||||
{ description: 'Step before', value: LineInterpolation.StepBefore, icon: 'gf-interpolation-step-before' },
|
||||
{ description: 'Step after', value: LineInterpolation.StepAfter, icon: 'gf-interpolation-step-after' },
|
||||
] as Array<SelectableValue<LineInterpolation>>,
|
||||
|
||||
showPoints: [
|
||||
|
@ -121,7 +121,11 @@ export type IconName =
|
||||
| 'sort-amount-down'
|
||||
| 'cloud'
|
||||
| 'draggabledots'
|
||||
| 'folder-upload';
|
||||
| 'folder-upload'
|
||||
| 'gf-interpolation-linear'
|
||||
| 'gf-interpolation-smooth'
|
||||
| 'gf-interpolation-step-before'
|
||||
| 'gf-interpolation-step-after';
|
||||
|
||||
export const getAvailableIcons = (): IconName[] => [
|
||||
'fa fa-spinner',
|
||||
@ -241,4 +245,8 @@ export const getAvailableIcons = (): IconName[] => [
|
||||
'cloud',
|
||||
'draggabledots',
|
||||
'folder-upload',
|
||||
'gf-interpolation-linear',
|
||||
'gf-interpolation-smooth',
|
||||
'gf-interpolation-step-before',
|
||||
'gf-interpolation-step-after',
|
||||
];
|
||||
|
@ -7,6 +7,7 @@ const setup = (propOverrides?: object) => {
|
||||
{
|
||||
link: {
|
||||
text: 'Hello',
|
||||
icon: 'cloud',
|
||||
url: '/asd',
|
||||
},
|
||||
},
|
||||
|
@ -8,14 +8,12 @@ export interface Props {
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
const TopSectionItem: FC<Props> = props => {
|
||||
const { link, onClick } = props;
|
||||
|
||||
const TopSectionItem: FC<Props> = ({ link, onClick }) => {
|
||||
return (
|
||||
<div className="sidemenu-item dropdown">
|
||||
<a className="sidemenu-link" href={link.url} target={link.target} onClick={onClick}>
|
||||
<span className="icon-circle sidemenu-icon">
|
||||
<Icon name={link.icon as any} size="xl" />
|
||||
{link.icon && <Icon name={link.icon as any} size="xl" />}
|
||||
{link.img && <img src={link.img} />}
|
||||
</span>
|
||||
</a>
|
||||
|
@ -4,6 +4,7 @@ exports[`Render should render component 1`] = `
|
||||
<TopSectionItem
|
||||
link={
|
||||
Object {
|
||||
"icon": "cloud",
|
||||
"text": "Hello",
|
||||
"url": "/asd",
|
||||
}
|
||||
@ -20,15 +21,38 @@ exports[`Render should render component 1`] = `
|
||||
className="icon-circle sidemenu-icon"
|
||||
>
|
||||
<Icon
|
||||
name="cloud"
|
||||
size="xl"
|
||||
>
|
||||
<div />
|
||||
<div
|
||||
className="css-1vzus6i-Icon"
|
||||
>
|
||||
<ni
|
||||
className="css-sr6nr"
|
||||
color="currentColor"
|
||||
size={24}
|
||||
>
|
||||
<svg
|
||||
className="css-sr6nr"
|
||||
fill="currentColor"
|
||||
height={24}
|
||||
viewBox="0 0 24 24"
|
||||
width={24}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M18.42,9.21a7,7,0,0,0-13.36,1.9A4,4,0,0,0,6,19H17a5,5,0,0,0,1.42-9.79ZM17,17H6a2,2,0,0,1,0-4,1,1,0,0,0,1-1,5,5,0,0,1,9.73-1.61,1,1,0,0,0,.78.66A3,3,0,0,1,17,17Z"
|
||||
/>
|
||||
</svg>
|
||||
</ni>
|
||||
</div>
|
||||
</Icon>
|
||||
</span>
|
||||
</a>
|
||||
<SideMenuDropDown
|
||||
link={
|
||||
Object {
|
||||
"icon": "cloud",
|
||||
"text": "Hello",
|
||||
"url": "/asd",
|
||||
}
|
||||
|
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 82.48 77.59"><defs><style>.cls-1{fill:url(#linear-gradient);}.cls-2{fill:url(#linear-gradient-2);}.cls-3{fill:#84aff1;}</style><linearGradient id="linear-gradient" y1="12" x2="82.48" y2="12" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#f2cc0c"/><stop offset="1" stop-color="#ff9830"/></linearGradient><linearGradient id="linear-gradient-2" x1="41.14" y1="77.98" x2="41.91" y2="13.73" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#1f60c4" stop-opacity="0"/><stop offset="1" stop-color="#3865ab"/></linearGradient></defs><g id="Layer_2" data-name="Layer 2"><g id="Icons"><path class="cls-1" d="M33.26,24a2,2,0,0,1-.83-.18l-14.7-6.66-15,5.71a2,2,0,1,1-1.42-3.74l15.8-6a2,2,0,0,1,1.53,0l14.16,6.41,15-16A2,2,0,0,1,50,3.16L65.15,9.73,79.38.33a2,2,0,1,1,2.2,3.34l-15.13,10a2,2,0,0,1-1.9.16L49.72,7.39l-15,16A2,2,0,0,1,33.26,24Z"/><polygon class="cls-2" points="80.48 17.72 65.35 33.72 49.22 21.72 33.26 33.72 17.8 30.72 2 43.72 2.11 43.72 2.11 77.58 80.69 77.58 80.48 17.72"/><path class="cls-3" d="M2.11,45.72H2A2,2,0,0,1,.73,42.18l15.8-13a2,2,0,0,1,1.65-.42l14.59,2.83L48,20.12a2,2,0,0,1,2.4,0l14.7,10.94L79,16.35a2,2,0,1,1,2.9,2.75L66.8,35.1a2,2,0,0,1-2.64.23L49.23,24.22,34.46,35.32a2,2,0,0,1-1.58.37L18.34,32.87l-14.6,12A2,2,0,0,1,2.11,45.72Z"/></g></g></svg>
|
After Width: | Height: | Size: 1.4 KiB |
@ -10,8 +10,8 @@
|
||||
"url": "https://grafana.com"
|
||||
},
|
||||
"logos": {
|
||||
"small": "img/icn-graph-panel.svg",
|
||||
"large": "img/icn-graph-panel.svg"
|
||||
"small": "img/icn-timeseries-panel.svg",
|
||||
"large": "img/icn-timeseries-panel.svg"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 82.59 82.5"><defs><style>.cls-1{fill:#3865ab;}.cls-2{fill:#84aff1;}.cls-3{fill:url(#linear-gradient);}</style><linearGradient id="linear-gradient" y1="21.17" x2="82.59" y2="21.17" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#f2cc0c"/><stop offset="1" stop-color="#ff9830"/></linearGradient></defs><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><rect class="cls-1" x="73.22" y="19.61" width="8" height="62.89" rx="1"/><rect class="cls-1" x="1.78" y="53.61" width="8" height="28.89" rx="1"/><path class="cls-2" d="M8.78,82.5h-6a1,1,0,0,1-1-1V71.61h8V81.5A1,1,0,0,1,8.78,82.5Z"/><path class="cls-2" d="M80.22,82.5h-6a1,1,0,0,1-1-1V46.61h8V81.5A1,1,0,0,1,80.22,82.5Z"/><rect class="cls-1" x="58.93" y="49.61" width="8" height="32.89" rx="1"/><path class="cls-2" d="M65.93,82.5h-6a1,1,0,0,1-1-1V64.61h8V81.5A1,1,0,0,1,65.93,82.5Z"/><rect class="cls-1" x="44.64" y="38.61" width="8" height="43.89" rx="1"/><path class="cls-2" d="M51.64,82.5h-6a1,1,0,0,1-1-1V75.61h8V81.5A1,1,0,0,1,51.64,82.5Z"/><rect class="cls-1" x="30.36" y="27.61" width="8" height="54.89" rx="1"/><path class="cls-2" d="M37.36,82.5h-6a1,1,0,0,1-1-1V42.61h8V81.5A1,1,0,0,1,37.36,82.5Z"/><rect class="cls-1" x="16.07" y="37.61" width="8" height="44.89" rx="1"/><path class="cls-2" d="M23.07,82.5h-6a1,1,0,0,1-1-1V55.61h8V81.5A1,1,0,0,1,23.07,82.5Z"/><path class="cls-3" d="M2,42.33a2,2,0,0,1-1.44-.61,2,2,0,0,1,0-2.83l26-25a2,2,0,0,1,2.2-.39L54.56,25,79.18.58A2,2,0,0,1,82,3.42L56.41,28.75a2,2,0,0,1-2.22.41L28.42,17.71l-25,24.06A2,2,0,0,1,2,42.33Z"/></g></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 85.58 85.59"><defs><style>.cls-1{fill:#3865ab;}.cls-2{fill:#84aff1;}.cls-3{fill:url(#linear-gradient);}</style><linearGradient id="linear-gradient" x1="18.52" y1="47.29" x2="83.59" y2="47.29" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#f2cc0c"/><stop offset="1" stop-color="#ff9830"/></linearGradient></defs><g id="Layer_2" data-name="Layer 2"><g id="Icons"><path class="cls-1" d="M83.58,78.59H7V2A2,2,0,0,0,3,2V78.59H2a2,2,0,0,0,0,4H3v1a2,2,0,0,0,4,0v-1H83.58a2,2,0,1,0,0-4Z"/><circle class="cls-2" cx="13.43" cy="68.2" r="2.34"/><path class="cls-3" d="M23.42,69.87a2.34,2.34,0,1,1-2.34-2.34A2.34,2.34,0,0,1,23.42,69.87Zm6-6.89a2.34,2.34,0,1,0,2.34,2.34A2.34,2.34,0,0,0,29.39,63ZM20.85,48.79a2.34,2.34,0,1,0,2.34,2.34A2.33,2.33,0,0,0,20.85,48.79Zm15.43,3.4a2.34,2.34,0,1,0,2.33,2.34A2.34,2.34,0,0,0,36.28,52.19ZM43.92,58a2.34,2.34,0,1,0,2.34,2.34A2.34,2.34,0,0,0,43.92,58Zm10.15-6.29a2.34,2.34,0,1,0,2.34,2.34A2.34,2.34,0,0,0,54.07,51.72Zm8.59-14.66A2.34,2.34,0,1,0,65,39.4,2.34,2.34,0,0,0,62.66,37.06ZM81.25,22.38a2.34,2.34,0,1,0,2.34,2.34A2.34,2.34,0,0,0,81.25,22.38Z"/><circle class="cls-2" cx="26.69" cy="56.59" r="2.34"/><circle class="cls-2" cx="36.16" cy="43.03" r="2.34"/><circle class="cls-2" cx="43.66" cy="52.15" r="2.34"/><circle class="cls-2" cx="54.37" cy="42.4" r="2.34"/><circle class="cls-2" cx="44.59" cy="20.16" r="2.34"/><circle class="cls-2" cx="79.25" cy="45.71" r="2.34"/><circle class="cls-2" cx="79.25" cy="4.34" r="2.34"/><circle class="cls-2" cx="73.25" cy="16.98" r="2.34"/><circle class="cls-2" cx="60.71" cy="4.34" r="2.34"/><circle class="cls-2" cx="59.66" cy="27.08" r="2.34"/><circle class="cls-2" cx="16.36" cy="59.58" r="2.34"/></g></g></svg>
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.7 KiB |
Loading…
Reference in New Issue
Block a user