TSK-828, TSK-832: Fixed the application panel and scroll bars (#2733)

Signed-off-by: Alexander Platov <sas_lord@mail.ru>
This commit is contained in:
Alexander Platov 2023-03-15 08:58:43 +03:00 committed by GitHub
parent 0db9d1f786
commit 874dfda395
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 247 additions and 99 deletions

View File

@ -19,6 +19,8 @@
import { closeTooltip, tooltipstore } from '../tooltips' import { closeTooltip, tooltipstore } from '../tooltips'
import type { FadeOptions } from '../types' import type { FadeOptions } from '../types'
import { defaultSP } from '../types' import { defaultSP } from '../types'
import IconUpOutline from './icons/UpOutline.svelte'
import IconDownOutline from './icons/DownOutline.svelte'
export let padding: string | undefined = undefined export let padding: string | undefined = undefined
export let autoscroll: boolean = false export let autoscroll: boolean = false
@ -28,22 +30,24 @@
export let horizontal: boolean = false export let horizontal: boolean = false
export let contentDirection: 'vertical' | 'vertical-reverse' | 'horizontal' = 'vertical' export let contentDirection: 'vertical' | 'vertical-reverse' | 'horizontal' = 'vertical'
export let noStretch: boolean = autoscroll export let noStretch: boolean = autoscroll
export let buttons: boolean = false
export let divScroll: HTMLElement | undefined = undefined export let divScroll: HTMLElement | undefined = undefined
export function scroll (top: number, left?: number, behavior: 'auto' | 'smooth' = 'auto') { export function scroll (top: number, left?: number, behavior: 'auto' | 'smooth' = 'auto') {
if (divScroll && divHScroll) { if (divScroll) {
if (top !== 0) divScroll.scroll({ top, left: 0, behavior }) if (top !== 0) divScroll.scroll({ top, left: 0, behavior })
if (left !== 0 || left !== undefined) divHScroll.scroll({ top: 0, left, behavior }) if (left !== 0 || left !== undefined) divScroll.scroll({ top: 0, left, behavior })
} }
} }
export function scrollBy (top: number, left?: number, behavior: 'auto' | 'smooth' = 'auto') { export function scrollBy (top: number, left?: number, behavior: 'auto' | 'smooth' = 'auto') {
if (divScroll && divHScroll) { if (divScroll) {
if (top !== 0) divScroll.scrollBy({ top, left: 0, behavior }) if (top !== 0) divScroll.scrollBy({ top, left: 0, behavior })
if (left !== 0 || left !== undefined) divHScroll.scrollBy({ top: 0, left, behavior }) if (left !== 0 || left !== undefined) divScroll.scrollBy({ top: 0, left, behavior })
} }
} }
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
const stepScroll = 52
let mask: 'top' | 'bottom' | 'both' | 'none' = 'none' let mask: 'top' | 'bottom' | 'both' | 'none' = 'none'
let topCrop: 'top' | 'bottom' | 'full' | 'none' = 'none' let topCrop: 'top' | 'bottom' | 'full' | 'none' = 'none'
@ -54,6 +58,7 @@
let divBox: HTMLElement let divBox: HTMLElement
let divBar: HTMLElement let divBar: HTMLElement
let divBarH: HTMLElement let divBarH: HTMLElement
let divScrollContainer: HTMLElement
let isScrolling: 'vertical' | 'horizontal' | false = false let isScrolling: 'vertical' | 'horizontal' | false = false
let dXY: number let dXY: number
let belowContent: number | undefined = undefined let belowContent: number | undefined = undefined
@ -62,6 +67,7 @@
let rightContent: number | undefined = undefined let rightContent: number | undefined = undefined
let scrolling: boolean = autoscroll let scrolling: boolean = autoscroll
let firstScroll: boolean = autoscroll let firstScroll: boolean = autoscroll
let orientir: 'vertical' | 'horizontal' = 'vertical'
let timer: number let timer: number
let timerH: number let timerH: number
@ -69,8 +75,9 @@
const inter = new Set<Element>() const inter = new Set<Element>()
$: fz = $themeOptions.fontSize $: fz = $themeOptions.fontSize
$: shiftTop = fade.offset?.top ? (fade.multipler?.top ?? 0) * fz : 0 $: shiftTop = fade.multipler?.top ? fade.multipler?.top * fz : 0
$: shiftBottom = fade.offset?.bottom ? fade.multipler?.bottom! * fz : 0 $: shiftBottom = fade.multipler?.bottom ? fade.multipler?.bottom * fz : 0
$: orientir = contentDirection === 'horizontal' ? 'horizontal' : 'vertical'
const checkBar = (): void => { const checkBar = (): void => {
if (divBar && divScroll) { if (divBar && divScroll) {
@ -78,7 +85,7 @@
const scrollH = divScroll.scrollHeight const scrollH = divScroll.scrollHeight
const proc = scrollH / trackH const proc = scrollH / trackH
divBar.style.height = divScroll.clientHeight / proc + 'px' divBar.style.height = divScroll.clientHeight / proc + 'px'
divBar.style.top = divScroll.scrollTop / proc + shiftTop + shiftBottom + 2 + 'px' divBar.style.top = divScroll.scrollTop / proc + shiftTop + 2 + 'px'
if (mask === 'none') divBar.style.visibility = 'hidden' if (mask === 'none') divBar.style.visibility = 'hidden'
else { else {
divBar.style.visibility = 'visible' divBar.style.visibility = 'visible'
@ -260,7 +267,7 @@
const checkIntersectionFade = () => { const checkIntersectionFade = () => {
topCrop = 'none' topCrop = 'none'
topCropValue = 0 topCropValue = 0
if (!fade.offset?.top || !divScroll) return if (!fade.multipler?.top || !divScroll) return
const offset = divScroll.getBoundingClientRect().top const offset = divScroll.getBoundingClientRect().top
inter.forEach((el) => { inter.forEach((el) => {
const rect = el.getBoundingClientRect() const rect = el.getBoundingClientRect()
@ -320,11 +327,24 @@
let divHeight: number let divHeight: number
const _resize = (): void => checkFade() const _resize = (): void => checkFade()
const tapScroll = (n: number, dir: 'up' | 'down') => {
if (divScroll) {
if (orientir === 'horizontal') divScroll.scrollBy({ top: 0, left: dir === 'up' ? -n : n, behavior: 'smooth' })
else divScroll.scrollBy({ top: dir === 'up' ? -n : n, left: 0, behavior: 'smooth' })
}
}
</script> </script>
<svelte:window on:resize={_resize} /> <svelte:window on:resize={_resize} />
<div class="scroller-container {invertScroll ? 'invert' : 'normal'}"> <div
bind:this={divScrollContainer}
class="scroller-container {orientir} {invertScroll ? 'invert' : 'normal'}"
class:buttons
style:--scroller-header-height={`${fade.multipler?.top ?? 0.125}rem`}
style:--scroller-footer-height={`${fade.multipler?.bottom ?? 0.125}rem`}
>
<div bind:this={divHScroll} class="horizontalBox flex-col flex-shrink"> <div bind:this={divHScroll} class="horizontalBox flex-col flex-shrink">
<div <div
bind:this={divScroll} bind:this={divScroll}
@ -362,6 +382,32 @@
</div> </div>
</div> </div>
</div> </div>
{#if buttons}
<button
class="scrollButton top {orientir}"
style:visibility={(orientir === 'vertical' && (mask === 'top' || mask === 'both')) ||
(orientir === 'horizontal' && (maskH === 'right' || maskH === 'both'))
? 'visible'
: 'hidden'}
on:click|preventDefault|stopPropagation={() => tapScroll(stepScroll, 'up')}
>
<div style:transform={orientir === 'horizontal' ? 'rotate(-90deg)' : ''}>
<IconUpOutline size={'medium'} />
</div>
</button>
<button
class="scrollButton bottom {orientir}"
style:visibility={(orientir === 'vertical' && (mask === 'bottom' || mask === 'both')) ||
(orientir === 'horizontal' && (maskH === 'left' || maskH === 'both'))
? 'visible'
: 'hidden'}
on:click|preventDefault|stopPropagation={() => tapScroll(stepScroll, 'down')}
>
<div style:transform={orientir === 'horizontal' ? 'rotate(-90deg)' : ''}>
<IconDownOutline size={'medium'} />
</div>
</button>
{/if}
<div <div
class="bar" class="bar"
class:hovered={isScrolling === 'vertical'} class:hovered={isScrolling === 'vertical'}
@ -372,8 +418,8 @@
<div <div
class="track" class="track"
class:hovered={isScrolling === 'vertical'} class:hovered={isScrolling === 'vertical'}
class:fadeTopOffset={fade.offset?.top} class:fadeTopOffset={fade.multipler?.top}
class:fadeBottomOffset={fade.offset?.bottom} class:fadeBottomOffset={fade.multipler?.bottom}
/> />
{#if horizontal} {#if horizontal}
<div <div
@ -392,6 +438,61 @@
</div> </div>
<style lang="scss"> <style lang="scss">
.scrollButton {
position: absolute;
color: var(--caption-color);
background-color: transparent;
border: 1px solid transparent;
border-radius: 0.25rem;
visibility: hidden;
transform-origin: center;
transition-property: opacity, transform;
transition-timing-function: var(--timing-main);
transition-duration: 0.1s;
transform: scale(0.8);
opacity: 0.1;
&:hover,
&:focus {
transform: scale(1);
opacity: 0.8;
}
&:hover {
background-color: var(--button-bg-color);
}
&:focus {
border-color: var(--primary-edit-border-color);
}
&.vertical {
width: 2rem;
height: 1.25rem;
}
&.horizontal {
width: 1.25rem;
height: 2rem;
}
&.top.vertical {
top: calc(var(--scroller-header-height) - 2rem);
left: 50%;
transform: translateX(-50%);
}
&.top.horizontal {
top: 50%;
left: -2rem;
transform: translateY(-50%);
}
&.bottom.vertical {
right: 50%;
bottom: calc(var(--scroller-footer-height) - 2rem);
transform: translateX(50%);
}
&.bottom.horizontal {
right: -2rem;
bottom: 50%;
transform: translateY(50%);
}
}
.overflowXauto { .overflowXauto {
overflow-x: auto; overflow-x: auto;
} }
@ -408,6 +509,12 @@
min-width: 0; min-width: 0;
min-height: 0; min-height: 0;
&.buttons.vertical {
margin: 1.5rem 0;
}
&.buttons.horizontal {
margin: 0 1.5rem;
}
&.normal { &.normal {
.track, .track,
.bar { .bar {
@ -415,7 +522,7 @@
} }
.track-horizontal, .track-horizontal,
.bar-horizontal { .bar-horizontal {
bottom: 2px; bottom: var(--scroller-footer-height);
} }
} }
&.invert { &.invert {
@ -476,11 +583,11 @@
top: var(--scroller-header-height); top: var(--scroller-header-height);
} }
&.fadeBottomOffset { &.fadeBottomOffset {
top: var(--scroller-footer-height); bottom: var(--scroller-footer-height);
} }
} }
.track-horizontal { .track-horizontal {
bottom: 2px; bottom: var(--scroller-footer-height);
left: 2px; left: 2px;
right: 2px; right: 2px;
height: 8px; height: 8px;
@ -529,7 +636,7 @@
} }
.bar-horizontal { .bar-horizontal {
left: 2px; left: 2px;
bottom: 2px; bottom: var(--scroller-footer-height);
height: 8px; height: 8px;
min-width: 2rem; min-width: 2rem;
max-width: calc(100% - 12px); max-width: calc(100% - 12px);

View File

@ -223,14 +223,14 @@ interface Sides<T> {
right?: T right?: T
} }
export interface FadeOptions { export interface FadeOptions {
offset?: Sides<boolean>
multipler?: Sides<number> multipler?: Sides<number>
} }
export const defaultSP: FadeOptions = { multipler: { top: 0, bottom: 0 } } export const defaultSP: FadeOptions = { multipler: { top: 0, bottom: 0 } }
export const tableSP: FadeOptions = { offset: { top: true, bottom: true }, multipler: { top: 2.5, bottom: 2.5 } } export const tableSP: FadeOptions = { multipler: { top: 2.5, bottom: 2.5 } }
export const tableHRscheduleY: FadeOptions = { offset: { top: true }, multipler: { top: 5, bottom: 0 } } export const topSP: FadeOptions = { multipler: { top: 2.5, bottom: 0 } }
export const issueSP: FadeOptions = { offset: { top: true }, multipler: { top: 3, bottom: 0 } } export const tableHRscheduleY: FadeOptions = { multipler: { top: 5, bottom: 0 } }
export const emojiSP: FadeOptions = { offset: { top: true }, multipler: { top: 1.5, bottom: 0 } } export const issueSP: FadeOptions = { multipler: { top: 3, bottom: 0 } }
export const emojiSP: FadeOptions = { multipler: { top: 1.5, bottom: 0 } }
export interface DeviceOptions { export interface DeviceOptions {
docWidth: number docWidth: number

View File

@ -16,7 +16,9 @@
import type { IntlString, Asset } from '@hcengineering/platform' import type { IntlString, Asset } from '@hcengineering/platform'
import type { AnySvelteComponent } from '@hcengineering/ui' import type { AnySvelteComponent } from '@hcengineering/ui'
import { createEventDispatcher } from 'svelte' import { createEventDispatcher } from 'svelte'
import { Icon, tooltip, IconColStar } from '@hcengineering/ui' import { Icon, tooltip } from '@hcengineering/ui'
import PreviewOn from './icons/PreviewOn.svelte'
import PreviewOff from './icons/PreviewOff.svelte'
export let label: IntlString export let label: IntlString
export let icon: Asset | AnySvelteComponent export let icon: Asset | AnySvelteComponent
@ -43,11 +45,11 @@
dispatch('visible', !hidden) dispatch('visible', !hidden)
}} }}
> >
<IconColStar {#if hidden}
size={'small'} <PreviewOff size={'small'} />
fill={hidden ? 'var(--warning-color)' : 'var(--activity-status-busy)'} {:else}
border={'var(--button-border-hover)'} <PreviewOn size={'small'} />
/> {/if}
</div> </div>
{/if} {/if}
</button> </button>
@ -125,6 +127,7 @@
bottom: 0.25rem; bottom: 0.25rem;
height: 1rem; height: 1rem;
width: 1rem; width: 1rem;
color: var(--activity-status-busy);
transform-origin: center center; transform-origin: center center;
transform: scale(1); transform: scale(1);
opacity: 0.8; opacity: 0.8;
@ -136,6 +139,7 @@
opacity: 1; opacity: 1;
} }
&.hidden { &.hidden {
color: var(--warning-color);
transform: scale(0.7); transform: scale(0.7);
opacity: 0.5; opacity: 0.5;

View File

@ -15,7 +15,7 @@
<script lang="ts"> <script lang="ts">
import type { Ref } from '@hcengineering/core' import type { Ref } from '@hcengineering/core'
import { createQuery } from '@hcengineering/presentation' import { createQuery } from '@hcengineering/presentation'
import { IconDownOutline, NavLink, Scroller } from '@hcengineering/ui' import { NavLink, Scroller } from '@hcengineering/ui'
import type { Application } from '@hcengineering/workbench' import type { Application } from '@hcengineering/workbench'
import workbench from '@hcengineering/workbench' import workbench from '@hcengineering/workbench'
import { hideApplication, showApplication } from '../utils' import { hideApplication, showApplication } from '../utils'
@ -24,6 +24,7 @@
export let active: Ref<Application> | undefined export let active: Ref<Application> | undefined
export let apps: Application[] = [] export let apps: Application[] = []
export let direction: 'vertical' | 'horizontal' = 'vertical' export let direction: 'vertical' | 'horizontal' = 'vertical'
export let shown: boolean = false
let loaded: boolean = false let loaded: boolean = false
let hiddenAppsIds: Ref<Application>[] = [] let hiddenAppsIds: Ref<Application>[] = []
@ -32,8 +33,6 @@
hiddenAppsIds = res.map((r) => r.attachedTo) hiddenAppsIds = res.map((r) => r.attachedTo)
loaded = true loaded = true
}) })
let shown: boolean = false
</script> </script>
<div class="flex-{direction === 'horizontal' ? 'row-center' : 'col'} clear-mins apps-{direction} relative"> <div class="flex-{direction === 'horizontal' ? 'row-center' : 'col'} clear-mins apps-{direction} relative">
@ -43,6 +42,7 @@
padding={direction === 'horizontal' ? '.25rem 0' : '0 .5rem'} padding={direction === 'horizontal' ? '.25rem 0' : '0 .5rem'}
horizontal={direction === 'horizontal'} horizontal={direction === 'horizontal'}
contentDirection={direction} contentDirection={direction}
buttons
> >
<div class="apps-space-{direction}" /> <div class="apps-space-{direction}" />
{#each apps.filter((it) => (shown ? true : !hiddenAppsIds.includes(it._id))) as app} {#each apps.filter((it) => (shown ? true : !hiddenAppsIds.includes(it._id))) as app}
@ -63,11 +63,6 @@
{/each} {/each}
<div class="apps-space-{direction}" /> <div class="apps-space-{direction}" />
</Scroller> </Scroller>
<div class="thinButton {direction}" class:shown on:click={() => (shown = !shown)}>
<div class="clear-mins pointer-events-none" class:rotate90={direction === 'horizontal'}>
<IconDownOutline size={'medium'} />
</div>
</div>
{/if} {/if}
</div> </div>
@ -93,66 +88,4 @@
width: 0.25rem; width: 0.25rem;
} }
} }
.thinButton {
position: absolute;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
transform-origin: center center;
border-radius: 0.5rem;
opacity: 0.2;
cursor: pointer;
transition-property: opacity, transform;
transition-timing-function: var(--timing-main);
transition-duration: 0.1s;
&.vertical {
top: 100%;
left: 50%;
width: 2.5rem;
transform: translateX(-50%) scale(0.6) rotate(0deg);
&:hover {
transform: translateX(-50%) scale(0.8);
}
}
&.horizontal {
left: 100%;
top: 50%;
height: 2.5rem;
transform: translateY(-50%) scale(0.6);
&:hover {
transform: translateY(-50%) scale(0.8);
}
&.shown {
transform: translateY(-50%) scale(0.9) rotate(180deg);
&:hover {
transform: translateY(-50%) scale(1) rotate(180deg);
}
}
}
&:hover {
transform: translateX(-50%) scale(0.8);
background-color: var(--accent-bg-color);
opacity: 0.9;
}
&.shown {
transform: translateX(-50%) scale(0.9) rotate(180deg);
opacity: 0.8;
&:hover {
transform: translateX(-50%) scale(1) rotate(180deg);
background-color: var(--accent-bg-color);
opacity: 1;
}
}
}
.rotate90 {
transform-origin: center center;
transform: rotate(-90deg);
}
</style> </style>

View File

@ -8,7 +8,8 @@
Label, Label,
ListView, ListView,
navigate, navigate,
Scroller Scroller,
topSP
} from '@hcengineering/ui' } from '@hcengineering/ui'
import setting, { settingId } from '@hcengineering/setting' import setting, { settingId } from '@hcengineering/setting'
import view, { Action, ActionCategory } from '@hcengineering/view' import view, { Action, ActionCategory } from '@hcengineering/view'
@ -134,7 +135,7 @@
{/each} {/each}
{:else} {:else}
<!-- Keyboard shortcuts --> <!-- Keyboard shortcuts -->
<Scroller padding={'0 .5rem'} fade={{ offset: { top: true }, multipler: { top: 2.5, bottom: 0 } }}> <Scroller padding={'0 .5rem'} fade={topSP}>
<ListView count={actions.length} noScroll> <ListView count={actions.length} noScroll>
<svelte:fragment slot="category" let:item> <svelte:fragment slot="category" let:item>
{@const action = actions[item]} {@const action = actions[item]}

View File

@ -56,9 +56,11 @@
import NavHeader from './NavHeader.svelte' import NavHeader from './NavHeader.svelte'
import Navigator from './Navigator.svelte' import Navigator from './Navigator.svelte'
import SpaceView from './SpaceView.svelte' import SpaceView from './SpaceView.svelte'
import Settings from './icons/Settings.svelte'
export let client: Client export let client: Client
let contentPanel: HTMLElement let contentPanel: HTMLElement
let shownMenu: boolean = false
const { setTheme } = getContext('theme') as any const { setTheme } = getContext('theme') as any
setClient(client) setClient(client)
@ -442,8 +444,12 @@
mini={appsMini} mini={appsMini}
notify={false} notify={false}
/> />
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div class="thinButton {appsDirection}" class:shownMenu on:click={() => (shownMenu = !shownMenu)}>
<Settings size={'small'} />
</div>
</div> </div>
<Applications {apps} active={currentApplication?._id} direction={appsDirection} /> <Applications {apps} active={currentApplication?._id} direction={appsDirection} bind:shown={shownMenu} />
<div class="info-box {appsDirection}" class:vertical-mobile={appsDirection === 'vertical' && appsMini}> <div class="info-box {appsDirection}" class:vertical-mobile={appsDirection === 'vertical' && appsMini}>
<AppItem <AppItem
icon={request.icon.Requests} icon={request.icon.Requests}
@ -473,6 +479,7 @@
notify={hasNotification} notify={hasNotification}
/> />
<div class="flex-center" class:mt-2={appsDirection === 'vertical'} class:ml-2={appsDirection === 'horizontal'}> <div class="flex-center" class:mt-2={appsDirection === 'vertical'} class:ml-2={appsDirection === 'horizontal'}>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div <div
id="profile-button" id="profile-button"
class="cursor-pointer" class="cursor-pointer"
@ -590,12 +597,14 @@
} }
.hamburger-container { .hamburger-container {
display: flex; display: flex;
align-items: center;
flex-shrink: 0; flex-shrink: 0;
&.portrait { &.portrait {
margin-left: 0.375rem; margin-left: 0.375rem;
} }
&.landscape { &.landscape {
flex-direction: column;
margin-top: 0.25rem; margin-top: 0.25rem;
} }
&.mini { &.mini {
@ -665,4 +674,47 @@
} }
} }
} }
.thinButton {
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
padding: 0.25rem;
background-color: transparent;
border-radius: 0.25rem;
opacity: 0.2;
cursor: pointer;
transition-property: opacity, color, background-color;
transition-timing-function: var(--timing-main);
transition-duration: 0.1s;
&.vertical {
margin-top: 0.5rem;
width: 2.5rem;
}
&.horizontal {
margin-left: 0.5rem;
height: 2.5rem;
}
&:hover {
color: var(--accent-color);
background-color: var(--accent-bg-color);
opacity: 0.9;
}
&.shownMenu {
color: var(--accent-color);
background-color: var(--button-bg-color);
opacity: 0.8;
&:hover {
color: var(--caption-color);
background-color: var(--button-bg-hover);
opacity: 1;
}
}
}
</style> </style>

