Merge branch 'develop' of https://github.com/hcengineering/platform into staging-new

Signed-off-by: Artem Savchenko <armisav@gmail.com>
This commit is contained in:
Artem Savchenko
2025-11-14 12:14:19 +07:00
14 changed files with 85 additions and 44 deletions
+3
View File
@@ -26143,6 +26143,9 @@ importers:
'@hcengineering/view':
specifier: workspace:^0.7.0
version: link:../view
'@hcengineering/workbench':
specifier: workspace:^0.7.0
version: link:../workbench
fast-equals:
specifier: ^5.2.2
version: 5.3.2
+1 -1
View File
@@ -23,7 +23,7 @@ table.proseTable {
--table-handle-size: 0.875rem;
--table-handle-indent: calc(var(--table-handle-size) * -1 - 1px);
--table-handle-col-indent: calc(var(--table-handle-size) * -0.5);
--table-handle-row-indent: calc(var(--table-handle-size) * -1 - 0.75rem);
--table-handle-row-indent: calc(var(--table-handle-size) * -1);
--table-insert-marker-indent: calc(-1.25rem - 1px);
--table-selection-z-index: 100;
@@ -423,7 +423,7 @@
editor = new Editor({
extensions: [kit],
element,
editable: false,
editable: !readonly,
editorProps: {
attributes: mergeAttributes(defaultEditorAttributes, editorAttributes, { class: 'flex-grow' })
},
@@ -195,7 +195,7 @@
&__col {
right: calc(var(--table-offscreen-spacing) - 1.5rem);
top: 0;
bottom: 0;
bottom: 1rem;
margin: 1.5rem 0;
.table-button {
@@ -204,7 +204,7 @@
}
&__row {
bottom: -0.25rem;
bottom: 1rem;
left: var(--table-offscreen-spacing);
right: var(--table-offscreen-spacing);
@@ -194,7 +194,24 @@ class ColumnHandler {
const dropMarker = getDropMarker()
const dragMarker = getColDragMarker()
const handleMove = (event: MouseEvent): void => {
if (dropMarker !== null && dragMarker !== null) {
const currentLeft = startLeft + event.clientX - startX
dropIndex = calculateColumnDropIndex(col, columns, currentLeft)
const dragMarkerWidthPx = columns[col].widthPx
const dragMarkerLeftPx = Math.max(0, Math.min(currentLeft, tableWidthPx - dragMarkerWidthPx))
const dropMarkerLeftPx =
dropIndex <= col ? columns[dropIndex].leftPx : columns[dropIndex].leftPx + columns[dropIndex].widthPx
updateColDropMarker(dropMarker, dropMarkerLeftPx - Math.floor(dropMarkerWidthPx / 2) - 1, dropMarkerWidthPx)
updateColDragMarker(dragMarker, dragMarkerLeftPx, dragMarkerWidthPx)
}
}
const handleFinish = (): void => {
window.removeEventListener('mousemove', handleMove)
if (dropMarker !== null) hideDropMarker(dropMarker)
if (dragMarker !== null) hideDragMarker(dragMarker)
@@ -211,21 +228,6 @@ class ColumnHandler {
}
}
const handleMove = (event: MouseEvent): void => {
if (dropMarker !== null && dragMarker !== null) {
const currentLeft = startLeft + event.clientX - startX
dropIndex = calculateColumnDropIndex(col, columns, currentLeft)
const dragMarkerWidthPx = columns[col].widthPx
const dragMarkerLeftPx = Math.max(0, Math.min(currentLeft, tableWidthPx - dragMarkerWidthPx))
const dropMarkerLeftPx =
dropIndex <= col ? columns[dropIndex].leftPx : columns[dropIndex].leftPx + columns[dropIndex].widthPx
updateColDropMarker(dropMarker, dropMarkerLeftPx - Math.floor(dropMarkerWidthPx / 2) - 1, dropMarkerWidthPx)
updateColDragMarker(dragMarker, dragMarkerLeftPx, dragMarkerWidthPx)
}
}
window.addEventListener('mouseup', handleFinish, { once: true })
window.addEventListener('mousemove', handleMove)
})
@@ -194,7 +194,24 @@ class RowHandler {
const dropMarker = getDropMarker()
const dragMarker = getRowDragMarker()
const handleMove = (event: MouseEvent): void => {
if (dropMarker !== null && dragMarker !== null) {
const cursorTop = startTop + event.clientY - startY
dropIndex = calculateRowDropIndex(row, rows, cursorTop)
const dragMarkerHeightPx = rows[row].heightPx
const dragMarkerTopPx = Math.max(0, Math.min(cursorTop, tableHeightPx - dragMarkerHeightPx))
const dropMarkerTopPx =
dropIndex <= row ? rows[dropIndex].topPx : rows[dropIndex].topPx + rows[dropIndex].heightPx
updateRowDropMarker(dropMarker, dropMarkerTopPx - dropMarkerWidthPx / 2, dropMarkerWidthPx)
updateRowDragMarker(dragMarker, dragMarkerTopPx, dragMarkerHeightPx)
}
}
const handleFinish = (): void => {
window.removeEventListener('mousemove', handleMove)
if (dropMarker !== null) hideDropMarker(dropMarker)
if (dragMarker !== null) hideDragMarker(dragMarker)
@@ -211,21 +228,6 @@ class RowHandler {
}
}
const handleMove = (event: MouseEvent): void => {
if (dropMarker !== null && dragMarker !== null) {
const cursorTop = startTop + event.clientY - startY
dropIndex = calculateRowDropIndex(row, rows, cursorTop)
const dragMarkerHeightPx = rows[row].heightPx
const dragMarkerTopPx = Math.max(0, Math.min(cursorTop, tableHeightPx - dragMarkerHeightPx))
const dropMarkerTopPx =
dropIndex <= row ? rows[dropIndex].topPx : rows[dropIndex].topPx + rows[dropIndex].heightPx
updateRowDropMarker(dropMarker, dropMarkerTopPx - dropMarkerWidthPx / 2, dropMarkerWidthPx)
updateRowDragMarker(dragMarker, dragMarkerTopPx, dragMarkerHeightPx)
}
}
window.addEventListener('mouseup', handleFinish, { once: true })
window.addEventListener('mousemove', handleMove)
})
+1
View File
@@ -61,6 +61,7 @@
"@hcengineering/query": "^0.7.17",
"@hcengineering/emoji": "workspace:^0.7.0",
"@hcengineering/theme": "workspace:^0.7.0",
"@hcengineering/workbench": "workspace:^0.7.0",
"@hcengineering/hls": "workspace:^0.7.0",
"fast-equals": "^5.2.2"
}
+9 -2
View File
@@ -19,6 +19,7 @@ import {
copyTextToClipboardOldBrowser,
getClient,
getMarkup,
hasResource,
updateAttribute
} from '@hcengineering/presentation'
import { markupToJSON } from '@hcengineering/text'
@@ -48,6 +49,7 @@ import {
selectionStore
} from './selection'
import { deleteObjects, getObjectId, getObjectLinkFragment, restrictionStore } from './utils'
import workbenchPlugin from '@hcengineering/workbench'
/**
* Action to be used for copying text to clipboard.
@@ -328,8 +330,13 @@ async function OpenInNewTab (
const panelComponent = hierarchy.classHierarchyMixin(d._class, view.mixin.ObjectPanel)
const component = props?.component ?? panelComponent?.component ?? view.component.EditDoc
const loc = await getObjectLinkFragment(hierarchy, d, {}, component)
const url = locationToUrl(loc)
window.open(url, '_blank')
if (hasResource(workbenchPlugin.function.OpenInNewTab) === true) {
const res = await getResource(workbenchPlugin.function.OpenInNewTab)
await res(loc)
} else {
const url = locationToUrl(loc)
window.open(url, '_blank')
}
}
/**
+1 -1
View File
@@ -453,7 +453,7 @@ export async function connect (title: string): Promise<Client | undefined> {
const hasEmail = (si: SocialId): boolean => {
return [SocialIdType.EMAIL, SocialIdType.GOOGLE, SocialIdType.GITHUB].some((type) => type === si.type)
}
const email = me.fullSocialIds.find((si) => hasEmail(si))?.key
const email = me.fullSocialIds.find((si) => hasEmail(si) && si.isDeleted !== true)?.key
const socialId = me.fullSocialIds.find((si) => si._id === me.primarySocialId)?.key
const data: Record<string, any> = {
+3 -2
View File
@@ -25,7 +25,7 @@ import Workbench from './components/Workbench.svelte'
import ServerManager from './components/ServerManager.svelte'
import WorkbenchTabs from './components/WorkbenchTabs.svelte'
import { isAdminUser } from '@hcengineering/presentation'
import { canCloseTab, closeCurrentTab, closeTab, pinTab, unpinTab } from './workbench'
import { canCloseTab, closeCurrentTab, closeTab, OpenInNewTab, pinTab, unpinTab } from './workbench'
import { closeWidget, closeWidgetTab, createWidgetTab, getSidebarObject } from './sidebar'
async function hasArchiveSpaces (spaces: Space[]): Promise<boolean> {
@@ -66,7 +66,8 @@ export default async (): Promise<Resources> => ({
CloseWidget: closeWidget,
GetSidebarObject: getSidebarObject,
LogIn: logIn,
LogOut: logOut
LogOut: logOut,
OpenInNewTab
},
actionImpl: {
Navigate: doNavigate,
@@ -176,6 +176,19 @@ export function getTabLocation (tab: Pick<WorkbenchTab, 'location'>): Location {
return parseLocation(url)
}
export async function OpenInNewTab (loc: Location): Promise<void> {
const client = getClient()
const data = await getTabDataByLocation(loc)
const name = data.name ?? (await translate(data.label, {}, get(languageStore)))
const url = locationToUrl(loc)
await client.createDoc(workbench.class.WorkbenchTab, core.space.Workspace, {
attachedTo: getCurrentAccount().uuid,
location: url,
isPinned: false,
name
})
}
export async function closeTab (tab: WorkbenchTab): Promise<void> {
const tabs = get(tabsStore)
const index = tabs.findIndex((t) => t._id === tab._id)
+3 -2
View File
@@ -17,7 +17,7 @@
import type { Class, Doc, Mixin, Ref, Space } from '@hcengineering/core'
import type { Asset, IntlString, Metadata, Plugin, Resource } from '@hcengineering/platform'
import { plugin } from '@hcengineering/platform'
import { AnyComponent, ComponentExtensionId } from '@hcengineering/ui'
import { AnyComponent, ComponentExtensionId, Location } from '@hcengineering/ui'
import { Action, ViewAction } from '@hcengineering/view'
import type {
@@ -95,7 +95,8 @@ export const workbenchPlugin = plugin(workbenchId, {
CloseWidget: '' as Resource<(widget: Ref<Widget>) => Promise<void>>,
GetSidebarObject: '' as Resource<() => Partial<Pick<Doc, '_id' | '_class'>>>,
LogIn: '' as Resource<(loginInfo: { account: string, token?: string }) => Promise<void>>,
LogOut: '' as Resource<() => Promise<void>>
LogOut: '' as Resource<() => Promise<void>>,
OpenInNewTab: '' as Resource<(loc: Location) => Promise<void>>
},
actionImpl: {
Navigate: '' as ViewAction<{
+2 -1
View File
@@ -222,7 +222,8 @@ async function processEmailNotifications (control: TriggerControl, notifications
const emails = await control.findAll(control.ctx, contact.class.SocialIdentity, {
attachedTo: employee._id,
type: { $in: [SocialIdType.EMAIL, SocialIdType.GOOGLE] },
verifiedOn: { $gt: 0 }
verifiedOn: { $gt: 0 },
isDeleted: { $ne: true }
})
if (emails.length === 0) continue
@@ -18,6 +18,7 @@ import core, { Doc, matchQuery, Ref, Timestamp } from '@hcengineering/core'
import { Execution, parseContext } from '@hcengineering/process'
import { ProcessControl } from '@hcengineering/server-process'
import { getContextValue } from './utils'
import cardPlugin from '@hcengineering/card'
// #region ArrayReduce
@@ -408,8 +409,17 @@ export async function RoleContext (
): Promise<Ref<Employee>[]> {
const targetRole = props.target
if (targetRole === undefined) return []
const users = await control.client.findAll(contact.class.UserRole, { role: targetRole })
return users.map((it) => it.user)
const targetCard =
control.cache.get(execution.card) ?? (await control.client.findOne(cardPlugin.class.Card, { _id: execution.card }))
if (targetCard === undefined) return []
const targetSpace =
control.cache.get(targetCard.space) ?? (await control.client.findOne(core.class.Space, { _id: targetCard.space }))
if (targetSpace === undefined) return []
const mixin = control.client.getHierarchy().as(targetSpace, core.mixin.SpacesTypeData)
const accs = (mixin as any)[targetRole]
if (accs === undefined || accs.length === 0) return []
const users = await control.client.findAll(contact.mixin.Employee, { personUuid: { $in: accs } })
return users.map((it) => it._id)
}
export async function CurrentUser (