tracker key actions (#2598)

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
Denis Bykhov 2023-02-07 19:27:46 +06:00 committed by GitHub
parent 243bc1dede
commit c5be92d870
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 264 additions and 144 deletions

View File

@ -284,7 +284,9 @@ export function createModel (builder: Builder): void {
input: 'any', input: 'any',
category: board.category.Card, category: board.category.Card,
target: board.class.Card, target: board.class.Card,
context: { mode: 'context', application: board.app.Board, group: 'create' } keyBinding: ['Enter'],
context: { mode: 'context', application: board.app.Board, group: 'create' },
override: [view.action.Open]
}, },
board.action.Open board.action.Open
) )

View File

@ -30,7 +30,7 @@ import {
Persons, Persons,
Status Status
} from '@hcengineering/contact' } from '@hcengineering/contact'
import { Class, DateRangeMode, Domain, Ref, Timestamp, DOMAIN_MODEL, IndexKind } from '@hcengineering/core' import { Class, DateRangeMode, Domain, DOMAIN_MODEL, IndexKind, Ref, Timestamp } from '@hcengineering/core'
import { import {
Builder, Builder,
Collection, Collection,
@ -48,7 +48,7 @@ import attachment from '@hcengineering/model-attachment'
import chunter from '@hcengineering/model-chunter' import chunter from '@hcengineering/model-chunter'
import core, { TAccount, TAttachedDoc, TDoc, TSpace } from '@hcengineering/model-core' import core, { TAccount, TAttachedDoc, TDoc, TSpace } from '@hcengineering/model-core'
import presentation from '@hcengineering/model-presentation' import presentation from '@hcengineering/model-presentation'
import view, { actionTemplates, createAction, ViewAction, Viewlet } from '@hcengineering/model-view' import view, { createAction, ViewAction, Viewlet } from '@hcengineering/model-view'
import workbench from '@hcengineering/model-workbench' import workbench from '@hcengineering/model-workbench'
import type { Asset, IntlString, Resource } from '@hcengineering/platform' import type { Asset, IntlString, Resource } from '@hcengineering/platform'
import setting from '@hcengineering/setting' import setting from '@hcengineering/setting'
@ -498,15 +498,6 @@ export function createModel (builder: Builder): void {
contact.completion.OrganizationCategory contact.completion.OrganizationCategory
) )
createAction(builder, {
...actionTemplates.open,
target: contact.class.Contact,
context: {
mode: ['browser', 'context'],
group: 'create'
}
})
createAction( createAction(
builder, builder,
{ {

View File

@ -245,11 +245,15 @@ export function createModel (builder: Builder): void {
createAction(builder, { createAction(builder, {
...actionTemplates.open, ...actionTemplates.open,
actionProps: {
component: document.component.EditDoc
},
target: document.class.Document, target: document.class.Document,
context: { context: {
mode: ['browser', 'context'], mode: ['browser', 'context'],
group: 'create' group: 'create'
} },
override: [view.action.Open]
}) })
builder.mixin(document.class.DocumentVersion, core.class.Class, view.mixin.CollectionEditor, { builder.mixin(document.class.DocumentVersion, core.class.Class, view.mixin.CollectionEditor, {
editor: document.component.DocumentVersions editor: document.component.DocumentVersions

View File

@ -179,7 +179,7 @@ export function createModel (builder: Builder): void {
label: gmail.string.WrtieEmail, label: gmail.string.WrtieEmail,
icon: contact.icon.Email, icon: contact.icon.Email,
keyBinding: [], keyBinding: [],
input: 'none', input: 'any',
category: contact.category.Contact, category: contact.category.Contact,
target: contact.class.Contact, target: contact.class.Contact,
context: { context: {

View File

@ -620,7 +620,7 @@ export function createModel (builder: Builder): void {
}, },
label: recruit.string.CreateTalent, label: recruit.string.CreateTalent,
icon: recruit.icon.Create, icon: recruit.icon.Create,
keyBinding: ['c'], keyBinding: ['keyC'],
input: 'none', input: 'none',
category: recruit.category.Recruit, category: recruit.category.Recruit,
target: core.class.Doc, target: core.class.Doc,
@ -720,7 +720,7 @@ export function createModel (builder: Builder): void {
}, },
input: 'focus', input: 'focus',
category: recruit.category.Recruit, category: recruit.category.Recruit,
keyBinding: ['e'], keyBinding: ['keyE'],
target: recruit.class.Vacancy, target: recruit.class.Vacancy,
context: { context: {
mode: ['context', 'browser'], mode: ['context', 'browser'],

View File

@ -117,6 +117,10 @@ export function createModel (builder: Builder): void {
component: tags.component.TagsFilter component: tags.component.TagsFilter
}) })
builder.mixin(tags.class.TagElement, core.class.Class, view.mixin.IgnoreActions, {
actions: [view.action.Open]
})
builder.createDoc( builder.createDoc(
view.class.FilterMode, view.class.FilterMode,
core.space.Model, core.space.Model,

View File

@ -48,7 +48,7 @@ import {
import attachment from '@hcengineering/model-attachment' import attachment from '@hcengineering/model-attachment'
import chunter from '@hcengineering/model-chunter' import chunter from '@hcengineering/model-chunter'
import core, { DOMAIN_SPACE, TAttachedDoc, TDoc, TSpace, TType } from '@hcengineering/model-core' import core, { DOMAIN_SPACE, TAttachedDoc, TDoc, TSpace, TType } from '@hcengineering/model-core'
import view, { classPresenter, createAction } from '@hcengineering/model-view' import view, { actionTemplates, classPresenter, createAction } from '@hcengineering/model-view'
import workbench, { createNavigateAction } from '@hcengineering/model-workbench' import workbench, { createNavigateAction } from '@hcengineering/model-workbench'
import notification from '@hcengineering/notification' import notification from '@hcengineering/notification'
import { Asset, IntlString } from '@hcengineering/platform' import { Asset, IntlString } from '@hcengineering/platform'
@ -1053,6 +1053,29 @@ export function createModel (builder: Builder): void {
tracker.category.Tracker tracker.category.Tracker
) )
createAction(
builder,
{
action: view.actionImpl.ShowPopup,
actionProps: {
component: tracker.component.CreateIssue,
element: 'top'
},
label: tracker.string.NewIssue,
icon: tracker.icon.Issue,
keyBinding: ['keyC'],
input: 'none',
category: tracker.category.Tracker,
target: tracker.class.Issue,
context: {
mode: ['browser'],
application: tracker.app.Tracker,
group: 'create'
}
},
tracker.action.NewSubIssue
)
createAction( createAction(
builder, builder,
{ {
@ -1156,6 +1179,32 @@ export function createModel (builder: Builder): void {
} }
}) })
createAction(builder, {
...actionTemplates.open,
actionProps: {
component: tracker.component.EditIssue
},
target: tracker.class.Issue,
context: {
mode: ['browser', 'context'],
group: 'create'
},
override: [view.action.Open]
})
createAction(builder, {
...actionTemplates.open,
actionProps: {
component: tracker.component.EditIssueTemplate
},
target: tracker.class.IssueTemplate,
context: {
mode: ['browser', 'context'],
group: 'create'
},
override: [view.action.Open]
})
builder.mixin(tracker.class.Issue, core.class.Class, view.mixin.ClassFilters, { builder.mixin(tracker.class.Issue, core.class.Class, view.mixin.ClassFilters, {
filters: ['status', 'priority', 'assignee', 'project', 'sprint', 'estimation', 'dueDate', 'modifiedOn'] filters: ['status', 'priority', 'assignee', 'project', 'sprint', 'estimation', 'dueDate', 'modifiedOn']
}) })
@ -1182,6 +1231,27 @@ export function createModel (builder: Builder): void {
sort: { rank: SortingOrder.Ascending } sort: { rank: SortingOrder.Ascending }
} }
createAction(builder, {
action: view.actionImpl.ShowPopup,
actionProps: {
component: tracker.component.TimeSpendReportPopup,
fillProps: {
_object: 'issue'
}
},
label: tracker.string.TimeSpendReportAdd,
icon: tracker.icon.TimeReport,
input: 'focus',
keyBinding: ['keyT'],
category: tracker.category.Tracker,
target: tracker.class.Issue,
context: {
mode: ['context', 'browser'],
application: tracker.app.Tracker,
group: 'edit'
}
})
createAction( createAction(
builder, builder,
{ {
@ -1198,12 +1268,12 @@ export function createModel (builder: Builder): void {
}, },
label: tracker.string.Status, label: tracker.string.Status,
icon: tracker.icon.CategoryBacklog, icon: tracker.icon.CategoryBacklog,
keyBinding: [], keyBinding: ['keyS->keyS'],
input: 'none', input: 'any',
category: tracker.category.Tracker, category: tracker.category.Tracker,
target: tracker.class.Issue, target: tracker.class.Issue,
context: { context: {
mode: ['context'], mode: ['context', 'browser'],
application: tracker.app.Tracker, application: tracker.app.Tracker,
group: 'edit' group: 'edit'
} }
@ -1222,12 +1292,12 @@ export function createModel (builder: Builder): void {
}, },
label: tracker.string.Priority, label: tracker.string.Priority,
icon: tracker.icon.PriorityHigh, icon: tracker.icon.PriorityHigh,
keyBinding: [], keyBinding: ['keyP-keyR'],
input: 'none', input: 'any',
category: tracker.category.Tracker, category: tracker.category.Tracker,
target: tracker.class.Issue, target: tracker.class.Issue,
context: { context: {
mode: ['context'], mode: ['context', 'browser'],
application: tracker.app.Tracker, application: tracker.app.Tracker,
group: 'edit' group: 'edit'
} }
@ -1247,12 +1317,12 @@ export function createModel (builder: Builder): void {
}, },
label: tracker.string.Assignee, label: tracker.string.Assignee,
icon: contact.icon.Person, icon: contact.icon.Person,
keyBinding: [], keyBinding: ['keyA'],
input: 'none', input: 'any',
category: tracker.category.Tracker, category: tracker.category.Tracker,
target: tracker.class.Issue, target: tracker.class.Issue,
context: { context: {
mode: ['context'], mode: ['context', 'browser'],
application: tracker.app.Tracker, application: tracker.app.Tracker,
group: 'edit' group: 'edit'
} }
@ -1274,12 +1344,12 @@ export function createModel (builder: Builder): void {
}, },
label: tracker.string.Project, label: tracker.string.Project,
icon: tracker.icon.Project, icon: tracker.icon.Project,
keyBinding: [], keyBinding: ['keyP->keyP'],
input: 'none', input: 'any',
category: tracker.category.Tracker, category: tracker.category.Tracker,
target: tracker.class.Issue, target: tracker.class.Issue,
context: { context: {
mode: ['context'], mode: ['context', 'browser'],
application: tracker.app.Tracker, application: tracker.app.Tracker,
group: 'edit' group: 'edit'
} }
@ -1307,12 +1377,12 @@ export function createModel (builder: Builder): void {
}, },
label: tracker.string.Sprint, label: tracker.string.Sprint,
icon: tracker.icon.Sprint, icon: tracker.icon.Sprint,
keyBinding: [], keyBinding: ['keyS->keyP'],
input: 'none', input: 'any',
category: tracker.category.Tracker, category: tracker.category.Tracker,
target: tracker.class.Issue, target: tracker.class.Issue,
context: { context: {
mode: ['context'], mode: ['context', 'browser'],
application: tracker.app.Tracker, application: tracker.app.Tracker,
group: 'edit' group: 'edit'
} }
@ -1334,8 +1404,8 @@ export function createModel (builder: Builder): void {
}, },
label: tracker.string.SetDueDate, label: tracker.string.SetDueDate,
icon: tracker.icon.DueDate, icon: tracker.icon.DueDate,
keyBinding: [], keyBinding: ['keyD'],
input: 'none', input: 'any',
category: tracker.category.Tracker, category: tracker.category.Tracker,
target: tracker.class.Issue, target: tracker.class.Issue,
context: { context: {
@ -1356,7 +1426,7 @@ export function createModel (builder: Builder): void {
label: tracker.string.CopyIssueId, label: tracker.string.CopyIssueId,
icon: tracker.icon.CopyID, icon: tracker.icon.CopyID,
keyBinding: [], keyBinding: [],
input: 'none', input: 'focus',
category: tracker.category.Tracker, category: tracker.category.Tracker,
target: tracker.class.Issue, target: tracker.class.Issue,
context: { context: {
@ -1377,7 +1447,7 @@ export function createModel (builder: Builder): void {
label: tracker.string.CopyIssueTitle, label: tracker.string.CopyIssueTitle,
icon: tracker.icon.CopyBranch, icon: tracker.icon.CopyBranch,
keyBinding: [], keyBinding: [],
input: 'none', input: 'focus',
category: tracker.category.Tracker, category: tracker.category.Tracker,
target: tracker.class.Issue, target: tracker.class.Issue,
context: { context: {
@ -1398,7 +1468,7 @@ export function createModel (builder: Builder): void {
label: tracker.string.CopyIssueUrl, label: tracker.string.CopyIssueUrl,
icon: tracker.icon.CopyURL, icon: tracker.icon.CopyURL,
keyBinding: [], keyBinding: [],
input: 'none', input: 'focus',
category: tracker.category.Tracker, category: tracker.category.Tracker,
target: tracker.class.Issue, target: tracker.class.Issue,
context: { context: {
@ -1416,7 +1486,7 @@ export function createModel (builder: Builder): void {
label: tracker.string.MoveToTeam, label: tracker.string.MoveToTeam,
icon: view.icon.Move, icon: view.icon.Move,
keyBinding: [], keyBinding: [],
input: 'none', input: 'any',
category: tracker.category.Tracker, category: tracker.category.Tracker,
target: tracker.class.Issue, target: tracker.class.Issue,
context: { context: {
@ -1465,7 +1535,7 @@ export function createModel (builder: Builder): void {
label: tracker.string.Duplicate, label: tracker.string.Duplicate,
icon: tracker.icon.Duplicate, icon: tracker.icon.Duplicate,
keyBinding: [], keyBinding: [],
input: 'none', input: 'focus',
category: tracker.category.Tracker, category: tracker.category.Tracker,
target: tracker.class.Issue, target: tracker.class.Issue,
context: { context: {

View File

@ -41,7 +41,8 @@ export default mergeIds(trackerId, tracker, {
// Required to pass build without errorsF // Required to pass build without errorsF
Nope: '' as AnyComponent, Nope: '' as AnyComponent,
SprintSelector: '' as AnyComponent, SprintSelector: '' as AnyComponent,
IssueStatistics: '' as AnyComponent IssueStatistics: '' as AnyComponent,
TimeSpendReportPopup: '' as AnyComponent
}, },
app: { app: {
Tracker: '' as Ref<Application> Tracker: '' as Ref<Application>

View File

@ -731,6 +731,17 @@ export function createModel (builder: Builder): void {
target: core.class.Doc, target: core.class.Doc,
context: { mode: ['context', 'browser', 'editor'] } context: { mode: ['context', 'browser', 'editor'] }
}) })
createAction(
builder,
{
...actionTemplates.open,
target: core.class.Doc,
category: view.category.Editor,
context: { mode: ['browser', 'context'], group: 'edit' }
},
view.action.Open
)
} }
export default view export default view

View File

@ -137,11 +137,6 @@
key.stopPropagation() key.stopPropagation()
handleSelection(key, selection) handleSelection(key, selection)
} }
if (key.code === 'Escape') {
key.preventDefault()
key.stopPropagation()
dispatch('close')
}
} }
const manager = createFocusManager() const manager = createFocusManager()

View File

@ -137,11 +137,6 @@
key.stopPropagation() key.stopPropagation()
handleSelection(key, objects, selection) handleSelection(key, objects, selection)
} }
if (key.code === 'Escape') {
key.preventDefault()
key.stopPropagation()
dispatch('close')
}
} }
const manager = createFocusManager() const manager = createFocusManager()

View File

@ -16,9 +16,9 @@
import type { IntlString } from '@hcengineering/platform' import type { IntlString } from '@hcengineering/platform'
import { translate } from '@hcengineering/platform' import { translate } from '@hcengineering/platform'
import { createEventDispatcher, onMount } from 'svelte' import { createEventDispatcher, onMount } from 'svelte'
import { deviceOptionsStore, resizeObserver } from '..'
import { getPlatformColor } from '../colors' import { getPlatformColor } from '../colors'
import ListView from './ListView.svelte' import ListView from './ListView.svelte'
import { resizeObserver, deviceOptionsStore } from '..'
export let placeholder: IntlString | undefined = undefined export let placeholder: IntlString | undefined = undefined
export let placeholderParam: any | undefined = undefined export let placeholderParam: any | undefined = undefined
@ -62,11 +62,6 @@
key.stopPropagation() key.stopPropagation()
handleSelection(key, selection) handleSelection(key, selection)
} }
if (key.code === 'Escape') {
key.preventDefault()
key.stopPropagation()
dispatch('close')
}
} }
let input: HTMLElement let input: HTMLElement
onMount(() => { onMount(() => {

View File

@ -16,11 +16,11 @@
import type { IntlString } from '@hcengineering/platform' import type { IntlString } from '@hcengineering/platform'
import { translate } from '@hcengineering/platform' import { translate } from '@hcengineering/platform'
import { createEventDispatcher, onMount } from 'svelte' import { createEventDispatcher, onMount } from 'svelte'
import type { DropdownTextItem } from '../types' import { deviceOptionsStore, resizeObserver } from '..'
import plugin from '../plugin' import plugin from '../plugin'
import type { DropdownTextItem } from '../types'
import CheckBox from './CheckBox.svelte' import CheckBox from './CheckBox.svelte'
import ListView from './ListView.svelte' import ListView from './ListView.svelte'
import { resizeObserver, deviceOptionsStore } from '..'
export let placeholder: IntlString = plugin.string.SearchDots export let placeholder: IntlString = plugin.string.SearchDots
export let items: DropdownTextItem[] export let items: DropdownTextItem[]
@ -76,11 +76,6 @@
key.stopPropagation() key.stopPropagation()
handleSelection(key, selection) handleSelection(key, selection)
} }
if (key.code === 'Escape') {
key.preventDefault()
key.stopPropagation()
dispatch('close')
}
} }
function isSelected ( function isSelected (

View File

@ -16,11 +16,11 @@
import type { Asset, IntlString } from '@hcengineering/platform' import type { Asset, IntlString } from '@hcengineering/platform'
import { translate } from '@hcengineering/platform' import { translate } from '@hcengineering/platform'
import { createEventDispatcher, onMount } from 'svelte' import { createEventDispatcher, onMount } from 'svelte'
import type { AnySvelteComponent, ListItem } from '../types' import { deviceOptionsStore, resizeObserver } from '..'
import plugin from '../plugin' import plugin from '../plugin'
import type { AnySvelteComponent, ListItem } from '../types'
import Icon from './Icon.svelte' import Icon from './Icon.svelte'
import ListView from './ListView.svelte' import ListView from './ListView.svelte'
import { resizeObserver, deviceOptionsStore } from '..'
export let icon: Asset | AnySvelteComponent export let icon: Asset | AnySvelteComponent
export let placeholder: IntlString = plugin.string.SearchDots export let placeholder: IntlString = plugin.string.SearchDots
@ -69,11 +69,6 @@
key.stopPropagation() key.stopPropagation()
handleSelection(key, selection) handleSelection(key, selection)
} }
if (key.code === 'Escape') {
key.preventDefault()
key.stopPropagation()
dispatch('close')
}
} }
</script> </script>

View File

@ -15,10 +15,10 @@
<script lang="ts"> <script lang="ts">
import { IntlString } from '@hcengineering/platform' import { IntlString } from '@hcengineering/platform'
import { createEventDispatcher } from 'svelte' import { createEventDispatcher } from 'svelte'
import { resizeObserver } from '..'
import CheckBox from './CheckBox.svelte' import CheckBox from './CheckBox.svelte'
import Label from './Label.svelte' import Label from './Label.svelte'
import ListView from './ListView.svelte' import ListView from './ListView.svelte'
import { resizeObserver } from '..'
export let items: Record<any, IntlString> export let items: Record<any, IntlString>
export let selected: any | undefined = undefined export let selected: any | undefined = undefined
@ -51,11 +51,6 @@
key.stopPropagation() key.stopPropagation()
handleSelection(key, selection) handleSelection(key, selection)
} }
if (key.code === 'Escape') {
key.preventDefault()
key.stopPropagation()
dispatch('close')
}
} }
</script> </script>

View File

@ -15,15 +15,15 @@
<script lang="ts"> <script lang="ts">
import type { Asset, IntlString } from '@hcengineering/platform' import type { Asset, IntlString } from '@hcengineering/platform'
import { createEventDispatcher } from 'svelte' import { createEventDispatcher } from 'svelte'
import { deviceOptionsStore, resizeObserver } from '..'
import { createFocusManager } from '../focus' import { createFocusManager } from '../focus'
import type { AnySvelteComponent } from '../types'
import EditBox from './EditBox.svelte' import EditBox from './EditBox.svelte'
import FocusHandler from './FocusHandler.svelte' import FocusHandler from './FocusHandler.svelte'
import Icon from './Icon.svelte' import Icon from './Icon.svelte'
import IconCheck from './icons/Check.svelte' import IconCheck from './icons/Check.svelte'
import Label from './Label.svelte' import Label from './Label.svelte'
import ListView from './ListView.svelte' import ListView from './ListView.svelte'
import type { AnySvelteComponent } from '../types'
import { resizeObserver, deviceOptionsStore } from '..'
interface ValueType { interface ValueType {
id: number | string | null id: number | string | null
@ -74,11 +74,6 @@
key.stopPropagation() key.stopPropagation()
dispatch('close', value[selection].id) dispatch('close', value[selection].id)
} }
if (key.code === 'Escape') {
key.preventDefault()
key.stopPropagation()
dispatch('close')
}
} }
const manager = createFocusManager() const manager = createFocusManager()

View File

@ -23,10 +23,11 @@
import { getTimeReportDate, getTimeReportDayType } from '../../../utils' import { getTimeReportDate, getTimeReportDayType } from '../../../utils'
import TimeReportDayDropdown from './TimeReportDayDropdown.svelte' import TimeReportDayDropdown from './TimeReportDayDropdown.svelte'
export let issueId: Ref<Issue> export let issue: Issue | undefined = undefined
export let issueClass: Ref<Class<Issue>> export let issueId: Ref<Issue> | undefined = issue?._id
export let space: Ref<Space> export let issueClass: Ref<Class<Issue>> = issue?._class ?? tracker.class.Issue
export let assignee: Ref<Employee> | undefined export let space: Ref<Space> | undefined = issue?.space
export let assignee: Ref<Employee> | null | undefined = issue?.assignee
export let value: TimeSpendReport | undefined export let value: TimeSpendReport | undefined
export let placeholder: IntlString = tracker.string.TimeSpendReportValue export let placeholder: IntlString = tracker.string.TimeSpendReportValue
@ -45,16 +46,20 @@
return true return true
} }
const client = getClient()
async function create (): Promise<void> { async function create (): Promise<void> {
if (value === undefined) { if (value === undefined) {
getClient().addCollection( if (space && issueId) {
tracker.class.TimeSpendReport, await client.addCollection(
space, tracker.class.TimeSpendReport,
issueId, space,
issueClass, issueId,
'reports', issueClass,
data as AttachedData<TimeSpendReport> 'reports',
) data as AttachedData<TimeSpendReport>
)
}
} else { } else {
const ops: DocumentUpdate<TimeSpendReport> = {} const ops: DocumentUpdate<TimeSpendReport> = {}
if (value.value !== data.value) { if (value.value !== data.value) {
@ -70,15 +75,17 @@
ops.date = data.date ops.date = data.date
} }
if (Object.keys(ops).length > 0) { if (Object.keys(ops).length > 0) {
getClient().update(value, ops) await client.update(value, ops)
} }
} }
} }
$: canSave = Number.isFinite(data.value) && data.value !== 0 && space !== undefined && issueId !== undefined
</script> </script>
<Card <Card
label={tracker.string.TimeSpendReportAdd} label={tracker.string.TimeSpendReportAdd}
canSave={Number.isFinite(data.value) && data.value !== 0} {canSave}
okAction={create} okAction={create}
on:close on:close
okLabel={value === undefined ? presentation.string.Create : presentation.string.Save} okLabel={value === undefined ? presentation.string.Create : presentation.string.Save}