View File

@ -0,0 +1,13 @@
<script lang="ts">
export let size: 'small' | 'medium' | 'large'
const fill: string = 'currentColor'
</script>
<svg class="svg-{size}" {fill} viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path
fill-rule="evenodd"
clip-rule="evenodd"
{fill}
d="M22,13l-2.4-2.4c0.1-0.1,0.3-0.2,0.4-0.3c0.7-0.6,1.2-1.2,1.6-1.9c0.2-0.4,0-0.8-0.3-1c-0.4-0.2-0.8,0-1,0.3c-0.3,0.5-0.7,1-1.2,1.4h0c-1.6,1.3-4.1,2.2-7.1,2.2c-3,0-5.6-0.9-7.1-2.2h0c-0.6-0.5-1-0.9-1.2-1.4C3.5,7.3,3,7.1,2.7,7.3C2.3,7.5,2.1,8,2.3,8.3c0.4,0.7,0.9,1.4,1.6,1.9c0.1,0.1,0.3,0.2,0.4,0.3L2,13c-0.3,0.3-0.3,0.8,0,1.1c0.3,0.3,0.8,0.3,1.1,0l2.7-2.7c0.9,0.4,1.9,0.8,2.9,1l-0.9,3.3c-0.1,0.4,0.1,0.8,0.5,0.9c0.4,0.1,0.8-0.1,0.9-0.5l0.9-3.4c0.6,0.1,1.2,0.1,1.9,0.1c0.6,0,1.3,0,1.9-0.1l0.9,3.4c0.1,0.4,0.5,0.6,0.9,0.5c0.4-0.1,0.6-0.5,0.5-0.9l-0.9-3.3c1.1-0.2,2.1-0.6,2.9-1L21,14c0.3,0.3,0.8,0.3,1.1,0C22.3,13.7,22.3,13.3,22,13z"
/>
</svg>

