mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-14 20:39:03 +00:00
Add TagsDropdownEditor. Fix layout. (#1422)
Signed-off-by: Alexander Platov <sas_lord@mail.ru>
This commit is contained in:
parent
f0cca26e9b
commit
e5187e7d4a
@ -88,6 +88,7 @@ table {
|
|||||||
--modal-padding: 1.5rem;
|
--modal-padding: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p { user-select: text; }
|
||||||
p:first-child { margin-block-start: 0; } // First and last padding
|
p:first-child { margin-block-start: 0; } // First and last padding
|
||||||
p:last-child { margin-block-end: 0; }
|
p:last-child { margin-block-end: 0; }
|
||||||
|
|
||||||
@ -109,8 +110,8 @@ p:last-child { margin-block-end: 0; }
|
|||||||
.inline-flex { display: inline-flex; }
|
.inline-flex { display: inline-flex; }
|
||||||
.flex-grow { flex-grow: 1; }
|
.flex-grow { flex-grow: 1; }
|
||||||
.flex-no-shrink { flex-shrink: 0; }
|
.flex-no-shrink { flex-shrink: 0; }
|
||||||
.flex-wrap { flex-wrap: wrap; }
|
.flex-wrap { flex-wrap: wrap !important; }
|
||||||
.flex-nowrap { flex-wrap: nowrap; }
|
.flex-nowrap { flex-wrap: nowrap !important; }
|
||||||
.flex-baseline {
|
.flex-baseline {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: baseline;
|
align-items: baseline;
|
||||||
@ -446,6 +447,7 @@ a.no-line {
|
|||||||
.cursor-default { cursor: default; }
|
.cursor-default { cursor: default; }
|
||||||
|
|
||||||
.pointer-events-none { pointer-events: none; }
|
.pointer-events-none { pointer-events: none; }
|
||||||
|
.select-text { user-select: text; }
|
||||||
|
|
||||||
/* Text */
|
/* Text */
|
||||||
|
|
||||||
|
@ -188,7 +188,7 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
padding: .75rem;
|
padding: .75rem;
|
||||||
height: auto;
|
height: auto;
|
||||||
border-top: 1px solid var(--button-bg-color);
|
border-top: 1px solid var(--divider-color);
|
||||||
|
|
||||||
&.reverse { flex-direction: row-reverse; }
|
&.reverse { flex-direction: row-reverse; }
|
||||||
&__error {
|
&__error {
|
||||||
|
@ -80,12 +80,6 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
&:checked + .checkSVG {
|
&:checked + .checkSVG {
|
||||||
& .back {
|
|
||||||
fill: var(--theme-bg-check);
|
|
||||||
&.primary {
|
|
||||||
fill: var(--primary-bg-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
& .check {
|
& .check {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
fill: var(--theme-button-bg-enabled);
|
fill: var(--theme-button-bg-enabled);
|
||||||
|
@ -50,16 +50,16 @@
|
|||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
margin-right: .25rem;
|
margin-right: .25rem;
|
||||||
color: var(--theme-content-color);
|
color: var(--content-color);
|
||||||
}
|
}
|
||||||
&:hover .icon { color: var(--theme-caption-color); }
|
&:hover .icon { color: var(--accent-color); }
|
||||||
&:active .icon { color: var(--theme-content-accent-color); }
|
&:active .icon { color: var(--caption-color); }
|
||||||
}
|
}
|
||||||
.disabled {
|
.disabled {
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
color: var(--theme-content-trans-color);
|
color: var(--dark-color);
|
||||||
.icon { color: var(--theme-content-trans-color); }
|
.icon { color: var(--dark-color); }
|
||||||
&:hover .icon { color: var(--theme-content-trans-color); }
|
&:hover .icon { color: var(--dark-color); }
|
||||||
&:active .icon { color: var(--theme-content-trans-color); }
|
&:active .icon { color: var(--dark-color); }
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -66,7 +66,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
<div class="flex-col h-full min-h-0" class:background-bg-accent={!transparent}>
|
<div class="flex-col h-full min-h-0" class:background-bg-accent={!transparent}>
|
||||||
<Scroller>
|
<Scroller>
|
||||||
<div class="p-10">
|
<div class="p-10 select-text">
|
||||||
{#if txes}
|
{#if txes}
|
||||||
<Grid column={1} rowGap={1.5}>
|
<Grid column={1} rowGap={1.5}>
|
||||||
{#each txes as tx (tx.tx._id)}
|
{#each txes as tx (tx.tx._id)}
|
||||||
|
@ -47,7 +47,7 @@
|
|||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.content {
|
.content {
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
color: var(--theme-caption-color);
|
color: var(--accent-color);
|
||||||
background: var(--theme-bg-accent-color);
|
background: var(--theme-bg-accent-color);
|
||||||
border: 1px solid var(--theme-bg-accent-color);
|
border: 1px solid var(--theme-bg-accent-color);
|
||||||
border-radius: .75rem;
|
border-radius: .75rem;
|
||||||
|
@ -399,10 +399,7 @@
|
|||||||
dispatch('close')
|
dispatch('close')
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class="flex-row-center">
|
<div class="flex-between">
|
||||||
<div class="mr-4">
|
|
||||||
<EditableAvatar bind:direct={avatar} avatar={object.avatar} size={'large'} on:remove={removeAvatar} on:done={onAvatarDone} />
|
|
||||||
</div>
|
|
||||||
<div class="flex-col">
|
<div class="flex-col">
|
||||||
<EditBox placeholder={recruit.string.PersonFirstNamePlaceholder} bind:value={firstName} kind={'large-style'} maxWidth={'32rem'} focus />
|
<EditBox placeholder={recruit.string.PersonFirstNamePlaceholder} bind:value={firstName} kind={'large-style'} maxWidth={'32rem'} focus />
|
||||||
<EditBox placeholder={recruit.string.PersonLastNamePlaceholder} bind:value={lastName} kind={'large-style'} maxWidth={'32rem'} />
|
<EditBox placeholder={recruit.string.PersonLastNamePlaceholder} bind:value={lastName} kind={'large-style'} maxWidth={'32rem'} />
|
||||||
@ -411,15 +408,28 @@
|
|||||||
</div>
|
</div>
|
||||||
<EditBox placeholder={recruit.string.Location} bind:value={object.city} kind={'small-style'} maxWidth={'32rem'} />
|
<EditBox placeholder={recruit.string.Location} bind:value={object.city} kind={'small-style'} maxWidth={'32rem'} />
|
||||||
</div>
|
</div>
|
||||||
|
<div class="ml-4">
|
||||||
|
<EditableAvatar bind:direct={avatar} avatar={object.avatar} size={'large'} on:remove={removeAvatar} on:done={onAvatarDone} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{#if channels.length > 0}
|
{#if channels.length > 0}
|
||||||
<div class="ml-22"><ChannelsView value={channels} size={'small'} on:click /></div>
|
<ChannelsView value={channels} size={'small'} on:click />
|
||||||
{/if}
|
{/if}
|
||||||
<svelte:fragment slot="pool">
|
<svelte:fragment slot="pool">
|
||||||
|
<Button
|
||||||
|
icon={contact.icon.SocialEdit}
|
||||||
|
kind={'no-border'}
|
||||||
|
size={'small'}
|
||||||
|
on:click={(ev) =>
|
||||||
|
showPopup(contact.component.SocialEditor, { values: channels }, ev.target, (result) => {
|
||||||
|
if (result !== undefined) channels = result
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
<YesNo label={recruit.string.Onsite} tooltip={recruit.string.WorkLocationPreferences} bind:value={object.onsite} />
|
<YesNo label={recruit.string.Onsite} tooltip={recruit.string.WorkLocationPreferences} bind:value={object.onsite} />
|
||||||
<YesNo label={recruit.string.Remote} tooltip={recruit.string.WorkLocationPreferences} bind:value={object.remote} />
|
<YesNo label={recruit.string.Remote} tooltip={recruit.string.WorkLocationPreferences} bind:value={object.remote} />
|
||||||
<Component
|
<Component
|
||||||
is={tags.component.TagsEditor}
|
is={tags.component.TagsDropdownEditor}
|
||||||
props={{
|
props={{
|
||||||
items: skills,
|
items: skills,
|
||||||
key,
|
key,
|
||||||
@ -437,30 +447,41 @@
|
|||||||
/>
|
/>
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
<svelte:fragment slot="footer">
|
<svelte:fragment slot="footer">
|
||||||
<Button
|
<div
|
||||||
icon={contact.icon.SocialEdit}
|
class="flex-center resume"
|
||||||
kind={'transparent'}
|
class:solid={dragover || resume.uuid}
|
||||||
on:click={(ev) =>
|
on:dragover|preventDefault={() => {
|
||||||
showPopup(contact.component.SocialEditor, { values: channels }, ev.target, (result) => {
|
dragover = true
|
||||||
if (result !== undefined) channels = result
|
}}
|
||||||
})
|
on:dragleave={() => {
|
||||||
}
|
dragover = false
|
||||||
/>
|
}}
|
||||||
<Button
|
on:drop|preventDefault|stopPropagation={drop}
|
||||||
icon={!resume.uuid && loading ? Spinner : IconAttachment}
|
>
|
||||||
kind={'transparent'}
|
{#if resume.uuid}
|
||||||
on:click={() => { inputFile.click() }}
|
<Link
|
||||||
/>
|
label={resume.name}
|
||||||
<input bind:this={inputFile} type="file" name="file" id="file" style="display: none" on:change={fileSelected} />
|
icon={FileIcon}
|
||||||
{#if resume.uuid}
|
maxLenght={16}
|
||||||
<Button
|
on:click={() => {
|
||||||
icon={FileIcon}
|
showPopup(PDFViewer, { file: resume.uuid, name: resume.name }, 'right')
|
||||||
kind={'link-bordered'}
|
}}
|
||||||
on:click={() => {
|
/>
|
||||||
showPopup(PDFViewer, { file: resume.uuid, name: resume.name }, 'right')
|
{:else}
|
||||||
}}
|
{#if loading}
|
||||||
><svelte:fragment slot="content">{resume.name}</svelte:fragment></Button>
|
<Link label={'Uploading...'} icon={Spinner} disabled />
|
||||||
{/if}
|
{:else}
|
||||||
|
<Link
|
||||||
|
label={'Add or drop resume'}
|
||||||
|
icon={FileUpload}
|
||||||
|
on:click={() => {
|
||||||
|
inputFile.click()
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
<input bind:this={inputFile} type="file" name="file" id="file" style="display: none" on:change={fileSelected} />
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
{#if matches.length > 0}
|
{#if matches.length > 0}
|
||||||
<div class="flex-row-center error-color">
|
<div class="flex-row-center error-color">
|
||||||
<IconInfo size={'small'} />
|
<IconInfo size={'small'} />
|
||||||
@ -472,3 +493,14 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.resume {
|
||||||
|
padding: .5rem .75rem;
|
||||||
|
background: var(--accent-bg-color);
|
||||||
|
border: 1px dashed var(--divider-color);
|
||||||
|
border-radius: .5rem;
|
||||||
|
|
||||||
|
&.solid { border-style: solid; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export let size: 'small' | 'medium' | 'large'
|
export let size: 'small' | 'medium' | 'large'
|
||||||
const fill: string = 'var(--theme-caption-color)'
|
const fill: string = 'currentColor'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svg class="svg-{size}" {fill} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
<svg class="svg-{size}" {fill} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||||
|
@ -41,7 +41,7 @@
|
|||||||
>
|
>
|
||||||
{name}
|
{name}
|
||||||
{#if action}
|
{#if action}
|
||||||
<div class="ml-2">
|
<div class="ml-1">
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
icon={action}
|
icon={action}
|
||||||
size={'small'}
|
size={'small'}
|
||||||
|
@ -57,11 +57,10 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<TagsEditor
|
<TagsEditor
|
||||||
{elements}
|
bind:elements
|
||||||
{key}
|
{key}
|
||||||
{items}
|
bind:items
|
||||||
targetClass={_class}
|
targetClass={_class}
|
||||||
on:open={(evt) => addRef(evt.detail)}
|
on:open={(evt) => addRef(evt.detail)}
|
||||||
on:delete={(evt) => removeTag(evt.detail)}
|
on:delete={(evt) => removeTag(evt.detail)}
|
||||||
countLabel={key.attr.label}
|
|
||||||
/>
|
/>
|
||||||
|
@ -0,0 +1,93 @@
|
|||||||
|
<!--
|
||||||
|
// Copyright © 2022 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 { AttachedDoc, Class, Collection, Doc, Ref } from '@anticrm/core'
|
||||||
|
import { IntlString, translate } from '@anticrm/platform'
|
||||||
|
import { KeyedAttribute } from '@anticrm/presentation'
|
||||||
|
import { TagElement, TagReference } from '@anticrm/tags'
|
||||||
|
import type { ButtonKind, ButtonSize, TooltipAlignment } from '@anticrm/ui'
|
||||||
|
import { Button, showPopup, Tooltip } from '@anticrm/ui'
|
||||||
|
import { createEventDispatcher } from 'svelte'
|
||||||
|
import tags from '../plugin'
|
||||||
|
import TagsPopup from './TagsPopup.svelte'
|
||||||
|
|
||||||
|
export let items: TagReference[] = []
|
||||||
|
export let targetClass: Ref<Class<Doc>>
|
||||||
|
export let key: KeyedAttribute
|
||||||
|
export let elements: Map<Ref<TagElement>, TagElement>
|
||||||
|
export let countLabel: IntlString
|
||||||
|
|
||||||
|
export let kind: ButtonKind = 'no-border'
|
||||||
|
export let size: ButtonSize = 'small'
|
||||||
|
export let justify: 'left' | 'center' = 'center'
|
||||||
|
export let width: string | undefined = undefined
|
||||||
|
export let labelDirection: TooltipAlignment | undefined = undefined
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
|
let keyLabel: string = ''
|
||||||
|
|
||||||
|
$: itemLabel = (key.attr.type as Collection<AttachedDoc>).itemLabel
|
||||||
|
|
||||||
|
$: translate(itemLabel ?? key.attr.label, {}).then((v) => {
|
||||||
|
keyLabel = v
|
||||||
|
})
|
||||||
|
|
||||||
|
async function addRef (tag: TagElement): Promise<void> {
|
||||||
|
dispatch('open', tag)
|
||||||
|
}
|
||||||
|
async function addTag (evt: Event): Promise<void> {
|
||||||
|
showPopup(
|
||||||
|
TagsPopup,
|
||||||
|
{
|
||||||
|
targetClass,
|
||||||
|
selected: items.map((it) => it.tag),
|
||||||
|
keyLabel
|
||||||
|
},
|
||||||
|
evt.target as HTMLElement,
|
||||||
|
() => { },
|
||||||
|
(result) => {
|
||||||
|
if (result != undefined) {
|
||||||
|
if (result.action === 'add') addRef(result.tag)
|
||||||
|
else if (result.action === 'remove') removeTag(items.filter(it => it.tag === result.tag._id)[0]._id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function removeTag (id: Ref<TagReference>): Promise<void> {
|
||||||
|
dispatch('delete', id)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Tooltip label={key.attr.label} direction={labelDirection}>
|
||||||
|
<Button
|
||||||
|
icon={tags.icon.Tags}
|
||||||
|
label={items.length > 0 ? undefined : key.attr.label}
|
||||||
|
width={width ?? 'min-content'}
|
||||||
|
{kind} {size} {justify}
|
||||||
|
on:click={addTag}
|
||||||
|
>
|
||||||
|
<svelte:fragment slot="content">
|
||||||
|
{#if items.length > 0}
|
||||||
|
<div class="flex-row-center flex-nowrap">
|
||||||
|
{#await translate(countLabel, { count: items.length }) then text}
|
||||||
|
{text}
|
||||||
|
{/await}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</svelte:fragment>
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
@ -18,23 +18,17 @@
|
|||||||
import { KeyedAttribute } from '@anticrm/presentation'
|
import { KeyedAttribute } from '@anticrm/presentation'
|
||||||
import { TagElement, TagReference } from '@anticrm/tags'
|
import { TagElement, TagReference } from '@anticrm/tags'
|
||||||
import type { ButtonKind, ButtonSize, TooltipAlignment } from '@anticrm/ui'
|
import type { ButtonKind, ButtonSize, TooltipAlignment } from '@anticrm/ui'
|
||||||
import { Button, showPopup, Tooltip } from '@anticrm/ui'
|
import { ShowMore, Label, CircleButton, Button, showPopup, Tooltip, IconAdd, IconClose } from '@anticrm/ui'
|
||||||
import { createEventDispatcher } from 'svelte'
|
import { createEventDispatcher, afterUpdate } from 'svelte'
|
||||||
import tags from '../plugin'
|
import tags from '../plugin'
|
||||||
import TagsPopup from './TagsPopup.svelte'
|
import TagsPopup from './TagsPopup.svelte'
|
||||||
|
import TagItem from './TagItem.svelte'
|
||||||
|
|
||||||
export let items: TagReference[] = []
|
export let items: TagReference[] = []
|
||||||
export let targetClass: Ref<Class<Doc>>
|
export let targetClass: Ref<Class<Doc>>
|
||||||
export let key: KeyedAttribute
|
export let key: KeyedAttribute
|
||||||
export let showTitle = true
|
export let showTitle = true
|
||||||
export let elements: Map<Ref<TagElement>, TagElement>
|
export let elements: Map<Ref<TagElement>, TagElement>
|
||||||
export let countLabel: IntlString
|
|
||||||
|
|
||||||
export let kind: ButtonKind = 'no-border'
|
|
||||||
export let size: ButtonSize = 'small'
|
|
||||||
export let justify: 'left' | 'center' = 'center'
|
|
||||||
export let width: string | undefined = undefined
|
|
||||||
export let labelDirection: TooltipAlignment | undefined = undefined
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
@ -54,12 +48,18 @@
|
|||||||
TagsPopup,
|
TagsPopup,
|
||||||
{
|
{
|
||||||
targetClass,
|
targetClass,
|
||||||
addRef,
|
|
||||||
removeTag,
|
|
||||||
selected: items.map((it) => it.tag),
|
selected: items.map((it) => it.tag),
|
||||||
keyLabel
|
keyLabel,
|
||||||
|
hideAdd: true
|
||||||
},
|
},
|
||||||
evt.target as HTMLElement
|
evt.target as HTMLElement,
|
||||||
|
() => { },
|
||||||
|
(result) => {
|
||||||
|
if (result != undefined) {
|
||||||
|
if (result.action === 'add') addRef(result.tag)
|
||||||
|
else if (result.action === 'remove') removeTag(items.filter(it => it.tag === result.tag._id)[0]._id)
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,22 +68,68 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Tooltip label={key.attr.label} direction={labelDirection}>
|
<div class="flex-row">
|
||||||
<Button
|
{#if showTitle}
|
||||||
icon={tags.icon.Tags}
|
<div class="flex-row-center">
|
||||||
label={items.length > 0 ? undefined : key.attr.label}
|
<div class="title">
|
||||||
width={width ?? 'min-content'}
|
<Label label={key.attr.label} />
|
||||||
{kind} {size} {justify}
|
</div>
|
||||||
on:click={addTag}
|
<div id='add-tag'>
|
||||||
>
|
<Tooltip label={tags.string.AddTagTooltip} props={{ word: keyLabel }}>
|
||||||
<svelte:fragment slot="content">
|
<CircleButton icon={IconAdd} size={'small'} selected on:click={addTag} />
|
||||||
{#if items.length > 0}
|
</Tooltip>
|
||||||
<div class="flex-row-center flex-nowrap">
|
</div>
|
||||||
{#await translate(countLabel, { count: items.length }) then text}
|
</div>
|
||||||
{text}
|
{/if}
|
||||||
{/await}
|
<ShowMore ignore={!showTitle}>
|
||||||
</div>
|
<div class:tags-container={showTitle} class:mt-4={showTitle}>
|
||||||
{/if}
|
<div class="tag-items" class:tag-items-scroll={!showTitle}>
|
||||||
</svelte:fragment>
|
{#if items.length === 0}
|
||||||
</Button>
|
{#if keyLabel}
|
||||||
</Tooltip>
|
<div class="flex flex-grow title-center">
|
||||||
|
<Label label={tags.string.NoItems} params={{ word: keyLabel }} />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
{#each items as tag}
|
||||||
|
<TagItem
|
||||||
|
{tag}
|
||||||
|
element={elements.get(tag.tag)}
|
||||||
|
action={IconClose}
|
||||||
|
on:action={() => {
|
||||||
|
removeTag(tag._id)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ShowMore>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.title {
|
||||||
|
margin-right: 0.75rem;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
color: var(--theme-caption-color);
|
||||||
|
}
|
||||||
|
.tags-container {
|
||||||
|
padding: 1rem;
|
||||||
|
color: var(--theme-caption-color);
|
||||||
|
background: var(--theme-bg-accent-color);
|
||||||
|
border: 1px solid var(--theme-bg-accent-color);
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
}
|
||||||
|
.tag-items {
|
||||||
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.tag-items-scroll {
|
||||||
|
overflow-y: scroll;
|
||||||
|
max-height: 10rem;
|
||||||
|
}
|
||||||
|
.title-center {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
import type { IntlString } from '@anticrm/platform'
|
import type { IntlString } from '@anticrm/platform'
|
||||||
import { translate } from '@anticrm/platform'
|
import { translate } from '@anticrm/platform'
|
||||||
import presentation, { createQuery, getClient } from '@anticrm/presentation'
|
import presentation, { createQuery, getClient } from '@anticrm/presentation'
|
||||||
import { TagCategory, TagElement } from '@anticrm/tags'
|
import { TagCategory, TagElement, TagReference } from '@anticrm/tags'
|
||||||
import { CheckBox, Button, Icon, IconAdd, IconClose, Label, showPopup, getPlatformColor } from '@anticrm/ui'
|
import { CheckBox, Button, Icon, IconAdd, IconClose, Label, showPopup, getPlatformColor } from '@anticrm/ui'
|
||||||
import { createEventDispatcher } from 'svelte'
|
import { createEventDispatcher } from 'svelte'
|
||||||
import tags from '../plugin'
|
import tags from '../plugin'
|
||||||
@ -27,10 +27,9 @@
|
|||||||
|
|
||||||
export let targetClass: Ref<Class<Doc>>
|
export let targetClass: Ref<Class<Doc>>
|
||||||
export let placeholder: IntlString = presentation.string.Search
|
export let placeholder: IntlString = presentation.string.Search
|
||||||
export let addRef: (tag: TagElement) => Promise<void>
|
|
||||||
export let removeTag: (tag: TagElement) => Promise<void>
|
|
||||||
export let selected: Ref<TagElement>[] = []
|
export let selected: Ref<TagElement>[] = []
|
||||||
export let keyLabel: string = ''
|
export let keyLabel: string = ''
|
||||||
|
export let hideAdd: boolean = false
|
||||||
|
|
||||||
let search: string = ''
|
let search: string = ''
|
||||||
let searchElement: HTMLInputElement
|
let searchElement: HTMLInputElement
|
||||||
@ -60,10 +59,6 @@
|
|||||||
async function createTagElement (): Promise<void> {
|
async function createTagElement (): Promise<void> {
|
||||||
showPopup(CreateTagElement, { targetClass }, 'top')
|
showPopup(CreateTagElement, { targetClass }, 'top')
|
||||||
}
|
}
|
||||||
async function addTag (element: TagElement): Promise<void> {
|
|
||||||
await addRef(element)
|
|
||||||
selected = [...selected, element._id]
|
|
||||||
}
|
|
||||||
|
|
||||||
const isSelected = (element: TagElement): boolean => {
|
const isSelected = (element: TagElement): boolean => {
|
||||||
if (selected.filter(p => p === element._id).length > 0) return true
|
if (selected.filter(p => p === element._id).length > 0) return true
|
||||||
@ -72,14 +67,14 @@
|
|||||||
const checkSelected = (element: TagElement): void => {
|
const checkSelected = (element: TagElement): void => {
|
||||||
if (isSelected(element)) {
|
if (isSelected(element)) {
|
||||||
selected = selected.filter(p => p !== element._id)
|
selected = selected.filter(p => p !== element._id)
|
||||||
removeTag(element)
|
dispatch('update', { action: 'remove', tag: element })
|
||||||
} else {
|
} else {
|
||||||
selected.push(element._id)
|
selected = [...selected, element._id]
|
||||||
addTag(element)
|
dispatch('update', { action: 'add', tag: element })
|
||||||
}
|
}
|
||||||
objects = objects
|
objects = objects
|
||||||
categories = categories
|
categories = categories
|
||||||
dispatch('update', selected)
|
dispatch('update', { action: 'selected', selected: selected})
|
||||||
}
|
}
|
||||||
const toggleGroup = (ev: MouseEvent): void => {
|
const toggleGroup = (ev: MouseEvent): void => {
|
||||||
const el: HTMLElement = ev.currentTarget as HTMLElement
|
const el: HTMLElement = ev.currentTarget as HTMLElement
|
||||||
@ -106,7 +101,7 @@
|
|||||||
{#if search !== ''}<div class="icon"><Icon icon={IconClose} size={'inline'} /></div>{/if}
|
{#if search !== ''}<div class="icon"><Icon icon={IconClose} size={'inline'} /></div>{/if}
|
||||||
</div>
|
</div>
|
||||||
<Button kind={'transparent'} size={'small'} icon={show ? IconView : IconViewHide} on:click={() => show = !show} />
|
<Button kind={'transparent'} size={'small'} icon={show ? IconView : IconViewHide} on:click={() => show = !show} />
|
||||||
<Button kind={'transparent'} size={'small'} icon={IconAdd} on:click={createTagElement} />
|
{#if !hideAdd}<Button kind={'transparent'} size={'small'} icon={IconAdd} on:click={createTagElement} />{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -21,6 +21,7 @@ import TagsPresenter from './components/TagsPresenter.svelte'
|
|||||||
import TagsItemPresenter from './components/TagsItemPresenter.svelte'
|
import TagsItemPresenter from './components/TagsItemPresenter.svelte'
|
||||||
import TagsView from './components/TagsView.svelte'
|
import TagsView from './components/TagsView.svelte'
|
||||||
import TagsEditor from './components/TagsEditor.svelte'
|
import TagsEditor from './components/TagsEditor.svelte'
|
||||||
|
import TagsDropdownEditor from './components/TagsDropdownEditor.svelte'
|
||||||
import CategoryPresenter from './components/CategoryPresenter.svelte'
|
import CategoryPresenter from './components/CategoryPresenter.svelte'
|
||||||
import tags from './plugin'
|
import tags from './plugin'
|
||||||
import TagsCategoryBar from './components/CategoryBar.svelte'
|
import TagsCategoryBar from './components/CategoryBar.svelte'
|
||||||
@ -33,6 +34,7 @@ export default async (): Promise<Resources> => ({
|
|||||||
TagsPresenter,
|
TagsPresenter,
|
||||||
TagsView,
|
TagsView,
|
||||||
TagsEditor,
|
TagsEditor,
|
||||||
|
TagsDropdownEditor,
|
||||||
TagsItemPresenter,
|
TagsItemPresenter,
|
||||||
CategoryPresenter,
|
CategoryPresenter,
|
||||||
TagsCategoryBar
|
TagsCategoryBar
|
||||||
|
@ -79,6 +79,7 @@ const tagsPlugin = plugin(tagsId, {
|
|||||||
component: {
|
component: {
|
||||||
TagsView: '' as AnyComponent,
|
TagsView: '' as AnyComponent,
|
||||||
TagsEditor: '' as AnyComponent,
|
TagsEditor: '' as AnyComponent,
|
||||||
|
TagsDropdownEditor: '' as AnyComponent,
|
||||||
TagsCategoryBar: '' as AnyComponent
|
TagsCategoryBar: '' as AnyComponent
|
||||||
},
|
},
|
||||||
category: {
|
category: {
|
||||||
|
Loading…
Reference in New Issue
Block a user