mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-22 16:27:22 +00:00
Enhanced drawing overlay for screenshots (#7231)
Some checks are pending
CI / build (push) Waiting to run
CI / svelte-check (push) Blocked by required conditions
CI / formatting (push) Blocked by required conditions
CI / test (push) Blocked by required conditions
CI / uitest (push) Waiting to run
CI / uitest-pg (push) Waiting to run
CI / uitest-qms (push) Waiting to run
CI / docker-build (push) Blocked by required conditions
CI / dist-build (push) Blocked by required conditions
Some checks are pending
CI / build (push) Waiting to run
CI / svelte-check (push) Blocked by required conditions
CI / formatting (push) Blocked by required conditions
CI / test (push) Blocked by required conditions
CI / uitest (push) Waiting to run
CI / uitest-pg (push) Waiting to run
CI / uitest-qms (push) Waiting to run
CI / docker-build (push) Blocked by required conditions
CI / dist-build (push) Blocked by required conditions
Signed-off-by: Nikolay Chunosov <Chunosov.N@gmail.com>
This commit is contained in:
parent
aea704fd0f
commit
27547e580c
@ -241,6 +241,10 @@ export function createModel (builder: Builder): void {
|
|||||||
{ _class: 1 }
|
{ _class: 1 }
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
builder.mixin(attachment.class.Drawing, core.class.Class, view.mixin.ObjectPresenter, {
|
||||||
|
presenter: attachment.component.DrawingPresenter
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export default attachment
|
export default attachment
|
||||||
|
@ -34,7 +34,8 @@
|
|||||||
"FailedToPreview": "Náhled se nezdařil",
|
"FailedToPreview": "Náhled se nezdařil",
|
||||||
"ContentType": "Typ obsahu",
|
"ContentType": "Typ obsahu",
|
||||||
"ContentTypeNotSupported": "Náhled není dostupný pro tento typ obsahu",
|
"ContentTypeNotSupported": "Náhled není dostupný pro tento typ obsahu",
|
||||||
"StartDrawing": "Načmárejte"
|
"StartDrawing": "Načmárejte",
|
||||||
|
"DrawingHistory": "Čmárání historie"
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"FileTooLarge": "Soubor je příliš velký"
|
"FileTooLarge": "Soubor je příliš velký"
|
||||||
|
@ -34,7 +34,8 @@
|
|||||||
"FailedToPreview": "Failed to preview",
|
"FailedToPreview": "Failed to preview",
|
||||||
"ContentType": "Content type",
|
"ContentType": "Content type",
|
||||||
"ContentTypeNotSupported": "Preview is not available for this content type",
|
"ContentTypeNotSupported": "Preview is not available for this content type",
|
||||||
"StartDrawing": "Scribble over"
|
"StartDrawing": "Scribble over",
|
||||||
|
"DrawingHistory": "Scribble history"
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"FileTooLarge": "File too large"
|
"FileTooLarge": "File too large"
|
||||||
|
@ -34,7 +34,8 @@
|
|||||||
"FailedToPreview": "Error al previsualizar",
|
"FailedToPreview": "Error al previsualizar",
|
||||||
"ContentType": "Tipo de contenido",
|
"ContentType": "Tipo de contenido",
|
||||||
"ContentTypeNotSupported": "La vista previa no está disponible para este tipo de contenido",
|
"ContentTypeNotSupported": "La vista previa no está disponible para este tipo de contenido",
|
||||||
"StartDrawing": "Garabatear encima"
|
"StartDrawing": "Garabatear encima",
|
||||||
|
"DrawingHistory": "Historia de garabatos"
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"FileTooLarge": "Archivo demasiado grande"
|
"FileTooLarge": "Archivo demasiado grande"
|
||||||
|
@ -34,7 +34,8 @@
|
|||||||
"FailedToPreview": "Échec de l'aperçu",
|
"FailedToPreview": "Échec de l'aperçu",
|
||||||
"ContentType": "Type de contenu",
|
"ContentType": "Type de contenu",
|
||||||
"ContentTypeNotSupported": "L'aperçu n'est pas disponible pour ce type de contenu",
|
"ContentTypeNotSupported": "L'aperçu n'est pas disponible pour ce type de contenu",
|
||||||
"StartDrawing": "Gribouiller dessus"
|
"StartDrawing": "Gribouiller dessus",
|
||||||
|
"DrawingHistory": "Histoire de gribouillage"
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"FileTooLarge": "Fichier trop volumineux"
|
"FileTooLarge": "Fichier trop volumineux"
|
||||||
|
@ -34,7 +34,8 @@
|
|||||||
"FailedToPreview": "Impossibile mostrare l'anteprima",
|
"FailedToPreview": "Impossibile mostrare l'anteprima",
|
||||||
"ContentType": "Tipo di contenuto",
|
"ContentType": "Tipo di contenuto",
|
||||||
"ContentTypeNotSupported": "Anteprima non disponibile per questo tipo di contenuto",
|
"ContentTypeNotSupported": "Anteprima non disponibile per questo tipo di contenuto",
|
||||||
"StartDrawing": "Scarabocchiare sopra"
|
"StartDrawing": "Scarabocchiare sopra",
|
||||||
|
"DrawingHistory": "Scarabocchiare la storia"
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"FileTooLarge": "File troppo grande"
|
"FileTooLarge": "File troppo grande"
|
||||||
|
@ -34,7 +34,8 @@
|
|||||||
"FailedToPreview": "Falha ao pré-visualizar",
|
"FailedToPreview": "Falha ao pré-visualizar",
|
||||||
"ContentType": "Tipo de conteúdo",
|
"ContentType": "Tipo de conteúdo",
|
||||||
"ContentTypeNotSupported": "A visualização não está disponível para este tipo de conteúdo",
|
"ContentTypeNotSupported": "A visualização não está disponível para este tipo de conteúdo",
|
||||||
"StartDrawing": "Scarabocchiare sopra"
|
"StartDrawing": "Scarabocchiare sopra",
|
||||||
|
"DrawingHistory": "História de rabiscos"
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"FileTooLarge": "Ficheiro demasiado grande"
|
"FileTooLarge": "Ficheiro demasiado grande"
|
||||||
|
@ -34,7 +34,8 @@
|
|||||||
"FailedToPreview": "Ошибка предпросмотра",
|
"FailedToPreview": "Ошибка предпросмотра",
|
||||||
"ContentType": "Тип контента",
|
"ContentType": "Тип контента",
|
||||||
"ContentTypeNotSupported": "Предварительный просмотр недоступен для этого типа контента",
|
"ContentTypeNotSupported": "Предварительный просмотр недоступен для этого типа контента",
|
||||||
"StartDrawing": "Сделать набросок"
|
"StartDrawing": "Сделать набросок",
|
||||||
|
"DrawingHistory": "История набросков"
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"FileTooLarge": "Файл слишком большой"
|
"FileTooLarge": "Файл слишком большой"
|
||||||
|
@ -34,7 +34,8 @@
|
|||||||
"FailedToPreview": "预览失败",
|
"FailedToPreview": "预览失败",
|
||||||
"ContentType": "内容类型",
|
"ContentType": "内容类型",
|
||||||
"ContentTypeNotSupported": "此內容類型無法預覽",
|
"ContentTypeNotSupported": "此內容類型無法預覽",
|
||||||
"StartDrawing": "随意涂鸦"
|
"StartDrawing": "随意涂鸦",
|
||||||
|
"DrawingHistory": "涂鸦的历史"
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"FileTooLarge": "文件太大"
|
"FileTooLarge": "文件太大"
|
||||||
|
@ -14,8 +14,9 @@
|
|||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { getObjectValue, type Class, type Doc, type Ref } from '@hcengineering/core'
|
import { getObjectValue, type Class, type Doc, type Ref } from '@hcengineering/core'
|
||||||
import type { IntlString } from '@hcengineering/platform'
|
import { getResource, type IntlString } from '@hcengineering/platform'
|
||||||
import {
|
import {
|
||||||
|
AnySvelteComponent,
|
||||||
Button,
|
Button,
|
||||||
EditWithIcon,
|
EditWithIcon,
|
||||||
FocusHandler,
|
FocusHandler,
|
||||||
@ -31,6 +32,7 @@
|
|||||||
showPopup,
|
showPopup,
|
||||||
tooltip
|
tooltip
|
||||||
} from '@hcengineering/ui'
|
} from '@hcengineering/ui'
|
||||||
|
import view from '@hcengineering/view'
|
||||||
import { createEventDispatcher } from 'svelte'
|
import { createEventDispatcher } from 'svelte'
|
||||||
import presentation from '..'
|
import presentation from '..'
|
||||||
import { ObjectCreate } from '../types'
|
import { ObjectCreate } from '../types'
|
||||||
@ -47,7 +49,7 @@
|
|||||||
export let placeholder: IntlString = presentation.string.Search
|
export let placeholder: IntlString = presentation.string.Search
|
||||||
export let selectedObjects: Ref<Doc>[] = []
|
export let selectedObjects: Ref<Doc>[] = []
|
||||||
export let shadows: boolean = true
|
export let shadows: boolean = true
|
||||||
export let width: 'medium' | 'large' | 'full' = 'medium'
|
export let width: 'medium' | 'large' | 'full' | 'auto' = 'medium'
|
||||||
export let size: 'small' | 'medium' | 'large' = 'large'
|
export let size: 'small' | 'medium' | 'large' = 'large'
|
||||||
|
|
||||||
export let noSearchField: boolean = false
|
export let noSearchField: boolean = false
|
||||||
@ -59,7 +61,7 @@
|
|||||||
export let created: Doc[] = []
|
export let created: Doc[] = []
|
||||||
export let embedded: boolean = false
|
export let embedded: boolean = false
|
||||||
export let loading: boolean = false
|
export let loading: boolean = false
|
||||||
export let type: 'text' | 'object' = 'text'
|
export let type: 'text' | 'object' | 'presenter' = 'text'
|
||||||
|
|
||||||
let search: string = ''
|
let search: string = ''
|
||||||
|
|
||||||
@ -71,6 +73,11 @@
|
|||||||
created.length > 0 ||
|
created.length > 0 ||
|
||||||
objects.map((it) => getObjectValue(groupBy, it)).filter((it, index, arr) => arr.indexOf(it) === index).length > 1
|
objects.map((it) => getObjectValue(groupBy, it)).filter((it, index, arr) => arr.indexOf(it) === index).length > 1
|
||||||
|
|
||||||
|
let presenter: AnySvelteComponent | undefined = undefined
|
||||||
|
$: if (type === 'presenter') {
|
||||||
|
findObjectPresenter(_class)
|
||||||
|
}
|
||||||
|
|
||||||
const checkSelected = (item?: Doc): void => {
|
const checkSelected = (item?: Doc): void => {
|
||||||
if (item === undefined) {
|
if (item === undefined) {
|
||||||
return
|
return
|
||||||
@ -154,6 +161,19 @@
|
|||||||
}
|
}
|
||||||
return getObjectValue(groupBy, toAny(doc))
|
return getObjectValue(groupBy, toAny(doc))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function findObjectPresenter (_class: Ref<Class<Doc>>): void {
|
||||||
|
const presenterMixin = client.getHierarchy().classHierarchyMixin(_class, view.mixin.ObjectPresenter)
|
||||||
|
if (presenterMixin?.presenter !== undefined) {
|
||||||
|
getResource(presenterMixin.presenter)
|
||||||
|
.then((result) => {
|
||||||
|
presenter = result
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error('Failed to find presenter for class ' + _class, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<FocusHandler {manager} />
|
<FocusHandler {manager} />
|
||||||
@ -164,6 +184,7 @@
|
|||||||
class:full-width={width === 'full'}
|
class:full-width={width === 'full'}
|
||||||
class:plainContainer={!shadows}
|
class:plainContainer={!shadows}
|
||||||
class:width-40={width === 'large'}
|
class:width-40={width === 'large'}
|
||||||
|
class:auto={width === 'auto'}
|
||||||
class:embedded
|
class:embedded
|
||||||
on:keydown={onKeydown}
|
on:keydown={onKeydown}
|
||||||
use:resizeObserver={() => {
|
use:resizeObserver={() => {
|
||||||
@ -229,6 +250,10 @@
|
|||||||
<span class="label" class:disabled={readonly || isDeselectDisabled || loading}>
|
<span class="label" class:disabled={readonly || isDeselectDisabled || loading}>
|
||||||
<slot name="item" item={obj} />
|
<slot name="item" item={obj} />
|
||||||
</span>
|
</span>
|
||||||
|
{:else if type === 'presenter'}
|
||||||
|
{#if presenter !== undefined}
|
||||||
|
<svelte:component this={presenter} value={obj} />
|
||||||
|
{/if}
|
||||||
{:else}
|
{:else}
|
||||||
<slot name="item" item={obj} />
|
<slot name="item" item={obj} />
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -14,25 +14,30 @@
|
|||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Button, IconDelete, IconEdit, resizeObserver } from '@hcengineering/ui'
|
import { Button, IconDelete, IconEdit, resizeObserver } from '@hcengineering/ui'
|
||||||
|
import { onDestroy } from 'svelte'
|
||||||
import { drawing, type DrawingData, type DrawingTool } from '../drawing'
|
import { drawing, type DrawingData, type DrawingTool } from '../drawing'
|
||||||
import IconEraser from './icons/Eraser.svelte'
|
import IconEraser from './icons/Eraser.svelte'
|
||||||
|
|
||||||
export let active = false
|
export let active = false
|
||||||
export let readonly = false
|
export let readonly = true
|
||||||
export let imageWidth: number | undefined
|
export let imageWidth: number | undefined
|
||||||
export let imageHeight: number | undefined
|
export let imageHeight: number | undefined
|
||||||
export let drawingData: DrawingData
|
export let drawings: DrawingData[]
|
||||||
export let saveDrawing: (data: any) => Promise<void>
|
export let createDrawing: (data: any) => Promise<void>
|
||||||
|
|
||||||
let drawingTool: DrawingTool = 'pen'
|
let drawingTool: DrawingTool = 'pen'
|
||||||
let penColor = 'blue'
|
let penColor = 'blue'
|
||||||
const penColors = ['red', 'green', 'blue', 'white', 'black']
|
const penColors = ['red', 'green', 'blue', 'white', 'black']
|
||||||
|
let drawingData: DrawingData | undefined
|
||||||
let board: HTMLDivElement
|
let board: HTMLDivElement
|
||||||
let toolbar: HTMLDivElement
|
let toolbar: HTMLDivElement
|
||||||
let toolbarInside = false
|
let toolbarInside = false
|
||||||
|
let oldReadonly: boolean
|
||||||
|
let oldDrawings: DrawingData[]
|
||||||
|
let modified = false
|
||||||
|
|
||||||
$: updateToolbarPosition(readonly, board, toolbar)
|
$: updateToolbarPosition(readonly, board, toolbar)
|
||||||
|
$: updateEditableState(drawings, readonly)
|
||||||
|
|
||||||
function updateToolbarPosition (readonly: boolean, board: HTMLDivElement, toolbar: HTMLDivElement): void {
|
function updateToolbarPosition (readonly: boolean, board: HTMLDivElement, toolbar: HTMLDivElement): void {
|
||||||
if (!readonly && board?.offsetTop !== undefined && toolbar?.clientHeight !== undefined) {
|
if (!readonly && board?.offsetTop !== undefined && toolbar?.clientHeight !== undefined) {
|
||||||
@ -41,9 +46,47 @@
|
|||||||
toolbarInside = board.offsetTop <= toolbar.clientHeight * 3
|
toolbarInside = board.offsetTop <= toolbar.clientHeight * 3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateEditableState (drawings: DrawingData[], readonly: boolean): void {
|
||||||
|
if (readonly !== oldReadonly || drawings !== oldDrawings) {
|
||||||
|
if (drawings !== undefined) {
|
||||||
|
if (readonly) {
|
||||||
|
if (modified && drawingData !== undefined) {
|
||||||
|
createDrawing(drawingData).catch((error) => {
|
||||||
|
console.error('Failed to save drawing', error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
drawingData = drawings[0]
|
||||||
|
modified = false
|
||||||
|
} else {
|
||||||
|
if (drawingData === undefined) {
|
||||||
|
drawingData = {}
|
||||||
|
} else {
|
||||||
|
// Edit current content as a new drawing
|
||||||
|
drawingData = {
|
||||||
|
content: drawingData.content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
modified = false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
drawingData = undefined
|
||||||
|
}
|
||||||
|
oldDrawings = drawings
|
||||||
|
oldReadonly = readonly
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onDestroy(() => {
|
||||||
|
if (modified && drawingData !== undefined) {
|
||||||
|
createDrawing(drawingData).catch((error) => {
|
||||||
|
console.error('Failed to save drawing', error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if active}
|
{#if active && drawingData !== undefined}
|
||||||
<div
|
<div
|
||||||
{...$$restProps}
|
{...$$restProps}
|
||||||
style:position="relative"
|
style:position="relative"
|
||||||
@ -56,9 +99,14 @@
|
|||||||
imageWidth,
|
imageWidth,
|
||||||
imageHeight,
|
imageHeight,
|
||||||
drawingData,
|
drawingData,
|
||||||
saveDrawing,
|
|
||||||
drawingTool,
|
drawingTool,
|
||||||
penColor
|
penColor,
|
||||||
|
changed: (content) => {
|
||||||
|
modified = true
|
||||||
|
if (drawingData !== undefined) {
|
||||||
|
drawingData.content = content
|
||||||
|
}
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{#if !readonly}
|
{#if !readonly}
|
||||||
@ -68,6 +116,7 @@
|
|||||||
kind="icon"
|
kind="icon"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
drawingData = {}
|
drawingData = {}
|
||||||
|
modified = true
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div class="divider buttons-divider" />
|
<div class="divider buttons-divider" />
|
||||||
|
@ -13,9 +13,9 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { type Blob, type Ref } from '@hcengineering/core'
|
import { SortingOrder, type Blob, type Ref } from '@hcengineering/core'
|
||||||
import { getEmbeddedLabel } from '@hcengineering/platform'
|
import { getEmbeddedLabel } from '@hcengineering/platform'
|
||||||
import { Button, Dialog, IconEdit, tooltip } from '@hcengineering/ui'
|
import { Button, Dialog, IconHistory, IconScribble, showPopup, tooltip } from '@hcengineering/ui'
|
||||||
import { createEventDispatcher, onMount } from 'svelte'
|
import { createEventDispatcher, onMount } from 'svelte'
|
||||||
|
|
||||||
import { BlobMetadata } from '../types'
|
import { BlobMetadata } from '../types'
|
||||||
@ -23,6 +23,7 @@
|
|||||||
import ActionContext from './ActionContext.svelte'
|
import ActionContext from './ActionContext.svelte'
|
||||||
import FilePreview from './FilePreview.svelte'
|
import FilePreview from './FilePreview.svelte'
|
||||||
import DownloadFileButton from './DownloadFileButton.svelte'
|
import DownloadFileButton from './DownloadFileButton.svelte'
|
||||||
|
import ObjectPopup from './ObjectPopup.svelte'
|
||||||
import { ComponentExtensions } from '../index'
|
import { ComponentExtensions } from '../index'
|
||||||
import presentation from '../plugin'
|
import presentation from '../plugin'
|
||||||
import FileTypeIcon from './FileTypeIcon.svelte'
|
import FileTypeIcon from './FileTypeIcon.svelte'
|
||||||
@ -31,12 +32,19 @@
|
|||||||
export let name: string
|
export let name: string
|
||||||
export let contentType: string
|
export let contentType: string
|
||||||
export let metadata: BlobMetadata | undefined
|
export let metadata: BlobMetadata | undefined
|
||||||
export let props: Record<string, any> = {}
|
export let props: Record<string, any> & {
|
||||||
|
drawings?: any[]
|
||||||
|
drawingAvailable?: boolean
|
||||||
|
drawingEditable?: boolean
|
||||||
|
loadDrawings?: () => Promise<any>
|
||||||
|
createDrawing?: (data: any) => Promise<any>
|
||||||
|
} = {}
|
||||||
|
|
||||||
export let fullSize = false
|
export let fullSize = false
|
||||||
export let showIcon = true
|
export let showIcon = true
|
||||||
|
|
||||||
let drawingLoading = false
|
let drawingLoading = false
|
||||||
|
let createDrawing: (data: any) => Promise<any>
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
@ -45,28 +53,67 @@
|
|||||||
dispatch('fullsize')
|
dispatch('fullsize')
|
||||||
}
|
}
|
||||||
if (props.drawingAvailable === true) {
|
if (props.drawingAvailable === true) {
|
||||||
loadDrawings(props.loadDrawings)
|
if (props.loadDrawings !== undefined) {
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
function toggleDrawingEdit (): void {
|
|
||||||
const editable = props.drawingEditable === true
|
|
||||||
props = { ...props, drawingEditable: !editable }
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadDrawings (load: () => Promise<any>): void {
|
|
||||||
if (load !== undefined) {
|
|
||||||
drawingLoading = true
|
drawingLoading = true
|
||||||
load()
|
props
|
||||||
|
.loadDrawings()
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
drawingLoading = false
|
drawingLoading = false
|
||||||
props.drawingData = result
|
props.drawings = result
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
drawingLoading = false
|
drawingLoading = false
|
||||||
console.error('Failed to load drawings for file', file, error)
|
console.error('Failed to load drawings for file', file, error)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if (props.createDrawing !== undefined) {
|
||||||
|
createDrawing = props.createDrawing
|
||||||
|
props.createDrawing = async (data: any): Promise<any> => {
|
||||||
|
const newDrawing = await createDrawing(data)
|
||||||
|
if (props.drawings !== undefined) {
|
||||||
|
props.drawings = [newDrawing, ...props.drawings]
|
||||||
|
} else {
|
||||||
|
props.drawings = [newDrawing]
|
||||||
|
}
|
||||||
|
return newDrawing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function toggleDrawingEdit (): void {
|
||||||
|
props.drawingEditable = !(props.drawingEditable === true)
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectCurrentDrawing (ev: MouseEvent): void {
|
||||||
|
if (props.drawings === undefined || props.drawings.length === 0) {
|
||||||
|
// no current means no history
|
||||||
|
return
|
||||||
|
}
|
||||||
|
showPopup(
|
||||||
|
ObjectPopup,
|
||||||
|
{
|
||||||
|
_class: props.drawings[0]._class,
|
||||||
|
selected: props.drawings[0]._id,
|
||||||
|
docQuery: {
|
||||||
|
parent: props.drawings[0].parent
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
sort: {
|
||||||
|
createdOn: SortingOrder.Descending
|
||||||
|
}
|
||||||
|
},
|
||||||
|
searchMode: 'disabled',
|
||||||
|
type: 'presenter',
|
||||||
|
width: 'auto'
|
||||||
|
},
|
||||||
|
ev.target as HTMLElement,
|
||||||
|
async (result) => {
|
||||||
|
if (result !== undefined) {
|
||||||
|
props.drawings = [result]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -91,13 +138,24 @@
|
|||||||
|
|
||||||
<svelte:fragment slot="utils">
|
<svelte:fragment slot="utils">
|
||||||
{#if props.drawingAvailable === true}
|
{#if props.drawingAvailable === true}
|
||||||
|
{#if props.drawings !== undefined && props.drawings.length > 0}
|
||||||
<Button
|
<Button
|
||||||
icon={IconEdit}
|
icon={IconHistory}
|
||||||
|
kind="icon"
|
||||||
|
disabled={drawingLoading || props.drawingEditable === true}
|
||||||
|
showTooltip={{ label: presentation.string.DrawingHistory }}
|
||||||
|
on:click={selectCurrentDrawing}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
<Button
|
||||||
|
icon={IconScribble}
|
||||||
kind="icon"
|
kind="icon"
|
||||||
disabled={drawingLoading}
|
disabled={drawingLoading}
|
||||||
|
selected={props.drawingEditable === true}
|
||||||
showTooltip={{ label: presentation.string.StartDrawing }}
|
showTooltip={{ label: presentation.string.StartDrawing }}
|
||||||
on:click={toggleDrawingEdit}
|
on:click={toggleDrawingEdit}
|
||||||
/>
|
/>
|
||||||
|
<div class="buttons-divider" />
|
||||||
{/if}
|
{/if}
|
||||||
<DownloadFileButton {name} {file} />
|
<DownloadFileButton {name} {file} />
|
||||||
<ComponentExtensions
|
<ComponentExtensions
|
||||||
|
@ -43,7 +43,7 @@
|
|||||||
export let selectedObjects: Ref<Doc>[] = []
|
export let selectedObjects: Ref<Doc>[] = []
|
||||||
export let ignoreObjects: Ref<Doc>[] = []
|
export let ignoreObjects: Ref<Doc>[] = []
|
||||||
export let shadows: boolean = true
|
export let shadows: boolean = true
|
||||||
export let width: 'medium' | 'large' | 'full' = 'medium'
|
export let width: 'medium' | 'large' | 'full' | 'auto' = 'medium'
|
||||||
export let size: 'small' | 'medium' | 'large' = 'large'
|
export let size: 'small' | 'medium' | 'large' = 'large'
|
||||||
|
|
||||||
export let searchMode: 'field' | 'fulltext' | 'disabled' = 'field'
|
export let searchMode: 'field' | 'fulltext' | 'disabled' = 'field'
|
||||||
@ -55,7 +55,7 @@
|
|||||||
export let disallowDeselect: Ref<Doc>[] | undefined = undefined
|
export let disallowDeselect: Ref<Doc>[] | undefined = undefined
|
||||||
export let embedded: boolean = false
|
export let embedded: boolean = false
|
||||||
export let loading: boolean = false
|
export let loading: boolean = false
|
||||||
export let type: 'text' | 'object' = 'text'
|
export let type: 'text' | 'object' | 'presenter' = 'text'
|
||||||
|
|
||||||
export let filter: (it: Doc) => boolean = () => {
|
export let filter: (it: Doc) => boolean = () => {
|
||||||
return true
|
return true
|
||||||
|
@ -14,19 +14,17 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
export interface DrawingData {
|
export interface DrawingData {
|
||||||
id?: string
|
|
||||||
content?: string
|
content?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DrawingProps {
|
export interface DrawingProps {
|
||||||
readonly?: boolean
|
readonly: boolean
|
||||||
imageWidth?: number
|
imageWidth?: number
|
||||||
imageHeight?: number
|
imageHeight?: number
|
||||||
drawingData?: DrawingData
|
drawingData: DrawingData
|
||||||
saveDrawing?: (data: any) => Promise<void>
|
|
||||||
|
|
||||||
drawingTool?: DrawingTool
|
drawingTool?: DrawingTool
|
||||||
penColor?: string
|
penColor?: string
|
||||||
|
changed?: (content: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DrawCmd {
|
interface DrawCmd {
|
||||||
@ -198,7 +196,6 @@ export function drawing (node: HTMLElement, props: DrawingProps): any {
|
|||||||
draw.penColor = props.penColor ?? 'blue'
|
draw.penColor = props.penColor ?? 'blue'
|
||||||
updateCanvasCursor()
|
updateCanvasCursor()
|
||||||
|
|
||||||
let modified = false
|
|
||||||
let commands: DrawCmd[] = []
|
let commands: DrawCmd[] = []
|
||||||
let drawingData = props.drawingData
|
let drawingData = props.drawingData
|
||||||
parseData()
|
parseData()
|
||||||
@ -300,7 +297,7 @@ export function drawing (node: HTMLElement, props: DrawingProps): any {
|
|||||||
points: draw.points
|
points: draw.points
|
||||||
}
|
}
|
||||||
commands.push(cmd)
|
commands.push(cmd)
|
||||||
modified = true
|
props.changed?.(JSON.stringify(commands))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -340,7 +337,7 @@ export function drawing (node: HTMLElement, props: DrawingProps): any {
|
|||||||
|
|
||||||
function parseData (): void {
|
function parseData (): void {
|
||||||
clearCanvas()
|
clearCanvas()
|
||||||
if (drawingData?.content !== undefined) {
|
if (drawingData.content !== undefined && drawingData.content !== null) {
|
||||||
try {
|
try {
|
||||||
commands = JSON.parse(drawingData.content)
|
commands = JSON.parse(drawingData.content)
|
||||||
replayCommands()
|
replayCommands()
|
||||||
@ -356,43 +353,25 @@ export function drawing (node: HTMLElement, props: DrawingProps): any {
|
|||||||
return {
|
return {
|
||||||
update (props: DrawingProps) {
|
update (props: DrawingProps) {
|
||||||
if (drawingData !== props.drawingData) {
|
if (drawingData !== props.drawingData) {
|
||||||
// Currently it expectes only the empty data on update
|
|
||||||
// which means we pressed the "Clear canvas" button
|
|
||||||
// We don't support yet creation of multiple drawings for the same image
|
|
||||||
// so preserve the id to continue editing the previous drawing
|
|
||||||
const oldId = drawingData?.id
|
|
||||||
drawingData = props.drawingData
|
drawingData = props.drawingData
|
||||||
if (drawingData !== undefined) {
|
|
||||||
drawingData.id = oldId
|
|
||||||
}
|
|
||||||
modified = true
|
|
||||||
parseData()
|
parseData()
|
||||||
}
|
}
|
||||||
|
let updateCursor = false
|
||||||
if (draw.tool !== props.drawingTool) {
|
if (draw.tool !== props.drawingTool) {
|
||||||
draw.tool = props.drawingTool ?? 'pen'
|
draw.tool = props.drawingTool ?? 'pen'
|
||||||
updateCanvasCursor()
|
updateCursor = true
|
||||||
}
|
}
|
||||||
if (draw.penColor !== props.penColor) {
|
if (draw.penColor !== props.penColor) {
|
||||||
draw.penColor = props.penColor ?? 'blue'
|
draw.penColor = props.penColor ?? 'blue'
|
||||||
updateCanvasCursor()
|
updateCursor = true
|
||||||
}
|
}
|
||||||
if (props.readonly !== readonly) {
|
if (props.readonly !== readonly) {
|
||||||
readonly = props.readonly ?? false
|
readonly = props.readonly ?? false
|
||||||
|
updateCursor = true
|
||||||
|
}
|
||||||
|
if (updateCursor) {
|
||||||
updateCanvasCursor()
|
updateCanvasCursor()
|
||||||
}
|
}
|
||||||
},
|
|
||||||
destroy () {
|
|
||||||
if (props.saveDrawing === undefined) {
|
|
||||||
console.log('Save drawing method is not provided')
|
|
||||||
} else {
|
|
||||||
if (modified && (commands.length > 0 || drawingData?.id !== undefined)) {
|
|
||||||
const data: DrawingData = drawingData ?? {}
|
|
||||||
data.content = JSON.stringify(commands)
|
|
||||||
props.saveDrawing(data).catch((error) => {
|
|
||||||
console.error('Failed to save drawing', error)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -122,7 +122,8 @@ export default plugin(presentationId, {
|
|||||||
FailedToPreview: '' as IntlString,
|
FailedToPreview: '' as IntlString,
|
||||||
ContentType: '' as IntlString,
|
ContentType: '' as IntlString,
|
||||||
ContentTypeNotSupported: '' as IntlString,
|
ContentTypeNotSupported: '' as IntlString,
|
||||||
StartDrawing: '' as IntlString
|
StartDrawing: '' as IntlString,
|
||||||
|
DrawingHistory: '' as IntlString
|
||||||
},
|
},
|
||||||
extension: {
|
extension: {
|
||||||
FilePreviewExtension: '' as ComponentExtensionId,
|
FilePreviewExtension: '' as ComponentExtensionId,
|
||||||
|
@ -133,6 +133,10 @@
|
|||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.auto {
|
||||||
|
max-width: unset;
|
||||||
|
}
|
||||||
|
|
||||||
&.full-width {
|
&.full-width {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
background: none;
|
background: none;
|
||||||
|
@ -120,7 +120,7 @@
|
|||||||
{#if showIcon}
|
{#if showIcon}
|
||||||
<div class="btn-icon {iconModifier}" class:buttonIconNoLabel={!shouldShowLabel}>
|
<div class="btn-icon {iconModifier}" class:buttonIconNoLabel={!shouldShowLabel}>
|
||||||
<Icon
|
<Icon
|
||||||
icon={icon ?? (iconModifier === 'overdue' && !shouldIgnoreOverdue) ? DPCalendarOver : DPCalendar}
|
icon={icon ?? (iconModifier === 'overdue' && !shouldIgnoreOverdue ? DPCalendarOver : DPCalendar)}
|
||||||
size={'full'}
|
size={'full'}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
10
packages/ui/src/components/icons/History.svelte
Normal file
10
packages/ui/src/components/icons/History.svelte
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
export let size: 'small' | 'medium' | 'large'
|
||||||
|
const fill: string = 'currentColor'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svg class="svg-{size}" {fill} viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M13.5 8H12v5l4.28 2.54.72-1.21-3.5-2.08V8M13 3a9 9 0 00-9 9H1l3.96 4.03L9 12H6a7 7 0 017-7 7 7 0 017 7 7 7 0 01-7 7c-1.93 0-3.68-.79-4.94-2.06l-1.42 1.42A8.896 8.896 0 0013 21a9 9 0 009-9 9 9 0 00-9-9"
|
||||||
|
/>
|
||||||
|
</svg>
|
10
packages/ui/src/components/icons/Scribble.svelte
Normal file
10
packages/ui/src/components/icons/Scribble.svelte
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
export let size: 'small' | 'medium' | 'large'
|
||||||
|
const fill: string = 'currentColor'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svg class="svg-{size}" {fill} viewBox="0 0 15 15" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M5.06 2.334c-1.077.463-2.426 1.515-3.113 2.89l-.894-.448c.813-1.625 2.364-2.823 3.612-3.36.316-.136.626-.236.912-.287.278-.05.571-.061.833.017a.923.923 0 01.645.619c.091.3.028.623-.09.92-.284.708-.897 1.514-1.538 2.302-.196.241-.396.482-.596.723-.478.574-.953 1.147-1.365 1.71-.593.811-.972 1.501-1.04 2.034-.032.247.007.434.103.588.1.16.293.338.668.498 1.104.474 2.543.426 3.98.028.05-.527.17-1.101.342-1.705.536-1.876 1.757-3.141 2.93-3.581.583-.219 1.223-.254 1.743.053.542.32.808.924.808 1.665 0 .48-.196.947-.483 1.367-.29.424-.692.834-1.164 1.213-.86.69-1.99 1.308-3.195 1.73.02.326.086.581.186.77.123.234.31.394.603.476.313.088.77.092 1.418-.062.643-.154 1.44-.456 2.411-.941l.448.894c-1.013.507-1.885.842-2.627 1.02-.738.175-1.382.202-1.92.052a1.917 1.917 0 01-1.218-.972 2.711 2.711 0 01-.279-.946c-1.483.37-3.064.421-4.377-.142-.5-.214-.885-.505-1.123-.888-.242-.389-.3-.819-.246-1.244.103-.81.63-1.683 1.225-2.497.43-.59.939-1.201 1.425-1.786.195-.235.386-.465.567-.688.656-.805 1.168-1.499 1.385-2.042a.832.832 0 00.06-.216 1.008 1.008 0 00-.343.015 3.27 3.27 0 00-.693.221zm3.172 7.88c.952-.375 1.825-.876 2.495-1.414.419-.336.745-.676.964-.996C11.91 7.48 12 7.209 12 7c0-.509-.171-.718-.317-.804-.168-.099-.465-.134-.882.022-.827.31-1.856 1.295-2.32 2.92a9.81 9.81 0 00-.249 1.077z"
|
||||||
|
/>
|
||||||
|
</svg>
|
@ -240,6 +240,8 @@ export { default as IconFolderCollapsed } from './components/icons/FolderCollaps
|
|||||||
export { default as IconFolderExpanded } from './components/icons/FolderExpanded.svelte'
|
export { default as IconFolderExpanded } from './components/icons/FolderExpanded.svelte'
|
||||||
export { default as IconCheckmark } from './components/icons/Checkmark.svelte'
|
export { default as IconCheckmark } from './components/icons/Checkmark.svelte'
|
||||||
export { default as IconToDetails } from './components/icons/ToDetails.svelte'
|
export { default as IconToDetails } from './components/icons/ToDetails.svelte'
|
||||||
|
export { default as IconHistory } from './components/icons/History.svelte'
|
||||||
|
export { default as IconScribble } from './components/icons/Scribble.svelte'
|
||||||
|
|
||||||
export { default as PanelInstance } from './components/PanelInstance.svelte'
|
export { default as PanelInstance } from './components/PanelInstance.svelte'
|
||||||
export { default as Panel } from './components/Panel.svelte'
|
export { default as Panel } from './components/Panel.svelte'
|
||||||
|
@ -0,0 +1,13 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { type Drawing } from '@hcengineering/attachment'
|
||||||
|
import core, { DateRangeMode } from '@hcengineering/core'
|
||||||
|
import { DatePresenter, IconScribble } from '@hcengineering/ui'
|
||||||
|
import { ObjectPresenter } from '@hcengineering/view-resources'
|
||||||
|
|
||||||
|
export let value: Drawing
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex-presenter flex-gap-3">
|
||||||
|
<DatePresenter value={value.createdOn} mode={DateRangeMode.DATETIME} kind="list" icon={IconScribble} />
|
||||||
|
<ObjectPresenter objectId={value.createdBy} _class={core.class.Account} shouldShowName={false} />
|
||||||
|
</div>
|
@ -42,6 +42,7 @@ import FileDownload from './components/icons/FileDownload.svelte'
|
|||||||
import IconUploadDuo from './components/icons/UploadDuo.svelte'
|
import IconUploadDuo from './components/icons/UploadDuo.svelte'
|
||||||
import PreviewWidget from './components/PreviewWidget.svelte'
|
import PreviewWidget from './components/PreviewWidget.svelte'
|
||||||
import PreviewPopupActions from './components/PreviewPopupActions.svelte'
|
import PreviewPopupActions from './components/PreviewPopupActions.svelte'
|
||||||
|
import DrawingPresenter from './components/DrawingPresenter.svelte'
|
||||||
|
|
||||||
export * from './types'
|
export * from './types'
|
||||||
|
|
||||||
@ -256,6 +257,7 @@ export default async (): Promise<Resources> => ({
|
|||||||
AttachmentPresenter,
|
AttachmentPresenter,
|
||||||
AttachmentGalleryPresenter,
|
AttachmentGalleryPresenter,
|
||||||
Attachments,
|
Attachments,
|
||||||
|
DrawingPresenter,
|
||||||
FileBrowser,
|
FileBrowser,
|
||||||
Photos,
|
Photos,
|
||||||
PDFViewer,
|
PDFViewer,
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
import { type BlobMetadata, type Attachment, type Drawing } from '@hcengineering/attachment'
|
import { type BlobMetadata, type Attachment, type Drawing } from '@hcengineering/attachment'
|
||||||
import core, {
|
import core, {
|
||||||
|
SortingOrder,
|
||||||
type Blob,
|
type Blob,
|
||||||
type Class,
|
type Class,
|
||||||
type TxOperations as Client,
|
type TxOperations as Client,
|
||||||
@ -151,28 +152,42 @@ export function showAttachmentPreviewPopup (value: WithLookup<Attachment>): Popu
|
|||||||
|
|
||||||
if (value?.type?.startsWith('image/')) {
|
if (value?.type?.startsWith('image/')) {
|
||||||
props.drawingAvailable = true
|
props.drawingAvailable = true
|
||||||
props.loadDrawings = async (): Promise<DrawingData | undefined> => {
|
props.loadDrawings = async (): Promise<Drawing[] | undefined> => {
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
const drawing = await client.findOne(attachment.class.Drawing, { parent: value.file })
|
const drawings = await client.findAll(
|
||||||
if (drawing !== undefined) {
|
attachment.class.Drawing,
|
||||||
return {
|
{
|
||||||
id: drawing._id,
|
parent: value.file,
|
||||||
content: drawing.content
|
space: value.space
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sort: {
|
||||||
|
createdOn: SortingOrder.Descending
|
||||||
|
},
|
||||||
|
limit: 1
|
||||||
|
}
|
||||||
|
)
|
||||||
|
const result = []
|
||||||
|
if (drawings !== undefined) {
|
||||||
|
for (const drawing of drawings) {
|
||||||
|
result.push(drawing)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
props.saveDrawing = async (data: DrawingData): Promise<void> => {
|
props.createDrawing = async (data: DrawingData): Promise<DrawingData> => {
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
if (data.id === undefined) {
|
const newId = await client.createDoc(attachment.class.Drawing, value.space, {
|
||||||
await client.createDoc(attachment.class.Drawing, value.space, {
|
|
||||||
parent: value.file,
|
parent: value.file,
|
||||||
parentClass: core.class.Blob,
|
parentClass: core.class.Blob,
|
||||||
content: data.content
|
content: data.content
|
||||||
})
|
})
|
||||||
|
const newDrawing = await client.findOne(attachment.class.Drawing, { _id: newId })
|
||||||
|
if (newDrawing !== undefined) {
|
||||||
|
return newDrawing
|
||||||
} else {
|
} else {
|
||||||
await client.updateDoc(attachment.class.Drawing, value.space, data.id as Ref<Drawing>, {
|
console.error('Unable to find just created drawing')
|
||||||
content: data.content
|
return data
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,6 +81,7 @@ export default plugin(attachmentId, {
|
|||||||
Attachments: '' as AnyComponent,
|
Attachments: '' as AnyComponent,
|
||||||
Photos: '' as AnyComponent,
|
Photos: '' as AnyComponent,
|
||||||
AttachmentsPresenter: '' as AnyComponent,
|
AttachmentsPresenter: '' as AnyComponent,
|
||||||
|
DrawingPresenter: '' as AnyComponent,
|
||||||
FileBrowser: '' as AnyComponent,
|
FileBrowser: '' as AnyComponent,
|
||||||
PDFViewer: '' as AnyComponent
|
PDFViewer: '' as AnyComponent
|
||||||
},
|
},
|
||||||
|
@ -24,8 +24,8 @@
|
|||||||
|
|
||||||
export let drawingAvailable: boolean
|
export let drawingAvailable: boolean
|
||||||
export let drawingEditable: boolean
|
export let drawingEditable: boolean
|
||||||
export let drawingData: any
|
export let drawings: any
|
||||||
export let saveDrawing: (data: any) => Promise<void>
|
export let createDrawing: (data: any) => Promise<any>
|
||||||
|
|
||||||
$: originalWidth = metadata?.originalWidth
|
$: originalWidth = metadata?.originalWidth
|
||||||
$: originalHeight = metadata?.originalHeight
|
$: originalHeight = metadata?.originalHeight
|
||||||
@ -49,8 +49,8 @@
|
|||||||
<DrawingBoard
|
<DrawingBoard
|
||||||
{imageWidth}
|
{imageWidth}
|
||||||
{imageHeight}
|
{imageHeight}
|
||||||
{drawingData}
|
{drawings}
|
||||||
{saveDrawing}
|
{createDrawing}
|
||||||
active={drawingAvailable && !loading}
|
active={drawingAvailable && !loading}
|
||||||
readonly={drawingAvailable && !drawingEditable}
|
readonly={drawingAvailable && !drawingEditable}
|
||||||
class="object-contain mx-auto"
|
class="object-contain mx-auto"
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import type { Attachment } from '@hcengineering/attachment'
|
import attachment, { type Attachment } from '@hcengineering/attachment'
|
||||||
import type { Tx, TxRemoveDoc } from '@hcengineering/core'
|
import type { Tx, TxRemoveDoc } from '@hcengineering/core'
|
||||||
import type { TriggerControl } from '@hcengineering/server-core'
|
import type { TriggerControl } from '@hcengineering/server-core'
|
||||||
|
|
||||||
@ -23,8 +23,9 @@ import type { TriggerControl } from '@hcengineering/server-core'
|
|||||||
*/
|
*/
|
||||||
export async function OnAttachmentDelete (
|
export async function OnAttachmentDelete (
|
||||||
txes: Tx[],
|
txes: Tx[],
|
||||||
{ removedMap, ctx, storageAdapter, workspace }: TriggerControl
|
{ removedMap, ctx, storageAdapter, workspace, findAll, txFactory }: TriggerControl
|
||||||
): Promise<Tx[]> {
|
): Promise<Tx[]> {
|
||||||
|
const result: Tx[] = []
|
||||||
const toDelete: string[] = []
|
const toDelete: string[] = []
|
||||||
for (const tx of txes) {
|
for (const tx of txes) {
|
||||||
const rmTx = tx as TxRemoveDoc<Attachment>
|
const rmTx = tx as TxRemoveDoc<Attachment>
|
||||||
@ -36,12 +37,18 @@ export async function OnAttachmentDelete (
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
toDelete.push(attach.file)
|
toDelete.push(attach.file)
|
||||||
|
|
||||||
|
const drawings = await findAll(ctx, attachment.class.Drawing, { parent: attach.file })
|
||||||
|
for (const drawing of drawings) {
|
||||||
|
const removeTx = txFactory.createTxRemoveDoc(drawing._class, drawing.space, drawing._id)
|
||||||
|
result.push(removeTx)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (toDelete.length > 0) {
|
if (toDelete.length > 0) {
|
||||||
await storageAdapter.remove(ctx, workspace, toDelete)
|
await storageAdapter.remove(ctx, workspace, toDelete)
|
||||||
}
|
}
|
||||||
|
|
||||||
return []
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||||
|
Loading…
Reference in New Issue
Block a user