mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-07 18:27:44 +02:00
Save last filter (#2003)
Signed-off-by: Denis Bykhov <80476319+BykhovDenis@users.noreply.github.com>
This commit is contained in:
@@ -10,6 +10,7 @@ Platform:
|
||||
- Fix skills/labels selection and show real usage counter
|
||||
- Fix skills/labels activity
|
||||
- Project selector in issue list
|
||||
- Save last filter for page
|
||||
|
||||
HR:
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
"@anticrm/tags-resources": "~0.6.0",
|
||||
"@anticrm/ui": "~0.6.0",
|
||||
"@anticrm/model-core": "~0.6.0",
|
||||
"@anticrm/view": "~0.6.0",
|
||||
"@anticrm/model-view": "~0.6.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,4 +104,24 @@ export function createModel (builder: Builder): void {
|
||||
builder.mixin(tags.class.TagReference, core.class.Class, view.mixin.AttributeFilter, {
|
||||
component: tags.component.TagsFilter
|
||||
})
|
||||
|
||||
builder.createDoc(
|
||||
view.class.FilterMode,
|
||||
core.space.Model,
|
||||
{
|
||||
label: view.string.FilterIsEither,
|
||||
result: tags.function.FilterTagsInResult
|
||||
},
|
||||
tags.ids.FilterTagsIn
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
view.class.FilterMode,
|
||||
core.space.Model,
|
||||
{
|
||||
label: view.string.FilterIsNot,
|
||||
result: tags.function.FilterTagsNinResult
|
||||
},
|
||||
tags.ids.FilterTagsNin
|
||||
)
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ import { TagCategory, tagsId } from '@anticrm/tags'
|
||||
import tags from '@anticrm/tags-resources/src/plugin'
|
||||
import type { AnyComponent } from '@anticrm/ui'
|
||||
import { ViewAction } from '@anticrm/model-view'
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
import view from '@anticrm/view'
|
||||
|
||||
export default mergeIds(tagsId, tags, {
|
||||
// Without it, CLI version is failed with some svelte dependency exception.
|
||||
|
||||
@@ -29,6 +29,7 @@ import type {
|
||||
ClassFilters,
|
||||
CollectionEditor,
|
||||
CollectionPresenter,
|
||||
FilterMode,
|
||||
HTMLPresenter,
|
||||
IgnoreActions,
|
||||
KeyBinding,
|
||||
@@ -82,6 +83,12 @@ export function classPresenter (
|
||||
}
|
||||
}
|
||||
|
||||
@Model(view.class.FilterMode, core.class.Doc, DOMAIN_MODEL)
|
||||
export class TFilterMode extends TDoc implements FilterMode {
|
||||
label!: IntlString
|
||||
result!: Resource<(values: any[], onUpdate: () => void) => Promise<any>>
|
||||
}
|
||||
|
||||
@Mixin(view.mixin.ClassFilters, core.class.Class)
|
||||
export class TClassFilters extends TClass implements ClassFilters {
|
||||
filters!: (string | KeyFilter)[]
|
||||
@@ -245,6 +252,7 @@ export const actionTemplates = template({
|
||||
|
||||
export function createModel (builder: Builder): void {
|
||||
builder.createModel(
|
||||
TFilterMode,
|
||||
TClassFilters,
|
||||
TAttributeFilter,
|
||||
TAttributeEditor,
|
||||
@@ -510,6 +518,66 @@ export function createModel (builder: Builder): void {
|
||||
component: view.component.TimestampFilter
|
||||
})
|
||||
|
||||
builder.createDoc(
|
||||
view.class.FilterMode,
|
||||
core.space.Model,
|
||||
{
|
||||
label: view.string.FilterIsEither,
|
||||
result: view.function.FilterValueInResult
|
||||
},
|
||||
view.ids.FilterValueIn
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
view.class.FilterMode,
|
||||
core.space.Model,
|
||||
{
|
||||
label: view.string.FilterIsNot,
|
||||
result: view.function.FilterValueNinResult
|
||||
},
|
||||
view.ids.FilterValueNin
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
view.class.FilterMode,
|
||||
core.space.Model,
|
||||
{
|
||||
label: view.string.FilterIsEither,
|
||||
result: view.function.FilterObjectInResult
|
||||
},
|
||||
view.ids.FilterObjectIn
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
view.class.FilterMode,
|
||||
core.space.Model,
|
||||
{
|
||||
label: view.string.FilterIsNot,
|
||||
result: view.function.FilterObjectNinResult
|
||||
},
|
||||
view.ids.FilterObjectNin
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
view.class.FilterMode,
|
||||
core.space.Model,
|
||||
{
|
||||
label: view.string.Before,
|
||||
result: view.function.FilterBeforeResult
|
||||
},
|
||||
view.ids.FilterBefore
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
view.class.FilterMode,
|
||||
core.space.Model,
|
||||
{
|
||||
label: view.string.After,
|
||||
result: view.function.FilterAfterResult
|
||||
},
|
||||
view.ids.FilterAfter
|
||||
)
|
||||
|
||||
classPresenter(builder, core.class.EnumOf, view.component.StringPresenter, view.component.EnumEditor)
|
||||
}
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { Ref } from '@anticrm/core'
|
||||
import { IntlString, mergeIds } from '@anticrm/platform'
|
||||
import { ObjQueryType, Ref } from '@anticrm/core'
|
||||
import { IntlString, mergeIds, Resource } from '@anticrm/platform'
|
||||
import type { AnyComponent } from '@anticrm/ui'
|
||||
import { Action, ViewAction, viewId } from '@anticrm/view'
|
||||
import view from '@anticrm/view-resources/src/plugin'
|
||||
@@ -104,5 +104,25 @@ export default mergeIds(viewId, view, {
|
||||
Navigation: '' as IntlString,
|
||||
Editor: '' as IntlString,
|
||||
MarkdownFormatting: '' as IntlString
|
||||
},
|
||||
function: {
|
||||
FilterObjectInResult: '' as Resource<
|
||||
(values: any[], onUpdate: () => void, index: number) => Promise<ObjQueryType<any>>
|
||||
>,
|
||||
FilterObjectNinResult: '' as Resource<
|
||||
(values: any[], onUpdate: () => void, index: number) => Promise<ObjQueryType<any>>
|
||||
>,
|
||||
FilterValueInResult: '' as Resource<
|
||||
(values: any[], onUpdate: () => void, index: number) => Promise<ObjQueryType<any>>
|
||||
>,
|
||||
FilterValueNinResult: '' as Resource<
|
||||
(values: any[], onUpdate: () => void, index: number) => Promise<ObjQueryType<any>>
|
||||
>,
|
||||
FilterBeforeResult: '' as Resource<
|
||||
(values: any[], onUpdate: () => void, index: number) => Promise<ObjQueryType<any>>
|
||||
>,
|
||||
FilterAfterResult: '' as Resource<
|
||||
(values: any[], onUpdate: () => void, index: number) => Promise<ObjQueryType<any>>
|
||||
>
|
||||
}
|
||||
})
|
||||
|
||||
@@ -12,92 +12,29 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script context="module" lang="ts">
|
||||
const liveQueries: Map<number, LiveQuery> = new Map<number, LiveQuery>()
|
||||
const results: Map<number, Ref<Doc>[]> = new Map<number, Ref<Doc>[]>()
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { Class, Doc, FindResult, Ref } from '@anticrm/core'
|
||||
import { translate } from '@anticrm/platform'
|
||||
import presentation, { createQuery, getClient, LiveQuery } from '@anticrm/presentation'
|
||||
import presentation, { getClient } from '@anticrm/presentation'
|
||||
import { Button, CheckBox, getPlatformColor, Loading } from '@anticrm/ui'
|
||||
import { Filter } from '@anticrm/view'
|
||||
import view from '@anticrm/view-resources/src/plugin'
|
||||
import { createEventDispatcher, onMount } from 'svelte'
|
||||
import tags from '../plugin'
|
||||
import { TagCategory, TagElement } from '@anticrm/tags'
|
||||
import { TagFilterQuery } from '../utils'
|
||||
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let filter: Filter
|
||||
export let onChange: (e: Filter) => void
|
||||
filter.onRemove = () => {
|
||||
const lq = liveQueries.get(filter.index)
|
||||
lq?.unsubscribe()
|
||||
liveQueries.delete(filter.index)
|
||||
results.delete(filter.index)
|
||||
TagFilterQuery.remove(filter.index)
|
||||
}
|
||||
const lq = getLiveQuery(filter.index)
|
||||
const client = getClient()
|
||||
let selected: Ref<TagElement>[] = filter.value
|
||||
|
||||
function getLiveQuery (index: number): LiveQuery {
|
||||
let lq = liveQueries.get(index)
|
||||
if (lq === undefined) {
|
||||
lq = createQuery(true)
|
||||
liveQueries.set(index, lq)
|
||||
}
|
||||
return lq
|
||||
}
|
||||
|
||||
async function getRefs (res: Ref<TagElement>[], onUpdate: () => void): Promise<Ref<Doc>[]> {
|
||||
const promise = new Promise<Ref<Doc>[]>((resolve, reject) => {
|
||||
const refresh = lq.query(
|
||||
tags.class.TagReference,
|
||||
{
|
||||
tag: { $in: res }
|
||||
},
|
||||
(refs) => {
|
||||
const result = Array.from(new Set(refs.map((p) => p.attachedTo)))
|
||||
results.set(filter.index, result)
|
||||
resolve(result)
|
||||
onUpdate()
|
||||
}
|
||||
)
|
||||
|
||||
if (!refresh) {
|
||||
resolve(results.get(filter.index) ?? [])
|
||||
}
|
||||
})
|
||||
return promise
|
||||
}
|
||||
|
||||
filter.modes = [
|
||||
{
|
||||
label: view.string.FilterIs,
|
||||
isAvailable: (res: any[]) => res.length <= 1,
|
||||
result: async (res: any[], onUpdate: () => void) => {
|
||||
const result = await getRefs(res, onUpdate)
|
||||
return { $in: result }
|
||||
}
|
||||
},
|
||||
{
|
||||
label: view.string.FilterIsEither,
|
||||
isAvailable: (res: any[]) => res.length > 1,
|
||||
result: async (res: any[], onUpdate: () => void) => {
|
||||
const result = await getRefs(res, onUpdate)
|
||||
return { $in: result }
|
||||
}
|
||||
},
|
||||
{
|
||||
label: view.string.FilterIsNot,
|
||||
isAvailable: () => true,
|
||||
result: async (res: any[], onUpdate: () => void) => {
|
||||
const result = await getRefs(res, onUpdate)
|
||||
return { $nin: result }
|
||||
}
|
||||
}
|
||||
]
|
||||
filter.modes = [tags.ids.FilterTagsIn, tags.ids.FilterTagsNin]
|
||||
filter.mode = filter.mode === undefined ? filter.modes[0] : filter.mode
|
||||
|
||||
let categories: TagCategory[] = []
|
||||
let objects: TagElement[] = []
|
||||
@@ -123,12 +60,6 @@
|
||||
objectsPromise = undefined
|
||||
}
|
||||
|
||||
function checkMode () {
|
||||
if (filter.mode?.isAvailable(filter.value)) return
|
||||
const newMode = filter.modes.find((p: any) => p.isAvailable(filter.value))
|
||||
filter.mode = newMode !== undefined ? newMode : filter.mode
|
||||
}
|
||||
|
||||
let search: string = ''
|
||||
let phTraslate: string = ''
|
||||
let searchInput: HTMLInputElement
|
||||
@@ -239,7 +170,6 @@
|
||||
label={view.string.Apply}
|
||||
on:click={async () => {
|
||||
filter.value = selected
|
||||
checkMode()
|
||||
onChange(filter)
|
||||
dispatch('close')
|
||||
}}
|
||||
|
||||
@@ -29,6 +29,18 @@ import TagElementCountPresenter from './components/TagElementCountPresenter.svel
|
||||
import TagsFilter from './components/TagsFilter.svelte'
|
||||
import TagsAttributeEditor from './components/TagsAttributeEditor.svelte'
|
||||
import TagsEditorPopup from './components/TagsEditorPopup.svelte'
|
||||
import { ObjQueryType } from '@anticrm/core'
|
||||
import { TagFilterQuery } from './utils'
|
||||
|
||||
export async function tagsInResult (res: any[], onUpdate: () => void, index: number): Promise<ObjQueryType<any>> {
|
||||
const result = await TagFilterQuery.getRefs(res, onUpdate, index)
|
||||
return { $in: result }
|
||||
}
|
||||
|
||||
export async function tagsNinResult (res: any[], onUpdate: () => void, index: number): Promise<ObjQueryType<any>> {
|
||||
const result = await TagFilterQuery.getRefs(res, onUpdate, index)
|
||||
return { $nin: result }
|
||||
}
|
||||
|
||||
export default async (): Promise<Resources> => ({
|
||||
component: {
|
||||
@@ -51,5 +63,9 @@ export default async (): Promise<Resources> => ({
|
||||
Open: (value: TagElement, evt: MouseEvent) => {
|
||||
showPopup(EditTagElement, { value, keyTitle: '' }, eventToHTMLElement(evt))
|
||||
}
|
||||
},
|
||||
function: {
|
||||
FilterTagsInResult: tagsInResult,
|
||||
FilterTagsNinResult: tagsNinResult
|
||||
}
|
||||
})
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { IntlString, mergeIds } from '@anticrm/platform'
|
||||
import { ObjQueryType } from '@anticrm/core'
|
||||
import { IntlString, mergeIds, Resource } from '@anticrm/platform'
|
||||
import tags, { tagsId } from '@anticrm/tags'
|
||||
import { AnyComponent } from '@anticrm/ui'
|
||||
|
||||
@@ -45,5 +46,13 @@ export default mergeIds(tagsId, tags, {
|
||||
SelectAll: '' as IntlString,
|
||||
SelectNone: '' as IntlString,
|
||||
ApplyTags: '' as IntlString
|
||||
},
|
||||
function: {
|
||||
FilterTagsInResult: '' as Resource<
|
||||
(values: any[], onUpdate: () => void, index: number) => Promise<ObjQueryType<any>>
|
||||
>,
|
||||
FilterTagsNinResult: '' as Resource<
|
||||
(values: any[], onUpdate: () => void, index: number) => Promise<ObjQueryType<any>>
|
||||
>
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,8 +1,55 @@
|
||||
// Copyright © 2022 Hardcore Engineering Inc.
|
||||
|
||||
import { Doc, FindResult, Ref } from '@anticrm/core'
|
||||
import { createQuery, LiveQuery } from '@anticrm/presentation'
|
||||
import { TagElement, TagReference } from '@anticrm/tags'
|
||||
import tags from './plugin'
|
||||
|
||||
export function getTagStyle (color: string, selected = false): string {
|
||||
return `
|
||||
background: ${color + (selected ? 'ff' : '33')};
|
||||
border: 1px solid ${color + (selected ? 'ff' : '66')};
|
||||
`
|
||||
}
|
||||
|
||||
export const TagFilterQuery = {
|
||||
queries: new Map<number, LiveQuery>(),
|
||||
results: new Map<number, Array<Ref<Doc>>>(),
|
||||
|
||||
getLiveQuery (index: number): LiveQuery {
|
||||
const current = TagFilterQuery.queries.get(index)
|
||||
if (current !== undefined) return current
|
||||
const query = createQuery(true)
|
||||
this.queries.set(index, query)
|
||||
return query
|
||||
},
|
||||
|
||||
async getRefs (res: Array<Ref<TagElement>>, onUpdate: () => void, index: number): Promise<Array<Ref<Doc>>> {
|
||||
const lq = this.getLiveQuery(index)
|
||||
const promise = new Promise<Array<Ref<Doc>>>((resolve, reject) => {
|
||||
const refresh = lq.query(
|
||||
tags.class.TagReference,
|
||||
{
|
||||
tag: { $in: res }
|
||||
},
|
||||
(refs: FindResult<TagReference>) => {
|
||||
const result = Array.from(new Set(refs.map((p) => p.attachedTo)))
|
||||
TagFilterQuery.results.set(index, result)
|
||||
resolve(result)
|
||||
onUpdate()
|
||||
}
|
||||
)
|
||||
if (!refresh) {
|
||||
resolve(TagFilterQuery.results.get(index) ?? [])
|
||||
}
|
||||
})
|
||||
return await promise
|
||||
},
|
||||
|
||||
remove (index: number): void {
|
||||
const lq = this.queries.get(index)
|
||||
lq?.unsubscribe()
|
||||
this.queries.delete(index)
|
||||
this.results.delete(index)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
"dependencies": {
|
||||
"@anticrm/platform": "~0.6.6",
|
||||
"@anticrm/core": "~0.6.16",
|
||||
"@anticrm/view": "~0.6.0",
|
||||
"@anticrm/ui": "~0.6.0",
|
||||
"svelte": "^3.47"
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import type { Asset, Plugin } from '@anticrm/platform'
|
||||
import { plugin } from '@anticrm/platform'
|
||||
import { AnyComponent } from '@anticrm/ui'
|
||||
import { writable } from 'svelte/store'
|
||||
import { FilterMode } from '@anticrm/view'
|
||||
|
||||
/**
|
||||
* @public
|
||||
@@ -87,6 +88,10 @@ const tagsPlugin = plugin(tagsId, {
|
||||
},
|
||||
category: {
|
||||
NoCategory: '' as Ref<TagCategory>
|
||||
},
|
||||
ids: {
|
||||
FilterTagsIn: '' as Ref<FilterMode>,
|
||||
FilterTagsNin: '' as Ref<FilterMode>
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -36,9 +36,8 @@
|
||||
"CustomizeView": "Customize view",
|
||||
"Filter": "Filter",
|
||||
"ClearFilters": "Clear filters",
|
||||
"FilterIs": "is",
|
||||
"FilterIsNot": "is not",
|
||||
"FilterIsEither": "is either of",
|
||||
"FilterIsEither": "{value, plural, =1 {is} other {is either of}}",
|
||||
"FilterStatesCount": "{value, plural, =1 {1 state} other {# states}}",
|
||||
"Before": "Before",
|
||||
"After": "After",
|
||||
|
||||
@@ -34,9 +34,8 @@
|
||||
"CustomizeView": "Настроить отображение",
|
||||
"Filter": "Фильтр",
|
||||
"ClearFilters": "Очистить",
|
||||
"FilterIs": "равен",
|
||||
"FilterIsNot": "не равен",
|
||||
"FilterIsEither": "один из",
|
||||
"FilterIsEither": "{value, plural, =1 {равен} other {один из}}",
|
||||
"FilterStatesCount": "{value, plural, =1 {1 состоянию} other {# состояний}}",
|
||||
"Before": "До",
|
||||
"After": "После",
|
||||
|
||||
@@ -14,8 +14,9 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Class, Doc, DocumentQuery, Ref } from '@anticrm/core'
|
||||
import { getResource } from '@anticrm/platform'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import { Button, eventToHTMLElement, IconAdd, showPopup } from '@anticrm/ui'
|
||||
import { Button, eventToHTMLElement, getCurrentLocation, IconAdd, locationToUrl, showPopup } from '@anticrm/ui'
|
||||
import { Filter } from '@anticrm/view'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import view from '../../plugin'
|
||||
@@ -59,12 +60,7 @@
|
||||
)
|
||||
}
|
||||
|
||||
$: clear(_class)
|
||||
|
||||
function clear (_class: Ref<Class<Doc>>) {
|
||||
filters.map((p) => p.onRemove?.())
|
||||
filters = []
|
||||
}
|
||||
$: load(_class)
|
||||
|
||||
function remove (i: number) {
|
||||
filters[i]?.onRemove?.()
|
||||
@@ -72,13 +68,44 @@
|
||||
filters = filters
|
||||
}
|
||||
|
||||
$: saveFilters(filters)
|
||||
|
||||
function saveFilters (filters: Filter[]) {
|
||||
const loc = getCurrentLocation()
|
||||
const key = 'filter' + locationToUrl(loc)
|
||||
if (filters.length > 0) {
|
||||
localStorage.setItem(key, JSON.stringify(filters))
|
||||
} else {
|
||||
localStorage.removeItem(key)
|
||||
}
|
||||
}
|
||||
|
||||
function load (_class: Ref<Class<Doc>>) {
|
||||
const loc = getCurrentLocation()
|
||||
const key = 'filter' + locationToUrl(loc)
|
||||
const saved = localStorage.getItem(key)
|
||||
filters.map((p) => p.onRemove?.())
|
||||
if (saved !== null) {
|
||||
filters = JSON.parse(saved)
|
||||
} else {
|
||||
filters = []
|
||||
}
|
||||
}
|
||||
|
||||
async function makeQuery (query: DocumentQuery<Doc>, filters: Filter[]): Promise<void> {
|
||||
const newQuery = hierarchy.clone(query)
|
||||
for (let i = 0; i < filters.length; i++) {
|
||||
const filter = filters[i]
|
||||
const newValue = await filter.mode.result(filter.value, () => {
|
||||
makeQuery(query, filters)
|
||||
})
|
||||
const mode = await client.findOne(view.class.FilterMode, { _id: filter.mode })
|
||||
if (mode === undefined) continue
|
||||
const result = await getResource(mode.result)
|
||||
const newValue = await result(
|
||||
filter.value,
|
||||
() => {
|
||||
makeQuery(query, filters)
|
||||
},
|
||||
filter.index
|
||||
)
|
||||
if (newQuery[filter.key.key] === undefined) {
|
||||
newQuery[filter.key.key] = newValue
|
||||
} else {
|
||||
@@ -139,6 +166,7 @@
|
||||
{filter}
|
||||
on:change={() => {
|
||||
makeQuery(query, filters)
|
||||
saveFilters(filters)
|
||||
}}
|
||||
on:remove={() => {
|
||||
remove(i)
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { translate } from '@anticrm/platform'
|
||||
import { IntlString, translate } from '@anticrm/platform'
|
||||
import { Class, Doc, Ref, RefTo } from '@anticrm/core'
|
||||
import { eventToHTMLElement, IconClose, showPopup, Icon, Label } from '@anticrm/ui'
|
||||
import { Filter } from '@anticrm/view'
|
||||
@@ -27,6 +27,7 @@
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let filter: Filter
|
||||
|
||||
let label: IntlString | undefined
|
||||
let current = 0
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
@@ -47,13 +48,15 @@
|
||||
}
|
||||
$: if (filter) getLabel()
|
||||
|
||||
function toggle () {
|
||||
const modes = filter.modes.filter((p) => p.isAvailable(filter.value))
|
||||
async function toggle () {
|
||||
current++
|
||||
filter.mode = modes[current % modes.length]
|
||||
filter.mode = filter.modes[current % filter.modes.length]
|
||||
label = (await client.findOne(view.class.FilterMode, { _id: filter.mode }))?.label
|
||||
dispatch('change')
|
||||
}
|
||||
|
||||
client.findOne(view.class.FilterMode, { _id: filter.mode }).then((p) => (label = p?.label))
|
||||
|
||||
function onChange (e: Filter | undefined) {
|
||||
filter = filter
|
||||
dispatch('change')
|
||||
@@ -74,7 +77,9 @@
|
||||
<span><Label label={filter.key.label} /></span>
|
||||
</button>
|
||||
<button class="filter-button" on:click={toggle}>
|
||||
<span><Label label={filter.mode.label} /></span>
|
||||
{#if label}
|
||||
<span><Label {label} params={{ value: filter.value.length }} /></span>
|
||||
{/if}
|
||||
</button>
|
||||
<button
|
||||
class="filter-button"
|
||||
|
||||
@@ -35,29 +35,8 @@
|
||||
const key = { key: tkey }
|
||||
const lookup = buildConfigLookup(hierarchy, _class, [tkey])
|
||||
const promise = getPresenter(client, _class, key, key, lookup)
|
||||
filter.modes = [
|
||||
{
|
||||
label: view.string.FilterIs,
|
||||
isAvailable: (res: any[]) => res.length <= 1,
|
||||
result: async (res: any[]) => {
|
||||
return { $in: res }
|
||||
}
|
||||
},
|
||||
{
|
||||
label: view.string.FilterIsEither,
|
||||
isAvailable: (res: any[]) => res.length > 1,
|
||||
result: async (res: any[]) => {
|
||||
return { $in: res }
|
||||
}
|
||||
},
|
||||
{
|
||||
label: view.string.FilterIsNot,
|
||||
isAvailable: () => true,
|
||||
result: async (res: any[]) => {
|
||||
return { $nin: res }
|
||||
}
|
||||
}
|
||||
]
|
||||
filter.modes = [view.ids.FilterObjectIn, view.ids.FilterObjectNin]
|
||||
filter.mode = filter.mode === undefined ? filter.modes[0] : filter.mode
|
||||
|
||||
let values: (Doc | undefined | null)[] = []
|
||||
let objectsPromise: Promise<FindResult<Doc>> | undefined
|
||||
@@ -120,12 +99,6 @@
|
||||
return values.includes(value?._id ?? value)
|
||||
}
|
||||
|
||||
function checkMode () {
|
||||
if (filter.mode?.isAvailable(filter.value)) return
|
||||
const newMode = filter.modes.find((p) => p.isAvailable(filter.value))
|
||||
filter.mode = newMode !== undefined ? newMode : filter.mode
|
||||
}
|
||||
|
||||
function toggle (value: Doc | undefined | null): void {
|
||||
if (isSelected(value, filter.value)) {
|
||||
if (isState) {
|
||||
@@ -146,7 +119,6 @@
|
||||
filter.value = [...filter.value, undefined]
|
||||
}
|
||||
}
|
||||
checkMode()
|
||||
}
|
||||
|
||||
let search: string = ''
|
||||
|
||||
@@ -25,22 +25,7 @@
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
filter.modes = [
|
||||
{
|
||||
label: view.string.Before,
|
||||
isAvailable: (res: any[]) => true,
|
||||
result: async (res: any[]) => {
|
||||
return { $lt: res[0] }
|
||||
}
|
||||
},
|
||||
{
|
||||
label: view.string.After,
|
||||
isAvailable: () => true,
|
||||
result: async (res: any[]) => {
|
||||
return { $gt: res[0] }
|
||||
}
|
||||
}
|
||||
]
|
||||
filter.modes = [view.ids.FilterBefore, view.ids.FilterAfter]
|
||||
filter.mode = filter.mode === undefined ? filter.modes[0] : filter.mode
|
||||
|
||||
function click (value: number): void {
|
||||
|
||||
@@ -27,29 +27,8 @@
|
||||
export let filter: Filter
|
||||
export let onChange: (e: Filter) => void
|
||||
|
||||
filter.modes = [
|
||||
{
|
||||
label: view.string.FilterIs,
|
||||
isAvailable: (res: any[]) => res.length <= 1,
|
||||
result: async (res: [any, any[]][]) => {
|
||||
return { $in: res.map((p) => p[1]).flat() }
|
||||
}
|
||||
},
|
||||
{
|
||||
label: view.string.FilterIsEither,
|
||||
isAvailable: (res: any[]) => res.length > 1,
|
||||
result: async (res: [any, any[]][]) => {
|
||||
return { $in: res.map((p) => p[1]).flat() }
|
||||
}
|
||||
},
|
||||
{
|
||||
label: view.string.FilterIsNot,
|
||||
isAvailable: () => true,
|
||||
result: async (res: [any, any[]][]) => {
|
||||
return { $nin: res.map((p) => p[1]).flat() }
|
||||
}
|
||||
}
|
||||
]
|
||||
filter.modes = [view.ids.FilterValueIn, view.ids.FilterValueNin]
|
||||
filter.mode = filter.mode === undefined ? filter.modes[0] : filter.mode
|
||||
|
||||
const client = getClient()
|
||||
const key = { key: filter.key.key }
|
||||
@@ -109,12 +88,6 @@
|
||||
return values.has(value)
|
||||
}
|
||||
|
||||
function checkMode () {
|
||||
if (filter.mode?.isAvailable(filter.value)) return
|
||||
const newMode = filter.modes.find((p) => p.isAvailable(filter.value))
|
||||
filter.mode = newMode !== undefined ? newMode : filter.mode
|
||||
}
|
||||
|
||||
function toggle (value: any): void {
|
||||
if (isSelected(value, selectedValues)) {
|
||||
selectedValues.delete(value)
|
||||
@@ -197,7 +170,6 @@
|
||||
filter.value = Array.from(selectedValues.values()).map((p) => {
|
||||
return [p, Array.from(realValues.get(p) ?? [])]
|
||||
})
|
||||
checkMode()
|
||||
onChange(filter)
|
||||
dispatch('close')
|
||||
}}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { ObjQueryType } from '@anticrm/core'
|
||||
import { Resources } from '@anticrm/platform'
|
||||
import { getEventPopupPositionElement, PopupAlignment } from '@anticrm/ui'
|
||||
import { actionImpl } from './actionImpl'
|
||||
@@ -80,6 +81,30 @@ export {
|
||||
ClassAttributeBar
|
||||
}
|
||||
|
||||
export async function objectInResult (res: any[]): Promise<ObjQueryType<any>> {
|
||||
return { $in: res }
|
||||
}
|
||||
|
||||
export async function objectNinResult (res: any[]): Promise<ObjQueryType<any>> {
|
||||
return { $nin: res }
|
||||
}
|
||||
|
||||
export async function valueInResult (res: any[]): Promise<ObjQueryType<any>> {
|
||||
return { $in: res.map((p) => p[1]).flat() }
|
||||
}
|
||||
|
||||
export async function valueNinResult (res: any[]): Promise<ObjQueryType<any>> {
|
||||
return { $nin: res.map((p) => p[1]).flat() }
|
||||
}
|
||||
|
||||
export async function beforeResult (res: any[]): Promise<ObjQueryType<any>> {
|
||||
return { $lt: res[0] }
|
||||
}
|
||||
|
||||
export async function afterResult (res: any[]): Promise<ObjQueryType<any>> {
|
||||
return { $gt: res[0] }
|
||||
}
|
||||
|
||||
export default async (): Promise<Resources> => ({
|
||||
actionImpl: actionImpl,
|
||||
component: {
|
||||
@@ -112,5 +137,13 @@ export default async (): Promise<Resources> => ({
|
||||
},
|
||||
popup: {
|
||||
PositionElementAlignment
|
||||
},
|
||||
function: {
|
||||
FilterObjectInResult: objectInResult,
|
||||
FilterObjectNinResult: objectNinResult,
|
||||
FilterValueInResult: valueInResult,
|
||||
FilterValueNinResult: valueNinResult,
|
||||
FilterBeforeResult: beforeResult,
|
||||
FilterAfterResult: afterResult
|
||||
}
|
||||
})
|
||||
|
||||
@@ -39,7 +39,6 @@ export default mergeIds(viewId, view, {
|
||||
RestoreDefaults: '' as IntlString,
|
||||
Filter: '' as IntlString,
|
||||
ClearFilters: '' as IntlString,
|
||||
FilterIs: '' as IntlString,
|
||||
FilterIsNot: '' as IntlString,
|
||||
FilterIsEither: '' as IntlString,
|
||||
FilterStatesCount: '' as IntlString,
|
||||
|
||||
+16
-10
@@ -30,10 +30,8 @@ import type {
|
||||
Type,
|
||||
UXObject
|
||||
} from '@anticrm/core'
|
||||
import type { Asset, IntlString, Plugin, Resource, Status } from '@anticrm/platform'
|
||||
import { plugin } from '@anticrm/platform'
|
||||
import type { AnyComponent, AnySvelteComponent } from '@anticrm/ui'
|
||||
import { PopupAlignment, PopupPosAlignment } from '@anticrm/ui/src/types'
|
||||
import { Asset, IntlString, Plugin, plugin, Resource, Status } from '@anticrm/platform'
|
||||
import type { AnyComponent, AnySvelteComponent, PopupAlignment, PopupPosAlignment } from '@anticrm/ui'
|
||||
import type { Preference } from '@anticrm/preference'
|
||||
|
||||
/**
|
||||
@@ -49,10 +47,9 @@ export interface KeyFilter {
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface FilterMode {
|
||||
export interface FilterMode extends Doc {
|
||||
label: IntlString
|
||||
isAvailable: (values: any[]) => boolean
|
||||
result: (values: any[], onUpdate: () => void) => Promise<ObjQueryType<any>>
|
||||
result: Resource<(values: any[], onUpdate: () => void, index: number) => Promise<ObjQueryType<any>>>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,8 +57,8 @@ export interface FilterMode {
|
||||
*/
|
||||
export interface Filter {
|
||||
key: KeyFilter
|
||||
mode: FilterMode
|
||||
modes: FilterMode[]
|
||||
mode: Ref<FilterMode>
|
||||
modes: Ref<FilterMode>[]
|
||||
value: any[]
|
||||
index: number
|
||||
onRemove?: () => void
|
||||
@@ -387,7 +384,8 @@ const view = plugin(viewId, {
|
||||
Viewlet: '' as Ref<Class<Viewlet>>,
|
||||
Action: '' as Ref<Class<Action>>,
|
||||
ActionCategory: '' as Ref<Class<ActionCategory>>,
|
||||
LinkPresenter: '' as Ref<Class<LinkPresenter>>
|
||||
LinkPresenter: '' as Ref<Class<LinkPresenter>>,
|
||||
FilterMode: '' as Ref<Class<FilterMode>>
|
||||
},
|
||||
viewlet: {
|
||||
Table: '' as Ref<ViewletDescriptor>
|
||||
@@ -422,6 +420,14 @@ const view = plugin(viewId, {
|
||||
Editor: '' as Ref<ActionCategory>,
|
||||
MarkdownFormatting: '' as Ref<ActionCategory>
|
||||
},
|
||||
ids: {
|
||||
FilterObjectIn: '' as Ref<FilterMode>,
|
||||
FilterObjectNin: '' as Ref<FilterMode>,
|
||||
FilterValueIn: '' as Ref<FilterMode>,
|
||||
FilterValueNin: '' as Ref<FilterMode>,
|
||||
FilterBefore: '' as Ref<FilterMode>,
|
||||
FilterAfter: '' as Ref<FilterMode>
|
||||
},
|
||||
popup: {
|
||||
PositionElementAlignment: '' as Resource<(e?: Event) => PopupAlignment | undefined>
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user