Update Telegram and Email layouts. Fix Channels. (#1639)

Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
Alexander Platov 2022-05-05 11:29:46 +03:00 committed by GitHub
parent b0fd3e6b50
commit c2766b4e19
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 1174 additions and 592 deletions

View File

@ -109,6 +109,9 @@
--toggle-on-bg-color: #5e6ad2; --toggle-on-bg-color: #5e6ad2;
--toggle-on-bg-hover: #828fff; --toggle-on-bg-hover: #828fff;
--incoming-msg: rgba(67, 67, 72, .3);
--outcoming-msg: rgba(67, 67, 72, .6);
--theme-bg-color: #18181e; --theme-bg-color: #18181e;
--theme-bg-selection: #282830; --theme-bg-selection: #282830;
--theme-bg-checked: #262b39; --theme-bg-checked: #262b39;
@ -125,8 +128,6 @@
--theme-border-modal: rgba(0, 0, 0, .2); --theme-border-modal: rgba(0, 0, 0, .2);
--theme-chat-selection: radial-gradient(135.96% 3333.35% at -2.36% -27.63%, rgba(210, 183, 156, 0.11) 0%, rgba(204, 196, 184, 0.0785128) 20.8%, rgba(104, 104, 114, 0.11) 100%); --theme-chat-selection: radial-gradient(135.96% 3333.35% at -2.36% -27.63%, rgba(210, 183, 156, 0.11) 0%, rgba(204, 196, 184, 0.0785128) 20.8%, rgba(104, 104, 114, 0.11) 100%);
--theme-chat-divider: rgb(36, 36, 41); --theme-chat-divider: rgb(36, 36, 41);
--theme-incoming-msg: rgba(67, 67, 72, .3);
--theme-outcoming-msg: rgba(67, 67, 72, .6);
--theme-card-bg: #282830; --theme-card-bg: #282830;
--theme-card-bg-dark: rgba(222, 222, 240, .1); --theme-card-bg-dark: rgba(222, 222, 240, .1);
@ -250,6 +251,9 @@
--toggle-on-bg-color: #6e79d6; --toggle-on-bg-color: #6e79d6;
--toggle-on-bg-hover: #535db3; --toggle-on-bg-hover: #535db3;
--incoming-msg: rgba(67, 67, 72, .3);
--outcoming-msg: rgba(67, 67, 72, .6);
--theme-bg-color: #FFFFFF; --theme-bg-color: #FFFFFF;
--theme-bg-selection: #F1F1F4; --theme-bg-selection: #F1F1F4;
--theme-menu-color: #E7E7E7; --theme-menu-color: #E7E7E7;
@ -264,8 +268,6 @@
--theme-border-modal: rgba(0, 0, 0, .2); --theme-border-modal: rgba(0, 0, 0, .2);
--theme-chat-selection: radial-gradient(135.96% 3333.35% at -2.36% -27.63%, rgba(210, 183, 156, 0.11) 0%, rgba(204, 196, 184, 0.0785128) 20.8%, rgba(104, 104, 114, 0.11) 100%); --theme-chat-selection: radial-gradient(135.96% 3333.35% at -2.36% -27.63%, rgba(210, 183, 156, 0.11) 0%, rgba(204, 196, 184, 0.0785128) 20.8%, rgba(104, 104, 114, 0.11) 100%);
--theme-chat-divider: rgb(233, 233, 233); --theme-chat-divider: rgb(233, 233, 233);
--theme-incoming-msg: rgba(67, 67, 72, .3);
--theme-outcoming-msg: rgba(67, 67, 72, .6);
--theme-card-bg: #FFF; --theme-card-bg: #FFF;
--theme-card-bg-dark: rgba(255, 255, 255, .3); --theme-card-bg-dark: rgba(255, 255, 255, .3);

View File

@ -116,6 +116,15 @@ p:last-child { margin-block-end: 0; }
float: left; float: left;
} }
input.search {
margin: 0;
color: #d6d6d6;
border: none;
caret-color: var(--caret-color);
&.padding { padding: .625rem .75rem; }
}
/* Flex */ /* Flex */
.flex { display: flex; } .flex { display: flex; }
.inline-flex { display: inline-flex; } .inline-flex { display: inline-flex; }
@ -374,6 +383,7 @@ p:last-child { margin-block-end: 0; }
.px-2 { padding: 0 .5rem; } .px-2 { padding: 0 .5rem; }
.px-3 { padding: 0 .75rem; } .px-3 { padding: 0 .75rem; }
.px-4 { padding: 0 1rem; } .px-4 { padding: 0 1rem; }
.py-4 { padding: 1rem 0; }
.py-10 { padding: 2.5rem 0; } .py-10 { padding: 2.5rem 0; }
.p-1 { padding: .25rem; } .p-1 { padding: .25rem; }
@ -606,6 +616,11 @@ a.no-line {
background-color: var(--theme-bg-accent-color); background-color: var(--theme-bg-accent-color);
&:hover { background-color: var(--theme-menu-divider); } &:hover { background-color: var(--theme-menu-divider); }
} }
.scroll-divider-color::-webkit-scrollbar-thumb {
background-color: var(--divider-color);
&:horizontal { border-radius: .25rem .25rem 0 0; }
&:hover { background-color: var(--popup-bg-hover); }
}
/* Backgrounds & Colors */ /* Backgrounds & Colors */
.background-body-color { background-color: var(--body-color); } .background-body-color { background-color: var(--body-color); }

View File

@ -209,23 +209,23 @@
/* Basic */ /* Basic */
.antiTitle { .antiTitle {
.icon-wrapper, .icon-wrapper, &.icon-wrapper,
.title-wrapper { .title-wrapper, &.title-wrapper {
display: flex; display: flex;
flex-wrap: nowrap; flex-wrap: nowrap;
min-width: 0; min-width: 0;
} }
.title-wrapper { .title-wrapper, &.title-wrapper {
flex-direction: column; flex-direction: column;
flex-grow: 1; flex-grow: 1;
} }
.icon-wrapper { align-items: center; } .icon-wrapper, &.icon-wrapper { align-items: center; }
.wrapped-icon { .wrapped-icon, &.wrapped-icon {
margin-right: .75rem; margin-right: .75rem;
color: var(--content-color); color: var(--content-color);
} }
.wrapped-title { .wrapped-title, &.wrapped-title {
min-width: 0; min-width: 0;
font-weight: 500; font-weight: 500;
font-size: 1rem; font-size: 1rem;
@ -236,7 +236,7 @@
white-space: nowrap; white-space: nowrap;
user-select: none; user-select: none;
} }
.wrapped-subtitle { .wrapped-subtitle, &.wrapped-subtitle {
min-width: 0; min-width: 0;
font-size: 0.75rem; font-size: 0.75rem;
color: var(--dark-color); color: var(--dark-color);
@ -249,5 +249,7 @@
-webkit-line-clamp: 2; -webkit-line-clamp: 2;
line-clamp: 2; line-clamp: 2;
user-select: none; user-select: none;
b { color: var(--content-color); }
} }
} }

View File

@ -425,6 +425,9 @@
} }
} }
// THead background-color in Tooltip
.popup-tooltip .antiTable .scroller-thead { background-color: var(--accent-bg-color); }
// Hide row menu in Tooltip // Hide row menu in Tooltip
.popup-tooltip .antiTable .antiTable-body__row:hover .antiTable-cells__firstCell .antiTable-cells__firstCell-menuRow { visibility: hidden; } .popup-tooltip .antiTable .antiTable-body__row:hover .antiTable-cells__firstCell .antiTable-cells__firstCell-menuRow { visibility: hidden; }

View File

@ -32,6 +32,7 @@
"MinutesAfter": "{minutes, plural, =1 {in a minute} other {in # minutes}}", "MinutesAfter": "{minutes, plural, =1 {in a minute} other {in # minutes}}",
"HoursAfter": "{hours, plural, =1 {in an hour} other {in # hours}}", "HoursAfter": "{hours, plural, =1 {in an hour} other {in # hours}}",
"DaysAfter": "{days, plural, =1 {in a day} other {in # days}}", "DaysAfter": "{days, plural, =1 {in a day} other {in # days}}",
"NoActionsDefined": "No actions applicable" "NoActionsDefined": "No actions applicable",
"Incoming": "Incoming"
} }
} }

View File

@ -32,6 +32,7 @@
"MinutesAfter": "{minutes, plural, =1 {через минуту} other {через # минут}}", "MinutesAfter": "{minutes, plural, =1 {через минуту} other {через # минут}}",
"HoursAfter": "{hours, plural, =1 {через час} other {через # часа}}", "HoursAfter": "{hours, plural, =1 {через час} other {через # часа}}",
"DaysAfter": "{days, plural, =1 {через день} other {через # дней}}", "DaysAfter": "{days, plural, =1 {через день} other {через # дней}}",
"NoActionsDefined": "Нет доступных действий" "NoActionsDefined": "Нет доступных действий",
"Incoming": "Входящие"
} }
} }

View File

