diff --git a/models/love/src/index.ts b/models/love/src/index.ts index b25c90400b..a5222eeef7 100644 --- a/models/love/src/index.ts +++ b/models/love/src/index.ts @@ -288,7 +288,8 @@ export function createModel (builder: Builder): void { label: love.string.Meeting, type: WidgetType.Flexible, icon: love.icon.Cam, - component: love.component.MeetingWidget + component: love.component.MeetingWidget, + switcherComponent: love.component.WidgetSwitcher }, love.ids.MeetingWidget ) diff --git a/models/love/src/plugin.ts b/models/love/src/plugin.ts index c7a5c088cf..ef026ba0cf 100644 --- a/models/love/src/plugin.ts +++ b/models/love/src/plugin.ts @@ -27,7 +27,8 @@ export default mergeIds(loveId, love, { WorkbenchExtension: '' as AnyComponent, Settings: '' as AnyComponent, LoveWidget: '' as AnyComponent, - MeetingWidget: '' as AnyComponent + MeetingWidget: '' as AnyComponent, + WidgetSwitcher: '' as AnyComponent }, app: { Love: '' as Ref diff --git a/models/test-management/src/index.ts b/models/test-management/src/index.ts index 43e288745a..811a4a3c52 100644 --- a/models/test-management/src/index.ts +++ b/models/test-management/src/index.ts @@ -93,7 +93,7 @@ function defineApplication (builder: Builder): void { parentKey: 'parent', noParentId: testManagement.ids.NoParent, getFolderLink: testManagement.function.GetTestSuiteLink, - allObjectsLabel: testManagement.string.AllTestCases, + allObjectsLabel: testManagement.string.AllTestSuites, allObjectsIcon: testManagement.icon.TestSuites }, syncWithLocationQuery: true @@ -335,20 +335,6 @@ function defineTestCase (builder: Builder): void { ignoreKeys: ['createdBy', 'modifiedBy', 'createdOn', 'modifiedOn', 'name'] }) - builder.createDoc( - view.class.Viewlet, - core.space.Model, - { - attachTo: testManagement.class.TestCase, - descriptor: view.viewlet.Table, - config: ['', { key: 'attachedTo', label: testManagement.string.TestSuite }, 'status', 'assignee'], - configOptions: { - strict: true - } - }, - testManagement.viewlet.TableTestCase - ) - const viewOptions: ViewOptionsModel = { groupBy: ['attachedTo'], orderBy: [ @@ -385,7 +371,6 @@ function defineTestCase (builder: Builder): void { props: { kind: 'list', size: 'small', shouldShowName: false }, displayProps: { key: 'status', fixed: 'left' } }, - { key: '', displayProps: { fixed: 'left', key: 'lead' } }, { key: '', displayProps: { grow: true } }, { key: 'modifiedOn', displayProps: { key: 'modified', fixed: 'right', dividerBefore: true } }, { @@ -398,6 +383,20 @@ function defineTestCase (builder: Builder): void { }, testManagement.viewlet.ListTestCase ) + + builder.createDoc( + view.class.Viewlet, + core.space.Model, + { + attachTo: testManagement.class.TestCase, + descriptor: view.viewlet.Table, + config: ['', { key: 'attachedTo', label: testManagement.string.TestSuite }, 'status', 'assignee'], + configOptions: { + strict: true + } + }, + testManagement.viewlet.TableTestCase + ) } function defineTestRun (builder: Builder): void { @@ -493,6 +492,8 @@ function defineTestResult (builder: Builder): void { key: 'status', props: { kind: 'list', size: 'small', shouldShowName: false } }, + { key: '', displayProps: { grow: true } }, + { key: 'modifiedOn', displayProps: { key: 'modified', fixed: 'right', dividerBefore: true } }, { key: 'assignee', props: { kind: 'list', shouldShowName: false, avatarSize: 'x-small' }, diff --git a/models/workbench/src/index.ts b/models/workbench/src/index.ts index 729bf39a56..96dae1a6f1 100644 --- a/models/workbench/src/index.ts +++ b/models/workbench/src/index.ts @@ -81,6 +81,7 @@ export class TWidget extends TDoc implements Widget { component!: AnyComponent tabComponent?: AnyComponent + switcherComponent?: AnyComponent headerLabel?: IntlString closeIfNoTabs?: boolean diff --git a/packages/presentation/src/components/DrawingBoardToolbar.svelte b/packages/presentation/src/components/DrawingBoardToolbar.svelte index 1d5f793615..856b96405e 100644 --- a/packages/presentation/src/components/DrawingBoardToolbar.svelte +++ b/packages/presentation/src/components/DrawingBoardToolbar.svelte @@ -80,7 +80,7 @@ case 'add-color': { if (colorSelector !== undefined) { colorSelector.value = penColor - colorSelector.click() + colorSelector.showPicker() } break } @@ -130,8 +130,8 @@ if (!penColors.includes(penColor)) { penColor = penColors[0] ?? defaultColor } - penWidth = parseInt(localStorage.getItem(storageKey.penWidth) ?? '4') - eraserWidth = parseInt(localStorage.getItem(storageKey.eraserWidth) ?? '30') + penWidth = parseInt(localStorage.getItem(storageKey.penWidth) ?? '6') + eraserWidth = parseInt(localStorage.getItem(storageKey.eraserWidth) ?? '50') }) function updatePenWidth (): void { @@ -181,9 +181,25 @@ {/if}
{#if tool === 'pen'} - + {:else} - + {/if}
{#each penColors as color} @@ -247,4 +263,8 @@ width: 0; opacity: 0; } + + .widthSelector { + width: 80px; + } diff --git a/packages/presentation/src/drawing.ts b/packages/presentation/src/drawing.ts index cc9a97b402..fd459fda00 100644 --- a/packages/presentation/src/drawing.ts +++ b/packages/presentation/src/drawing.ts @@ -295,7 +295,6 @@ export function drawing ( } }) prevPos = { x, y } - props.panned?.(draw.offset) }) } } @@ -310,6 +309,8 @@ export function drawing ( if (draw.isDrawingTool()) { draw.drawLive(e.offsetX, e.offsetY, true) storeCommand() + } else if (draw.tool === 'pan') { + props.panned?.(draw.offset) } draw.on = false } @@ -347,6 +348,7 @@ export function drawing ( canvas.style.cursor = props.defaultCursor ?? 'default' } else if (draw.isDrawingTool()) { canvas.style.cursor = 'none' + canvasCursor.style.visibility = 'visible' const erasing = draw.tool === 'erase' const w = draw.cursorWidth() canvasCursor.style.background = erasing ? 'none' : draw.penColor diff --git a/packages/ui/src/components/Dialog.svelte b/packages/ui/src/components/Dialog.svelte index ef8f85087d..9679461d9f 100644 --- a/packages/ui/src/components/Dialog.svelte +++ b/packages/ui/src/components/Dialog.svelte @@ -35,6 +35,10 @@ const dispatch = createEventDispatcher() + export function maximize (): void { + toggleFullSize = true + } + let fullSize: boolean = false let toggleFullSize: boolean = fullSize $: needFullSize = checkAdaptiveMatching($deviceInfo.size, 'md') diff --git a/plugins/love-resources/src/components/EditRoom.svelte b/plugins/love-resources/src/components/EditRoom.svelte index 4a5d3a0304..970cd09657 100644 --- a/plugins/love-resources/src/components/EditRoom.svelte +++ b/plugins/love-resources/src/components/EditRoom.svelte @@ -75,7 +75,13 @@ connectLabel = love.string.StartMeeting } - function showConnectionButton (object: Room, isConnected: boolean, myOffice?: Room, currentRoom?: Room): boolean { + function showConnectionButton ( + object: Room, + connecting: boolean, + isConnected: boolean, + myOffice?: Room, + currentRoom?: Room + ): boolean { // Do not show connect button in my office if (object._id === myOffice?._id) return false // Show during connecting with spinner @@ -98,7 +104,7 @@ focusIndex={1} />
- {#if showConnectionButton(object, $isConnected, $myOffice, $currentRoom)} + {#if showConnectionButton(object, connecting, $isConnected, $myOffice, $currentRoom)} {/if}
diff --git a/plugins/love-resources/src/components/widget/WidgetSwitcher.svelte b/plugins/love-resources/src/components/widget/WidgetSwitcher.svelte new file mode 100644 index 0000000000..d0e7718bb5 --- /dev/null +++ b/plugins/love-resources/src/components/widget/WidgetSwitcher.svelte @@ -0,0 +1,34 @@ + + + + diff --git a/plugins/love-resources/src/index.ts b/plugins/love-resources/src/index.ts index 0a32f9b44f..c06f50ae41 100644 --- a/plugins/love-resources/src/index.ts +++ b/plugins/love-resources/src/index.ts @@ -11,6 +11,7 @@ import Settings from './components/Settings.svelte' import WorkbenchExtension from './components/WorkbenchExtension.svelte' import LoveWidget from './components/LoveWidget.svelte' import MeetingWidget from './components/widget/MeetingWidget.svelte' +import WidgetSwitcher from './components/widget/WidgetSwitcher.svelte' import MeetingMinutesPresenter from './components/MeetingMinutesPresenter.svelte' import MeetingMinutesSection from './components/MeetingMinutesSection.svelte' import EditMeetingMinutes from './components/EditMeetingMinutes.svelte' @@ -47,6 +48,7 @@ export default async (): Promise => ({ EditMeetingData, LoveWidget, MeetingWidget, + WidgetSwitcher, MeetingMinutesPresenter, MeetingMinutesSection, EditMeetingMinutes, diff --git a/plugins/love-resources/src/utils.ts b/plugins/love-resources/src/utils.ts index 1c3681f244..00c4a9e363 100644 --- a/plugins/love-resources/src/utils.ts +++ b/plugins/love-resources/src/utils.ts @@ -374,16 +374,12 @@ lk.on(RoomEvent.RecordingStatusChanged, (evt) => { isRecording.set(evt) }) lk.on(RoomEvent.RoomMetadataChanged, (metadata) => { - try { - const data = JSON.parse(metadata) as RoomMetadata - if (data.recording !== undefined) { - isRecording.set(data.recording) - } - if (data.transcription !== undefined) { - isTranscription.set(data.transcription === TranscriptionStatus.InProgress) - } - } catch (err: any) { - Analytics.handleError(err) + const data = parseMetadata(metadata) + if (data.recording !== undefined) { + isRecording.set(data.recording) + } + if (data.transcription !== undefined) { + isTranscription.set(data.transcription === TranscriptionStatus.InProgress) } }) @@ -392,7 +388,7 @@ lk.on(RoomEvent.Connected, () => { sendMessage({ type: 'connect', value: true }) isCurrentInstanceConnected.set(true) isRecording.set(lk.isRecording) - void initRoomMetadata(lk.metadata) + void initRoom() Analytics.handleEvent(LoveEvents.ConnectedToRoom) }) lk.on(RoomEvent.Disconnected, () => { @@ -402,25 +398,19 @@ lk.on(RoomEvent.Disconnected, () => { Analytics.handleEvent(LoveEvents.DisconnectedFromRoom) }) -async function initRoomMetadata (metadata: string | undefined): Promise { - let data: RoomMetadata - try { - data = metadata == null || metadata === '' ? {} : JSON.parse(metadata) - } catch (err: any) { - data = {} - Analytics.handleError(err) - } - - isTranscription.set(data.transcription === TranscriptionStatus.InProgress) - +async function initRoom (): Promise { const room = get(currentRoom) - const meetingMinutes = get(currentMeetingMinutes) - const isValidMeeting = - meetingMinutes != null && meetingMinutes.attachedTo === room?._id && meetingMinutes.status === MeetingStatus.Active - - if (room != null && !isValidMeeting) { + if (room !== undefined) { await initMeetingMinutes(room) } + await initRoomMetadata(lk.metadata) +} + +async function initRoomMetadata (metadata: string | undefined): Promise { + const room = get(currentRoom) + const data: RoomMetadata = parseMetadata(metadata) + + isTranscription.set(data.transcription === TranscriptionStatus.InProgress) if ( (data.transcription == null || data.transcription === TranscriptionStatus.Idle) && @@ -434,6 +424,15 @@ async function initRoomMetadata (metadata: string | undefined): Promise { } } +function parseMetadata (metadata: string | undefined): RoomMetadata { + try { + return metadata == null || metadata === '' ? {} : JSON.parse(metadata) + } catch (err: any) { + Analytics.handleError(err) + return {} + } +} + async function withRetries (fn: () => Promise, retries: number, delay: number): Promise { for (let attempt = 0; attempt < retries; attempt++) { try { @@ -738,7 +737,6 @@ export async function connectRoom ( 3, 1000 ) - await initMeetingMinutes(room) } catch (err) { console.error(err) await leaveRoom(currentInfo, get(myOffice)) diff --git a/plugins/test-management-assets/lang/cs.json b/plugins/test-management-assets/lang/cs.json index 9f075c90e7..154069f81f 100644 --- a/plugins/test-management-assets/lang/cs.json +++ b/plugins/test-management-assets/lang/cs.json @@ -21,7 +21,7 @@ "TestCase": "Testovací případ", "TestProject": "Projekt", "TestManagementApplication": "Správa testů", - "AllTestCases": "Všechny testovací případy", + "AllTestSuites": "Všechny testovací sady", "AllProjects": "Všechny projekty", "Projects": "Projekty", "CreateProject": "Vytvořit projekt", diff --git a/plugins/test-management-assets/lang/en.json b/plugins/test-management-assets/lang/en.json index 943bbaa95a..d318793798 100644 --- a/plugins/test-management-assets/lang/en.json +++ b/plugins/test-management-assets/lang/en.json @@ -21,7 +21,7 @@ "TestCase": "Test case", "TestProject": "Project", "TestManagementApplication": "Test Management", - "AllTestCases": "All test cases", + "AllTestSuites": "All test suites", "AllProjects": "All projects", "Projects": "Projects", "CreateProject": "Create project", diff --git a/plugins/test-management-assets/lang/es.json b/plugins/test-management-assets/lang/es.json index f0fdb2bc09..8a0a249378 100644 --- a/plugins/test-management-assets/lang/es.json +++ b/plugins/test-management-assets/lang/es.json @@ -21,7 +21,7 @@ "TestCase": "Caso de prueba", "TestProject": "Proyecto", "TestManagementApplication": "Gestión de Pruebas", - "AllTestCases": "Todos los casos de prueba", + "AllTestSuites": "Todos los conjuntos de pruebas", "AllProjects": "Todos los proyectos", "Projects": "Proyectos", "CreateProject": "Crear proyecto", diff --git a/plugins/test-management-assets/lang/fr.json b/plugins/test-management-assets/lang/fr.json index 3fd4f7c877..8ab2c8135f 100644 --- a/plugins/test-management-assets/lang/fr.json +++ b/plugins/test-management-assets/lang/fr.json @@ -21,7 +21,7 @@ "TestCase": "Cas de test", "TestProject": "Projet", "TestManagementApplication": "Gestion des tests", - "AllTestCases": "Tous les cas de tests", + "AllTestSuites": "Tous les ensembles de tests", "AllProjects": "Tous les projets", "Projects": "Projets", "CreateProject": "Créer un projet", diff --git a/plugins/test-management-assets/lang/it.json b/plugins/test-management-assets/lang/it.json index a1a56b4ab7..195bfb66f9 100644 --- a/plugins/test-management-assets/lang/it.json +++ b/plugins/test-management-assets/lang/it.json @@ -21,7 +21,7 @@ "TestCase": "Caso di test", "TestProject": "Progetto", "TestManagementApplication": "Gestione Test", - "AllTestCases": "Tutti i casi di test", + "AllTestSuites": "Tutti i set di test", "AllProjects": "Tutti i progetti", "Projects": "Progetti", "CreateProject": "Crea progetto", diff --git a/plugins/test-management-assets/lang/pt.json b/plugins/test-management-assets/lang/pt.json index c6a71d734c..a470584a46 100644 --- a/plugins/test-management-assets/lang/pt.json +++ b/plugins/test-management-assets/lang/pt.json @@ -21,7 +21,7 @@ "TestCase": "Caso de teste", "TestProject": "Projeto", "TestManagementApplication": "Gestão de Testes", - "AllTestCases": "Todos os casos de teste", + "AllTestSuites": "Todos os conjuntos de testes", "AllProjects": "Todos os projetos", "Projects": "Projetos", "CreateProject": "Criar projeto", diff --git a/plugins/test-management-assets/lang/ru.json b/plugins/test-management-assets/lang/ru.json index e18849137c..aace7962d2 100644 --- a/plugins/test-management-assets/lang/ru.json +++ b/plugins/test-management-assets/lang/ru.json @@ -21,7 +21,7 @@ "TestCase": "Тест-кейс", "TestProject": "Проект", "TestManagementApplication": "Управление тестированием", - "AllTestCases": "Все тест-кейсы", + "AllTestSuites": "Все категории тестов", "AllProjects": "Все проекты", "Projects": "Проекты", "CreateProject": "Создать проект", @@ -36,14 +36,14 @@ "RoleLabel": "Роль", "ProjectMembers": "Участники проекта", "TestSuites": "Тестовые наборы", - "CreateTestSuite": "Создать тестовый набор", + "CreateTestSuite": "Создать новую категорию тестов", "NamePlaceholder": "Имя тестового набора", "DescriptionPlaceholder": "Описание (опционально)", "TestRuns": "Выполнение тестов", "TestRun": "Тест план", "TestNamePlaceholder": "Имя тест кейса", "ChooseIcon": "Выберите иконку", - "NoTestSuite": "Тестовый набор не задан", + "NoTestSuite": "Категория тестов не задана", "StatusDraft": "В прогрессе", "StatusReview": "Готов для ревью", "StatusReviewComments": "Требует исправлений", @@ -58,7 +58,7 @@ "NoTestCases": "Тест-кейсы отсутствует", "CreateTestRun": "Создать тест план", "TestRunNamePlaceholder": "Название тест плана", - "SelectTestSuites": "Выбрать наборы тестов", + "SelectTestSuites": "Выбрать категории тестов", "SelectTestCases": "Выбрать тест-кейсы", "TestLibrary": "Библиотека тестов", "TestResult": "Результат", diff --git a/plugins/test-management-assets/lang/zh.json b/plugins/test-management-assets/lang/zh.json index 3854292f46..5eb4833e29 100644 --- a/plugins/test-management-assets/lang/zh.json +++ b/plugins/test-management-assets/lang/zh.json @@ -21,7 +21,7 @@ "TestCase": "測試用例", "TestProject": "專案", "TestManagementApplication": "測試管理", - "AllTestCases": "所有測試用例", + "AllTestSuites": "所有測試套件", "AllProjects": "所有項目", "Projects": "專案", "CreateProject": "創建專案", diff --git a/plugins/test-management/src/plugin.ts b/plugins/test-management/src/plugin.ts index fc13a88910..1ebfde133d 100644 --- a/plugins/test-management/src/plugin.ts +++ b/plugins/test-management/src/plugin.ts @@ -128,7 +128,7 @@ export const testManagementPlugin = plugin(testManagementId, { TestCase: '' as IntlString, TestProject: '' as IntlString, TestManagementApplication: '' as IntlString, - AllTestCases: '' as IntlString, + AllTestSuites: '' as IntlString, AllProjects: '' as IntlString, Projects: '' as IntlString, CreateProject: '' as IntlString, diff --git a/plugins/text-editor-resources/src/components/CollaborativeTextEditor.svelte b/plugins/text-editor-resources/src/components/CollaborativeTextEditor.svelte index 938e5d7714..711a7c4b34 100644 --- a/plugins/text-editor-resources/src/components/CollaborativeTextEditor.svelte +++ b/plugins/text-editor-resources/src/components/CollaborativeTextEditor.svelte @@ -16,7 +16,15 @@ --> + +{#if savedCmds !== undefined && savedProps !== undefined} + {#if loading} +
+ + +
+ {:else} +
{ + savedCmds.push([cmd]) + }, + panned: (offset) => { + savedProps.set('offset', offset) + } + }} + > + {#if grabFocus} + + + + {/if} + {#if !readonly} + { + savedCmds.delete(0, savedCmds.length) + savedProps.set('offset', { x: 0, y: 0 }) + }} + /> + {/if} + +
+ {/if} +{/if} + + diff --git a/plugins/text-editor-resources/src/components/DrawingBoardNodeView.svelte b/plugins/text-editor-resources/src/components/DrawingBoardNodeView.svelte new file mode 100644 index 0000000000..6c8d20b80e --- /dev/null +++ b/plugins/text-editor-resources/src/components/DrawingBoardNodeView.svelte @@ -0,0 +1,176 @@ + + + +{#if savedBoard?.commands !== undefined && savedBoard?.props !== undefined} + + +
+
+ {#if selected} +
+ + + +
+
+ + + +
+ {/if} +
+
+{/if} + + diff --git a/plugins/text-editor-resources/src/components/DrawingBoardPopup.svelte b/plugins/text-editor-resources/src/components/DrawingBoardPopup.svelte index 75348ad5e2..2d894be680 100644 --- a/plugins/text-editor-resources/src/components/DrawingBoardPopup.svelte +++ b/plugins/text-editor-resources/src/components/DrawingBoardPopup.svelte @@ -13,109 +13,36 @@ // limitations under the License. --> {#if savedCmds !== undefined && savedProps !== undefined} { dispatch('close') }} > -
{ - savedCmds.push([cmd]) - }, - panned: (offset) => { - savedProps.set('offset', offset) - } - }} - > - - - - - { - savedCmds.delete(0, savedCmds.length) - savedProps.set('offset', { x: 0, y: 0 }) - }} - /> -
+
{/if} diff --git a/plugins/text-editor-resources/src/components/extension/drawingBoard.ts b/plugins/text-editor-resources/src/components/extension/drawingBoard.ts index c1476599cb..e01d31a569 100644 --- a/plugins/text-editor-resources/src/components/extension/drawingBoard.ts +++ b/plugins/text-editor-resources/src/components/extension/drawingBoard.ts @@ -13,41 +13,26 @@ // limitations under the License. // -import { type DrawingCmd, type DrawingProps, drawing } from '@hcengineering/presentation' +import { type DrawingCmd } from '@hcengineering/presentation' import { showPopup } from '@hcengineering/ui' import { type Editor, mergeAttributes, Node } from '@tiptap/core' import { NodeSelection } from '@tiptap/pm/state' -import type { Array as YArray, Doc as YDoc, Map as YMap } from 'yjs' +import type { Array as YArray, Map as YMap } from 'yjs' +import DrawingBoardNodeView from '../DrawingBoardNodeView.svelte' import DrawingBoardPopup from '../DrawingBoardPopup.svelte' - -const defaultHeight = 500 -const maxHeight = 1000 -const minHeight = 100 +import { SvelteNodeViewRenderer } from '../node-view' export interface DrawingBoardOptions { - ydoc?: YDoc + getSavedBoard: (id: string) => SavedBoard } -const scribbleSvg = ` - -` -const chevronSvg = ` - -` - -interface SavedBoard { +export interface SavedBoard { commands: YArray props: YMap + loading: boolean } -function getSavedBoard (ydoc: YDoc | undefined, id: string): SavedBoard { - const board = ydoc?.getMap(`drawing-board-${id}`) - const commands = board?.get('commands') as YArray - const props = board?.get('props') as YMap - return { commands, props } -} - -function showBoardPopup (board: SavedBoard, editor: Editor): void { +export function showBoardPopup (board: SavedBoard, editor: Editor): void { if (board.commands !== undefined && board.props !== undefined) { showPopup( DrawingBoardPopup, @@ -76,23 +61,35 @@ export const DrawingBoardExtension = Node.create({ group: 'block', + draggable: true, + renderHTML ({ HTMLAttributes }) { return ['div', mergeAttributes({ 'data-type': this.name }, HTMLAttributes)] }, + parseHTML () { + return [ + { + tag: 'div[data-type="drawingBoard"]' + } + ] + }, + addOptions () { return { - ydoc: undefined + getSavedBoard: (id) => ({}) as any as SavedBoard } }, addAttributes () { return { id: { - renderHTML: (attrs) => ({ 'data-id': attrs.id }) + renderHTML: (attrs) => ({ 'data-id': attrs.id }), + parseHTML: (element) => element.getAttribute('data-id') }, height: { - renderHTML: (attrs) => ({ 'data-height': attrs.height }) + renderHTML: (attrs) => ({ 'data-height': attrs.height }), + parseHTML: (element) => parseInt(element.getAttribute('data-height') ?? '500') } } }, @@ -105,8 +102,10 @@ export const DrawingBoardExtension = Node.create({ if (state.selection instanceof NodeSelection) { const node = state.selection.node if (node?.type.name === this.name) { - const board = getSavedBoard(this.options.ydoc, node.attrs.id) - showBoardPopup(board, this.editor) + const board = this.options.getSavedBoard(node.attrs.id) + if (!board.loading) { + showBoardPopup(board, this.editor) + } return true } } @@ -122,155 +121,11 @@ export const DrawingBoardExtension = Node.create({ }, addNodeView () { - return ({ node, getPos }) => { - const board = getSavedBoard(this.options.ydoc, node.attrs.id) - if (board.commands === undefined || board.props === undefined) { - return {} + return SvelteNodeViewRenderer(DrawingBoardNodeView, { + contentAs: 'div', + componentProps: { + getSavedBoard: this.options.getSavedBoard } - - const normalBorder = '1px solid var(--theme-navpanel-border)' - const selectedBorder = '1px solid var(--theme-editbox-focus-border)' - - const dom = document.createElement('div') - dom.id = node.attrs.id - dom.contentEditable = 'false' - dom.style.width = '100%' - dom.style.position = 'relative' - dom.style.height = `${node.attrs.height ?? defaultHeight}px` - dom.style.border = normalBorder - dom.style.borderRadius = 'var(--small-BorderRadius)' - dom.style.backgroundColor = 'var(--theme-navpanel-color)' - dom.ondblclick = () => { - showBoardPopup(board, this.editor) - } - - const drawingProps: DrawingProps = { - readonly: true, - autoSize: true, - commands: board.commands.toArray(), - commandCount: board.commands.length, - offset: board.props.get('offset') - } - - const { canvas, update: updateDrawing } = drawing(dom, drawingProps) - if (canvas === undefined || updateDrawing === undefined) { - return {} - } - dom.appendChild(canvas) - - const listenSavedCommands = (): void => { - let update = false - if (board.commands.length === 0) { - update = true - drawingProps.commands = [] - } else if (board.commands.length > drawingProps.commands.length) { - update = true - for (let i = drawingProps.commands.length; i < board.commands.length; i++) { - drawingProps.commands.push(board.commands.get(i)) - } - } - if (update) { - drawingProps.commandCount = board.commands.length - updateDrawing(drawingProps) - } - } - const listenSavedProps = (): void => { - drawingProps.offset = board.props.get('offset') - updateDrawing(drawingProps) - } - board.commands.observe(listenSavedCommands) - board.props.observe(listenSavedProps) - - const button = document.createElement('div') - button.style.visibility = 'hidden' - button.style.cursor = 'pointer' - button.style.position = 'absolute' - button.style.top = '0.3rem' - button.style.right = '0.3rem' - button.style.width = '2.2rem' - button.style.height = '2.2rem' - button.style.color = 'var(--primary-button-color)' - button.style.backgroundColor = 'var(--primary-button-default)' - button.style.borderRadius = 'var(--extra-small-BorderRadius)' - button.style.border = normalBorder - button.style.display = 'flex' - button.style.alignItems = 'center' - button.style.justifyContent = 'center' - button.innerHTML = scribbleSvg - button.onclick = () => { - showBoardPopup(board, this.editor) - } - dom.appendChild(button) - - const resizer = document.createElement('div') - resizer.style.position = 'absolute' - resizer.style.bottom = '0' - resizer.style.left = 'calc(50% - 4rem)' - resizer.style.width = '8rem' - resizer.style.height = '0.6rem' - resizer.style.cursor = 'row-resize' - resizer.style.color = 'var(--global-on-accent-TextColor)' - resizer.style.backgroundColor = 'var(--global-accent-IconColor)' - resizer.style.border = selectedBorder - resizer.style.display = 'flex' - resizer.style.alignItems = 'center' - resizer.style.justifyContent = 'center' - resizer.style.visibility = 'hidden' - resizer.style.borderTopLeftRadius = 'var(--small-BorderRadius)' - resizer.style.borderTopRightRadius = 'var(--small-BorderRadius)' - resizer.style.borderBottom = 'none' - resizer.style.opacity = '0.5' - resizer.innerHTML = chevronSvg - resizer.onpointerenter = () => { - resizer.style.opacity = '1' - } - resizer.onpointerleave = () => { - resizer.style.opacity = '0.5' - } - resizer.onpointerdown = (e: PointerEvent): void => { - e.preventDefault() - resizer.setPointerCapture(e.pointerId) - const { offsetHeight } = dom - const startY = e.clientY - offsetHeight - let height = node.attrs.height ?? defaultHeight - const onPointerMove = (e: PointerEvent): void => { - e.preventDefault() - height = Math.max(minHeight, e.clientY - startY) - height = Math.min(maxHeight, height) - dom.style.height = `${height}px` - } - const onPointerUp = (e: PointerEvent): void => { - e.preventDefault() - resizer.releasePointerCapture(e.pointerId) - if (typeof getPos === 'function') { - const tr = this.editor.state.tr.setNodeMarkup(getPos(), undefined, { ...node.attrs, height }) - this.editor.view.dispatch(tr) - } - dom.removeEventListener('pointermove', onPointerMove) - dom.removeEventListener('pointerup', onPointerUp) - } - dom.addEventListener('pointermove', onPointerMove) - dom.addEventListener('pointerup', onPointerUp) - } - dom.appendChild(resizer) - - return { - dom, - selectNode: () => { - dom.style.border = selectedBorder - button.style.visibility = 'visible' - resizer.style.visibility = 'visible' - }, - deselectNode: () => { - dom.style.border = normalBorder - button.style.visibility = 'hidden' - resizer.style.visibility = 'hidden' - }, - destroy: () => { - board.commands.unobserve(listenSavedCommands) - board.props.unobserve(listenSavedProps) - } - } - } + }) } }) diff --git a/plugins/workbench-resources/src/components/AppItem.svelte b/plugins/workbench-resources/src/components/AppItem.svelte index 6ac0e969dc..02c26f6b3b 100644 --- a/plugins/workbench-resources/src/components/AppItem.svelte +++ b/plugins/workbench-resources/src/components/AppItem.svelte @@ -21,13 +21,14 @@ export let icon: Asset | AnySvelteComponent export let selected: boolean = false export let size: 'small' | 'medium' | 'large' = 'large' + export let kind: 'default' | 'positive' | 'negative' = 'default' export let loading: boolean = false export let notify: boolean = false export let navigator: boolean = false