mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-22 19:11:33 +03:00
Unify Popup and Panel behaviour (#1347)
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
parent
9b9c785da5
commit
662cca9282
@ -238,7 +238,7 @@
|
|||||||
}
|
}
|
||||||
.kanban-content {
|
.kanban-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin: 1.5rem 2rem;
|
margin: 0.5rem 2rem;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,20 +15,17 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from 'svelte'
|
|
||||||
import type { IntlString } from '@anticrm/platform'
|
|
||||||
import { getClient } from '../utils'
|
|
||||||
import { Label, showPopup, Button, Tooltip } from '@anticrm/ui'
|
|
||||||
import type { TooltipAligment } from '@anticrm/ui'
|
|
||||||
import Avatar from './Avatar.svelte'
|
|
||||||
import UsersPopup from './UsersPopup.svelte'
|
|
||||||
import UserInfo from './UserInfo.svelte'
|
|
||||||
import IconPerson from './icons/Person.svelte'
|
|
||||||
|
|
||||||
import type { Ref, Class } from '@anticrm/core'
|
|
||||||
import contact, { Contact, formatName } from '@anticrm/contact'
|
import contact, { Contact, formatName } from '@anticrm/contact'
|
||||||
|
import type { Class, Ref } from '@anticrm/core'
|
||||||
|
import type { IntlString } from '@anticrm/platform'
|
||||||
|
import type { TooltipAligment } from '@anticrm/ui'
|
||||||
|
import { Button, Label, showPopup, Tooltip } from '@anticrm/ui'
|
||||||
import { createEventDispatcher } from 'svelte'
|
import { createEventDispatcher } from 'svelte'
|
||||||
import presentation from '..'
|
import presentation from '..'
|
||||||
|
import { getClient } from '../utils'
|
||||||
|
import IconPerson from './icons/Person.svelte'
|
||||||
|
import UserInfo from './UserInfo.svelte'
|
||||||
|
import UsersPopup from './UsersPopup.svelte'
|
||||||
|
|
||||||
export let _class: Ref<Class<Contact>>
|
export let _class: Ref<Class<Contact>>
|
||||||
export let label: IntlString
|
export let label: IntlString
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import contact, { Person } from '@anticrm/contact'
|
import { Person } from '@anticrm/contact'
|
||||||
import type { Class, Doc, Ref } from '@anticrm/core'
|
import type { Class, Doc, Ref } from '@anticrm/core'
|
||||||
import { IntlString } from '@anticrm/platform'
|
import { IntlString } from '@anticrm/platform'
|
||||||
import { ActionIcon, CircleButton, IconAdd, IconClose, Label, ShowMore, showPopup } from '@anticrm/ui'
|
import { ActionIcon, CircleButton, IconAdd, IconClose, Label, ShowMore, showPopup } from '@anticrm/ui'
|
||||||
@ -78,7 +78,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{#each persons as person}
|
{#each persons as person}
|
||||||
<div class="antiComponentBox flex-center margin_025 antiComponentBoxFocused">
|
<div class="antiComponentBox flex-center antiComponentBoxFocused">
|
||||||
<UserInfo value={person} size={'x-small'} />
|
<UserInfo value={person} size={'x-small'} />
|
||||||
<div class="ml-1">
|
<div class="ml-1">
|
||||||
<ActionIcon icon={IconClose} size={'small'} action={() => removePerson(person)} />
|
<ActionIcon icon={IconClose} size={'small'} action={() => removePerson(person)} />
|
||||||
@ -93,16 +93,13 @@
|
|||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.persons-container {
|
.persons-container {
|
||||||
padding: 0.5rem;
|
|
||||||
color: var(--theme-caption-color);
|
color: var(--theme-caption-color);
|
||||||
background: var(--theme-bg-accent-color);
|
|
||||||
border: 1px solid var(--theme-bg-accent-color);
|
|
||||||
border-radius: 0.75rem;
|
|
||||||
}
|
}
|
||||||
.person-items {
|
.person-items {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
display: flex;
|
display: grid;
|
||||||
flex-wrap: wrap;
|
gap: 0.25rem;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
}
|
}
|
||||||
.margin_025 {
|
.margin_025 {
|
||||||
margin: 0.25rem;
|
margin: 0.25rem;
|
||||||
|
@ -324,6 +324,7 @@ p:last-child { margin-block-end: 0; }
|
|||||||
|
|
||||||
.p-2 { padding: .5rem; }
|
.p-2 { padding: .5rem; }
|
||||||
.p-3 { padding: .75rem; }
|
.p-3 { padding: .75rem; }
|
||||||
|
.p-4 { padding: 1rem; }
|
||||||
.p-6 { padding: 1.5rem; }
|
.p-6 { padding: 1.5rem; }
|
||||||
.p-10 { padding: 2.5rem; }
|
.p-10 { padding: 2.5rem; }
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
.ad-tools {
|
.ad-tools {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
display: flex;
|
display: flex;
|
||||||
top: 1rem;
|
top: 1.375rem;
|
||||||
right: 2rem;
|
right: 2rem;
|
||||||
&.grow-reverse {
|
&.grow-reverse {
|
||||||
left: 0px;
|
left: 0px;
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
import { afterUpdate } from 'svelte'
|
import { afterUpdate } from 'svelte'
|
||||||
import { AnySvelteComponent, Spinner } from '..'
|
import { AnySvelteComponent, Spinner } from '..'
|
||||||
import { closePanel, PanelProps, panelstore } from '../panelup'
|
import { closePanel, PanelProps, panelstore } from '../panelup'
|
||||||
import { popupstore } from '../popups'
|
import { fitPopupElement, popupstore } from '../popups'
|
||||||
|
|
||||||
export let contentPanel: HTMLElement
|
export let contentPanel: HTMLElement
|
||||||
|
|
||||||
@ -55,67 +55,7 @@
|
|||||||
|
|
||||||
const fitPopup = (props: PanelProps, contentPanel: HTMLElement): void => {
|
const fitPopup = (props: PanelProps, contentPanel: HTMLElement): void => {
|
||||||
if (modalHTML) {
|
if (modalHTML) {
|
||||||
if (props.element) {
|
show = fitPopupElement(modalHTML, props.element, contentPanel)
|
||||||
show = false
|
|
||||||
modalHTML.style.left = modalHTML.style.right = modalHTML.style.top = modalHTML.style.bottom = ''
|
|
||||||
modalHTML.style.maxHeight = modalHTML.style.height = ''
|
|
||||||
if (typeof props.element !== 'string') {
|
|
||||||
const el = props.element as HTMLElement
|
|
||||||
const rect = el.getBoundingClientRect()
|
|
||||||
const rectPopup = modalHTML.getBoundingClientRect()
|
|
||||||
// Vertical
|
|
||||||
if (rect.bottom + rectPopup.height + 28 <= document.body.clientHeight) {
|
|
||||||
modalHTML.style.top = `calc(${rect.bottom}px + .75rem)`
|
|
||||||
} else if (rectPopup.height + 28 < rect.top) {
|
|
||||||
modalHTML.style.bottom = `calc(${document.body.clientHeight - rect.y}px + .75rem)`
|
|
||||||
} else {
|
|
||||||
modalHTML.style.top = modalHTML.style.bottom = '1rem'
|
|
||||||
}
|
|
||||||
|
|
||||||
// Horizontal
|
|
||||||
if (rect.left + rectPopup.width + 16 > document.body.clientWidth) {
|
|
||||||
modalHTML.style.right = document.body.clientWidth - rect.right + 'px'
|
|
||||||
} else {
|
|
||||||
modalHTML.style.left = rect.left + 'px'
|
|
||||||
}
|
|
||||||
} else if (props.element === 'right' && contentPanel !== undefined) {
|
|
||||||
const rect = contentPanel.getBoundingClientRect()
|
|
||||||
modalHTML.style.top = `calc(${rect.top}px + 0.5rem)`
|
|
||||||
modalHTML.style.bottom = '0.75rem'
|
|
||||||
modalHTML.style.right = '0.75rem'
|
|
||||||
} else if (props.element === 'top') {
|
|
||||||
modalHTML.style.top = '15vh'
|
|
||||||
modalHTML.style.left = '50%'
|
|
||||||
modalHTML.style.transform = 'translateX(-50%)'
|
|
||||||
show = true
|
|
||||||
} else if (props.element === 'account') {
|
|
||||||
modalHTML.style.bottom = '2.75rem'
|
|
||||||
modalHTML.style.left = '5rem'
|
|
||||||
} else if (props.element === 'full' && contentPanel !== undefined) {
|
|
||||||
const rect = contentPanel.getBoundingClientRect()
|
|
||||||
modalHTML.style.top = `calc(${rect.top}px + 0.5rem)`
|
|
||||||
modalHTML.style.bottom = '0.75rem'
|
|
||||||
modalHTML.style.left = '0.75rem'
|
|
||||||
modalHTML.style.right = '0.75rem'
|
|
||||||
} else if (props.element === 'content' && contentPanel !== undefined) {
|
|
||||||
const rect = contentPanel.getBoundingClientRect()
|
|
||||||
modalHTML.style.top = `calc(${rect.top}px + 0.5rem)`
|
|
||||||
modalHTML.style.bottom = '0.75rem'
|
|
||||||
modalHTML.style.left = `calc(${rect.left}px + 0.5rem)`
|
|
||||||
modalHTML.style.right = '0.75rem'
|
|
||||||
} else if (props.element === 'middle' && contentPanel !== undefined) {
|
|
||||||
const rect = contentPanel.getBoundingClientRect()
|
|
||||||
modalHTML.style.top = `calc(${rect.top}px + 0.5rem)`
|
|
||||||
modalHTML.style.bottom = '0.75rem'
|
|
||||||
modalHTML.style.left = '50%'
|
|
||||||
modalHTML.style.transform = 'translateX(-50%)'
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
modalHTML.style.top = '50%'
|
|
||||||
modalHTML.style.left = '50%'
|
|
||||||
modalHTML.style.transform = 'translate(-50%, -50%)'
|
|
||||||
show = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,14 +81,15 @@
|
|||||||
{#if !component}
|
{#if !component}
|
||||||
<Spinner />
|
<Spinner />
|
||||||
{:else}
|
{:else}
|
||||||
<div class="antiPanel panel-instance" bind:this={modalHTML}>
|
<div class="panel-instance" class:bg={props.element === 'content'} bind:this={modalHTML}>
|
||||||
|
<div class="p-2 w-full h-full">
|
||||||
<svelte:component
|
<svelte:component
|
||||||
this={component}
|
this={component}
|
||||||
bind:this={componentInstance}
|
bind:this={componentInstance}
|
||||||
_id={props._id}
|
_id={props._id}
|
||||||
_class={props._class}
|
_class={props._class}
|
||||||
rightSection={props.rightSection}
|
rightSection={props.rightSection}
|
||||||
position={props.element }
|
position={props.element}
|
||||||
on:close={_close}
|
on:close={_close}
|
||||||
on:update={() => {
|
on:update={() => {
|
||||||
if (props) {
|
if (props) {
|
||||||
@ -157,8 +98,9 @@
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
{#if props.element !== 'content'}
|
{#if props.element !== 'content'}
|
||||||
<div class="modal-overlay" class:show style={'z-index: 400'} on:click={() => escapeClose()} />
|
<div class="modal-overlay" class:show on:click={() => escapeClose()} />
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
@ -168,8 +110,12 @@
|
|||||||
z-index: 401;
|
z-index: 401;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
|
&.bg {
|
||||||
|
background-color: var(--theme-bg-color);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.modal-overlay {
|
.modal-overlay {
|
||||||
|
z-index: 400;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
@ -16,8 +16,8 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { afterUpdate } from 'svelte'
|
import { afterUpdate } from 'svelte'
|
||||||
import { fade } from 'svelte/transition'
|
import { fitPopupElement } from '../popups'
|
||||||
import type { AnySvelteComponent, AnyComponent, PopupAlignment } from '../types'
|
import type { AnyComponent, AnySvelteComponent, PopupAlignment } from '../types'
|
||||||
|
|
||||||
export let is: AnyComponent | AnySvelteComponent
|
export let is: AnyComponent | AnySvelteComponent
|
||||||
export let props: object
|
export let props: object
|
||||||
@ -51,53 +51,7 @@
|
|||||||
|
|
||||||
const fitPopup = (): void => {
|
const fitPopup = (): void => {
|
||||||
if (modalHTML) {
|
if (modalHTML) {
|
||||||
if (element) {
|
show = fitPopupElement(modalHTML, element)
|
||||||
show = false
|
|
||||||
modalHTML.style.left = modalHTML.style.right = modalHTML.style.top = modalHTML.style.bottom = ''
|
|
||||||
modalHTML.style.maxHeight = modalHTML.style.height = ''
|
|
||||||
if (typeof element !== 'string') {
|
|
||||||
const el = element as HTMLElement
|
|
||||||
const rect = el.getBoundingClientRect()
|
|
||||||
const rectPopup = modalHTML.getBoundingClientRect()
|
|
||||||
// Vertical
|
|
||||||
if (rect.bottom + rectPopup.height + 28 <= document.body.clientHeight) {
|
|
||||||
modalHTML.style.top = `calc(${rect.bottom}px + 1px)`
|
|
||||||
} else if (rectPopup.height + 28 < rect.top) {
|
|
||||||
modalHTML.style.bottom = `calc(${document.body.clientHeight - rect.y}px + 1px)`
|
|
||||||
} else {
|
|
||||||
modalHTML.style.top = modalHTML.style.bottom = '1rem'
|
|
||||||
}
|
|
||||||
|
|
||||||
// Horizontal
|
|
||||||
if (rect.left + rectPopup.width + 16 > document.body.clientWidth) {
|
|
||||||
modalHTML.style.right = document.body.clientWidth - rect.right + 'px'
|
|
||||||
} else {
|
|
||||||
modalHTML.style.left = rect.left + 'px'
|
|
||||||
}
|
|
||||||
} else if (element === 'right') {
|
|
||||||
modalHTML.style.top = '0'
|
|
||||||
modalHTML.style.bottom = '0'
|
|
||||||
modalHTML.style.right = '0'
|
|
||||||
} else if (element === 'top') {
|
|
||||||
modalHTML.style.top = '15vh'
|
|
||||||
modalHTML.style.left = '50%'
|
|
||||||
modalHTML.style.transform = 'translateX(-50%)'
|
|
||||||
show = true
|
|
||||||
} else if (element === 'account') {
|
|
||||||
modalHTML.style.bottom = '2.75rem'
|
|
||||||
modalHTML.style.left = '5rem'
|
|
||||||
} else if (element === 'full') {
|
|
||||||
modalHTML.style.top = '0'
|
|
||||||
modalHTML.style.bottom = '0'
|
|
||||||
modalHTML.style.left = '0'
|
|
||||||
modalHTML.style.right = '0'
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
modalHTML.style.top = '50%'
|
|
||||||
modalHTML.style.left = '50%'
|
|
||||||
modalHTML.style.transform = 'translate(-50%, -50%)'
|
|
||||||
show = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,12 +109,12 @@
|
|||||||
}
|
}
|
||||||
if (value !== null && value !== undefined) dateToEdits()
|
if (value !== null && value !== undefined) dateToEdits()
|
||||||
else if (value === null) {
|
else if (value === null) {
|
||||||
edits.map(edit => edit.value = -1)
|
edits.forEach((edit) => { edit.value = -1 })
|
||||||
currentDate = today
|
currentDate = today
|
||||||
}
|
}
|
||||||
|
|
||||||
const fixEdits = (): void => {
|
const fixEdits = (): void => {
|
||||||
let tempValues: number[] = []
|
const tempValues: number[] = []
|
||||||
edits.forEach((edit, i) => {
|
edits.forEach((edit, i) => {
|
||||||
tempValues[i] = edit.value > 0 ? edit.value : getValue(currentDate, edit.id)
|
tempValues[i] = edit.value > 0 ? edit.value : getValue(currentDate, edit.id)
|
||||||
})
|
})
|
||||||
@ -139,7 +139,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const keyDown = (ev: KeyboardEvent, ed: TEdits): void => {
|
const keyDown = (ev: KeyboardEvent, ed: TEdits): void => {
|
||||||
const target = ev.target as HTMLElement
|
|
||||||
const index = getIndex(ed)
|
const index = getIndex(ed)
|
||||||
|
|
||||||
if (ev.key >= '0' && ev.key <= '9') {
|
if (ev.key >= '0' && ev.key <= '9') {
|
||||||
@ -175,9 +174,9 @@
|
|||||||
edits[index].value = -1
|
edits[index].value = -1
|
||||||
startTyping = true
|
startTyping = true
|
||||||
}
|
}
|
||||||
if (ev.code === 'ArrowUp' || ev.code === 'ArrowDown' && edits[index].el) {
|
if (ev.code === 'ArrowUp' || (ev.code === 'ArrowDown' && edits[index].el)) {
|
||||||
if (edits[index].value !== -1) {
|
if (edits[index].value !== -1) {
|
||||||
let val = (ev.code === 'ArrowUp')
|
const val = (ev.code === 'ArrowUp')
|
||||||
? edits[index].value + 1
|
? edits[index].value + 1
|
||||||
: edits[index].value - 1
|
: edits[index].value - 1
|
||||||
if (currentDate) {
|
if (currentDate) {
|
||||||
@ -243,7 +242,7 @@
|
|||||||
let popupComp: HTMLElement
|
let popupComp: HTMLElement
|
||||||
$: if (opened && $dpstore.popup) popupComp = $dpstore.popup
|
$: if (opened && $dpstore.popup) popupComp = $dpstore.popup
|
||||||
$: if (opened && edits[0].el && $dpstore.frendlyFocus === undefined) {
|
$: if (opened && edits[0].el && $dpstore.frendlyFocus === undefined) {
|
||||||
let frendlyFocus: HTMLElement[] = []
|
const frendlyFocus: HTMLElement[] = []
|
||||||
edits.forEach((edit, i) => {
|
edits.forEach((edit, i) => {
|
||||||
if (edit.el) frendlyFocus[i] = edit.el
|
if (edit.el) frendlyFocus[i] = edit.el
|
||||||
})
|
})
|
||||||
@ -318,7 +317,7 @@
|
|||||||
selected = 'day'
|
selected = 'day'
|
||||||
startTyping = true
|
startTyping = true
|
||||||
value = null
|
value = null
|
||||||
edits.forEach(edit => edit.value = -1)
|
edits.forEach(edit => { edit.value = -1 })
|
||||||
if (edits[0].el) edits[0].el.focus()
|
if (edits[0].el) edits[0].el.focus()
|
||||||
}}
|
}}
|
||||||
on:blur={(ev) => unfocus(ev, closeBtn)}
|
on:blur={(ev) => unfocus(ev, closeBtn)}
|
||||||
|
@ -73,8 +73,12 @@ export function isWeekend (date: Date): boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getMonthName (date: Date, option: 'narrow' | 'short' | 'long' | 'numeric' | '2-digit' = 'long'): string {
|
export function getMonthName (date: Date, option: 'narrow' | 'short' | 'long' | 'numeric' | '2-digit' = 'long'): string {
|
||||||
|
try {
|
||||||
const locale = new Intl.NumberFormat().resolvedOptions().locale
|
const locale = new Intl.NumberFormat().resolvedOptions().locale
|
||||||
return new Intl.DateTimeFormat(locale, { month: option }).format(date)
|
return new Intl.DateTimeFormat(locale, { month: option }).format(date)
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TCellStyle = 'not-selected' | 'selected'
|
export type TCellStyle = 'not-selected' | 'selected'
|
||||||
|
@ -16,12 +16,26 @@ let currentLocation: string | undefined
|
|||||||
location.subscribe((loc) => {
|
location.subscribe((loc) => {
|
||||||
if (loc.fragment !== currentLocation && loc.fragment !== undefined && loc.fragment.trim().length > 0) {
|
if (loc.fragment !== currentLocation && loc.fragment !== undefined && loc.fragment.trim().length > 0) {
|
||||||
const props = decodeURIComponent(loc.fragment).split('|')
|
const props = decodeURIComponent(loc.fragment).split('|')
|
||||||
showPanel(props[0] as AnyComponent, props[1], props[2], 'full')
|
showPanel(props[0] as AnyComponent, props[1], props[2], (props[3] ?? undefined) as PopupAlignment)
|
||||||
} else if ((loc.fragment === undefined || (loc.fragment !== undefined && loc.fragment.trim().length === 0)) && currentLocation !== undefined) {
|
} else if ((loc.fragment === undefined || (loc.fragment !== undefined && loc.fragment.trim().length === 0)) && currentLocation !== undefined) {
|
||||||
closePanel()
|
closePanel()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export function getPanelURI (
|
||||||
|
component: AnyComponent,
|
||||||
|
_id: string,
|
||||||
|
_class: string,
|
||||||
|
element?: PopupAlignment,
|
||||||
|
rightSection?: AnyComponent
|
||||||
|
): string {
|
||||||
|
const panelProps = [component, _id, _class]
|
||||||
|
if (typeof element === 'string') {
|
||||||
|
panelProps.push(element)
|
||||||
|
}
|
||||||
|
return encodeURIComponent(panelProps.join('|'))
|
||||||
|
}
|
||||||
|
|
||||||
export function showPanel (
|
export function showPanel (
|
||||||
component: AnyComponent,
|
component: AnyComponent,
|
||||||
_id: string,
|
_id: string,
|
||||||
@ -29,7 +43,7 @@ export function showPanel (
|
|||||||
element?: PopupAlignment,
|
element?: PopupAlignment,
|
||||||
rightSection?: AnyComponent
|
rightSection?: AnyComponent
|
||||||
): void {
|
): void {
|
||||||
const newLoc = encodeURIComponent([component, _id, _class].join('|'))
|
const newLoc = getPanelURI(component, _id, _class, element)
|
||||||
if (currentLocation === newLoc) {
|
if (currentLocation === newLoc) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -103,3 +103,82 @@ export function closeDatePopup (): void {
|
|||||||
onChange: undefined
|
onChange: undefined
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*
|
||||||
|
* Place element based on position and underline content element.
|
||||||
|
*
|
||||||
|
* return boolean to show or not modal overlay.
|
||||||
|
*/
|
||||||
|
export function fitPopupElement (modalHTML: HTMLElement, element?: PopupAlignment, contentPanel?: HTMLElement): boolean {
|
||||||
|
let show = true
|
||||||
|
if (element != null) {
|
||||||
|
show = false
|
||||||
|
modalHTML.style.left = modalHTML.style.right = modalHTML.style.top = modalHTML.style.bottom = ''
|
||||||
|
modalHTML.style.maxHeight = modalHTML.style.height = ''
|
||||||
|
if (typeof element !== 'string') {
|
||||||
|
const el = element as HTMLElement
|
||||||
|
const rect = el.getBoundingClientRect()
|
||||||
|
const rectPopup = modalHTML.getBoundingClientRect()
|
||||||
|
// Vertical
|
||||||
|
if (rect.bottom + rectPopup.height + 28 <= document.body.clientHeight) {
|
||||||
|
modalHTML.style.top = `calc(${rect.bottom}px + .75rem)`
|
||||||
|
} else if (rectPopup.height + 28 < rect.top) {
|
||||||
|
modalHTML.style.bottom = `calc(${document.body.clientHeight - rect.y}px + .75rem)`
|
||||||
|
} else {
|
||||||
|
modalHTML.style.top = modalHTML.style.bottom = '1rem'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Horizontal
|
||||||
|
if (rect.left + rectPopup.width + 16 > document.body.clientWidth) {
|
||||||
|
modalHTML.style.right = `${document.body.clientWidth - rect.right}px`
|
||||||
|
} else {
|
||||||
|
modalHTML.style.left = `${rect.left}px`
|
||||||
|
}
|
||||||
|
} else if (element === 'right' && contentPanel !== undefined) {
|
||||||
|
const rect = contentPanel.getBoundingClientRect()
|
||||||
|
modalHTML.style.top = `calc(${rect.top}px + 0.5rem)`
|
||||||
|
modalHTML.style.bottom = '0.75rem'
|
||||||
|
modalHTML.style.right = '0.75rem'
|
||||||
|
show = true
|
||||||
|
} else if (element === 'top') {
|
||||||
|
modalHTML.style.top = '15vh'
|
||||||
|
modalHTML.style.left = '50%'
|
||||||
|
modalHTML.style.transform = 'translateX(-50%)'
|
||||||
|
show = true
|
||||||
|
} else if (element === 'account') {
|
||||||
|
modalHTML.style.bottom = '2.75rem'
|
||||||
|
modalHTML.style.left = '5rem'
|
||||||
|
} else if (element === 'full' && contentPanel !== undefined) {
|
||||||
|
const rect = contentPanel.getBoundingClientRect()
|
||||||
|
modalHTML.style.top = `calc(${rect.top}px + 0.25rem)`
|
||||||
|
modalHTML.style.bottom = '0.25rem'
|
||||||
|
modalHTML.style.left = '0.25rem'
|
||||||
|
modalHTML.style.right = '0.25rem'
|
||||||
|
show = true
|
||||||
|
} else if (element === 'content' && contentPanel !== undefined) {
|
||||||
|
const rect = contentPanel.getBoundingClientRect()
|
||||||
|
modalHTML.style.top = `calc(${rect.top}px)`
|
||||||
|
modalHTML.style.height = `${rect.height}px`
|
||||||
|
modalHTML.style.left = `calc(${rect.left}px)`
|
||||||
|
modalHTML.style.width = `${rect.width}px`
|
||||||
|
} else if (element === 'middle') {
|
||||||
|
if (contentPanel !== undefined) {
|
||||||
|
const rect = contentPanel.getBoundingClientRect()
|
||||||
|
modalHTML.style.top = `calc(${rect.top}px)`
|
||||||
|
} else {
|
||||||
|
modalHTML.style.top = '15%'
|
||||||
|
}
|
||||||
|
modalHTML.style.bottom = '0.75rem'
|
||||||
|
modalHTML.style.left = '50%'
|
||||||
|
modalHTML.style.transform = 'translateX(-50%)'
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
modalHTML.style.top = '50%'
|
||||||
|
modalHTML.style.left = '50%'
|
||||||
|
modalHTML.style.transform = 'translate(-50%, -50%)'
|
||||||
|
show = true
|
||||||
|
}
|
||||||
|
return show
|
||||||
|
}
|
||||||
|
@ -14,11 +14,11 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
// import type { Metadata } from '@anticrm/platform'
|
// import type { Metadata } from '@anticrm/platform'
|
||||||
import { setMetadata } from '@anticrm/platform'
|
|
||||||
import type { Metadata } from '@anticrm/platform'
|
import type { Metadata } from '@anticrm/platform'
|
||||||
|
import { setMetadata } from '@anticrm/platform'
|
||||||
|
|
||||||
export function setMetadataLocalStorage(id: Metadata<string>, value: string | null): void {
|
export function setMetadataLocalStorage (id: Metadata<string>, value: string | null): void {
|
||||||
if (value) {
|
if (value != null) {
|
||||||
localStorage.setItem(id, value)
|
localStorage.setItem(id, value)
|
||||||
} else {
|
} else {
|
||||||
localStorage.removeItem(id)
|
localStorage.removeItem(id)
|
||||||
@ -26,95 +26,10 @@ export function setMetadataLocalStorage(id: Metadata<string>, value: string | nu
|
|||||||
setMetadata(id, value)
|
setMetadata(id, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fetchMetadataLocalStorage(id: Metadata<string>): string | null {
|
export function fetchMetadataLocalStorage (id: Metadata<string>): string | null {
|
||||||
const value = localStorage.getItem(id)
|
const value = localStorage.getItem(id)
|
||||||
if (value !== null) {
|
if (value !== null) {
|
||||||
setMetadata(id, value)
|
setMetadata(id, value)
|
||||||
}
|
}
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
// import { Readable, derived, writable } from 'svelte/store'
|
|
||||||
// import { onDestroy, getContext, setContext } from 'svelte'
|
|
||||||
|
|
||||||
// function windowLocation (): Location {
|
|
||||||
// return parseLocation(window.location)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const locationWritable = writable(windowLocation())
|
|
||||||
// window.addEventListener('popstate', () => {
|
|
||||||
// locationWritable.set(windowLocation())
|
|
||||||
// })
|
|
||||||
|
|
||||||
// const location: Readable<Location> = derived(locationWritable, (loc) => loc)
|
|
||||||
|
|
||||||
// function subscribeLocation (listener: (location: Location) => void, destroyFactory: (op: () => void) => void): void {
|
|
||||||
// const unsubscribe = location.subscribe((location) => {
|
|
||||||
// listener(location)
|
|
||||||
// })
|
|
||||||
// destroyFactory(unsubscribe)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// function navigate (newUrl: string): void {
|
|
||||||
// const curUrl = locationToUrl(windowLocation())
|
|
||||||
// if (curUrl === newUrl) {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// history.pushState(null, '', newUrl)
|
|
||||||
// locationWritable.set(windowLocation())
|
|
||||||
// }
|
|
||||||
|
|
||||||
// function navigateJoin (
|
|
||||||
// path: string[] | undefined,
|
|
||||||
// query: Record<string, string> | undefined,
|
|
||||||
// fragment: string | undefined
|
|
||||||
// ): void {
|
|
||||||
// const newLocation = windowLocation()
|
|
||||||
// if (path != null) {
|
|
||||||
// newLocation.path = path
|
|
||||||
// }
|
|
||||||
// if (query != null) {
|
|
||||||
// // For query we do replace
|
|
||||||
// const currentQuery = newLocation.query || {}
|
|
||||||
// for (const kv of Object.entries(query)) {
|
|
||||||
// currentQuery[kv[0]] = kv[1]
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if (fragment) {
|
|
||||||
// newLocation.fragment = fragment
|
|
||||||
// }
|
|
||||||
// navigate(locationToUrl(newLocation))
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const CONTEXT_ROUTE_VALUE = 'routes.context'
|
|
||||||
|
|
||||||
// export function newRouter<T> (
|
|
||||||
// pattern: string,
|
|
||||||
// matcher: (match: T) => void,
|
|
||||||
// defaults: T | undefined = undefined
|
|
||||||
// ): ApplicationRouter<T> {
|
|
||||||
// const r: Router<any> = getContext(CONTEXT_ROUTE_VALUE)
|
|
||||||
// const navigateOp = (loc: Location): void => {
|
|
||||||
// navigate(locationToUrl(loc))
|
|
||||||
// }
|
|
||||||
// const result = r ? r.newRouter<T>(pattern, defaults) : new Router<T>(pattern, r, defaults, navigateOp)
|
|
||||||
// result.subscribe(matcher)
|
|
||||||
// if (!r) {
|
|
||||||
// // No parent, we need to subscribe for location changes.
|
|
||||||
// subscribeLocation((loc) => {
|
|
||||||
// result.update(loc)
|
|
||||||
// }, onDestroy)
|
|
||||||
// }
|
|
||||||
// if (r) {
|
|
||||||
// // We need to remove child router from parent, if component is destroyed
|
|
||||||
// onDestroy(() => r.clearChildRouter())
|
|
||||||
// }
|
|
||||||
// setContext(CONTEXT_ROUTE_VALUE, result)
|
|
||||||
// return result
|
|
||||||
// }
|
|
||||||
|
|
||||||
// R O U T E R M E T A D A T A K E Y S
|
|
||||||
|
|
||||||
// export function applicationShortcutKey (shortcut: string): Metadata<AnyComponent> {
|
|
||||||
// return ('shortcut:ui.' + shortcut) as Metadata<AnyComponent>
|
|
||||||
// }
|
|
||||||
|
@ -15,24 +15,20 @@
|
|||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Card } from '@anticrm/board'
|
import type { Card } from '@anticrm/board'
|
||||||
import { Icon, showPanel } from '@anticrm/ui'
|
import { getPanelURI, Icon } from '@anticrm/ui'
|
||||||
import view from '@anticrm/view'
|
import view from '@anticrm/view'
|
||||||
import board from '../plugin'
|
import board from '../plugin'
|
||||||
|
|
||||||
export let value: Card
|
export let value: Card
|
||||||
export let inline: boolean = false
|
export let inline: boolean = false
|
||||||
|
|
||||||
async function show () {
|
|
||||||
showPanel(board.component.EditCard, value._id, value._class, 'middle')
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if value}
|
{#if value}
|
||||||
<a
|
<a
|
||||||
class="flex-presenter"
|
class="flex-presenter"
|
||||||
class:inline-presenter={inline}
|
class:inline-presenter={inline}
|
||||||
href="#{encodeURIComponent([view.component.EditDoc, value._id, value._class].join('|'))}"
|
href="#{getPanelURI(view.component.EditDoc, value._id, value._class, 'full')}"
|
||||||
on:click={show}
|
|
||||||
>
|
>
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
<Icon icon={board.icon.Card} size={'small'} />
|
<Icon icon={board.icon.Card} size={'small'} />
|
||||||
|
@ -15,24 +15,20 @@
|
|||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Organization } from '@anticrm/contact'
|
import { Organization } from '@anticrm/contact'
|
||||||
import { showPanel } from '@anticrm/ui'
|
import { getPanelURI } from '@anticrm/ui'
|
||||||
import view from '@anticrm/view'
|
import view from '@anticrm/view'
|
||||||
import Company from './icons/Company.svelte'
|
import Company from './icons/Company.svelte'
|
||||||
|
|
||||||
export let value: Organization
|
export let value: Organization
|
||||||
export let inline: boolean = false
|
export let inline: boolean = false
|
||||||
|
|
||||||
async function onClick () {
|
|
||||||
showPanel(view.component.EditDoc, value._id, value._class, 'full')
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if value}
|
{#if value}
|
||||||
<a
|
<a
|
||||||
class="flex-presenter"
|
class="flex-presenter"
|
||||||
class:inline-presenter={inline}
|
class:inline-presenter={inline}
|
||||||
href="#{encodeURIComponent([view.component.EditDoc, value._id, value._class].join('|'))}"
|
href="#{getPanelURI(view.component.EditDoc, value._id, value._class, 'full')}"
|
||||||
on:click={onClick}
|
|
||||||
>
|
>
|
||||||
<div class="icon circle"><Company size={'small'} /></div>
|
<div class="icon circle"><Company size={'small'} /></div>
|
||||||
<span class="label">{value.name}</span>
|
<span class="label">{value.name}</span>
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
import { formatName, Person } from '@anticrm/contact'
|
import { formatName, Person } from '@anticrm/contact'
|
||||||
import { Hierarchy } from '@anticrm/core'
|
import { Hierarchy } from '@anticrm/core'
|
||||||
import { Avatar } from '@anticrm/presentation'
|
import { Avatar } from '@anticrm/presentation'
|
||||||
import { showPanel } from '@anticrm/ui'
|
import { getPanelURI } from '@anticrm/ui'
|
||||||
import view from '@anticrm/view'
|
import view from '@anticrm/view'
|
||||||
|
|
||||||
export let value: Person | undefined
|
export let value: Person | undefined
|
||||||
@ -25,12 +25,6 @@
|
|||||||
export let shouldShowPlaceholder = false
|
export let shouldShowPlaceholder = false
|
||||||
|
|
||||||
const avatarSize = 'x-small'
|
const avatarSize = 'x-small'
|
||||||
|
|
||||||
const onClick = async () => {
|
|
||||||
if (value) {
|
|
||||||
showPanel(view.component.EditDoc, value._id, Hierarchy.mixinOrClass(value), 'full')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if value || shouldShowPlaceholder}
|
{#if value || shouldShowPlaceholder}
|
||||||
@ -38,8 +32,7 @@
|
|||||||
<a
|
<a
|
||||||
class="flex-presenter"
|
class="flex-presenter"
|
||||||
class:inline-presenter={inline}
|
class:inline-presenter={inline}
|
||||||
href="#{encodeURIComponent([view.component.EditDoc, value._id, Hierarchy.mixinOrClass(value)].join('|'))}"
|
href="#{getPanelURI(view.component.EditDoc, value._id, Hierarchy.mixinOrClass(value), 'full')}"
|
||||||
on:click={onClick}
|
|
||||||
>
|
>
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
<Avatar size={avatarSize} avatar={value?.avatar} />
|
<Avatar size={avatarSize} avatar={value?.avatar} />
|
||||||
|
@ -15,25 +15,19 @@
|
|||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Product } from '@anticrm/inventory'
|
import { Product } from '@anticrm/inventory'
|
||||||
import { Icon, showPanel } from '@anticrm/ui'
|
import { getPanelURI, Icon } from '@anticrm/ui'
|
||||||
import view from '@anticrm/view'
|
import view from '@anticrm/view'
|
||||||
import inventory from '../plugin'
|
import inventory from '../plugin'
|
||||||
|
|
||||||
export let value: Product
|
export let value: Product
|
||||||
export let inline: boolean = false
|
export let inline: boolean = false
|
||||||
|
|
||||||
|
|
||||||
function show () {
|
|
||||||
showPanel(view.component.EditDoc, value._id, value._class, 'full')
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if value}
|
{#if value}
|
||||||
<a
|
<a
|
||||||
class="flex-presenter"
|
class="flex-presenter"
|
||||||
class:inline-presenter={inline}
|
class:inline-presenter={inline}
|
||||||
href="#{encodeURIComponent([view.component.EditDoc, value._id, value._class].join('|'))}"
|
href="#{getPanelURI(view.component.EditDoc, value._id, value._class, 'full')}"
|
||||||
on:click={show}
|
|
||||||
>
|
>
|
||||||
<div class="icon"><Icon icon={inventory.icon.Products} size={'small'} /></div>
|
<div class="icon"><Icon icon={inventory.icon.Products} size={'small'} /></div>
|
||||||
<span class="label">{value.name}</span>
|
<span class="label">{value.name}</span>
|
||||||
|
@ -15,24 +15,19 @@
|
|||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Lead } from '@anticrm/lead'
|
import type { Lead } from '@anticrm/lead'
|
||||||
import { Icon, showPanel } from '@anticrm/ui'
|
import { getPanelURI, Icon } from '@anticrm/ui'
|
||||||
import view from '@anticrm/view'
|
import view from '@anticrm/view'
|
||||||
import lead from '../plugin'
|
import lead from '../plugin'
|
||||||
|
|
||||||
export let value: Lead
|
export let value: Lead
|
||||||
export let inline: boolean = false
|
export let inline: boolean = false
|
||||||
|
|
||||||
async function show () {
|
|
||||||
showPanel(view.component.EditDoc, value._id, value._class, 'full')
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if value}
|
{#if value}
|
||||||
<a
|
<a
|
||||||
class="flex-presenter"
|
class="flex-presenter"
|
||||||
class:inline-presenter={inline}
|
class:inline-presenter={inline}
|
||||||
href="#{encodeURIComponent([view.component.EditDoc, value._id, value._class].join('|'))}"
|
href="#{getPanelURI(view.component.EditDoc, value._id, value._class, 'full')}"
|
||||||
on:click={show}
|
|
||||||
>
|
>
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
<Icon icon={lead.icon.Lead} size={'small'} />
|
<Icon icon={lead.icon.Lead} size={'small'} />
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
import type { Applicant } from '@anticrm/recruit'
|
import type { Applicant } from '@anticrm/recruit'
|
||||||
import recruit from '@anticrm/recruit'
|
import recruit from '@anticrm/recruit'
|
||||||
import { Icon, Label } from '@anticrm/ui'
|
import { Icon, Label } from '@anticrm/ui'
|
||||||
import { showPanel } from '@anticrm/ui/src/panelup'
|
import { getPanelURI } from '@anticrm/ui/src/panelup'
|
||||||
import view from '@anticrm/view'
|
import view from '@anticrm/view'
|
||||||
|
|
||||||
export let value: Applicant
|
export let value: Applicant
|
||||||
@ -26,18 +26,13 @@
|
|||||||
|
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
const shortLabel = client.getHierarchy().getClass(value._class).shortLabel
|
const shortLabel = client.getHierarchy().getClass(value._class).shortLabel
|
||||||
|
|
||||||
function show () {
|
|
||||||
showPanel(view.component.EditDoc, value._id, value._class, 'full')
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if value && shortLabel}
|
{#if value && shortLabel}
|
||||||
<a
|
<a
|
||||||
class="flex-presenter"
|
class="flex-presenter"
|
||||||
class:inline-presenter={inline}
|
class:inline-presenter={inline}
|
||||||
href="#{encodeURIComponent([view.component.EditDoc, value._id, value._class].join('|'))}"
|
href="#{getPanelURI(view.component.EditDoc, value._id, value._class, 'full')}"
|
||||||
on:click={show}
|
|
||||||
>
|
>
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
<Icon icon={recruit.icon.Application} size={'small'} />
|
<Icon icon={recruit.icon.Application} size={'small'} />
|
||||||
|
@ -15,24 +15,19 @@
|
|||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Vacancy } from '@anticrm/recruit'
|
import type { Vacancy } from '@anticrm/recruit'
|
||||||
import recruit from '../plugin'
|
|
||||||
import { Icon } from '@anticrm/ui'
|
import { Icon } from '@anticrm/ui'
|
||||||
import { showPanel } from '@anticrm/ui/src/panelup'
|
import { getPanelURI } from '@anticrm/ui/src/panelup'
|
||||||
|
import recruit from '../plugin'
|
||||||
|
|
||||||
export let value: Vacancy
|
export let value: Vacancy
|
||||||
export let inline: boolean = false
|
export let inline: boolean = false
|
||||||
|
|
||||||
function show () {
|
|
||||||
showPanel(recruit.component.EditVacancy, value._id, value._class, 'right')
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if value}
|
{#if value}
|
||||||
<a
|
<a
|
||||||
class="flex-presenter"
|
class="flex-presenter"
|
||||||
class:inline-presenter={inline}
|
class:inline-presenter={inline}
|
||||||
href="#{encodeURIComponent([recruit.component.EditVacancy, value._id, value._class].join('|'))}"
|
href="#{getPanelURI(recruit.component.EditVacancy, value._id, value._class, 'right')}"
|
||||||
on:click={show}
|
|
||||||
>
|
>
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
<Icon icon={recruit.icon.Vacancy} size={'small'} />
|
<Icon icon={recruit.icon.Vacancy} size={'small'} />
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
import { OrganizationSelector } from '@anticrm/contact-resources'
|
import { OrganizationSelector } from '@anticrm/contact-resources'
|
||||||
import { Account, Class, Client, Doc, generateId, getCurrentAccount, Ref } from '@anticrm/core'
|
import { Account, Class, Client, Doc, generateId, getCurrentAccount, Ref } from '@anticrm/core'
|
||||||
import { getResource, OK, Resource, Severity, Status } from '@anticrm/platform'
|
import { getResource, OK, Resource, Severity, Status } from '@anticrm/platform'
|
||||||
import { Card, getClient, UserBox } from '@anticrm/presentation'
|
import { Card, getClient, UserBox, UserBoxList } from '@anticrm/presentation'
|
||||||
import type { Candidate, Review } from '@anticrm/recruit'
|
import type { Candidate, Review } from '@anticrm/recruit'
|
||||||
import task, { SpaceWithStates } from '@anticrm/task'
|
import task, { SpaceWithStates } from '@anticrm/task'
|
||||||
import { StyledTextBox } from '@anticrm/text-editor'
|
import { StyledTextBox } from '@anticrm/text-editor'
|
||||||
@ -26,6 +26,7 @@
|
|||||||
import view from '@anticrm/view'
|
import view from '@anticrm/view'
|
||||||
import { createEventDispatcher } from 'svelte'
|
import { createEventDispatcher } from 'svelte'
|
||||||
import recruit from '../../plugin'
|
import recruit from '../../plugin'
|
||||||
|
import calendar from '@anticrm/calendar'
|
||||||
|
|
||||||
export let space: Ref<SpaceWithStates>
|
export let space: Ref<SpaceWithStates>
|
||||||
export let candidate: Ref<Person>
|
export let candidate: Ref<Person>
|
||||||
@ -166,18 +167,43 @@
|
|||||||
label={recruit.string.Candidate}
|
label={recruit.string.Candidate}
|
||||||
placeholder={recruit.string.Candidates}
|
placeholder={recruit.string.Candidates}
|
||||||
bind:value={doc.attachedTo}
|
bind:value={doc.attachedTo}
|
||||||
kind={'link'} size={'x-large'} justify={'left'} width={'100%'} labelDirection={'left'}
|
kind={'link'}
|
||||||
|
size={'x-large'}
|
||||||
|
justify={'left'}
|
||||||
|
width={'100%'}
|
||||||
|
labelDirection={'left'}
|
||||||
/>
|
/>
|
||||||
{:else}
|
{:else}
|
||||||
<div></div>
|
<div />
|
||||||
{/if}
|
{/if}
|
||||||
<EditBox label={recruit.string.Location} icon={recruit.icon.Location} bind:value={location} maxWidth={'13rem'} />
|
<EditBox label={recruit.string.Location} icon={recruit.icon.Location} bind:value={location} maxWidth={'13rem'} />
|
||||||
<OrganizationSelector
|
<OrganizationSelector
|
||||||
bind:value={company} label={recruit.string.Company}
|
bind:value={company}
|
||||||
kind={'link'} size={'x-large'} justify={'left'} width={'100%'} labelDirection={'left'}
|
label={recruit.string.Company}
|
||||||
|
kind={'link'}
|
||||||
|
size={'x-large'}
|
||||||
|
justify={'left'}
|
||||||
|
width={'100%'}
|
||||||
|
labelDirection={'left'}
|
||||||
/>
|
/>
|
||||||
<DateRangePicker title={recruit.string.StartDate} bind:value={startDate} withTime on:change={updateStart} />
|
<DateRangePicker title={recruit.string.StartDate} bind:value={startDate} withTime on:change={updateStart} />
|
||||||
<DateRangePicker title={recruit.string.DueDate} bind:value={dueDate} withTime />
|
<DateRangePicker title={recruit.string.DueDate} bind:value={dueDate} withTime />
|
||||||
|
|
||||||
|
<Row>
|
||||||
|
<UserBoxList
|
||||||
|
_class={contact.class.Employee}
|
||||||
|
items={doc.participants}
|
||||||
|
title={calendar.string.Participants}
|
||||||
|
on:open={(evt) => {
|
||||||
|
doc.participants = [...(doc.participants ?? []), evt.detail._id]
|
||||||
|
}}
|
||||||
|
on:delete={(evt) => {
|
||||||
|
doc.participants = doc.participants?.filter((it) => it !== evt.detail._id) ?? [currentUser.employee]
|
||||||
|
}}
|
||||||
|
noItems={calendar.string.NoParticipants}
|
||||||
|
/>
|
||||||
|
</Row>
|
||||||
|
|
||||||
<Row>
|
<Row>
|
||||||
<StyledTextBox
|
<StyledTextBox
|
||||||
emphasized
|
emphasized
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
import { getClient } from '@anticrm/presentation'
|
import { getClient } from '@anticrm/presentation'
|
||||||
import type { Review } from '@anticrm/recruit'
|
import type { Review } from '@anticrm/recruit'
|
||||||
import recruit from '@anticrm/recruit'
|
import recruit from '@anticrm/recruit'
|
||||||
import { closeTooltip, Icon, showPanel } from '@anticrm/ui'
|
import { getPanelURI, Icon } from '@anticrm/ui'
|
||||||
import view from '@anticrm/view'
|
import view from '@anticrm/view'
|
||||||
|
|
||||||
export let value: Review
|
export let value: Review
|
||||||
@ -34,19 +34,13 @@
|
|||||||
shortLabel = r
|
shortLabel = r
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function show () {
|
|
||||||
closeTooltip()
|
|
||||||
showPanel(view.component.EditDoc, value._id, value._class, 'right')
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if value && shortLabel}
|
{#if value && shortLabel}
|
||||||
<a
|
<a
|
||||||
class="flex-presenter"
|
class="flex-presenter"
|
||||||
class:inline-presenter={inline}
|
class:inline-presenter={inline}
|
||||||
href="#{encodeURIComponent([view.component.EditDoc, value._id, value._class].join('|'))}"
|
href="#{getPanelURI(view.component.EditDoc, value._id, value._class, 'right')}"
|
||||||
on:click={show}
|
|
||||||
>
|
>
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
<Icon icon={recruit.icon.Application} size={'small'} />
|
<Icon icon={recruit.icon.Application} size={'small'} />
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { getClient } from '@anticrm/presentation'
|
import { getClient } from '@anticrm/presentation'
|
||||||
import type { Issue } from '@anticrm/task'
|
import type { Issue } from '@anticrm/task'
|
||||||
import { Icon, Label, showPanel } from '@anticrm/ui'
|
import { getPanelURI, Icon, Label } from '@anticrm/ui'
|
||||||
import view from '@anticrm/view'
|
import view from '@anticrm/view'
|
||||||
import task from '../plugin'
|
import task from '../plugin'
|
||||||
|
|
||||||
@ -25,18 +25,13 @@
|
|||||||
|
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
const shortLabel = client.getHierarchy().getClass(value._class).shortLabel
|
const shortLabel = client.getHierarchy().getClass(value._class).shortLabel
|
||||||
|
|
||||||
function show () {
|
|
||||||
showPanel(view.component.EditDoc, value._id, value._class, 'full')
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if value && shortLabel}
|
{#if value && shortLabel}
|
||||||
<a
|
<a
|
||||||
class="flex-presenter"
|
class="flex-presenter"
|
||||||
class:inline-presenter={inline}
|
class:inline-presenter={inline}
|
||||||
href="#{encodeURIComponent([view.component.EditDoc, value._id, value._class].join('|'))}"
|
href="#{getPanelURI(view.component.EditDoc, value._id, value._class, 'full')}"
|
||||||
on:click={show}
|
|
||||||
>
|
>
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
<Icon icon={task.icon.Task} size={'small'} />
|
<Icon icon={task.icon.Task} size={'small'} />
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import contact from '@anticrm/contact'
|
import contact from '@anticrm/contact'
|
||||||
import { FindOptions, Ref, WithLookup } from '@anticrm/core'
|
import { FindOptions, Ref, WithLookup } from '@anticrm/core'
|
||||||
@ -58,7 +57,7 @@
|
|||||||
/* eslint-disable no-unused-vars */
|
/* eslint-disable no-unused-vars */
|
||||||
let issue: Issue
|
let issue: Issue
|
||||||
|
|
||||||
function toIssue (object:any): WithLookup<Issue> {
|
function toIssue (object: any): WithLookup<Issue> {
|
||||||
return object as WithLookup<Issue>
|
return object as WithLookup<Issue>
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,6 +69,9 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if currentTeam}
|
{#if currentTeam}
|
||||||
|
<div class="flex-between label font-medium w-full p-4">
|
||||||
|
Board
|
||||||
|
</div>
|
||||||
<Kanban
|
<Kanban
|
||||||
_class={tracker.class.Issue}
|
_class={tracker.class.Issue}
|
||||||
space={currentSpace}
|
space={currentSpace}
|
||||||
@ -81,63 +83,67 @@
|
|||||||
rankFieldName={'rank'}
|
rankFieldName={'rank'}
|
||||||
>
|
>
|
||||||
<svelte:fragment slot="header" let:state let:count>
|
<svelte:fragment slot="header" let:state let:count>
|
||||||
<div class="header flex">
|
<div class="header flex-col">
|
||||||
<div class="flex-between label">
|
<div class="flex-between label font-medium w-full h-full mb-4">
|
||||||
<div class="flex-row-center gap-2">
|
<div class="flex-row-center gap-2">
|
||||||
<Icon icon={state.icon} size={'small'} />
|
<Icon icon={state.icon} size={'small'} />
|
||||||
<span class="lines-limit-2">{state.title}</span>
|
<span class="lines-limit-2 ml-2">{state.title}</span>
|
||||||
<span class="counter ml-2">{count}</span>
|
<span class="counter ml-2 text-md">{count}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class='flex gap-1'>
|
<div class="flex gap-1">
|
||||||
<Tooltip label={tracker.string.AddIssueTooltip} direction={'left'}>
|
<Tooltip label={tracker.string.AddIssueTooltip} direction={'left'}>
|
||||||
<Button icon={IconAdd} kind={'transparent'} on:click={(evt) => {
|
<Button
|
||||||
|
icon={IconAdd}
|
||||||
|
kind={'transparent'}
|
||||||
|
on:click={(evt) => {
|
||||||
showPopup(CreateIssue, { space: currentSpace, issueStatus: state._id }, evt.target)
|
showPopup(CreateIssue, { space: currentSpace, issueStatus: state._id }, evt.target)
|
||||||
}}/>
|
}}
|
||||||
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Button icon={IconMoreH} kind={'transparent'}/>
|
<Button icon={IconMoreH} kind={'transparent'} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
<svelte:fragment slot="card" let:object>
|
<svelte:fragment slot="card" let:object>
|
||||||
{@const issue = toIssue(object) }
|
{@const issue = toIssue(object)}
|
||||||
<div class='flex-row h-18'>
|
<div class="flex-row h-18">
|
||||||
<div class='flex-between mb-2'>
|
<div class="flex-between mb-2">
|
||||||
<IssuePresenter value={object} {currentTeam}/>
|
<IssuePresenter value={object} {currentTeam} />
|
||||||
{#if issue.$lookup?.assignee }
|
{#if issue.$lookup?.assignee}
|
||||||
<Component is={view.component.ObjectPresenter} props={{ value: issue.$lookup.assignee, props: { shouldShowName: false } }}/>
|
<Component
|
||||||
|
is={view.component.ObjectPresenter}
|
||||||
|
props={{ value: issue.$lookup.assignee, props: { showLabel: false } }}
|
||||||
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<span class='fs-bold title'>
|
<span class="fs-bold title">
|
||||||
{object.title}
|
{object.title}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
</Kanban>
|
</Kanban>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.header {
|
.header {
|
||||||
display: flex;
|
height: 6rem;
|
||||||
flex-direction: column;
|
min-height: 6rem;
|
||||||
height: 3rem;
|
|
||||||
min-height: 3rem;
|
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
|
||||||
|
.filter {
|
||||||
|
border-bottom: 1px solid var(--divider-color);
|
||||||
|
}
|
||||||
|
|
||||||
.label {
|
.label {
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
font-weight: 500;
|
|
||||||
color: var(--theme-caption-color);
|
color: var(--theme-caption-color);
|
||||||
border-bottom: 1px solid var(--divider-color);
|
border-bottom: 1px solid var(--divider-color);
|
||||||
.counter {
|
.counter {
|
||||||
color: rgba(var(--theme-caption-color), 0.8) !important;
|
color: rgba(var(--theme-caption-color), 0.8);
|
||||||
font-weight: unset;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.title {
|
.title {
|
||||||
color: var(--theme-caption-color)
|
color: var(--theme-caption-color);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
Loading…
Reference in New Issue
Block a user