@ -74,6 +74,7 @@
on:click on:click
on:focus on:focus
on:blur on:blur
on:mousemove
> >
{#if icon && !loading} {#if icon && !loading}
<div <div

View File

@ -47,7 +47,7 @@
<div class="popupPanel-title__content"><slot name="title" /></div> <div class="popupPanel-title__content"><slot name="title" /></div>
<div class="buttons-group xsmall-gap"> <div class="buttons-group xsmall-gap">
<slot name="utils" /> <slot name="utils" />
{#if asideFloat} {#if asideFloat && $$slots.aside && isAside}
{#if $$slots.utils}<div class="buttons-divider" />{/if} {#if $$slots.utils}<div class="buttons-divider" />{/if}
<Button <Button
icon={IconDetails} icon={IconDetails}

View File

@ -32,6 +32,7 @@
let isScrolling: boolean = false let isScrolling: boolean = false
let dY: number let dY: number
let belowContent: number | undefined = undefined let belowContent: number | undefined = undefined
let beforeContent: number | undefined = undefined
let scrolling: boolean = autoscroll let scrolling: boolean = autoscroll
let firstScroll: boolean = autoscroll let firstScroll: boolean = autoscroll
@ -74,6 +75,7 @@
} }
const onScroll = (event: MouseEvent): void => { const onScroll = (event: MouseEvent): void => {
scrolling = false
if (isScrolling && divBar && divScroll) { if (isScrolling && divBar && divScroll) {
const rectScroll = divScroll.getBoundingClientRect() const rectScroll = divScroll.getBoundingClientRect()
let Y = event.clientY - dY let Y = event.clientY - dY
@ -97,6 +99,7 @@
isScrolling = false isScrolling = false
} }
const onScrollStart = (event: MouseEvent): void => { const onScrollStart = (event: MouseEvent): void => {
scrolling = false
const el: HTMLElement = event.currentTarget as HTMLElement const el: HTMLElement = event.currentTarget as HTMLElement
if (el && divScroll) { if (el && divScroll) {
dY = event.clientY - el.getBoundingClientRect().y dY = event.clientY - el.getBoundingClientRect().y
@ -110,35 +113,37 @@
const checkFade = (): void => { const checkFade = (): void => {
if (divScroll) { if (divScroll) {
const t = divScroll.scrollTop beforeContent = divScroll.scrollTop
const b = divScroll.scrollHeight - divScroll.clientHeight - t belowContent = divScroll.scrollHeight - divScroll.clientHeight - beforeContent
if (t > 0 && b > 0) mask = 'both' if (beforeContent > 1 && belowContent > 1) mask = 'both'
else if (t > 0) mask = 'bottom' else if (beforeContent > 1) mask = 'bottom'
else if (b > 0) mask = 'top' else if (belowContent > 1) mask = 'top'
else mask = 'none' else mask = 'none'
if (scrolling && divScroll.scrollHeight - divScroll.clientHeight - divScroll.scrollTop > 10 && !firstScroll) {
scrolling = false
}
if (!scrolling && belowContent && belowContent <= 10) scrolling = true
} }
checkBack() checkBack()
if (!isScrolling) checkBar() if (!isScrolling) checkBar()
if (scrolling && belowContent && belowContent > 1) {
divScroll.scrollTop = divScroll.scrollHeight - divScroll.clientHeight
}
} }
const observer = new IntersectionObserver(() => checkFade(), { root: null, threshold: 0.1 }) const observer = new IntersectionObserver(() => checkFade(), { root: null, threshold: 0.1 })
$: if (autoscroll && !scrolling && belowContent && belowContent < 1 && divScroll) { const scrollDown = (): void => {
divScroll.scrollTop = divScroll.scrollHeight - divScroll.clientHeight divScroll.scrollTop = divScroll.scrollHeight
scrolling = true
} }
$: if (scrolling && divScroll && divScroll.scrollHeight - divScroll.scrollTop - divScroll.clientHeight < 5) { $: if (scrolling && belowContent && belowContent > 10) scrollDown()
divScroll.scrollTop = divScroll.scrollHeight - divScroll.clientHeight
}
onMount(() => { onMount(() => {
if (divScroll && divBox) { if (divScroll && divBox) {
divScroll.addEventListener('scroll', checkFade) divScroll.addEventListener('scroll', checkFade)
const tempEl = divBox.querySelector('*') as HTMLElement const tempEl = divBox.querySelector('*') as HTMLElement
if (tempEl) observer.observe(tempEl) if (tempEl) observer.observe(tempEl)
if (scrolling) {
scrollDown()
firstScroll = false
}
checkFade() checkFade()
} }
if (divBack) checkBack() if (divBack) checkBack()
@ -150,27 +155,14 @@
if (divScroll && divBox) { if (divScroll && divBox) {
const tempEl = divBox.querySelector('*') as HTMLElement const tempEl = divBox.querySelector('*') as HTMLElement
if (tempEl) observer.observe(tempEl) if (tempEl) observer.observe(tempEl)
if (scrolling) divScroll.scrollTop = divScroll.scrollHeight - divScroll.clientHeight if (scrolling) scrollDown()
belowContent = divScroll.scrollHeight - divScroll.clientHeight - divScroll.scrollTop
checkFade() checkFade()
} }
}) })
let divWidth: number = 0 let divWidth: number = 0
const _resize = (): void => { const _resize = (): void => checkFade()
checkFade()
}
$: if (divWidth) _resize() $: if (divWidth) _resize()
const _scroll = (ev: Event): void => {
if (ev.type === 'scroll') {
firstScroll ? (firstScroll = false) : (scrolling = false)
if (ev.target) {
const el: HTMLElement = ev.target as HTMLElement
if (el.scrollHeight - el.scrollTop - el.clientHeight < 5) scrolling = true
}
}
}
</script> </script>
<svelte:window on:resize={_resize} /> <svelte:window on:resize={_resize} />
@ -178,7 +170,6 @@
<div <div
bind:this={divScroll} bind:this={divScroll}
bind:clientWidth={divWidth} bind:clientWidth={divWidth}
on:scroll={_scroll}
class="scroll relative" class="scroll relative"
class:tableFade class:tableFade
class:antiNav-topFade={mask === 'top'} class:antiNav-topFade={mask === 'top'}

View File

@ -121,6 +121,7 @@
closeTooltip() closeTooltip()
} }
let timeout: number
const whileShow = (ev: MouseEvent): void => { const whileShow = (ev: MouseEvent): void => {
if ($tooltip.element && tooltipHTML) { if ($tooltip.element && tooltipHTML) {
const rectP = tooltipHTML.getBoundingClientRect() const rectP = tooltipHTML.getBoundingClientRect()
@ -129,10 +130,20 @@
const inTrigger: boolean = ev.x >= rect.left && ev.x <= rect.right && ev.y >= rect.top && ev.y <= rect.bottom const inTrigger: boolean = ev.x >= rect.left && ev.x <= rect.right && ev.y >= rect.top && ev.y <= rect.bottom
const inPopup: boolean = const inPopup: boolean =
ev.x >= rectP.left && ev.x <= rectP.right && ev.y >= rectP.top - dT && ev.y <= rectP.bottom + dB ev.x >= rectP.left && ev.x <= rectP.right && ev.y >= rectP.top - dT && ev.y <= rectP.bottom + dB
clearTimeout(timeout)
if (tooltipSW) { if (tooltipSW) {
if (!inTrigger) hideTooltip() if (!inTrigger) {
timeout = setTimeout(() => {
hideTooltip()
}, 100)
}
} else { } else {
if (!(inTrigger || inPopup)) hideTooltip() if (!(inTrigger || inPopup)) {
timeout = setTimeout(() => {
hideTooltip()
}, 100)
}
} }
} }
} }
@ -148,7 +159,7 @@
}} }}
/> />
{#if $tooltip.component} {#if $tooltip.component}
<div class="popup-tooltip antiPopup" bind:clientWidth={clWidth} bind:this={tooltipHTML}> <div class="popup-tooltip" class:doublePadding={$tooltip.label} bind:clientWidth={clWidth} bind:this={tooltipHTML}>
{#if $tooltip.label}<div class="fs-title mb-4"> {#if $tooltip.label}<div class="fs-title mb-4">
<Label label={$tooltip.label} params={$tooltip.props ?? {}} /> <Label label={$tooltip.label} params={$tooltip.props ?? {}} />
</div>{/if} </div>{/if}
@ -179,14 +190,18 @@
position: fixed; position: fixed;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding: 1rem; padding: 0.5rem;
color: var(--theme-caption-color); color: var(--caption-color);
background-color: var(--theme-tooltip-color); background-color: var(--accent-bg-color);
border: 1px solid var(--theme-bg-accent-color); border: 1px solid var(--divider-color);
border-radius: 0.75rem; border-radius: 0.75rem;
box-shadow: 0px 10px 20px rgba(0, 0, 0, 0.2); box-shadow: 0px 10px 20px rgba(0, 0, 0, 0.2);
user-select: none; user-select: none;
z-index: 10000; z-index: 10000;
&.doublePadding {
padding: 1rem;
}
} }
.nub { .nub {
@ -203,12 +218,12 @@
height: 7px; height: 7px;
} }
&::before { &::before {
background-color: var(--theme-tooltip-color); background-color: var(--accent-bg-color);
clip-path: url('#nub-bg'); clip-path: url('#nub-bg');
z-index: 1; z-index: 1;
} }
&::after { &::after {
background-color: var(--theme-bg-accent-color); background-color: var(--divider-color);
clip-path: url('#nub-border'); clip-path: url('#nub-border');
z-index: 2; z-index: 2;
} }
@ -265,9 +280,9 @@
position: fixed; position: fixed;
padding: 0.5rem 0.75rem; padding: 0.5rem 0.75rem;
text-align: center; text-align: center;
color: var(--theme-caption-color); color: var(--caption-color);
background-color: var(--theme-tooltip-color); background-color: var(--accent-bg-color);
border: 1px solid var(--theme-bg-accent-color); border: 1px solid var(--divider-color);
border-radius: 0.75rem; border-radius: 0.75rem;
box-shadow: 0px 10px 20px rgba(0, 0, 0, 0.2); box-shadow: 0px 10px 20px rgba(0, 0, 0, 0.2);
user-select: none; user-select: none;
@ -281,12 +296,12 @@
height: 7px; height: 7px;
} }
&::before { &::before {
background-color: var(--theme-tooltip-color); background-color: var(--accent-bg-color);
clip-path: url('#nub-bg'); clip-path: url('#nub-bg');
z-index: 1; z-index: 1;
} }
&::after { &::after {
background-color: var(--theme-bg-accent-color); background-color: var(--divider-color);
clip-path: url('#nub-border'); clip-path: url('#nub-border');
z-index: 2; z-index: 2;
} }
@ -302,7 +317,7 @@
bottom: 100%; bottom: 100%;
&::after, &::after,
&::before { &::before {
bottom: -7px; bottom: -6px;
transform: rotate(180deg); transform: rotate(180deg);
} }
} }

View File

