[UBER-329] Remove "Project" filter from a single project view (#3345)

Signed-off-by: Sergei Ogorelkov <sergei.ogorelkov@icloud.com>
This commit is contained in:
Sergei Ogorelkov 2023-06-06 15:18:32 +04:00 committed by GitHub
parent 36b7014327
commit 34147e980f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 76 additions and 57 deletions

View File

@ -1467,7 +1467,8 @@ export function createModel (builder: Builder): void {
showNested: false showNested: false
} }
], ],
ignoreKeys: ['number', 'estimation', 'attachedTo'] ignoreKeys: ['number', 'estimation', 'attachedTo'],
getVisibleFilters: tracker.function.GetVisibleFilters
}) })
builder.mixin(tracker.class.IssueTemplate, core.class.Class, view.mixin.ClassFilters, { builder.mixin(tracker.class.IssueTemplate, core.class.Class, view.mixin.ClassFilters, {

View File

@ -52,6 +52,7 @@ import {
IgnoreActions, IgnoreActions,
InlineAttributEditor, InlineAttributEditor,
KeyBinding, KeyBinding,
KeyFilter,
KeyFilterPreset, KeyFilterPreset,
LinkPresenter, LinkPresenter,
LinkProvider, LinkProvider,
@ -153,6 +154,7 @@ export class TClassFilters extends TClass implements ClassFilters {
filters!: (string | KeyFilterPreset)[] filters!: (string | KeyFilterPreset)[]
ignoreKeys?: string[] | undefined ignoreKeys?: string[] | undefined
strict?: boolean | undefined strict?: boolean | undefined
getVisibleFilters?: Resource<(filters: KeyFilter[], space?: Ref<Space>) => Promise<KeyFilter[]>>
} }
@Mixin(view.mixin.AttributeFilter, core.class.Class) @Mixin(view.mixin.AttributeFilter, core.class.Class)

View File

@ -117,7 +117,8 @@ import {
issueStatusSort, issueStatusSort,
moveIssuesToAnotherMilestone, moveIssuesToAnotherMilestone,
milestoneSort, milestoneSort,
subIssueQuery subIssueQuery,
getVisibleFilters
} from './utils' } from './utils'
import { EmployeeAccount } from '@hcengineering/contact' import { EmployeeAccount } from '@hcengineering/contact'
@ -497,7 +498,8 @@ export default async (): Promise<Resources> => ({
SubIssueQuery: subIssueQuery, SubIssueQuery: subIssueQuery,
GetAllPriority: getAllPriority, GetAllPriority: getAllPriority,
GetAllComponents: getAllComponents, GetAllComponents: getAllComponents,
GetAllMilestones: getAllMilestones GetAllMilestones: getAllMilestones,
GetVisibleFilters: getVisibleFilters
}, },
actionImpl: { actionImpl: {
Move: move, Move: move,

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
// //
import { Client, Doc, Ref } from '@hcengineering/core' import { Client, Doc, Ref, Space } from '@hcengineering/core'
import type { Asset, IntlString, Metadata, Resource } from '@hcengineering/platform' import type { Asset, IntlString, Metadata, Resource } from '@hcengineering/platform'
import { mergeIds } from '@hcengineering/platform' import { mergeIds } from '@hcengineering/platform'
import { IssueDraft } from '@hcengineering/tracker' import { IssueDraft } from '@hcengineering/tracker'
@ -24,7 +24,8 @@ import {
SortFunc, SortFunc,
Viewlet, Viewlet,
ViewletDescriptor, ViewletDescriptor,
ViewQueryAction ViewQueryAction,
KeyFilter
} from '@hcengineering/view' } from '@hcengineering/view'
import tracker, { trackerId } from '../../tracker/lib' import tracker, { trackerId } from '../../tracker/lib'
@ -392,7 +393,8 @@ export default mergeIds(trackerId, tracker, {
SubIssueQuery: '' as ViewQueryAction, SubIssueQuery: '' as ViewQueryAction,
GetAllPriority: '' as GetAllValuesFunc, GetAllPriority: '' as GetAllValuesFunc,
GetAllComponents: '' as GetAllValuesFunc, GetAllComponents: '' as GetAllValuesFunc,
GetAllMilestones: '' as GetAllValuesFunc GetAllMilestones: '' as GetAllValuesFunc,
GetVisibleFilters: '' as Resource<(filters: KeyFilter[], space?: Ref<Space>) => Promise<KeyFilter[]>>
}, },
aggregation: { aggregation: {
CreateComponentAggregationManager: '' as CreateAggregationManagerFunc, CreateComponentAggregationManager: '' as CreateAggregationManagerFunc,

View File

@ -25,6 +25,7 @@ import core, {
DocumentUpdate, DocumentUpdate,
Ref, Ref,
SortingOrder, SortingOrder,
Space,
Status, Status,
StatusCategory, StatusCategory,
StatusManager, StatusManager,
@ -58,7 +59,7 @@ import {
MILLISECONDS_IN_WEEK, MILLISECONDS_IN_WEEK,
PaletteColorIndexes PaletteColorIndexes
} from '@hcengineering/ui' } from '@hcengineering/ui'
import { ViewletDescriptor } from '@hcengineering/view' import { KeyFilter, ViewletDescriptor } from '@hcengineering/view'
import { CategoryQuery, groupBy, ListSelectionProvider, SelectDirection } from '@hcengineering/view-resources' import { CategoryQuery, groupBy, ListSelectionProvider, SelectDirection } from '@hcengineering/view-resources'
import tracker from './plugin' import tracker from './plugin'
import { defaultPriorities, defaultMilestoneStatuses } from './types' import { defaultPriorities, defaultMilestoneStatuses } from './types'
@ -654,3 +655,8 @@ export const IssuePriorityColor = {
[IssuePriority.Medium]: PaletteColorIndexes.Ocean, [IssuePriority.Medium]: PaletteColorIndexes.Ocean,
[IssuePriority.Low]: PaletteColorIndexes.Cloud [IssuePriority.Low]: PaletteColorIndexes.Cloud
} }
export async function getVisibleFilters (filters: KeyFilter[], space?: Ref<Space>): Promise<KeyFilter[]> {
// Removes the "Project" filter if a specific space is provided
return space === undefined ? filters : filters.filter((f) => f.key !== 'space')
}

View File

@ -17,6 +17,7 @@
import { getClient } from '@hcengineering/presentation' import { getClient } from '@hcengineering/presentation'
import { Label, Scroller, Submenu, closePopup, closeTooltip, resizeObserver, showPopup } from '@hcengineering/ui' import { Label, Scroller, Submenu, closePopup, closeTooltip, resizeObserver, showPopup } from '@hcengineering/ui'
import { ClassFilters, Filter, KeyFilter, KeyFilterPreset } from '@hcengineering/view' import { ClassFilters, Filter, KeyFilter, KeyFilterPreset } from '@hcengineering/view'
import { getResource } from '@hcengineering/platform'
import { createEventDispatcher } from 'svelte' import { createEventDispatcher } from 'svelte'
import { FilterQuery, buildFilterKey } from '../../filter' import { FilterQuery, buildFilterKey } from '../../filter'
import view from '../../plugin' import view from '../../plugin'
@ -101,12 +102,12 @@
} }
} }
function getTypes (_class: Ref<Class<Doc>>, nestedFrom: KeyFilter | undefined): KeyFilter[] { async function getTypes (_class: Ref<Class<Doc>>, nestedFrom: KeyFilter | undefined): Promise<KeyFilter[]> {
let res: KeyFilter[] = [] let res: KeyFilter[] = []
if (nestedFrom !== undefined) { if (nestedFrom !== undefined) {
res = getNestedTypes(nestedFrom) res = await getNestedTypes(nestedFrom)
} else { } else {
res = getOwnTypes(_class) res = await getOwnTypes(_class)
} }
res.sort((a, b) => { res.sort((a, b) => {
if (a.group === b.group) return 0 if (a.group === b.group) return 0
@ -119,20 +120,23 @@
return res return res
} }
function getNestedTypes (type: KeyFilter): KeyFilter[] { async function getNestedTypes (type: KeyFilter): Promise<KeyFilter[]> {
const targetClass = (hierarchy.getAttribute(type._class, type.key).type as RefTo<Doc>).to const targetClass = (hierarchy.getAttribute(type._class, type.key).type as RefTo<Doc>).to
return getOwnTypes(targetClass) return await getOwnTypes(targetClass)
} }
function getOwnTypes (_class: Ref<Class<Doc>>): KeyFilter[] { async function getOwnTypes (_class: Ref<Class<Doc>>): Promise<KeyFilter[]> {
const mixin = hierarchy.classHierarchyMixin(_class, view.mixin.ClassFilters) const mixin = hierarchy.classHierarchyMixin(_class, view.mixin.ClassFilters)
if (mixin === undefined) return [] if (mixin === undefined) return []
_class = hierarchy.getBaseClass(_class) _class = hierarchy.getBaseClass(_class)
const result = getFilters(_class, mixin) const result = getFilters(_class, mixin)
const getVisibleFilters = mixin.getVisibleFilters
? await getResource(mixin.getVisibleFilters)
: async (filters: KeyFilter[]) => filters
if (mixin.strict) { if (mixin.strict) {
// Attributes not specified in "mixing.filters" are ignored // Attributes not specified in "mixin.filters" are ignored in "strict" mode
return result return await getVisibleFilters(result, space)
} }
const allAttributes = hierarchy.getAllAttributes(_class) const allAttributes = hierarchy.getAllAttributes(_class)
@ -166,7 +170,7 @@
} }
} }
return result return await getVisibleFilters(result, space)
} }
const actionElements: HTMLButtonElement[] = [] const actionElements: HTMLButtonElement[] = []
@ -260,8 +264,6 @@
const elements: HTMLElement[] = [] const elements: HTMLElement[] = []
$: types = getTypes(_class, nestedFrom)
function nextDiffCat (types: KeyFilter[], i: number): boolean { function nextDiffCat (types: KeyFilter[], i: number): boolean {
if (types[i + 1] === undefined) return false if (types[i + 1] === undefined) return false
return types[i].group !== types[i + 1].group return types[i].group !== types[i + 1].group
@ -289,45 +291,47 @@
</button> </button>
<div class="divider" /> <div class="divider" />
{/if} {/if}
{#each types as type, i} {#await getTypes(_class, nestedFrom) then types}
{#if filter === undefined && hasNested(type)} {#each types as type, i}
<Submenu {#if filter === undefined && hasNested(type)}
bind:element={elements[i]} <Submenu
on:keydown={(event) => keyDown(event, i)} bind:element={elements[i]}
on:mouseover={() => { on:keydown={(event) => keyDown(event, i)}
elements[i]?.focus() on:mouseover={() => {
}} elements[i]?.focus()
label={type.label} }}
props={{ label={type.label}
_class, props={{
space, _class,
index, space,
target, index,
onChange, target,
nestedFrom: type onChange,
}} nestedFrom: type
options={{ component: view.component.FilterTypePopup }} }}
withHover options={{ component: view.component.FilterTypePopup }}
/> withHover
{:else} />
<!-- svelte-ignore a11y-mouse-events-have-key-events --> {:else}
<button <!-- svelte-ignore a11y-mouse-events-have-key-events -->
class="menu-item" <button
on:keydown={(event) => keyDown(event, i)} class="menu-item"
on:mouseover={(event) => { on:keydown={(event) => keyDown(event, i)}
event.currentTarget.focus() on:mouseover={(event) => {
}} event.currentTarget.focus()
on:click={() => { }}
click(type) on:click={() => {
}} click(type)
> }}
<div class="overflow-label pr-1"><Label label={type.label} /></div> >
</button> <div class="overflow-label pr-1"><Label label={type.label} /></div>
{/if} </button>
{#if nextDiffCat(types, i)} {/if}
<div class="menu-separator" /> {#if nextDiffCat(types, i)}
{/if} <div class="menu-separator" />
{/each} {/if}
{/each}
{/await}
</Scroller> </Scroller>
<div class="menu-space" /> <div class="menu-space" />
</div> </div>

View File

@ -128,6 +128,8 @@ export interface ClassFilters extends Class<Doc> {
// Ignore attributes not specified in the "filters" array // Ignore attributes not specified in the "filters" array
strict?: boolean strict?: boolean
// Allows to filter out the provided keys, leaving only the necessary ones
getVisibleFilters?: Resource<(filters: KeyFilter[], space?: Ref<Space>) => Promise<KeyFilter[]>>
} }
/** /**