mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-22 11:01:54 +03:00
UBER-351 Use rank for table view configuration to allow reordering of columns (#3321)
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
parent
f40750ec95
commit
467c7736e6
@ -286,7 +286,8 @@ export function createModel (builder: Builder): void {
|
||||
}
|
||||
],
|
||||
configOptions: {
|
||||
hiddenKeys: ['name']
|
||||
hiddenKeys: ['name'],
|
||||
sortable: true
|
||||
}
|
||||
},
|
||||
contact.viewlet.TableContact
|
||||
|
@ -111,7 +111,10 @@ export function createModel (builder: Builder): void {
|
||||
{
|
||||
attachTo: inventory.class.Product,
|
||||
descriptor: view.viewlet.Table,
|
||||
config: ['', 'attachedTo', 'modifiedOn']
|
||||
config: ['', 'attachedTo', 'modifiedOn'],
|
||||
configOptions: {
|
||||
sortable: true
|
||||
}
|
||||
},
|
||||
inventory.viewlet.TableProduct
|
||||
)
|
||||
|
@ -209,7 +209,8 @@ export function createModel (builder: Builder): void {
|
||||
}
|
||||
],
|
||||
configOptions: {
|
||||
hiddenKeys: ['name']
|
||||
hiddenKeys: ['name'],
|
||||
sortable: true
|
||||
},
|
||||
options: {
|
||||
lookup: {
|
||||
@ -241,11 +242,6 @@ export function createModel (builder: Builder): void {
|
||||
'state',
|
||||
'doneState',
|
||||
'attachments',
|
||||
{
|
||||
key: '',
|
||||
presenter: tracker.component.RelatedIssueSelector,
|
||||
label: tracker.string.Relations
|
||||
},
|
||||
'comments',
|
||||
'modifiedOn',
|
||||
{
|
||||
@ -253,6 +249,9 @@ export function createModel (builder: Builder): void {
|
||||
sortingKey: ['$lookup.attachedTo.$lookup.channels.lastMessage', '$lookup.attachedTo.channels']
|
||||
}
|
||||
],
|
||||
configOptions: {
|
||||
sortable: true
|
||||
},
|
||||
options: {
|
||||
lookup: {
|
||||
_id: {
|
||||
@ -292,6 +291,7 @@ export function createModel (builder: Builder): void {
|
||||
descriptor: view.viewlet.List,
|
||||
configOptions: {
|
||||
hiddenKeys: ['title'],
|
||||
sortable: true,
|
||||
extraProps: {
|
||||
displayProps: {
|
||||
optional: true
|
||||
|
@ -400,7 +400,8 @@ export function createModel (builder: Builder): void {
|
||||
}
|
||||
],
|
||||
configOptions: {
|
||||
hiddenKeys: ['name']
|
||||
hiddenKeys: ['name'],
|
||||
sortable: true
|
||||
},
|
||||
options: {
|
||||
lookup: {
|
||||
@ -420,6 +421,9 @@ export function createModel (builder: Builder): void {
|
||||
attachTo: recruit.class.Applicant,
|
||||
descriptor: view.viewlet.Table,
|
||||
config: ['', '$lookup.attachedTo', 'state', 'doneState', 'modifiedOn'],
|
||||
configOptions: {
|
||||
sortable: true
|
||||
},
|
||||
variant: 'short'
|
||||
},
|
||||
recruit.viewlet.VacancyApplicationsShort
|
||||
@ -432,6 +436,9 @@ export function createModel (builder: Builder): void {
|
||||
attachTo: recruit.class.Applicant,
|
||||
descriptor: view.viewlet.Table,
|
||||
config: ['', '$lookup.space.name', '$lookup.space.$lookup.company', 'state', 'comments', 'doneState'],
|
||||
configOptions: {
|
||||
sortable: true
|
||||
},
|
||||
variant: 'embedded'
|
||||
},
|
||||
recruit.viewlet.VacancyApplicationsEmbeddeed
|
||||
@ -460,7 +467,8 @@ export function createModel (builder: Builder): void {
|
||||
}
|
||||
],
|
||||
configOptions: {
|
||||
hiddenKeys: ['name', 'space', 'modifiedOn']
|
||||
hiddenKeys: ['name', 'space', 'modifiedOn'],
|
||||
sortable: true
|
||||
}
|
||||
},
|
||||
recruit.viewlet.TableVacancy
|
||||
@ -492,7 +500,8 @@ export function createModel (builder: Builder): void {
|
||||
}
|
||||
],
|
||||
configOptions: {
|
||||
hiddenKeys: ['name', 'space', 'modifiedOn']
|
||||
hiddenKeys: ['name', 'space', 'modifiedOn'],
|
||||
sortable: true
|
||||
}
|
||||
},
|
||||
recruit.viewlet.TableVacancyList
|
||||
@ -533,7 +542,8 @@ export function createModel (builder: Builder): void {
|
||||
}
|
||||
],
|
||||
configOptions: {
|
||||
hiddenKeys: ['name', 'attachedTo']
|
||||
hiddenKeys: ['name', 'attachedTo'],
|
||||
sortable: true
|
||||
},
|
||||
options: {
|
||||
lookup: {
|
||||
@ -588,7 +598,8 @@ export function createModel (builder: Builder): void {
|
||||
}
|
||||
},
|
||||
configOptions: {
|
||||
hiddenKeys: ['name', 'attachedTo']
|
||||
hiddenKeys: ['name', 'attachedTo'],
|
||||
sortable: true
|
||||
},
|
||||
baseQuery: {
|
||||
doneState: null,
|
||||
@ -722,6 +733,7 @@ export function createModel (builder: Builder): void {
|
||||
},
|
||||
configOptions: {
|
||||
hiddenKeys: ['name', 'attachedTo'],
|
||||
sortable: true,
|
||||
extraProps: {
|
||||
displayProps: {
|
||||
optional: true
|
||||
|
@ -505,6 +505,7 @@ export function createModel (builder: Builder): void {
|
||||
'dueDate',
|
||||
'attachedTo'
|
||||
],
|
||||
sortable: true,
|
||||
extraProps: {
|
||||
displayProps: {
|
||||
optional: true
|
||||
@ -643,32 +644,51 @@ export function createModel (builder: Builder): void {
|
||||
descriptor: view.viewlet.List,
|
||||
viewOptions: subIssuesOptions,
|
||||
variant: 'subissue',
|
||||
configOptions: {
|
||||
sortable: true,
|
||||
hiddenKeys: ['priority', 'number', 'status', 'title', 'dueDate', 'milestone', 'estimation'],
|
||||
extraProps: {
|
||||
displayProps: {
|
||||
optional: true
|
||||
}
|
||||
}
|
||||
},
|
||||
config: [
|
||||
{
|
||||
key: '',
|
||||
label: tracker.string.Priority,
|
||||
presenter: tracker.component.PriorityEditor,
|
||||
props: { type: 'priority', kind: 'list', size: 'small' }
|
||||
},
|
||||
{
|
||||
key: '',
|
||||
label: tracker.string.Issue,
|
||||
presenter: tracker.component.IssuePresenter,
|
||||
props: { type: 'issue' },
|
||||
displayProps: { fixed: 'left' }
|
||||
},
|
||||
{
|
||||
key: '',
|
||||
label: tracker.string.Status,
|
||||
presenter: tracker.component.StatusEditor,
|
||||
props: { kind: 'list', size: 'small', justify: 'center' }
|
||||
},
|
||||
{ key: '', presenter: tracker.component.TitlePresenter, props: { shouldUseMargin: true, showParent: false } },
|
||||
{ key: '', presenter: tracker.component.SubIssuesSelector, props: {} },
|
||||
{
|
||||
key: '',
|
||||
label: tracker.string.Title,
|
||||
presenter: tracker.component.TitlePresenter,
|
||||
props: { shouldUseMargin: true, showParent: false }
|
||||
},
|
||||
{ key: '', label: tracker.string.SubIssues, presenter: tracker.component.SubIssuesSelector, props: {} },
|
||||
{
|
||||
key: '',
|
||||
label: tracker.string.DueDate,
|
||||
presenter: tracker.component.DueDatePresenter,
|
||||
props: { kind: 'list' }
|
||||
},
|
||||
{
|
||||
key: '',
|
||||
label: tracker.string.Milestone,
|
||||
presenter: tracker.component.MilestoneEditor,
|
||||
props: {
|
||||
kind: 'list',
|
||||
@ -683,6 +703,7 @@ export function createModel (builder: Builder): void {
|
||||
},
|
||||
{
|
||||
key: '',
|
||||
label: tracker.string.Estimation,
|
||||
presenter: tracker.component.EstimationEditor,
|
||||
props: { kind: 'list', size: 'small' },
|
||||
displayProps: { optional: true }
|
||||
@ -720,7 +741,8 @@ export function createModel (builder: Builder): void {
|
||||
},
|
||||
configOptions: {
|
||||
hiddenKeys: ['milestone', 'estimation', 'component', 'title', 'description'],
|
||||
extraProps: { displayProps: { optional: true } }
|
||||
extraProps: { displayProps: { optional: true } },
|
||||
sortable: true
|
||||
},
|
||||
config: [
|
||||
// { key: '', presenter: tracker.component.PriorityEditor, props: { kind: 'list', size: 'small' } },
|
||||
@ -1861,7 +1883,8 @@ export function createModel (builder: Builder): void {
|
||||
viewOptions: milestoneOptions,
|
||||
configOptions: {
|
||||
hiddenKeys: ['targetDate', 'label', 'description'],
|
||||
extraProps: { displayProps: { optional: true } }
|
||||
extraProps: { displayProps: { optional: true } },
|
||||
sortable: true
|
||||
},
|
||||
config: [
|
||||
{
|
||||
@ -1941,7 +1964,8 @@ export function createModel (builder: Builder): void {
|
||||
viewOptions: componentListViewOptions,
|
||||
configOptions: {
|
||||
hiddenKeys: ['label', 'description'],
|
||||
extraProps: { displayProps: { optional: true } }
|
||||
extraProps: { displayProps: { optional: true } },
|
||||
sortable: true
|
||||
},
|
||||
config: [
|
||||
{
|
||||
|
@ -67,7 +67,6 @@ import SetParentIssueActionPopup from './components/SetParentIssueActionPopup.sv
|
||||
import MilestoneDatePresenter from './components/milestones/MilestoneDatePresenter.svelte'
|
||||
import EditMilestone from './components/milestones/EditMilestone.svelte'
|
||||
import CreateIssueTemplate from './components/templates/CreateIssueTemplate.svelte'
|
||||
import Views from './components/views/Views.svelte'
|
||||
import Statuses from './components/workflow/Statuses.svelte'
|
||||
|
||||
import {
|
||||
@ -384,7 +383,6 @@ export default async (): Promise<Resources> => ({
|
||||
Inbox,
|
||||
MyIssues,
|
||||
Components,
|
||||
Views,
|
||||
IssuePresenter,
|
||||
ComponentPresenter,
|
||||
ComponentTitlePresenter,
|
||||
|
@ -17,7 +17,7 @@
|
||||
import { Asset, IntlString } from '@hcengineering/platform'
|
||||
import preferencePlugin from '@hcengineering/preference'
|
||||
import { createQuery, getAttributePresenterClass, getClient, hasResource } from '@hcengineering/presentation'
|
||||
import { Loading, ToggleWithLabel } from '@hcengineering/ui'
|
||||
import { Button, Loading, ToggleWithLabel } from '@hcengineering/ui'
|
||||
import { BuildModelKey, Viewlet, ViewletPreference } from '@hcengineering/view'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
import view from '../plugin'
|
||||
@ -36,8 +36,7 @@
|
||||
},
|
||||
(res) => {
|
||||
preference = res[0]
|
||||
attributes = getConfig(viewlet, preference)
|
||||
classes = groupByClasses(attributes)
|
||||
items = getConfig(viewlet, preference)
|
||||
loading = false
|
||||
},
|
||||
{ limit: 1 }
|
||||
@ -48,7 +47,7 @@
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
let attributes: AttributeConfig[] = []
|
||||
let items: AttributeConfig[] = []
|
||||
let loading = true
|
||||
|
||||
interface AttributeConfig {
|
||||
@ -57,6 +56,7 @@
|
||||
value: string | BuildModelKey
|
||||
_class: Ref<Class<Doc>>
|
||||
icon: Asset | undefined
|
||||
order?: number
|
||||
}
|
||||
|
||||
function getObjectConfig (_class: Ref<Class<Doc>>, param: string): AttributeConfig {
|
||||
@ -201,10 +201,7 @@
|
||||
}
|
||||
|
||||
async function save (): Promise<void> {
|
||||
const config = Array.from(classes.values())
|
||||
.flat()
|
||||
.filter((p) => p.enabled)
|
||||
.map((p) => p.value)
|
||||
const config = items.filter((p) => p.enabled).map((p) => p.value)
|
||||
if (preference !== undefined) {
|
||||
await client.update(preference, {
|
||||
config
|
||||
@ -217,29 +214,51 @@
|
||||
}
|
||||
}
|
||||
|
||||
// function restoreDefault (): void {
|
||||
// attributes = getConfig(viewlet, undefined)
|
||||
// classes = groupByClasses(attributes)
|
||||
// }
|
||||
function restoreDefault (): void {
|
||||
items = getConfig(viewlet, undefined)
|
||||
save()
|
||||
}
|
||||
|
||||
function setStatus (result: AttributeConfig[], preference: ViewletPreference): AttributeConfig[] {
|
||||
for (const key of result) {
|
||||
key.enabled = preference.config.findIndex((p) => deepEqual(p, key.value)) !== -1
|
||||
const index = preference.config.findIndex((p) => deepEqual(p, key.value))
|
||||
key.enabled = index !== -1
|
||||
key.order = index !== -1 ? index : undefined
|
||||
}
|
||||
result.sort((a, b) => {
|
||||
if (a.order === undefined && b.order === undefined) return 0
|
||||
if (a.order === undefined) return 1
|
||||
if (b.order === undefined) return -1
|
||||
return a.order - b.order
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
function groupByClasses (attributes: AttributeConfig[]): Map<Ref<Class<Doc>>, AttributeConfig[]> {
|
||||
const res = new Map()
|
||||
for (const attribute of attributes) {
|
||||
const arr = res.get(attribute._class) ?? []
|
||||
arr.push(attribute)
|
||||
res.set(attribute._class, arr)
|
||||
}
|
||||
return res
|
||||
function dragEnd () {
|
||||
selected = undefined
|
||||
save()
|
||||
}
|
||||
|
||||
let classes: Map<Ref<Class<Doc>>, AttributeConfig[]> = new Map()
|
||||
function dragOver (e: DragEvent, i: number) {
|
||||
const s = selected as number
|
||||
if (dragswap(e, i, s)) {
|
||||
;[items[i], items[s]] = [items[s], items[i]]
|
||||
selected = i
|
||||
}
|
||||
}
|
||||
|
||||
const elements: HTMLElement[] = []
|
||||
|
||||
function dragswap (ev: MouseEvent, i: number, s: number): boolean {
|
||||
if (i < s) {
|
||||
return ev.offsetY < elements[i].offsetHeight / 2
|
||||
} else if (i > s) {
|
||||
return ev.offsetY > elements[i].offsetHeight / 2
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
let selected: number | undefined
|
||||
</script>
|
||||
|
||||
<div class="selectPopup p-2">
|
||||
@ -247,23 +266,29 @@
|
||||
{#if loading}
|
||||
<Loading />
|
||||
{:else}
|
||||
{#each Array.from(classes.keys()) as _class, i}
|
||||
{@const items = classes.get(_class) ?? []}
|
||||
{#if i !== 0}
|
||||
<div class="menu-separator" />
|
||||
{/if}
|
||||
{#each items as item}
|
||||
<div class="item">
|
||||
<ToggleWithLabel
|
||||
on={item.enabled}
|
||||
label={item.label}
|
||||
on:change={(e) => {
|
||||
item.enabled = e.detail
|
||||
save()
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/each}
|
||||
<div class="flex-row-reverse">
|
||||
<Button on:click={restoreDefault} label={view.string.RestoreDefaults} size={'x-small'} kind={'link'} noFocus />
|
||||
</div>
|
||||
{#each items as item, i}
|
||||
<div
|
||||
class="item"
|
||||
bind:this={elements[i]}
|
||||
draggable={viewlet.configOptions?.sortable}
|
||||
on:dragstart={() => {
|
||||
selected = i
|
||||
}}
|
||||
on:dragover|preventDefault={(e) => dragOver(e, i)}
|
||||
on:dragend={dragEnd}
|
||||
>
|
||||
<ToggleWithLabel
|
||||
on={item.enabled}
|
||||
label={item.label}
|
||||
on:change={(e) => {
|
||||
item.enabled = e.detail
|
||||
save()
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
|
@ -329,6 +329,7 @@ export interface ViewletConfigOptions {
|
||||
hiddenKeys?: string[]
|
||||
strict?: boolean
|
||||
extraProps?: Omit<BuildModelKey, 'key'>
|
||||
sortable?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user