@ -57,7 +57,8 @@ export default plugin(uiId, {
MinutesAfter: '' as IntlString, MinutesAfter: '' as IntlString,
HoursAfter: '' as IntlString, HoursAfter: '' as IntlString,
DaysAfter: '' as IntlString, DaysAfter: '' as IntlString,
NoActionsDefined: '' as IntlString NoActionsDefined: '' as IntlString,
Incoming: '' as IntlString
}, },
metadata: { metadata: {
DefaultApplication: '' as Metadata<AnyComponent> DefaultApplication: '' as Metadata<AnyComponent>

View File

@ -178,7 +178,7 @@ export function fitPopupElement (modalHTML: HTMLElement, element?: PopupAlignmen
show = false show = false
newProps.left = newProps.right = newProps.top = newProps.bottom = '' newProps.left = newProps.right = newProps.top = newProps.bottom = ''
newProps.maxHeight = newProps.height = '' newProps.maxHeight = newProps.height = ''
newProps.maxWidth = newProps.width = '' newProps.maxWidth = newProps.width = newProps.minWidth = ''
if (typeof element !== 'string') { if (typeof element !== 'string') {
const result = fitPopupPositionedElement(modalHTML, element, newProps) const result = fitPopupPositionedElement(modalHTML, element, newProps)
applyStyle(newProps, modalHTML) applyStyle(newProps, modalHTML)
@ -198,8 +198,8 @@ export function fitPopupElement (modalHTML: HTMLElement, element?: PopupAlignmen
} else if (element === 'float') { } else if (element === 'float') {
newProps.top = 'calc(var(--status-bar-height) + .25rem)' newProps.top = 'calc(var(--status-bar-height) + .25rem)'
newProps.bottom = '.25rem' newProps.bottom = '.25rem'
newProps.minWidth = '40rem'
newProps.width = '40%' newProps.width = '40%'
newProps.maxWidth = '60%'
newProps.right = '.25rem' newProps.right = '.25rem'
show = true show = true
} else if (element === 'account') { } else if (element === 'account') {

View File

@ -5,7 +5,7 @@
"toolPackages": [ "toolPackages": [
{ {
"packageName": "@microsoft/api-extractor", "packageName": "@microsoft/api-extractor",
"packageVersion": "7.19.2" "packageVersion": "7.23.0"
} }
] ]
} }

View File

@ -14,12 +14,16 @@
// limitations under the License. // limitations under the License.
--> -->
<script lang="ts"> <script lang="ts">
import { createEventDispatcher } from 'svelte'
import type { Attachment } from '@anticrm/attachment' import type { Attachment } from '@anticrm/attachment'
import { showPopup, closeTooltip } from '@anticrm/ui' import { showPopup, closeTooltip, Icon, IconClose } from '@anticrm/ui'
import { PDFViewer, getFileUrl } from '@anticrm/presentation' import { PDFViewer, getFileUrl } from '@anticrm/presentation'
import filesize from 'filesize' import filesize from 'filesize'
export let value: Attachment export let value: Attachment
export let removable: boolean = false
const dispatch = createEventDispatcher()
const maxLenght: number = 16 const maxLenght: number = 16
const trimFilename = (fname: string): string => const trimFilename = (fname: string): string =>
@ -36,7 +40,7 @@
} }
</script> </script>
<div class="flex-row-center"> <div class="flex-row-center attachment-container">
{#if openEmbedded(value.type)} {#if openEmbedded(value.type)}
<div <div
class="flex-center icon" class="flex-center icon"
@ -46,11 +50,33 @@
}} }}
> >
{iconLabel(value.name)} {iconLabel(value.name)}
{#if removable}
<div
class="remove-btn"
on:click|preventDefault={() => {
dispatch('remove')
}}
>
<Icon icon={IconClose} size={'medium'} />
</div>
{/if}
</div> </div>
{:else} {:else}
<a class="no-line" href={getFileUrl(value.file)} download={value.name} <a class="no-line" href={getFileUrl(value.file)} download={value.name}>
><div class="flex-center icon">{iconLabel(value.name)}</div></a <div class="flex-center icon">
{iconLabel(value.name)}
{#if removable}
<div
class="remove-btn"
on:click|preventDefault={() => {
dispatch('remove')
}}
> >
<Icon icon={IconClose} size={'medium'} />
</div>
{/if}
</div>
</a>
{/if} {/if}
<div class="flex-col info"> <div class="flex-col info">
{#if openEmbedded(value.type)} {#if openEmbedded(value.type)}
@ -72,17 +98,38 @@
<style lang="scss"> <style lang="scss">
.icon { .icon {
position: relative;
flex-shrink: 0; flex-shrink: 0;
margin-right: 1rem; margin-right: 1rem;
width: 2rem; width: 2rem;
height: 2rem; height: 2rem;
font-weight: 500; font-weight: 500;
font-size: 0.625rem; font-size: 0.625rem;
color: var(--primary-button-color); color: var(--white-color);
background-color: var(--primary-button-enabled); background-color: var(--primary-bg-color);
border: 1px solid rgba(0, 0, 0, 0.1); border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 0.5rem; border-radius: 0.5rem;
cursor: pointer; cursor: pointer;
.remove-btn {
position: absolute;
display: flex;
justify-content: center;
align-items: center;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: var(--primary-bg-hover);
border-radius: 0.5rem;
opacity: 0;
}
}
.attachment-container {
margin-right: 1rem;
&:hover .remove-btn {
opacity: 1;
}
} }
.name { .name {

View File

@ -16,10 +16,12 @@
import { createEventDispatcher, onMount } from 'svelte' import { createEventDispatcher, onMount } from 'svelte'
import type { IntlString } from '@anticrm/platform' import type { IntlString } from '@anticrm/platform'
import { translate } from '@anticrm/platform' import { translate } from '@anticrm/platform'
import { Button, Icon, IconClose, IconBlueCheck, IconArrowRight } from '@anticrm/ui' import { Button, IconClose, closeTooltip, IconBlueCheck } from '@anticrm/ui'
import IconCopy from './icons/Copy.svelte'
export let value: string = '' export let value: string = ''
export let placeholder: IntlString export let placeholder: IntlString
export let editable: boolean = false
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
let input: HTMLInputElement let input: HTMLInputElement
@ -31,38 +33,55 @@
}) })
</script> </script>
<div class="selectPopup relative"> <div class="buttons-group xsmall-gap">
<div class="header no-border"> {#if editable}
<div class="flex-between flex-grow pr-2">
<div class="flex-grow">
<input <input
bind:this={input} bind:this={input}
class="search"
type="text" type="text"
bind:value bind:value
placeholder={phTraslate} placeholder={phTraslate}
style="width: 100%;" style="width: 100%;"
on:keypress={(ev) => { on:keypress={(ev) => {
if (ev.key === 'Enter') dispatch('close', value) if (ev.key === 'Enter') {
}} dispatch('update', value)
on:keydown={(ev) => { closeTooltip()
if (ev.key === 'ArrowLeft' && ev.altKey) dispatch('update', 'left') }
if (ev.key === 'ArrowRight' && ev.altKey) dispatch('update', 'right')
}} }}
on:change on:change
/> />
</div> <Button
<div class="buttons-group small-gap"> kind={'transparent'}
<div size={'small'}
class="clear-btn show" icon={IconClose}
disabled={value === ''}
on:click={() => { on:click={() => {
dispatch('close', value) if (input) {
value = ''
input.focus()
}
}} }}
> />
<div class="icon"><Icon icon={IconClose} size={'inline'} /></div> {:else}
</div> <span>{value}</span>
<Button kind={'transparent'} size={'small'} icon={IconArrowRight} on:click={() => dispatch('update', 'open')} /> {/if}
<Button kind={'transparent'} size={'small'} icon={IconBlueCheck} on:click={() => dispatch('close', value)} /> <Button
</div> kind={'transparent'}
</div> size={'small'}
</div> icon={IconCopy}
on:click={() => {
navigator.clipboard.writeText(value)
}}
/>
{#if editable}
<Button
kind={'transparent'}
size={'small'}
icon={IconBlueCheck}
on:click={() => {
dispatch('update', value)
closeTooltip()
}}
/>
{/if}
</div> </div>

View File

@ -19,7 +19,7 @@
import contact from '@anticrm/contact' import contact from '@anticrm/contact'
import type { AttachedData, Doc, Ref, Timestamp } from '@anticrm/core' import type { AttachedData, Doc, Ref, Timestamp } from '@anticrm/core'
import type { Asset, IntlString } from '@anticrm/platform' import type { Asset, IntlString } from '@anticrm/platform'
import { AnyComponent, showPopup, Button, Menu, closePopup } from '@anticrm/ui' import { AnyComponent, showPopup, Button, Menu, showTooltip, closeTooltip, eventToHTMLElement } from '@anticrm/ui'
import type { Action, ButtonKind, ButtonSize } from '@anticrm/ui' import type { Action, ButtonKind, ButtonSize } from '@anticrm/ui'
import presentation from '@anticrm/presentation' import presentation from '@anticrm/presentation'
import { getChannelProviders } from '../utils' import { getChannelProviders } from '../utils'
@ -108,6 +108,7 @@
let actions: Action[] = [] let actions: Action[] = []
let addBtn: HTMLButtonElement let addBtn: HTMLButtonElement
const btns: HTMLButtonElement[] = [] const btns: HTMLButtonElement[] = []
let anchor: HTMLElement
function filterUndefined (channels: AttachedData<Channel>[]): AttachedData<Channel>[] { function filterUndefined (channels: AttachedData<Channel>[]): AttachedData<Channel>[] {
return channels.filter((channel) => channel.value !== undefined && channel.value.length > 0) return channels.filter((channel) => channel.value !== undefined && channel.value.length > 0)
@ -145,94 +146,41 @@
updateMenu() updateMenu()
} }
const editChannel = (channel: Item, n: number, ev: MouseEvent): void => {
showPopup(
ChannelEditor,
{ value: channel.value, placeholder: channel.placeholder },
ev.target as HTMLElement,
(result) => {
if (result !== undefined) {
if (result === null || result === '') displayItems = dropItem(n)
else displayItems[n].value = result
saveItems()
if (displayItems.length < providers.size && addBtn) addBtn.click()
}
},
(result) => {
if (result !== undefined) {
if (result === 'left') {
closePopup()
if (displayItems[n].value === '') {
displayItems = dropItem(n)
saveItems()
}
if (n === 0) {
if (addBtn) addBtn.click()
else btns[displayItems.length - 1].click()
} else btns[n - 1].click()
} else if (result === 'right') {
closePopup()
if (displayItems[n].value === '') {
displayItems = dropItem(n)
saveItems()
}
if (n === displayItems.length - 1) {
if (addBtn) addBtn.click()
else btns[0].click()
} else btns[n + 1].click()
} else if (result === 'open') {
closePopup()
dispatch('open', { presenter: channel.presenter })
}
}
}
)
}
const showMenu = (ev: MouseEvent): void => { const showMenu = (ev: MouseEvent): void => {
showPopup( showPopup(Menu, { actions }, ev.target as HTMLElement)
Menu,
{ actions },
ev.target as HTMLElement,
() => {},
(result) => {
if (result !== undefined && displayItems.length > 0) {
if (result === 'left') {
closePopup()
btns[displayItems.length - 1].click()
} else if (result === 'right') {
closePopup()
btns[0].click()
} }
const editChannel = (el: HTMLElement, n: number, item: Item): void => {
showTooltip(
undefined,
el,
undefined,
ChannelEditor,
{ value: item.value, placeholder: item.placeholder, editable },
anchor,
(result) => {
if (result.detail !== undefined) {
if (result.detail === '') displayItems = dropItem(n)
else displayItems[n].value = result.detail
saveItems()
} }
} }
) )
} }
let copied: boolean = false const _focus = (ev: Event, n: number, item: Item): void => {
const el = ev.target as HTMLButtonElement
if (el) editChannel(el, n, item)
}
</script> </script>
<div <div
bind:this={anchor}
class="{displayItems.length === 0 ? 'clear-mins' : 'buttons-group'} {kind === 'no-border' class="{displayItems.length === 0 ? 'clear-mins' : 'buttons-group'} {kind === 'no-border'
? 'xsmall-gap' ? 'xsmall-gap'
: 'xxsmall-gap'}" : 'xxsmall-gap'}"
class:short={displayItems.length > 4 && length === 'short'} class:short={displayItems.length > 4 && length === 'short'}
> >
{#each displayItems as item, i} {#each displayItems as item, i}
{#if item.value === ''}
<Button
icon={item.icon}
{kind}
{size}
{shape}
click={item.value === ''}
on:click={(ev) => {
if (editable) editChannel(item, i, ev)
}}
/>
{:else}
<div class="tooltip-container">
<div class="tooltip">
{item.value}{#if copied}<span class="ml-1 text-sm dark-color">(copied)</span>{/if}
</div>
<Button <Button
bind:input={btns[i]} bind:input={btns[i]}
icon={item.icon} icon={item.icon}
@ -240,23 +188,18 @@
{size} {size}
{shape} {shape}
highlight={item.integration || item.notification} highlight={item.integration || item.notification}
on:mousemove={(ev) => {
_focus(ev, i, item)
}}
on:focus={(ev) => {
_focus(ev, i, item)
}}
on:click={(ev) => { on:click={(ev) => {
if (editable) { if (editable) editChannel(eventToHTMLElement(ev), i, item)
editChannel(item, i, ev) else closeTooltip()
} else {
dispatch('open', item) dispatch('open', item)
if (!copied) {
navigator.clipboard.writeText(item.value)
copied = true
setTimeout(() => {
copied = false
}, 1000)
}
}
}} }}
/> />
</div>
{/if}
{/each} {/each}
{#if actions.length > 0 && editable} {#if actions.length > 0 && editable}
<Button <Button
@ -270,43 +213,3 @@
/> />
{/if} {/if}
</div> </div>
<style lang="scss">
.tooltip-container {
position: relative;
display: flex;
justify-content: center;
align-items: center;
min-width: 0;
min-height: 0;
width: min-content;
.tooltip {
overflow: hidden;
position: absolute;
padding: 0.25rem 0.5rem;
bottom: 100%;
left: 50%;
width: auto;
min-width: 0;
white-space: nowrap;
text-overflow: ellipsis;
background-color: var(--accent-bg-color);
border: 1px solid var(--button-border-color);
border-radius: 0.25rem;
transform-origin: center center;
transform: translate(-50%, -0.25rem) scale(0.9);
opacity: 0;
box-shadow: var(--accent-shadow);
transition-property: transform, opacity;
transition-duration: 0.15s;
transition-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1.275);
pointer-events: none;
z-index: 1000;
}
&:hover .tooltip {
transform: translate(-50%, -0.5rem) scale(1);
opacity: 1;
}
}
</style>

View File

@ -16,10 +16,10 @@
<script lang="ts"> <script lang="ts">
import type { Class, Doc, Ref } from '@anticrm/core' import type { Class, Doc, Ref } from '@anticrm/core'
import { createQuery, getClient } from '@anticrm/presentation' import { createQuery, getClient } from '@anticrm/presentation'
import type { ButtonKind, ButtonSize } from '@anticrm/ui' import { ButtonKind, ButtonSize, closeTooltip } from '@anticrm/ui'
import { ChannelProvider, Channel } from '@anticrm/contact' import { ChannelProvider, Channel } from '@anticrm/contact'
import { showPanel } from '@anticrm/ui' import { showPopup } from '@anticrm/ui'
import contact from '../plugin' import contact from '../plugin'
import ChannelsDropdown from './ChannelsDropdown.svelte' import ChannelsDropdown from './ChannelsDropdown.svelte'
@ -108,7 +108,8 @@
if (ev.detail.presenter !== undefined && Array.isArray(channels)) { if (ev.detail.presenter !== undefined && Array.isArray(channels)) {
const channel = channels[0] const channel = channels[0]
if (channel !== undefined) { if (channel !== undefined) {
showPanel(ev.detail.presenter, channel.attachedTo, channel.attachedToClass, 'float') closeTooltip()
showPopup(ev.detail.presenter, { _id: channel.attachedTo, _class: channel.attachedToClass }, 'float')
} }
} }
} }

View File

@ -17,7 +17,7 @@
import type { Channel } from '@anticrm/contact' import type { Channel } from '@anticrm/contact'
import type { ButtonKind, ButtonSize } from '@anticrm/ui' import type { ButtonKind, ButtonSize } from '@anticrm/ui'
import ChannelsDropdown from './ChannelsDropdown.svelte' import ChannelsDropdown from './ChannelsDropdown.svelte'
import { showPanel } from '@anticrm/ui' import { showPopup } from '@anticrm/ui'
export let value: Channel[] | Channel | null export let value: Channel[] | Channel | null
@ -31,7 +31,7 @@
if (ev.detail.presenter !== undefined && Array.isArray(value)) { if (ev.detail.presenter !== undefined && Array.isArray(value)) {
const channel = value[0] const channel = value[0]
if (channel !== undefined) { if (channel !== undefined) {
showPanel(ev.detail.presenter, channel.attachedTo, channel.attachedToClass, 'float') showPopup(ev.detail.presenter, { _id: channel.attachedTo, _class: channel.attachedToClass }, 'float')
} }
} }
} }

View File

@ -18,6 +18,7 @@
"Reply": "Reply", "Reply": "Reply",
"Subject": "Subject", "Subject": "Subject",
"Send": "Send", "Send": "Send",
"NewMessage": "New message",
"NewMessageTo": "New message to", "NewMessageTo": "New message to",
"Cancel": "Cancel", "Cancel": "Cancel",
"SubjectPlaceholder": "Message subject", "SubjectPlaceholder": "Message subject",

View File

@ -18,7 +18,8 @@
"Reply": "Ответить", "Reply": "Ответить",
"Subject": "Тема", "Subject": "Тема",
"Send": "Отправить", "Send": "Отправить",
"NewMessageTo": "Новое сообщение to", "NewMessage": "Новое сообщение",
"NewMessageTo": "Новое сообщение для",
"Cancel": "Отменить", "Cancel": "Отменить",
"SubjectPlaceholder": "Тема сообщения", "SubjectPlaceholder": "Тема сообщения",
"CopyPlaceholder": "Копия", "CopyPlaceholder": "Копия",

View File

@ -5,7 +5,7 @@
"toolPackages": [ "toolPackages": [
{ {
"packageName": "@microsoft/api-extractor", "packageName": "@microsoft/api-extractor",
"packageVersion": "7.19.2" "packageVersion": "7.23.0"
} }
] ]
} }

View File

