Skip to content

Commit 47d67c3

Browse files
authored
feat(data-processing): add column header menu (#167)
The changes introduce a new `ColumnHeaderMenu` component that provides a dropdown menu with various options for the column header. This includes: - Sorting the column in ascending or descending order - Filtering the column - Grouping by the column - Applying common text transformations to the column cells (trim whitespace, collapse consecutive whitespace, convert to uppercase) The component is added to the `DataTabPanel` component, where it is displayed next to the column header if the column is not a primary key. This provides users with more control and flexibility over the data processing features.
1 parent cff016f commit 47d67c3

File tree

7 files changed

+137
-0
lines changed

7 files changed

+137
-0
lines changed

frontend/.eslintrc-auto-import.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"Label": true,
3737
"MaybeRef": true,
3838
"MaybeRefOrGetter": true,
39+
"MenuItem": true,
3940
"PageState": true,
4041
"ProjectColumn": true,
4142
"ProjectFile": true,
@@ -58,6 +59,7 @@
5859
"StatementSchema1": true,
5960
"StatementSchemaMapping": true,
6061
"TermsSchemaMapping": true,
62+
"TieredMenu": true,
6163
"TransformationFunction": true,
6264
"TransformationParameter": true,
6365
"TransformationRule": true,

frontend/auto-imports.d.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ declare global {
99
const ApiKey: typeof import('./src/core/plugins/api')['ApiKey']
1010
const ApiPlugin: typeof import('./src/core/plugins/api')['ApiPlugin']
1111
const EffectScope: typeof import('vue')['EffectScope']
12+
const MenuItem: typeof import('primevue/menuitem')['MenuItem']
1213
const ValidationMessages: typeof import('./src/shared/types/wikibase-schema')['ValidationMessages']
1314
const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate']
1415
const api: typeof import('./src/core/plugins/api')['api']
@@ -359,9 +360,15 @@ declare global {
359360
export type { FileUploadUploaderEvent } from 'primevue/fileupload'
360361
import('primevue/fileupload')
361362
// @ts-ignore
363+
export type { MenuItem } from 'primevue/menuitem'
364+
import('primevue/menuitem')
365+
// @ts-ignore
362366
export type { PageState } from 'primevue/paginator'
363367
import('primevue/paginator')
364368
// @ts-ignore
369+
export type { TieredMenu } from 'primevue/tieredmenu'
370+
import('primevue/tieredmenu')
371+
// @ts-ignore
365372
export type { UUID } from 'crypto'
366373
import('crypto')
367374
// @ts-ignore

frontend/components.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ declare module 'vue' {
1515
Chip: typeof import('primevue/chip')['default']
1616
ClaimEditor: typeof import('./src/features/wikibase-schema/components/ClaimEditor.vue')['default']
1717
Column: typeof import('primevue/column')['default']
18+
ColumnHeaderMenu: typeof import('./src/features/data-processing/components/ColumnHeaderMenu.vue')['default']
1819
ColumnPalette: typeof import('./src/features/data-processing/components/ColumnPalette.vue')['default']
1920
ConfirmDialog: typeof import('primevue/confirmdialog')['default']
2021
CreateProject: typeof import('./src/features/project-management/pages/CreateProject.vue')['default']
@@ -54,6 +55,7 @@ declare module 'vue' {
5455
Tag: typeof import('primevue/tag')['default']
5556
TermSection: typeof import('./src/features/wikibase-schema/components/TermSection.vue')['default']
5657
TermsEditor: typeof import('./src/features/wikibase-schema/components/TermsEditor.vue')['default']
58+
TieredMenu: typeof import('primevue/tieredmenu')['default']
5759
Toast: typeof import('primevue/toast')['default']
5860
ToggleSwitch: typeof import('primevue/toggleswitch')['default']
5961
ValidationDisplay: typeof import('./src/features/wikibase-schema/components/ValidationDisplay.vue')['default']

frontend/eslint.config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import js from '@eslint/js'
22
import prettierConfig from 'eslint-config-prettier'
33
import pluginVue from 'eslint-plugin-vue'
44
import { defineConfig } from 'eslint/config'
5+
import globals from 'globals'
56
import tseslint from 'typescript-eslint'
67
import vueParser from 'vue-eslint-parser'
78
import autoImportGlobals from './.eslintrc-auto-import.json'
@@ -35,6 +36,7 @@ export default defineConfig(
3536
extraFileExtensions: ['.vue'],
3637
},
3738
globals: {
39+
...globals.browser,
3840
...autoImportGlobals.globals,
3941
},
4042
},
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
<script setup lang="ts">
2+
const props = defineProps<{
3+
columnField: string
4+
columnHeader: string
5+
isPrimaryKey: boolean
6+
}>()
7+
8+
const menu = ref()
9+
const isOpen = ref(false)
10+
11+
const menuItems = ref<MenuItem[]>([
12+
{
13+
label: 'Sort',
14+
icon: 'pi pi-sort-amount-down',
15+
items: [
16+
{
17+
label: 'Ascending',
18+
icon: 'pi pi-sort-amount-up',
19+
command: () => console.log(`Sort ${props.columnHeader} Ascending`),
20+
},
21+
{
22+
label: 'Descending',
23+
icon: 'pi pi-sort-amount-down',
24+
command: () => console.log(`Sort ${props.columnHeader} Descending`),
25+
},
26+
],
27+
},
28+
{
29+
separator: true,
30+
},
31+
{
32+
label: 'Filter',
33+
icon: 'pi pi-filter',
34+
command: () => console.log(`Filter ${props.columnHeader}`),
35+
},
36+
{
37+
label: 'Group By',
38+
icon: 'pi pi-objects-column',
39+
command: () => console.log(`Group By ${props.columnHeader}`),
40+
},
41+
{
42+
separator: true,
43+
},
44+
{
45+
label: 'Edit cells',
46+
icon: 'pi pi-code',
47+
items: [
48+
{
49+
label: 'Common transforms',
50+
icon: 'pi pi-text',
51+
items: [
52+
{
53+
label: 'Trim leading and trailing whitespace',
54+
command: () => console.log(`Trim whitespace in ${props.columnHeader}`),
55+
},
56+
{
57+
label: 'Collapse consecutive whitespace',
58+
command: () => console.log(`Collapse consecutive whitespace in ${props.columnHeader}`),
59+
},
60+
{
61+
separator: true,
62+
},
63+
{
64+
label: 'To uppercase',
65+
command: () => console.log(`Transform ${props.columnHeader} to Uppercase`),
66+
},
67+
{
68+
label: 'To lowercase',
69+
command: () => console.log(`Transform ${props.columnHeader} to Lowercase`),
70+
},
71+
{
72+
label: 'To titlecase',
73+
command: () => console.log(`Transform ${props.columnHeader} to Title Case`),
74+
},
75+
],
76+
},
77+
{
78+
label: 'Replace',
79+
icon: 'pi pi-code',
80+
command: () => console.log(`Replace in ${props.columnHeader}`),
81+
},
82+
],
83+
},
84+
])
85+
</script>
86+
87+
<template>
88+
<div class="flex items-center">
89+
<Button
90+
v-if="!isPrimaryKey"
91+
type="button"
92+
icon="pi pi-chevron-down"
93+
class="p-button-rounded p-button-text p-button-sm"
94+
:aria-controls="`column-menu-${columnField}`"
95+
aria-haspopup="true"
96+
:aria-expanded="isOpen"
97+
@click="(event) => menu.toggle(event)"
98+
/>
99+
<TieredMenu
100+
:id="`column-menu-${columnField}`"
101+
ref="menu"
102+
:model="menuItems"
103+
popup
104+
@show="() => (isOpen = true)"
105+
@hide="() => (isOpen = false)"
106+
/>
107+
</div>
108+
</template>

frontend/src/features/data-processing/components/DataTabPanel.vue

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@ onUnmounted(() => clearProject())
5858
v-if="col.pk"
5959
class="pi pi-key text-blue-600"
6060
/>
61+
<ColumnHeaderMenu
62+
v-if="!col.pk"
63+
:column-field="col.field"
64+
:column-header="col.header"
65+
:is-primary-key="col.pk"
66+
/>
6167
<span>{{ col.header }}</span>
6268
</div>
6369
</template>

frontend/vite.config.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,21 @@ export default defineConfig({
3434
imports: ['FileUploadUploaderEvent'],
3535
type: true,
3636
},
37+
{
38+
from: 'primevue/menuitem',
39+
imports: ['MenuItem'],
40+
type: true,
41+
},
3742
{
3843
from: 'primevue/paginator',
3944
imports: ['PageState'],
4045
type: true,
4146
},
47+
{
48+
from: 'primevue/tieredmenu',
49+
imports: ['TieredMenu'],
50+
type: true,
51+
},
4252
{
4353
from: 'primevue/useconfirm',
4454
imports: ['useConfirm'],

0 commit comments

Comments
 (0)