mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-22 11:01:54 +03:00
Make possible to archive spaces with states (#688)
Signed-off-by: Ilya Sumbatyants <ilya.sumb@gmail.com>
This commit is contained in:
parent
3fac9ab8e2
commit
9a24af1788
@ -36,6 +36,7 @@ describe('client', () => {
|
||||
name: 'xxx',
|
||||
description: 'desc',
|
||||
private: false,
|
||||
archived: false,
|
||||
members: []
|
||||
})
|
||||
const txes = await client.findAll(core.class.Space, { name: 'xxx' })
|
||||
|
@ -75,6 +75,7 @@ export async function generateContacts (transactorUrl: string, dbName: string, o
|
||||
location: faker.address.city(),
|
||||
company: faker.company.companyName(),
|
||||
members: accountIds,
|
||||
archived: false,
|
||||
private: false
|
||||
}
|
||||
const vacancyId = (options.random ? `vacancy-${generateId()}-${i}` : `vacancy-genid-${i}`) as Ref<Vacancy>
|
||||
|
@ -16,6 +16,7 @@
|
||||
import { MigrateOperation } from '@anticrm/model'
|
||||
|
||||
// Import migrate operations.
|
||||
import { coreOperation } from '@anticrm/model-core'
|
||||
import { taskOperation } from '@anticrm/model-task'
|
||||
import { attachmentOperation } from '@anticrm/model-attachment'
|
||||
import { leadOperation } from '@anticrm/model-lead'
|
||||
@ -23,6 +24,7 @@ import { recruitOperation } from '@anticrm/model-recruit'
|
||||
import { viewOperation } from '@anticrm/model-view'
|
||||
|
||||
export const migrateOperations: MigrateOperation[] = [
|
||||
coreOperation,
|
||||
taskOperation,
|
||||
attachmentOperation,
|
||||
leadOperation,
|
||||
|
@ -95,12 +95,14 @@ export function createModel (builder: Builder): void {
|
||||
name: 'general',
|
||||
description: 'General Channel',
|
||||
private: false,
|
||||
archived: false,
|
||||
members: []
|
||||
}, chunter.space.General)
|
||||
builder.createDoc(chunter.class.Channel, core.space.Model, {
|
||||
name: 'random',
|
||||
description: 'Random Talks',
|
||||
private: false,
|
||||
archived: false,
|
||||
members: []
|
||||
}, chunter.space.Random)
|
||||
|
||||
|
@ -251,6 +251,7 @@ export function createModel (builder: Builder): void {
|
||||
name: 'Employees',
|
||||
description: 'Employees',
|
||||
private: false,
|
||||
archived: false,
|
||||
members: []
|
||||
},
|
||||
contact.space.Employee
|
||||
|
@ -23,6 +23,7 @@ export * from './core'
|
||||
export * from './security'
|
||||
export * from './tx'
|
||||
export { core as default }
|
||||
export { coreOperation } from './migration'
|
||||
|
||||
export function createModel (builder: Builder): void {
|
||||
builder.createModel(
|
||||
|
36
models/core/src/migration.ts
Normal file
36
models/core/src/migration.ts
Normal file
@ -0,0 +1,36 @@
|
||||
//
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
import { TxOperations } from '@anticrm/core'
|
||||
import {
|
||||
MigrateOperation,
|
||||
MigrationClient,
|
||||
MigrationUpgradeClient
|
||||
} from '@anticrm/model'
|
||||
|
||||
import core from './component'
|
||||
|
||||
export const coreOperation: MigrateOperation = {
|
||||
async migrate (client: MigrationClient): Promise<void> {},
|
||||
async upgrade (client: MigrationUpgradeClient): Promise<void> {
|
||||
const ops = new TxOperations(client, core.account.System)
|
||||
const targetSpaces = (await client.findAll(core.class.Space, {}))
|
||||
.filter((space) => space.archived == null)
|
||||
|
||||
await Promise.all(targetSpaces.map(
|
||||
(space) => ops.updateDoc(space._class, space.space, space._id, { archived: false })
|
||||
)).catch((e) => console.error(e))
|
||||
}
|
||||
}
|
@ -33,6 +33,9 @@ export class TSpace extends TDoc implements Space {
|
||||
@Prop(TypeBoolean(), 'Private' as IntlString)
|
||||
private!: boolean
|
||||
|
||||
@Prop(TypeBoolean(), 'Archived' as IntlString)
|
||||
archived!: boolean
|
||||
|
||||
members!: Arr<Ref<Account>>
|
||||
}
|
||||
|
||||
|
@ -91,6 +91,7 @@ export function createModel (builder: Builder): void {
|
||||
name: 'Funnel',
|
||||
description: 'Default funnel',
|
||||
private: false,
|
||||
archived: false,
|
||||
members: []
|
||||
},
|
||||
lead.space.DefaultFunnel
|
||||
@ -157,6 +158,7 @@ export function createModel (builder: Builder): void {
|
||||
description: 'Manage funnel statuses',
|
||||
members: [],
|
||||
private: false,
|
||||
archived: false,
|
||||
icon: lead.component.TemplatesIcon
|
||||
},
|
||||
lead.space.FunnelTemplates
|
||||
|
@ -141,7 +141,8 @@ export function createModel (builder: Builder): void {
|
||||
name: 'public',
|
||||
description: 'Public Candidates',
|
||||
private: false,
|
||||
members: []
|
||||
members: [],
|
||||
archived: false
|
||||
},
|
||||
recruit.space.CandidatesPublic
|
||||
)
|
||||
@ -262,6 +263,7 @@ export function createModel (builder: Builder): void {
|
||||
description: 'Manage vacancy statuses',
|
||||
members: [],
|
||||
private: false,
|
||||
archived: false,
|
||||
icon: recruit.component.TemplatesIcon
|
||||
},
|
||||
recruit.space.VacancyTemplates
|
||||
|
@ -33,6 +33,7 @@
|
||||
"@anticrm/platform": "~0.6.5",
|
||||
"@anticrm/model-core": "~0.6.0",
|
||||
"@anticrm/model-view": "~0.6.0",
|
||||
"@anticrm/model-workbench": "~0.6.1"
|
||||
"@anticrm/model-workbench": "~0.6.1",
|
||||
"@anticrm/task": "~0.6.0"
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ import core, { TDoc } from '@anticrm/model-core'
|
||||
import setting from '@anticrm/setting'
|
||||
import type { Integration, IntegrationType, Handler } from '@anticrm/setting'
|
||||
import type { IntlString } from '@anticrm/platform'
|
||||
import task from '@anticrm/task'
|
||||
|
||||
import workbench from '@anticrm/model-workbench'
|
||||
import { AnyComponent } from '@anticrm/ui'
|
||||
@ -64,7 +65,7 @@ export function createModel (builder: Builder): void {
|
||||
{
|
||||
id: 'statuses',
|
||||
label: setting.string.ManageStatuses,
|
||||
icon: setting.icon.Statuses,
|
||||
icon: task.icon.ManageStatuses,
|
||||
component: setting.component.ManageStatuses
|
||||
},
|
||||
{
|
||||
|
@ -323,6 +323,7 @@ export function createModel (builder: Builder): void {
|
||||
name: 'public',
|
||||
description: 'Public tasks',
|
||||
private: false,
|
||||
archived: false,
|
||||
members: []
|
||||
},
|
||||
task.space.TasksPublic
|
||||
@ -336,6 +337,7 @@ export function createModel (builder: Builder): void {
|
||||
description: 'Manage project statuses',
|
||||
members: [],
|
||||
private: false,
|
||||
archived: false,
|
||||
icon: task.component.TemplatesIcon
|
||||
},
|
||||
task.space.ProjectTemplates
|
||||
@ -368,9 +370,50 @@ export function createModel (builder: Builder): void {
|
||||
task.action.EditStatuses
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
view.class.Action,
|
||||
core.space.Model,
|
||||
{
|
||||
label: 'Archive' as IntlString,
|
||||
icon: view.icon.Archive,
|
||||
action: task.actionImpl.ArchiveSpace
|
||||
},
|
||||
task.action.ArchiveSpace
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
view.class.Action,
|
||||
core.space.Model,
|
||||
{
|
||||
label: 'Unarchive' as IntlString,
|
||||
icon: view.icon.Archive,
|
||||
action: task.actionImpl.UnarchiveSpace
|
||||
},
|
||||
task.action.UnarchiveSpace
|
||||
)
|
||||
|
||||
builder.createDoc(view.class.ActionTarget, core.space.Model, {
|
||||
target: task.class.SpaceWithStates,
|
||||
action: task.action.EditStatuses
|
||||
action: task.action.EditStatuses,
|
||||
query: {
|
||||
archived: false
|
||||
}
|
||||
})
|
||||
|
||||
builder.createDoc(view.class.ActionTarget, core.space.Model, {
|
||||
target: task.class.SpaceWithStates,
|
||||
action: task.action.ArchiveSpace,
|
||||
query: {
|
||||
archived: false
|
||||
}
|
||||
})
|
||||
|
||||
builder.createDoc(view.class.ActionTarget, core.space.Model, {
|
||||
target: task.class.SpaceWithStates,
|
||||
action: task.action.UnarchiveSpace,
|
||||
query: {
|
||||
archived: true
|
||||
}
|
||||
})
|
||||
|
||||
builder.mixin(task.class.State, core.class.Class, view.mixin.AttributeEditor, {
|
||||
@ -399,7 +442,8 @@ export function createModel (builder: Builder): void {
|
||||
name: 'Sequences',
|
||||
description: 'Internal space to store sequence numbers',
|
||||
members: [],
|
||||
private: false
|
||||
private: false,
|
||||
archived: false
|
||||
},
|
||||
task.space.Sequence
|
||||
)
|
||||
|
@ -30,13 +30,17 @@ export default mergeIds(taskId, task, {
|
||||
CreateTask: '' as Ref<Action>,
|
||||
EditStatuses: '' as Ref<Action>,
|
||||
TodoItemMarkDone: '' as Ref<Action>,
|
||||
TodoItemMarkUnDone: '' as Ref<Action>
|
||||
TodoItemMarkUnDone: '' as Ref<Action>,
|
||||
ArchiveSpace: '' as Ref<Action>,
|
||||
UnarchiveSpace: '' as Ref<Action>
|
||||
},
|
||||
actionImpl: {
|
||||
CreateTask: '' as Resource<(object: Doc) => Promise<void>>,
|
||||
EditStatuses: '' as Resource<(object: Doc) => Promise<void>>,
|
||||
TodoItemMarkDone: '' as Resource<(object: Doc) => Promise<void>>,
|
||||
TodoItemMarkUnDone: '' as Resource<(object: Doc) => Promise<void>>
|
||||
TodoItemMarkUnDone: '' as Resource<(object: Doc) => Promise<void>>,
|
||||
ArchiveSpace: '' as Resource<(object: Doc) => Promise<void>>,
|
||||
UnarchiveSpace: '' as Resource<(object: Doc) => Promise<void>>
|
||||
},
|
||||
component: {
|
||||
ProjectView: '' as AnyComponent,
|
||||
|
@ -87,6 +87,7 @@ export function createModel (builder: Builder): void {
|
||||
name: 'Telegram',
|
||||
description: 'Space for all telegram messages',
|
||||
private: false,
|
||||
archived: false,
|
||||
members: []
|
||||
},
|
||||
telegram.space.Telegram
|
||||
|
@ -31,12 +31,13 @@ describe('client', () => {
|
||||
private: false,
|
||||
name: 'NewSpace',
|
||||
description: '',
|
||||
archived: false,
|
||||
members: []
|
||||
})
|
||||
const result2 = await client.findAll(klass, {})
|
||||
expect(result2).toHaveLength(3)
|
||||
|
||||
await client.createDoc(klass, core.space.Model, { private: false, name: 'NewSpace', description: '', members: [] })
|
||||
await client.createDoc(klass, core.space.Model, { private: false, name: 'NewSpace', description: '', members: [], archived: false })
|
||||
const result3 = await client.findAll(klass, {})
|
||||
expect(result3).toHaveLength(4)
|
||||
})
|
||||
|
@ -65,12 +65,13 @@ describe('memdb', () => {
|
||||
private: false,
|
||||
name: 'NewSpace',
|
||||
description: '',
|
||||
members: []
|
||||
members: [],
|
||||
archived: false
|
||||
})
|
||||
const result2 = await client.findAll(core.class.Space, {})
|
||||
expect(result2).toHaveLength(3)
|
||||
|
||||
await client.createDoc(core.class.Space, core.space.Model, { private: false, name: 'NewSpace', description: '', members: [] })
|
||||
await client.createDoc(core.class.Space, core.space.Model, { private: false, name: 'NewSpace', description: '', members: [], archived: false })
|
||||
const result3 = await client.findAll(core.class.Space, {})
|
||||
expect(result3).toHaveLength(4)
|
||||
})
|
||||
@ -180,7 +181,8 @@ describe('memdb', () => {
|
||||
name: 'name',
|
||||
description: 'desc',
|
||||
private: false,
|
||||
members: []
|
||||
members: [],
|
||||
archived: false
|
||||
})
|
||||
const account = await model.createDoc(core.class.Account, core.space.Model, { email: 'email' })
|
||||
await model.updateDoc(core.class.Space, core.space.Model, space, { $push: { members: account } })
|
||||
|
@ -107,7 +107,8 @@ export function genMinModel (): TxCUD<Doc>[] {
|
||||
name: 'Sp1',
|
||||
description: '',
|
||||
private: false,
|
||||
members: []
|
||||
members: [],
|
||||
archived: false
|
||||
})
|
||||
)
|
||||
|
||||
@ -116,7 +117,8 @@ export function genMinModel (): TxCUD<Doc>[] {
|
||||
name: 'Sp2',
|
||||
description: '',
|
||||
private: false,
|
||||
members: []
|
||||
members: [],
|
||||
archived: false
|
||||
})
|
||||
)
|
||||
return txes
|
||||
|
@ -211,6 +211,7 @@ export interface Space extends Doc {
|
||||
description: string
|
||||
private: boolean
|
||||
members: Arr<Ref<Account>>
|
||||
archived: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -97,7 +97,8 @@ export function genMinModel (): TxCUD<Doc>[] {
|
||||
name: 'Sp1',
|
||||
description: '',
|
||||
private: false,
|
||||
members: [u1, u2]
|
||||
members: [u1, u2],
|
||||
archived: false
|
||||
})
|
||||
)
|
||||
|
||||
@ -106,7 +107,8 @@ export function genMinModel (): TxCUD<Doc>[] {
|
||||
name: 'Sp2',
|
||||
description: '',
|
||||
private: false,
|
||||
members: [u1]
|
||||
members: [u1],
|
||||
archived: false
|
||||
})
|
||||
)
|
||||
return txes
|
||||
|
@ -97,6 +97,7 @@ describe('query', () => {
|
||||
name: '#0',
|
||||
description: '',
|
||||
members: [],
|
||||
archived: false,
|
||||
x: 0
|
||||
})
|
||||
await factory.createDoc<Channel>(core.class.Space, core.space.Model, {
|
||||
@ -104,6 +105,7 @@ describe('query', () => {
|
||||
name: '#1',
|
||||
description: '',
|
||||
members: [],
|
||||
archived: false,
|
||||
x: 1
|
||||
})
|
||||
await factory.createDoc<Channel>(core.class.Space, core.space.Model, {
|
||||
@ -111,6 +113,7 @@ describe('query', () => {
|
||||
name: '#2',
|
||||
description: '',
|
||||
members: [],
|
||||
archived: false,
|
||||
x: 2
|
||||
})
|
||||
await factory.createDoc<Channel>(core.class.Space, core.space.Model, {
|
||||
@ -118,6 +121,7 @@ describe('query', () => {
|
||||
name: '#3',
|
||||
description: '',
|
||||
members: [],
|
||||
archived: false,
|
||||
x: 3
|
||||
})
|
||||
await pp
|
||||
@ -144,18 +148,21 @@ describe('query', () => {
|
||||
private: false,
|
||||
name: '#1',
|
||||
description: '',
|
||||
archived: false,
|
||||
members: []
|
||||
})
|
||||
await factory.createDoc(core.class.Space, core.space.Model, {
|
||||
private: false,
|
||||
name: '#2',
|
||||
description: '',
|
||||
archived: false,
|
||||
members: []
|
||||
})
|
||||
await factory.createDoc(core.class.Space, core.space.Model, {
|
||||
private: false,
|
||||
name: '#3',
|
||||
description: '',
|
||||
archived: false,
|
||||
members: []
|
||||
})
|
||||
})
|
||||
@ -180,6 +187,7 @@ describe('query', () => {
|
||||
private: false,
|
||||
name: '#1',
|
||||
description: '',
|
||||
archived: false,
|
||||
members: []
|
||||
})
|
||||
await factory.createDoc<Channel>(core.class.Space, core.space.Model, {
|
||||
@ -187,6 +195,7 @@ describe('query', () => {
|
||||
private: false,
|
||||
name: '#2',
|
||||
description: '',
|
||||
archived: false,
|
||||
members: []
|
||||
})
|
||||
await factory.createDoc<Channel>(core.class.Space, core.space.Model, {
|
||||
@ -194,6 +203,7 @@ describe('query', () => {
|
||||
private: false,
|
||||
name: '#3',
|
||||
description: '',
|
||||
archived: false,
|
||||
members: []
|
||||
})
|
||||
await pp
|
||||
@ -244,6 +254,7 @@ describe('query', () => {
|
||||
private: true,
|
||||
name: i.toString(),
|
||||
description: '',
|
||||
archived: false,
|
||||
members: []
|
||||
})
|
||||
}
|
||||
|
@ -229,7 +229,7 @@ export class LiveQuery extends TxProcessor implements Client {
|
||||
|
||||
// Check if query is partially matched.
|
||||
private matchQuery (q: Query, tx: TxUpdateDoc<Doc>): boolean {
|
||||
if (!this.client.getHierarchy().isDerived(q._class, tx.objectClass)) {
|
||||
if (!this.client.getHierarchy().isDerived(tx.objectClass, q._class)) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -23,8 +23,9 @@
|
||||
export let actions: {
|
||||
label: IntlString
|
||||
icon?: Asset | AnySvelteComponent
|
||||
action: () => void | Promise<void>
|
||||
action: (ctx?: any) => void | Promise<void>
|
||||
}[] = []
|
||||
export let ctx: any = undefined
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
</script>
|
||||
@ -33,7 +34,7 @@
|
||||
{#each actions as action}
|
||||
<div class="flex-row-center menu-item" on:click={() => {
|
||||
dispatch('close')
|
||||
action.action()
|
||||
action.action(ctx)
|
||||
}}>
|
||||
{#if action.icon}
|
||||
<div class="scale-75">
|
||||
|
@ -36,6 +36,7 @@
|
||||
name,
|
||||
description,
|
||||
private: false,
|
||||
archived: false,
|
||||
members: [getCurrentAccount()._id]
|
||||
})
|
||||
}
|
||||
|
@ -36,6 +36,7 @@
|
||||
name,
|
||||
description,
|
||||
private: false,
|
||||
archived: false,
|
||||
members: [getCurrentAccount()._id]
|
||||
})
|
||||
}
|
||||
|
@ -36,6 +36,7 @@
|
||||
name,
|
||||
description,
|
||||
private: false,
|
||||
archived: false,
|
||||
members: [getCurrentAccount()._id]
|
||||
})
|
||||
}
|
||||
|
@ -46,6 +46,7 @@
|
||||
name,
|
||||
description,
|
||||
private: false,
|
||||
archived: false,
|
||||
members: []
|
||||
}
|
||||
)
|
||||
|
@ -38,6 +38,7 @@
|
||||
name,
|
||||
description,
|
||||
private: false,
|
||||
archived: false,
|
||||
members: []
|
||||
})
|
||||
}
|
||||
|
@ -48,6 +48,7 @@
|
||||
name,
|
||||
description,
|
||||
private: false,
|
||||
archived: false,
|
||||
members: []
|
||||
})
|
||||
|
||||
|
@ -19,8 +19,4 @@
|
||||
<path d="M4,7.7h0.8c0.3,0,0.6-0.3,0.6-0.6S5.1,6.5,4.8,6.5H4c-0.3,0-0.6,0.3-0.6,0.6S3.6,7.7,4,7.7z"/>
|
||||
<path d="M6.4,9.7H4c-0.3,0-0.6,0.3-0.6,0.6s0.3,0.6,0.6,0.6h2.4c0.3,0,0.6-0.3,0.6-0.6S6.7,9.7,6.4,9.7z"/>
|
||||
</symbol>
|
||||
<symbol id="statuses" viewBox="0 0 16 16">
|
||||
<path d="M13.3,8.3c-0.1,2.8-2.5,5.1-5.4,5.1C5,13.4,2.6,11,2.6,8c0-2.9,2.3-5.2,5.1-5.4c0.1-0.4,0.2-0.7,0.4-1c0,0-0.1,0-0.1,0 C4.4,1.7,1.6,4.5,1.6,8c0,3.5,2.9,6.4,6.4,6.4s6.4-2.9,6.4-6.4c0,0,0-0.1,0-0.1C14,8.1,13.7,8.2,13.3,8.3z"/>
|
||||
<ellipse cx="12.1" cy="3.9" rx="2.5" ry="2.5"/>
|
||||
</symbol>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 2.9 KiB |
@ -20,7 +20,6 @@ const icons = require('../assets/icons.svg')
|
||||
loadMetadata(setting.icon, {
|
||||
Setting: `${icons}#settings`,
|
||||
Integrations: `${icons}#integration`,
|
||||
Statuses: `${icons}#statuses`,
|
||||
Support: `${icons}#support`,
|
||||
Privacy: `${icons}#privacy`,
|
||||
Terms: `${icons}#terms`
|
||||
|
@ -21,6 +21,7 @@
|
||||
import type { KanbanTemplate, KanbanTemplateSpace, StateTemplate } from '@anticrm/task'
|
||||
import { KanbanTemplateEditor } from '@anticrm/task-resources'
|
||||
import setting from '@anticrm/setting'
|
||||
import task from '@anticrm/task'
|
||||
|
||||
import Folders from './Folders.svelte'
|
||||
import Templates from './Templates.svelte'
|
||||
@ -53,7 +54,7 @@
|
||||
|
||||
<div class="flex-col h-full">
|
||||
<div class="flex-row-center header">
|
||||
<div class="content-color mr-3"><Icon icon={setting.icon.Statuses} size={'medium'} /></div>
|
||||
<div class="content-color mr-3"><Icon icon={task.icon.ManageStatuses} size={'medium'} /></div>
|
||||
<div class="fs-title"><Label label={setting.string.ManageStatuses}/></div>
|
||||
</div>
|
||||
<div class="flex-row-top h-full overflow-x-auto scroll-m-10">
|
||||
|
@ -82,7 +82,6 @@ export default plugin(settingId, {
|
||||
icon: {
|
||||
Setting: '' as Asset,
|
||||
Integrations: '' as Asset,
|
||||
Statuses: '' as Asset,
|
||||
Support: '' as Asset,
|
||||
Privacy: '' as Asset,
|
||||
Terms: '' as Asset
|
||||
|
@ -19,4 +19,8 @@
|
||||
<symbol id='todo-uncheck' viewBox="0 0 16 16">
|
||||
<circle cx="8" cy="8" r="6" stroke="white" fill="none"/>
|
||||
</symbol>
|
||||
<symbol id="manage-statuses" viewBox="0 0 16 16">
|
||||
<path d="M13.3,8.3c-0.1,2.8-2.5,5.1-5.4,5.1C5,13.4,2.6,11,2.6,8c0-2.9,2.3-5.2,5.1-5.4c0.1-0.4,0.2-0.7,0.4-1c0,0-0.1,0-0.1,0 C4.4,1.7,1.6,4.5,1.6,8c0,3.5,2.9,6.4,6.4,6.4s6.4-2.9,6.4-6.4c0,0,0-0.1,0-0.1C14,8.1,13.7,8.2,13.3,8.3z"/>
|
||||
<ellipse cx="12.1" cy="3.9" rx="2.5" ry="2.5"/>
|
||||
</symbol>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.9 KiB |
@ -22,7 +22,8 @@ loadMetadata(task.icon, {
|
||||
Kanban: `${icons}#kanban`,
|
||||
Status: `${icons}#status`,
|
||||
TodoCheck: `${icons}#todo-check`,
|
||||
TodoUnCheck: `${icons}#todo-uncheck`
|
||||
TodoUnCheck: `${icons}#todo-uncheck`,
|
||||
ManageStatuses: `${icons}#manage-statuses`
|
||||
})
|
||||
|
||||
addStringsLoader(taskId, async (lang: string) => await import(`../lang/${lang}.json`))
|
||||
|
@ -45,6 +45,7 @@
|
||||
name,
|
||||
description,
|
||||
private: false,
|
||||
archived: false,
|
||||
members: []
|
||||
}
|
||||
)
|
||||
|
@ -24,7 +24,7 @@ import TemplatesIcon from './components/TemplatesIcon.svelte'
|
||||
import EditIssue from './components/EditIssue.svelte'
|
||||
import { Doc } from '@anticrm/core'
|
||||
import { showPopup } from '@anticrm/ui'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import { getClient, MessageBox } from '@anticrm/presentation'
|
||||
|
||||
import KanbanView from './components/kanban/KanbanView.svelte'
|
||||
import StateEditor from './components/state/StateEditor.svelte'
|
||||
@ -63,6 +63,48 @@ async function toggleDone (value: boolean, object: TodoItem): Promise<void> {
|
||||
)
|
||||
}
|
||||
|
||||
async function ArchiveSpace (object: SpaceWithStates): Promise<void> {
|
||||
showPopup(
|
||||
MessageBox,
|
||||
{
|
||||
label: 'Archive',
|
||||
message: `Do you want to archive ${object.name}?`
|
||||
},
|
||||
undefined,
|
||||
(result: boolean) => {
|
||||
if (result) {
|
||||
const client = getClient()
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
client.updateDoc(object._class, object.space, object._id, {
|
||||
archived: true
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
async function UnarchiveSpace (object: SpaceWithStates): Promise<void> {
|
||||
showPopup(
|
||||
MessageBox,
|
||||
{
|
||||
label: 'Unarchive',
|
||||
message: `Do you want to unarchive ${object.name}?`
|
||||
},
|
||||
undefined,
|
||||
(result: boolean) => {
|
||||
if (result) {
|
||||
const client = getClient()
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
client.updateDoc(object._class, object.space, object._id, {
|
||||
archived: false
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export default async (): Promise<Resources> => ({
|
||||
component: {
|
||||
CreateTask,
|
||||
@ -83,6 +125,8 @@ export default async (): Promise<Resources> => ({
|
||||
CreateTask: createTask,
|
||||
EditStatuses: editStatuses,
|
||||
TodoItemMarkDone: async (obj: TodoItem) => await toggleDone(true, obj),
|
||||
TodoItemMarkUnDone: async (obj: TodoItem) => await toggleDone(false, obj)
|
||||
TodoItemMarkUnDone: async (obj: TodoItem) => await toggleDone(false, obj),
|
||||
ArchiveSpace,
|
||||
UnarchiveSpace
|
||||
}
|
||||
})
|
||||
|
@ -190,7 +190,8 @@ const task = plugin(taskId, {
|
||||
Kanban: '' as Asset,
|
||||
Status: '' as Asset,
|
||||
TodoCheck: '' as Asset,
|
||||
TodoUnCheck: '' as Asset
|
||||
TodoUnCheck: '' as Asset,
|
||||
ManageStatuses: '' as Asset
|
||||
},
|
||||
global: {
|
||||
// Global task root, if not attached to some other object.
|
||||
|
@ -35,4 +35,8 @@
|
||||
<path d="M12.8,6.6c-0.8,0-1.4,0.6-1.4,1.4c0,0.8,0.6,1.4,1.4,1.4s1.4-0.6,1.4-1.4C14.2,7.2,13.6,6.6,12.8,6.6z" />
|
||||
</symbol>
|
||||
|
||||
<symbol id="archive" viewBox="0 0 16 16" >
|
||||
<path d="M3.33333 5.33366V9.33366C3.33333 11.2193 3.33333 12.1621 3.91912 12.7479C4.50491 13.3337 5.44772 13.3337 7.33333 13.3337H8.66667C10.5523 13.3337 11.4951 13.3337 12.0809 12.7479C12.6667 12.1621 12.6667 11.2193 12.6667 9.33366V5.33366M3.33333 5.33366H12.6667M3.33333 5.33366H2.24242C2.20301 5.33366 2.1833 5.33366 2.16683 5.33089C2.08277 5.31675 2.01691 5.25089 2.00277 5.16682C2 5.15036 2 5.13065 2 5.09123V5.09123C2 4.69708 2 4.50001 2.02769 4.33533C2.16905 3.49469 2.8277 2.83604 3.66834 2.69468C3.83301 2.66699 4.03009 2.66699 4.42424 2.66699H11.5758C11.9699 2.66699 12.167 2.66699 12.3317 2.69468C13.1723 2.83604 13.8309 3.49469 13.9723 4.33533C14 4.50001 14 4.69708 14 5.09123V5.09123C14 5.13065 14 5.15036 13.9972 5.16682C13.9831 5.25089 13.9172 5.31675 13.8332 5.33089C13.8167 5.33366 13.797 5.33366 13.7576 5.33366H12.6667" />
|
||||
<path d="M6.66663 10.667H9.33329" stroke-linecap="round"/>
|
||||
</symbol>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 3.3 KiB |
@ -21,7 +21,8 @@ loadMetadata(view.icon, {
|
||||
Table: `${icons}#table`,
|
||||
Delete: `${icons}#delete`,
|
||||
Move: `${icons}#move`,
|
||||
MoreH: `${icons}#more-h`
|
||||
MoreH: `${icons}#more-h`,
|
||||
Archive: `${icons}#archive`
|
||||
})
|
||||
|
||||
addStringsLoader(viewId, async (lang: string) => await import(`../lang/${lang}.json`))
|
||||
|
@ -14,7 +14,7 @@
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import type { Doc } from '@anticrm/core'
|
||||
import type { Doc, Class, Ref } from '@anticrm/core'
|
||||
import type { Asset, IntlString, Resource } from '@anticrm/platform'
|
||||
import { getResource } from '@anticrm/platform'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
@ -22,10 +22,10 @@
|
||||
import { getActions } from '../utils'
|
||||
|
||||
export let object: Doc
|
||||
|
||||
let actions: {
|
||||
export let baseMenuClass: Ref<Class<Doc>> | undefined = undefined
|
||||
export let actions: {
|
||||
label: IntlString
|
||||
icon?: Asset
|
||||
icon: Asset
|
||||
action: () => void
|
||||
}[] = []
|
||||
|
||||
@ -36,7 +36,7 @@
|
||||
await impl(object)
|
||||
}
|
||||
|
||||
getActions(client, object).then(result => {
|
||||
getActions(client, object, baseMenuClass).then(result => {
|
||||
actions = result.map(a => ({
|
||||
label: a.label,
|
||||
icon: a.icon,
|
||||
@ -46,5 +46,5 @@
|
||||
|
||||
</script>
|
||||
|
||||
<Menu actions={actions} on:close/>
|
||||
<Menu {actions} on:close/>
|
||||
|
||||
|
@ -26,7 +26,8 @@
|
||||
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let query: DocumentQuery<Doc>
|
||||
export let options: FindOptions<Doc> | undefined
|
||||
export let options: FindOptions<Doc> | undefined = undefined
|
||||
export let baseMenuClass: Ref<Class<Doc>> | undefined = undefined
|
||||
export let config: (BuildModelKey|string)[]
|
||||
|
||||
let sortKey = 'modifiedOn'
|
||||
@ -53,9 +54,9 @@
|
||||
|
||||
const client = getClient()
|
||||
|
||||
const showMenu = (ev: MouseEvent, object: Doc, row: number): void => {
|
||||
const showMenu = async (ev: MouseEvent, object: Doc, row: number): Promise<void> => {
|
||||
selectRow = row
|
||||
showPopup(Menu, { object }, ev.target as HTMLElement, () => { selectRow = undefined })
|
||||
showPopup(Menu, { object, baseMenuClass }, ev.target as HTMLElement, () => { selectRow = undefined })
|
||||
}
|
||||
|
||||
function changeSorting (key: string): void {
|
||||
|
@ -15,7 +15,7 @@
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import type { Class, Doc, FindOptions, Ref, Space } from '@anticrm/core'
|
||||
import type { Class, Doc, DocumentQuery, FindOptions, Ref, Space } from '@anticrm/core'
|
||||
import { SortingOrder } from '@anticrm/core'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import { CheckBox, IconDown, IconUp, Label, Loading, ScrollBox, showPopup } from '@anticrm/ui'
|
||||
@ -24,10 +24,12 @@
|
||||
import Menu from './Menu.svelte'
|
||||
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let space: Ref<Space>
|
||||
export let options: FindOptions<Doc> | undefined
|
||||
export let space: Ref<Space> | undefined = undefined
|
||||
export let query: DocumentQuery<Doc> = {}
|
||||
export let options: FindOptions<Doc> | undefined = undefined
|
||||
export let baseMenuClass: Ref<Class<Doc>> | undefined = undefined
|
||||
export let config: string[]
|
||||
export let search: string
|
||||
export let search: string = ''
|
||||
|
||||
let sortKey = 'modifiedOn'
|
||||
let sortOrder = SortingOrder.Descending
|
||||
@ -35,10 +37,14 @@
|
||||
|
||||
let objects: Doc[]
|
||||
|
||||
const query = createQuery()
|
||||
$: query.query(
|
||||
const q = createQuery()
|
||||
$: q.query(
|
||||
_class,
|
||||
search === '' ? { space } : { $search: search },
|
||||
{
|
||||
...query,
|
||||
...search !== '' ? { $search: search } : {},
|
||||
...search === '' && space !== undefined ? { space } : {}
|
||||
},
|
||||
(result) => {
|
||||
objects = result
|
||||
},
|
||||
@ -63,7 +69,7 @@
|
||||
|
||||
const showMenu = (ev: MouseEvent, object: Doc, row: number): void => {
|
||||
selectRow = row
|
||||
showPopup(Menu, { object }, ev.target as HTMLElement, () => {
|
||||
showPopup(Menu, { object, baseMenuClass }, ev.target as HTMLElement, () => {
|
||||
selectRow = undefined
|
||||
})
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ import MoveView from './components/Move.svelte'
|
||||
|
||||
export { default as ContextMenu } from './components/Menu.svelte'
|
||||
export { buildModel, getActions, getObjectPresenter } from './utils'
|
||||
export { Table }
|
||||
export { Table, TableView }
|
||||
|
||||
function Delete (object: Doc): void {
|
||||
showPopup(
|
||||
@ -55,8 +55,12 @@ async function Move (object: Doc): Promise<void> {
|
||||
|
||||
export default async (): Promise<Resources> => ({
|
||||
actionImpl: {
|
||||
Delete,
|
||||
Move
|
||||
Delete: {
|
||||
apply: Delete
|
||||
},
|
||||
Move: {
|
||||
apply: Move
|
||||
}
|
||||
},
|
||||
component: {
|
||||
StringEditor,
|
||||
|
@ -144,7 +144,8 @@ const view = plugin(viewId, {
|
||||
Table: '' as Asset,
|
||||
Delete: '' as Asset,
|
||||
MoreH: '' as Asset,
|
||||
Move: '' as Asset
|
||||
Move: '' as Asset,
|
||||
Archive: '' as Asset
|
||||
}
|
||||
})
|
||||
export default view
|
||||
|
@ -4,6 +4,8 @@
|
||||
"Delete": "Delete",
|
||||
"Create": "Create",
|
||||
"ShowMenu": "Show menu",
|
||||
"HideMenu": "Hide menu"
|
||||
"HideMenu": "Hide menu",
|
||||
"Archive": "Archive",
|
||||
"Archived": "Archived {object}"
|
||||
}
|
||||
}
|
81
plugins/workbench-resources/src/components/Archive.svelte
Normal file
81
plugins/workbench-resources/src/components/Archive.svelte
Normal file
@ -0,0 +1,81 @@
|
||||
<!--
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
//
|
||||
// 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 core from '@anticrm/core'
|
||||
import type { Space } from '@anticrm/core'
|
||||
import { translate } from '@anticrm/platform'
|
||||
import { Label, Icon } from '@anticrm/ui'
|
||||
import view from '@anticrm/view'
|
||||
import { TableView } from '@anticrm/view-resources'
|
||||
import { createQuery } from '@anticrm/presentation'
|
||||
import { NavigatorModel } from '@anticrm/workbench'
|
||||
|
||||
import workbench from '../plugin'
|
||||
|
||||
export let model: NavigatorModel | undefined
|
||||
|
||||
const query = createQuery()
|
||||
let spaceSample: Space | undefined
|
||||
$: if (model) {
|
||||
query.query(
|
||||
core.class.Space,
|
||||
{
|
||||
_class: { $in: model.spaces.map(x => x.spaceClass) },
|
||||
archived: true
|
||||
},
|
||||
(result) => { spaceSample = result[0] },
|
||||
{ limit: 1 }
|
||||
)
|
||||
}
|
||||
|
||||
let spaceName = ''
|
||||
$: {
|
||||
const spaceClass = spaceSample?._class ?? ''
|
||||
const spaceModel = model?.spaces.find(x => x.spaceClass == spaceClass)
|
||||
|
||||
const label = spaceModel?.label
|
||||
|
||||
if (label) {
|
||||
void translate(label, {}).then((l) => { spaceName = l.toLowerCase() })
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex-col h-full">
|
||||
<div class="flex-row-center header">
|
||||
<div class="content-color mr-3"><Icon icon={view.icon.Archive} size={'medium'} /></div>
|
||||
<div class="fs-title"><Label label={workbench.string.Archived} params={{ object: spaceName }} /></div>
|
||||
</div>
|
||||
{#if spaceSample !== undefined}
|
||||
<TableView
|
||||
_class={spaceSample._class}
|
||||
config={['name', 'company', 'location', 'modifiedOn']}
|
||||
options={{}}
|
||||
baseMenuClass={core.class.Space}
|
||||
query={{
|
||||
_class: { $in: model?.spaces.map(x => x.spaceClass) ?? [] },
|
||||
archived: true
|
||||
}} />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.header {
|
||||
padding: 0 1.75rem 0 2.5rem;
|
||||
height: 4rem;
|
||||
min-height: 4rem;
|
||||
}
|
||||
</style>
|
@ -14,17 +14,36 @@
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import type { NavigatorModel, SpecialNavModel } from '@anticrm/workbench'
|
||||
import core from '@anticrm/core'
|
||||
import type { Space } from '@anticrm/core'
|
||||
import type { NavigatorModel } from '@anticrm/workbench'
|
||||
import { getCurrentLocation, navigate } from '@anticrm/ui'
|
||||
import { createQuery } from '@anticrm/presentation'
|
||||
import view from '@anticrm/view'
|
||||
|
||||
import workbench from '../plugin'
|
||||
|
||||
import SpacesNav from './navigator/SpacesNav.svelte'
|
||||
import TreeSeparator from './navigator/TreeSeparator.svelte'
|
||||
import SpecialElement from './navigator/SpecialElement.svelte'
|
||||
import { getCurrentLocation, navigate } from '@anticrm/ui'
|
||||
|
||||
export let model: NavigatorModel | undefined
|
||||
|
||||
const query = createQuery()
|
||||
let archivedSpaces: Space[] = []
|
||||
$: if (model) {
|
||||
query.query(
|
||||
core.class.Space,
|
||||
{
|
||||
_class: { $in: model.spaces.map(x => x.spaceClass) },
|
||||
archived: true
|
||||
},
|
||||
(result) => { archivedSpaces = result })
|
||||
}
|
||||
|
||||
function selectSpecial (sp: SpecialNavModel): void {
|
||||
function selectSpecial (id: string): void {
|
||||
const loc = getCurrentLocation()
|
||||
loc.path[2] = sp.id
|
||||
loc.path[2] = id
|
||||
loc.path.length = 3
|
||||
navigate(loc)
|
||||
}
|
||||
@ -33,13 +52,26 @@
|
||||
{#if model}
|
||||
{#if model.specials}
|
||||
{#each model.specials as special}
|
||||
<SpecialElement label={special.label} icon={special.icon} on:click={() => selectSpecial(special)} />
|
||||
<SpecialElement label={special.label} icon={special.icon} on:click={() => selectSpecial(special.id)} />
|
||||
{/each}
|
||||
{#if model.spaces.length}
|
||||
{/if}
|
||||
{#if archivedSpaces.length > 0}
|
||||
<SpecialElement label={workbench.string.Archive} icon={view.icon.Archive} on:click={() => selectSpecial('archive')} />
|
||||
{/if}
|
||||
{#if model.spaces.length}
|
||||
<div class="separator">
|
||||
<TreeSeparator />
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{#each model.spaces as m (m.label)}
|
||||
<SpacesNav model={m}/>
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.separator {
|
||||
&:nth-child(2) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -29,11 +29,12 @@
|
||||
import SpaceHeader from './SpaceHeader.svelte'
|
||||
import SpaceView from './SpaceView.svelte'
|
||||
|
||||
import { AnyComponent, Component, location, Popup, showPopup, TooltipInstance, closeTooltip, ActionIcon, IconEdit } from '@anticrm/ui'
|
||||
import { AnyComponent, Component, location, Popup, showPopup, TooltipInstance, closeTooltip, ActionIcon, IconEdit, AnySvelteComponent } from '@anticrm/ui'
|
||||
import core from '@anticrm/core'
|
||||
import AccountPopup from './AccountPopup.svelte'
|
||||
import AppItem from './AppItem.svelte'
|
||||
import TopMenu from './icons/TopMenu.svelte'
|
||||
import Archive from './Archive.svelte'
|
||||
|
||||
export let client: Client
|
||||
|
||||
@ -42,6 +43,7 @@
|
||||
let currentApp: Ref<Application> | undefined
|
||||
let currentApplication: Application | undefined
|
||||
let currentSpace: Ref<Space> | undefined
|
||||
let ownSpecialComponent: AnySvelteComponent | undefined
|
||||
let specialComponent: AnyComponent | undefined
|
||||
let currentView: ViewConfiguration | undefined
|
||||
let createItemDialog: AnyComponent | undefined
|
||||
@ -52,22 +54,37 @@
|
||||
currentApplication = (await client.findAll(workbench.class.Application, { _id: currentApp }))[0]
|
||||
navigatorModel = currentApplication?.navigatorModel
|
||||
let currentFolder = loc.path[2] as Ref<Space>
|
||||
ownSpecialComponent = getOwnSpecialComponent(currentFolder)
|
||||
|
||||
if (ownSpecialComponent !== undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
specialComponent = getSpecialComponent(currentFolder)
|
||||
if (!specialComponent) {
|
||||
currentSpace = currentFolder
|
||||
const space = (await client.findAll(core.class.Space, { _id: currentSpace }))[0]
|
||||
if (space) {
|
||||
const spaceClass = client.getHierarchy().getClass(space._class) // (await client.findAll(core.class.Class, { _id: space._class }))[0]
|
||||
const view = client.getHierarchy().as(spaceClass, workbench.mixin.SpaceView)
|
||||
currentView = view.view
|
||||
createItemDialog = currentView.createItemDialog
|
||||
} else {
|
||||
currentView = undefined
|
||||
createItemDialog = undefined
|
||||
}
|
||||
|
||||
if (specialComponent !== undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
currentSpace = currentFolder
|
||||
const space = (await client.findAll(core.class.Space, { _id: currentSpace }))[0]
|
||||
if (space) {
|
||||
const spaceClass = client.getHierarchy().getClass(space._class) // (await client.findAll(core.class.Class, { _id: space._class }))[0]
|
||||
const view = client.getHierarchy().as(spaceClass, workbench.mixin.SpaceView)
|
||||
currentView = view.view
|
||||
createItemDialog = currentView.createItemDialog
|
||||
} else {
|
||||
currentView = undefined
|
||||
createItemDialog = undefined
|
||||
}
|
||||
}))
|
||||
|
||||
function getOwnSpecialComponent (id: string): AnySvelteComponent | undefined {
|
||||
if (id === 'archive') {
|
||||
return Archive
|
||||
}
|
||||
}
|
||||
|
||||
function getSpecialComponent (id: string): AnyComponent | undefined {
|
||||
let special = navigatorModel?.specials?.find((x) => x.id === id)
|
||||
return special?.component
|
||||
@ -119,7 +136,9 @@
|
||||
</div>
|
||||
{/if}
|
||||
<div class="panel-component">
|
||||
{#if specialComponent}
|
||||
{#if ownSpecialComponent}
|
||||
<svelte:component this={ownSpecialComponent} model={navigatorModel} />
|
||||
{:else if specialComponent}
|
||||
<Component is={specialComponent} />
|
||||
{:else}
|
||||
<SpaceHeader space={currentSpace} {createItemDialog} />
|
||||
|
@ -34,7 +34,7 @@
|
||||
let spaces: Space[] = []
|
||||
let selected: Ref<Space> | undefined = undefined
|
||||
|
||||
$: query.query(model.spaceClass, {}, result => { spaces = result })
|
||||
$: query.query(model.spaceClass, { archived: false }, result => { spaces = result })
|
||||
|
||||
const addSpace: Action = {
|
||||
label: model.addSpaceLabel,
|
||||
@ -72,7 +72,7 @@
|
||||
result.push({
|
||||
icon: act.icon ?? IconEdit,
|
||||
label: act.label,
|
||||
action: async (props, ev) => {
|
||||
action: async () => {
|
||||
const impl = await getResource(act.action)
|
||||
await impl(space)
|
||||
}
|
||||
|
@ -20,7 +20,7 @@
|
||||
import type { Asset, IntlString } from '@anticrm/platform'
|
||||
import type { Action } from '@anticrm/ui'
|
||||
import type { Ref, Space } from '@anticrm/core'
|
||||
import { Icon, Label, ActionIcon } from '@anticrm/ui'
|
||||
import { Icon, Label, ActionIcon, Menu, showPopup, IconMoreH } from '@anticrm/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
export let _id: Ref<Space> | undefined = undefined
|
||||
@ -34,9 +34,15 @@
|
||||
export let actions: Action[] = []
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
let hovered = false
|
||||
function onMenuClick(ev: MouseEvent) {
|
||||
hovered = true
|
||||
showPopup(Menu, { actions, ctx: _id }, ev.target as HTMLElement, () => { hovered = false })
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="container" class:selected
|
||||
<div class="container" class:selected class:hovered
|
||||
on:click|stopPropagation={() => {
|
||||
if (node && !icon) collapsed = !collapsed
|
||||
dispatch('click')
|
||||
@ -52,11 +58,15 @@
|
||||
<span class="label" class:sub={node}>
|
||||
{#if label}<Label {label}/>{:else}{title}{/if}
|
||||
</span>
|
||||
{#each actions as action}
|
||||
{#if actions.length === 1}
|
||||
<div class="tool">
|
||||
<ActionIcon label={action.label} icon={action.icon} size={'small'} action={(ev) => { action.action(_id, ev) }} />
|
||||
<ActionIcon label={actions[0].label} icon={actions[0].icon} size={'small'} action={(ev) => { actions[0].action(_id, ev) }} />
|
||||
</div>
|
||||
{/each}
|
||||
{:else if actions.length > 1}
|
||||
<div class="tool" on:click|stopPropagation={onMenuClick}>
|
||||
<IconMoreH size={'small'} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if notifications > 0 && collapsed}
|
||||
<div class="counter">{notifications}</div>
|
||||
{/if}
|
||||
@ -106,7 +116,7 @@
|
||||
color: var(--theme-caption-color);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
&:hover, &.hovered {
|
||||
background-color: var(--theme-button-bg-enabled);
|
||||
.tool {
|
||||
visibility: visible;
|
||||
|
@ -14,9 +14,9 @@
|
||||
//
|
||||
|
||||
import { mergeIds } from '@anticrm/platform'
|
||||
import type { IntlString } from '@anticrm/platform'
|
||||
|
||||
import workbench, { workbenchId } from '@anticrm/workbench'
|
||||
import { IntlString } from '@anticrm/platform'
|
||||
|
||||
export default mergeIds(workbenchId, workbench, {
|
||||
string: {
|
||||
@ -24,6 +24,8 @@ export default mergeIds(workbenchId, workbench, {
|
||||
Delete: '' as IntlString,
|
||||
Create: '' as IntlString,
|
||||
ShowMenu: '' as IntlString,
|
||||
HideMenu: '' as IntlString
|
||||
HideMenu: '' as IntlString,
|
||||
Archive: '' as IntlString,
|
||||
Archived: '' as IntlString
|
||||
}
|
||||
})
|
||||
|
@ -180,6 +180,7 @@ export function genMinModel (): TxCUD<Doc>[] {
|
||||
name: 'Sp1',
|
||||
description: '',
|
||||
private: false,
|
||||
archived: false,
|
||||
members: [u1, u2]
|
||||
})
|
||||
)
|
||||
@ -189,6 +190,7 @@ export function genMinModel (): TxCUD<Doc>[] {
|
||||
name: 'Sp2',
|
||||
description: '',
|
||||
private: false,
|
||||
archived: false,
|
||||
members: [u1]
|
||||
})
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user