Update UserBox and Button layouts (#1343)

Signed-off-by: Alexander Platov <sas_lord@mail.ru>
This commit is contained in:
Alexander Platov 2022-04-09 06:46:40 +03:00 committed by GitHub
parent b2c6b0840e
commit 2938013af6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 282 additions and 271 deletions

View File

@ -10,6 +10,8 @@
"Deselect": "Deselect",
"AddSocialLinks": "Add social links",
"Change": "Change",
"Remove": "Remove"
"Remove": "Remove",
"Search": "Search...",
"Unassigned": "Unassigned"
}
}

View File

@ -10,6 +10,8 @@
"Deselect": "Снять выделение",
"AddSocialLinks": "Добавить контактную информацию",
"Change": "Изменить",
"Remove": "Удалить"
"Remove": "Удалить",
"Search": "Поиск...",
"Unassigned": "Не назначен"
}
}

View File

@ -50,7 +50,7 @@
flex-shrink: 0;
position: relative;
overflow: hidden;
background-color: rgba(196, 196, 204, .2);
background-color: var(--popup-bg-hover);
border-radius: 50%;
pointer-events: none;

View File

@ -14,18 +14,19 @@
-->
<script lang="ts">
import { afterUpdate, createEventDispatcher } from 'svelte'
import ui, { Label, EditWithIcon, IconSearch } from '@anticrm/ui'
import SpaceInfo from './SpaceInfo.svelte'
import { translate } from '@anticrm/platform'
import type { Ref, Class, Space, DocumentQuery } from '@anticrm/core'
import { createQuery } from '../utils'
import SpaceInfo from './SpaceInfo.svelte'
import presentation from '..'
export let _class: Ref<Class<Space>>
export let spaceQuery: DocumentQuery<Space> | undefined
let search: string = ''
let objects: Space[] = []
let phTraslate: string = ''
$: translate(presentation.string.Search, {}).then(res => { phTraslate = res })
const dispatch = createEventDispatcher()
const query = createQuery()
@ -33,21 +34,17 @@
afterUpdate(() => { dispatch('update', Date.now()) })
</script>
<div class="antiPopup antiPopup-withHeader">
<div class="ap-space" />
<div class="ap-header">
<EditWithIcon icon={IconSearch} bind:value={search} placeholder={ui.string.SearchDots} focus />
<div class="ap-caption"><Label label={ui.string.Suggested} /></div>
<div class="selectPopup">
<div class="header">
<input type='text' bind:value={search} placeholder={phTraslate} on:input={(ev) => { }} on:change/>
</div>
<div class="ap-space" />
<div class="ap-scroll">
<div class="ap-box">
<div class="scroll">
<div class="box">
{#each objects as space}
<button class="ap-menuItem" on:click={() => { dispatch('close', space) }}>
<button class="menu-item flex-between" on:click={() => { dispatch('close', space) }}>
<SpaceInfo size={'large'} value={space} />
</button>
{/each}
</div>
</div>
<div class="ap-space" />
</div>

View File

@ -18,10 +18,12 @@
import { onMount } from 'svelte'
import type { IntlString } from '@anticrm/platform'
import { getClient } from '../utils'
import { Label, showPopup } from '@anticrm/ui'
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'
@ -29,18 +31,22 @@
import presentation from '..'
export let _class: Ref<Class<Contact>>
export let title: IntlString
export let caption: IntlString
export let label: IntlString
export let placeholder: IntlString = presentation.string.Search
export let value: Ref<Contact> | null | undefined
export let show: boolean = false
export let allowDeselect = false
export let titleDeselect: IntlString | undefined = undefined
export let readonly = false
export let kind: 'primary' | 'secondary' | 'no-border' | 'transparent' | 'link' | 'dangerous' = 'no-border'
export let size: 'small' | 'medium' | 'large' | 'x-large' = 'small'
export let justify: 'left' | 'center' = 'center'
export let width: string | undefined = undefined
export let labelDirection: TooltipAligment | undefined = undefined
const dispatch = createEventDispatcher()
let selected: Contact | undefined
let btn: HTMLElement
let container: HTMLElement
let opened: boolean = false
@ -50,16 +56,7 @@
selected = await client.findOne(_class, { _id: value })
}
$: if (value != null) {
updateSelected(value)
}
onMount(() => {
if (btn && show) {
btn.click()
show = false
}
})
$: if (value != null) updateSelected(value)
function getName (obj: Contact): string {
const isPerson = client.getHierarchy().isDerived(obj._class, contact.class.Person)
@ -67,12 +64,16 @@
}
</script>
<div class="antiSelect" bind:this={container}
on:click|preventDefault={() => {
btn.focus()
<div bind:this={container} class="min-w-0">
<Tooltip label={label} fill={width === '100%'} direction={labelDirection}>
<Button
icon={(size === 'x-large' && selected) ? undefined : IconPerson}
width={width ?? 'min-content'}
{size} {kind} {justify}
on:click={() => {
if (!opened && !readonly) {
opened = true
showPopup(UsersPopup, { _class, title, caption, allowDeselect, selected: value, titleDeselect }, container, (result) => {
showPopup(UsersPopup, { _class, allowDeselect, selected: value, titleDeselect, placeholder }, container, (result) => {
if (result === null) {
value = null
selected = undefined
@ -86,14 +87,17 @@
}
}}
>
<button class="button circle" class:selected={value} bind:this={btn}>
<Avatar avatar={selected ? selected.avatar : undefined} size={'medium'} />
</button>
<div class="group">
<span class="label"><Label label={title} /></span>
<span class="result" class:selected={value} class:not-selected={!value}>
{#if selected}{getName(selected)}{:else}<Label label={presentation.string.NotSelected} />{/if}
<span slot="content" style="overflow: hidden">
{#if selected}
{#if size === 'x-large'}
<UserInfo value={selected} size={'medium'} />
{:else}
{getName(selected)}
{/if}
{:else}
<Label label={label} />
{/if}
</span>
</div>
</Button>
</Tooltip>
</div>

View File

@ -14,24 +14,23 @@
-->
<script lang="ts">
import type { IntlString } from '@anticrm/platform'
import { translate } from '@anticrm/platform'
import { afterUpdate, createEventDispatcher } from 'svelte'
import ui, { Label, EditWithIcon, IconSearch } from '@anticrm/ui'
import { Tooltip, CheckBox } from '@anticrm/ui'
import UserInfo from './UserInfo.svelte'
import type { Ref, Class } from '@anticrm/core'
import type { Person } from '@anticrm/contact'
import { createQuery } from '../utils'
import presentation from '..'
import { ActionIcon, IconBlueCheck } from '@anticrm/ui'
export let _class: Ref<Class<Person>>
export let title: IntlString
export let caption: IntlString = ui.string.Suggested
export let selected: Ref<Person> | undefined
export let allowDeselect: boolean = false
export let titleDeselect: IntlString | undefined = undefined
export let placeholder: IntlString = presentation.string.Search
export let ignoreUsers: Ref<Person>[] = []
@ -41,29 +40,30 @@
const dispatch = createEventDispatcher()
const query = createQuery()
$: query.query<Person>(_class, { name: { $like: '%' + search + '%' }, _id: { $nin: ignoreUsers } }, result => { objects = result }, { limit: 200 })
let phTraslate: string = ''
$: if (placeholder) translate(placeholder, {}).then(res => { phTraslate = res })
afterUpdate(() => { dispatch('update', Date.now()) })
</script>
<div class="antiPopup antiPopup-withHeader antiPopup-withTitle">
<div class="ap-title"><Label label={title} /></div>
<div class="ap-header">
<EditWithIcon icon={IconSearch} bind:value={search} placeholder={ui.string.SearchDots} focus />
<div class="ap-caption"><Label label={caption} /></div>
<div class="selectPopup">
<div class="header">
<input type='text' bind:value={search} placeholder={phTraslate} on:input={(ev) => { }} on:change/>
</div>
<div class="ap-space" />
<div class="ap-scroll">
<div class="ap-box">
<div class="scroll">
<div class="box">
{#each objects as person}
<button class="ap-menuItem withCheck" on:click={() => { dispatch('close', person) }}>
<button class="menu-item flex-between" on:click={() => { dispatch('close', person) }}>
<UserInfo size={'x-small'} value={person} />
{#if allowDeselect && person._id === selected}
<div class="ap-check">
<ActionIcon direction={'top'} label={titleDeselect ?? presentation.string.Deselect} icon={IconBlueCheck} action={() => { dispatch('close', null) }} size={'small'} />
<div class="check" on:click|stopPropagation={() => { dispatch('close', null) }}>
<Tooltip label={titleDeselect ?? presentation.string.Deselect}>
<CheckBox checked primary />
</Tooltip>
</div>
{/if}
</button>
{/each}
</div>
</div>
<div class="ap-space" />
</div>

View File

@ -0,0 +1,25 @@
<!--
// Copyright © 2020, 2021 Anticrm Platform Contributors.
// Copyright © 2021 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
export let size: 'small' | 'medium' | 'large'
const fill: string = 'currentColor'
</script>
<svg class="svg-{size}" {fill} viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<path d="M8 4C6.89545 4 6 4.89545 6 6V6.5C6 7.60455 6.89545 8.5 8 8.5C9.10455 8.5 10 7.60455 10 6.5V6C10 4.89545 9.10455 4 8 4Z" />
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 15C11.866 15 15 11.866 15 8C15 4.13403 11.866 1 8 1C4.13403 1 1 4.13403 1 8C1 11.866 4.13403 15 8 15ZM5.12134 10.8787L4.1109 11.8891C3.1156 10.8938 2.5 9.5188 2.5 8C2.5 4.96246 4.96246 2.5 8 2.5C11.0375 2.5 13.5 4.96246 13.5 8C13.5 9.5188 12.8844 10.8938 11.8891 11.8891L10.8787 10.8787C10.316 10.3161 9.55298 10 8.75739 10H7.24261C6.44702 10 5.68396 10.316 5.12134 10.8787Z" />
</svg>

View File

@ -39,7 +39,9 @@ export default plugin(presentationId, {
Deselect: '' as IntlString,
AddSocialLinks: '' as IntlString,
Change: '' as IntlString,
Remove: '' as IntlString
Remove: '' as IntlString,
Search: '' as IntlString,
Unassigned: '' as IntlString
},
metadata: {
RequiredVersion: '' as Metadata<string>

View File

@ -182,6 +182,12 @@ p:last-child { margin-block-end: 0; }
.icon {
margin-right: .5rem;
color: var(--theme-content-dark-color);
&.circle {
padding: .25rem;
background-color: var(--popup-bg-hover);
border-radius: 50%;
}
}
.label {
min-width: 0;

View File

@ -35,6 +35,8 @@
color: #d6d6d6;
border: none;
caret-color: var(--caret-color);
&::placeholder { color: var(--content-color); }
}
}
@ -74,6 +76,14 @@
text-overflow: ellipsis;
overflow: hidden;
}
.check {
display: flex;
justify-content: flex-end;
align-items: center;
margin-left: 1rem;
width: 1rem;
height: 2rem;
}
&:hover { background-color: var(--popup-bg-hover); }
}
}

View File

@ -21,7 +21,7 @@
import { onMount } from 'svelte'
export let label: IntlString | undefined = undefined
export let kind: 'primary' | 'secondary' | 'no-border' | 'transparent' | 'dangerous' = 'secondary'
export let kind: 'primary' | 'secondary' | 'no-border' | 'transparent' | 'link' | 'dangerous' = 'secondary'
export let size: 'small' | 'medium' | 'large' | 'x-large' = 'medium'
export let icon: Asset | AnySvelteComponent | undefined = undefined
export let justify: 'left' | 'center' = 'center'
@ -33,6 +33,8 @@
export let input: HTMLButtonElement | undefined = undefined
$: iconOnly = label === undefined && $$slots.content === undefined
onMount(() => {
if (focus && input) {
input.focus()
@ -44,15 +46,15 @@
<button
bind:this={input}
class="button {kind} {size} jf-{justify}"
class:only-icon={label === undefined}
class:only-icon={iconOnly}
disabled={disabled || loading}
style={width ? 'width: ' + width : ''}
on:click
>
{#if icon && !loading}
<div class="btn-icon"
class:mr-1={label && kind === 'no-border'}
class:mr-2={label && kind !== 'no-border'}
class:mr-1={!iconOnly && kind === 'no-border'}
class:mr-2={!iconOnly && kind !== 'no-border'}
class:resetIconSize
>
<Icon {icon} size={'small'}/>
@ -63,6 +65,8 @@
{:else}
{#if label}
<Label {label} />
{:else if $$slots.content}
<slot name="content" />
{/if}
{/if}
</button>
@ -158,6 +162,15 @@
}
}
&.transparent:hover { background-color: var(--button-bg-hover); }
&.link {
padding: 0 .875rem;
&:hover {
color: var(--caption-color);
background-color: var(--body-color);
border-color: var(--divider-color);
.btn-icon { color: var(--content-color); }
}
}
&.primary {
padding: 0 1rem;
color: var(--white-color);

View File

@ -63,7 +63,7 @@
& .back {
fill: var(--theme-bg-check);
&.primary {
fill: var(--primary-button-enabled);
fill: var(--primary-bg-color);
}
}
& .check {

View File

@ -19,9 +19,8 @@
import Label from './Label.svelte'
import Icon from './Icon.svelte'
import { showPopup } from '..'
import type { AnySvelteComponent, ListItem } from '../types'
import DropdownPopup from './DropdownPopup.svelte'
import { showPopup, Button, Tooltip, DropdownPopup } from '..'
import type { AnySvelteComponent, ListItem, TooltipAligment } from '../types'
import Add from './icons/Add.svelte'
export let icon: Asset | AnySvelteComponent = Add
@ -29,23 +28,24 @@
export let placeholder: IntlString
export let items: ListItem[] = []
export let selected: ListItem | undefined = undefined
export let show: boolean = false
let btn: HTMLElement
export let kind: 'primary' | 'secondary' | 'no-border' | 'transparent' | 'link' | 'dangerous' = 'no-border'
export let size: 'small' | 'medium' | 'large' | 'x-large' = 'small'
export let justify: 'left' | 'center' = 'center'
export let width: string | undefined = undefined
export let labelDirection: TooltipAligment | undefined = undefined
let container: HTMLElement
let opened: boolean = false
onMount(() => {
if (btn && show) {
btn.click()
show = false
}
})
</script>
<div class="flex-row-center container" bind:this={container}
on:click|preventDefault={() => {
btn.focus()
<div bind:this={container} class="min-w-0">
<Tooltip label={label} fill={width === '100%'} direction={labelDirection}>
<Button
{icon}
width={width ?? 'min-content'}
{size} {kind} {justify}
on:click={() => {
if (!opened) {
opened = true
showPopup(DropdownPopup, { title: label, items, icon }, container, (result) => {
@ -55,51 +55,9 @@
}
}}
>
<div class="flex-center focused-button btn" class:selected bind:this={btn} tabindex={0} on:focus={() => container.click()}>
{#if selected && selected.image}
<img src={selected.image} alt={selected.label} />
{:else}
{#if typeof (icon) === 'string'}
<Icon {icon} size={'small'} />
{:else}
<svelte:component this={icon} size={'small'} />
{/if}
{/if}
</div>
<div class="selectUser">
<div class="title"><Label {label} /></div>
<div class="caption-color" class:empty={selected ? false : true}>
<span slot="content" style="overflow: hidden">
{#if selected}{selected.label}{:else}<Label label={placeholder} />{/if}
</span>
</Button>
</Tooltip>
</div>
</div>
</div>
<style lang="scss">
.container { cursor: pointer; }
.btn {
flex-shrink: 0;
width: 2.25rem;
height: 2.25rem;
color: var(--theme-caption-color);
background-color: transparent;
border: 1px solid var(--theme-card-divider);
border-radius: .5rem;
outline: none;
overflow: hidden;
}
.selected {
border-color: transparent;
img { max-width: fit-content; }
}
.selectUser {
margin-left: .75rem;
.title {
font-size: .75rem;
font-weight: 500;
color: var(--theme-content-accent-color);
}
.empty { color: var(--theme-content-trans-color); }
}
</style>

View File

@ -26,11 +26,10 @@
import { createEventDispatcher } from 'svelte'
import ui from '../plugin'
export let title: IntlString
export let caption: IntlString | undefined = undefined
export let label: IntlString
export let placeholder: IntlString | undefined = undefined
export let items: DropdownTextItem[]
export let selected: DropdownTextItem['id'] | undefined = undefined
export let header: boolean = false
let btn: HTMLElement
let opened: boolean = false
@ -52,7 +51,7 @@
on:click|preventDefault={() => {
if (!opened) {
opened = true
showPopup(DropdownLabelsPopup, { title, caption, items, selected, header }, btn, (result) => {
showPopup(DropdownLabelsPopup, { placeholder, items, selected }, btn, (result) => {
if (result) {
selected = result
dispatch('selected', result)
@ -62,7 +61,7 @@
}
}}
>
<div class="overflow-label label"><Label label={title} /></div>
<div class="overflow-label label"><Label label={label} /></div>
<div class="flex-row-center space">
<span class="mr-1">
{#if opened}

View File

@ -15,53 +15,36 @@
<script lang="ts">
import type { IntlString } from '@anticrm/platform'
import { translate } from '@anticrm/platform'
import { createEventDispatcher } from 'svelte'
import Label from './Label.svelte'
import EditWithIcon from './EditWithIcon.svelte'
import IconSearch from './icons/Search.svelte'
import IconBlueCheck from './icons/BlueCheck.svelte'
import CheckBox from './CheckBox.svelte'
import type { DropdownTextItem } from '../types'
import plugin from '../plugin'
export let title: IntlString | undefined = undefined
export let caption: IntlString = plugin.string.Suggested
export let placeholder: IntlString = plugin.string.SearchDots
export let items: DropdownTextItem[]
export let selected: DropdownTextItem['id'] | undefined = undefined
export let header: boolean = false
let search: string = ''
let phTraslate: string = ''
$: translate(placeholder, {}).then(res => { phTraslate = res })
const dispatch = createEventDispatcher()
</script>
<div
class="antiPopup"
class:antiPopup-withHeader={header}
class:antiPopup-withTitle={title}
>
{#if header}
{#if title}
<div class="ap-title"><Label label={title} /></div>
{:else}
<div class="ap-space" />
{/if}
<div class="ap-header">
<EditWithIcon icon={IconSearch} bind:value={search} placeholder={plugin.string.SearchDots} focus />
<div class="ap-caption"><Label label={caption} /></div>
<div class="selectPopup">
<div class="header">
<input type='text' bind:value={search} placeholder={phTraslate} on:input={(ev) => { }} on:change/>
</div>
{/if}
<div class="ap-space" />
<div class="ap-scroll">
<div class="ap-box">
<div class="scroll">
<div class="box">
{#each items.filter((x) => x.label.toLowerCase().includes(search.toLowerCase())) as item}
<button class="ap-menuItem flex-row-center h-9" on:click={() => { dispatch('close', item.id) }}>
<div class="flex-grow caption-color">{item.label}</div>
<button class="menu-item flex-between" on:click={() => { dispatch('close', item.id) }}>
<div class="flex-grow caption-color lines-limit-2">{item.label}</div>
{#if item.id === selected}
<div class="ap-check"><IconBlueCheck size={'small'} /></div>
<div class="check"><CheckBox checked primary /></div>
{/if}
</button>
{/each}
</div>
</div>
<div class="ap-space" />
</div>

View File

@ -14,46 +14,31 @@
-->
<script lang="ts">
import type { Asset, IntlString } from '@anticrm/platform'
import { translate } from '@anticrm/platform'
import { createEventDispatcher } from 'svelte'
import Label from './Label.svelte'
import EditWithIcon from './EditWithIcon.svelte'
import IconSearch from './icons/Search.svelte'
import type { AnySvelteComponent, ListItem } from '../types'
import plugin from '../plugin'
import Icon from './Icon.svelte'
export let title: IntlString | undefined = undefined
export let icon: Asset | AnySvelteComponent
export let caption: IntlString = plugin.string.Suggested
export let placeholder: IntlString = plugin.string.SearchDots
export let items: ListItem[]
export let header: boolean = true
let search: string = ''
let phTraslate: string = ''
$: if (placeholder) translate(placeholder, {}).then(res => { phTraslate = res })
const dispatch = createEventDispatcher()
</script>
<div
class="antiPopup"
class:antiPopup-withHeader={header}
class:antiPopup-withTitle={title}
>
{#if header}
{#if title}
<div class="ap-title"><Label label={title} /></div>
{:else}
<div class="ap-space" />
{/if}
<div class="ap-header">
<EditWithIcon icon={IconSearch} bind:value={search} placeholder={plugin.string.SearchDots} focus />
<div class="ap-caption"><Label label={caption} /></div>
<div class="selectPopup">
<div class="header">
<input type='text' bind:value={search} placeholder={phTraslate} on:input={(ev) => { }} on:change/>
</div>
{/if}
<div class="ap-space" />
<div class="ap-scroll">
<div class="ap-box">
<div class="scroll">
<div class="box">
{#each items.filter((x) => x.label.toLowerCase().includes(search.toLowerCase())) as item}
<button class="ap-menuItem" on:click={() => { dispatch('close', item) }}>
<button class="menu-item flex-between" on:click={() => { dispatch('close', item) }}>
<div class="flex-center img" class:image={item.image}>
{#if item.image}
<img src={item.image} alt={item.label} />
@ -70,19 +55,17 @@
{/each}
</div>
</div>
<div class="ap-space" />
</div>
<style lang="scss">
.img {
margin-right: .75rem;
flex-shrink: 0;
width: 2.25rem;
height: 2.25rem;
color: var(--theme-caption-color);
background-color: transparent;
border: 1px solid var(--theme-card-divider);
border-radius: .5rem;
width: 1.5rem;
height: 1.5rem;
color: var(--caption-color);
background-color: var(--popup-bg-hover);
border-radius: 50%;
outline: none;
overflow: hidden;
}

View File

@ -16,7 +16,7 @@
import type { Asset, IntlString } from '@anticrm/platform'
import { translate } from '@anticrm/platform'
import { createEventDispatcher } from 'svelte'
import { Icon, Label } from '@anticrm/ui'
import { Icon, Label } from '..'
// export let _class: Ref<Class<Space>>
// export let spaceQuery: DocumentQuery<Space> | undefined

View File

@ -42,7 +42,7 @@ export { default as Progress } from './components/Progress.svelte'
export { default as Tabs } from './components/Tabs.svelte'
export { default as ScrollBox } from './components/ScrollBox.svelte'
export { default as PopupMenu } from './components/PopupMenu.svelte'
// export { default as PopupItem } from './components/PopupItem.svelte'
export { default as SelectPopup } from './components/SelectPopup.svelte'
export { default as TextArea } from './components/TextArea.svelte'
export { default as Section } from './components/Section.svelte'
export { default as DatePickerPopup } from './components/calendar/DatePickerPopup.svelte'

View File

@ -34,7 +34,7 @@
href="#{encodeURIComponent([view.component.EditDoc, value._id, value._class].join('|'))}"
on:click={onClick}
>
<div class="icon"><Company size={'small'} /></div>
<div class="icon circle"><Company size={'small'} /></div>
<span class="label">{value.name}</span>
</a>
{/if}

View File

@ -19,6 +19,7 @@
import { IntlString } from '@anticrm/platform'
import { createQuery } from '@anticrm/presentation'
import { Dropdown } from '@anticrm/ui'
import type { TooltipAligment } from '@anticrm/ui'
import { ListItem } from '@anticrm/ui/src/types'
import { createEventDispatcher } from 'svelte'
import contact from '../plugin'
@ -27,6 +28,12 @@
export let value: Ref<Organization> | undefined
export let label: IntlString = contact.string.Organization
export let kind: 'primary' | 'secondary' | 'no-border' | 'transparent' | 'link' | 'dangerous' = 'no-border'
export let size: 'small' | 'medium' | 'large' | 'x-large' = 'small'
export let justify: 'left' | 'center' = 'center'
export let width: string | undefined = undefined
export let labelDirection: TooltipAligment | undefined = undefined
const query = createQuery()
const dispatch = createEventDispatcher()
@ -63,4 +70,8 @@
}
</script>
<Dropdown icon={Company} label={label} placeholder={label} {items} bind:selected />
<Dropdown
icon={Company} label={label} placeholder={label}
{items} bind:selected
{kind} {size} {justify} {width} {labelDirection}
/>

View File

@ -19,5 +19,6 @@
</script>
<svg class="svg-{size}" {fill} viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<path d="M13.5,13H13V5c0-0.2-0.2-0.4-0.4-0.5L7.1,3C7,3,6.8,3,6.7,3.1C6.6,3.2,6.5,3.3,6.5,3.5V6h-3C3.2,6,3,6.2,3,6.5V13H2.5 C2.2,13,2,13.2,2,13.5S2.2,14,2.5,14h11c0.3,0,0.5-0.2,0.5-0.5S13.8,13,13.5,13z M4,13V7h4.5v6H4z M9,6H7.5V4.2L12,5.4V13H9.5V6.5 C9.5,6.2,9.3,6,9,6z"/>
<path d="M3.25 3C3.11193 3 2.99836 2.88751 3.01541 2.7505C3.13822 1.76368 3.97992 1 5 1H11C12.0201 1 12.8618 1.76368 12.9846 2.7505C13.0016 2.88751 12.8881 3 12.75 3L3.25 3Z" />
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.5 4C3.22386 4 3 4.22386 3 4.5V14.5C3 14.7761 3.22386 15 3.5 15H5.5C5.77614 15 6 14.7761 6 14.5V12.5C6 12.2239 6.22386 12 6.5 12H9.5C9.77614 12 10 12.2239 10 12.5V14.5C10 14.7761 10.2239 15 10.5 15H12.5C12.7761 15 13 14.7761 13 14.5V4.5C13 4.22386 12.7761 4 12.5 4H3.5ZM11 8C10.4477 8 10 8.44772 10 9V9.6C10 9.82091 10.1791 10 10.4 10H11.6C11.8209 10 12 9.82091 12 9.6V9C12 8.44772 11.5523 8 11 8ZM10 6C10 5.44772 10.4477 5 11 5C11.5523 5 12 5.44772 12 6V6.6C12 6.82091 11.8209 7 11.6 7H10.4C10.1791 7 10 6.82091 10 6.6V6ZM8 5C7.44772 5 7 5.44772 7 6V6.6C7 6.82091 7.17909 7 7.4 7H8.6C8.82091 7 9 6.82091 9 6.6V6C9 5.44772 8.55228 5 8 5ZM7 9C7 8.44772 7.44772 8 8 8C8.55228 8 9 8.44772 9 9V9.6C9 9.82091 8.82091 10 8.6 10H7.4C7.17909 10 7 9.82091 7 9.6V9ZM5 8C4.44772 8 4 8.44772 4 9V9.6C4 9.82091 4.17909 10 4.4 10H5.6C5.82091 10 6 9.82091 6 9.6V9C6 8.44772 5.55228 8 5 8ZM4 6C4 5.44772 4.44772 5 5 5C5.55228 5 6 5.44772 6 6V6.6C6 6.82091 5.82091 7 5.6 7H4.4C4.17909 7 4 6.82091 4 6.6V6Z" />
</svg>

View File

@ -87,11 +87,10 @@
focus
/>
<DropdownLabels
header
items={categories}
bind:selected={doc.attachedTo}
caption={inventory.string.Categories}
title={inventory.string.Category}
placeholder={inventory.string.Categories}
label={inventory.string.Category}
/>
</Grid>
</Card>

View File

@ -99,7 +99,7 @@
}}
>
<StatusControl slot="error" {status} />
<Grid column={1} rowGap={1.5}>
<Grid column={1} rowGap={1}>
<EditBox
label={lead.string.LeadName}
bind:value={title}
@ -110,9 +110,10 @@
/>
<UserBox
_class={contact.class.Contact}
title={lead.string.Customer}
caption={lead.string.SelectCustomer}
label={lead.string.Customer}
placeholder={lead.string.SelectCustomer}
bind:value={customer}
kind={'link'} size={'x-large'} justify={'left'} width={'100%'} labelDirection={'left'}
/>
</Grid>
</Card>

View File

@ -36,7 +36,7 @@
</script>
{#if object !== undefined}
<Grid column={1} rowGap={1.5}>
<Grid column={2} rowGap={1}>
<EditBox
label={lead.string.LeadName}
bind:value={object.title}
@ -48,8 +48,9 @@
/>
<UserBox
_class={contact.class.Contact}
title={lead.string.Customer}
caption={lead.string.SelectCustomer}
label={lead.string.Customer}
placeholder={lead.string.SelectCustomer}
kind={'link'} size={'x-large'} justify={'left'} width={'100%'}
bind:value={object.attachedTo}
on:change={() => {
change('attachedTo', object.attachedTo)

View File

@ -131,17 +131,24 @@
}}
>
<StatusControl slot="error" {status} />
<Grid column={1} rowGap={1.75}>
<Grid column={1} rowGap={1}>
{#if !preserveCandidate}
<UserBox _class={contact.class.Person} title={recruit.string.Candidate} caption={recruit.string.Candidates} bind:value={doc.attachedTo} />
<UserBox
_class={contact.class.Person}
label={recruit.string.Candidate}
placeholder={recruit.string.Candidates}
bind:value={doc.attachedTo}
kind={'link'} size={'x-large'} justify={'left'} width={'100%'} labelDirection={'left'}
/>
{/if}
<UserBox
_class={contact.class.Employee}
title={recruit.string.AssignRecruiter}
caption={recruit.string.Recruiters}
label={recruit.string.AssignRecruiter}
placeholder={recruit.string.Recruiters}
bind:value={doc.assignee}
allowDeselect
titleDeselect={recruit.string.UnAssignRecruiter}
kind={'link'} size={'x-large'} justify={'left'} width={'100%'} labelDirection={'left'}
/>
</Grid>
</Card>

View File

@ -63,7 +63,10 @@
>
<Grid column={1} rowGap={1.5}>
<EditBox label={recruit.string.VacancyName} bind:value={name} icon={Vacancy} placeholder={recruit.string.VacancyPlaceholder} maxWidth={'16rem'} focus/>
<OrganizationSelector bind:value={company} label={recruit.string.Company} />
<OrganizationSelector
bind:value={company} label={recruit.string.Company}
kind={'link'} size={'x-large'} justify={'left'} width={'100%'} labelDirection={'left'}
/>
<Component is={task.component.KanbanTemplateSelector} props={{
folders: [recruit.space.VacancyTemplates],

View File

@ -163,15 +163,19 @@
{#if !preserveCandidate}
<UserBox
_class={contact.class.Person}
title={recruit.string.Candidate}
caption={recruit.string.Candidates}
label={recruit.string.Candidate}
placeholder={recruit.string.Candidates}
bind:value={doc.attachedTo}
kind={'link'} size={'x-large'} justify={'left'} width={'100%'} labelDirection={'left'}
/>
{:else}
<div></div>
{/if}
<EditBox label={recruit.string.Location} icon={recruit.icon.Location} bind:value={location} maxWidth={'13rem'} />
<OrganizationSelector bind:value={company} label={recruit.string.Company} />
<OrganizationSelector
bind:value={company} 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.DueDate} bind:value={dueDate} withTime />
<Row>

View File

@ -62,8 +62,8 @@
<UserBox
readonly
_class={contact.class.Person}
title={recruit.string.Candidate}
caption={recruit.string.Candidates}
label={recruit.string.Candidate}
placeholder={recruit.string.Candidates}
value={object.attachedTo}
/>
</div>

View File

@ -122,7 +122,7 @@
<div class="text-sm mt-4">
<DropdownLabels
title={tags.string.CategoryLabel}
label={tags.string.CategoryLabel}
bind:selected={category}
items={categoryItems}
on:selected={() => {

View File

@ -127,7 +127,7 @@
</div>
<div class="text-sm mt-4">
<DropdownLabels title={tags.string.CategoryLabel} bind:selected={data.category} items={categoryItems} />
<DropdownLabels label={tags.string.CategoryLabel} bind:selected={data.category} items={categoryItems} />
</div>
</div>
</div>

View File

@ -118,8 +118,8 @@
/>
<UserBox
_class={contact.class.Employee}
title={plugin.string.TaskAssignee}
caption={plugin.string.AssignThisTask}
label={plugin.string.TaskAssignee}
placeholder={plugin.string.AssignThisTask}
bind:value={assignee}
allowDeselect
titleDeselect={plugin.string.TaskUnAssign}

View File

@ -55,8 +55,8 @@
<div class="flex-center">
<UserBox
_class={getAssigneeClass(object)}
title={assigneeTitle}
caption={assigneeTitle}
label={assigneeTitle}
placeholder={assigneeTitle}
bind:value={object.assignee}
on:change={change}
allowDeselect

View File

@ -41,4 +41,4 @@
}
</script>
<DropdownLabels {items} bind:selected={selectedItem} title={plugin.string.States} />
<DropdownLabels {items} bind:selected={selectedItem} label={plugin.string.States} />

View File

@ -33,13 +33,14 @@
"High": "High",
"Medium": "Medium",
"Low": "Low",
"Unassigned": "Unassigned",
"Title": "Title",
"Description": "",
"Status": "",
"Number": "Number",
"Assignee": "Assignee",
"AssignTo": "Assign to",
"AssignTo": "Assign to...",
"AssignedTo": "Assigned to {value}",
"Parent": "Set parent issue\u2026",
"BlockedBy": "",

View File

@ -29,6 +29,7 @@
"High": "Высокий",
"Medium": "Средний",
"Low": "Низкий",
"Unassigned": "Не назначен",
"AddIssueTooltip": "Добавить задачу\u2026",
"Title": "Заголовок",
@ -36,6 +37,7 @@
"Status": "",
"Number": "Number",
"Assignee": "Исполнитель",
"AssignTo": "Назначить...",
"Parent": "Указать родительскую задачу\u2026",
"BlockedBy": "",
"RelatedTo": "",

View File

@ -19,12 +19,11 @@
import { getClient, UserBox } from '@anticrm/presentation'
import { Issue, IssuePriority, IssueStatus, Team } from '@anticrm/tracker'
import { StyledTextBox } from '@anticrm/text-editor'
import { EditBox, Button, showPopup, DatePresenter } from '@anticrm/ui'
import { EditBox, Button, showPopup, DatePresenter, SelectPopup } from '@anticrm/ui'
import { createEventDispatcher } from 'svelte'
import tracker from '../plugin'
import { calcRank } from '../utils'
import Card from './Card.svelte'
import SelectPopup from './SelectPopup.svelte'
import StatusSelector from './StatusSelector.svelte'
import PrioritySelector from './PrioritySelector.svelte'
@ -131,11 +130,11 @@
<PrioritySelector bind:priority={object.priority} />
<UserBox
_class={contact.class.Employee}
title={tracker.string.Assignee}
caption={tracker.string.Assignee}
label={tracker.string.Assignee}
placeholder={tracker.string.AssignTo}
bind:value={assignee}
allowDeselect
titleDeselect={tracker.string.TaskUnAssign}
titleDeselect={tracker.string.Unassigned}
/>
<Button
label={tracker.string.Labels}

View File

@ -14,10 +14,9 @@
-->
<script lang="ts">
import { IssuePriority } from '@anticrm/tracker'
import { Button, showPopup } from '@anticrm/ui'
import { Button, showPopup, SelectPopup } from '@anticrm/ui'
import { issuePriorities } from '../utils'
import tracker from '../plugin'
import SelectPopup from './SelectPopup.svelte'
export let priority: IssuePriority

View File

@ -14,10 +14,9 @@
-->
<script lang="ts">
import { IssueStatus } from '@anticrm/tracker'
import { Button, showPopup } from '@anticrm/ui'
import { Button, showPopup, SelectPopup } from '@anticrm/ui'
import { issueStatuses } from '../utils'
import tracker from '../plugin'
import SelectPopup from './SelectPopup.svelte'
export let status: IssueStatus

View File

@ -97,11 +97,11 @@
/>
<UserBox
_class={contact.class.Employee}
title={tracker.string.Assignee}
caption={tracker.string.Assignee}
label={tracker.string.Assignee}
placeholder={tracker.string.Assignee}
bind:value={object.assignee}
allowDeselect
titleDeselect={tracker.string.TaskUnAssign}
titleDeselect={tracker.string.Unassigned}
on:change={() => change('assignee', object?.assignee)}
/>
</Grid>

View File

@ -79,7 +79,7 @@ export default mergeIds(trackerId, tracker, {
IssueTitlePlaceholder: '' as IntlString,
IssueDescriptionPlaceholder: '' as IntlString,
TaskUnAssign: '' as IntlString,
Unassigned: '' as IntlString,
AddIssueTooltip: '' as IntlString
},
component: {

View File

@ -65,7 +65,7 @@ test.describe('recruit tests', () => {
await page.click('button:has-text("Create")')
await page.locator(`tr:has-text("${vacancyId}") >> text=APP-`).click()
await page.click('text=Assigned recruiter Not selected')
await page.click('button:has-text("Assigned recruiter")')
await page.click('button:has-text("Rosamund Chen")')
})
@ -83,7 +83,7 @@ test.describe('recruit tests', () => {
// Create Applicatio n1
await page.click('button:has-text("Application")')
await page.click('text=Not selected')
await page.click('button:has-text("Candidate")')
await page.click('button:has-text("Alex P.")')
await page.click('button:has-text("Create")')
@ -154,7 +154,7 @@ test.describe('recruit tests', () => {
// Click button:has-text("Apple")
// await page.click('button:has-text("Apple")')
// Click text=Candidate Not selected >> span
await page.click('text=Candidate Not selected >> span')
await page.click('button:has-text("Candidate")')
// Click button:has-text("Andrey P.")
await page.click('button:has-text("Andrey P.")')
// Click text=Create