@ -19,12 +19,13 @@
import gmail from '../plugin' import gmail from '../plugin'
import { Channel, Contact, EmployeeAccount, formatName } from '@anticrm/contact' import { Channel, Contact, EmployeeAccount, formatName } from '@anticrm/contact'
import contact from '@anticrm/contact' import contact from '@anticrm/contact'
import { ActionIcon, IconShare, Button, ScrollBox, showPopup, Icon, Label, eventToHTMLElement } from '@anticrm/ui' import plugin, { IconShare, Button, Tooltip, showPopup, Icon, Label, eventToHTMLElement, Scroller } from '@anticrm/ui'
import { getCurrentAccount, Ref, SortingOrder, Space } from '@anticrm/core' import { getCurrentAccount, Ref, SortingOrder, Space } from '@anticrm/core'
import setting from '@anticrm/setting' import setting from '@anticrm/setting'
import Connect from './Connect.svelte' import Connect from './Connect.svelte'
import Messages from './Messages.svelte' import Messages from './Messages.svelte'
import { NotificationClientImpl } from '@anticrm/notification-resources' import { NotificationClientImpl } from '@anticrm/notification-resources'
import IconInbox from './icons/Inbox.svelte'
export let object: Contact export let object: Contact
export let channel: Channel export let channel: Channel
@ -121,56 +122,43 @@
} }
</script> </script>
<div class="flex-between header"> <div class="popupPanel-body__main-header bottom-divider">
{#if selectable} {#if selectable}
<div class="flex-between w-full"> <div class="flex-between w-full">
<span>{selected.size} <Label label={gmail.string.MessagesSelected} /></span> <span><b>{selected.size}</b> <Label label={gmail.string.MessagesSelected} /></span>
<div class="flex"> <div class="flex">
<div> <div>
<Button label={gmail.string.Cancel} size={'small'} on:click={clear} /> <Button label={gmail.string.Cancel} on:click={clear} />
</div> </div>
<div class="ml-3"> <div class="ml-3">
<Button <Button label={gmail.string.PublishSelected} kind={'primary'} disabled={!selected.size} on:click={share} />
label={gmail.string.PublishSelected}
size={'small'}
kind={'primary'}
disabled={!selected.size}
on:click={share}
/>
</div> </div>
</div> </div>
</div> </div>
{:else if enabled} {:else if enabled}
<div class="flex-center icon"><Icon icon={contact.icon.Email} size="small" /></div> <div class="flex-between">
<div class="flex-grow flex-col">
<div class="fs-title">Gmail</div>
<div class="text-sm content-dark-color"><Label label={gmail.string.YouAnd} /> {formatName(object.name)}</div>
</div>
<div class="mr-3">
<Button <Button
label={gmail.string.CreateMessage} label={gmail.string.CreateMessage}
size={'small'}
kind={'primary'} kind={'primary'}
on:click={() => { on:click={() => {
newMessage = true newMessage = true
}} }}
/> />
</div> <Tooltip label={gmail.string.ShareMessages}>
<ActionIcon <Button
icon={IconShare} icon={IconShare}
size={'medium'} kind={'transparent'}
label={gmail.string.ShareMessages} on:click={async () => {
direction={'bottom'}
action={async () => {
selectable = !selectable selectable = !selectable
}} }}
/> />
</Tooltip>
</div>
{:else} {:else}
<div class="flex-center"> <div class="flex-center flex-grow">
<Button <Button
label={gmail.string.Connect} label={gmail.string.Connect}
kind={'primary'} kind={'primary'}
size={'small'}
on:click={(e) => { on:click={(e) => {
showPopup(Connect, {}, eventToHTMLElement(e)) showPopup(Connect, {}, eventToHTMLElement(e))
}} }}
@ -178,35 +166,16 @@
</div> </div>
{/if} {/if}
</div> </div>
<div class="h-full right-content"> <Scroller>
<ScrollBox vertical stretch> <div class="popupPanel-body__main-content py-4 clear-mins flex-no-shrink">
{#if messages} {#if messages && messages.length > 0}
<Messages messages={convertMessages(messages, accounts)} {selectable} bind:selected on:select /> <Messages messages={convertMessages(messages, accounts)} {selectable} bind:selected on:select />
<div class="clear-mins h-4 flex-no-shrink" />
{:else}
<div class="flex-col-center justify-center h-full">
<Icon icon={IconInbox} size={'full'} />
<div class="mt-4 fs-bold dark-color"><Label label={plugin.string.Incoming} /></div>
</div>
{/if} {/if}
</ScrollBox> </div>
</div> </Scroller>
<style lang="scss">
.header {
flex-shrink: 0;
padding: 0 6rem 0 2.5rem;
height: 4rem;
color: var(--theme-content-accent-color);
border-bottom: 1px solid var(--theme-zone-bg);
.icon {
flex-shrink: 0;
margin-right: 1rem;
width: 2.25rem;
height: 2.25rem;
color: var(--theme-caption-color);
background-color: var(--primary-button-enabled);
border-radius: 50%;
}
}
.right-content {
flex-grow: 1;
padding: 1.5rem 0;
}
</style>

View File

@ -17,7 +17,7 @@
import { SharedMessage } from '@anticrm/gmail' import { SharedMessage } from '@anticrm/gmail'
import Button from '@anticrm/ui/src/components/Button.svelte' import Button from '@anticrm/ui/src/components/Button.svelte'
import { createEventDispatcher } from 'svelte' import { createEventDispatcher } from 'svelte'
import { IconArrowLeft, Label } from '@anticrm/ui' import { IconArrowLeft, Label, Scroller } from '@anticrm/ui'
import gmail from '../plugin' import gmail from '../plugin'
import FullMessageContent from './FullMessageContent.svelte' import FullMessageContent from './FullMessageContent.svelte'
import { createQuery } from '@anticrm/presentation' import { createQuery } from '@anticrm/presentation'
@ -48,23 +48,25 @@
$: user = currentMessage.incoming ? currentMessage.receiver : currentMessage.sender $: user = currentMessage.incoming ? currentMessage.receiver : currentMessage.sender
</script> </script>
<div class="flex-between clear-mins header"> <div class="popupPanel-body__main-header bottom-divider">
<div <div class="flex-between">
class="flex-center icon" <div class="buttons-group">
<Button
icon={IconArrowLeft}
kind={'transparent'}
on:click={() => { on:click={() => {
dispatch('close') dispatch('close')
}} }}
> />
<IconArrowLeft size="medium" /> <div class="flex-grow flex-col">
</div> <span>{currentMessage.subject}</span>
<div class="flex-grow flex-col mr-4 min-w-0"> <span class="content-accent-color">
<div class="fs-title overflow-label">{currentMessage.subject}</div>
<div class="text-sm content-dark-color overflow-label">
<Label label={currentMessage.incoming ? gmail.string.From : gmail.string.To} /> <Label label={currentMessage.incoming ? gmail.string.From : gmail.string.To} />
{title} <b>{title}</b>
</span>
</div> </div>
</div> </div>
<div class="mr-3"> <div class="buttons-group small-gap">
<Button <Button
label={gmail.string.Reply} label={gmail.string.Reply}
size={'small'} size={'small'}
@ -74,8 +76,10 @@
}} }}
/> />
</div> </div>
</div>
</div> </div>
<div class="flex-col clear-mins content"> <Scroller>
<div class="popupPanel-body__main-content py-4">
<Label label={currentMessage.incoming ? gmail.string.To : gmail.string.From} /> <Label label={currentMessage.incoming ? gmail.string.To : gmail.string.From} />
{user} {user}
{#if currentMessage.copy?.length} {#if currentMessage.copy?.length}
@ -90,47 +94,30 @@
{/each} {/each}
</div> </div>
{/if} {/if}
<div class="flex-col h-full clear-mins mt-4"> <div class="flex-col content clear-mins">
<FullMessageContent content={currentMessage.content} /> <FullMessageContent content={currentMessage.content} />
</div> </div>
</div> </div>
</Scroller>
<style lang="scss"> <style lang="scss">
.header {
flex-shrink: 0;
padding: 0 6rem 0 2.5rem;
height: 4rem;
color: var(--theme-content-accent-color);
border-bottom: 1px solid var(--theme-zone-bg);
.icon {
flex-shrink: 0;
margin-right: 1rem;
width: 2.25rem;
height: 2.25rem;
color: var(--theme-caption-color);
border-radius: 50%;
cursor: pointer;
}
}
.content {
flex-grow: 1;
padding: 1.5rem 2.5rem;
.list { .list {
padding: 1rem; padding: 0.5rem;
color: var(--theme-caption-color); color: var(--theme-caption-color);
overflow-x: auto; overflow-x: auto;
overflow-y: hidden; overflow-y: hidden;
background-color: var(--theme-bg-accent-color); background-color: var(--accent-bg-color);
border: 1px solid var(--theme-bg-accent-color); border: 1px solid var(--divider-color);
border-radius: 0.75rem; border-radius: 0.25rem;
.item + .item { .item + .item {
padding-left: 1rem; padding-left: 1rem;
border-left: 1px solid var(--theme-bg-accent-color); border-left: 1px solid var(--divider-color);
} }
} }
.content {
margin-top: 1rem;
background-color: var(--incoming-msg);
border-radius: 0.25rem;
} }
</style> </style>

View File

@ -22,11 +22,13 @@
<style lang="scss"> <style lang="scss">
iframe { iframe {
overflow: auto; overflow: auto;
border: none;
border-radius: 0.5rem;
height: 100%; height: 100%;
background-color: #fff; font-family: var(--font-family);
color: #1f212b; font-weight: 400;
font-size: 0.875rem;
background-color: #ffffffc0;
border: none;
border-radius: 0.25rem;
:global(a) { :global(a) {
font: inherit; font: inherit;

View File

@ -15,15 +15,16 @@
--> -->
<script lang="ts"> <script lang="ts">
import { Ref, Doc, Class } from '@anticrm/core' import { Ref, Doc, Class } from '@anticrm/core'
import contact, { Channel } from '@anticrm/contact' import contact, { Channel, formatName } from '@anticrm/contact'
import { SharedMessage } from '@anticrm/gmail' import { SharedMessage } from '@anticrm/gmail'
import NewMessage from './NewMessage.svelte' import NewMessage from './NewMessage.svelte'
import FullMessage from './FullMessage.svelte' import FullMessage from './FullMessage.svelte'
import Chats from './Chats.svelte' import Chats from './Chats.svelte'
import { createQuery, getClient } from '@anticrm/presentation' import { createQuery, getClient } from '@anticrm/presentation'
import { NotificationClientImpl } from '@anticrm/notification-resources' import { NotificationClientImpl } from '@anticrm/notification-resources'
import { Panel } from '@anticrm/panel' import { Panel, Icon, Label } from '@anticrm/ui'
import { createEventDispatcher } from 'svelte' import { createEventDispatcher } from 'svelte'
import gmail from '../plugin'
export let _id: Ref<Doc> export let _id: Ref<Doc>
export let _class: Ref<Class<Doc>> export let _class: Ref<Class<Doc>>
@ -71,16 +72,24 @@
{#if channel && object} {#if channel && object}
<Panel <Panel
icon={contact.icon.Email} isHeader={true}
title={'Email'}
withoutActivity
{object}
isHeader={false}
isAside={false} isAside={false}
on:close={() => { on:close={() => {
dispatch('close') dispatch('close')
}} }}
> >
<svelte:fragment slot="title">
<div class="antiTitle icon-wrapper">
<div class="wrapped-icon"><Icon icon={contact.icon.Email} size={'medium'} /></div>
<div class="title-wrapper">
<span class="wrapped-title">Email</span>
<span class="wrapped-subtitle">
<Label label={gmail.string.YouAnd} />
<b>{formatName(object.name)}</b>
</span>
</div>
</div>
</svelte:fragment>
{#if newMessage} {#if newMessage}
<NewMessage {object} {channel} {currentMessage} on:close={back} /> <NewMessage {object} {channel} {currentMessage} on:close={back} />
{:else if currentMessage} {:else if currentMessage}

View File

@ -34,21 +34,20 @@
dispatch('select', message) dispatch('select', message)
}} }}
> >
{#if selectable}
<div class="mr-4"><CheckBox circle primary bind:checked={selected} /></div>
{/if}
<div class="flex-col message" class:selected> <div class="flex-col message" class:selected>
<div class="flex-between text-sm mb-1"> <div class="flex-between text-sm mb-1">
<div class="content-trans-color overflow-label mr-4"> <div class="dark-color overflow-label mr-4">
<Label label={gmail.string.From} /><span class="content-accent-color">{message.sender}</span> <Label label={gmail.string.From} />
<span class="content-accent-color">{message.sender}</span>
</div> </div>
<div class="content-trans-color flex"> <div class="dark-color flex">
<AttachmentsPresenter value={message} /> <AttachmentsPresenter value={message} />
{getTime(message.sendOn)} <span class="content-accent-color">{getTime(message.sendOn)}</span>
</div> </div>
</div> </div>
<div class="content-trans-color text-sm overflow-label mr-4 mb-4"> <div class="dark-color text-sm overflow-label mr-4 mb-4">
<Label label={gmail.string.To} /><span class="content-accent-color">{message.receiver}</span> <Label label={gmail.string.To} />
<span class="content-accent-color">{message.receiver}</span>
</div> </div>
<div class="fs-title overflow-label mb-1"> <div class="fs-title overflow-label mb-1">
{message.subject} {message.subject}
@ -57,18 +56,22 @@
{message.textContent} {message.textContent}
</div> </div>
</div> </div>
{#if selectable}
<div class="ml-4"><CheckBox circle primary bind:checked={selected} /></div>
{/if}
</div> </div>
<style lang="scss"> <style lang="scss">
.message-conatiner { .message-conatiner {
margin: 0 1.5rem; flex-shrink: 0;
margin: 0.25rem 0;
cursor: pointer; cursor: pointer;
} }
.message { .message {
padding: 1rem; padding: 1rem;
min-width: 0; min-width: 0;
background-color: var(--theme-incoming-msg); background-color: var(--incoming-msg);
border-radius: 0.75rem; border-radius: 0.75rem;
white-space: nowrap; white-space: nowrap;
flex-grow: 1; flex-grow: 1;

View File

@ -15,7 +15,6 @@
--> -->
<script lang="ts"> <script lang="ts">
import type { SharedMessage } from '@anticrm/gmail' import type { SharedMessage } from '@anticrm/gmail'
import { Grid } from '@anticrm/ui'
import MessageView from './Message.svelte' import MessageView from './Message.svelte'
import { Ref } from '@anticrm/core' import { Ref } from '@anticrm/core'
import { createEventDispatcher } from 'svelte' import { createEventDispatcher } from 'svelte'
@ -40,8 +39,7 @@
} }
</script> </script>
<Grid column={1} rowGap={0.5}> {#if messages}
{#if messages}
{#each messages as message (message._id)} {#each messages as message (message._id)}
<MessageView <MessageView
{message} {message}
@ -52,5 +50,4 @@
}} }}
/> />
{/each} {/each}
{/if} {/if}
</Grid>

View File

@ -22,7 +22,7 @@
import { getResource, setPlatformStatus, unknownError } from '@anticrm/platform' import { getResource, setPlatformStatus, unknownError } from '@anticrm/platform'
import { createQuery, getClient } from '@anticrm/presentation' import { createQuery, getClient } from '@anticrm/presentation'
import { TextEditor } from '@anticrm/text-editor' import { TextEditor } from '@anticrm/text-editor'
import { ActionIcon, IconArrowLeft, IconAttachment, IconClose, Label } from '@anticrm/ui' import { Scroller, IconArrowLeft, IconAttachment, Label } from '@anticrm/ui'
import Button from '@anticrm/ui/src/components/Button.svelte' import Button from '@anticrm/ui/src/components/Button.svelte'
import EditBox from '@anticrm/ui/src/components/EditBox.svelte' import EditBox from '@anticrm/ui/src/components/EditBox.svelte'
import { createEventDispatcher } from 'svelte' import { createEventDispatcher } from 'svelte'
@ -147,41 +147,40 @@
style="display: none" style="display: none"
on:change={fileSelected} on:change={fileSelected}
/> />
<div class="flex-between clear-mins header"> <div class="popupPanel-body__main-header bottom-divider">
<div <div class="flex-between">
class="flex-center icon" <div class="buttons-group">
<Button
icon={IconArrowLeft}
kind={'transparent'}
on:click={() => { on:click={() => {
dispatch('close') dispatch('close')
}} }}
> />
<IconArrowLeft size="medium" />
</div>
<div class="flex-grow flex-col"> <div class="flex-grow flex-col">
<div class="fs-title">Gmail</div> <Label label={plugin.string.NewMessage} />
<div class="text-sm content-dark-color overflow-label"> <span class="content-accent-color"><b>{formatName(object.name)} ({channel.value})</b></span>
<Label label={plugin.string.NewMessageTo} />
<span class="content-accent-color">{formatName(object.name)} ({channel.value})</span>
</div> </div>
</div> </div>
<div class="mr-3 flex-row-center"> <div class="buttons-group small-gap">
<div class="mr-2"> <Button
<ActionIcon
icon={IconAttachment} icon={IconAttachment}
size={'small'} kind={'transparent'}
action={() => { on:click={() => {
inputFile.click() inputFile.click()
}} }}
/> />
<Button label={plugin.string.Send} kind={'primary'} on:click={sendMsg} />
</div> </div>
<Button label={plugin.string.Send} size={'small'} kind={'primary'} on:click={sendMsg} />
</div> </div>
</div> </div>
<div <Scroller>
class="flex-col clear-mins right-content" <div
class="popupPanel-body__main-content py-4"
on:dragover|preventDefault={() => {}} on:dragover|preventDefault={() => {}}
on:dragleave={() => {}} on:dragleave={() => {}}
on:drop|preventDefault|stopPropagation={fileDrop} on:drop|preventDefault|stopPropagation={fileDrop}
> >
<div class="mb-2"> <div class="mb-2">
<EditBox <EditBox
label={plugin.string.Subject} label={plugin.string.Subject}
@ -199,90 +198,56 @@
/> />
</div> </div>
{#if attachments.length} {#if attachments.length}
<div class="flex-row-center list mt-2"> <div class="flex-row-center list mt-2 scroll-divider-color">
{#each attachments as attachment} {#each attachments as attachment}
<div class="item flex"> <div class="item flex-row-center flex-no-shrink">
<AttachmentPresenter value={attachment} /> <AttachmentPresenter
<div class="remove"> value={attachment}
<ActionIcon removable
icon={IconClose} on:remove={(result) => {
action={() => { if (result !== undefined) removeAttachment(attachment)
removeAttachment(attachment)
}} }}
size="small"
/> />
</div> </div>
</div>
{/each} {/each}
</div> </div>
{/if} {/if}
<div class="input mt-4 clear-mins"> <div class="input mt-4 clear-mins">
<TextEditor bind:this={editor} bind:content={obj.content} on:blur={editor.submit} /> <TextEditor bind:this={editor} bind:content={obj.content} on:blur={editor.submit} />
</div> </div>
</div> </div>
</Scroller>
<style lang="scss"> <style lang="scss">
.header {
flex-shrink: 0;
padding: 0 6rem 0 2.5rem;
height: 4rem;
color: var(--theme-content-accent-color);
border-bottom: 1px solid var(--theme-zone-bg);
.icon {
flex-shrink: 0;
margin-right: 1rem;
width: 2.25rem;
height: 2.25rem;
color: var(--theme-caption-color);
border-radius: 50%;
cursor: pointer;
}
}
.right-content {
flex-grow: 1;
padding: 1.5rem 2.5rem;
.list { .list {
padding: 1rem; padding: 0.5rem;
color: var(--theme-caption-color); color: var(--theme-caption-color);
overflow-x: auto; overflow-x: auto;
overflow-y: hidden; overflow-y: hidden;
background-color: var(--theme-bg-accent-color); background-color: var(--accent-bg-color);
border: 1px solid var(--theme-bg-accent-color); border: 1px solid var(--divider-color);
border-radius: 0.75rem; border-radius: 0.25rem;
.item + .item { .item + .item {
padding-left: 1rem; padding-left: 1rem;
border-left: 1px solid var(--theme-bg-accent-color); border-left: 1px solid var(--divider-color);
}
.item {
.remove {
visibility: hidden;
}
}
.item:hover {
.remove {
visibility: visible;
}
} }
} }
.input { .input {
overflow: auto; overflow: auto;
padding: 1rem; padding: 1rem;
background-color: #fff; background-color: var(--outcoming-msg);
color: #1f212b; color: #d6d6d6;
height: 100%; caret-color: var(--caret-color);
border-radius: 0.5rem; min-height: 0;
height: calc(100% - 12rem);
border-radius: 0.25rem;
:global(.ProseMirror) { :global(.ProseMirror) {
min-height: 0; min-height: 0;
max-height: 100%; max-height: 100%;
height: 100%; height: auto;
} }
:global(a) { :global(a) {
@ -304,5 +269,4 @@
} }
} }
} }
}
</style> </style>

View File

@ -35,7 +35,7 @@
<style lang="scss"> <style lang="scss">
.container { .container {
padding: 1.25rem 0 1.5rem; padding: 1rem;
background-color: var(--theme-bg-accent-color); background-color: var(--theme-bg-accent-color);
border: 1px solid var(--theme-bg-accent-color); border: 1px solid var(--theme-bg-accent-color);
border-radius: 0.75rem; border-radius: 0.75rem;

View File

@ -0,0 +1,637 @@
<!--
// Copyright © 2020, 2021 Anticrm Platform Contributors.
// Copyright © 2021 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.
-->
<svg viewBox="0 0 102 98" width="102" height="98" fill="none">
<mask id="A" fill="#fff">
<path
d="M6 67h1v1H6v-1zm2 0h1v1H8v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zM3 65h1v1H3v-1zm2 0h1v1H5v-1zm2 0h1v1H7v-1zm2 0h1v1H9v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm50 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zM5 69h1v1H5v-1zm2 0h1v1H7v-1zm2 0h1v1H9v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zM6 71h1v1H6v-1zm2 0h1v1H8v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zM7 73h1v1H7v-1zm2 0h1v1H9v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zM8 75h1v1H8v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zM9 77h1v1H9v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm22 0h1v1h-1v-1zm-25-2h1v1h-1v-1zm27 6h1v1h-1v-1zm2 0h1v1h-1v-1zm-1 2h1v1h-1v-1zm-24 4h1v1h-1v-1zM8 79h1v1H8v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1z"
/>
<path
d="M9 81h1v1H9v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm-25 2h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm-25 2h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm-25 2h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm32 0h1v1h-1v-1zm-28 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm1 2h1v1h-1v-1zm-28 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm5 2h1v1h-1v-1zm-28 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm3 2h1v1h-1v-1zm-28 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm-58-4h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1z"
/>
<path
d="M37 89h1v1h-1v-1zm2 0h1v1h-1v-1zm-23 2h1v1h-1v-1zm-2 0h1v1h-1v-1zm-2 0h1v1h-1v-1zm-1-2h1v1h-1v-1zm9 6h1v1h-1v-1zm-5-2h1v1h-1v-1zM7 77h1v1H7v-1zM4 67h1v1H4v-1zm14 24h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm-25 2h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm-21 2h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm2 0h1v1h-1v-1zm45-28h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm23 2h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm25 2h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm25 2h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm25 2h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm25 2h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1z"
/>
<path
d="M80 77h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-1 2h-1v1h1v-1zm-2 0h-1v1h1v-1zm1-2h-1v1h1v-1zm-2 0h-1v1h1v-1zm3-2h-1v1h1v-1zm1-2h-1v1h1v-1zm1-2h-1v1h1v-1zm4-4h-1v1h1v-1zm20 12h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm25 2h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm25 2h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm25 2h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm25 2h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm25 2h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm23 2h-1v1h1v-1zm2 0h-1v1h1v-1zm-3 2h-1v1h1v-1zm2 0h-1v1h1v-1zm-5 2h-1v1h1v-1zm2-4h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm23 2h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1z"
/>
<path
d="M60 93h-1v1h1v-1zm21 2h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm8-16h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm25 2h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm27 2h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm27 2h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1zm-2 0h-1v1h1v-1z"
/>
</mask>
<g mask="url(#A)" stroke="url(#B)" stroke-width="2">
<path d="M6 67h1v1H6v-1z" />
<path d="M8 67h1v1H8v-1z" />
<path d="M10 67h1v1h-1v-1z" />
<path d="M12 67h1v1h-1v-1z" />
<path d="M14 67h1v1h-1v-1z" />
<path d="M16 67h1v1h-1v-1z" />
<path d="M18 67h1v1h-1v-1z" />
<path d="M20 67h1v1h-1v-1z" />
<path d="M22 67h1v1h-1v-1z" />
<path d="M24 67h1v1h-1v-1z" />
<path d="M26 67h1v1h-1v-1z" />
<path d="M28 67h1v1h-1v-1z" />
<path d="M3 65h1v1H3v-1z" />
<path d="M5 65h1v1H5v-1z" />
<path d="M7 65h1v1H7v-1z" />
<path d="M9 65h1v1H9v-1z" />
<path d="M11 65h1v1h-1v-1z" />
<path d="M13 65h1v1h-1v-1z" />
<path d="M15 65h1v1h-1v-1z" />
<path d="M17 65h1v1h-1v-1z" />
<path d="M19 65h1v1h-1v-1z" />
<path d="M21 65h1v1h-1v-1z" />
<path d="M23 65h1v1h-1v-1z" />
<path d="M25 65h1v1h-1v-1z" />
<path d="M75 65h1v1h-1v-1z" />
<path d="M77 65h1v1h-1v-1z" />
<path d="M79 65h1v1h-1v-1z" />
<path d="M81 65h1v1h-1v-1z" />
<path d="M83 65h1v1h-1v-1z" />
<path d="M85 65h1v1h-1v-1z" />
<path d="M87 65h1v1h-1v-1z" />
<path d="M89 65h1v1h-1v-1z" />
<path d="M91 65h1v1h-1v-1z" />
<path d="M93 65h1v1h-1v-1z" />
<path d="M95 65h1v1h-1v-1z" />
<path d="M97 65h1v1h-1v-1z" />
<path d="M5 69h1v1H5v-1z" />
<path d="M7 69h1v1H7v-1z" />
<path d="M9 69h1v1H9v-1z" />
<path d="M11 69h1v1h-1v-1z" />
<path d="M13 69h1v1h-1v-1z" />
<path d="M15 69h1v1h-1v-1z" />
<path d="M17 69h1v1h-1v-1z" />
<path d="M19 69h1v1h-1v-1z" />
<path d="M21 69h1v1h-1v-1z" />
<path d="M23 69h1v1h-1v-1z" />
<path d="M25 69h1v1h-1v-1z" />
<path d="M27 69h1v1h-1v-1z" />
<path d="M29 69h1v1h-1v-1z" />
<path d="M31 69h1v1h-1v-1z" />
<path d="M6 71h1v1H6v-1z" />
<path d="M8 71h1v1H8v-1z" />
<path d="M10 71h1v1h-1v-1z" />
<path d="M12 71h1v1h-1v-1z" />
<path d="M14 71h1v1h-1v-1z" />
<path d="M16 71h1v1h-1v-1z" />
<path d="M18 71h1v1h-1v-1z" />
<path d="M20 71h1v1h-1v-1z" />
<path d="M22 71h1v1h-1v-1z" />
<path d="M24 71h1v1h-1v-1z" />
<path d="M26 71h1v1h-1v-1z" />
<path d="M28 71h1v1h-1v-1z" />
<path d="M30 71h1v1h-1v-1z" />
<path d="M32 71h1v1h-1v-1z" />
<path d="M7 73h1v1H7v-1z" />
<path d="M9 73h1v1H9v-1z" />
<path d="M11 73h1v1h-1v-1z" />
<path d="M13 73h1v1h-1v-1z" />
<path d="M15 73h1v1h-1v-1z" />
<path d="M17 73h1v1h-1v-1z" />
<path d="M19 73h1v1h-1v-1z" />
<path d="M21 73h1v1h-1v-1z" />
<path d="M23 73h1v1h-1v-1z" />
<path d="M25 73h1v1h-1v-1z" />
<path d="M27 73h1v1h-1v-1z" />
<path d="M29 73h1v1h-1v-1z" />
<path d="M31 73h1v1h-1v-1z" />
<path d="M33 73h1v1h-1v-1z" />
<path d="M8 75h1v1H8v-1z" />
<path d="M10 75h1v1h-1v-1z" />
<path d="M12 75h1v1h-1v-1z" />
<path d="M14 75h1v1h-1v-1z" />
<path d="M16 75h1v1h-1v-1z" />
<path d="M18 75h1v1h-1v-1z" />
<path d="M20 75h1v1h-1v-1z" />
<path d="M22 75h1v1h-1v-1z" />
<path d="M24 75h1v1h-1v-1z" />
<path d="M26 75h1v1h-1v-1z" />
<path d="M28 75h1v1h-1v-1z" />
<path d="M30 75h1v1h-1v-1z" />
<path d="M32 75h1v1h-1v-1z" />
<path d="M34 75h1v1h-1v-1z" />
<path d="M9 77h1v1H9v-1z" />
<path d="M11 77h1v1h-1v-1z" />
<path d="M13 77h1v1h-1v-1z" />
<path d="M15 77h1v1h-1v-1z" />
<path d="M17 77h1v1h-1v-1z" />
<path d="M19 77h1v1h-1v-1z" />
<path d="M21 77h1v1h-1v-1z" />
<path d="M23 77h1v1h-1v-1z" />
<path d="M25 77h1v1h-1v-1z" />
<path d="M27 77h1v1h-1v-1z" />
<path d="M29 77h1v1h-1v-1z" />
<path d="M31 77h1v1h-1v-1z" />
<path d="M33 77h1v1h-1v-1z" />
<path d="M35 77h1v1h-1v-1z" />
<path d="M37 77h1v1h-1v-1z" />
<path d="M39 77h1v1h-1v-1z" />
<path d="M61 77h1v1h-1v-1z" />
<path d="M36 75h1v1h-1v-1z" />
<path d="M63 81h1v1h-1v-1z" />
<path d="M65 81h1v1h-1v-1z" />
<path d="M64 83h1v1h-1v-1z" />
<path d="M40 87h1v1h-1v-1z" />
<path d="M8 79h1v1H8v-1z" />
<path d="M10 79h1v1h-1v-1z" />
<path d="M12 79h1v1h-1v-1z" />
<path d="M14 79h1v1h-1v-1z" />
<path d="M16 79h1v1h-1v-1z" />
<path d="M18 79h1v1h-1v-1z" />
<path d="M20 79h1v1h-1v-1z" />
<path d="M22 79h1v1h-1v-1z" />
<path d="M24 79h1v1h-1v-1z" />
<path d="M26 79h1v1h-1v-1z" />
<path d="M28 79h1v1h-1v-1z" />
<path d="M30 79h1v1h-1v-1z" />
<path d="M32 79h1v1h-1v-1z" />
<path d="M34 79h1v1h-1v-1z" />
<path d="M9 81h1v1H9v-1z" />
<path d="M11 81h1v1h-1v-1z" />
<path d="M13 81h1v1h-1v-1z" />
<path d="M15 81h1v1h-1v-1z" />
<path d="M17 81h1v1h-1v-1z" />
<path d="M19 81h1v1h-1v-1z" />
<path d="M21 81h1v1h-1v-1z" />
<path d="M23 81h1v1h-1v-1z" />
<path d="M25 81h1v1h-1v-1z" />
<path d="M27 81h1v1h-1v-1z" />
<path d="M29 81h1v1h-1v-1z" />
<path d="M31 81h1v1h-1v-1z" />
<path d="M33 81h1v1h-1v-1z" />
<path d="M35 81h1v1h-1v-1z" />
<path d="M10 83h1v1h-1v-1z" />
<path d="M12 83h1v1h-1v-1z" />
<path d="M14 83h1v1h-1v-1z" />
<path d="M16 83h1v1h-1v-1z" />
<path d="M18 83h1v1h-1v-1z" />
<path d="M20 83h1v1h-1v-1z" />
<path d="M22 83h1v1h-1v-1z" />
<path d="M24 83h1v1h-1v-1z" />
<path d="M26 83h1v1h-1v-1z" />
<path d="M28 83h1v1h-1v-1z" />
<path d="M30 83h1v1h-1v-1z" />
<path d="M32 83h1v1h-1v-1z" />
<path d="M34 83h1v1h-1v-1z" />
<path d="M36 83h1v1h-1v-1z" />
<path d="M11 85h1v1h-1v-1z" />
<path d="M13 85h1v1h-1v-1z" />
<path d="M15 85h1v1h-1v-1z" />
<path d="M17 85h1v1h-1v-1z" />
<path d="M19 85h1v1h-1v-1z" />
<path d="M21 85h1v1h-1v-1z" />
<path d="M23 85h1v1h-1v-1z" />
<path d="M25 85h1v1h-1v-1z" />
<path d="M27 85h1v1h-1v-1z" />
<path d="M29 85h1v1h-1v-1z" />
<path d="M31 85h1v1h-1v-1z" />
<path d="M33 85h1v1h-1v-1z" />
<path d="M35 85h1v1h-1v-1z" />
<path d="M37 85h1v1h-1v-1z" />
<path d="M12 87h1v1h-1v-1z" />
<path d="M14 87h1v1h-1v-1z" />
<path d="M16 87h1v1h-1v-1z" />
<path d="M18 87h1v1h-1v-1z" />
<path d="M20 87h1v1h-1v-1z" />
<path d="M22 87h1v1h-1v-1z" />
<path d="M24 87h1v1h-1v-1z" />
<path d="M26 87h1v1h-1v-1z" />
<path d="M28 87h1v1h-1v-1z" />
<path d="M30 87h1v1h-1v-1z" />
<path d="M32 87h1v1h-1v-1z" />
<path d="M34 87h1v1h-1v-1z" />
<path d="M36 87h1v1h-1v-1z" />
<path d="M38 87h1v1h-1v-1z" />
<path d="M70 87h1v1h-1v-1z" />
<path d="M42 87h1v1h-1v-1z" />
<path d="M44 87h1v1h-1v-1z" />
<path d="M46 87h1v1h-1v-1z" />
<path d="M48 87h1v1h-1v-1z" />
<path d="M50 87h1v1h-1v-1z" />
<path d="M52 87h1v1h-1v-1z" />
<path d="M54 87h1v1h-1v-1z" />
<path d="M56 87h1v1h-1v-1z" />
<path d="M58 87h1v1h-1v-1z" />
<path d="M60 87h1v1h-1v-1z" />
<path d="M62 87h1v1h-1v-1z" />
<path d="M64 87h1v1h-1v-1z" />
<path d="M66 87h1v1h-1v-1z" />
<path d="M68 87h1v1h-1v-1z" />
<path d="M69 89h1v1h-1v-1z" />
<path d="M41 89h1v1h-1v-1z" />
<path d="M43 89h1v1h-1v-1z" />
<path d="M45 89h1v1h-1v-1z" />
<path d="M47 89h1v1h-1v-1z" />
<path d="M49 89h1v1h-1v-1z" />
<path d="M51 89h1v1h-1v-1z" />
<path d="M53 89h1v1h-1v-1z" />
<path d="M55 89h1v1h-1v-1z" />
<path d="M57 89h1v1h-1v-1z" />
<path d="M59 89h1v1h-1v-1z" />
<path d="M61 89h1v1h-1v-1z" />
<path d="M63 89h1v1h-1v-1z" />
<path d="M65 89h1v1h-1v-1z" />
<path d="M67 89h1v1h-1v-1z" />
<path d="M72 91h1v1h-1v-1z" />
<path d="M44 91h1v1h-1v-1z" />
<path d="M46 91h1v1h-1v-1z" />
<path d="M48 91h1v1h-1v-1z" />
<path d="M50 91h1v1h-1v-1z" />
<path d="M52 91h1v1h-1v-1z" />
<path d="M54 91h1v1h-1v-1z" />
<path d="M56 91h1v1h-1v-1z" />
<path d="M58 91h1v1h-1v-1z" />
<path d="M60 91h1v1h-1v-1z" />
<path d="M62 91h1v1h-1v-1z" />
<path d="M64 91h1v1h-1v-1z" />
<path d="M66 91h1v1h-1v-1z" />
<path d="M68 91h1v1h-1v-1z" />
<path d="M70 91h1v1h-1v-1z" />
<path d="M73 93h1v1h-1v-1z" />
<path d="M45 93h1v1h-1v-1z" />
<path d="M47 93h1v1h-1v-1z" />
<path d="M49 93h1v1h-1v-1z" />
<path d="M51 93h1v1h-1v-1z" />
<path d="M53 93h1v1h-1v-1z" />
<path d="M55 93h1v1h-1v-1z" />
<path d="M57 93h1v1h-1v-1z" />
<path d="M59 93h1v1h-1v-1z" />
<path d="M61 93h1v1h-1v-1z" />
<path d="M63 93h1v1h-1v-1z" />
<path d="M65 93h1v1h-1v-1z" />
<path d="M67 93h1v1h-1v-1z" />
<path d="M69 93h1v1h-1v-1z" />
<path d="M71 93h1v1h-1v-1z" />
<path d="M13 89h1v1h-1v-1z" />
<path d="M15 89h1v1h-1v-1z" />
<path d="M17 89h1v1h-1v-1z" />
<path d="M19 89h1v1h-1v-1z" />
<path d="M21 89h1v1h-1v-1z" />
<path d="M23 89h1v1h-1v-1z" />
<path d="M25 89h1v1h-1v-1z" />
<path d="M27 89h1v1h-1v-1z" />
<path d="M29 89h1v1h-1v-1z" />
<path d="M31 89h1v1h-1v-1z" />
<path d="M33 89h1v1h-1v-1z" />
<path d="M35 89h1v1h-1v-1z" />
<path d="M37 89h1v1h-1v-1z" />
<path d="M39 89h1v1h-1v-1z" />
<path d="M16 91h1v1h-1v-1z" />
<path d="M14 91h1v1h-1v-1z" />
<path d="M12 91h1v1h-1v-1z" />
<path d="M11 89h1v1h-1v-1z" />
<path d="M20 95h1v1h-1v-1z" />
<path d="M15 93h1v1h-1v-1z" />
<path d="M7 77h1v1H7v-1z" />
<path d="M4 67h1v1H4v-1z" />
<path d="M18 91h1v1h-1v-1z" />
<path d="M20 91h1v1h-1v-1z" />
<path d="M22 91h1v1h-1v-1z" />
<path d="M24 91h1v1h-1v-1z" />
<path d="M26 91h1v1h-1v-1z" />
<path d="M28 91h1v1h-1v-1z" />
<path d="M30 91h1v1h-1v-1z" />
<path d="M32 91h1v1h-1v-1z" />
<path d="M34 91h1v1h-1v-1z" />
<path d="M36 91h1v1h-1v-1z" />
<path d="M38 91h1v1h-1v-1z" />
<path d="M40 91h1v1h-1v-1z" />
<path d="M42 91h1v1h-1v-1z" />
<path d="M17 93h1v1h-1v-1z" />
<path d="M19 93h1v1h-1v-1z" />
<path d="M21 93h1v1h-1v-1z" />
<path d="M23 93h1v1h-1v-1z" />
<path d="M25 93h1v1h-1v-1z" />
<path d="M27 93h1v1h-1v-1z" />
<path d="M29 93h1v1h-1v-1z" />
<path d="M31 93h1v1h-1v-1z" />
<path d="M33 93h1v1h-1v-1z" />
<path d="M35 93h1v1h-1v-1z" />
<path d="M37 93h1v1h-1v-1z" />
<path d="M39 93h1v1h-1v-1z" />
<path d="M41 93h1v1h-1v-1z" />
<path d="M43 93h1v1h-1v-1z" />
<path d="M22 95h1v1h-1v-1z" />
<path d="M24 95h1v1h-1v-1z" />
<path d="M26 95h1v1h-1v-1z" />
<path d="M28 95h1v1h-1v-1z" />
<path d="M30 95h1v1h-1v-1z" />
<path d="M32 95h1v1h-1v-1z" />
<path d="M34 95h1v1h-1v-1z" />
<path d="M36 95h1v1h-1v-1z" />
<path d="M38 95h1v1h-1v-1z" />
<path d="M40 95h1v1h-1v-1z" />
<path d="M42 95h1v1h-1v-1z" />
<path d="M44 95h1v1h-1v-1z" />
<path d="M46 95h1v1h-1v-1z" />
<path d="M48 95h1v1h-1v-1z" />
<path d="M50 95h1v1h-1v-1z" />
<path d="M52 95h1v1h-1v-1z" />
<path d="M97 67h-1v1h1v-1z" />
<path d="M95 67h-1v1h1v-1z" />
<path d="M93 67h-1v1h1v-1z" />
<path d="M91 67h-1v1h1v-1z" />
<path d="M89 67h-1v1h1v-1z" />
<path d="M87 67h-1v1h1v-1z" />
<path d="M85 67h-1v1h1v-1z" />
<path d="M83 67h-1v1h1v-1z" />
<path d="M81 67h-1v1h1v-1z" />
<path d="M79 67h-1v1h1v-1z" />
<path d="M77 67h-1v1h1v-1z" />
<path d="M75 67h-1v1h1v-1z" />
<path d="M98 69h-1v1h1v-1z" />
<path d="M96 69h-1v1h1v-1z" />
<path d="M94 69h-1v1h1v-1z" />
<path d="M92 69h-1v1h1v-1z" />
<path d="M90 69h-1v1h1v-1z" />
<path d="M88 69h-1v1h1v-1z" />
<path d="M86 69h-1v1h1v-1z" />
<path d="M84 69h-1v1h1v-1z" />
<path d="M82 69h-1v1h1v-1z" />
<path d="M80 69h-1v1h1v-1z" />
<path d="M78 69h-1v1h1v-1z" />
<path d="M76 69h-1v1h1v-1z" />
<path d="M74 69h-1v1h1v-1z" />
<path d="M72 69h-1v1h1v-1z" />
<path d="M97 71h-1v1h1v-1z" />
<path d="M95 71h-1v1h1v-1z" />
<path d="M93 71h-1v1h1v-1z" />
<path d="M91 71h-1v1h1v-1z" />
<path d="M89 71h-1v1h1v-1z" />
<path d="M87 71h-1v1h1v-1z" />
<path d="M85 71h-1v1h1v-1z" />
<path d="M83 71h-1v1h1v-1z" />
<path d="M81 71h-1v1h1v-1z" />
<path d="M79 71h-1v1h1v-1z" />
<path d="M77 71h-1v1h1v-1z" />
<path d="M75 71h-1v1h1v-1z" />
<path d="M73 71h-1v1h1v-1z" />
<path d="M71 71h-1v1h1v-1z" />
<path d="M96 73h-1v1h1v-1z" />
<path d="M94 73h-1v1h1v-1z" />
<path d="M92 73h-1v1h1v-1z" />
<path d="M90 73h-1v1h1v-1z" />
<path d="M88 73h-1v1h1v-1z" />
<path d="M86 73h-1v1h1v-1z" />
<path d="M84 73h-1v1h1v-1z" />
<path d="M82 73h-1v1h1v-1z" />
<path d="M80 73h-1v1h1v-1z" />
<path d="M78 73h-1v1h1v-1z" />
<path d="M76 73h-1v1h1v-1z" />
<path d="M74 73h-1v1h1v-1z" />
<path d="M72 73h-1v1h1v-1z" />
<path d="M70 73h-1v1h1v-1z" />
<path d="M95 75h-1v1h1v-1z" />
<path d="M93 75h-1v1h1v-1z" />
<path d="M91 75h-1v1h1v-1z" />
<path d="M89 75h-1v1h1v-1z" />
<path d="M87 75h-1v1h1v-1z" />
<path d="M85 75h-1v1h1v-1z" />
<path d="M83 75h-1v1h1v-1z" />
<path d="M81 75h-1v1h1v-1z" />
<path d="M79 75h-1v1h1v-1z" />
<path d="M77 75h-1v1h1v-1z" />
<path d="M75 75h-1v1h1v-1z" />
<path d="M73 75h-1v1h1v-1z" />
<path d="M71 75h-1v1h1v-1z" />
<path d="M69 75h-1v1h1v-1z" />
<path d="M94 77h-1v1h1v-1z" />
<path d="M92 77h-1v1h1v-1z" />
<path d="M90 77h-1v1h1v-1z" />
<path d="M88 77h-1v1h1v-1z" />
<path d="M86 77h-1v1h1v-1z" />
<path d="M84 77h-1v1h1v-1z" />
<path d="M82 77h-1v1h1v-1z" />
<path d="M80 77h-1v1h1v-1z" />
<path d="M78 77h-1v1h1v-1z" />
<path d="M76 77h-1v1h1v-1z" />
<path d="M74 77h-1v1h1v-1z" />
<path d="M72 77h-1v1h1v-1z" />
<path d="M70 77h-1v1h1v-1z" />
<path d="M68 77h-1v1h1v-1z" />
<path d="M67 79h-1v1h1v-1z" />
<path d="M65 79h-1v1h1v-1z" />
<path d="M66 77h-1v1h1v-1z" />
<path d="M64 77h-1v1h1v-1z" />
<path d="M67 75h-1v1h1v-1z" />
<path d="M68 73h-1v1h1v-1z" />
<path d="M69 71h-1v1h1v-1z" />
<path d="M73 67h-1v1h1v-1z" />
<path d="M93 79h-1v1h1v-1z" />
<path d="M91 79h-1v1h1v-1z" />
<path d="M89 79h-1v1h1v-1z" />
<path d="M87 79h-1v1h1v-1z" />
<path d="M85 79h-1v1h1v-1z" />
<path d="M83 79h-1v1h1v-1z" />
<path d="M81 79h-1v1h1v-1z" />
<path d="M79 79h-1v1h1v-1z" />
<path d="M77 79h-1v1h1v-1z" />
<path d="M75 79h-1v1h1v-1z" />
<path d="M73 79h-1v1h1v-1z" />
<path d="M71 79h-1v1h1v-1z" />
<path d="M69 79h-1v1h1v-1z" />
<path d="M94 81h-1v1h1v-1z" />
<path d="M92 81h-1v1h1v-1z" />
<path d="M90 81h-1v1h1v-1z" />
<path d="M88 81h-1v1h1v-1z" />
<path d="M86 81h-1v1h1v-1z" />
<path d="M84 81h-1v1h1v-1z" />
<path d="M82 81h-1v1h1v-1z" />
<path d="M80 81h-1v1h1v-1z" />
<path d="M78 81h-1v1h1v-1z" />
<path d="M76 81h-1v1h1v-1z" />
<path d="M74 81h-1v1h1v-1z" />
<path d="M72 81h-1v1h1v-1z" />
<path d="M70 81h-1v1h1v-1z" />
<path d="M68 81h-1v1h1v-1z" />
<path d="M93 83h-1v1h1v-1z" />
<path d="M91 83h-1v1h1v-1z" />
<path d="M89 83h-1v1h1v-1z" />
<path d="M87 83h-1v1h1v-1z" />
<path d="M85 83h-1v1h1v-1z" />
<path d="M83 83h-1v1h1v-1z" />
<path d="M81 83h-1v1h1v-1z" />
<path d="M79 83h-1v1h1v-1z" />
<path d="M77 83h-1v1h1v-1z" />
<path d="M75 83h-1v1h1v-1z" />
<path d="M73 83h-1v1h1v-1z" />
<path d="M71 83h-1v1h1v-1z" />
<path d="M69 83h-1v1h1v-1z" />
<path d="M67 83h-1v1h1v-1z" />
<path d="M92 85h-1v1h1v-1z" />
<path d="M90 85h-1v1h1v-1z" />
<path d="M88 85h-1v1h1v-1z" />
<path d="M86 85h-1v1h1v-1z" />
<path d="M84 85h-1v1h1v-1z" />
<path d="M82 85h-1v1h1v-1z" />
<path d="M80 85h-1v1h1v-1z" />
<path d="M78 85h-1v1h1v-1z" />
<path d="M76 85h-1v1h1v-1z" />
<path d="M74 85h-1v1h1v-1z" />
<path d="M72 85h-1v1h1v-1z" />
<path d="M70 85h-1v1h1v-1z" />
<path d="M68 85h-1v1h1v-1z" />
<path d="M66 85h-1v1h1v-1z" />
<path d="M91 87h-1v1h1v-1z" />
<path d="M89 87h-1v1h1v-1z" />
<path d="M87 87h-1v1h1v-1z" />
<path d="M85 87h-1v1h1v-1z" />
<path d="M83 87h-1v1h1v-1z" />
<path d="M81 87h-1v1h1v-1z" />
<path d="M79 87h-1v1h1v-1z" />
<path d="M77 87h-1v1h1v-1z" />
<path d="M75 87h-1v1h1v-1z" />
<path d="M73 87h-1v1h1v-1z" />
<path d="M71 87h-1v1h1v-1z" />
<path d="M69 87h-1v1h1v-1z" />
<path d="M67 87h-1v1h1v-1z" />
<path d="M65 87h-1v1h1v-1z" />
<path d="M90 89h-1v1h1v-1z" />
<path d="M88 89h-1v1h1v-1z" />
<path d="M86 89h-1v1h1v-1z" />
<path d="M84 89h-1v1h1v-1z" />
<path d="M82 89h-1v1h1v-1z" />
<path d="M80 89h-1v1h1v-1z" />
<path d="M78 89h-1v1h1v-1z" />
<path d="M76 89h-1v1h1v-1z" />
<path d="M74 89h-1v1h1v-1z" />
<path d="M72 89h-1v1h1v-1z" />
<path d="M70 89h-1v1h1v-1z" />
<path d="M68 89h-1v1h1v-1z" />
<path d="M66 89h-1v1h1v-1z" />
<path d="M64 89h-1v1h1v-1z" />
<path d="M87 91h-1v1h1v-1z" />
<path d="M89 91h-1v1h1v-1z" />
<path d="M86 93h-1v1h1v-1z" />
<path d="M88 93h-1v1h1v-1z" />
<path d="M83 95h-1v1h1v-1z" />
<path d="M85 91h-1v1h1v-1z" />
<path d="M83 91h-1v1h1v-1z" />
<path d="M81 91h-1v1h1v-1z" />
<path d="M79 91h-1v1h1v-1z" />
<path d="M77 91h-1v1h1v-1z" />
<path d="M75 91h-1v1h1v-1z" />
<path d="M73 91h-1v1h1v-1z" />
<path d="M71 91h-1v1h1v-1z" />
<path d="M69 91h-1v1h1v-1z" />
<path d="M67 91h-1v1h1v-1z" />
<path d="M65 91h-1v1h1v-1z" />
<path d="M63 91h-1v1h1v-1z" />
<path d="M61 91h-1v1h1v-1z" />
<path d="M84 93h-1v1h1v-1z" />
<path d="M82 93h-1v1h1v-1z" />
<path d="M80 93h-1v1h1v-1z" />
<path d="M78 93h-1v1h1v-1z" />
<path d="M76 93h-1v1h1v-1z" />
<path d="M74 93h-1v1h1v-1z" />
<path d="M72 93h-1v1h1v-1z" />
<path d="M70 93h-1v1h1v-1z" />
<path d="M68 93h-1v1h1v-1z" />
<path d="M66 93h-1v1h1v-1z" />
<path d="M64 93h-1v1h1v-1z" />
<path d="M62 93h-1v1h1v-1z" />
<path d="M60 93h-1v1h1v-1z" />
<path d="M81 95h-1v1h1v-1z" />
<path d="M79 95h-1v1h1v-1z" />
<path d="M77 95h-1v1h1v-1z" />
<path d="M75 95h-1v1h1v-1z" />
<path d="M73 95h-1v1h1v-1z" />
<path d="M71 95h-1v1h1v-1z" />
<path d="M69 95h-1v1h1v-1z" />
<path d="M67 95h-1v1h1v-1z" />
<path d="M65 95h-1v1h1v-1z" />
<path d="M63 95h-1v1h1v-1z" />
<path d="M61 95h-1v1h1v-1z" />
<path d="M59 95h-1v1h1v-1z" />
<path d="M57 95h-1v1h1v-1z" />
<path d="M55 95h-1v1h1v-1z" />
<path d="M63 79h-1v1h1v-1z" />
<path d="M61 79h-1v1h1v-1z" />
<path d="M59 79h-1v1h1v-1z" />
<path d="M57 79h-1v1h1v-1z" />
<path d="M55 79h-1v1h1v-1z" />
<path d="M53 79h-1v1h1v-1z" />
<path d="M51 79h-1v1h1v-1z" />
<path d="M49 79h-1v1h1v-1z" />
<path d="M47 79h-1v1h1v-1z" />
<path d="M45 79h-1v1h1v-1z" />
<path d="M43 79h-1v1h1v-1z" />
<path d="M41 79h-1v1h1v-1z" />
<path d="M39 79h-1v1h1v-1z" />
<path d="M37 79h-1v1h1v-1z" />
<path d="M62 81h-1v1h1v-1z" />
<path d="M60 81h-1v1h1v-1z" />
<path d="M58 81h-1v1h1v-1z" />
<path d="M56 81h-1v1h1v-1z" />
<path d="M54 81h-1v1h1v-1z" />
<path d="M52 81h-1v1h1v-1z" />
<path d="M50 81h-1v1h1v-1z" />
<path d="M48 81h-1v1h1v-1z" />
<path d="M46 81h-1v1h1v-1z" />
<path d="M44 81h-1v1h1v-1z" />
<path d="M42 81h-1v1h1v-1z" />
<path d="M40 81h-1v1h1v-1z" />
<path d="M38 81h-1v1h1v-1z" />
<path d="M36 81h-1v1h1v-1z" />
<path d="M63 83h-1v1h1v-1z" />
<path d="M61 83h-1v1h1v-1z" />
<path d="M59 83h-1v1h1v-1z" />
<path d="M57 83h-1v1h1v-1z" />
<path d="M55 83h-1v1h1v-1z" />
<path d="M53 83h-1v1h1v-1z" />
<path d="M51 83h-1v1h1v-1z" />
<path d="M49 83h-1v1h1v-1z" />
<path d="M47 83h-1v1h1v-1z" />
<path d="M45 83h-1v1h1v-1z" />
<path d="M43 83h-1v1h1v-1z" />
<path d="M41 83h-1v1h1v-1z" />
<path d="M39 83h-1v1h1v-1z" />
<path d="M37 83h-1v1h1v-1z" />
<path d="M64 85h-1v1h1v-1z" />
<path d="M62 85h-1v1h1v-1z" />
<path d="M60 85h-1v1h1v-1z" />
<path d="M58 85h-1v1h1v-1z" />
<path d="M56 85h-1v1h1v-1z" />
<path d="M54 85h-1v1h1v-1z" />
<path d="M52 85h-1v1h1v-1z" />
<path d="M50 85h-1v1h1v-1z" />
<path d="M48 85h-1v1h1v-1z" />
<path d="M46 85h-1v1h1v-1z" />
<path d="M44 85h-1v1h1v-1z" />
<path d="M42 85h-1v1h1v-1z" />
<path d="M40 85h-1v1h1v-1z" />
<path d="M38 85h-1v1h1v-1z" />
</g>
<g fill-rule="evenodd">
<path
d="M28.037 10.25a12.75 12.75 0 0 0-11.898 8.167L1.442 56.574a6.68 6.68 0 0 0-.184 4.32.75.75 0 0 0 .026.328L9.67 88.364a12.75 12.75 0 0 0 12.182 8.986h57.991a12.75 12.75 0 0 0 12.143-8.862l8.729-27.26-.072-.023a6.68 6.68 0 0 0-.084-4.632L85.861 18.417a12.75 12.75 0 0 0-11.898-8.167H28.037zm70.029 54.33c-1.069.733-2.37 1.17-3.807 1.17H78.405a10.46 10.46 0 0 0-9.149 5.391 14.76 14.76 0 0 1-12.912 7.609H45.151a14.21 14.21 0 0 1-12.593-7.625 10.02 10.02 0 0 0-8.878-5.375H7.741c-1.461 0-2.782-.452-3.861-1.207l7.224 23.379a11.25 11.25 0 0 0 10.749 7.929h57.991a11.25 11.25 0 0 0 10.714-7.819l7.51-23.451zM17.539 18.956a11.25 11.25 0 0 1 10.498-7.206h45.926a11.25 11.25 0 0 1 10.498 7.206l14.697 38.157c1.325 3.439-1.214 7.137-4.899 7.137H78.405a11.96 11.96 0 0 0-10.461 6.164 13.26 13.26 0 0 1-11.6 6.836H45.151a12.71 12.71 0 0 1-11.264-6.82 11.52 11.52 0 0 0-10.207-6.18H7.741c-3.685 0-6.224-3.698-4.899-7.137l14.697-38.157z"
fill="#8a8f98"
/>
<path
opacity=".6"
d="M19.909 18.801a8.75 8.75 0 0 1 8.144-5.551h45.894a8.75 8.75 0 0 1 8.144 5.551L97.09 56.987c.966 2.46-.847 5.121-3.49 5.121H76.406a9.54 9.54 0 0 0-8.499 5.209A13.61 13.61 0 0 1 55.78 74.75h-9.006a13.15 13.15 0 0 1-11.848-7.448 9.17 9.17 0 0 0-8.261-5.194H8.402c-2.643 0-4.457-2.661-3.49-5.121L19.91 18.801zm8.144-4.051a7.25 7.25 0 0 0-6.748 4.6L6.308 57.536a2.25 2.25 0 0 0 2.094 3.072h18.264a10.67 10.67 0 0 1 9.613 6.044 11.65 11.65 0 0 0 10.496 6.598h9.006a12.11 12.11 0 0 0 10.79-6.614 11.04 11.04 0 0 1 9.835-6.028h17.193a2.25 2.25 0 0 0 2.094-3.072L80.696 19.35a7.25 7.25 0 0 0-6.748-4.6H28.054z"
fill="#62666D"
/>
</g>
<defs>
<linearGradient id="B" x1="50.5" y1="65" x2="50.5" y2="96" gradientUnits="userSpaceOnUse">
<stop stop-color="#62666D" />
<stop offset="1" stop-color="#3C3F44" stop-opacity="0" />
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 32 KiB

View File

@ -34,6 +34,7 @@ export default mergeIds(gmailId, gmail, {
Reply: '' as IntlString, Reply: '' as IntlString,
Subject: '' as IntlString, Subject: '' as IntlString,
Send: '' as IntlString, Send: '' as IntlString,
NewMessage: '' as IntlString,
NewMessageTo: '' as IntlString, NewMessageTo: '' as IntlString,
Cancel: '' as IntlString, Cancel: '' as IntlString,
SubjectPlaceholder: '' as IntlString, SubjectPlaceholder: '' as IntlString,

View File

@ -21,6 +21,7 @@
"Messages": "Messages", "Messages": "Messages",
"Telegram": "Telegram", "Telegram": "Telegram",
"TelegramIntegrationDesc": "Use telegram integration", "TelegramIntegrationDesc": "Use telegram integration",
"Status": "Status" "Status": "Status",
"YouAnd": "You and"
} }
} }

View File

@ -21,6 +21,7 @@
"Messages": "Сообщения", "Messages": "Сообщения",
"Telegram": "Telegram", "Telegram": "Telegram",
"TelegramIntegrationDesc": "Подключить Telegram", "TelegramIntegrationDesc": "Подключить Telegram",
"Status": "Статус" "Status": "Статус",
"YouAnd": "Вы и"
} }
} }

