mirror of
https://github.com/hcengineering/platform.git
synced 2025-06-02 13:52:40 +00:00
Nested filter fixes (#2123)
Signed-off-by: Denis Bykhov <80476319+BykhovDenis@users.noreply.github.com>
This commit is contained in:
parent
0cadc19331
commit
41ebded8c9
@ -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,
|
||||||
{
|
{
|
||||||
|
@ -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>
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -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,
|
||||||
|
@ -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} />
|
||||||
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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": "Добавить сотрудника"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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>
|
||||||
|
@ -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
|
||||||
|
@ -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) ?? [])
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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: {
|
||||||
|
Loading…
Reference in New Issue
Block a user