View File

@ -116,6 +116,7 @@ import StatusRefPresenter from './components/issues/StatusRefPresenter.svelte'
import SprintRefPresenter from './components/sprints/SprintRefPresenter.svelte' import SprintRefPresenter from './components/sprints/SprintRefPresenter.svelte'
import { EmployeeAccount } from '@hcengineering/contact' import { EmployeeAccount } from '@hcengineering/contact'
import DeleteProjectPresenter from './components/projects/DeleteProjectPresenter.svelte' import DeleteProjectPresenter from './components/projects/DeleteProjectPresenter.svelte'
import TimeSpendReportPopup from './components/issues/timereport/TimeSpendReportPopup.svelte'
export { default as SubIssueList } from './components/issues/edit/SubIssueList.svelte' export { default as SubIssueList } from './components/issues/edit/SubIssueList.svelte'
@ -368,7 +369,8 @@ export default async (): Promise<Resources> => ({
StatusRefPresenter, StatusRefPresenter,
RelatedIssuesSection, RelatedIssuesSection,
RelatedIssueSelector, RelatedIssueSelector,
DeleteProjectPresenter DeleteProjectPresenter,
TimeSpendReportPopup
}, },
completion: { completion: {
IssueQuery: async (client: Client, query: string, filter?: { in?: RelatedDocument[], nin?: RelatedDocument[] }) => IssueQuery: async (client: Client, query: string, filter?: { in?: RelatedDocument[], nin?: RelatedDocument[] }) =>

View File

@ -11,7 +11,7 @@ import {
showPanel, showPanel,
showPopup showPopup
} from '@hcengineering/ui' } from '@hcengineering/ui'
import { Action, ViewContext } from '@hcengineering/view' import { ViewContext } from '@hcengineering/view'
import MoveView from './components/Move.svelte' import MoveView from './components/Move.svelte'
import { contextStore } from './context' import { contextStore } from './context'
import view from './plugin' import view from './plugin'
@ -96,12 +96,13 @@ export function select (evt: Event | undefined, offset: 1 | -1 | 0, of?: Doc, di
} }
} }
function SelectItem (doc: Doc | undefined, evt: Event): void { function SelectItem (doc: Doc | Doc[] | undefined, evt: Event): void {
if (doc !== undefined) { const focus = $focusStore.focus
if (focus !== undefined) {
selectionStore.update((selection) => { selectionStore.update((selection) => {
const ind = selection.findIndex((it) => it._id === doc._id) const ind = selection.findIndex((it) => it._id === focus._id)
if (ind === -1) { if (ind === -1) {
selection.push(doc) selection.push(focus)
} else { } else {
selection.splice(ind, 1) selection.splice(ind, 1)
} }
@ -122,10 +123,10 @@ function SelectItemAll (doc: Doc | undefined, evt: Event): void {
evt.preventDefault() evt.preventDefault()
} }
const MoveUp = (doc: Doc | undefined, evt: Event): void => select(evt, -1, doc, 'vertical') const MoveUp = (doc: Doc | undefined, evt: Event): void => select(evt, -1, $focusStore.focus, 'vertical')
const MoveDown = (doc: Doc | undefined, evt: Event): void => select(evt, 1, doc, 'vertical') const MoveDown = (doc: Doc | undefined, evt: Event): void => select(evt, 1, $focusStore.focus, 'vertical')
const MoveLeft = (doc: Doc | undefined, evt: Event): void => select(evt, -1, doc, 'horizontal') const MoveLeft = (doc: Doc | undefined, evt: Event): void => select(evt, -1, $focusStore.focus, 'horizontal')
const MoveRight = (doc: Doc | undefined, evt: Event): void => select(evt, 1, doc, 'horizontal') const MoveRight = (doc: Doc | undefined, evt: Event): void => select(evt, 1, $focusStore.focus, 'horizontal')
function ShowActions (doc: Doc | Doc[] | undefined, evt: Event): void { function ShowActions (doc: Doc | Doc[] | undefined, evt: Event): void {
evt.preventDefault() evt.preventDefault()
@ -143,9 +144,17 @@ function ShowPreview (doc: Doc | undefined, evt: Event): void {
evt.preventDefault() evt.preventDefault()
} }
function Open (doc: Doc, evt: Event): void { function Open (
doc: Doc,
evt: Event,
props:
| {
component?: AnyComponent
}
| undefined
): void {
evt.preventDefault() evt.preventDefault()
showPanel(view.component.EditDoc, doc._id, Hierarchy.mixinOrClass(doc), 'content') showPanel(props?.component ?? view.component.EditDoc, doc._id, Hierarchy.mixinOrClass(doc), 'content')
} }
/** /**
@ -319,7 +328,7 @@ function ValueSelector (
doc: Doc | Doc[], doc: Doc | Doc[],
evt: Event, evt: Event,
props: { props: {
action: Action actionPopup: AnyComponent
attribute: string attribute: string
@ -340,9 +349,7 @@ function ValueSelector (
placeholder?: IntlString placeholder?: IntlString
} }
): void { ): void {
if (props.action.actionPopup !== undefined) { showPopup(view.component.ValueSelector, { ...props, value: doc, width: 'large' }, 'top')
showPopup(props.action.actionPopup, { ...props, ...props.action.actionProps, value: doc, width: 'large' }, 'top')
}
} }
async function getPopupAlignment ( async function getPopupAlignment (

View File

@ -148,9 +148,6 @@ export function filterActions (
if (role < AccountRole.Maintainer && action.secured === true) { if (role < AccountRole.Maintainer && action.secured === true) {
continue continue
} }
if (action.override !== undefined) {
overrideRemove.push(...action.override)
}
if (action.query !== undefined) { if (action.query !== undefined) {
const r = matchQuery([doc], action.query, doc._class, hierarchy) const r = matchQuery([doc], action.query, doc._class, hierarchy)
if (r.length === 0) { if (r.length === 0) {
@ -161,6 +158,9 @@ export function filterActions (
(hierarchy.isDerived(doc._class, action.target) && client.getHierarchy().isDerived(action.target, derived)) || (hierarchy.isDerived(doc._class, action.target) && client.getHierarchy().isDerived(action.target, derived)) ||
(hierarchy.isMixin(action.target) && hierarchy.hasMixin(doc, action.target)) (hierarchy.isMixin(action.target) && hierarchy.hasMixin(doc, action.target))
) { ) {
if (action.override !== undefined) {
overrideRemove.push(...action.override)
}
result.push(action) result.push(action)
} }
} }

View File

@ -38,6 +38,9 @@
}) })
let lastKey: KeyboardEvent | undefined let lastKey: KeyboardEvent | undefined
let sequences: Action<Doc, Record<string, any>>[] = []
let delayedAction: (() => Promise<void>) | undefined
let timer: number | undefined
async function getCurrentActions ( async function getCurrentActions (
context: { context: {
@ -70,23 +73,39 @@
) )
} }
function m (s1: string, s2: string): boolean { function m (s1: string, s2: string): boolean {
return s1 === s2.toLowerCase() return s1 === s2
} }
function matchKey (key: KeyboardEvent, pattern: string, lastKey?: KeyboardEvent): boolean {
function findKeySequence (pattern: string, key: KeyboardEvent): boolean {
const lc = pattern.toLowerCase()
const pfp = (keyPrefix(key) + key.key).toLowerCase()
const pfp2 = (keyPrefix(key) + key.code).toLowerCase()
return lc.startsWith(`${pfp}->`) || lc.startsWith(`${pfp2}->`)
}
function getSequences (
key: KeyboardEvent,
currentActions: Action<Doc, Record<string, any>>[]
): Action<Doc, Record<string, any>>[] {
const res = currentActions.filter((p) => p.keyBinding?.find((p) => findKeySequence(p, key)))
return res
}
function matchKeySequence (key: KeyboardEvent, pattern: string, lastKey: KeyboardEvent): boolean {
const fp = (keyPrefix(key) + key.key).toLowerCase() const fp = (keyPrefix(key) + key.key).toLowerCase()
const fp2 = (keyPrefix(key) + key.code).toLowerCase() const fp2 = (keyPrefix(key) + key.code).toLowerCase()
const lc = pattern.toLowerCase() const lc = pattern.toLowerCase()
if (m(fp, lc) || m(fp2, lc)) { const pfp = (keyPrefix(lastKey) + lastKey.key).toLowerCase()
return true const pfp2 = (keyPrefix(lastKey) + lastKey.code).toLowerCase()
}
if (lastKey !== undefined) {
// Check with last key
const pfp = (keyPrefix(key) + lastKey.key).toLowerCase()
const pfp2 = (keyPrefix(key) + lastKey.code).toLowerCase()
return m(`${pfp}->${fp}`, lc) || m(`${pfp2}->${fp}`, lc) || m(`${pfp}->${fp2}`, lc) || m(`${pfp2}->${fp2}`, lc) return m(`${pfp}->${fp}`, lc) || m(`${pfp2}->${fp}`, lc) || m(`${pfp}->${fp2}`, lc) || m(`${pfp2}->${fp2}`, lc)
} }
return false
function matchKey (key: KeyboardEvent, pattern: string): boolean {
const fp = (keyPrefix(key) + key.key).toLowerCase()
const fp2 = (keyPrefix(key) + key.code).toLowerCase()
const lc = pattern.toLowerCase()
return m(fp, lc) || m(fp2, lc)
} }
async function handleKeys (evt: KeyboardEvent): Promise<void> { async function handleKeys (evt: KeyboardEvent): Promise<void> {
@ -114,6 +133,7 @@
if (ctx.mode === 'none') { if (ctx.mode === 'none') {
return return
} }
clearTimeout(timer)
const docs = getSelection($focusStore, $selectionStore) const docs = getSelection($focusStore, $selectionStore)
@ -121,22 +141,56 @@
// Retrieve actual list of actions for input context // Retrieve actual list of actions for input context
currentActions = await getContextActions(client, docs, { ...ctx, mode: 'input' }) currentActions = await getContextActions(client, docs, { ...ctx, mode: 'input' })
} }
for (const a of currentActions) { currentActions = currentActions.filter((p) => p.keyBinding !== undefined && p.keyBinding.length > 0)
// TODO: Handle multiple keys here if (lastKey !== undefined) {
if (a.keyBinding?.find((it) => matchKey(evt, it, lastKey)) !== undefined) { for (const a of sequences) {
lastKey = undefined // TODO: Handle multiple keys here
const action = await getResource(a.action) if (a.keyBinding?.find((it) => (lastKey ? matchKeySequence(evt, it, lastKey) : false)) !== undefined) {
if (action !== undefined) { const action = await getResource(a.action)
await action($focusStore.focus, evt, a.actionProps) if (action !== undefined) {
sequences = []
lastKey = undefined
delayedAction = undefined
return await action(docs, evt, a.actionProps)
}
} }
} }
} }
sequences = getSequences(evt, currentActions)
let found = false
for (const a of currentActions) {
// TODO: Handle multiple keys here
if (a.keyBinding?.find((it) => matchKey(evt, it)) !== undefined) {
const action = await getResource(a.action)
if (action !== undefined) {
if (sequences.length === 0) {
lastKey = undefined
sequences = []
delayedAction = undefined
return await action(docs, evt, a.actionProps)
} else {
delayedAction = async () => await action(docs, evt, a.actionProps)
found = true
}
}
}
}
if (!found && delayedAction) {
delayedAction()
delayedAction = undefined
}
lastKey = evt lastKey = evt
setTimeout(() => { timer = setTimeout(() => {
lastKey = undefined lastKey = undefined
}, 500) sequences = []
if (delayedAction !== undefined) {
delayedAction()
delayedAction = undefined
}
}, 300)
} }
let presenter: AnyComponent | undefined let presenter: AnyComponent | undefined

View File

@ -147,11 +147,6 @@
key.stopPropagation() key.stopPropagation()
handleSelection(key, selection) handleSelection(key, selection)
} }
if (key.code === 'Escape') {
key.preventDefault()
key.stopPropagation()
closePopup()
}
} }
function formatKey (key: string): string[][] { function formatKey (key: string): string[][] {
const thens = key.split('->') const thens = key.split('->')

View File

@ -33,6 +33,7 @@
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
const changeStatus = async (newStatus: any) => { const changeStatus = async (newStatus: any) => {
console.log('CHANGE VALUE', newStatus)
if (newStatus === '#null') { if (newStatus === '#null') {
newStatus = null newStatus = null
return return
@ -118,7 +119,10 @@
{#if values} {#if values}
<SelectPopup <SelectPopup
value={valuesToShow} value={valuesToShow}
on:close={(evt) => changeStatus(evt.detail)} on:close={(evt) => {
console.log(evt)
changeStatus(evt.detail)
}}
placeholder={placeholder ?? view.string.Filter} placeholder={placeholder ?? view.string.Filter}
searchable searchable
{width} {width}
@ -133,7 +137,10 @@
{searchField} {searchField}
allowDeselect={true} allowDeselect={true}
selected={current} selected={current}
on:close={(evt) => changeStatus(evt.detail === null ? null : evt.detail?._id)} on:close={(evt) => {
console.log(evt)
changeStatus(evt.detail === null ? null : evt.detail?._id)
}}
placeholder={placeholder ?? view.string.Filter} placeholder={placeholder ?? view.string.Filter}
{width} {width}
{size} {size}