mirror of
https://github.com/hcengineering/platform.git
synced 2025-03-23 16:24:57 +00:00
346 lines
9.1 KiB
Svelte
346 lines
9.1 KiB
Svelte
<!--
|
|
// 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 core, { Enum } from '@hcengineering/core'
|
|
import presentation, { getClient, MessageBox } from '@hcengineering/presentation'
|
|
import {
|
|
IconAdd,
|
|
IconAttachment,
|
|
IconDelete,
|
|
showPopup,
|
|
Modal,
|
|
ModernEditbox,
|
|
Label,
|
|
ButtonIcon,
|
|
IconMoreV,
|
|
IconMoreV2,
|
|
eventToHTMLElement,
|
|
ModernPopup
|
|
} from '@hcengineering/ui'
|
|
import type { DropdownIntlItem } from '@hcengineering/ui'
|
|
import { createEventDispatcher } from 'svelte'
|
|
import setting from '../plugin'
|
|
import EnumValuesList from './EnumValuesList.svelte'
|
|
import IconBulletList from './icons/BulletList.svelte'
|
|
import Report from './icons/Report.svelte'
|
|
import { IntlString } from '@hcengineering/platform'
|
|
|
|
export let value: Enum | undefined
|
|
export let name: string = value?.name ?? ''
|
|
export let values: string[] = value?.enumValues ?? []
|
|
export let title: IntlString = setting.string.EditEnum
|
|
|
|
const client = getClient()
|
|
const dispatch = createEventDispatcher()
|
|
|
|
let matched = false
|
|
$: matched = values.includes(newValue.trim())
|
|
|
|
async function save (): Promise<void> {
|
|
if (value === undefined) {
|
|
await client.createDoc(core.class.Enum, core.space.Model, {
|
|
name,
|
|
enumValues: values
|
|
})
|
|
} else {
|
|
await client.update(value, {
|
|
name,
|
|
enumValues: values
|
|
})
|
|
}
|
|
dispatch('close')
|
|
}
|
|
|
|
function add () {
|
|
newValue = newValue.trim()
|
|
if (!newValue.length) return
|
|
if (matched) return
|
|
values.push(newValue)
|
|
values = values
|
|
newValue = ''
|
|
}
|
|
function remove (value: string) {
|
|
values = values.filter((p) => p !== value)
|
|
}
|
|
const handleKeydown = (evt: KeyboardEvent) => {
|
|
if (evt.key === 'Enter') {
|
|
add()
|
|
}
|
|
if (evt.key === 'Escape') {
|
|
newItem = false
|
|
newValue = ''
|
|
evt.stopPropagation()
|
|
}
|
|
}
|
|
|
|
let newValue = ''
|
|
let newItem: boolean = false
|
|
let opened: boolean = false
|
|
let inputFile: HTMLInputElement
|
|
|
|
function processText (text: string): void {
|
|
const newValues = text.split('\n').map((it) => it.trim())
|
|
for (const v of newValues) {
|
|
if (!values.includes(v)) {
|
|
values.push(v)
|
|
}
|
|
}
|
|
values = values
|
|
newValue = ''
|
|
}
|
|
async function processFile (file: File): Promise<void> {
|
|
const text = await file.text()
|
|
processText(text)
|
|
}
|
|
|
|
function fileSelected () {
|
|
const list = inputFile.files
|
|
if (list === null || list.length === 0) return
|
|
for (let index = 0; index < list.length; index++) {
|
|
const file = list.item(index)
|
|
if (file !== null) {
|
|
processFile(file)
|
|
}
|
|
}
|
|
inputFile.value = ''
|
|
}
|
|
|
|
function fileDrop (e: DragEvent) {
|
|
dragover = false
|
|
const list = e.dataTransfer?.files
|
|
if (list === undefined || list.length === 0) return
|
|
for (let index = 0; index < list.length; index++) {
|
|
const file = list.item(index)
|
|
if (file !== null) {
|
|
processFile(file)
|
|
}
|
|
}
|
|
}
|
|
function pasteAction (evt: ClipboardEvent): void {
|
|
const items = evt.clipboardData?.items ?? []
|
|
for (const index in items) {
|
|
const item = items[index]
|
|
if (item.kind === 'file') {
|
|
const blob = item.getAsFile()
|
|
if (blob !== null) {
|
|
processFile(blob)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
async function handleClipboard (): Promise<void> {
|
|
const text = await navigator.clipboard.readText()
|
|
processText(text)
|
|
}
|
|
|
|
let dragover = false
|
|
const selection: number = 0
|
|
|
|
// $: filtered = newValue.length > 0 ? values.filter((it) => it.includes(newValue)) : values
|
|
|
|
// function onDelete () {
|
|
// showPopup(
|
|
// MessageBox,
|
|
// {
|
|
// label: view.string.DeleteObject,
|
|
// message: view.string.DeleteObjectConfirm,
|
|
// params: { count: filtered.length }
|
|
// },
|
|
// 'top',
|
|
// (result?: boolean) => {
|
|
// if (result === true) {
|
|
// values = values.filter((it) => !filtered.includes(it))
|
|
// newValue = ''
|
|
// }
|
|
// }
|
|
// )
|
|
// }
|
|
|
|
const items: (DropdownIntlItem & { action: () => void })[] = [
|
|
{
|
|
id: 'import',
|
|
icon: IconAttachment,
|
|
label: setting.string.ImportEnum,
|
|
action: () => {
|
|
inputFile.click()
|
|
}
|
|
},
|
|
{
|
|
id: 'paste',
|
|
icon: Report,
|
|
label: setting.string.ImportEnumCopy,
|
|
action: () => {
|
|
handleClipboard()
|
|
}
|
|
}
|
|
]
|
|
|
|
const openPopup = (ev: MouseEvent): void => {
|
|
if (!opened) {
|
|
opened = true
|
|
showPopup(ModernPopup, { items }, eventToHTMLElement(ev), (result) => {
|
|
if (result) items.find((it) => it.id === result)?.action()
|
|
opened = false
|
|
})
|
|
}
|
|
}
|
|
|
|
async function showConfirmationDialog (): Promise<void> {
|
|
const isEnumEmpty = values.length === 0
|
|
|
|
if (isEnumEmpty) {
|
|
dispatch('close')
|
|
} else {
|
|
showPopup(
|
|
MessageBox,
|
|
{
|
|
label: setting.string.NewEnumDialogClose,
|
|
message: setting.string.NewEnumDialogCloseNote
|
|
},
|
|
'top',
|
|
(result?: boolean) => {
|
|
if (result === true) dispatch('close')
|
|
}
|
|
)
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<svelte:window on:paste={pasteAction} />
|
|
|
|
<input
|
|
bind:this={inputFile}
|
|
disabled={inputFile == null}
|
|
multiple
|
|
type="file"
|
|
name="file"
|
|
id="file"
|
|
style="display: none"
|
|
on:change={fileSelected}
|
|
/>
|
|
|
|
<Modal
|
|
label={title}
|
|
type={'type-popup'}
|
|
okLabel={presentation.string.Save}
|
|
okAction={save}
|
|
canSave={name.trim().length > 0 && values.length > 0}
|
|
onCancel={() => {
|
|
showConfirmationDialog()
|
|
}}
|
|
>
|
|
<div class="flex-col">
|
|
<ModernEditbox bind:value={name} label={setting.string.EnumTitle} kind={'ghost'} size={'large'} width={'100%'} />
|
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
|
<div
|
|
class="hulyTableAttr-container mt-6"
|
|
class:dragDropZone={dragover}
|
|
on:dragover|preventDefault={() => {
|
|
dragover = true
|
|
}}
|
|
on:dragleave={() => {
|
|
dragover = false
|
|
}}
|
|
on:drop|preventDefault|stopPropagation={fileDrop}
|
|
>
|
|
<div class="hulyTableAttr-header font-medium-12">
|
|
<IconBulletList size={'small'} />
|
|
<span><Label label={setting.string.Options} /></span>
|
|
<div class="buttons-group tertiary-textColor">
|
|
<ButtonIcon
|
|
kind={'tertiary'}
|
|
icon={IconMoreV}
|
|
size={'small'}
|
|
pressed={opened}
|
|
inheritColor
|
|
hasMenu
|
|
on:click={(ev) => {
|
|
openPopup(ev)
|
|
}}
|
|
/>
|
|
<ButtonIcon
|
|
kind={'primary'}
|
|
icon={IconAdd}
|
|
size={'small'}
|
|
tooltip={{ label: setting.string.Add }}
|
|
on:click={() => {
|
|
if (newItem) {
|
|
add()
|
|
} else {
|
|
newItem = true
|
|
}
|
|
}}
|
|
/>
|
|
</div>
|
|
</div>
|
|
{#if values.length > 0 || newItem}
|
|
<div class="hulyTableAttr-content options">
|
|
<EnumValuesList
|
|
bind:values
|
|
disableMouseOver={newItem}
|
|
on:remove={(e) => {
|
|
remove(e.detail)
|
|
}}
|
|
/>
|
|
{#if newItem}
|
|
<div class="hulyTableAttr-content__row hovered">
|
|
<div class="hulyTableAttr-content__row-dragMenu">
|
|
<IconMoreV2 size={'small'} />
|
|
</div>
|
|
<div class="hulyTableAttr-content__row-label font-regular-14 accent grow">
|
|
<ModernEditbox
|
|
kind={'ghost'}
|
|
size={'small'}
|
|
label={setting.string.EnterOptionTitle}
|
|
on:keydown={handleKeydown}
|
|
on:blur={() => {
|
|
newValue = newValue.trim()
|
|
if (!newValue.length) return
|
|
add()
|
|
newItem = false
|
|
}}
|
|
bind:value={newValue}
|
|
width={'100%'}
|
|
autoFocus
|
|
/>
|
|
</div>
|
|
{#if matched}
|
|
<div class="hulyChip-item error font-medium-12">
|
|
<Label label={presentation.string.Match} />
|
|
</div>
|
|
{/if}
|
|
<ButtonIcon
|
|
kind={'tertiary'}
|
|
icon={IconDelete}
|
|
size={'small'}
|
|
on:click={() => {
|
|
newValue = ''
|
|
newItem = false
|
|
}}
|
|
/>
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
</div>
|
|
</Modal>
|
|
|
|
<style lang="scss">
|
|
.dragDropZone {
|
|
border: 2px dashed var(--theme-popup-hover);
|
|
}
|
|
</style>
|