Nested filter fixes (#2123)

Signed-off-by: Denis Bykhov <80476319+BykhovDenis@users.noreply.github.com>
This commit is contained in:
Denis Bykhov 2022-06-22 11:58:28 +06:00 committed by GitHub
parent 0cadc19331
commit 41ebded8c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 97 additions and 95 deletions

View File

@ -120,25 +120,6 @@ export function createModel (builder: Builder): void {
hr.action.EditDepartment hr.action.EditDepartment
) )
createAction(
builder,
{
action: view.actionImpl.ShowPopup,
actionProps: {
component: hr.component.DepartmentStaff,
element: 'float'
},
label: hr.string.ShowEmployees,
icon: contact.icon.Person,
keyBinding: ['m'],
input: 'any',
category: hr.category.HR,
target: hr.class.Department,
context: { mode: 'context', application: hr.app.HR, group: 'top' }
},
hr.action.ShowEmployees
)
createAction( createAction(
builder, builder,
{ {

View File

@ -23,8 +23,7 @@ import { Action, ActionCategory } from '@anticrm/view'
export default mergeIds(hrId, hr, { export default mergeIds(hrId, hr, {
string: { string: {
HRApplication: '' as IntlString, HRApplication: '' as IntlString,
Departments: '' as IntlString, Departments: '' as IntlString
ShowEmployees: '' as IntlString
}, },
component: { component: {
Structure: '' as AnyComponent, Structure: '' as AnyComponent,
@ -37,7 +36,6 @@ export default mergeIds(hrId, hr, {
}, },
action: { action: {
EditDepartment: '' as Ref<Action>, EditDepartment: '' as Ref<Action>,
ShowEmployees: '' as Ref<Action>,
DeleteDepartment: '' as Ref<Action> DeleteDepartment: '' as Ref<Action>
} }
}) })

View File

@ -60,9 +60,6 @@ export default mergeIds(viewId, view, {
Open: '' as ViewAction Open: '' as ViewAction
}, },
component: { component: {
ObjectFilter: '' as AnyComponent,
ValueFilter: '' as AnyComponent,
TimestampFilter: '' as AnyComponent,
StringEditor: '' as AnyComponent, StringEditor: '' as AnyComponent,
StringEditorPopup: '' as AnyComponent, StringEditorPopup: '' as AnyComponent,
StringPresenter: '' as AnyComponent, StringPresenter: '' as AnyComponent,

View File

@ -28,13 +28,21 @@
export let label: IntlString | undefined = undefined export let label: IntlString | undefined = undefined
export let labelProps: Record<string, any> = {} export let labelProps: Record<string, any> = {}
export let withHover: boolean = false export let withHover: boolean = false
export let element: HTMLElement | undefined = undefined
let element: HTMLElement
let optionsMod: LabelAndProps let optionsMod: LabelAndProps
$: optionsMod = { component: options.component ?? Menu, props, element, kind: 'submenu' } $: optionsMod = { component: options.component ?? Menu, props, element, kind: 'submenu' }
</script> </script>
<div bind:this={element} use:tooltip={optionsMod} class="antiPopup-submenu" class:withHover tabindex={focusIndex}> <div
bind:this={element}
on:keydown
on:click
use:tooltip={optionsMod}
class="antiPopup-submenu"
class:withHover
tabindex={focusIndex}
>
{#if component} {#if component}
{#if typeof component === 'string'} {#if typeof component === 'string'}
<Component is={component} {props} /> <Component is={component} {props} />

View File

@ -4,7 +4,7 @@
"ParentDepartmentLabel": "Parent department", "ParentDepartmentLabel": "Parent department",
"Structure":"Structure", "Structure":"Structure",
"CreateDepartment": "Create department", "CreateDepartment": "Create department",
"CreateDepartmentLabel": "Create department", "CreateDepartmentLabel": "Department",
"DepartmentPlaceholder": "Department", "DepartmentPlaceholder": "Department",
"TeamLead": "Team lead", "TeamLead": "Team lead",
"UnAssignLead": "Unassign team lead", "UnAssignLead": "Unassign team lead",
@ -15,7 +15,6 @@
"MoveStaff": "Employee transfer", "MoveStaff": "Employee transfer",
"MoveStaffDescr": "Do you want to transfer employee from {current} to {department}", "MoveStaffDescr": "Do you want to transfer employee from {current} to {department}",
"Departments": "Departments", "Departments": "Departments",
"ShowEmployees": "Show employees",
"AddEmployee": "Add employee" "AddEmployee": "Add employee"
} }
} }

View File

@ -4,7 +4,7 @@
"ParentDepartmentLabel": "Родительский департамент", "ParentDepartmentLabel": "Родительский департамент",
"Structure":"Структура", "Structure":"Структура",
"CreateDepartment": "Создать департамент", "CreateDepartment": "Создать департамент",
"CreateDepartmentLabel": "Создать департамент", "CreateDepartmentLabel": "Департамент",
"DepartmentPlaceholder": "Департамент", "DepartmentPlaceholder": "Департамент",
"TeamLead": "Менеджер", "TeamLead": "Менеджер",
"UnAssignLead": "Отменить назначение менеджера", "UnAssignLead": "Отменить назначение менеджера",
@ -15,7 +15,6 @@
"MoveStaff": "Перевод сотрудника", "MoveStaff": "Перевод сотрудника",
"MoveStaffDescr": "Вы действительно хотите перевести сотрудника из {current} в {department}", "MoveStaffDescr": "Вы действительно хотите перевести сотрудника из {current} в {department}",
"Departments": "Департаменты", "Departments": "Департаменты",
"ShowEmployees": "Просмотреть сотрудников",
"AddEmployee": "Добавить сотрудника" "AddEmployee": "Добавить сотрудника"
} }
} }

View File

@ -13,11 +13,12 @@
// limitations under the License. // limitations under the License.
--> -->
<script lang="ts"> <script lang="ts">
import core, { AnyAttribute, ArrOf, AttachedDoc, Class, Collection, Doc, Ref, Type } from '@anticrm/core' import core, { AnyAttribute, ArrOf, AttachedDoc, Class, Collection, Doc, Ref, RefTo, Type } from '@anticrm/core'
import { getClient } from '@anticrm/presentation' import { getClient } from '@anticrm/presentation'
import { Icon, Label, showPopup } from '@anticrm/ui' import { closePopup, closeTooltip, Icon, Label, showPopup, Submenu } from '@anticrm/ui'
import { Filter, KeyFilter } from '@anticrm/view' import { Filter, KeyFilter } from '@anticrm/view'
import { createEventDispatcher } from 'svelte' import { createEventDispatcher } from 'svelte'
import { FilterQuery } from '../../filter'
import view from '../../plugin' import view from '../../plugin'
export let _class: Ref<Class<Doc>> export let _class: Ref<Class<Doc>>
@ -106,7 +107,8 @@
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
function click (type: KeyFilter): void { function click (type: KeyFilter): void {
dispatch('close') closePopup()
closeTooltip()
showPopup( showPopup(
type.component, type.component,
@ -122,28 +124,78 @@
target target
) )
} }
function hasNested (type: KeyFilter): boolean {
const targetClass = (hierarchy.getAttribute(_class, type.key).type as RefTo<Doc>).to
const clazz = hierarchy.getClass(targetClass)
return hierarchy.hasMixin(clazz, view.mixin.ClassFilters)
}
function setNestedFilter (type: KeyFilter, e: Filter | undefined) {
const filter: Filter = {
value: [],
key: type,
index,
mode: view.filter.FilterNestedMatch,
modes: [view.filter.FilterNestedMatch, view.filter.FilterNestedDontMatch],
onRemove: () => {
FilterQuery.remove(index)
}
}
if (e === undefined || filter === undefined) return
filter.nested = e
filter.value = e.value
onChange(filter)
dispatch('close')
}
function getNestedProps (type: KeyFilter): any {
const targetClass = (hierarchy.getAttribute(_class, type.key).type as RefTo<Doc>).to
return {
_class: targetClass,
index: index,
target,
onChange: (e: Filter | undefined) => {
setNestedFilter(type, e)
}
}
}
</script> </script>
<div class="selectPopup"> <div class="selectPopup">
<div class="scroll"> <div class="scroll">
<div class="box"> <div class="box">
{#each getTypes(_class) as type, i} {#each getTypes(_class) as type, i}
<!-- svelte-ignore a11y-mouse-events-have-key-events --> {#if filter === undefined && type.component === view.component.ObjectFilter && hasNested(type)}
<button <Submenu
class="menu-item withIcon" on:keydown={(event) => keyDown(event, i)}
on:keydown={(event) => keyDown(event, i)} on:click={(event) => {
on:mouseover={(event) => { click(type)
event.currentTarget.focus() }}
}} icon={type.icon}
on:click={(event) => { label={type.label}
click(type) props={getNestedProps(type)}
}} options={{ component: view.component.FilterTypePopup }}
> withHover
{#if type.icon} />
<div class="icon"><Icon icon={type.icon} size={'small'} /></div> {:else}
{/if} <!-- svelte-ignore a11y-mouse-events-have-key-events -->
<div class="ml-3 pr-1"><Label label={type.label} /></div> <button
</button> class="menu-item withIcon"
on:keydown={(event) => keyDown(event, i)}
on:mouseover={(event) => {
event.currentTarget.focus()
}}
on:click={(event) => {
click(type)
}}
>
{#if type.icon}
<div class="icon mr-3"><Icon icon={type.icon} size={'small'} /></div>
{/if}
<div class="pr-1"><Label label={type.label} /></div>
</button>
{/if}
{/each} {/each}
</div> </div>
</div> </div>

View File

@ -16,16 +16,13 @@
import { Class, Doc, FindResult, getObjectValue, Ref, RefTo, SortingOrder } from '@anticrm/core' import { Class, Doc, FindResult, getObjectValue, Ref, RefTo, SortingOrder } from '@anticrm/core'
import { translate } from '@anticrm/platform' import { translate } from '@anticrm/platform'
import presentation, { getClient } from '@anticrm/presentation' import presentation, { getClient } from '@anticrm/presentation'
import ui, { Button, CheckBox, eventToHTMLElement, Label, Loading, showPopup } from '@anticrm/ui'
import { Filter } from '@anticrm/view'
import { onMount } from 'svelte'
import { buildConfigLookup, getPresenter } from '../../utils'
import view from '../../plugin'
import { createEventDispatcher } from 'svelte'
import task from '@anticrm/task'
import type { State } from '@anticrm/task' import type { State } from '@anticrm/task'
import FilterTypePopup from './FilterTypePopup.svelte' import task from '@anticrm/task'
import { FilterQuery } from '../../filter' import ui, { Button, CheckBox, Label, Loading } from '@anticrm/ui'
import { Filter } from '@anticrm/view'
import { createEventDispatcher, onMount } from 'svelte'
import view from '../../plugin'
import { buildConfigLookup, getPresenter } from '../../utils'
export let _class: Ref<Class<Doc>> export let _class: Ref<Class<Doc>>
export let filter: Filter export let filter: Filter
@ -136,42 +133,9 @@
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
getValues(search) getValues(search)
$: byCriteria = hierarchy.hasMixin(clazz, view.mixin.ClassFilters)
function setNestedFilter (e: Filter | undefined) {
if (e === undefined) return
filter.nested = e
filter.mode = view.filter.FilterNestedMatch
filter.modes = [view.filter.FilterNestedMatch, view.filter.FilterNestedDontMatch]
filter.value = e.value
filter.onRemove = () => {
FilterQuery.remove(filter.index)
}
onChange(filter)
dispatch('close')
}
function nestedFilter (e: MouseEvent) {
const target = eventToHTMLElement(e)
showPopup(
FilterTypePopup,
{
_class: targetClass,
target,
index: filter.index,
filter: filter.nested,
onChange: setNestedFilter
},
target
)
}
</script> </script>
<div class="selectPopup"> <div class="selectPopup">
{#if byCriteria}
<Button shape={'round'} label={view.string.MatchCriteria} on:click={nestedFilter} />
{/if}
{#if clazz.sortingKey} {#if clazz.sortingKey}
<div class="header"> <div class="header">
<input <input

View File

@ -56,7 +56,6 @@ export const FilterQuery = {
getLiveQuery (index: number): LiveQuery { getLiveQuery (index: number): LiveQuery {
const current = FilterQuery.queries.get(index) const current = FilterQuery.queries.get(index)
if (current !== undefined) return current if (current !== undefined) return current
console.error('create new query')
const query = createQuery(true) const query = createQuery(true)
this.queries.set(index, query) this.queries.set(index, query)
return query return query
@ -89,7 +88,6 @@ export async function getRefs (filter: Filter, onUpdate: () => void): Promise<Ar
onUpdate() onUpdate()
} }
) )
console.error(refresh)
if (!refresh) { if (!refresh) {
resolve(FilterQuery.results.get(filter.index) ?? []) resolve(FilterQuery.results.get(filter.index) ?? [])
} }

View File

@ -33,6 +33,7 @@ import FilterBar from './components/filter/FilterBar.svelte'
import ObjectFilter from './components/filter/ObjectFilter.svelte' import ObjectFilter from './components/filter/ObjectFilter.svelte'
import TimestampFilter from './components/filter/TimestampFilter.svelte' import TimestampFilter from './components/filter/TimestampFilter.svelte'
import ValueFilter from './components/filter/ValueFilter.svelte' import ValueFilter from './components/filter/ValueFilter.svelte'
import FilterTypePopup from './components/filter/FilterTypePopup.svelte'
import HTMLPresenter from './components/HTMLPresenter.svelte' import HTMLPresenter from './components/HTMLPresenter.svelte'
import IntlStringPresenter from './components/IntlStringPresenter.svelte' import IntlStringPresenter from './components/IntlStringPresenter.svelte'
import GithubPresenter from './components/linkPresenters/GithubPresenter.svelte' import GithubPresenter from './components/linkPresenters/GithubPresenter.svelte'
@ -129,7 +130,8 @@ export default async (): Promise<Resources> => ({
ActionsPopup, ActionsPopup,
StringEditorPopup: EditBoxPopup, StringEditorPopup: EditBoxPopup,
BooleanTruePresenter, BooleanTruePresenter,
EnumEditor EnumEditor,
FilterTypePopup
}, },
popup: { popup: {
PositionElementAlignment PositionElementAlignment

View File

@ -20,6 +20,10 @@ import view, { viewId } from '@anticrm/view'
export default mergeIds(viewId, view, { export default mergeIds(viewId, view, {
component: { component: {
ObjectFilter: '' as AnyComponent,
ValueFilter: '' as AnyComponent,
TimestampFilter: '' as AnyComponent,
FilterTypePopup: '' as AnyComponent,
ActionsPopup: '' as AnyComponent ActionsPopup: '' as AnyComponent
}, },
string: { string: {