View File

@ -0,0 +1,19 @@
<script lang="ts">
export let size: 'small' | 'medium' | 'large'
const fill: string = 'currentColor'
</script>
<svg class="svg-{size}" {fill} viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path
fill-rule="evenodd"
clip-rule="evenodd"
{fill}
d="M4.5,8.4C6.3,6.9,9,5.2,12,5.2c3,0,5.7,1.6,7.5,3.2c0.9,0.8,1.7,1.6,2.2,2.1c0.3,0.3,0.5,0.5,0.6,0.7c0.1,0.1,0.1,0.2,0.2,0.2c0,0,0,0,0,0.1l0,0l0,0l0,0c0,0,0,0-0.6,0.4c0.6,0.4,0.6,0.4,0.6,0.4l0,0l0,0l0,0c0,0,0,0,0,0.1c0,0-0.1,0.1-0.2,0.2c-0.1,0.2-0.3,0.4-0.6,0.7c-0.5,0.6-1.3,1.4-2.2,2.1c-1.9,1.5-4.5,3.2-7.5,3.2c-3,0-5.7-1.6-7.5-3.2c-0.9-0.8-1.7-1.6-2.2-2.1c-0.3-0.3-0.5-0.5-0.6-0.7c-0.1-0.1-0.1-0.2-0.2-0.2c0,0,0,0,0-0.1l0,0l0,0l0,0c0,0,0,0,0.6-0.4c-0.6-0.4-0.6-0.4-0.6-0.4l0,0l0,0l0,0c0,0,0,0,0-0.1c0,0,0.1-0.1,0.2-0.2c0.1-0.2,0.3-0.4,0.6-0.7C2.8,10,3.5,9.2,4.5,8.4z M2,12l-0.6-0.4c-0.2,0.3-0.2,0.6,0,0.9L2,12z M3,12c0.1,0.1,0.2,0.3,0.4,0.4c0.5,0.5,1.2,1.3,2.1,2c1.8,1.5,4.1,2.8,6.6,2.8c2.5,0,4.8-1.4,6.6-2.8c0.9-0.7,1.6-1.4,2.1-2c0.1-0.2,0.3-0.3,0.4-0.4c-0.1-0.1-0.2-0.3-0.4-0.4c-0.5-0.5-1.2-1.3-2.1-2c-1.8-1.5-4.1-2.8-6.6-2.8c-2.5,0-4.8,1.4-6.6,2.8c-0.9,0.7-1.6,1.4-2.1,2C3.2,11.7,3.1,11.9,3,12z M22,12l0.6,0.4c0.2-0.3,0.2-0.6,0-0.9L22,12z"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
{fill}
d="M8.8,12c0-1.8,1.5-3.2,3.2-3.2s3.2,1.5,3.2,3.2s-1.5,3.2-3.2,3.2S8.8,13.8,8.8,12z M12,10.2c-1,0-1.8,0.8-1.8,1.8s0.8,1.8,1.8,1.8s1.8-0.8,1.8-1.8S13,10.2,12,10.2z"
/>
</svg>

