mirror of
https://github.com/hcengineering/platform.git
synced 2026-08-17 18:05:42 +02:00
Add ability to copy all data from cards/docs tables (#10505)
Signed-off-by: Artem Savchenko <armisav@gmail.com>
This commit is contained in:
Generated
+31
-1
@@ -19017,6 +19017,12 @@ importers:
|
||||
'@hcengineering/platform':
|
||||
specifier: workspace:^0.7.19
|
||||
version: link:../../foundations/core/packages/platform
|
||||
'@hcengineering/presentation':
|
||||
specifier: workspace:^0.7.0
|
||||
version: link:../../packages/presentation
|
||||
'@hcengineering/ui':
|
||||
specifier: workspace:^0.7.0
|
||||
version: link:../../packages/ui
|
||||
'@hcengineering/view':
|
||||
specifier: workspace:^0.7.0
|
||||
version: link:../view
|
||||
@@ -19090,6 +19096,9 @@ importers:
|
||||
'@hcengineering/view-resources':
|
||||
specifier: workspace:^0.7.0
|
||||
version: link:../view-resources
|
||||
svelte:
|
||||
specifier: ^4.2.20
|
||||
version: 4.2.20
|
||||
devDependencies:
|
||||
'@hcengineering/platform-rig':
|
||||
specifier: workspace:^0.7.19
|
||||
@@ -19118,15 +19127,36 @@ importers:
|
||||
eslint-plugin-promise:
|
||||
specifier: ^6.1.1
|
||||
version: 6.6.0(eslint@8.57.1)
|
||||
eslint-plugin-svelte:
|
||||
specifier: ^2.35.1
|
||||
version: 2.46.1(eslint@8.57.1)(svelte@4.2.20)(ts-node@10.9.2(@swc/core@1.15.1)(@types/node@22.19.0)(typescript@5.9.3))
|
||||
jest:
|
||||
specifier: ^29.7.0
|
||||
version: 29.7.0(@types/node@22.19.0)(ts-node@10.9.2(@swc/core@1.15.1)(@types/node@22.19.0)(typescript@5.9.3))
|
||||
prettier:
|
||||
specifier: ^3.6.2
|
||||
version: 3.6.2
|
||||
prettier-plugin-svelte:
|
||||
specifier: ^3.4.0
|
||||
version: 3.4.0(prettier@3.6.2)(svelte@4.2.20)
|
||||
sass:
|
||||
specifier: ^1.80.0
|
||||
version: 1.93.3
|
||||
svelte-check:
|
||||
specifier: ^3.6.9
|
||||
version: 3.8.6(@babel/core@7.28.5)(postcss-load-config@4.0.2(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.15.1)(@types/node@22.19.0)(typescript@5.9.3)))(postcss@8.5.6)(sass@1.93.3)(svelte@4.2.20)
|
||||
svelte-eslint-parser:
|
||||
specifier: ^0.33.1
|
||||
version: 0.33.1(svelte@4.2.20)
|
||||
svelte-loader:
|
||||
specifier: ^3.2.0
|
||||
version: 3.2.4(svelte@4.2.20)
|
||||
svelte-preprocess:
|
||||
specifier: ^5.1.4
|
||||
version: 5.1.4(@babel/core@7.28.5)(postcss-load-config@4.0.2(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.15.1)(@types/node@22.19.0)(typescript@5.9.3)))(postcss@8.5.6)(sass@1.93.3)(svelte@4.2.20)(typescript@5.9.3)
|
||||
ts-jest:
|
||||
specifier: ^29.1.1
|
||||
version: 29.4.5(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@30.2.0)(babel-jest@29.7.0(@babel/core@7.28.5))(jest-util@30.2.0)(jest@29.7.0)(typescript@5.9.3)
|
||||
version: 29.4.5(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@30.2.0)(babel-jest@29.7.0(@babel/core@7.28.5))(esbuild@0.25.12)(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.0)(ts-node@10.9.2(@swc/core@1.15.1)(@types/node@22.19.0)(typescript@5.9.3)))(typescript@5.9.3)
|
||||
typescript:
|
||||
specifier: ^5.9.3
|
||||
version: 5.9.3
|
||||
|
||||
@@ -728,6 +728,60 @@ export function createModel (builder: Builder): void {
|
||||
card.viewlet.CardRelationshipTable
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
presentation.class.ComponentPointExtension,
|
||||
core.space.Model,
|
||||
{
|
||||
extension: converter.extensions.CopyAsMarkdownAction,
|
||||
component: converter.component.CopyAsMarkdownButton
|
||||
},
|
||||
converter.extensions.CopyAsMarkdownButton
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
view.class.ViewletViewAction,
|
||||
core.space.Model,
|
||||
{
|
||||
descriptor: view.viewlet.RelationshipTable,
|
||||
extension: converter.extensions.CopyAsMarkdownAction,
|
||||
applicableToClass: card.class.Card
|
||||
},
|
||||
card.specialViewAction.CardRelationshipTable
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
view.class.ViewletViewAction,
|
||||
core.space.Model,
|
||||
{
|
||||
descriptor: view.viewlet.Table,
|
||||
extension: converter.extensions.CopyAsMarkdownAction,
|
||||
applicableToClass: card.class.Card
|
||||
},
|
||||
card.specialViewAction.CardTable
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
view.class.ViewletViewAction,
|
||||
core.space.Model,
|
||||
{
|
||||
descriptor: view.viewlet.Table,
|
||||
extension: converter.extensions.CopyAsMarkdownAction,
|
||||
applicableToClass: card.class.Card
|
||||
},
|
||||
card.specialViewAction.CopyAsMarkdownTable
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
view.class.ViewletViewAction,
|
||||
core.space.Model,
|
||||
{
|
||||
descriptor: view.viewlet.RelationshipTable,
|
||||
extension: converter.extensions.CopyAsMarkdownAction,
|
||||
applicableToClass: card.class.Card
|
||||
},
|
||||
card.specialViewAction.CopyAsMarkdownRelationshipTable
|
||||
)
|
||||
|
||||
builder.mixin(card.class.Card, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: card.component.CardPresenter
|
||||
})
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import type { ViewletViewAction, Action, ActionCategory, ViewAction } from '@hcengineering/view'
|
||||
import { type Card, cardId } from '@hcengineering/card'
|
||||
import card from '@hcengineering/card-resources/src/plugin'
|
||||
import type { Client, Doc, Ref } from '@hcengineering/core'
|
||||
@@ -20,7 +21,6 @@ import {} from '@hcengineering/core'
|
||||
import { mergeIds, type Resource } from '@hcengineering/platform'
|
||||
import { type TagCategory } from '@hcengineering/tags'
|
||||
import { type Location, type ResolvedLocation } from '@hcengineering/ui/src/types'
|
||||
import { type Action, type ActionCategory, type ViewAction } from '@hcengineering/view'
|
||||
import { type LocationData } from '@hcengineering/workbench'
|
||||
import { type NotificationGroup, type NotificationType } from '@hcengineering/notification'
|
||||
|
||||
@@ -44,6 +44,12 @@ export default mergeIds(cardId, card, {
|
||||
Card: '' as Ref<ActionCategory>,
|
||||
Labels: '' as Ref<TagCategory>
|
||||
},
|
||||
specialViewAction: {
|
||||
CardRelationshipTable: '' as Ref<ViewletViewAction>,
|
||||
CardTable: '' as Ref<ViewletViewAction>,
|
||||
CopyAsMarkdownTable: '' as Ref<ViewletViewAction>,
|
||||
CopyAsMarkdownRelationshipTable: '' as Ref<ViewletViewAction>
|
||||
},
|
||||
ids: {
|
||||
MasterTags: '' as Ref<Doc>,
|
||||
ManageMasterTags: '' as Ref<Doc>,
|
||||
|
||||
@@ -21,6 +21,7 @@ import documentsPlugin, {
|
||||
DocumentState,
|
||||
type Document,
|
||||
type DocumentSpace,
|
||||
type DocumentTemplate,
|
||||
type ProjectDocument,
|
||||
type ChangeControl,
|
||||
type DocumentRequest
|
||||
@@ -43,7 +44,7 @@ import setting from '@hcengineering/setting'
|
||||
import tags from '@hcengineering/tags'
|
||||
import textEditor from '@hcengineering/text-editor'
|
||||
|
||||
import { AccountRole, type ClassCollaborators, type Class, type Doc, type Ref } from '@hcengineering/core'
|
||||
import { AccountRole, type ClassCollaborators, type Class, type Doc, type Lookup, type Ref } from '@hcengineering/core'
|
||||
import { type Action } from '@hcengineering/view'
|
||||
import { definePermissions } from './permissions'
|
||||
import documents from './plugin'
|
||||
@@ -255,6 +256,11 @@ export function createModel (builder: Builder): void {
|
||||
)
|
||||
|
||||
// Workflow
|
||||
const documentTableLookup: Lookup<Document> = {
|
||||
owner: contact.mixin.Employee,
|
||||
category: documents.class.DocumentCategory,
|
||||
template: documents.mixin.DocumentTemplate
|
||||
}
|
||||
builder.createDoc(
|
||||
view.class.Viewlet,
|
||||
core.space.Model,
|
||||
@@ -308,16 +314,27 @@ export function createModel (builder: Builder): void {
|
||||
'modifiedOn'
|
||||
],
|
||||
options: {
|
||||
lookup: {
|
||||
owner: contact.mixin.Employee,
|
||||
category: documents.class.DocumentCategory,
|
||||
template: documents.mixin.DocumentTemplate
|
||||
}
|
||||
lookup: documentTableLookup
|
||||
}
|
||||
},
|
||||
documents.viewlet.TableDocument
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
view.class.ViewletViewAction,
|
||||
core.space.Model,
|
||||
{
|
||||
descriptor: view.viewlet.Table,
|
||||
extension: converter.extensions.CopyAsMarkdownAction,
|
||||
applicableToClass: documents.class.Document
|
||||
},
|
||||
documents.specialViewAction.TableDocument
|
||||
)
|
||||
|
||||
const documentTemplateTableLookup: Lookup<DocumentTemplate> = {
|
||||
owner: contact.mixin.Employee,
|
||||
category: documents.class.DocumentCategory
|
||||
}
|
||||
builder.createDoc(
|
||||
view.class.Viewlet,
|
||||
core.space.Model,
|
||||
@@ -367,10 +384,7 @@ export function createModel (builder: Builder): void {
|
||||
hiddenKeys: ['attachedTo']
|
||||
},
|
||||
options: {
|
||||
lookup: {
|
||||
owner: contact.mixin.Employee,
|
||||
category: documents.class.DocumentCategory
|
||||
}
|
||||
lookup: documentTemplateTableLookup
|
||||
}
|
||||
},
|
||||
documents.viewlet.TableDocumentTemplate
|
||||
|
||||
@@ -20,7 +20,7 @@ import { type ObjectSearchCategory, type ObjectSearchFactory } from '@hcengineer
|
||||
import { mergeIds, type Resource } from '@hcengineering/platform'
|
||||
import { type TagCategory } from '@hcengineering/tags'
|
||||
import { type AnyComponent } from '@hcengineering/ui/src/types'
|
||||
import { type ActionCategory, type ViewAction } from '@hcengineering/view'
|
||||
import type { ActionCategory, ViewAction, Viewlet, ViewletViewAction } from '@hcengineering/view'
|
||||
import { type NotificationType, type NotificationGroup } from '@hcengineering/notification'
|
||||
import { type TextActionVisibleFunction, type TextActionFunction } from '@hcengineering/text-editor'
|
||||
|
||||
@@ -78,11 +78,14 @@ export default mergeIds(documentsId, documents, {
|
||||
TransferDocument: '' as ViewAction,
|
||||
EditDocSpace: '' as ViewAction
|
||||
},
|
||||
specialViewAction: {
|
||||
TableDocument: '' as Ref<ViewletViewAction>
|
||||
},
|
||||
viewlet: {
|
||||
TableDocument: '' as Ref<Doc>,
|
||||
ListDocument: '' as Ref<Doc>,
|
||||
TableDocumentTemplate: '' as Ref<Doc>,
|
||||
TableDocumentDomain: '' as Ref<Doc>
|
||||
TableDocument: '' as Ref<Viewlet>,
|
||||
ListDocument: '' as Ref<Viewlet>,
|
||||
TableDocumentTemplate: '' as Ref<Viewlet>,
|
||||
TableDocumentDomain: '' as Ref<Viewlet>
|
||||
},
|
||||
notification: {
|
||||
DocumentsNotificationGroup: '' as Ref<NotificationGroup>,
|
||||
|
||||
@@ -323,14 +323,13 @@ export function createModel (builder: Builder): void {
|
||||
}
|
||||
}
|
||||
builder.createDoc(
|
||||
workbench.class.ViewletSpecialViewAction,
|
||||
view.class.ViewletViewAction,
|
||||
core.space.Model,
|
||||
{
|
||||
attachedTo: recruit.viewlet.TableCandidate,
|
||||
attachedToClass: view.class.Viewlet,
|
||||
collection: 'specialViewActions',
|
||||
descriptor: view.viewlet.Table,
|
||||
extension: workbench.extensions.SpecialViewAction,
|
||||
config: tableCandidateExportConfig
|
||||
config: tableCandidateExportConfig,
|
||||
applicableToClass: recruit.mixin.Candidate
|
||||
},
|
||||
recruit.specialViewAction.TableCandidate
|
||||
)
|
||||
|
||||
@@ -23,14 +23,14 @@ import { recruitId } from '@hcengineering/recruit'
|
||||
import recruit from '@hcengineering/recruit-resources/src/plugin'
|
||||
import { type ProjectType, type TaskTypeDescriptor } from '@hcengineering/task'
|
||||
import type { AnyComponent, Location } from '@hcengineering/ui/src/types'
|
||||
import type { ViewletSpecialViewAction } from '@hcengineering/workbench'
|
||||
import type {
|
||||
Action,
|
||||
ActionCategory,
|
||||
ViewAction,
|
||||
ViewActionAvailabilityFunction,
|
||||
ViewQueryAction,
|
||||
Viewlet
|
||||
Viewlet,
|
||||
ViewletViewAction
|
||||
} from '@hcengineering/view'
|
||||
|
||||
export default mergeIds(recruitId, recruit, {
|
||||
@@ -134,7 +134,7 @@ export default mergeIds(recruitId, recruit, {
|
||||
DefaultVacancy: '' as Ref<ProjectType>
|
||||
},
|
||||
specialViewAction: {
|
||||
TableCandidate: '' as Ref<ViewletSpecialViewAction>
|
||||
TableCandidate: '' as Ref<ViewletViewAction>
|
||||
},
|
||||
viewlet: {
|
||||
TableCandidate: '' as Ref<Viewlet>,
|
||||
|
||||
@@ -487,6 +487,17 @@ export function createModel (builder: Builder): void {
|
||||
|
||||
defineViewlets(builder)
|
||||
|
||||
builder.createDoc(
|
||||
view.class.ViewletViewAction,
|
||||
core.space.Model,
|
||||
{
|
||||
descriptor: view.viewlet.List,
|
||||
extension: converter.extensions.CopyAsMarkdownAction,
|
||||
applicableToClass: tracker.class.Issue
|
||||
},
|
||||
tracker.specialViewAction.IssueList
|
||||
)
|
||||
|
||||
const issuesId = 'issues'
|
||||
const componentsId = 'components'
|
||||
const milestonesId = 'milestones'
|
||||
|
||||
@@ -22,7 +22,7 @@ import { type ProjectType } from '@hcengineering/task'
|
||||
import { trackerId } from '@hcengineering/tracker'
|
||||
import tracker from '@hcengineering/tracker-resources/src/plugin'
|
||||
import type { AnyComponent } from '@hcengineering/ui/src/types'
|
||||
import { type Action, type ViewAction, type Viewlet } from '@hcengineering/view'
|
||||
import { type Action, type ViewAction, type Viewlet, type ViewletViewAction } from '@hcengineering/view'
|
||||
import { type Application } from '@hcengineering/workbench'
|
||||
|
||||
export default mergeIds(trackerId, tracker, {
|
||||
@@ -63,6 +63,9 @@ export default mergeIds(trackerId, tracker, {
|
||||
app: {
|
||||
Tracker: '' as Ref<Application>
|
||||
},
|
||||
specialViewAction: {
|
||||
IssueList: '' as Ref<ViewletViewAction>
|
||||
},
|
||||
viewlet: {
|
||||
IssueList: '' as Ref<Viewlet>,
|
||||
IssueTemplateList: '' as Ref<Viewlet>,
|
||||
|
||||
@@ -29,12 +29,17 @@ import {
|
||||
type Ref,
|
||||
type Space
|
||||
} from '@hcengineering/core'
|
||||
import { type Builder, Mixin, Model, UX } from '@hcengineering/model'
|
||||
import { type Builder, Mixin, Model, Prop, TypeRecord, TypeRef, TypeString, UX } from '@hcengineering/model'
|
||||
import core, { TClass, TDoc } from '@hcengineering/model-core'
|
||||
import preference, { TPreference } from '@hcengineering/model-preference'
|
||||
import presentation from '@hcengineering/model-presentation'
|
||||
import { type Asset, type IntlString, type Resource, type Status } from '@hcengineering/platform'
|
||||
import { type AnyComponent, type LabelAndProps, type Location } from '@hcengineering/ui/src/types'
|
||||
import { type Asset, getEmbeddedLabel, type IntlString, type Resource, type Status } from '@hcengineering/platform'
|
||||
import {
|
||||
type AnyComponent,
|
||||
type ComponentExtensionId,
|
||||
type LabelAndProps,
|
||||
type Location
|
||||
} from '@hcengineering/ui/src/types'
|
||||
import {
|
||||
type TypeEditor,
|
||||
type Action,
|
||||
@@ -99,7 +104,8 @@ import {
|
||||
type ViewOptionsModel,
|
||||
type Viewlet,
|
||||
type ViewletDescriptor,
|
||||
type ViewletPreference
|
||||
type ViewletPreference,
|
||||
type ViewletViewAction
|
||||
} from '@hcengineering/view'
|
||||
|
||||
import view from './plugin'
|
||||
@@ -318,6 +324,28 @@ export class TViewletDescriptor extends TDoc implements ViewletDescriptor {
|
||||
label!: IntlString
|
||||
}
|
||||
|
||||
@Model(view.class.ViewletViewAction, core.class.Doc, DOMAIN_MODEL)
|
||||
@UX(view.string.ViewletViewAction)
|
||||
export class TViewletViewAction extends TDoc implements ViewletViewAction {
|
||||
@Prop(TypeRef(view.class.Viewlet), getEmbeddedLabel('Viewlet'))
|
||||
declare viewlet?: Ref<Viewlet>
|
||||
|
||||
@Prop(TypeRef(view.class.ViewletDescriptor), getEmbeddedLabel('Descriptor'))
|
||||
declare descriptor?: Ref<ViewletDescriptor>
|
||||
|
||||
@Prop(TypeString(), getEmbeddedLabel('Extension'))
|
||||
declare extension: ComponentExtensionId
|
||||
|
||||
@Prop(TypeRecord(), getEmbeddedLabel('Config'))
|
||||
declare config?: Record<string, any>
|
||||
|
||||
@Prop(TypeRef(core.class.Class), getEmbeddedLabel('ApplicableToClass'))
|
||||
declare applicableToClass?: Ref<Class<Doc>>
|
||||
|
||||
@Prop(TypeRef(core.class.Class), getEmbeddedLabel('DisabledForClass'))
|
||||
declare disabledForClass?: Ref<Class<Doc>>
|
||||
}
|
||||
|
||||
@Model(view.class.Viewlet, core.class.Doc, DOMAIN_MODEL)
|
||||
export class TViewlet extends TDoc implements Viewlet {
|
||||
attachTo!: Ref<Class<Doc>>
|
||||
@@ -472,6 +500,7 @@ export function createModel (builder: Builder): void {
|
||||
TViewletPreference,
|
||||
TViewletDescriptor,
|
||||
TViewlet,
|
||||
TViewletViewAction,
|
||||
TAction,
|
||||
TActionCategory,
|
||||
TObjectValidator,
|
||||
|
||||
@@ -14,26 +14,25 @@
|
||||
//
|
||||
|
||||
import { AccountRole, type AccountUuid, type Class, DOMAIN_MODEL, type Ref, type Space } from '@hcengineering/core'
|
||||
import { type Builder, Mixin, Model, Prop, TypeRecord, TypeRef, TypeString, UX } from '@hcengineering/model'
|
||||
import { type Builder, Mixin, Model, Prop, TypeRef, UX } from '@hcengineering/model'
|
||||
import preference, { TPreference } from '@hcengineering/model-preference'
|
||||
import { createAction } from '@hcengineering/model-view'
|
||||
import core, { TAttachedDoc, TClass, TDoc } from '@hcengineering/model-core'
|
||||
import core, { TClass, TDoc } from '@hcengineering/model-core'
|
||||
import { type Asset, getEmbeddedLabel, type IntlString, type Resource } from '@hcengineering/platform'
|
||||
import view, { type KeyBinding, type Viewlet } from '@hcengineering/view'
|
||||
import view, { type KeyBinding } from '@hcengineering/view'
|
||||
import type {
|
||||
Application,
|
||||
ApplicationNavModel,
|
||||
HiddenApplication,
|
||||
SpaceView,
|
||||
ViewConfiguration,
|
||||
ViewletSpecialViewAction,
|
||||
Widget,
|
||||
WidgetPreference,
|
||||
WidgetTab,
|
||||
WidgetType,
|
||||
WorkbenchTab
|
||||
} from '@hcengineering/workbench'
|
||||
import { type AnyComponent, type ComponentExtensionId } from '@hcengineering/ui/src/types'
|
||||
import { type AnyComponent } from '@hcengineering/ui/src/types'
|
||||
import presentation from '@hcengineering/model-presentation'
|
||||
|
||||
import workbench from './plugin'
|
||||
@@ -106,29 +105,10 @@ export class TWorkbenchTab extends TPreference implements WorkbenchTab {
|
||||
isPinned!: boolean
|
||||
}
|
||||
|
||||
@Model(workbench.class.ViewletSpecialViewAction, core.class.AttachedDoc, DOMAIN_MODEL)
|
||||
@UX(workbench.string.ViewletSpecialViewAction)
|
||||
export class TViewletSpecialViewAction extends TAttachedDoc implements ViewletSpecialViewAction {
|
||||
@Prop(TypeRef(view.class.Viewlet), core.string.AttachedTo)
|
||||
declare attachedTo: Ref<Viewlet>
|
||||
|
||||
@Prop(TypeRef(view.class.Viewlet), core.string.AttachedToClass)
|
||||
declare attachedToClass: Ref<Class<Viewlet>>
|
||||
|
||||
override collection: 'specialViewActions' = 'specialViewActions'
|
||||
|
||||
@Prop(TypeString(), getEmbeddedLabel('Extension'))
|
||||
declare extension: ComponentExtensionId
|
||||
|
||||
@Prop(TypeRecord(), getEmbeddedLabel('Config'))
|
||||
declare config?: Record<string, any>
|
||||
}
|
||||
|
||||
export function createModel (builder: Builder): void {
|
||||
builder.createModel(
|
||||
TApplication,
|
||||
TSpaceView,
|
||||
TViewletSpecialViewAction,
|
||||
THiddenApplication,
|
||||
TApplicationNavModel,
|
||||
TWidget,
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@hcengineering/platform-rig/profiles/default/eslint.config.json'],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: './tsconfig.json'
|
||||
}
|
||||
extends: ['./node_modules/@hcengineering/platform-rig/profiles/ui/eslint.config.json'],
|
||||
parserOptions: { tsconfigRootDir: __dirname }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"trailingComma": "none",
|
||||
"tabWidth": 2,
|
||||
"semi": false,
|
||||
"singleQuote": true,
|
||||
"printWidth": 120,
|
||||
"useTabs": false,
|
||||
"bracketSpacing": true,
|
||||
"proseWrap": "preserve",
|
||||
"plugins": [
|
||||
"prettier-plugin-svelte"
|
||||
],
|
||||
"overrides": [
|
||||
{
|
||||
"files": "*.svelte",
|
||||
"options": {
|
||||
"parser": "svelte"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,21 +1,18 @@
|
||||
{
|
||||
"name": "@hcengineering/converter-resources",
|
||||
"version": "0.7.0",
|
||||
"main": "lib/index.js",
|
||||
"main": "src/index.ts",
|
||||
"svelte": "src/index.ts",
|
||||
"types": "types/index.d.ts",
|
||||
"files": [
|
||||
"lib/**/*",
|
||||
"types/**/*",
|
||||
"tsconfig.json"
|
||||
],
|
||||
"author": "Copyright © Hardcore Engineering Inc.",
|
||||
"license": "EPL-2.0",
|
||||
"scripts": {
|
||||
"build": "compile",
|
||||
"build": "compile ui",
|
||||
"format": "format src",
|
||||
"build:watch": "compile",
|
||||
"_phase:build": "compile transpile src",
|
||||
"svelte-check": "do-svelte-check",
|
||||
"_phase:svelte-check": "do-svelte-check",
|
||||
"build:watch": "compile ui",
|
||||
"_phase:build": "compile ui",
|
||||
"_phase:format": "format src",
|
||||
"_phase:validate": "compile validate",
|
||||
"_phase:test": "jest --passWithNoTests --silent",
|
||||
@@ -33,8 +30,15 @@
|
||||
"typescript": "^5.9.3",
|
||||
"eslint": "^8.54.0",
|
||||
"jest": "^29.7.0",
|
||||
"svelte-check": "^3.6.9",
|
||||
"svelte-eslint-parser": "^0.33.1",
|
||||
"svelte-loader": "^3.2.0",
|
||||
"svelte-preprocess": "^5.1.4",
|
||||
"ts-jest": "^29.1.1",
|
||||
"@types/jest": "^29.5.5"
|
||||
"@types/jest": "^29.5.5",
|
||||
"sass": "^1.80.0",
|
||||
"eslint-plugin-svelte": "^2.35.1",
|
||||
"prettier-plugin-svelte": "^3.4.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hcengineering/platform": "workspace:^0.7.19",
|
||||
@@ -45,6 +49,7 @@
|
||||
"@hcengineering/ui": "workspace:^0.7.0",
|
||||
"@hcengineering/theme": "workspace:^0.7.0",
|
||||
"@hcengineering/contact": "workspace:^0.7.0",
|
||||
"@hcengineering/view-resources": "workspace:^0.7.0"
|
||||
"@hcengineering/view-resources": "workspace:^0.7.0",
|
||||
"svelte": "^4.2.20"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
plugins: [
|
||||
require('autoprefixer')
|
||||
]
|
||||
}
|
||||
@@ -13,7 +13,8 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { buildMarkdownTableFromDocs, copyAsMarkdownTable, isIntlString } from '../index'
|
||||
import { buildMarkdownTableFromDocs, copyAsMarkdownTable } from '../markdown'
|
||||
import { isIntlString } from '../formatter'
|
||||
import type { Doc, Class, Ref } from '@hcengineering/core'
|
||||
import type { CopyAsMarkdownTableProps } from '../types'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
<!--
|
||||
// Copyright © 2026 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use it except in compliance with the License. You may obtain
|
||||
// a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { Class, Doc, DocumentQuery, Ref } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Button, IconCopy } from '@hcengineering/ui'
|
||||
import view from '@hcengineering/view'
|
||||
import { viewletContextStore } from '@hcengineering/view-resources'
|
||||
import { copyAsMarkdownTableFromResource, copyRelationshipTableAsMarkdown } from '../markdown/copyActions'
|
||||
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let query: DocumentQuery<Doc> = {}
|
||||
export let config: Array<string | import('@hcengineering/view').BuildModelKey> = []
|
||||
|
||||
$: ctx = $viewletContextStore?.getLastContext()
|
||||
$: relationshipTableData = ctx?.relationshipTableData
|
||||
$: viewlet = ctx?.viewlet
|
||||
|
||||
$: hasData = relationshipTableData !== undefined || (_class !== undefined && query !== undefined)
|
||||
|
||||
async function handleClick (e: MouseEvent): Promise<void> {
|
||||
if (relationshipTableData !== undefined) {
|
||||
await copyRelationshipTableAsMarkdown(e, relationshipTableData)
|
||||
} else if (_class !== undefined && query !== undefined) {
|
||||
const client = getClient()
|
||||
const docs = await client.findAll(_class, query, { limit: 1000 })
|
||||
await copyAsMarkdownTableFromResource(e, {
|
||||
cardClass: _class,
|
||||
viewlet,
|
||||
config,
|
||||
query,
|
||||
docs
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if hasData}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<Button icon={IconCopy} label={view.string.CopyAll} on:click={handleClick} />
|
||||
{/if}
|
||||
@@ -47,10 +47,10 @@ function expandRowsForRoot (
|
||||
root: Doc,
|
||||
associationAttrs: AttributeModel[],
|
||||
pathPerAttr: string[][]
|
||||
): Array<{ docsByLevel: (Doc | undefined)[] }> {
|
||||
const rows: Array<{ docsByLevel: (Doc | undefined)[] }> = []
|
||||
): Array<{ docsByLevel: Array<Doc | undefined> }> {
|
||||
const rows: Array<{ docsByLevel: Array<Doc | undefined> }> = []
|
||||
|
||||
function expand (parent: Doc | undefined, level: number, docsSoFar: (Doc | undefined)[]): void {
|
||||
function expand (parent: Doc | undefined, level: number, docsSoFar: Array<Doc | undefined>): void {
|
||||
if (level >= associationAttrs.length) {
|
||||
rows.push({ docsByLevel: docsSoFar })
|
||||
return
|
||||
@@ -78,7 +78,7 @@ function expandRowsForRoot (
|
||||
/**
|
||||
* Compute rowSpan for a given level at each row index: how many consecutive rows share the same doc at this level.
|
||||
*/
|
||||
function computeRowSpans (rows: Array<{ docsByLevel: (Doc | undefined)[] }>, level: number): number[] {
|
||||
function computeRowSpans (rows: Array<{ docsByLevel: Array<Doc | undefined> }>, level: number): number[] {
|
||||
const spans: number[] = []
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
const doc = rows[i].docsByLevel[level]
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
buildMarkdownTableFromDocs as buildMarkdownTableFromDocsImpl
|
||||
} from './markdown'
|
||||
import { copyAsMarkdownTableAction } from './actionImpl'
|
||||
import CopyAsMarkdownButton from './components/CopyAsMarkdownButton.svelte'
|
||||
|
||||
export * from './formatter'
|
||||
export * from './model'
|
||||
@@ -50,5 +51,8 @@ export default async (): Promise<Resources> => ({
|
||||
},
|
||||
actionImpl: {
|
||||
CopyAsMarkdownTable: copyAsMarkdownTableAction
|
||||
},
|
||||
component: {
|
||||
CopyAsMarkdownButton
|
||||
}
|
||||
})
|
||||
|
||||
@@ -15,7 +15,13 @@
|
||||
|
||||
import type { Class, Client, Doc, Hierarchy, Ref, PersonId } from '@hcengineering/core'
|
||||
import { getCurrentLanguage } from '@hcengineering/theme'
|
||||
import type { AttributeModel, BuildMarkdownTableMetadata, TableMetadata, Viewlet } from '@hcengineering/view'
|
||||
import type {
|
||||
AttributeModel,
|
||||
BuildMarkdownTableMetadata,
|
||||
TableMetadata,
|
||||
Viewlet,
|
||||
BuildModelKey
|
||||
} from '@hcengineering/view'
|
||||
import viewPlugin from '@hcengineering/view'
|
||||
import { buildConfigLookup, buildModel, getAttributeValue } from '@hcengineering/view-resources'
|
||||
import type { CopyAsMarkdownTableProps, CopyRelationshipTableAsMarkdownProps } from '../types'
|
||||
@@ -133,7 +139,7 @@ export async function buildMarkdownTableFromDocs (
|
||||
const model = await buildModel({
|
||||
client,
|
||||
_class: props.cardClass,
|
||||
keys: actualConfig.filter((key: string | import('@hcengineering/view').BuildModelKey) => {
|
||||
keys: actualConfig.filter((key: string | BuildModelKey) => {
|
||||
if (typeof key === 'string') {
|
||||
return !hiddenKeys.includes(key)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
const sveltePreprocess = require('svelte-preprocess')
|
||||
|
||||
module.exports = {
|
||||
preprocess: sveltePreprocess({
|
||||
scss: {
|
||||
// This is expected as svelte-preprocess hasn't fully migrated to the modern API yet
|
||||
silenceDeprecations: ['legacy-js-api']
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -1,11 +1,10 @@
|
||||
{
|
||||
"extends": "./node_modules/@hcengineering/platform-rig/profiles/default/tsconfig.json",
|
||||
"extends": "./node_modules/@hcengineering/platform-rig/profiles/ui/tsconfig.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib",
|
||||
"declarationDir": "./types",
|
||||
"tsBuildInfoFile": ".build/build.tsbuildinfo"
|
||||
"declarationDir": "./types"
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "lib", "dist", "types", "bundle"]
|
||||
|
||||
@@ -39,6 +39,8 @@
|
||||
"dependencies": {
|
||||
"@hcengineering/platform": "workspace:^0.7.19",
|
||||
"@hcengineering/core": "workspace:^0.7.24",
|
||||
"@hcengineering/view": "workspace:^0.7.0"
|
||||
"@hcengineering/view": "workspace:^0.7.0",
|
||||
"@hcengineering/presentation": "workspace:^0.7.0",
|
||||
"@hcengineering/ui": "workspace:^0.7.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
|
||||
import type { Client, Doc, Mixin, Ref } from '@hcengineering/core'
|
||||
import { type Plugin, type Resource, plugin } from '@hcengineering/platform'
|
||||
import type { AnyComponent, ComponentExtensionId } from '@hcengineering/ui'
|
||||
import type { ComponentPointExtension } from '@hcengineering/presentation'
|
||||
import type { Action, BuildMarkdownTableMetadata, ViewAction } from '@hcengineering/view'
|
||||
import type {
|
||||
CopyAsMarkdownTableFunction,
|
||||
@@ -39,6 +41,13 @@ export const converterPlugin = plugin(converterId, {
|
||||
(docs: Doc[], props: CopyAsMarkdownTableProps, client: Client) => Promise<string>
|
||||
>
|
||||
},
|
||||
extensions: {
|
||||
CopyAsMarkdownAction: '' as ComponentExtensionId,
|
||||
CopyAsMarkdownButton: '' as Ref<ComponentPointExtension>
|
||||
},
|
||||
component: {
|
||||
CopyAsMarkdownButton: '' as AnyComponent
|
||||
},
|
||||
action: {
|
||||
CopyAsMarkdownTable: '' as Ref<Action<Doc, Record<string, any>>>
|
||||
},
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
{viewlets}
|
||||
{label}
|
||||
{space}
|
||||
{resultQuery}
|
||||
{modeSelectorProps}
|
||||
>
|
||||
<svelte:fragment slot="header-tools">
|
||||
|
||||
@@ -113,6 +113,7 @@
|
||||
"Or": "Nebo",
|
||||
"HyperlinkPlaceholder": "https://jappleseed.com",
|
||||
"CopyToClipboard": "Kopírovat do schránky",
|
||||
"CopyAll": "Kopírovat vše",
|
||||
"CopyDocumentMarkdown": "Kopírovat jako Markdown",
|
||||
"CopyAsMarkdownTable": "Kopírovat jako Markdown tabulka",
|
||||
"TableCopiedToClipboard": "Tabulka zkopírována do schránky",
|
||||
|
||||
@@ -113,6 +113,7 @@
|
||||
"Or": "Oder",
|
||||
"HyperlinkPlaceholder": "https://jappleseed.com",
|
||||
"CopyToClipboard": "In die Zwischenablage kopieren",
|
||||
"CopyAll": "Alle kopieren",
|
||||
"CopyDocumentMarkdown": "Als Markdown kopieren",
|
||||
"CopyAsMarkdownTable": "Als Markdown-Tabelle kopieren",
|
||||
"TableCopiedToClipboard": "Tabelle in die Zwischenablage kopiert",
|
||||
|
||||
@@ -113,6 +113,7 @@
|
||||
"Or": "Or",
|
||||
"HyperlinkPlaceholder": "https://jappleseed.com",
|
||||
"CopyToClipboard": "Copy to clipboard",
|
||||
"CopyAll": "Copy All",
|
||||
"CopyDocumentMarkdown": "Copy as Markdown",
|
||||
"CopyAsMarkdownTable": "Copy as Markdown Table",
|
||||
"TableCopiedToClipboard": "Table copied to clipboard",
|
||||
|
||||
@@ -109,6 +109,7 @@
|
||||
"Or": "O",
|
||||
"HyperlinkPlaceholder": "https://jappleseed.com",
|
||||
"CopyToClipboard": "Copiar al portapapeles",
|
||||
"CopyAll": "Copiar todo",
|
||||
"CopyDocumentMarkdown": "Copiar como Markdown",
|
||||
"CopyAsMarkdownTable": "Copiar como tabla Markdown",
|
||||
"TableCopiedToClipboard": "Tabla copiada al portapapeles",
|
||||
|
||||
@@ -109,6 +109,7 @@
|
||||
"Or": "Ou",
|
||||
"HyperlinkPlaceholder": "https://jappleseed.com",
|
||||
"CopyToClipboard": "Copier dans le presse-papiers",
|
||||
"CopyAll": "Tout copier",
|
||||
"CopyDocumentMarkdown": "Copier en Markdown",
|
||||
"CopyAsMarkdownTable": "Copier en tableau Markdown",
|
||||
"TableCopiedToClipboard": "Tableau copié dans le presse-papiers",
|
||||
|
||||
@@ -109,6 +109,7 @@
|
||||
"Or": "O",
|
||||
"HyperlinkPlaceholder": "https://jappleseed.com",
|
||||
"CopyToClipboard": "Copia negli appunti",
|
||||
"CopyAll": "Copia tutto",
|
||||
"CopyDocumentMarkdown": "Copia come Markdown",
|
||||
"CopyAsMarkdownTable": "Copia come tabella Markdown",
|
||||
"TableCopiedToClipboard": "Tabella copiata negli appunti",
|
||||
|
||||
@@ -109,6 +109,7 @@
|
||||
"Or": "または",
|
||||
"HyperlinkPlaceholder": "https://jappleseed.com",
|
||||
"CopyToClipboard": "クリップボードにコピー",
|
||||
"CopyAll": "すべてコピー",
|
||||
"CopyDocumentMarkdown": "Markdownとしてコピー",
|
||||
"CopyAsMarkdownTable": "Markdownテーブルとしてコピー",
|
||||
"TableCopiedToClipboard": "テーブルをクリップボードにコピーしました",
|
||||
|
||||
@@ -109,6 +109,7 @@
|
||||
"Or": "Ou",
|
||||
"HyperlinkPlaceholder": "https://jappleseed.com",
|
||||
"CopyToClipboard": "Copiar para área de transferência",
|
||||
"CopyAll": "Copiar tudo",
|
||||
"CopyDocumentMarkdown": "Copiar como Markdown",
|
||||
"CopyAsMarkdownTable": "Copiar como tabela Markdown",
|
||||
"TableCopiedToClipboard": "Tabela copiada para área de transferência",
|
||||
|
||||
@@ -109,6 +109,7 @@
|
||||
"Or": "Ou",
|
||||
"HyperlinkPlaceholder": "https://jappleseed.com",
|
||||
"CopyToClipboard": "Copiar para área de transferência",
|
||||
"CopyAll": "Copiar tudo",
|
||||
"CopyDocumentMarkdown": "Copiar como Markdown",
|
||||
"CopyAsMarkdownTable": "Copiar como tabela Markdown",
|
||||
"TableCopiedToClipboard": "Tabela copiada para área de transferência",
|
||||
|
||||
@@ -109,6 +109,7 @@
|
||||
"Or": "Или",
|
||||
"HyperlinkPlaceholder": "https://jappleseed.com",
|
||||
"CopyToClipboard": "Скопировать в буфер обмена",
|
||||
"CopyAll": "Копировать всё",
|
||||
"CopyDocumentMarkdown": "Скопировать как Markdown",
|
||||
"CopyAsMarkdownTable": "Скопировать как таблицу Markdown",
|
||||
"TableCopiedToClipboard": "Таблица скопирована в буфер обмена",
|
||||
|
||||
@@ -109,6 +109,7 @@
|
||||
"Or": "Veya",
|
||||
"HyperlinkPlaceholder": "https://ornek.com",
|
||||
"CopyToClipboard": "Panoya kopyala",
|
||||
"CopyAll": "Tümünü kopyala",
|
||||
"CopyDocumentMarkdown": "Markdown olarak kopyala",
|
||||
"CopyAsMarkdownTable": "Markdown tablosu olarak kopyala",
|
||||
"TableCopiedToClipboard": "Tablo panoya kopyalandı",
|
||||
|
||||
@@ -113,6 +113,7 @@
|
||||
"Or": "或",
|
||||
"HyperlinkPlaceholder": "https://jappleseed.com",
|
||||
"CopyToClipboard": "复制到剪贴板",
|
||||
"CopyAll": "复制全部",
|
||||
"CopyDocumentMarkdown": "复制为 Markdown",
|
||||
"CopyAsMarkdownTable": "复制为 Markdown 表格",
|
||||
"TableCopiedToClipboard": "表格已复制到剪贴板",
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
import { createQuery, getClient, reduceCalls, updateAttribute } from '@hcengineering/presentation'
|
||||
import ui, {
|
||||
Button,
|
||||
IconCopy,
|
||||
Label,
|
||||
Loading,
|
||||
eventToHTMLElement,
|
||||
@@ -44,14 +43,14 @@
|
||||
} from '@hcengineering/ui'
|
||||
import { AttributeModel, BuildModelKey, BuildModelOptions, ViewOptionModel, ViewOptions } from '@hcengineering/view'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
import { createEventDispatcher, onMount } from 'svelte'
|
||||
import { createEventDispatcher, onDestroy, onMount } from 'svelte'
|
||||
import { Readable } from 'svelte/store'
|
||||
import { showMenu } from '../actions'
|
||||
import { canChangeAttribute } from '../permissions'
|
||||
import view from '../plugin'
|
||||
import { ViewletContextStore, viewletContextStore } from '../viewletContextStore'
|
||||
import { buildConfigAssociation, buildConfigLookup, buildModel, getAttributeValue, restrictionStore } from '../utils'
|
||||
import { getResultOptions, getResultQuery } from '../viewOptions'
|
||||
import converter from '@hcengineering/converter'
|
||||
import IconUpDown from './icons/UpDown.svelte'
|
||||
import RelationsSelectorPopup from './RelationsSelectorPopup.svelte'
|
||||
|
||||
@@ -539,16 +538,38 @@
|
||||
)
|
||||
}
|
||||
|
||||
async function handleCopyAsMarkdown (e: MouseEvent): Promise<void> {
|
||||
if (model === undefined || viewModel.length === 0) return
|
||||
const copyFn = await getResource(converter.function.CopyRelationshipAsMarkdown)
|
||||
await copyFn(e, {
|
||||
viewModel,
|
||||
model,
|
||||
objects,
|
||||
cardClass: _class
|
||||
$: relationshipTableData =
|
||||
model !== undefined && viewModel.length > 0 ? { viewModel, model, objects, cardClass: _class } : undefined
|
||||
|
||||
$: {
|
||||
viewletContextStore.update((cur) => {
|
||||
const contexts = cur.contexts
|
||||
const last = contexts[contexts.length - 1]
|
||||
if (last === undefined) return cur
|
||||
const updated =
|
||||
relationshipTableData !== undefined
|
||||
? { ...last, relationshipTableData }
|
||||
: (() => {
|
||||
const rest = { ...last }
|
||||
delete rest.relationshipTableData
|
||||
return rest
|
||||
})()
|
||||
return new ViewletContextStore([...contexts.slice(0, -1), updated])
|
||||
})
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
viewletContextStore.update((cur) => {
|
||||
const contexts = cur.contexts
|
||||
const last = contexts[contexts.length - 1]
|
||||
if (last?.relationshipTableData !== undefined) {
|
||||
const rest = { ...last }
|
||||
delete rest.relationshipTableData
|
||||
return new ViewletContextStore([...contexts.slice(0, -1), rest])
|
||||
}
|
||||
return cur
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if !model || isBuildingModel}
|
||||
@@ -677,19 +698,6 @@
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if objects.length > 0 && viewModel.length > 0 && model !== undefined}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div class="px-1">
|
||||
<Button
|
||||
icon={IconCopy}
|
||||
label={view.string.CopyToClipboard}
|
||||
kind={'ghost'}
|
||||
size={'small'}
|
||||
on:click={handleCopyAsMarkdown}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
import { Class, Doc, DocumentQuery, Ref, Space, WithLookup } from '@hcengineering/core'
|
||||
import { IModeSelector, ModeSelector, SearchInput, Header, Breadcrumb, HeaderAdaptive } from '@hcengineering/ui'
|
||||
import type { Asset } from '@hcengineering/platform'
|
||||
import { Viewlet } from '@hcengineering/view'
|
||||
import { ComponentExtensions, getClient } from '@hcengineering/presentation'
|
||||
import view, { Viewlet, type ViewletViewAction } from '@hcengineering/view'
|
||||
import { getViewletSpecialActions } from '../viewletUtils'
|
||||
import ViewletSelector from './ViewletSelector.svelte'
|
||||
import FilterButton from './filter/FilterButton.svelte'
|
||||
|
||||
@@ -17,8 +19,11 @@
|
||||
export let showLabelSelector = false
|
||||
export let modeSelectorProps: IModeSelector | undefined = undefined
|
||||
export let adaptive: HeaderAdaptive = 'doubleRow'
|
||||
export let resultQuery: DocumentQuery<Doc> = {}
|
||||
|
||||
let scroller: HTMLElement
|
||||
|
||||
$: viewletActions = viewlet != null ? getViewletSpecialActions(getClient(), viewlet) : []
|
||||
</script>
|
||||
|
||||
<Header
|
||||
@@ -48,6 +53,16 @@
|
||||
<FilterButton {_class} {space} />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="actions">
|
||||
{#each viewletActions as action (action._id)}
|
||||
<ComponentExtensions
|
||||
extension={action.extension}
|
||||
props={{
|
||||
_class,
|
||||
query: resultQuery,
|
||||
config: action.config ?? {}
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
<slot name="actions" />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="extra">
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
SearchInput,
|
||||
themeStore
|
||||
} from '@hcengineering/ui'
|
||||
import { ViewOptions, Viewlet, ViewletPreference } from '@hcengineering/view'
|
||||
import view, { ViewOptions, Viewlet, ViewletPreference, type ViewletViewAction } from '@hcengineering/view'
|
||||
import { ComponentExtensions, getClient } from '@hcengineering/presentation'
|
||||
import { getViewletSpecialActions } from '../viewletUtils'
|
||||
|
||||
import FilterBar from './filter/FilterBar.svelte'
|
||||
import FilterButton from './filter/FilterButton.svelte'
|
||||
@@ -51,6 +53,8 @@
|
||||
$: searchQuery = search === '' ? { ...query } : { ...query, $search: search }
|
||||
|
||||
$: hideExtra = modeSelectorProps === undefined && $$slots.extra === undefined
|
||||
|
||||
$: viewletActions = viewlet != null ? getViewletSpecialActions(getClient(), viewlet) : []
|
||||
</script>
|
||||
|
||||
<Header {adaptive} {hideActions} {hideExtra} overflowExtra>
|
||||
@@ -67,6 +71,16 @@
|
||||
<FilterButton {_class} />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="actions">
|
||||
{#each viewletActions as action (action._id)}
|
||||
<ComponentExtensions
|
||||
extension={action.extension}
|
||||
props={{
|
||||
_class,
|
||||
query: resultQuery,
|
||||
config: action.config ?? {}
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
<slot name="actions" />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="extra">
|
||||
|
||||
@@ -217,6 +217,7 @@ export {
|
||||
} from './utils'
|
||||
export * from './viewOptions'
|
||||
export * from './viewletContextStore'
|
||||
export { getViewletSpecialActions } from './viewletUtils'
|
||||
export { copyMarkdown } from './actionImpl'
|
||||
export { default as SimpleNotification } from './components/SimpleNotification.svelte'
|
||||
export type { BuildMarkdownTableMetadata } from '@hcengineering/view'
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { type Class, type Doc, type DocumentQuery, type Ref } from '@hcengineering/core'
|
||||
import { type BuildModelKey, type Viewlet, type ViewOptions } from '@hcengineering/view'
|
||||
import { writable } from 'svelte/store'
|
||||
import type { CopyRelationshipTableAsMarkdownProps } from '@hcengineering/converter'
|
||||
|
||||
/**
|
||||
* Context for the current viewlet table being displayed
|
||||
@@ -14,6 +15,7 @@ export interface ViewletContext {
|
||||
query?: DocumentQuery<Doc>
|
||||
viewOptions?: ViewOptions
|
||||
_class?: Ref<Class<Doc>>
|
||||
relationshipTableData?: CopyRelationshipTableAsMarkdownProps
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
//
|
||||
// Copyright © 2026 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.
|
||||
//
|
||||
|
||||
import type { Client, Ref } from '@hcengineering/core'
|
||||
import view, { type Viewlet, type ViewletViewAction } from '@hcengineering/view'
|
||||
|
||||
/** Shape used by the header UI; both viewlet- and descriptor-scoped actions provide this. */
|
||||
export interface ResolvedViewletSpecialAction {
|
||||
_id: Ref<ViewletViewAction>
|
||||
extension: ViewletViewAction['extension']
|
||||
config?: Record<string, any>
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve special view actions for a viewlet by:
|
||||
* 1. One query: ViewletViewAction where viewlet is in (current + template viewlets) OR descriptor matches.
|
||||
* 2. For rows with descriptor set, filter by applicableToClass and disabledForClass.
|
||||
* Actions are deduplicated by extension (viewlet-scoped take precedence).
|
||||
*/
|
||||
export function getViewletSpecialActions (client: Client, viewlet: Viewlet): ResolvedViewletSpecialAction[] {
|
||||
const model = client.getModel()
|
||||
const hierarchy = client.getHierarchy()
|
||||
|
||||
let actions = model.findAllSync(view.class.ViewletViewAction, {
|
||||
viewlet: viewlet._id
|
||||
}) as ViewletViewAction[]
|
||||
if (actions == null || actions.length === 0) {
|
||||
actions = model.findAllSync(view.class.ViewletViewAction, {
|
||||
descriptor: viewlet.descriptor
|
||||
}) as ViewletViewAction[]
|
||||
}
|
||||
|
||||
if (actions == null || actions.length === 0) {
|
||||
return []
|
||||
}
|
||||
|
||||
const filtered = actions.filter((a) => {
|
||||
if (a.descriptor != null) {
|
||||
if (a.applicableToClass != null && !hierarchy.isDerived(viewlet.attachTo, a.applicableToClass)) {
|
||||
return false
|
||||
}
|
||||
if (a.disabledForClass != null && hierarchy.isDerived(viewlet.attachTo, a.disabledForClass)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
const seen = new Set<string>()
|
||||
const result: ResolvedViewletSpecialAction[] = []
|
||||
const byViewlet = filtered.filter((a) => a.viewlet != null)
|
||||
const byDescriptor = filtered.filter((a) => a.descriptor != null)
|
||||
for (const a of [...byViewlet, ...byDescriptor]) {
|
||||
if (seen.has(a.extension)) continue
|
||||
seen.add(a.extension)
|
||||
result.push({ _id: a._id, extension: a.extension, config: a.config })
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -66,7 +66,8 @@ import {
|
||||
ViewAction,
|
||||
Viewlet,
|
||||
ViewletDescriptor,
|
||||
ViewletPreference
|
||||
ViewletPreference,
|
||||
ViewletViewAction
|
||||
} from './types'
|
||||
|
||||
export * from './types'
|
||||
@@ -126,6 +127,7 @@ const view = plugin(viewId, {
|
||||
ViewletPreference: '' as Ref<Class<ViewletPreference>>,
|
||||
ViewletDescriptor: '' as Ref<Class<ViewletDescriptor>>,
|
||||
Viewlet: '' as Ref<Class<Viewlet>>,
|
||||
ViewletViewAction: '' as Ref<Class<ViewletViewAction>>,
|
||||
Action: '' as Ref<Class<Action>>,
|
||||
ActionCategory: '' as Ref<Class<ActionCategory>>,
|
||||
LinkPresenter: '' as Ref<Class<LinkPresenter>>,
|
||||
@@ -224,8 +226,10 @@ const view = plugin(viewId, {
|
||||
Subscribed: '' as IntlString,
|
||||
HyperlinkPlaceholder: '' as IntlString,
|
||||
CopyToClipboard: '' as IntlString,
|
||||
CopyAll: '' as IntlString,
|
||||
NoGrouping: '' as IntlString,
|
||||
Type: '' as IntlString,
|
||||
ViewletViewAction: '' as IntlString,
|
||||
UnArchive: '' as IntlString,
|
||||
Archive: '' as IntlString,
|
||||
Save: '' as IntlString,
|
||||
|
||||
@@ -46,6 +46,7 @@ import { Preference } from '@hcengineering/preference'
|
||||
import {
|
||||
AnyComponent,
|
||||
AnySvelteComponent,
|
||||
type ComponentExtensionId,
|
||||
Location,
|
||||
Location as PlatformLocation,
|
||||
type LabelAndProps
|
||||
@@ -458,6 +459,24 @@ export interface ViewletConfigOptions {
|
||||
sortable?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Special view action shown in the viewlet header. Exactly one of viewlet or descriptor must be set.
|
||||
* When descriptor is set, scope is filtered by applicableToClass / disabledForClass.
|
||||
* @public
|
||||
*/
|
||||
export interface ViewletViewAction extends Doc {
|
||||
/** When set, action applies to this viewlet (and template viewlets). */
|
||||
viewlet?: Ref<Viewlet>
|
||||
/** When set, action applies to viewlets with this descriptor; use applicableToClass / disabledForClass to scope. */
|
||||
descriptor?: Ref<ViewletDescriptor>
|
||||
extension: ComponentExtensionId
|
||||
config?: Record<string, any>
|
||||
/** When descriptor is set: show only when viewlet.attachTo is this class or a subclass. */
|
||||
applicableToClass?: Ref<Class<Doc>>
|
||||
/** When descriptor is set: hide when viewlet.attachTo is this class or a subclass. */
|
||||
disabledForClass?: Ref<Class<Doc>>
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
|
||||
@@ -36,10 +36,11 @@
|
||||
FilterButton,
|
||||
getResultOptions,
|
||||
getResultQuery,
|
||||
getViewletSpecialActions,
|
||||
ViewletSelector,
|
||||
ViewletSettingButton
|
||||
} from '@hcengineering/view-resources'
|
||||
import workbench, { type ParentsNavigationModel, type ViewletSpecialViewAction } from '@hcengineering/workbench'
|
||||
import workbench, { type ParentsNavigationModel } from '@hcengineering/workbench'
|
||||
import ComponentNavigator from './ComponentNavigator.svelte'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
import { ComponentType } from 'svelte'
|
||||
@@ -94,12 +95,7 @@
|
||||
$: void updateQuery(_baseQuery, viewOptions, viewlet, queryBuilder)
|
||||
$: void updateOptions(viewlet?.options, viewOptions, viewlet)
|
||||
|
||||
$: viewletActions =
|
||||
viewlet != null
|
||||
? (client.getModel().findAllSync(workbench.class.ViewletSpecialViewAction, {
|
||||
attachedTo: viewlet._id
|
||||
}) as ViewletSpecialViewAction[])
|
||||
: []
|
||||
$: viewletActions = viewlet != null ? getViewletSpecialActions(client, viewlet) : []
|
||||
|
||||
async function updateOptions (
|
||||
_options: FindOptions<Doc> | undefined,
|
||||
@@ -141,7 +137,9 @@
|
||||
|
||||
<Header
|
||||
adaptive={modes !== undefined ? 'doubleRow' : filterVisible ? 'freezeActions' : 'disabled'}
|
||||
hideActions={!(createLabel && createComponent) && createButton === undefined}
|
||||
hideActions={!(createLabel && createComponent) &&
|
||||
createButton === undefined &&
|
||||
(viewletActions == null || viewletActions.length === 0)}
|
||||
hideExtra={modes === undefined}
|
||||
freezeBefore
|
||||
>
|
||||
|
||||
@@ -17,7 +17,6 @@ import { workbenchId, workbenchPlugin } from './plugin'
|
||||
|
||||
export * from './analytics'
|
||||
export * from './types'
|
||||
export type { ViewletSpecialViewAction } from './types'
|
||||
export * from './utils'
|
||||
|
||||
export { workbenchId }
|
||||
|
||||
@@ -25,7 +25,6 @@ import type {
|
||||
ApplicationNavModel,
|
||||
HiddenApplication,
|
||||
SpaceView,
|
||||
ViewletSpecialViewAction,
|
||||
Widget,
|
||||
WidgetPreference,
|
||||
WidgetTab,
|
||||
@@ -43,8 +42,7 @@ export const workbenchPlugin = plugin(workbenchId, {
|
||||
HiddenApplication: '' as Ref<Class<HiddenApplication>>,
|
||||
Widget: '' as Ref<Class<Widget>>,
|
||||
WidgetPreference: '' as Ref<Class<WidgetPreference>>,
|
||||
WorkbenchTab: '' as Ref<Class<WorkbenchTab>>,
|
||||
ViewletSpecialViewAction: '' as Ref<Class<ViewletSpecialViewAction>>
|
||||
WorkbenchTab: '' as Ref<Class<WorkbenchTab>>
|
||||
},
|
||||
mixin: {
|
||||
SpaceView: '' as Ref<Mixin<SpaceView>>
|
||||
@@ -61,7 +59,6 @@ export const workbenchPlugin = plugin(workbenchId, {
|
||||
string: {
|
||||
Archive: '' as IntlString,
|
||||
View: '' as IntlString,
|
||||
ViewletSpecialViewAction: '' as IntlString,
|
||||
ServerUnderMaintenance: '' as IntlString,
|
||||
UpgradeDownloadProgress: '' as IntlString,
|
||||
OpenInSidebar: '' as IntlString,
|
||||
|
||||
@@ -14,28 +14,11 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import type {
|
||||
AccountRole,
|
||||
AccountUuid,
|
||||
AttachedDoc,
|
||||
Class,
|
||||
Doc,
|
||||
DocumentQuery,
|
||||
Obj,
|
||||
Ref,
|
||||
Space
|
||||
} from '@hcengineering/core'
|
||||
import type { Viewlet } from '@hcengineering/view'
|
||||
import type { AccountRole, AccountUuid, Class, Doc, DocumentQuery, Obj, Ref, Space } from '@hcengineering/core'
|
||||
import { DocNotifyContext, InboxNotification } from '@hcengineering/notification'
|
||||
import type { Asset, IntlString, Resource } from '@hcengineering/platform'
|
||||
import type { Preference } from '@hcengineering/preference'
|
||||
import {
|
||||
AnyComponent,
|
||||
type AnySvelteComponent,
|
||||
type ComponentExtensionId,
|
||||
Location,
|
||||
ResolvedLocation
|
||||
} from '@hcengineering/ui'
|
||||
import { AnyComponent, type AnySvelteComponent, Location, ResolvedLocation } from '@hcengineering/ui'
|
||||
|
||||
/** @public */
|
||||
export interface LocationData {
|
||||
@@ -229,9 +212,3 @@ export interface ViewConfiguration {
|
||||
export interface SpaceView extends Class<Obj> {
|
||||
view: ViewConfiguration
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface ViewletSpecialViewAction extends AttachedDoc<Viewlet, 'specialViewActions'> {
|
||||
extension: ComponentExtensionId
|
||||
config?: Record<string, any>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user