mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-22 11:01:54 +03:00
Update Panel, Telegram, Scroller layouts. (#1621)
Signed-off-by: Alexander Platov <sas_lord@mail.ru>
This commit is contained in:
parent
797e72332e
commit
864095e324
@ -19,18 +19,19 @@
|
||||
import type { Doc } from '@anticrm/core'
|
||||
import notification from '@anticrm/notification'
|
||||
import type { Asset } from '@anticrm/platform'
|
||||
import { AnyComponent, AnySvelteComponent, Component, Panel, Icon } from '@anticrm/ui'
|
||||
import { AnySvelteComponent, Component, Panel, Icon, Scroller } from '@anticrm/ui'
|
||||
|
||||
export let title: string | undefined = undefined
|
||||
export let subtitle: string | undefined = undefined
|
||||
export let icon: Asset | AnySvelteComponent | undefined = undefined
|
||||
export let rightSection: AnyComponent | undefined = undefined
|
||||
export let withoutActivity: boolean = false
|
||||
export let object: Doc
|
||||
export let panelWidth: number = 0
|
||||
export let innerWidth: number = 0
|
||||
export let isHeader: boolean = true
|
||||
export let isSub: boolean = true
|
||||
export let isAside: boolean = true
|
||||
export let isCustomAttr: boolean = true
|
||||
export let minimize: boolean = false
|
||||
|
||||
let docWidth: number = 0
|
||||
@ -39,14 +40,7 @@
|
||||
</script>
|
||||
|
||||
<svelte:window bind:innerWidth={docWidth} />
|
||||
<Panel
|
||||
rightSection={rightSection !== undefined}
|
||||
bind:isAside
|
||||
isHeader={needHeader}
|
||||
bind:panelWidth
|
||||
bind:innerWidth
|
||||
on:close
|
||||
>
|
||||
<Panel bind:isAside isHeader={needHeader} bind:panelWidth bind:innerWidth on:close>
|
||||
<svelte:fragment slot="title">
|
||||
<div class="popupPanel-title__content-container antiTitle">
|
||||
{#if $$slots.navigator}
|
||||
@ -90,7 +84,7 @@
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{#if $$slots['custom-attributes']}
|
||||
{#if $$slots['custom-attributes'] && isCustomAttr}
|
||||
{#if isSub}<div class="header-row"><slot name="custom-attributes" direction="row" /></div>{/if}
|
||||
{:else if $$slots.attributes && minimize}<div class="header-row">
|
||||
<slot name="attributes" direction="row" />
|
||||
@ -107,18 +101,22 @@
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{#if $$slots['custom-attributes']}
|
||||
{#if $$slots['custom-attributes'] && isCustomAttr}
|
||||
<slot name="custom-attributes" direction="column" />
|
||||
{:else if $$slots.attributes}<slot name="attributes" direction="column" />{/if}
|
||||
{#if $$slots.aside}<slot name="aside" />{/if}
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
|
||||
{#if rightSection !== undefined}
|
||||
{#if withoutActivity}
|
||||
<slot />
|
||||
{:else}
|
||||
<Component is={activity.component.Activity} props={{ object, integrate: true }}>
|
||||
<slot />
|
||||
</Component>
|
||||
<Scroller>
|
||||
<div class="popupPanel-body__main-content py-10 clear-mins">
|
||||
<Component is={activity.component.Activity} props={{ object, integrate: true }}>
|
||||
<slot />
|
||||
</Component>
|
||||
</div>
|
||||
</Scroller>
|
||||
{/if}
|
||||
</Panel>
|
||||
|
@ -370,9 +370,11 @@ p:last-child { margin-block-end: 0; }
|
||||
.pb-2 { padding-bottom: .5rem; }
|
||||
.pb-3 { padding-bottom: .75rem; }
|
||||
.pb-4 { padding-bottom: 1rem; }
|
||||
.pb-6 { padding-bottom: 1.5rem; }
|
||||
.px-2 { padding: 0 .5rem; }
|
||||
.px-3 { padding: 0 .75rem; }
|
||||
.px-4 { padding: 0 1rem; }
|
||||
.py-10 { padding: 2.5rem 0; }
|
||||
|
||||
.p-1 { padding: .25rem; }
|
||||
.p-2 { padding: .5rem; }
|
||||
|
@ -202,6 +202,10 @@
|
||||
.antiNav-bottomFade { mask-image: linear-gradient(0deg, rgba(0, 0, 0, 0) 0, rgba(0, 0, 0, 1) 1px, rgba(0, 0, 0, 1) calc(100% - 2rem), rgba(0, 0, 0, 0) 100%); }
|
||||
.antiNav-bothFade { mask-image: linear-gradient(0deg, rgba(0, 0, 0, 0) 0, rgba(0, 0, 0, 1) 2rem, rgba(0, 0, 0, 1) calc(100% - 2rem), rgba(0, 0, 0, 0) 100%); }
|
||||
.antiNav-noneFade { mask-image: linear-gradient(0deg, rgba(0, 0, 0, 0) 0, rgba(0, 0, 0, 1) 1px, rgba(0, 0, 0, 1) calc(100% - 1px), rgba(0, 0, 0, 0) 100%); }
|
||||
.tableFade.antiNav-topFade { mask-image: linear-gradient(0deg, rgba(0, 0, 0, 0) 0, rgba(0, 0, 0, 1) 2rem, rgba(0, 0, 0, 1) calc(100% - 1px), rgba(0, 0, 0, 0) 100%); }
|
||||
.tableFade.antiNav-bottomFade { mask-image: linear-gradient(0deg, rgba(0, 0, 0, 0) 0, rgba(0, 0, 0, 1) 1px, rgba(0, 0, 0, 1) calc(100% - 4.5rem - 1px), rgba(0, 0, 0, 0) calc(100% - 2.5rem), rgba(0, 0, 0, 1) calc(100% - 2.5rem + .5px)); }
|
||||
.tableFade.antiNav-bothFade { mask-image: linear-gradient(0deg, rgba(0, 0, 0, 0) 0, rgba(0, 0, 0, 1) 2rem, rgba(0, 0, 0, 1) calc(100% - 4.5rem - 1px), rgba(0, 0, 0, 0) calc(100% - 2.5rem), rgba(0, 0, 0, 1) calc(100% - 2.5rem + .5px)); }
|
||||
.tableFade.antiNav-noneFade { mask-image: linear-gradient(0deg, rgba(0, 0, 0, 0) 0, rgba(0, 0, 0, 1) 1px, rgba(0, 0, 0, 1) calc(100% - 1px), rgba(0, 0, 0, 0) 100%); }
|
||||
|
||||
/* Basic */
|
||||
.antiTitle {
|
||||
|
@ -324,6 +324,7 @@
|
||||
|
||||
/* Table */
|
||||
.antiTable {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
th, td {
|
||||
@ -415,6 +416,13 @@
|
||||
&.checking { background-color: var(--highlight-select); } // --theme-table-bg-hover
|
||||
&.selected { background-color: var(--highlight-hover); } // --menu-bg-select
|
||||
}
|
||||
|
||||
.scroller-thead {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 999;
|
||||
background-color: var(--body-color);
|
||||
}
|
||||
}
|
||||
|
||||
// Hide row menu in Tooltip
|
||||
|
@ -127,8 +127,9 @@
|
||||
width: calc(100% - 5rem);
|
||||
min-width: 0;
|
||||
max-width: 900px;
|
||||
border-bottom: 1px solid var(--divider-color);
|
||||
|
||||
&.bottom-divider { border-bottom: 1px solid var(--divider-color); }
|
||||
&.top-divider { border-top: 1px solid var(--divider-color); }
|
||||
.header-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
@ -14,9 +14,8 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { Button, IconClose, IconDetails, Scroller } from '..'
|
||||
import { Button, IconClose, IconDetails } from '..'
|
||||
|
||||
export let rightSection: boolean = false
|
||||
export let innerWidth: number = 0
|
||||
export let panelWidth: number = 0
|
||||
export let isHeader: boolean = true
|
||||
@ -65,15 +64,11 @@
|
||||
<div class="popupPanel-body" class:asideShown>
|
||||
<div class="popupPanel-body__main" bind:clientWidth={innerWidth}>
|
||||
{#if $$slots.header && isHeader}
|
||||
<div class="popupPanel-body__main-header">
|
||||
<div class="popupPanel-body__main-header bottom-divider">
|
||||
<slot name="header" />
|
||||
</div>
|
||||
{/if}
|
||||
<Scroller>
|
||||
<div class="popupPanel-body__main-content">
|
||||
<slot />
|
||||
</div>
|
||||
</Scroller>
|
||||
<slot />
|
||||
</div>
|
||||
{#if $$slots.aside && isAside}
|
||||
<div class="popupPanel-body__aside" class:float={asideFloat} class:shown={asideShown}>
|
||||
|
@ -17,8 +17,9 @@
|
||||
|
||||
export let padding: boolean = false
|
||||
export let autoscroll: boolean = false
|
||||
export let correctPadding: number = 0
|
||||
// export let correctPadding: number = 0
|
||||
export let bottomStart: boolean = false
|
||||
export let tableFade: boolean = false
|
||||
|
||||
let mask: 'top' | 'bottom' | 'both' | 'none' = 'bottom'
|
||||
|
||||
@ -27,17 +28,11 @@
|
||||
let divBack: HTMLElement
|
||||
let divBar: HTMLElement
|
||||
let divTrack: HTMLElement
|
||||
let divTHead: HTMLElement
|
||||
let elTHead: Element
|
||||
let isBack: boolean = false // ?
|
||||
let isTHead: boolean = false
|
||||
let hasTHeads: boolean = false // ?
|
||||
let isScrolling: boolean = false
|
||||
let enabledChecking: boolean = false
|
||||
let dY: number
|
||||
let visibleEl: number | undefined = undefined
|
||||
let belowContent: number | undefined = undefined
|
||||
let scrolling: boolean = false
|
||||
let scrolling: boolean = autoscroll
|
||||
let firstScroll: boolean = autoscroll
|
||||
|
||||
const checkBack = (): void => {
|
||||
@ -65,86 +60,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
const checkTHeadSizes = (): void => {
|
||||
if (elTHead && divTHead && divScroll) {
|
||||
const elems = divTHead.querySelectorAll('div')
|
||||
elems.forEach((el, i) => {
|
||||
const th = elTHead.children.item(i)
|
||||
if (th) el.style.width = th.clientWidth + 'px'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const clearTHead = (): void => {
|
||||
visibleEl = undefined
|
||||
divTHead.innerHTML = ''
|
||||
divTHead.style.visibility = 'hidden'
|
||||
divTHead.style.opacity = '0'
|
||||
isTHead = false
|
||||
}
|
||||
|
||||
const fillTHead = (el: Element): boolean => {
|
||||
const tr: Element | null = el.children.item(0)
|
||||
if (tr) {
|
||||
for (let i = 0; i < tr.children.length; i++) {
|
||||
const th = tr.children.item(i)
|
||||
if (th) {
|
||||
let newStyle = `flex-shrink: 0; width: ${th.clientWidth}px; `
|
||||
if ((i === 0 && !enabledChecking) || (i === 1 && enabledChecking)) newStyle += 'padding-right: 1.5rem;'
|
||||
else if (i === tr.children.length - 1) newStyle += 'padding-left: 1.5rem;'
|
||||
else if (i === 0 && enabledChecking) newStyle += 'padding: 0 .75rem;'
|
||||
else newStyle += 'padding: 0 1.5rem;'
|
||||
if (th.classList.contains('sorted')) newStyle += ' margin-right: .25rem;'
|
||||
divTHead.insertAdjacentHTML('beforeend', `<div style="${newStyle}">${tr.children.item(i)?.textContent}</div>`)
|
||||
}
|
||||
}
|
||||
isTHead = true
|
||||
elTHead = tr
|
||||
}
|
||||
return isTHead
|
||||
}
|
||||
|
||||
const findTHeaders = (): void => {
|
||||
if (divBox) {
|
||||
const elements = divBox.querySelectorAll('.scroller-thead')
|
||||
if (elements && elements.length > 0 && divScroll) {
|
||||
const rectScroll = divScroll.getBoundingClientRect()
|
||||
hasTHeads = true
|
||||
elements.forEach((el, i) => {
|
||||
const rect = el.getBoundingClientRect()
|
||||
enabledChecking = el.parentElement?.classList.contains('enableChecking') ?? false
|
||||
const rectTable = el.parentElement?.getBoundingClientRect()
|
||||
if (rectTable) {
|
||||
if (rectTable.top < rectScroll.top && rectTable.bottom > rectScroll.top + rect.height) {
|
||||
if (!isTHead && divTHead) if (fillTHead(el)) visibleEl = i
|
||||
if (isTHead) {
|
||||
if (rect.width > rectScroll.width) divTHead.style.width = rectScroll.width - correctPadding + 'px'
|
||||
else divTHead.style.width = rect.width + 'px'
|
||||
divTHead.style.height = rect.height + 'px'
|
||||
divTHead.style.left = rect.left + 'px'
|
||||
if (rect.bottom - 16 < rectScroll.top && rectTable.bottom > rectScroll.top + rect.height) {
|
||||
divTHead.style.top = rectScroll.top + 'px'
|
||||
divTHead.style.visibility = 'visible'
|
||||
divTHead.style.opacity = '.9'
|
||||
} else {
|
||||
divTHead.style.top = rect.top + 'px'
|
||||
divTHead.style.visibility = 'hidden'
|
||||
divTHead.style.opacity = '0'
|
||||
}
|
||||
}
|
||||
} else if (
|
||||
(rectTable.top > rectScroll.top + rect.height || rectTable.bottom < rectScroll.top + rect.height) &&
|
||||
isTHead &&
|
||||
visibleEl === i
|
||||
) {
|
||||
clearTHead()
|
||||
}
|
||||
}
|
||||
})
|
||||
} else hasTHeads = false
|
||||
}
|
||||
}
|
||||
|
||||
const checkBar = (): void => {
|
||||
if (divBar && divScroll) {
|
||||
const proc = (divScroll.clientHeight / divScroll.scrollHeight) * 100
|
||||
@ -195,7 +110,6 @@
|
||||
|
||||
const checkFade = (): void => {
|
||||
if (divScroll) {
|
||||
scrolling = false
|
||||
const t = divScroll.scrollTop
|
||||
const b = divScroll.scrollHeight - divScroll.clientHeight - t
|
||||
if (t > 0 && b > 0) mask = 'both'
|
||||
@ -204,19 +118,19 @@
|
||||
else mask = 'none'
|
||||
}
|
||||
checkBack()
|
||||
findTHeaders()
|
||||
if (isTHead) checkTHeadSizes()
|
||||
if (!isScrolling) checkBar()
|
||||
if (scrolling && belowContent && belowContent > 1) {
|
||||
divScroll.scrollTop = divScroll.scrollHeight - divScroll.clientHeight
|
||||
}
|
||||
}
|
||||
|
||||
const observer = new IntersectionObserver(() => checkFade(), { root: null, threshold: 0.1 })
|
||||
|
||||
$: if (firstScroll && divScroll && divScroll.clientHeight !== divScroll.scrollHeight) {
|
||||
$: if (autoscroll && !scrolling && belowContent && belowContent < 1 && divScroll) {
|
||||
divScroll.scrollTop = divScroll.scrollHeight - divScroll.clientHeight
|
||||
firstScroll = false
|
||||
scrolling = true
|
||||
}
|
||||
$: if (autoscroll && belowContent && belowContent <= 50) scrolling = true
|
||||
$: if (scrolling && belowContent && belowContent > 50) {
|
||||
$: if (scrolling && divScroll && divScroll.scrollHeight - divScroll.scrollTop - divScroll.clientHeight < 5) {
|
||||
divScroll.scrollTop = divScroll.scrollHeight - divScroll.clientHeight
|
||||
}
|
||||
|
||||
@ -237,19 +151,26 @@
|
||||
const tempEl = divBox.querySelector('*') as HTMLElement
|
||||
if (tempEl) observer.observe(tempEl)
|
||||
if (scrolling) divScroll.scrollTop = divScroll.scrollHeight - divScroll.clientHeight
|
||||
checkFade()
|
||||
clearTHead()
|
||||
findTHeaders()
|
||||
belowContent = divScroll.scrollHeight - divScroll.clientHeight - divScroll.scrollTop
|
||||
checkFade()
|
||||
}
|
||||
})
|
||||
|
||||
let divWidth: number = 0
|
||||
const _resize = (): void => {
|
||||
clearTHead()
|
||||
checkFade()
|
||||
}
|
||||
$: 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>
|
||||
|
||||
<svelte:window on:resize={_resize} />
|
||||
@ -257,7 +178,9 @@
|
||||
<div
|
||||
bind:this={divScroll}
|
||||
bind:clientWidth={divWidth}
|
||||
on:scroll={_scroll}
|
||||
class="scroll relative"
|
||||
class:tableFade
|
||||
class:antiNav-topFade={mask === 'top'}
|
||||
class:antiNav-bottomFade={mask === 'bottom'}
|
||||
class:antiNav-bothFade={mask === 'both'}
|
||||
@ -270,7 +193,6 @@
|
||||
<div bind:this={divBack} class="back" />
|
||||
<div class="bar" class:hovered={isScrolling} bind:this={divBar} on:mousedown={onScrollStart} />
|
||||
<div class="track" class:hovered={isScrolling} bind:this={divTrack} />
|
||||
<div bind:this={divTHead} class="fly-head thead-style" />
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
@ -366,23 +288,4 @@
|
||||
background-color: var(--body-color);
|
||||
z-index: -1;
|
||||
}
|
||||
.fly-head {
|
||||
overflow: hidden;
|
||||
position: fixed;
|
||||
pointer-events: none;
|
||||
visibility: hidden;
|
||||
transition: top 0.2s ease-out, opacity 0.2s;
|
||||
}
|
||||
.thead-style {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
height: 2.5rem;
|
||||
font-weight: 500;
|
||||
font-size: 0.75rem;
|
||||
color: var(--dark-color);
|
||||
background-color: var(--board-bg-color);
|
||||
box-shadow: inset 0 -1px 0 0 var(--theme-bg-focused-color);
|
||||
user-select: none;
|
||||
}
|
||||
</style>
|
||||
|
@ -20,6 +20,6 @@
|
||||
|
||||
<svg class="svg-{size}" {fill} viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M12.7,7.5H3.9L7,4.4L6.3,3.6l-4,4L2,8l0.4,0.4l4,4L7,11.6L3.9,8.5h8.8c0.3,0,0.5-0.2,0.5-0.5S12.9,7.5,12.7,7.5z"
|
||||
d="M13.1,7.5H4.3l3.1-3.1L6.7,3.6l-4,4L2.4,8l0.4,0.4l4,4l0.6-0.8L4.3,8.5h8.8c0.3,0,0.5-0.2,0.5-0.5S13.3,7.5,13.1,7.5z"
|
||||
/>
|
||||
</svg>
|
||||
|
25
packages/ui/src/components/icons/ArrowRight.svelte
Normal file
25
packages/ui/src/components/icons/ArrowRight.svelte
Normal file
@ -0,0 +1,25 @@
|
||||
<!--
|
||||
// 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.
|
||||
-->
|
||||
<script lang="ts">
|
||||
export let size: 'small' | 'medium' | 'large'
|
||||
const fill: string = 'currentColor'
|
||||
</script>
|
||||
|
||||
<svg class="svg-{size}" {fill} viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M2.4,8c0,0.3,0.2,0.5,0.5,0.5h8.8l-3.1,3.1l0.6,0.8l4-4L13.6,8l-0.3-0.4l-4-4L8.6,4.4l3.1,3.1l-8.8,0C2.7,7.5,2.4,7.7,2.4,8z"
|
||||
/>
|
||||
</svg>
|
@ -114,6 +114,7 @@ export { default as IconInfo } from './components/icons/Info.svelte'
|
||||
export { default as IconBlueCheck } from './components/icons/BlueCheck.svelte'
|
||||
export { default as IconCheck } from './components/icons/Check.svelte'
|
||||
export { default as IconArrowLeft } from './components/icons/ArrowLeft.svelte'
|
||||
export { default as IconArrowRight } from './components/icons/ArrowRight.svelte'
|
||||
export { default as IconNavPrev } from './components/icons/NavPrev.svelte'
|
||||
export { default as IconNavNext } from './components/icons/NavNext.svelte'
|
||||
export { default as IconDPCalendar } from './components/calendar/icons/DPCalendar.svelte'
|
||||
|
@ -8,7 +8,6 @@ export interface PanelProps {
|
||||
_class: string
|
||||
element?: PopupAlignment
|
||||
rightSection?: AnyComponent
|
||||
fullSize?: boolean
|
||||
}
|
||||
|
||||
export const panelstore = writable<{ panel?: PanelProps | undefined }>({ panel: undefined })
|
||||
@ -17,7 +16,13 @@ let currentLocation: string | undefined
|
||||
location.subscribe((loc) => {
|
||||
if (loc.fragment !== currentLocation && loc.fragment !== undefined && loc.fragment.trim().length > 0) {
|
||||
const props = decodeURIComponent(loc.fragment).split('|')
|
||||
showPanel(props[0] as AnyComponent, props[1], props[2], (props[3] ?? undefined) as PopupAlignment)
|
||||
showPanel(
|
||||
props[0] as AnyComponent,
|
||||
props[1],
|
||||
props[2],
|
||||
(props[3] ?? undefined) as PopupAlignment,
|
||||
(props[4] ?? undefined) as AnyComponent
|
||||
)
|
||||
} else if (
|
||||
(loc.fragment === undefined || (loc.fragment !== undefined && loc.fragment.trim().length === 0)) &&
|
||||
currentLocation !== undefined
|
||||
@ -47,7 +52,7 @@ export function showPanel (
|
||||
element?: PopupAlignment,
|
||||
rightSection?: AnyComponent
|
||||
): void {
|
||||
const newLoc = getPanelURI(component, _id, _class, element)
|
||||
const newLoc = getPanelURI(component, _id, _class, element, rightSection)
|
||||
if (currentLocation === newLoc) {
|
||||
return
|
||||
}
|
||||
|
@ -198,8 +198,8 @@ export function fitPopupElement (modalHTML: HTMLElement, element?: PopupAlignmen
|
||||
} else if (element === 'float') {
|
||||
newProps.top = 'calc(var(--status-bar-height) + .25rem)'
|
||||
newProps.bottom = '.25rem'
|
||||
newProps.width = '40rem'
|
||||
newProps.maxWidth = '40%'
|
||||
newProps.width = '40%'
|
||||
newProps.maxWidth = '60%'
|
||||
newProps.right = '.25rem'
|
||||
show = true
|
||||
} else if (element === 'account') {
|
||||
|
@ -87,7 +87,7 @@
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="mt-4 pb-4 bottom-highlight-select">
|
||||
<div class="pb-6 bottom-highlight-select">
|
||||
<slot />
|
||||
</div>
|
||||
<div class="flex-row-center h-14 px-3 mt-4 antiTitle">
|
||||
|
@ -16,7 +16,7 @@
|
||||
import { createEventDispatcher, onMount } from 'svelte'
|
||||
import type { IntlString } from '@anticrm/platform'
|
||||
import { translate } from '@anticrm/platform'
|
||||
import { Button, Icon, IconClose, IconBlueCheck } from '@anticrm/ui'
|
||||
import { Button, Icon, IconClose, IconBlueCheck, IconArrowRight } from '@anticrm/ui'
|
||||
|
||||
export let value: string = ''
|
||||
export let placeholder: IntlString
|
||||
@ -60,6 +60,7 @@
|
||||
>
|
||||
<div class="icon"><Icon icon={IconClose} size={'inline'} /></div>
|
||||
</div>
|
||||
<Button kind={'transparent'} size={'small'} icon={IconArrowRight} on:click={() => dispatch('update', 'open')} />
|
||||
<Button kind={'transparent'} size={'small'} icon={IconBlueCheck} on:click={() => dispatch('close', value)} />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -25,23 +25,19 @@
|
||||
import { getChannelProviders } from '../utils'
|
||||
import ChannelEditor from './ChannelEditor.svelte'
|
||||
import { NotificationClientImpl } from '@anticrm/notification-resources'
|
||||
import { onDestroy } from 'svelte'
|
||||
|
||||
export let value: AttachedData<Channel>[] | Channel | null
|
||||
export let editable = false
|
||||
export let editable: boolean = false
|
||||
export let kind: ButtonKind = 'no-border'
|
||||
export let size: ButtonSize = 'small'
|
||||
export let length: 'short' | 'full' = 'full'
|
||||
export let shape: 'circle' | undefined = undefined
|
||||
// export let reverse: boolean = false
|
||||
export let integrations: Set<Ref<Doc>> = new Set<Ref<Doc>>()
|
||||
|
||||
const notificationClient = NotificationClientImpl.getClient()
|
||||
const lastViews = notificationClient.getLastViews()
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
let editMode = false
|
||||
|
||||
interface Item {
|
||||
label: IntlString
|
||||
icon: Asset
|
||||
@ -140,8 +136,8 @@
|
||||
}
|
||||
$: if (providers) updateMenu()
|
||||
|
||||
const dropItem = (n: number): void => {
|
||||
displayItems = displayItems.filter((it, i) => i !== n)
|
||||
const dropItem = (n: number): Item[] => {
|
||||
return displayItems.filter((it, i) => i !== n)
|
||||
}
|
||||
const saveItems = (): void => {
|
||||
value = filterUndefined(displayItems)
|
||||
@ -156,15 +152,10 @@
|
||||
ev.target as HTMLElement,
|
||||
(result) => {
|
||||
if (result !== undefined) {
|
||||
if (result == null || result === '') dropItem(n)
|
||||
if (result === null || result === '') displayItems = dropItem(n)
|
||||
else displayItems[n].value = result
|
||||
} else if (displayItems[n].value === '') dropItem(n)
|
||||
saveItems()
|
||||
if (actions.length > 0 && addBtn) {
|
||||
if (result !== undefined) addBtn.click()
|
||||
else disableEdit()
|
||||
} else {
|
||||
disableEdit()
|
||||
saveItems()
|
||||
if (displayItems.length < providers.size && addBtn) addBtn.click()
|
||||
}
|
||||
},
|
||||
(result) => {
|
||||
@ -172,22 +163,26 @@
|
||||
if (result === 'left') {
|
||||
closePopup()
|
||||
if (displayItems[n].value === '') {
|
||||
dropItem(n)
|
||||
displayItems = dropItem(n)
|
||||
saveItems()
|
||||
}
|
||||
if (n === 0) addBtn.click()
|
||||
else btns[n - 1].click()
|
||||
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 === '') {
|
||||
dropItem(n)
|
||||
displayItems = dropItem(n)
|
||||
saveItems()
|
||||
if (n === displayItems.length) addBtn.click()
|
||||
else btns[n + 1].click()
|
||||
} else {
|
||||
if (n === displayItems.length - 1) addBtn.click()
|
||||
else btns[n + 1].click()
|
||||
}
|
||||
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 })
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -198,11 +193,7 @@
|
||||
Menu,
|
||||
{ actions },
|
||||
ev.target as HTMLElement,
|
||||
(result) => {
|
||||
if (result === undefined) {
|
||||
disableEdit()
|
||||
}
|
||||
},
|
||||
() => {},
|
||||
(result) => {
|
||||
if (result !== undefined && displayItems.length > 0) {
|
||||
if (result === 'left') {
|
||||
@ -217,31 +208,9 @@
|
||||
)
|
||||
}
|
||||
let copied: boolean = false
|
||||
let div: HTMLDivElement
|
||||
|
||||
function listener (e: MouseEvent): void {
|
||||
if (e.target !== null && !div.contains(e.target as Node)) {
|
||||
disableEdit()
|
||||
}
|
||||
}
|
||||
|
||||
function enableEdit () {
|
||||
window.addEventListener('click', listener)
|
||||
editMode = true
|
||||
}
|
||||
|
||||
function disableEdit () {
|
||||
window.removeEventListener('click', listener)
|
||||
editMode = false
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
window.removeEventListener('click', listener)
|
||||
})
|
||||
</script>
|
||||
|
||||
<div
|
||||
bind:this={div}
|
||||
class="{displayItems.length === 0 ? 'clear-mins' : 'buttons-group'} {kind === 'no-border'
|
||||
? 'xsmall-gap'
|
||||
: 'xxsmall-gap'}"
|
||||
@ -256,7 +225,7 @@
|
||||
{shape}
|
||||
click={item.value === ''}
|
||||
on:click={(ev) => {
|
||||
if (editMode) editChannel(item, i, ev)
|
||||
if (editable) editChannel(item, i, ev)
|
||||
}}
|
||||
/>
|
||||
{:else}
|
||||
@ -270,12 +239,12 @@
|
||||
{kind}
|
||||
{size}
|
||||
{shape}
|
||||
highlight={item.integration || item.notification || editMode}
|
||||
highlight={item.integration || item.notification}
|
||||
on:click={(ev) => {
|
||||
if (editMode) {
|
||||
if (editable) {
|
||||
editChannel(item, i, ev)
|
||||
} else {
|
||||
dispatch('click', item)
|
||||
dispatch('open', item)
|
||||
if (!copied) {
|
||||
navigator.clipboard.writeText(item.value)
|
||||
copied = true
|
||||
@ -293,12 +262,11 @@
|
||||
<Button
|
||||
bind:input={addBtn}
|
||||
icon={contact.icon.SocialEdit}
|
||||
highlight={editMode}
|
||||
label={displayItems.length === 0 ? presentation.string.AddSocialLinks : undefined}
|
||||
{kind}
|
||||
{size}
|
||||
{shape}
|
||||
on:click={editMode ? showMenu : enableEdit}
|
||||
on:click={showMenu}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
@ -334,6 +302,7 @@
|
||||
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);
|
||||
|
@ -20,7 +20,6 @@
|
||||
|
||||
import { ChannelProvider, Channel } from '@anticrm/contact'
|
||||
import { showPanel } from '@anticrm/ui'
|
||||
import view from '@anticrm/view'
|
||||
import contact from '../plugin'
|
||||
import ChannelsDropdown from './ChannelsDropdown.svelte'
|
||||
|
||||
@ -105,11 +104,11 @@
|
||||
Promise.all(promises)
|
||||
}
|
||||
|
||||
function click (ev: any) {
|
||||
function _open (ev: any) {
|
||||
if (ev.detail.presenter !== undefined && Array.isArray(channels)) {
|
||||
const channel = channels[0]
|
||||
if (channel !== undefined) {
|
||||
showPanel(view.component.EditDoc, channel.attachedTo, channel.attachedToClass, 'content', ev.detail.presenter)
|
||||
showPanel(ev.detail.presenter, channel.attachedTo, channel.attachedToClass, 'float')
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -126,5 +125,5 @@
|
||||
on:change={(e) => {
|
||||
if (editable) save(e.detail)
|
||||
}}
|
||||
on:click={click}
|
||||
on:open={_open}
|
||||
/>
|
||||
|
@ -27,14 +27,14 @@
|
||||
export let length: 'short' | 'full' = 'short'
|
||||
export let shape: 'circle' | undefined = 'circle'
|
||||
|
||||
function click (ev: any) {
|
||||
function _open (ev: any) {
|
||||
if (ev.detail.presenter !== undefined && Array.isArray(value)) {
|
||||
const channel = value[0]
|
||||
if (channel !== undefined) {
|
||||
showPanel(ev.detail.presenter, channel.attachedTo, channel.attachedToClass, 'float', ev.detail.presenter)
|
||||
showPanel(ev.detail.presenter, channel.attachedTo, channel.attachedToClass, 'float')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<ChannelsDropdown bind:value {length} {kind} {size} {shape} {editable} on:click={click} />
|
||||
<ChannelsDropdown bind:value {length} {kind} {size} {shape} {editable} on:open={_open} />
|
||||
|
@ -66,7 +66,7 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Scroller>
|
||||
<Scroller tableFade>
|
||||
{#await tableDescriptor then descr}
|
||||
{#if descr}
|
||||
<TableBrowser
|
||||
|
@ -143,9 +143,8 @@
|
||||
attachedTo={object._id}
|
||||
attachedClass={object._class}
|
||||
{editable}
|
||||
{integrations}
|
||||
bind:integrations
|
||||
shape={'circle'}
|
||||
on:click
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
@ -14,7 +14,6 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { AnyComponent } from '@anticrm/ui'
|
||||
import { Ref, Doc, Class } from '@anticrm/core'
|
||||
import contact, { Channel } from '@anticrm/contact'
|
||||
import { SharedMessage } from '@anticrm/gmail'
|
||||
@ -28,10 +27,8 @@
|
||||
|
||||
export let _id: Ref<Doc>
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let rightSection: AnyComponent | undefined = undefined
|
||||
|
||||
// export let object: Contact
|
||||
// $: console.log('!!!!!!!!!!!! id: ', _id, ' - class: ', _class)
|
||||
let object: any
|
||||
let newMessage: boolean = false
|
||||
let currentMessage: SharedMessage | undefined = undefined
|
||||
@ -76,7 +73,7 @@
|
||||
<Panel
|
||||
icon={contact.icon.Email}
|
||||
title={'Email'}
|
||||
{rightSection}
|
||||
withoutActivity
|
||||
{object}
|
||||
isHeader={false}
|
||||
isAside={false}
|
||||
|
@ -67,7 +67,7 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Scroller>
|
||||
<Scroller tableFade>
|
||||
{#await tableDescriptor then descr}
|
||||
{#if descr}
|
||||
<Table
|
||||
|
@ -49,7 +49,7 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Scroller>
|
||||
<Scroller tableFade>
|
||||
{#await tableDescriptor then descr}
|
||||
{#if descr}
|
||||
<Table
|
||||
|
@ -82,6 +82,6 @@
|
||||
<Button icon={IconAdd} label={recruit.string.ApplicationCreateLabel} kind={'primary'} on:click={showCreateDialog} />
|
||||
</div>
|
||||
|
||||
<Scroller>
|
||||
<Scroller tableFade>
|
||||
<TableBrowser _class={recruit.class.Applicant} {config} {options} query={resultQuery} showNotification />
|
||||
</Scroller>
|
||||
|
@ -93,7 +93,7 @@
|
||||
mode: 'browser'
|
||||
}}
|
||||
/>
|
||||
<Scroller>
|
||||
<Scroller tableFade>
|
||||
{#await tableDescriptor then descr}
|
||||
{#if descr}
|
||||
<TableBrowser
|
||||
|
@ -89,7 +89,7 @@
|
||||
/>
|
||||
<Button icon={IconAdd} label={recruit.string.VacancyCreateLabel} kind={'primary'} on:click={showCreateDialog} />
|
||||
</div>
|
||||
<Scroller>
|
||||
<Scroller tableFade>
|
||||
<Table
|
||||
_class={recruit.class.Vacancy}
|
||||
config={[
|
||||
|
@ -183,13 +183,13 @@
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.counter {
|
||||
padding-right: 0.125rem;
|
||||
min-width: 1.5rem;
|
||||
text-align: right;
|
||||
font-size: 0.8125rem;
|
||||
color: var(--caption-color);
|
||||
}
|
||||
// .counter {
|
||||
// padding-right: 0.125rem;
|
||||
// min-width: 1.5rem;
|
||||
// text-align: right;
|
||||
// font-size: 0.8125rem;
|
||||
// color: var(--caption-color);
|
||||
// }
|
||||
.empty {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
@ -108,7 +108,7 @@
|
||||
updateResultQuery(search, category)
|
||||
}}
|
||||
/>
|
||||
<Scroller>
|
||||
<Scroller tableFade>
|
||||
<TableBrowser
|
||||
_class={tags.class.TagElement}
|
||||
config={[
|
||||
|
@ -99,7 +99,7 @@
|
||||
on:change={(evt) => updateCategory(evt.detail)}
|
||||
/>
|
||||
|
||||
<Scroller>
|
||||
<Scroller tableFade>
|
||||
<Table
|
||||
{_class}
|
||||
config={[
|
||||
|
@ -24,7 +24,6 @@
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import setting, { Integration } from '@anticrm/setting'
|
||||
import type { NewTelegramMessage, SharedTelegramMessage, TelegramMessage } from '@anticrm/telegram'
|
||||
import type { AnyComponent } from '@anticrm/ui'
|
||||
import { Button, eventToHTMLElement, IconShare, Tooltip, Scroller, showPopup } from '@anticrm/ui'
|
||||
import telegram from '../plugin'
|
||||
import Connect from './Connect.svelte'
|
||||
@ -34,7 +33,6 @@
|
||||
|
||||
export let _id: Ref<Contact>
|
||||
export let _class: Ref<Class<Contact>>
|
||||
export let rightSection: AnyComponent | undefined = undefined
|
||||
|
||||
let object: Contact
|
||||
let channel: Channel | undefined = undefined
|
||||
@ -77,7 +75,7 @@
|
||||
telegram.class.Message,
|
||||
{ attachedTo: channelId },
|
||||
(res) => {
|
||||
messages = res.reverse()
|
||||
messages = res
|
||||
if (channel !== undefined) {
|
||||
notificationClient.updateLastView(channel._id, channel._class, undefined, true)
|
||||
}
|
||||
@ -191,7 +189,7 @@
|
||||
<Panel
|
||||
icon={TelegramIcon}
|
||||
title={'Telegram'}
|
||||
{rightSection}
|
||||
withoutActivity
|
||||
{object}
|
||||
isHeader={false}
|
||||
isAside={false}
|
||||
@ -203,27 +201,43 @@
|
||||
You and {formatName(object.name)}
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="tools">
|
||||
<Tooltip label={telegram.string.Share}>
|
||||
{#if integration === undefined}
|
||||
<Button
|
||||
icon={IconShare}
|
||||
kind={'transparent'}
|
||||
size={'medium'}
|
||||
on:click={async () => {
|
||||
selectable = !selectable
|
||||
label={telegram.string.Connect}
|
||||
kind={'primary'}
|
||||
on:click={(e) => {
|
||||
showPopup(Connect, {}, eventToHTMLElement(e), onConnectClose)
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
{:else if integration.disabled}
|
||||
<Button
|
||||
label={setting.string.Reconnect}
|
||||
kind={'primary'}
|
||||
on:click={(e) => {
|
||||
showPopup(Reconnect, {}, eventToHTMLElement(e), onReconnect)
|
||||
}}
|
||||
/>
|
||||
{:else}
|
||||
<Tooltip label={telegram.string.Share}>
|
||||
<Button
|
||||
icon={IconShare}
|
||||
kind={'transparent'}
|
||||
size={'medium'}
|
||||
on:click={async () => {
|
||||
selectable = !selectable
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
|
||||
<div class="telegram-content" class:selectable>
|
||||
<Scroller bottomStart autoscroll>
|
||||
{#if messages && accounts}
|
||||
<Messages messages={convertMessages(messages, accounts)} {selectable} bind:selected />
|
||||
{/if}
|
||||
</Scroller>
|
||||
</div>
|
||||
<Scroller bottomStart autoscroll>
|
||||
{#if messages && accounts}
|
||||
<Messages messages={convertMessages(messages, accounts)} {selectable} bind:selected />
|
||||
{/if}
|
||||
</Scroller>
|
||||
|
||||
<div class="ref-input" class:selectable>
|
||||
<div class="popupPanel-body__main-header ref-input" class:selectable>
|
||||
{#if selectable}
|
||||
<div class="flex-between">
|
||||
<span>{selected.size} messages selected</span>
|
||||
@ -242,26 +256,8 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{:else if integration === undefined}
|
||||
<div class="flex-center">
|
||||
<Button
|
||||
label={telegram.string.Connect}
|
||||
kind={'primary'}
|
||||
on:click={(e) => {
|
||||
showPopup(Connect, {}, eventToHTMLElement(e), onConnectClose)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{:else if integration.disabled}
|
||||
<div class="flex-center">
|
||||
<Button
|
||||
label={setting.string.Reconnect}
|
||||
kind={'primary'}
|
||||
on:click={(e) => {
|
||||
showPopup(Reconnect, {}, eventToHTMLElement(e), onReconnect)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{:else if integration === undefined || integration.disabled}
|
||||
<div class="flex-center h-18">No integration</div>
|
||||
{:else}
|
||||
<AttachmentRefInput
|
||||
space={telegram.space.Telegram}
|
||||
@ -276,7 +272,7 @@
|
||||
|
||||
<style lang="scss">
|
||||
.ref-input {
|
||||
padding: 0 0 1.5rem;
|
||||
padding: 0.5rem 0 1.5rem;
|
||||
|
||||
&.selectable {
|
||||
padding: 1rem 0;
|
||||
@ -284,14 +280,4 @@
|
||||
border-top: 1px solid var(--divider-color);
|
||||
}
|
||||
}
|
||||
|
||||
.telegram-content {
|
||||
padding-bottom: 1.5rem;
|
||||
min-height: 0;
|
||||
height: 100%;
|
||||
|
||||
&.selectable {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -32,29 +32,31 @@
|
||||
const dispatch = createEventDispatcher()
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="message-row"
|
||||
class:selectable
|
||||
class:selected-row={selected}
|
||||
on:click={() => {
|
||||
dispatch('select', message)
|
||||
}}
|
||||
>
|
||||
<div class="check-box">
|
||||
{#if selectable}<CheckBox circle primary bind:checked={selected} />{/if}
|
||||
</div>
|
||||
<div class="message-container" class:out={!message.incoming}>
|
||||
<div class="message" class:outcoming={!message.incoming} class:selected>
|
||||
{#if showName}
|
||||
<div class="name" style="color: {getPlatformColorForText(message.sender)}">{formatName(message.sender)}</div>
|
||||
{/if}
|
||||
{#if attachments}
|
||||
<AttachmentList {attachments} />
|
||||
{/if}
|
||||
<div class="flex">
|
||||
<div class="caption-color mr-4"><MessageViewer message={message.content} /></div>
|
||||
<div class="time">
|
||||
{new Date(message.sendOn).toLocaleString('default', { hour: 'numeric', minute: 'numeric' })}
|
||||
<div class="message-row-bg" class:selectable class:selected-row={selected}>
|
||||
<div
|
||||
class="message-row"
|
||||
class:selectable
|
||||
class:selected-row={selected}
|
||||
on:click={() => {
|
||||
dispatch('select', message)
|
||||
}}
|
||||
>
|
||||
<div class="check-box">
|
||||
{#if selectable}<CheckBox circle primary bind:checked={selected} />{/if}
|
||||
</div>
|
||||
<div class="message-container" class:out={!message.incoming}>
|
||||
<div class="message" class:outcoming={!message.incoming} class:selected>
|
||||
{#if showName}
|
||||
<div class="name" style="color: {getPlatformColorForText(message.sender)}">{formatName(message.sender)}</div>
|
||||
{/if}
|
||||
{#if attachments}
|
||||
<AttachmentList {attachments} />
|
||||
{/if}
|
||||
<div class="flex">
|
||||
<div class="caption-color mr-4"><MessageViewer message={message.content} /></div>
|
||||
<div class="time">
|
||||
{new Date(message.sendOn).toLocaleString('default', { hour: 'numeric', minute: 'numeric' })}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -62,36 +64,51 @@
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.message-row-bg {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
}
|
||||
.message-row {
|
||||
display: flex;
|
||||
justify-content: stretch;
|
||||
align-items: center;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
max-width: 900px;
|
||||
}
|
||||
|
||||
&.selectable {
|
||||
cursor: pointer;
|
||||
.message-row.selectable,
|
||||
.message-row-bg.selectable {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--button-bg-hover);
|
||||
}
|
||||
&.selected-row {
|
||||
background-color: var(--button-bg-color);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--highlight-hover);
|
||||
background-color: var(--button-bg-hover);
|
||||
}
|
||||
&.selected-row {
|
||||
background-color: var(--highlight-select);
|
||||
}
|
||||
.message {
|
||||
cursor: pointer;
|
||||
}
|
||||
.selected {
|
||||
background-color: var(--primary-bg-color);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--highlight-hover);
|
||||
}
|
||||
}
|
||||
.message {
|
||||
cursor: pointer;
|
||||
}
|
||||
.selected {
|
||||
background-color: var(--primary-bg-color);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--primary-bg-hover);
|
||||
}
|
||||
&:hover {
|
||||
background-color: var(--primary-bg-hover);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.check-box {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
@ -69,6 +69,7 @@
|
||||
span {
|
||||
margin-left: 0.25rem;
|
||||
width: 1.6rem;
|
||||
white-space: nowrap;
|
||||
text-transform: capitalize;
|
||||
font-weight: 500;
|
||||
color: var(--theme-caption-color);
|
||||
|
@ -59,6 +59,7 @@
|
||||
span {
|
||||
margin-left: 0.25rem;
|
||||
width: 1.6rem;
|
||||
white-space: nowrap;
|
||||
text-transform: capitalize;
|
||||
font-weight: 500;
|
||||
color: var(--theme-caption-color);
|
||||
|
@ -14,11 +14,11 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import contact, { ChannelProvider, formatName } from '@anticrm/contact'
|
||||
import core, { Class, ClassifierKind, Doc, getCurrentAccount, Mixin, Obj, Ref, Space } from '@anticrm/core'
|
||||
import contact, { formatName } from '@anticrm/contact'
|
||||
import core, { Class, ClassifierKind, Doc, Mixin, Obj, Ref } from '@anticrm/core'
|
||||
import notification from '@anticrm/notification'
|
||||
import { Panel } from '@anticrm/panel'
|
||||
import { Asset, getResource, IntlString, translate } from '@anticrm/platform'
|
||||
import { Asset, getResource, translate } from '@anticrm/platform'
|
||||
import {
|
||||
AttributesBar,
|
||||
createQuery,
|
||||
@ -26,7 +26,6 @@
|
||||
getClient,
|
||||
KeyedAttribute
|
||||
} from '@anticrm/presentation'
|
||||
import setting, { IntegrationType } from '@anticrm/setting'
|
||||
import { AnyComponent, Component } from '@anticrm/ui'
|
||||
import view from '@anticrm/view'
|
||||
import { createEventDispatcher, onDestroy } from 'svelte'
|
||||
@ -36,7 +35,6 @@
|
||||
|
||||
export let _id: Ref<Doc>
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let rightSection: AnyComponent | undefined = undefined
|
||||
|
||||
let lastId: Ref<Doc> = _id
|
||||
let lastClass: Ref<Class<Doc>> = _class
|
||||
@ -237,51 +235,6 @@
|
||||
}
|
||||
let panelWidth: number = 0
|
||||
let innerWidth: number = 0
|
||||
|
||||
const accountId = getCurrentAccount()._id
|
||||
let channelProviders: ChannelProvider[] | undefined
|
||||
let currentProviders: ChannelProvider[] | undefined
|
||||
let integrations: Set<Ref<IntegrationType>> = new Set<Ref<IntegrationType>>()
|
||||
let displayedIntegrations:
|
||||
| {
|
||||
icon: Asset | undefined
|
||||
label: IntlString
|
||||
presenter: AnyComponent | undefined
|
||||
value: string
|
||||
}[]
|
||||
| undefined = []
|
||||
|
||||
client.findAll(contact.class.ChannelProvider, {}).then((res) => {
|
||||
channelProviders = res
|
||||
})
|
||||
const settingsQuery = createQuery()
|
||||
$: settingsQuery.query(
|
||||
setting.class.Integration,
|
||||
{ space: accountId as string as Ref<Space>, disabled: false },
|
||||
(res) => {
|
||||
integrations = new Set(res.map((p) => p.type))
|
||||
}
|
||||
)
|
||||
const channelsQuery = createQuery()
|
||||
$: _id &&
|
||||
integrations &&
|
||||
channelProviders &&
|
||||
channelsQuery.query(contact.class.Channel, { attachedTo: _id }, (res) => {
|
||||
const channels = res
|
||||
currentProviders = channelProviders?.filter((provider) =>
|
||||
provider.integrationType ? integrations.has(provider.integrationType as Ref<IntegrationType>) : false
|
||||
)
|
||||
displayedIntegrations = []
|
||||
currentProviders?.forEach((provider) => {
|
||||
displayedIntegrations?.push({
|
||||
icon: provider.icon,
|
||||
label: provider.label,
|
||||
presenter: provider.presenter,
|
||||
value: channels.filter((ch) => ch.provider === provider._id)[0].value
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
let minimize: boolean = false
|
||||
</script>
|
||||
|
||||
@ -295,7 +248,6 @@
|
||||
<Panel
|
||||
{icon}
|
||||
{title}
|
||||
{rightSection}
|
||||
{object}
|
||||
bind:minimize
|
||||
isHeader={false}
|
||||
@ -321,23 +273,18 @@
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
|
||||
<div class="main-editor">
|
||||
{#if mainEditor}
|
||||
<Component
|
||||
is={mainEditor}
|
||||
props={{ object }}
|
||||
on:open={(ev) => {
|
||||
ignoreKeys = ev.detail.ignoreKeys
|
||||
ignoreMixins = new Set(ev.detail.ignoreMixins)
|
||||
updateKeys()
|
||||
getMixins()
|
||||
}}
|
||||
on:click={(ev) => {
|
||||
rightSection = ev.detail.presenter
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{#if mainEditor}
|
||||
<Component
|
||||
is={mainEditor}
|
||||
props={{ object }}
|
||||
on:open={(ev) => {
|
||||
ignoreKeys = ev.detail.ignoreKeys
|
||||
ignoreMixins = new Set(ev.detail.ignoreMixins)
|
||||
updateKeys()
|
||||
getMixins()
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
{#each collectionEditors as collection}
|
||||
{#if collection.editor}
|
||||
<div class="mt-6">
|
||||
@ -357,11 +304,3 @@
|
||||
{/each}
|
||||
</Panel>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.main-editor {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
</style>
|
||||
|
@ -35,6 +35,6 @@
|
||||
mode: 'browser'
|
||||
}}
|
||||
/>
|
||||
<Scroller>
|
||||
<Scroller tableFade>
|
||||
<TableBrowser {_class} {config} {options} query={resultQuery} {baseMenuClass} showNotification />
|
||||
</Scroller>
|
||||
|
@ -27,14 +27,13 @@ test.describe('contact tests', () => {
|
||||
await page.fill('[placeholder="Location"]', 'LoPlaza')
|
||||
// Click .flex-center.icon-button
|
||||
await page.click('text=Edit profile John Appleseed LoPlaza >> button')
|
||||
await page.click('text=Edit profile John Appleseed LoPlaza >> button')
|
||||
// await page.click('button:has-text("Add social links")')
|
||||
// Click [placeholder="john\.appleseed\@apple\.com"]
|
||||
await page.click('button:has-text("Email")')
|
||||
// Fill [placeholder="john\.appleseed\@apple\.com"]
|
||||
await page.fill('[placeholder="john\\.appleseed\\@apple\\.com"]', 'wer@qwe.com')
|
||||
// Click text=Apply
|
||||
await page.click('.button.transparent')
|
||||
await page.click('button:nth-child(3)')
|
||||
})
|
||||
test('create-template', async ({ page }) => {
|
||||
// Go to http://localhost:8083/workbench%3Acomponent%3AWorkbenchApp
|
||||
|
Loading…
Reference in New Issue
Block a user