Update EditChannel panel, Attributes (#1735)

Signed-off-by: Alexander Platov <sas_lord@mail.ru>
This commit is contained in:
Alexander Platov 2022-05-13 09:41:30 +03:00 committed by GitHub
parent e91c5d073a
commit 5fea32fd6f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 564 additions and 260 deletions

View File

@ -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}

View File

@ -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>

View File

@ -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; }

View File

@ -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); }

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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>

View File

@ -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>

View File

@ -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}

View File

@ -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>

View File

@ -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)} />

View File

@ -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>

View File

@ -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'} />

View File

@ -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 {

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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)
}}

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>[]) {

View File

@ -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>