View File

@ -16,7 +16,6 @@
<script lang="ts"> <script lang="ts">
import attachment from '@anticrm/attachment' import attachment from '@anticrm/attachment'
import { AttachmentRefInput } from '@anticrm/attachment-resources' import { AttachmentRefInput } from '@anticrm/attachment-resources'
import { Panel } from '@anticrm/panel'
import { createEventDispatcher } from 'svelte' import { createEventDispatcher } from 'svelte'
import contact, { Channel, Contact, EmployeeAccount, formatName } from '@anticrm/contact' import contact, { Channel, Contact, EmployeeAccount, formatName } from '@anticrm/contact'
import { generateId, getCurrentAccount, Ref, SortingOrder, Space, Class } from '@anticrm/core' import { generateId, getCurrentAccount, Ref, SortingOrder, Space, Class } from '@anticrm/core'
@ -24,7 +23,7 @@
import { createQuery, getClient } from '@anticrm/presentation' import { createQuery, getClient } from '@anticrm/presentation'
import setting, { Integration } from '@anticrm/setting' import setting, { Integration } from '@anticrm/setting'
import type { NewTelegramMessage, SharedTelegramMessage, TelegramMessage } from '@anticrm/telegram' import type { NewTelegramMessage, SharedTelegramMessage, TelegramMessage } from '@anticrm/telegram'
import { Button, eventToHTMLElement, IconShare, Tooltip, Scroller, showPopup } from '@anticrm/ui' import { Button, eventToHTMLElement, IconShare, Tooltip, Scroller, showPopup, Panel, Icon, Label } from '@anticrm/ui'
import telegram from '../plugin' import telegram from '../plugin'
import Connect from './Connect.svelte' import Connect from './Connect.svelte'
import TelegramIcon from './icons/Telegram.svelte' import TelegramIcon from './icons/Telegram.svelte'
@ -187,20 +186,26 @@
{#if object !== undefined} {#if object !== undefined}
<Panel <Panel
icon={TelegramIcon} isHeader={true}
title={'Telegram'}
withoutActivity
{object}
isHeader={false}
isAside={false} isAside={false}
on:close={() => { on:close={() => {
dispatch('close') dispatch('close')
}} }}
> >
<svelte:fragment slot="header"> <svelte:fragment slot="title">
You and {formatName(object.name)} <div class="antiTitle icon-wrapper">
<div class="wrapped-icon"><Icon icon={TelegramIcon} size={'medium'} /></div>
<div class="title-wrapper">
<span class="wrapped-title">Telegram</span>
<span class="wrapped-subtitle">
<Label label={telegram.string.YouAnd} />
<b>{formatName(object.name)}</b>
</span>
</div>
</div>
<!-- You and {formatName(object.name)} -->
</svelte:fragment> </svelte:fragment>
<svelte:fragment slot="tools"> <svelte:fragment slot="utils">
{#if integration === undefined} {#if integration === undefined}
<Button <Button
label={telegram.string.Connect} label={telegram.string.Connect}
@ -257,7 +262,7 @@
</div> </div>
</div> </div>
{:else if integration === undefined || integration.disabled} {:else if integration === undefined || integration.disabled}
<div class="flex-center h-18">No integration</div> <div class="flex-center h-18" />
{:else} {:else}
<AttachmentRefInput <AttachmentRefInput
space={telegram.space.Telegram} space={telegram.space.Telegram}

View File

@ -132,14 +132,14 @@
padding: 0.5rem 0.75rem; padding: 0.5rem 0.75rem;
max-width: 66%; max-width: 66%;
width: fit-content; width: fit-content;
background-color: var(--theme-incoming-msg); background-color: var(--incoming-msg);
border-radius: 0.75rem 0.75rem 0.75rem 0.25rem; border-radius: 0.75rem 0.75rem 0.75rem 0.25rem;
overflow-wrap: anywhere; overflow-wrap: anywhere;
user-select: text; user-select: text;
cursor: default; cursor: default;
&.outcoming { &.outcoming {
background-color: var(--theme-outcoming-msg); background-color: var(--outcoming-msg);
border-radius: 0.75rem 0.75rem 0.25rem 0.75rem; border-radius: 0.75rem 0.75rem 0.25rem 0.75rem;
} }
.time { .time {

View File

@ -26,7 +26,7 @@
<style lang="scss"> <style lang="scss">
.container { .container {
padding: 1.25rem 0 1.5rem; padding: 1rem;
background-color: var(--theme-bg-accent-color); background-color: var(--theme-bg-accent-color);
border: 1px solid var(--theme-bg-accent-color); border: 1px solid var(--theme-bg-accent-color);
border-radius: 0.75rem; border-radius: 0.75rem;

View File

@ -33,6 +33,7 @@ export default mergeIds(telegramId, telegram, {
CodeDescr: '' as IntlString, CodeDescr: '' as IntlString,
Cancel: '' as IntlString, Cancel: '' as IntlString,
Share: '' as IntlString, Share: '' as IntlString,
PublishSelected: '' as IntlString PublishSelected: '' as IntlString,
YouAnd: '' as IntlString
} }
}) })

View File

@ -31,6 +31,7 @@ test.describe('contact tests', () => {
// Click [placeholder="john\.appleseed\@apple\.com"] // Click [placeholder="john\.appleseed\@apple\.com"]
await page.click('button:has-text("Email")') await page.click('button:has-text("Email")')
// Fill [placeholder="john\.appleseed\@apple\.com"] // Fill [placeholder="john\.appleseed\@apple\.com"]
await page.click('text=Edit profile John Appleseed LoPlaza >> button')
await page.fill('[placeholder="john\\.appleseed\\@apple\\.com"]', 'wer@qwe.com') await page.fill('[placeholder="john\\.appleseed\\@apple\\.com"]', 'wer@qwe.com')
// Click text=Apply // Click text=Apply
await page.click('button:nth-child(3)') await page.click('button:nth-child(3)')