View File

@ -0,0 +1,19 @@
<script lang="ts">
export let size: 'small' | 'medium' | 'large'
const fill: string = 'currentColor'
</script>
<svg class="svg-{size}" {fill} viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path
fill-rule="evenodd"
clip-rule="evenodd"
{fill}
d="M9.12596 1.6386C9.46935 1.54354 9.83221 1.7032 9.99413 2.02058C10.3672 2.75182 11.1261 3.24991 12 3.24991C12.8739 3.24991 13.6328 2.75182 14.0059 2.02058C14.1678 1.7032 14.5307 1.54354 14.874 1.6386C16.5585 2.10488 18.0759 2.97031 19.3184 4.12565C19.5611 4.35128 19.6269 4.70939 19.4804 5.00657C19.3331 5.30525 19.25 5.64181 19.25 5.99991C19.25 7.24254 20.2574 8.24991 21.5 8.24991L21.5314 8.24968C21.8629 8.24495 22.1583 8.4585 22.2576 8.77486C22.5777 9.79388 22.75 10.8775 22.75 11.9999C22.75 12.7407 22.6749 13.4648 22.5318 14.1647C22.4548 14.541 22.1059 14.7984 21.7237 14.7608C21.6504 14.7536 21.5758 14.7499 21.5 14.7499C20.2574 14.7499 19.25 15.7573 19.25 16.9999C19.25 17.553 19.4486 18.0578 19.7793 18.4497C20.027 18.7433 20.0128 19.1766 19.7465 19.4534C18.4777 20.7718 16.8745 21.7677 15.0721 22.3043C14.6807 22.4208 14.2681 22.2028 14.1437 21.8139C13.8535 20.9059 13.0024 20.2499 12 20.2499C10.9976 20.2499 10.1465 20.9059 9.85629 21.8139C9.73195 22.2028 9.31927 22.4208 8.92789 22.3043C7.12545 21.7677 5.52229 20.7718 4.25353 19.4534C3.98716 19.1766 3.97299 18.7433 4.22072 18.4497C4.55139 18.0578 4.75 17.553 4.75 16.9999C4.75 15.7573 3.74264 14.7499 2.5 14.7499C2.42423 14.7499 2.34963 14.7536 2.27634 14.7608C1.89406 14.7984 1.54519 14.541 1.46821 14.1647C1.32505 13.4648 1.25 12.7407 1.25 11.9999C1.25 10.8775 1.42227 9.79389 1.74236 8.77485C1.84173 8.4585 2.13705 8.24495 2.4686 8.24968L2.5 8.24991C3.74264 8.24991 4.75 7.24254 4.75 5.99991C4.75 5.64181 4.66689 5.30526 4.51962 5.00657C4.3731 4.70939 4.43894 4.35128 4.68159 4.12565C5.92411 2.97031 7.44153 2.10488 9.12596 1.6386ZM6.08277 4.88982C6.19152 5.24114 6.25 5.61415 6.25 5.99991C6.25 7.88936 4.85261 9.45244 3.03493 9.71204C2.84898 10.4431 2.75 11.2095 2.75 11.9999C2.75 12.4293 2.77921 12.8515 2.83567 13.2647C4.74948 13.4345 6.25 15.042 6.25 16.9999C6.25 17.6668 6.0754 18.294 5.76971 18.8371C6.62645 19.6183 7.63002 20.2403 8.73362 20.6569C9.37706 19.5189 10.598 18.7499 12 18.7499C13.402 18.7499 14.6229 19.5189 15.2664 20.6569C16.37 20.2403 17.3735 19.6183 18.2303 18.8371C17.9246 18.294 17.75 17.6668 17.75 16.9999C17.75 15.042 19.2505 13.4345 21.1643 13.2647C21.2208 12.8515 21.25 12.4293 21.25 11.9999C21.25 11.2095 21.151 10.4431 20.9651 9.71204C19.1474 9.45243 17.75 7.88935 17.75 5.99991C17.75 5.61415 17.8085 5.24114 17.9172 4.88982C17.0618 4.17701 16.0762 3.61605 15.0018 3.24766C14.3184 4.15887 13.2287 4.74991 12 4.74991C10.7713 4.74991 9.68158 4.15887 8.99819 3.24766C7.92382 3.61605 6.93823 4.17701 6.08277 4.88982Z"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
{fill}
d="M7.75 12C7.75 9.65279 9.65279 7.75 12 7.75C14.3472 7.75 16.25 9.65279 16.25 12C16.25 14.3472 14.3472 16.25 12 16.25C9.65279 16.25 7.75 14.3472 7.75 12ZM12 9.25C10.4812 9.25 9.25 10.4812 9.25 12C9.25 13.5188 10.4812 14.75 12 14.75C13.5188 14.75 14.75 13.5188 14.75 12C14.75 10.4812 13.5188 9.25 12 9.25Z"
/>
</svg>