mirror of
https://github.com/hcengineering/platform.git
synced 2026-10-01 05:55:09 +02:00
Chunter: file browser additional fixes (#1547)
Signed-off-by: Ruslan Izhitsky <ruslan.izhitskiy@xored.com>
This commit is contained in:
@@ -16,127 +16,46 @@
|
||||
import { Attachment } from '@anticrm/attachment'
|
||||
import contact, { Employee } from '@anticrm/contact'
|
||||
import { EmployeeAccount } from '@anticrm/contact'
|
||||
import { Doc, getCurrentAccount, Ref, SortingOrder, SortingQuery, Space } from '@anticrm/core'
|
||||
import { IntlString } from '@anticrm/platform'
|
||||
import { getClient, UserBoxList } from '@anticrm/presentation'
|
||||
import { DropdownLabelsIntl, IconMoreV, Label, Menu as UIMenu, showPopup } from '@anticrm/ui'
|
||||
import core, { Class, Doc, getCurrentAccount, Ref, Space } from '@anticrm/core'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import ui, {
|
||||
getCurrentLocation,
|
||||
location,
|
||||
IconMoreV,
|
||||
IconSearch,
|
||||
Label,
|
||||
showPopup,
|
||||
navigate,
|
||||
EditWithIcon,
|
||||
Spinner
|
||||
} from '@anticrm/ui'
|
||||
import { Menu } from '@anticrm/view-resources'
|
||||
import { AttachmentPresenter } from '..'
|
||||
import { onDestroy } from 'svelte'
|
||||
import {
|
||||
AttachmentPresenter,
|
||||
FileBrowserSortMode,
|
||||
dateFileBrowserFilters,
|
||||
fileTypeFileBrowserFilters,
|
||||
sortModeToOptionObject
|
||||
} from '..'
|
||||
import attachment from '../plugin'
|
||||
|
||||
enum SortMode {
|
||||
NewestFile,
|
||||
OldestFile,
|
||||
AscendingAlphabetical,
|
||||
DescendingAlphabetical
|
||||
}
|
||||
|
||||
const msInDay = 24 * 60 * 60 * 1000
|
||||
const getBeginningOfToday = () => {
|
||||
const date = new Date()
|
||||
date.setUTCHours(0, 0, 0, 0)
|
||||
return date.getTime()
|
||||
}
|
||||
const dateObjects = [
|
||||
{
|
||||
id: 'dateAny',
|
||||
label: attachment.string.FileBrowserDateFilterAny,
|
||||
getDate: () => {
|
||||
return undefined
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'dateToday',
|
||||
label: attachment.string.FileBrowserDateFilterToday,
|
||||
getDate: () => {
|
||||
return { $gte: getBeginningOfToday() }
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'dateYesterday',
|
||||
label: attachment.string.FileBrowserDateFilterYesterday,
|
||||
getDate: () => {
|
||||
return { $gte: getBeginningOfToday() - msInDay, $lt: getBeginningOfToday() }
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'date7Days',
|
||||
label: attachment.string.FileBrowserDateFilter7Days,
|
||||
getDate: () => {
|
||||
return { $gte: getBeginningOfToday() - msInDay * 6 }
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'date30Days',
|
||||
label: attachment.string.FileBrowserDateFilter30Days,
|
||||
getDate: () => {
|
||||
return { $gte: getBeginningOfToday() - msInDay * 29 }
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'date3Months',
|
||||
label: attachment.string.FileBrowserDateFilter3Months,
|
||||
getDate: () => {
|
||||
return { $gte: getBeginningOfToday() - msInDay * 90 }
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'date12Months',
|
||||
label: attachment.string.FileBrowserDateFilter12Months,
|
||||
getDate: () => {
|
||||
return { $gte: getBeginningOfToday() - msInDay * 364 }
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
const fileTypeObjects = [
|
||||
{
|
||||
id: 'typeAny',
|
||||
label: attachment.string.FileBrowserTypeFilterAny,
|
||||
getType: () => {
|
||||
return undefined
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'typeImage',
|
||||
label: attachment.string.FileBrowserTypeFilterImages,
|
||||
getType: () => {
|
||||
return { $like: '%image/%' }
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'typeAudio',
|
||||
label: attachment.string.FileBrowserTypeFilterAudio,
|
||||
getType: () => {
|
||||
return { $like: '%audio/%' }
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'typeVideo',
|
||||
label: attachment.string.FileBrowserTypeFilterVideos,
|
||||
getType: () => {
|
||||
return { $like: '%video/%' }
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'typePDF',
|
||||
label: attachment.string.FileBrowserTypeFilterPDFs,
|
||||
getType: () => {
|
||||
return 'application/pdf'
|
||||
}
|
||||
}
|
||||
]
|
||||
import FileBrowserFilters from './FileBrowserFilters.svelte'
|
||||
import FileBrowserSortMenu from './FileBrowserSortMenu.svelte'
|
||||
|
||||
const client = getClient()
|
||||
export let space: Space | undefined
|
||||
const loc = getCurrentLocation()
|
||||
const spaceId: Ref<Space> | undefined = loc.query?.spaceId as Ref<Space> | undefined
|
||||
export let requestedSpaceClasses: Ref<Class<Space>>[] = []
|
||||
const currentUser = getCurrentAccount() as EmployeeAccount
|
||||
let participants: Ref<Employee>[] = [currentUser.employee]
|
||||
const assignee: Ref<Employee> | null = null
|
||||
let selectedParticipants: Ref<Employee>[] = [currentUser.employee]
|
||||
let selectedSpaces: Ref<Space>[] = []
|
||||
let searchQuery: string = ''
|
||||
let isLoading = false
|
||||
|
||||
let attachments: Attachment[] = []
|
||||
let selectedFileNumber: number | undefined
|
||||
|
||||
let selectedSort: SortMode = SortMode.NewestFile
|
||||
let selectedSort: FileBrowserSortMode = FileBrowserSortMode.NewestFile
|
||||
let selectedDateId = 'dateAny'
|
||||
let selectedFileTypeId = 'typeAny'
|
||||
|
||||
@@ -147,135 +66,89 @@
|
||||
})
|
||||
}
|
||||
|
||||
const showSortMenu = async (ev: Event): Promise<void> => {
|
||||
showPopup(
|
||||
UIMenu,
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
label: sortModeToString(SortMode.NewestFile),
|
||||
action: () => {
|
||||
selectedSort = SortMode.NewestFile
|
||||
}
|
||||
},
|
||||
{
|
||||
label: sortModeToString(SortMode.OldestFile),
|
||||
action: () => {
|
||||
selectedSort = SortMode.OldestFile
|
||||
}
|
||||
},
|
||||
{
|
||||
label: sortModeToString(SortMode.AscendingAlphabetical),
|
||||
action: () => {
|
||||
selectedSort = SortMode.AscendingAlphabetical
|
||||
}
|
||||
},
|
||||
{
|
||||
label: sortModeToString(SortMode.DescendingAlphabetical),
|
||||
action: () => {
|
||||
selectedSort = SortMode.DescendingAlphabetical
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
ev.target as HTMLElement
|
||||
)
|
||||
}
|
||||
$: fetch(searchQuery, selectedSort, selectedFileTypeId, selectedDateId, selectedParticipants, selectedSpaces)
|
||||
|
||||
const sortModeToString = (sortMode: SortMode): IntlString<{}> => {
|
||||
switch (sortMode) {
|
||||
case SortMode.NewestFile:
|
||||
return attachment.string.FileBrowserSortNewest
|
||||
case SortMode.OldestFile:
|
||||
return attachment.string.FileBrowserSortOldest
|
||||
case SortMode.AscendingAlphabetical:
|
||||
return attachment.string.FileBrowserSortAZ
|
||||
case SortMode.DescendingAlphabetical:
|
||||
return attachment.string.FileBrowserSortZA
|
||||
async function fetch(
|
||||
searchQuery_: string,
|
||||
selectedSort_: FileBrowserSortMode,
|
||||
selectedFileTypeId_: string,
|
||||
selectedDateId_: string,
|
||||
selectedParticipants_: Ref<Employee>[],
|
||||
selectedSpaces_: Ref<Space>[]
|
||||
) {
|
||||
isLoading = true
|
||||
|
||||
const nameQuery = searchQuery_ ? { name: { $like: '%' + searchQuery_ + '%' } } : {}
|
||||
|
||||
const accounts = await client.findAll(contact.class.EmployeeAccount, { employee: { $in: selectedParticipants_ } })
|
||||
const senderQuery = accounts.length ? { modifiedBy: { $in: accounts.map((a) => a._id) } } : {}
|
||||
|
||||
let spaceQuery: { space: any }
|
||||
if (selectedSpaces_.length) {
|
||||
spaceQuery = { space: { $in: selectedSpaces_ } }
|
||||
} else {
|
||||
// nothing is selected in space filter - show all available attachments (except for the archived channels)
|
||||
const allSpaces = await client.findAll(core.class.Space, {
|
||||
archived: false,
|
||||
_class: { $in: requestedSpaceClasses }
|
||||
})
|
||||
const availableSpaces = allSpaces
|
||||
.filter((sp) => !sp.private || sp.members.includes(currentUser._id))
|
||||
.map((sp) => sp._id)
|
||||
spaceQuery = { space: { $in: availableSpaces } }
|
||||
}
|
||||
}
|
||||
|
||||
const sortModeToOptionObject = (sortMode: SortMode): SortingQuery<Attachment> => {
|
||||
switch (sortMode) {
|
||||
case SortMode.NewestFile:
|
||||
return { modifiedOn: SortingOrder.Descending }
|
||||
case SortMode.OldestFile:
|
||||
return { modifiedOn: SortingOrder.Ascending }
|
||||
case SortMode.AscendingAlphabetical:
|
||||
return { name: SortingOrder.Ascending }
|
||||
case SortMode.DescendingAlphabetical:
|
||||
return { name: SortingOrder.Descending }
|
||||
}
|
||||
}
|
||||
|
||||
$: fetch(selectedSort, selectedFileTypeId, selectedDateId)
|
||||
|
||||
async function fetch (selectedSort_: SortMode, selectedFileTypeId_: string, selectedDateId_: string) {
|
||||
const spaceQuery = space && { space: space._id }
|
||||
const fileType = fileTypeObjects.find((o) => o.id === selectedFileTypeId_)?.getType()
|
||||
const typeQuery = fileType && { type: fileType }
|
||||
const date = dateObjects.find((o) => o.id === selectedDateId_)?.getDate()
|
||||
const date = dateFileBrowserFilters.find((o) => o.id === selectedDateId_)?.getDate()
|
||||
const dateQuery = date && { modifiedOn: date }
|
||||
|
||||
const fileType = fileTypeFileBrowserFilters.find((o) => o.id === selectedFileTypeId_)?.getType()
|
||||
const fileTypeQuery = fileType && { type: fileType }
|
||||
|
||||
attachments = await client.findAll(
|
||||
attachment.class.Attachment,
|
||||
{ ...spaceQuery, ...typeQuery, ...dateQuery },
|
||||
{ ...nameQuery, ...senderQuery, ...spaceQuery, ...dateQuery, ...fileTypeQuery },
|
||||
{
|
||||
sort: sortModeToOptionObject(selectedSort_)
|
||||
sort: sortModeToOptionObject(selectedSort_),
|
||||
limit: 200
|
||||
}
|
||||
)
|
||||
isLoading = false
|
||||
}
|
||||
|
||||
onDestroy(
|
||||
location.subscribe(async (loc) => {
|
||||
loc.query = undefined
|
||||
navigate(loc)
|
||||
})
|
||||
)
|
||||
</script>
|
||||
|
||||
<div class="ac-header full divide">
|
||||
<div class="ac-header__wrap-title">
|
||||
<span class="ac-header__title"><Label label={attachment.string.FileBrowser} /></span>
|
||||
</div>
|
||||
<EditWithIcon icon={IconSearch} bind:value={searchQuery} placeholder={ui.string.SearchDots} />
|
||||
</div>
|
||||
<div class="filterBlockContainer">
|
||||
<div class="simpleFilterButton">
|
||||
<UserBoxList
|
||||
_class={contact.class.Employee}
|
||||
items={participants}
|
||||
label={attachment.string.FileBrowserFilterFrom}
|
||||
on:update={(evt) => {
|
||||
participants = evt.detail
|
||||
}}
|
||||
noItems={attachment.string.NoParticipants}
|
||||
/>
|
||||
</div>
|
||||
<!-- TODO: wait for In filter -->
|
||||
<!-- <div class="simpleFilterButton">
|
||||
<UserBox _class={contact.class.Employee} label={attachment.string.FileBrowserFilterIn} bind:value={assignee} />
|
||||
</div> -->
|
||||
<div class="simpleFilterButton">
|
||||
<DropdownLabelsIntl
|
||||
items={dateObjects}
|
||||
placeholder={attachment.string.FileBrowserFilterDate}
|
||||
label={attachment.string.FileBrowserFilterDate}
|
||||
bind:selected={selectedDateId}
|
||||
/>
|
||||
</div>
|
||||
<div class="simpleFilterButton">
|
||||
<DropdownLabelsIntl
|
||||
items={fileTypeObjects}
|
||||
placeholder={attachment.string.FileBrowserFilterFileType}
|
||||
label={attachment.string.FileBrowserFilterFileType}
|
||||
bind:selected={selectedFileTypeId}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<FileBrowserFilters
|
||||
{requestedSpaceClasses}
|
||||
{spaceId}
|
||||
bind:selectedParticipants
|
||||
bind:selectedSpaces
|
||||
bind:selectedDateId
|
||||
bind:selectedFileTypeId
|
||||
/>
|
||||
<div class="group">
|
||||
<div class="groupHeader">
|
||||
<div class="eGroupHeaderCount">
|
||||
<Label label={attachment.string.FileBrowserFileCounter} params={{ results: attachments?.length ?? 0 }} />
|
||||
</div>
|
||||
<div class="eGroupHeaderSortMenu" on:click={(event) => showSortMenu(event)}>
|
||||
{'Sort: '}
|
||||
<Label label={sortModeToString(selectedSort)} />
|
||||
</div>
|
||||
<FileBrowserSortMenu bind:selectedSort />
|
||||
</div>
|
||||
{#if attachments?.length}
|
||||
{#if isLoading}
|
||||
<div class="ml-4">
|
||||
<Spinner />
|
||||
</div>
|
||||
{:else if attachments?.length}
|
||||
<div class="flex-col">
|
||||
{#each attachments as attachment, i}
|
||||
<div class="flex-between attachmentRow" class:fixed={i === selectedFileNumber}>
|
||||
@@ -299,9 +172,8 @@
|
||||
|
||||
<style lang="scss">
|
||||
.group {
|
||||
border: 1px solid var(--theme-bg-focused-border);
|
||||
border-radius: 1rem;
|
||||
padding: 1rem 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.groupHeader {
|
||||
@@ -313,10 +185,6 @@
|
||||
font-size: 0.75rem;
|
||||
color: var(--theme-caption-color);
|
||||
}
|
||||
|
||||
.eGroupHeaderSortMenu {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.attachmentRow {
|
||||
@@ -360,16 +228,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.filterBlockContainer {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.simpleFilterButton {
|
||||
min-width: 4rem;
|
||||
max-width: 12rem;
|
||||
margin-left: 0.75rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
<!--
|
||||
// Copyright © 2022 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import contact, { Employee } from '@anticrm/contact'
|
||||
import { Class, Ref, Space } from '@anticrm/core'
|
||||
import { SpaceMultiBoxList, UserBoxList } from '@anticrm/presentation'
|
||||
import { DropdownLabelsIntl } from '@anticrm/ui'
|
||||
import attachment from '../plugin'
|
||||
import { dateFileBrowserFilters, fileTypeFileBrowserFilters } from '..'
|
||||
|
||||
export let requestedSpaceClasses: Ref<Class<Space>>[]
|
||||
export let spaceId: Ref<Space> | undefined
|
||||
export let selectedParticipants: Ref<Employee>[]
|
||||
export let selectedSpaces: Ref<Space>[]
|
||||
export let selectedDateId: string
|
||||
export let selectedFileTypeId: string
|
||||
</script>
|
||||
|
||||
<div class="filterBlockContainer">
|
||||
<div class="simpleFilterButton">
|
||||
<UserBoxList
|
||||
_class={contact.class.Employee}
|
||||
items={selectedParticipants}
|
||||
label={attachment.string.FileBrowserFilterFrom}
|
||||
on:update={(evt) => {
|
||||
selectedParticipants = evt.detail
|
||||
}}
|
||||
noItems={attachment.string.NoParticipants}
|
||||
/>
|
||||
</div>
|
||||
<div class="simpleFilterButton">
|
||||
<SpaceMultiBoxList
|
||||
_classes={requestedSpaceClasses}
|
||||
label={attachment.string.FileBrowserFilterIn}
|
||||
selectedItems={spaceId ? [spaceId] : []}
|
||||
on:update={(evt) => {
|
||||
selectedSpaces = evt.detail
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div class="simpleFilterButton">
|
||||
<DropdownLabelsIntl
|
||||
items={dateFileBrowserFilters}
|
||||
placeholder={attachment.string.FileBrowserFilterDate}
|
||||
label={attachment.string.FileBrowserFilterDate}
|
||||
bind:selected={selectedDateId}
|
||||
/>
|
||||
</div>
|
||||
<div class="simpleFilterButton">
|
||||
<DropdownLabelsIntl
|
||||
items={fileTypeFileBrowserFilters}
|
||||
placeholder={attachment.string.FileBrowserFilterFileType}
|
||||
label={attachment.string.FileBrowserFilterFileType}
|
||||
bind:selected={selectedFileTypeId}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.filterBlockContainer {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.simpleFilterButton {
|
||||
max-width: 12rem;
|
||||
margin-left: 0.75rem;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,83 @@
|
||||
<script lang="ts">
|
||||
import { IntlString } from '@anticrm/platform'
|
||||
import { Label, Menu, showPopup } from '@anticrm/ui'
|
||||
import { FileBrowserSortMode } from '..'
|
||||
import attachment from '../plugin'
|
||||
|
||||
export let selectedSort: FileBrowserSortMode
|
||||
|
||||
const sortModeToString = (sortMode: FileBrowserSortMode): IntlString<{}> => {
|
||||
switch (sortMode) {
|
||||
case FileBrowserSortMode.NewestFile:
|
||||
return attachment.string.FileBrowserSortNewest
|
||||
case FileBrowserSortMode.OldestFile:
|
||||
return attachment.string.FileBrowserSortOldest
|
||||
case FileBrowserSortMode.AscendingAlphabetical:
|
||||
return attachment.string.FileBrowserSortAZ
|
||||
case FileBrowserSortMode.DescendingAlphabetical:
|
||||
return attachment.string.FileBrowserSortZA
|
||||
case FileBrowserSortMode.SmallestSize:
|
||||
return attachment.string.FileBrowserSortSmallest
|
||||
case FileBrowserSortMode.BiggestSize:
|
||||
return attachment.string.FileBrowserSortBiggest
|
||||
}
|
||||
}
|
||||
|
||||
const showSortMenu = async (ev: Event): Promise<void> => {
|
||||
showPopup(
|
||||
Menu,
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
label: sortModeToString(FileBrowserSortMode.NewestFile),
|
||||
action: () => {
|
||||
selectedSort = FileBrowserSortMode.NewestFile
|
||||
}
|
||||
},
|
||||
{
|
||||
label: sortModeToString(FileBrowserSortMode.OldestFile),
|
||||
action: () => {
|
||||
selectedSort = FileBrowserSortMode.OldestFile
|
||||
}
|
||||
},
|
||||
{
|
||||
label: sortModeToString(FileBrowserSortMode.AscendingAlphabetical),
|
||||
action: () => {
|
||||
selectedSort = FileBrowserSortMode.AscendingAlphabetical
|
||||
}
|
||||
},
|
||||
{
|
||||
label: sortModeToString(FileBrowserSortMode.DescendingAlphabetical),
|
||||
action: () => {
|
||||
selectedSort = FileBrowserSortMode.DescendingAlphabetical
|
||||
}
|
||||
},
|
||||
{
|
||||
label: sortModeToString(FileBrowserSortMode.SmallestSize),
|
||||
action: () => {
|
||||
selectedSort = FileBrowserSortMode.SmallestSize
|
||||
}
|
||||
},
|
||||
{
|
||||
label: sortModeToString(FileBrowserSortMode.BiggestSize),
|
||||
action: () => {
|
||||
selectedSort = FileBrowserSortMode.BiggestSize
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
ev.target as HTMLElement
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="sortMenu" on:click={(event) => showSortMenu(event)}>
|
||||
<Label label={attachment.string.FileBrowserSort} />
|
||||
<Label label={sortModeToString(selectedSort)} />
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.sortMenu {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
@@ -19,11 +19,25 @@
|
||||
|
||||
<svg class="svg-{size}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||
<g {fill}>
|
||||
<path d="M39.806,72.858h-8.915c-2.176,0-3.94-1.764-3.94-3.94V31.119c0-2.176,1.764-3.94,3.94-3.94h8.915 c2.176,0,3.94,1.764,3.94,3.94v37.799C43.746,71.094,41.982,72.858,39.806,72.858z"/>
|
||||
<path d="M68.109,72.821h-8.915c-2.176,0-3.94-1.764-3.94-3.94V31.082c0-2.176,1.764-3.94,3.94-3.94h8.915 c2.176,0,3.94,1.764,3.94,3.94v37.799C72.049,71.057,70.285,72.821,68.109,72.821z"/>
|
||||
<path d="M40.489,27.248c0.769,0.719,1.257,1.735,1.257,2.871v37.799c0,2.176-1.764,3.94-3.94,3.94h-8.915 c-0.234,0-0.46-0.03-0.683-0.069c0.704,0.658,1.643,1.069,2.683,1.069h8.915c2.176,0,3.94-1.764,3.94-3.94V31.119 C43.746,29.177,42.338,27.573,40.489,27.248z"/>
|
||||
<path d="M68.792,27.211c0.769,0.719,1.257,1.735,1.257,2.871v37.799c0,2.176-1.764,3.94-3.94,3.94h-8.915 c-0.234,0-0.46-0.03-0.683-0.069c0.704,0.658,1.643,1.069,2.683,1.069h8.915c2.176,0,3.94-1.764,3.94-3.94V31.082 C72.049,29.14,70.641,27.535,68.792,27.211z"/>
|
||||
<path d="M39.806,72.858h-8.915c-2.176,0-3.94-1.764-3.94-3.94V31.119 c0-2.176,1.764-3.94,3.94-3.94h8.915c2.176,0,3.94,1.764,3.94,3.94v37.799C43.746,71.094,41.982,72.858,39.806,72.858z" style="fill:none;stroke:#000000;stroke-miterlimit:10;"/>
|
||||
<path d="M68.109,72.821h-8.915c-2.176,0-3.94-1.764-3.94-3.94V31.082 c0-2.176,1.764-3.94,3.94-3.94h8.915c2.176,0,3.94,1.764,3.94,3.94v37.799C72.049,71.057,70.285,72.821,68.109,72.821z" style="fill:none;stroke:#000000;stroke-miterlimit:10;"/>
|
||||
<path
|
||||
d="M39.806,72.858h-8.915c-2.176,0-3.94-1.764-3.94-3.94V31.119c0-2.176,1.764-3.94,3.94-3.94h8.915 c2.176,0,3.94,1.764,3.94,3.94v37.799C43.746,71.094,41.982,72.858,39.806,72.858z"
|
||||
/>
|
||||
<path
|
||||
d="M68.109,72.821h-8.915c-2.176,0-3.94-1.764-3.94-3.94V31.082c0-2.176,1.764-3.94,3.94-3.94h8.915 c2.176,0,3.94,1.764,3.94,3.94v37.799C72.049,71.057,70.285,72.821,68.109,72.821z"
|
||||
/>
|
||||
<path
|
||||
d="M40.489,27.248c0.769,0.719,1.257,1.735,1.257,2.871v37.799c0,2.176-1.764,3.94-3.94,3.94h-8.915 c-0.234,0-0.46-0.03-0.683-0.069c0.704,0.658,1.643,1.069,2.683,1.069h8.915c2.176,0,3.94-1.764,3.94-3.94V31.119 C43.746,29.177,42.338,27.573,40.489,27.248z"
|
||||
/>
|
||||
<path
|
||||
d="M68.792,27.211c0.769,0.719,1.257,1.735,1.257,2.871v37.799c0,2.176-1.764,3.94-3.94,3.94h-8.915 c-0.234,0-0.46-0.03-0.683-0.069c0.704,0.658,1.643,1.069,2.683,1.069h8.915c2.176,0,3.94-1.764,3.94-3.94V31.082 C72.049,29.14,70.641,27.535,68.792,27.211z"
|
||||
/>
|
||||
<path
|
||||
d="M39.806,72.858h-8.915c-2.176,0-3.94-1.764-3.94-3.94V31.119 c0-2.176,1.764-3.94,3.94-3.94h8.915c2.176,0,3.94,1.764,3.94,3.94v37.799C43.746,71.094,41.982,72.858,39.806,72.858z"
|
||||
style="fill:none;stroke:#000000;stroke-miterlimit:10;"
|
||||
/>
|
||||
<path
|
||||
d="M68.109,72.821h-8.915c-2.176,0-3.94-1.764-3.94-3.94V31.082 c0-2.176,1.764-3.94,3.94-3.94h8.915c2.176,0,3.94,1.764,3.94,3.94v37.799C72.049,71.057,70.285,72.821,68.109,72.821z"
|
||||
style="fill:none;stroke:#000000;stroke-miterlimit:10;"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
@@ -19,8 +19,15 @@
|
||||
|
||||
<svg class="svg-{size}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||
<g {fill}>
|
||||
<path d="M31.356,25.677l38.625,22.3c1.557,0.899,1.557,3.147,0,4.046l-38.625,22.3c-1.557,0.899-3.504-0.225-3.504-2.023V27.7 C27.852,25.902,29.798,24.778,31.356,25.677z"/>
|
||||
<path d="M69.981,47.977l-38.625-22.3c-0.233-0.134-0.474-0.21-0.716-0.259l37.341,21.559c1.557,0.899,1.557,3.147,0,4.046 l-38.625,22.3c-0.349,0.201-0.716,0.288-1.078,0.301c0.656,0.938,1.961,1.343,3.078,0.699l38.625-22.3 C71.538,51.124,71.538,48.876,69.981,47.977z"/>
|
||||
<path d="M31.356,25.677l38.625,22.3c1.557,0.899,1.557,3.147,0,4.046 l-38.625,22.3c-1.557,0.899-3.504-0.225-3.504-2.023V27.7C27.852,25.902,29.798,24.778,31.356,25.677z" style="fill:none;stroke:#000000;stroke-miterlimit:10;"/>
|
||||
<path
|
||||
d="M31.356,25.677l38.625,22.3c1.557,0.899,1.557,3.147,0,4.046l-38.625,22.3c-1.557,0.899-3.504-0.225-3.504-2.023V27.7 C27.852,25.902,29.798,24.778,31.356,25.677z"
|
||||
/>
|
||||
<path
|
||||
d="M69.981,47.977l-38.625-22.3c-0.233-0.134-0.474-0.21-0.716-0.259l37.341,21.559c1.557,0.899,1.557,3.147,0,4.046 l-38.625,22.3c-0.349,0.201-0.716,0.288-1.078,0.301c0.656,0.938,1.961,1.343,3.078,0.699l38.625-22.3 C71.538,51.124,71.538,48.876,69.981,47.977z"
|
||||
/>
|
||||
<path
|
||||
d="M31.356,25.677l38.625,22.3c1.557,0.899,1.557,3.147,0,4.046 l-38.625,22.3c-1.557,0.899-3.504-0.225-3.504-2.023V27.7C27.852,25.902,29.798,24.778,31.356,25.677z"
|
||||
style="fill:none;stroke:#000000;stroke-miterlimit:10;"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
Reference in New Issue
Block a user