mirror of
https://github.com/hcengineering/platform.git
synced 2025-01-08 21:27:45 +03:00
Update EditChannel panel, Attributes (#1735)
Signed-off-by: Alexander Platov <sas_lord@mail.ru>
This commit is contained in:
parent
e91c5d073a
commit
5fea32fd6f
@ -117,10 +117,6 @@
|
||||
label={attribute?.label}
|
||||
placeholder={attribute?.label}
|
||||
type={attribute?.type}
|
||||
kind={'link'}
|
||||
size={'large'}
|
||||
width={'100%'}
|
||||
justify={'left'}
|
||||
{maxWidth}
|
||||
value={getAttribute(client, object, { key: attributeKey, attr: attribute })}
|
||||
space={object.space}
|
||||
@ -131,7 +127,7 @@
|
||||
</div>
|
||||
{:else}
|
||||
<span class="fs-bold"><Label label={attribute.label} /></span>
|
||||
<div class="flex flex-grow">
|
||||
<div class="flex flex-grow min-w-0">
|
||||
<svelte:component
|
||||
this={instance}
|
||||
label={attribute?.label}
|
||||
|
@ -58,10 +58,55 @@
|
||||
return isPerson ? formatName(obj.name) : obj.name
|
||||
}
|
||||
const mgr = getFocusManager()
|
||||
|
||||
const _click = (): void => {
|
||||
if (!readonly) {
|
||||
showPopup(
|
||||
UsersPopup,
|
||||
{ _class, allowDeselect, selected: value, titleDeselect, placeholder },
|
||||
container,
|
||||
(result) => {
|
||||
if (result === null) {
|
||||
value = null
|
||||
selected = undefined
|
||||
dispatch('change', null)
|
||||
} else if (result !== undefined && result._id !== value) {
|
||||
value = result._id
|
||||
dispatch('change', value)
|
||||
}
|
||||
mgr?.setFocusPos(focusIndex)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div bind:this={container} class="min-w-0">
|
||||
<Tooltip {label} fill={width === '100%'} direction={labelDirection}>
|
||||
<div bind:this={container} class="min-w-0" class:w-full={width === '100%'}>
|
||||
{#if kind !== 'link'}
|
||||
<Tooltip {label} fill={width === '100%'} direction={labelDirection}>
|
||||
<Button
|
||||
{focusIndex}
|
||||
icon={size === 'x-large' && selected ? undefined : IconPerson}
|
||||
width={width ?? 'min-content'}
|
||||
{size}
|
||||
{kind}
|
||||
{justify}
|
||||
on:click={_click}
|
||||
>
|
||||
<span slot="content" style="overflow: hidden">
|
||||
{#if selected}
|
||||
{#if size === 'x-large'}
|
||||
<UserInfo value={selected} size={'medium'} />
|
||||
{:else}
|
||||
{getName(selected)}
|
||||
{/if}
|
||||
{:else}
|
||||
<Label {label} />
|
||||
{/if}
|
||||
</span>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
{:else}
|
||||
<Button
|
||||
{focusIndex}
|
||||
icon={size === 'x-large' && selected ? undefined : IconPerson}
|
||||
@ -69,26 +114,7 @@
|
||||
{size}
|
||||
{kind}
|
||||
{justify}
|
||||
on:click={() => {
|
||||
if (!readonly) {
|
||||
showPopup(
|
||||
UsersPopup,
|
||||
{ _class, allowDeselect, selected: value, titleDeselect, placeholder },
|
||||
container,
|
||||
(result) => {
|
||||
if (result === null) {
|
||||
value = null
|
||||
selected = undefined
|
||||
dispatch('change', null)
|
||||
} else if (result !== undefined && result._id !== value) {
|
||||
value = result._id
|
||||
dispatch('change', value)
|
||||
}
|
||||
mgr?.setFocusPos(focusIndex)
|
||||
}
|
||||
)
|
||||
}
|
||||
}}
|
||||
on:click={_click}
|
||||
>
|
||||
<span slot="content" style="overflow: hidden">
|
||||
{#if selected}
|
||||
@ -102,5 +128,5 @@
|
||||
{/if}
|
||||
</span>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
</div>
|
||||
|
@ -529,7 +529,7 @@ a.no-line {
|
||||
text-transform: uppercase;
|
||||
font-weight: 600;
|
||||
font-size: .75rem;
|
||||
color: var(--theme-content-trans-color);
|
||||
color: var(--dark-color);
|
||||
user-select: none;
|
||||
}
|
||||
.text-xs { font-size: .625rem; }
|
||||
|
@ -131,7 +131,8 @@
|
||||
margin-right: .75rem;
|
||||
}
|
||||
.check-right { margin: 0 0 0 2rem; }
|
||||
&:focus {
|
||||
&:focus,
|
||||
&:hover {
|
||||
background-color: var(--popup-bg-hover);
|
||||
|
||||
.icon { color: var(--accent-color); }
|
||||
|
@ -29,7 +29,7 @@
|
||||
export let placeholder: IntlString = plugin.string.EditBoxPlaceholder
|
||||
export let placeholderParam: any | undefined = undefined
|
||||
export let format: 'text' | 'password' | 'number' = 'text'
|
||||
export let kind: 'editbox' | 'large-style' | 'small-style' = 'editbox'
|
||||
export let kind: 'editbox' | 'large-style' | 'small-style' | 'search-style' = 'editbox'
|
||||
export let focus: boolean = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
@ -109,6 +109,8 @@
|
||||
{style}
|
||||
on:input={(ev) => ev.target && computeSize(ev.target)}
|
||||
on:change
|
||||
on:keydown
|
||||
on:keypress
|
||||
/>
|
||||
{:else if format === 'number'}
|
||||
<input
|
||||
@ -119,6 +121,8 @@
|
||||
{style}
|
||||
on:input={(ev) => ev.target && computeSize(ev.target)}
|
||||
on:change
|
||||
on:keydown
|
||||
on:keypress
|
||||
/>
|
||||
{:else}
|
||||
<input
|
||||
@ -129,6 +133,8 @@
|
||||
{style}
|
||||
on:input={(ev) => ev.target && computeSize(ev.target)}
|
||||
on:change
|
||||
on:keydown
|
||||
on:keypress
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
@ -157,6 +163,10 @@
|
||||
font-weight: 400;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
.search-style {
|
||||
font-weight: 400;
|
||||
padding: 0.625rem 0.75rem;
|
||||
}
|
||||
|
||||
input {
|
||||
margin: 0;
|
||||
|
@ -28,6 +28,7 @@
|
||||
export let icon: 'normal' | 'warning' | 'overdue' = 'normal'
|
||||
export let labelOver: IntlString | undefined = undefined // label instead of date
|
||||
export let labelNull: IntlString = ui.string.NoDate
|
||||
export let kind: 'no-border' | 'link' = 'no-border'
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@ -266,7 +267,7 @@
|
||||
|
||||
<button
|
||||
bind:this={datePresenter}
|
||||
class="datetime-button"
|
||||
class="datetime-button {kind}"
|
||||
class:editable
|
||||
class:edit
|
||||
on:click={() => {
|
||||
@ -389,6 +390,7 @@
|
||||
.datetime-button {
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
padding: 0 0.5rem;
|
||||
@ -399,13 +401,11 @@
|
||||
white-space: nowrap;
|
||||
line-height: 1.5rem;
|
||||
color: var(--accent-color);
|
||||
background-color: var(--noborder-bg-color);
|
||||
border: 1px solid transparent;
|
||||
border-radius: 0.25rem;
|
||||
box-shadow: var(--button-shadow);
|
||||
transition-property: border, background-color, color, box-shadow;
|
||||
transition-duration: 0.15s;
|
||||
cursor: default;
|
||||
cursor: pointer;
|
||||
|
||||
.btn-icon {
|
||||
margin-right: 0.375rem;
|
||||
@ -425,55 +425,86 @@
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--caption-color);
|
||||
transition-duration: 0;
|
||||
}
|
||||
&.editable {
|
||||
cursor: pointer;
|
||||
&.no-border {
|
||||
font-weight: 400;
|
||||
color: var(--accent-color);
|
||||
background-color: var(--noborder-bg-color);
|
||||
box-shadow: var(--button-shadow);
|
||||
|
||||
&:hover {
|
||||
color: var(--caption-color);
|
||||
background-color: var(--noborder-bg-hover);
|
||||
transition-duration: 0;
|
||||
|
||||
.btn-icon {
|
||||
&.normal {
|
||||
color: var(--caption-color);
|
||||
}
|
||||
&.warning {
|
||||
color: var(--warning-color);
|
||||
}
|
||||
&.overdue {
|
||||
color: var(--error-color);
|
||||
}
|
||||
}
|
||||
.time-divider {
|
||||
background-color: var(--button-border-hover);
|
||||
color: var(--caption-color);
|
||||
}
|
||||
}
|
||||
&:focus-within {
|
||||
background-color: var(--button-bg-color);
|
||||
&:disabled {
|
||||
color: var(--content-color);
|
||||
background-color: var(--button-disabled-color);
|
||||
cursor: default;
|
||||
&:hover {
|
||||
color: var(--content-color);
|
||||
.btn-icon {
|
||||
color: var(--content-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.editable {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--noborder-bg-hover);
|
||||
.btn-icon {
|
||||
&.normal {
|
||||
color: var(--caption-color);
|
||||
}
|
||||
&.warning {
|
||||
color: var(--warning-color);
|
||||
}
|
||||
&.overdue {
|
||||
color: var(--error-color);
|
||||
}
|
||||
}
|
||||
.time-divider {
|
||||
background-color: var(--button-border-hover);
|
||||
}
|
||||
}
|
||||
&:focus-within {
|
||||
background-color: var(--button-bg-color);
|
||||
border-color: var(--primary-edit-border-color);
|
||||
&:hover {
|
||||
background-color: var(--button-bg-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.edit {
|
||||
padding: 0 0.125rem;
|
||||
background-color: transparent;
|
||||
border-color: var(--primary-edit-border-color);
|
||||
&:hover {
|
||||
background-color: var(--button-bg-color);
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
&:disabled {
|
||||
background-color: var(--button-disabled-color);
|
||||
cursor: default;
|
||||
|
||||
&.link {
|
||||
padding: 0 0.875rem;
|
||||
width: 100%;
|
||||
height: 2rem;
|
||||
&:hover {
|
||||
color: var(--content-color);
|
||||
color: var(--caption-color);
|
||||
background-color: var(--body-color);
|
||||
border-color: var(--divider-color);
|
||||
.btn-icon {
|
||||
color: var(--content-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
&.edit {
|
||||
padding: 0 0.125rem;
|
||||
background-color: transparent;
|
||||
border-color: var(--primary-edit-border-color);
|
||||
&:hover {
|
||||
background-color: transparent;
|
||||
&.edit {
|
||||
padding: 0 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@
|
||||
|
||||
async function onSpaceEdit (): Promise<void> {
|
||||
if (channel === undefined) return
|
||||
showPanel(chunter.component.EditChannel, channel._id, channel._class, 'right')
|
||||
showPanel(chunter.component.EditChannel, channel._id, channel._class, 'content')
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -17,9 +17,8 @@
|
||||
import { ChunterSpace } from '@anticrm/chunter'
|
||||
import { EmployeeAccount } from '@anticrm/contact'
|
||||
import type { Class, Ref } from '@anticrm/core'
|
||||
import type { IntlString } from '@anticrm/platform'
|
||||
import { createQuery, getClient, Members } from '@anticrm/presentation'
|
||||
import { ActionIcon, Icon, IconClose, Label, Scroller, showPopup } from '@anticrm/ui'
|
||||
import { Icon, Label, Scroller, showPopup, Panel } from '@anticrm/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
import chunter from '../plugin'
|
||||
@ -36,20 +35,13 @@
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const client = getClient()
|
||||
const clazz = client.getHierarchy().getClass(_class)
|
||||
$: clazz = client.getHierarchy().getClass(_class)
|
||||
|
||||
const query = createQuery()
|
||||
$: query.query(chunter.class.ChunterSpace, { _id }, (result) => {
|
||||
channel = result[0]
|
||||
})
|
||||
|
||||
const tabLabels: IntlString[] = [
|
||||
chunter.string.About,
|
||||
chunter.string.Members,
|
||||
...(_class === chunter.class.Channel ? [chunter.string.Settings] : [])
|
||||
]
|
||||
let selectedTabIndex = 0
|
||||
|
||||
function openAddMembersPopup () {
|
||||
showPopup(
|
||||
AddMembersPopup,
|
||||
@ -69,56 +61,54 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
on:click={() => {
|
||||
<Panel
|
||||
isHeader={false}
|
||||
isAside={_class === chunter.class.Channel}
|
||||
on:close={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
/>
|
||||
<div class="antiDialogs antiComponent">
|
||||
<div class="ac-header short mirror divide">
|
||||
<div class="ac-header__wrap-title">
|
||||
<div class="ac-header__icon">
|
||||
{#if channel}
|
||||
{#if channel.private}
|
||||
<Lock size={'medium'} />
|
||||
{:else if clazz.icon}<Icon icon={clazz.icon} size={'medium'} />{/if}
|
||||
{/if}
|
||||
>
|
||||
<svelte:fragment slot="title">
|
||||
{#if clazz && channel}
|
||||
<div class="antiTitle icon-wrapper">
|
||||
<div class="wrapped-icon">
|
||||
{#if clazz.icon}<Icon icon={channel.private ? Lock : clazz.icon} size={'medium'} />{/if}
|
||||
</div>
|
||||
<div class="title-wrapper">
|
||||
<span class="wrapped-title"
|
||||
><span class="trans-title content-color"><Label label={clazz.label} />›</span> {channel.name}</span
|
||||
>
|
||||
<span class="wrapped-subtitle">{channel.description}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ac-header__title"><Label label={clazz.label} /></div>
|
||||
</div>
|
||||
<div class="tool">
|
||||
<ActionIcon
|
||||
icon={IconClose}
|
||||
size={'small'}
|
||||
action={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ac-tabs">
|
||||
{#each tabLabels as tabLabel, i}
|
||||
<div
|
||||
class="ac-tabs__tab"
|
||||
class:selected={i === selectedTabIndex}
|
||||
on:click={() => {
|
||||
selectedTabIndex = i
|
||||
}}
|
||||
>
|
||||
<Label label={tabLabel} />
|
||||
</div>
|
||||
{/each}
|
||||
<div class="ac-tabs__empty" />
|
||||
</div>
|
||||
{#if channel}
|
||||
{#if selectedTabIndex === 0}
|
||||
<Scroller padding>
|
||||
<EditChannelDescriptionTab {channel} on:close />
|
||||
</Scroller>
|
||||
{:else if selectedTabIndex === 1}
|
||||
<Members space={channel} withAddButton={true} on:addMembers={openAddMembersPopup} />
|
||||
{:else if selectedTabIndex === 2}
|
||||
<EditChannelSettingsTab {channel} on:close />
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="aside">
|
||||
{#if _class === chunter.class.Channel}
|
||||
<div class="flex-col-center">
|
||||
<span class="fs-title text-xl overflow-label mt-4 mb-2"><Label label={chunter.string.Settings} /></span>
|
||||
<EditChannelSettingsTab {channel} on:close />
|
||||
</div>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
|
||||
<Scroller>
|
||||
<div class="popupPanel-body__main-content py-10 h-full clear-mins">
|
||||
{#if channel}
|
||||
<div class="flex-col flex-no-shrink">
|
||||
<span class="fs-title text-xl overflow-label mb-2 flex-no-shrink">
|
||||
<Label label={chunter.string.About} />
|
||||
</span>
|
||||
<EditChannelDescriptionTab {channel} on:close />
|
||||
</div>
|
||||
<div class="flex-col mt-10 flex-no-shrink">
|
||||
<span class="fs-title text-xl overflow-label mb-2 flex-no-shrink">
|
||||
<Label label={chunter.string.Members} />
|
||||
</span>
|
||||
<Members space={channel} withAddButton={true} on:addMembers={openAddMembersPopup} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</Scroller>
|
||||
</Panel>
|
||||
|
@ -11,14 +11,12 @@
|
||||
</script>
|
||||
|
||||
{#if channel}
|
||||
<div class="mt-4 ml-8 mr-8 flex-col p-4">
|
||||
<Button
|
||||
label={chunter.string.ArchiveChannel}
|
||||
justify={'left'}
|
||||
size={'x-large'}
|
||||
on:click={() => {
|
||||
ArchiveChannel(channel, () => dispatch('close'))
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
label={chunter.string.ArchiveChannel}
|
||||
justify={'left'}
|
||||
size={'x-large'}
|
||||
on:click={() => {
|
||||
ArchiveChannel(channel, () => dispatch('close'))
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
|
@ -25,6 +25,7 @@
|
||||
export let value: Ref<Organization> | undefined
|
||||
export let label: IntlString = contact.string.Organization
|
||||
export let onChange: (value: any) => void
|
||||
export let kind: 'no-border' | 'link' = 'no-border'
|
||||
|
||||
const query = createQuery()
|
||||
|
||||
@ -60,7 +61,7 @@
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="caption-color cursor-pointer"
|
||||
class="org-container {kind}"
|
||||
bind:this={container}
|
||||
class:empty={selected === undefined}
|
||||
on:click|preventDefault={() => {
|
||||
@ -73,11 +74,47 @@
|
||||
}
|
||||
}}
|
||||
>
|
||||
{#if selected}{selected.label}{:else}<Label {label} />{/if}
|
||||
{#if selected}
|
||||
<div class="flex-row-center">
|
||||
<div class="icon"><Company size={'small'} /></div>
|
||||
{selected.label}
|
||||
</div>
|
||||
{:else}
|
||||
<Label {label} />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.empty {
|
||||
color: var(--theme-content-trans-color);
|
||||
.org-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--caption-color);
|
||||
border: 1px solid transparent;
|
||||
border-radius: 0.25rem;
|
||||
transition-property: border, background-color, color, box-shadow;
|
||||
transition-duration: 0.15s;
|
||||
cursor: pointer;
|
||||
|
||||
.icon {
|
||||
margin-right: 0.5rem;
|
||||
padding: 0.25rem;
|
||||
color: var(--caption-color);
|
||||
background-color: var(--avatar-bg-color);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
&.empty {
|
||||
color: var(--theme-content-trans-color);
|
||||
}
|
||||
|
||||
&.link {
|
||||
padding: 0 0.875rem;
|
||||
height: 2rem;
|
||||
&:hover {
|
||||
color: var(--caption-color);
|
||||
background-color: var(--body-color);
|
||||
border-color: var(--divider-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -18,13 +18,18 @@
|
||||
import { IntlString } from '@anticrm/platform'
|
||||
import { UserBox } from '@anticrm/presentation'
|
||||
import contact from '../plugin'
|
||||
import { ButtonKind, ButtonSize } from '@anticrm/ui'
|
||||
|
||||
export let value: Ref<Person> | undefined
|
||||
export let label: IntlString = contact.string.Person
|
||||
export let onChange: (value: any) => void
|
||||
export let type: RefTo<Person> | undefined
|
||||
export let kind: ButtonKind = 'no-border'
|
||||
export let size: ButtonSize = 'small'
|
||||
export let justify: 'left' | 'center' = 'center'
|
||||
export let width: string | undefined = undefined
|
||||
|
||||
$: _class = type?.to ?? contact.class.Person
|
||||
</script>
|
||||
|
||||
<UserBox {_class} {label} kind={'link'} size={'large'} bind:value on:change={(e) => onChange(e.detail)} />
|
||||
<UserBox {_class} {label} {kind} {size} {justify} {width} bind:value on:change={(e) => onChange(e.detail)} />
|
||||
|
@ -20,7 +20,7 @@
|
||||
import { AttributesBar, createQuery, getClient, Members } from '@anticrm/presentation'
|
||||
import { Vacancy } from '@anticrm/recruit'
|
||||
import { StyledTextBox } from '@anticrm/text-editor'
|
||||
import { EditBox } from '@anticrm/ui'
|
||||
import { EditBox, Label, Grid } from '@anticrm/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import recruit from '../plugin'
|
||||
|
||||
@ -66,37 +66,39 @@
|
||||
{#if dir === 'column'}
|
||||
<div class="ac-subtitle">
|
||||
<div class="ac-subtitle-content">
|
||||
<AttributesBar {object} keys={['dueTo', 'location', 'company']} vertical={dir === 'column'} />
|
||||
<AttributesBar {object} keys={['dueTo', 'location', 'company']} vertical />
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
|
||||
<EditBox
|
||||
label={recruit.string.VacancyName}
|
||||
bind:value={object.name}
|
||||
placeholder={recruit.string.VacancyPlaceholder}
|
||||
maxWidth="39rem"
|
||||
focus
|
||||
on:change={() => {
|
||||
if (object.name.trim().length > 0) {
|
||||
onChange('name', object.name)
|
||||
} else {
|
||||
// Revert previos object.name
|
||||
updateObject(_id)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<EditBox
|
||||
label={recruit.string.Description}
|
||||
bind:value={object.description}
|
||||
placeholder={recruit.string.VacancyDescription}
|
||||
maxWidth="39rem"
|
||||
focus
|
||||
on:change={() => {
|
||||
onChange('description', object.description)
|
||||
}}
|
||||
/>
|
||||
<Grid column={1} rowGap={1.5}>
|
||||
<EditBox
|
||||
label={recruit.string.VacancyName}
|
||||
bind:value={object.name}
|
||||
placeholder={recruit.string.VacancyPlaceholder}
|
||||
maxWidth="39rem"
|
||||
focus
|
||||
on:change={() => {
|
||||
if (object.name.trim().length > 0) {
|
||||
onChange('name', object.name)
|
||||
} else {
|
||||
// Revert previos object.name
|
||||
updateObject(_id)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<EditBox
|
||||
label={recruit.string.Description}
|
||||
bind:value={object.description}
|
||||
placeholder={recruit.string.VacancyDescription}
|
||||
maxWidth="39rem"
|
||||
focus
|
||||
on:change={() => {
|
||||
onChange('description', object.description)
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<div class="mt-10">
|
||||
<span class="title">Details</span>
|
||||
<div class="description-container">
|
||||
@ -108,10 +110,13 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-14">
|
||||
<div class="mt-10">
|
||||
<Attachments objectId={object._id} _class={object._class} space={object.space} />
|
||||
</div>
|
||||
<div class="mt-14">
|
||||
<div class="flex-col mt-10 flex-no-shrink">
|
||||
<span class="fs-title text-xl overflow-label mb-2 flex-no-shrink">
|
||||
<Label label={recruit.string.Members} />
|
||||
</span>
|
||||
<Members space={object} />
|
||||
</div>
|
||||
</Panel>
|
||||
|
@ -27,7 +27,7 @@
|
||||
<a
|
||||
class="flex-presenter"
|
||||
class:inline-presenter={inline}
|
||||
href="#{getPanelURI(recruit.component.EditVacancy, value._id, value._class, 'right')}"
|
||||
href="#{getPanelURI(recruit.component.EditVacancy, value._id, value._class, 'content')}"
|
||||
>
|
||||
<div class="icon">
|
||||
<Icon icon={recruit.icon.Vacancy} size={'small'} />
|
||||
|
@ -17,7 +17,8 @@
|
||||
import { Ref } from '@anticrm/core'
|
||||
import { createQuery } from '@anticrm/presentation'
|
||||
import { DoneState, SpaceWithStates } from '@anticrm/task'
|
||||
import { Label, showPopup } from '@anticrm/ui'
|
||||
import { Label, showPopup, Button } from '@anticrm/ui'
|
||||
import type { ButtonKind, ButtonSize } from '@anticrm/ui'
|
||||
import DoneStatePresenter from './DoneStatePresenter.svelte'
|
||||
import DoneStatesPopup from './DoneStatesPopup.svelte'
|
||||
import task from '../../plugin'
|
||||
@ -25,8 +26,13 @@
|
||||
export let value: Ref<DoneState> | null | undefined
|
||||
export let onChange: (value: any) => void
|
||||
export let space: Ref<SpaceWithStates>
|
||||
export let kind: ButtonKind = 'no-border'
|
||||
export let size: ButtonSize = 'small'
|
||||
export let justify: 'left' | 'center' = 'center'
|
||||
export let width: string = 'min-content'
|
||||
|
||||
let state: DoneState | undefined
|
||||
let container: HTMLElement
|
||||
let container: HTMLButtonElement
|
||||
let opened: boolean = false
|
||||
|
||||
const query = createQuery()
|
||||
@ -42,10 +48,13 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="flex-row-center cursor-pointer p-4"
|
||||
bind:this={container}
|
||||
on:click|preventDefault={() => {
|
||||
<Button
|
||||
{kind}
|
||||
{size}
|
||||
{justify}
|
||||
{width}
|
||||
bind:input={container}
|
||||
on:click={() => {
|
||||
if (!opened) {
|
||||
opened = true
|
||||
showPopup(DoneStatesPopup, { space }, container, (result) => {
|
||||
@ -62,13 +71,17 @@
|
||||
}
|
||||
}}
|
||||
>
|
||||
{#if state}
|
||||
<DoneStatePresenter value={state} showTitle />
|
||||
{:else}
|
||||
<div class="color background-card-divider" />
|
||||
<Label label={task.string.NoDoneState} />
|
||||
{/if}
|
||||
</div>
|
||||
<svelte:fragment slot="content">
|
||||
{#if state}
|
||||
<DoneStatePresenter value={state} showTitle />
|
||||
{:else}
|
||||
<div class="flex-center clear-mins">
|
||||
<div class="color background-card-divider" />
|
||||
<Label label={task.string.NoDoneState} />
|
||||
</div>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</Button>
|
||||
|
||||
<style lang="scss">
|
||||
.color {
|
||||
|
@ -27,6 +27,8 @@
|
||||
export let onChange: (value: any) => void
|
||||
export let kind: ButtonKind = 'no-border'
|
||||
export let size: ButtonSize = 'small'
|
||||
export let width: string = 'min-content'
|
||||
export let justify: 'left' | 'center' = 'center'
|
||||
|
||||
let state: State
|
||||
let opened: boolean = false
|
||||
@ -43,9 +45,10 @@
|
||||
</script>
|
||||
|
||||
<Button
|
||||
width="min-content"
|
||||
{kind}
|
||||
{size}
|
||||
{width}
|
||||
{justify}
|
||||
on:click={(ev) => {
|
||||
if (!opened) {
|
||||
opened = true
|
||||
|
@ -14,13 +14,12 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
// import type { IntlString } from '@anticrm/platform'
|
||||
import { Label, Button } from '@anticrm/ui'
|
||||
import { Button, showPopup, eventToHTMLElement } from '@anticrm/ui'
|
||||
import type { ButtonKind, ButtonSize } from '@anticrm/ui'
|
||||
import { getBooleanLabel } from '../utils'
|
||||
import BooleanPresenter from './BooleanPresenter.svelte'
|
||||
import BooleanEditorPopup from './BooleanEditorPopup.svelte'
|
||||
|
||||
export let value: any
|
||||
export let maxWidth: string | undefined = undefined
|
||||
export let withoutUndefined: boolean = false
|
||||
export let onChange: (value: any) => void
|
||||
|
||||
@ -28,6 +27,8 @@
|
||||
export let size: ButtonSize = 'small'
|
||||
export let justify: 'left' | 'center' = 'center'
|
||||
export let width: string | undefined = 'fit-content'
|
||||
|
||||
let shown: boolean = false
|
||||
</script>
|
||||
|
||||
<Button
|
||||
@ -35,67 +36,21 @@
|
||||
{size}
|
||||
{justify}
|
||||
{width}
|
||||
on:click={() => {
|
||||
if (value === true) value = false
|
||||
else if (value === false && !withoutUndefined) value = undefined
|
||||
else value = true
|
||||
onChange(value)
|
||||
on:click={(ev) => {
|
||||
if (!shown) {
|
||||
showPopup(BooleanEditorPopup, { value, withoutUndefined }, eventToHTMLElement(ev), (res) => {
|
||||
if (res !== undefined) {
|
||||
if (res === 1) value = true
|
||||
else if (res === 2) value = false
|
||||
else if (res === 3) value = undefined
|
||||
onChange(value)
|
||||
}
|
||||
shown = false
|
||||
})
|
||||
}
|
||||
}}
|
||||
>
|
||||
<svelte:fragment slot="content">
|
||||
<div
|
||||
class="flex-row-center yesno-container"
|
||||
class:yes={value === true}
|
||||
class:no={value === false}
|
||||
class:unknown={value === undefined}
|
||||
style={maxWidth ? `max-width: ${maxWidth};` : ''}
|
||||
>
|
||||
<svg class="yesno-svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<circle class:yes={value === true} class:no={value === false} cx="8" cy="8" r="6" />
|
||||
{#if value === true}
|
||||
<polygon fill="#fff" points="7.4,10.9 4.9,8.4 5.7,7.6 7.3,9.1 10.2,5.6 11.1,6.4 " />
|
||||
{:else if value === false}
|
||||
<polygon
|
||||
fill="#fff"
|
||||
points="10.7,6 10,5.3 8,7.3 6,5.3 5.3,6 7.3,8 5.3,10 6,10.7 8,8.7 10,10.7 10.7,10 8.7,8 "
|
||||
/>
|
||||
{:else}
|
||||
<path
|
||||
fill="#fff"
|
||||
d="M7.3,9.3h1.3V9c0.1-0.5,0.6-0.9,1.1-1.4c0.4-0.4,0.8-0.9,0.8-1.6c0-1.1-0.8-1.8-2.2-1.8c-1.4,0-2.4,0.8-2.5,2.2 h1.4c0.1-0.6,0.4-1,1-1C8.8,5.4,9,5.7,9,6.2c0,0.4-0.3,0.7-0.7,1.1c-0.5,0.5-1,0.9-1,1.7V9.3z M8,11.6c0.5,0,0.9-0.4,0.9-0.9 c0-0.5-0.4-0.9-0.9-0.9c-0.5,0-0.9,0.4-0.9,0.9C7.1,11.2,7.5,11.6,8,11.6z"
|
||||
/>
|
||||
{/if}
|
||||
</svg>
|
||||
<span><Label label={getBooleanLabel(value)} /></span>
|
||||
</div>
|
||||
<BooleanPresenter bind:value />
|
||||
</svelte:fragment>
|
||||
</Button>
|
||||
|
||||
<style lang="scss">
|
||||
.yesno-container {
|
||||
max-width: fit-content;
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
|
||||
fill: #77818e;
|
||||
&.yes {
|
||||
fill: #77c07b;
|
||||
}
|
||||
&.no {
|
||||
fill: #f96e50;
|
||||
}
|
||||
|
||||
span {
|
||||
margin-left: 0.25rem;
|
||||
width: 1.6rem;
|
||||
white-space: nowrap;
|
||||
text-transform: capitalize;
|
||||
font-weight: 500;
|
||||
color: var(--theme-caption-color);
|
||||
}
|
||||
}
|
||||
.yesno-svg {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
}
|
||||
</style>
|
||||
|
@ -0,0 +1,80 @@
|
||||
<!--
|
||||
// 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">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { CheckBox } from '@anticrm/ui'
|
||||
import BooleanPresenter from './BooleanPresenter.svelte'
|
||||
|
||||
export let value: boolean
|
||||
export let withoutUndefined: boolean = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
</script>
|
||||
|
||||
<div class="selectPopup">
|
||||
<div class="menu-item" on:click={() => dispatch('close', 1)}>
|
||||
<BooleanPresenter value={true} />
|
||||
{#if value === true}
|
||||
<div class="check">
|
||||
<CheckBox checked={value === true} primary />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="menu-item" on:click={() => dispatch('close', 2)}>
|
||||
<BooleanPresenter value={false} />
|
||||
{#if value === false}
|
||||
<div class="check">
|
||||
<CheckBox checked={value === false} primary />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{#if !withoutUndefined}
|
||||
<div class="menu-item" on:click={() => dispatch('close', 3)}>
|
||||
<BooleanPresenter value={undefined} />
|
||||
{#if value === undefined}
|
||||
<div class="check">
|
||||
<CheckBox checked={value === undefined} primary />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.menu-item {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0.25rem 0.75rem;
|
||||
min-height: 2rem;
|
||||
text-align: left;
|
||||
color: var(--caption-color);
|
||||
cursor: pointer;
|
||||
|
||||
.check {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
&:focus,
|
||||
&:hover {
|
||||
background-color: var(--popup-bg-hover);
|
||||
}
|
||||
}
|
||||
</style>
|
@ -21,6 +21,7 @@
|
||||
export let type: TypeDate | undefined
|
||||
// export let label: IntlString
|
||||
export let onChange: (value: any) => void
|
||||
export let kind: 'no-border' | 'link' = 'no-border'
|
||||
|
||||
$: withTime = type?.withTime ?? true
|
||||
</script>
|
||||
@ -29,6 +30,7 @@
|
||||
{value}
|
||||
{withTime}
|
||||
editable
|
||||
{kind}
|
||||
on:change={(res) => {
|
||||
if (res.detail !== undefined) onChange(res.detail)
|
||||
}}
|
||||
|
@ -15,18 +15,70 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { IntlString } from '@anticrm/platform'
|
||||
import { EditBox } from '@anticrm/ui'
|
||||
import { EditBox, Label, showPopup, eventToHTMLElement } from '@anticrm/ui'
|
||||
import StringEditorPopup from './StringEditorPopup.svelte'
|
||||
|
||||
// export let label: IntlString
|
||||
export let placeholder: IntlString
|
||||
export let value: any
|
||||
export let focus: boolean
|
||||
export let maxWidth: string
|
||||
export let maxWidth: string = '10rem'
|
||||
export let onChange: (value: number) => void
|
||||
export let kind: 'no-border' | 'link' = 'no-border'
|
||||
|
||||
let shown: boolean = false
|
||||
|
||||
function _onchange (ev: Event) {
|
||||
onChange((ev.target as HTMLInputElement).valueAsNumber)
|
||||
}
|
||||
</script>
|
||||
|
||||
<EditBox {placeholder} {maxWidth} bind:value format="number" {focus} on:change={_onchange} />
|
||||
{#if kind === 'link'}
|
||||
<div
|
||||
class="link-container"
|
||||
on:click={(ev) => {
|
||||
if (!shown) {
|
||||
showPopup(
|
||||
StringEditorPopup,
|
||||
{ value, format: 'number' },
|
||||
eventToHTMLElement(ev),
|
||||
(res) => {
|
||||
if (res !== undefined) value = res
|
||||
onChange(value)
|
||||
shown = false
|
||||
},
|
||||
(res) => {
|
||||
if (res !== undefined) value = res
|
||||
}
|
||||
)
|
||||
}
|
||||
}}
|
||||
>
|
||||
{#if value}
|
||||
<span class="overflow-label">{value}</span>
|
||||
{:else}
|
||||
<span class="dark-color"><Label label={placeholder} /></span>
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<EditBox {placeholder} {maxWidth} bind:value format={'number'} {focus} on:change={_onchange} />
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.link-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 0.875rem;
|
||||
width: 100%;
|
||||
height: 2rem;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 0.25rem;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: var(--caption-color);
|
||||
background-color: var(--body-color);
|
||||
border-color: var(--divider-color);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -15,7 +15,8 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { IntlString } from '@anticrm/platform'
|
||||
import { EditBox } from '@anticrm/ui'
|
||||
import { EditBox, Label, showPopup, eventToHTMLElement } from '@anticrm/ui'
|
||||
import StringEditorPopup from './StringEditorPopup.svelte'
|
||||
|
||||
// export let label: IntlString
|
||||
export let placeholder: IntlString
|
||||
@ -23,10 +24,61 @@
|
||||
export let focus: boolean
|
||||
export let maxWidth: string = '10rem'
|
||||
export let onChange: (value: string) => void
|
||||
export let kind: 'no-border' | 'link' = 'no-border'
|
||||
|
||||
let shown: boolean = false
|
||||
|
||||
function _onchange (ev: Event) {
|
||||
onChange((ev.target as HTMLInputElement).value)
|
||||
}
|
||||
</script>
|
||||
|
||||
<EditBox {placeholder} {maxWidth} bind:value {focus} on:change={_onchange} />
|
||||
{#if kind === 'link'}
|
||||
<div
|
||||
class="link-container"
|
||||
on:click={(ev) => {
|
||||
if (!shown) {
|
||||
showPopup(
|
||||
StringEditorPopup,
|
||||
{ value },
|
||||
eventToHTMLElement(ev),
|
||||
(res) => {
|
||||
if (res !== undefined) value = res
|
||||
onChange(value)
|
||||
shown = false
|
||||
},
|
||||
(res) => {
|
||||
if (res !== undefined) value = res
|
||||
}
|
||||
)
|
||||
}
|
||||
}}
|
||||
>
|
||||
{#if value}
|
||||
<span class="overflow-label">{value}</span>
|
||||
{:else}
|
||||
<span class="dark-color"><Label label={placeholder} /></span>
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<EditBox {placeholder} {maxWidth} bind:value {focus} on:change={_onchange} />
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.link-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 0.875rem;
|
||||
width: 100%;
|
||||
height: 2rem;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 0.25rem;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: var(--caption-color);
|
||||
background-color: var(--body-color);
|
||||
border-color: var(--divider-color);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -0,0 +1,46 @@
|
||||
<!--
|
||||
// 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">
|
||||
import type { IntlString } from '@anticrm/platform'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { EditBox } from '@anticrm/ui'
|
||||
|
||||
export let value: string
|
||||
export let placeholder: IntlString
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
function _onchange (ev: Event) {
|
||||
dispatch('update', value)
|
||||
}
|
||||
function _onkeypress (ev: KeyboardEvent) {
|
||||
if (ev.key === 'Enter') dispatch('close', value)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="selectPopup">
|
||||
<div class="header no-border">
|
||||
<EditBox
|
||||
bind:value
|
||||
{placeholder}
|
||||
kind={'search-style'}
|
||||
maxWidth={'10rem'}
|
||||
focus
|
||||
on:change={_onchange}
|
||||
on:keypress={_onkeypress}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
@ -75,7 +75,7 @@
|
||||
async function onSpaceEdit (): Promise<void> {
|
||||
if (space === undefined) return
|
||||
const editor = await getEditor(space._class)
|
||||
showPanel(editor ?? plugin.component.SpacePanel, space._id, space._class, 'right')
|
||||
showPanel(editor ?? plugin.component.SpacePanel, space._id, space._class, 'content')
|
||||
}
|
||||
|
||||
function updateViewlets (viewlets: WithLookup<Viewlet>[]) {
|
||||
|
@ -60,12 +60,14 @@
|
||||
}}
|
||||
>
|
||||
<svelte:fragment slot="title">
|
||||
<div class="antiTitle icon-wrapper">
|
||||
<div class="wrapped-icon">
|
||||
{#if clazz.icon}<Icon icon={clazz.icon} size={'medium'} />{/if}
|
||||
{#if clazz}
|
||||
<div class="antiTitle icon-wrapper">
|
||||
<div class="wrapped-icon">
|
||||
{#if clazz.icon}<Icon icon={clazz.icon} size={'medium'} />{/if}
|
||||
</div>
|
||||
<span class="wrapped-title"><Label label={clazz.label} /></span>
|
||||
</div>
|
||||
<span class="wrapped-title"><Label label={clazz.label} /></span>
|
||||
</div>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
|
||||
<Scroller>
|
||||
|
Loading…
Reference in New Issue
Block a user