mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-27 20:14:57 +02:00
216 lines
7.1 KiB
Svelte
216 lines
7.1 KiB
Svelte
<!--
|
|
// Copyright © 2022-2023 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 chunter from '@hcengineering/chunter'
|
|
import { getClient } from '@hcengineering/presentation'
|
|
import { isEmptyMarkup } from '@hcengineering/text'
|
|
import { Heading } from '@hcengineering/text-editor'
|
|
import { Button, Component, EditBox } from '@hcengineering/ui'
|
|
import documents, { Document, DocumentSection, DocumentTemplateSection } from '@hcengineering/controlled-documents'
|
|
|
|
import Info from '../icons/Info.svelte'
|
|
import {
|
|
$documentCommentHighlightedLocation as highlighted,
|
|
$canAddDocumentComments as canAddDocumentComments,
|
|
$canViewDocumentComments as canViewDocumentComments,
|
|
$collapsedDocumentSectionIds as collapsedSectionIds,
|
|
$documentSectionEditingDescription as documentSectionEditingDescription,
|
|
$groupedDocumentComments as groupedDocumentComments,
|
|
$isEditable as isEditable,
|
|
documentCommentsDisplayRequested,
|
|
documentSectionToggled,
|
|
showAddCommentPopupFx,
|
|
updateDocumentSectionDescriptionFx
|
|
} from '../../stores/editors/document'
|
|
import { openGuidanceEditor } from '../../utils'
|
|
import FieldSectionEditor from '../FieldSectionEditor.svelte'
|
|
import DescriptionEditor from './editors/DescriptionEditor.svelte'
|
|
|
|
export let document: Document
|
|
export let value: DocumentSection
|
|
export let index: number
|
|
export let dragging = false
|
|
|
|
export let headings: Heading[] = [] // out
|
|
|
|
const client = getClient()
|
|
|
|
const h = client.getHierarchy()
|
|
|
|
$: editor = h.as(h.getClass(value._class), documents.mixin.DocumentSectionEditor)?.editor
|
|
|
|
let sectionHasGuidance = false
|
|
let templateSection: DocumentTemplateSection | undefined
|
|
$: getDocumentTemplateSection(value)
|
|
async function getDocumentTemplateSection (section: DocumentSection) {
|
|
if (h.hasMixin(value, documents.mixin.DocumentTemplateSection)) {
|
|
templateSection = h.as(value, documents.mixin.DocumentTemplateSection)
|
|
} else if (value.templateSectionId != null) {
|
|
templateSection = await client.findOne(documents.mixin.DocumentTemplateSection, { _id: value.templateSectionId })
|
|
}
|
|
|
|
if (templateSection) {
|
|
const guidance = templateSection.guidance
|
|
sectionHasGuidance = !isEmptyMarkup(guidance)
|
|
}
|
|
}
|
|
|
|
let contentHeadings: Heading[] = []
|
|
$: sectionHeading = makeSectionHeading(value, index)
|
|
$: headings = [sectionHeading, ...contentHeadings]
|
|
|
|
function handleSectionContentHeadings (headings: Heading[]): void {
|
|
contentHeadings = headings
|
|
}
|
|
|
|
function makeSectionHeading (section: DocumentSection, index: number): Heading {
|
|
index = index + 1
|
|
const id = `section-${index}`
|
|
const title = `${index}. ${section.title}`
|
|
return { id, title, level: 0 }
|
|
}
|
|
|
|
async function handleOpenAddCommentPopup (ev?: Event): Promise<void> {
|
|
if (!$canAddDocumentComments) {
|
|
return
|
|
}
|
|
|
|
await showAddCommentPopupFx({
|
|
element: (ev as MouseEvent).target as HTMLElement,
|
|
sectionKey: value.key
|
|
})
|
|
}
|
|
|
|
function handleDisplayDocumentComments (ev?: Event): void {
|
|
if (!$canViewDocumentComments) {
|
|
return
|
|
}
|
|
if (!$groupedDocumentComments.hasDocumentComments(value.key)) {
|
|
return
|
|
}
|
|
|
|
ev?.stopPropagation()
|
|
|
|
documentCommentsDisplayRequested({ element: (ev as MouseEvent).target as HTMLElement, sectionKey: value.key })
|
|
}
|
|
|
|
async function updateSectionTitle (title: string) {
|
|
await client.update(value, { title })
|
|
}
|
|
|
|
let sectionElement: HTMLElement
|
|
export function getSectionElement (): HTMLElement {
|
|
return sectionElement
|
|
}
|
|
|
|
let title = value.title
|
|
|
|
let descr = ''
|
|
$: getDescription(templateSection)
|
|
$: isActiveSectionNode = !!$highlighted && $highlighted.sectionKey === value.key && !$highlighted.nodeId
|
|
function getDescription (section?: DocumentTemplateSection) {
|
|
if (section != null) {
|
|
descr = h.as(section, documents.mixin.DocumentTemplateSection).description ?? ''
|
|
}
|
|
}
|
|
|
|
$: isEditingDescription = $documentSectionEditingDescription === value._id
|
|
|
|
async function stopEditingDescription () {
|
|
if (!templateSection) {
|
|
return
|
|
}
|
|
updateDocumentSectionDescriptionFx({ section: templateSection, description: descr })
|
|
}
|
|
|
|
function showGuidance (ev: MouseEvent) {
|
|
if (templateSection == null) {
|
|
return
|
|
}
|
|
|
|
const isEditingTemplate = templateSection._id === value._id
|
|
openGuidanceEditor(client, templateSection, index + 1, isEditingTemplate ? 'canEdit' : 'readonly', ev)
|
|
}
|
|
</script>
|
|
|
|
<FieldSectionEditor
|
|
editable={$isEditable}
|
|
expanded={!$collapsedSectionIds.has(value._id)}
|
|
on:toggle={() => documentSectionToggled(value._id)}
|
|
>
|
|
<svelte:fragment slot="before-header">
|
|
<slot name="before-header" />
|
|
</svelte:fragment>
|
|
<svelte:fragment slot="index">
|
|
<span class="sectionTitle">
|
|
{index + 1}
|
|
</span>
|
|
</svelte:fragment>
|
|
<svelte:fragment slot="header">
|
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
|
<div
|
|
id={sectionHeading.id}
|
|
class:text-editor-highlighted-node-warning={isActiveSectionNode ||
|
|
$groupedDocumentComments.hasDocumentComments(value.key)}
|
|
class:text-editor-highlighted-node-selected={isActiveSectionNode}
|
|
class="sectionTitle"
|
|
bind:this={sectionElement}
|
|
on:click={handleDisplayDocumentComments}
|
|
>
|
|
{#if $isEditable && !dragging}
|
|
<EditBox maxWidth="100%" bind:value={title} propagateClick on:change={() => updateSectionTitle(title)} />
|
|
{:else}
|
|
{title}
|
|
{/if}
|
|
</div>
|
|
</svelte:fragment>
|
|
<svelte:fragment slot="header-extra">
|
|
<div class="flex-row-center ml-2 mr-10">
|
|
{#if $canAddDocumentComments}
|
|
<Button icon={chunter.icon.Chunter} kind="list-header" size="small" on:click={handleOpenAddCommentPopup} />
|
|
{/if}
|
|
{#if templateSection && sectionHasGuidance}
|
|
<Button icon={Info} kind="list-header" size="small" on:click={showGuidance} />
|
|
{/if}
|
|
</div>
|
|
</svelte:fragment>
|
|
<svelte:fragment slot="before-content">
|
|
{#if isEditingDescription || descr.length > 0}
|
|
<div class="section-descr no-print">
|
|
<DescriptionEditor bind:value={descr} disabled={!isEditingDescription} on:blur={stopEditingDescription} />
|
|
</div>
|
|
{/if}
|
|
</svelte:fragment>
|
|
<svelte:fragment slot="content">
|
|
<Component
|
|
is={editor}
|
|
props={{
|
|
value,
|
|
document,
|
|
onHeadings: handleSectionContentHeadings
|
|
}}
|
|
on:change
|
|
/>
|
|
</svelte:fragment>
|
|
</FieldSectionEditor>
|
|
|
|
<style lang="scss">
|
|
.sectionTitle {
|
|
font-size: 1.75rem;
|
|
line-height: 150%;
|
|
}
|
|
</style>
|