@@ -3,12 +3,12 @@ q-card.transparent(style='min-width: 45vw; max-width: 90vw')
33 q-toolbar.bg-secondary.text-white ( dense flat style ='height: 32px;' )
44 q-toolbar-title
55 span {{ title }}
6- small( v-if ='columnExists (initialFilter.field || "")' )
6+ small( v-if ='isRecognizedFilterField (initialFilter.field || "")' )
77 i( v-if ='initialFilter.label' ) <{{ initialFilter.label }}>
88 q-chip(
99 @click ='copy(initialFilter.field || "")'
1010 v-if ='initialFilter && initialFilter.field'
11- :color ='$q.dark.isActive ? "amber-9" : "amber-3"'
11+ :color ='isRecognizedFilterField(initialFilter.field || "") ? ( $q.dark.isActive ? "grey-9" : "grey-3") : ($q.dark.isActive ? " amber-9" : "amber-3") '
1212 text-color ='black'
1313 size ='xs'
1414 class ='q-ml-sm'
@@ -48,11 +48,36 @@ q-card.transparent(style='min-width: 45vw; max-width: 90vw')
4848 //- pre(v-html='JSON.stringify(scope)')
4949 q-chip(
5050 style ='overflow-x: hidden;'
51- :class ="[!columnExists (scope.opt.name || scope.opt) ? 'text-black' : '']"
52- :color ="!columnExists (scope.opt.name || scope.opt) ? ($q.dark.isActive ? 'amber-9' : 'amber-3') : ''"
51+ :class ="[!isRecognizedFilterField (scope.opt.name || scope.opt) ? 'text-black' : '']"
52+ :color ="!isRecognizedFilterField (scope.opt.name || scope.opt) ? ($q.dark.isActive ? 'amber-9' : 'amber-3') : ''"
5353 dense
5454 )
55- | {{ scope.opt.label || scope.opt.name || scope.opt }}
55+ template( v-if ='isCustomFilterFieldOption(scope.opt)' )
56+ | {{ scope.opt.name || scope.opt }}
57+ template( v-else )
58+ | {{ scope.opt.label || scope.opt.name || scope.opt }}
59+ small.filter-field-path ( v-if ='scope.opt.name && scope.opt.label !== scope.opt.name' ) ({{ scope.opt.name }})
60+ template( v-slot:option ="scope" )
61+ q-item( v-bind ="scope.itemProps" )
62+ q-item-section
63+ template( v-if ='isCustomFilterFieldOption(scope.opt)' )
64+ q-item-label {{ scope.opt.name }}
65+ template( v-else )
66+ q-item-label
67+ | {{ scope.opt.label }}
68+ small.filter-field-path ( v-if ='scope.opt.name && scope.opt.label !== scope.opt.name' ) ({{ scope.opt.name }})
69+ q-item-section( v-if ='isCustomFilterFieldOption(scope.opt) && customFilterFieldsStorageKey' side )
70+ q-btn(
71+ icon ='mdi-delete-outline'
72+ flat
73+ dense
74+ round
75+ size ='sm'
76+ color ='negative'
77+ aria-label ='Supprimer le champ personnalisé'
78+ @click.stop ='removeCustomFilterFieldOption(scope.opt.name)'
79+ )
80+ q-tooltip Supprimer ce champ personnalisé
5681 template( #no-option ="{ inputValue }" )
5782 q-item( clickable @click ="triggerEnter" )
5883 q-item-section
@@ -61,12 +86,12 @@ q-card.transparent(style='min-width: 45vw; max-width: 90vw')
6186 |
6287 code <{{ inputValue }}>
6388 q-select.col-1 (
64- v-if ='filter.key && !columnExists (filter.key || "")'
89+ v-if ='filter.key && !isRecognizedFilterField (filter.key || "")'
6590 style ='min-width: 120px'
6691 v-model ='fieldType'
6792 label ='Type'
6893 :options ='["text", "number", "date", "boolean", "array"]'
69- :readonly ='!filter.key || !!columnExists (filter.key || "")'
94+ :readonly ='!filter.key || !!isRecognizedFilterField (filter.key || "")'
7095 dense
7196 outlined
7297 options-dense
@@ -253,11 +278,21 @@ export default defineNuxtComponent({
253278 type: Object as () => InitialFilter ,
254279 default : () => ({}),
255280 },
281+ defaultFilterFieldPaths: {
282+ type: Array as PropType <readonly string []>,
283+ required: false ,
284+ default : () => [],
285+ },
286+ customFilterFieldsStorageKey: {
287+ type: String ,
288+ required: false ,
289+ default: undefined ,
290+ },
256291 },
257292 watch: {
258293 ' filter.key' : {
259294 handler() {
260- this .fieldType = this . columnsType . find (( col ) => col . name === this .filter .key )?. type || ' text '
295+ this .fieldType = resolveFilterFieldType ( this .filter .key || ' ' , this . columnsType )
261296 this .filter .operator = ' '
262297 },
263298 },
@@ -277,10 +312,10 @@ export default defineNuxtComponent({
277312 data() {
278313 return {
279314 filterOptions: [] as { name: string ; label: string }[],
315+ allFilterOptions: [] as { name: string ; label: string }[],
280316 }
281317 },
282318 setup({ columns , initialFilter , columnsType }) {
283- console .log (' initialFilter' , initialFilter )
284319 const { fieldTypes, comparatorTypes, writeFilter } = useFiltersQuery (ref (columns ), ref (columnsType ))
285320
286321 const detectInitialOperator = () => {
@@ -395,7 +430,7 @@ export default defineNuxtComponent({
395430 return this .fieldType || ' text'
396431 },
397432 availableComparators(): { label: string ; value: string ; prefix? : string ; suffix? : string ; multiplefields? : boolean }[] {
398- if (! this .columnExists (this .filter .key || ' ' ) && ! this .fieldType ) {
433+ if (! this .isRecognizedFilterField (this .filter .key || ' ' ) && ! this .fieldType ) {
399434 return this .comparatorTypes || []
400435 }
401436
@@ -418,33 +453,73 @@ export default defineNuxtComponent({
418453 },
419454 },
420455 methods: {
421- columnExists(field : string ) {
422- return this .columns .find ((col ) => col .name === field )
456+ isRecognizedFilterField(field : string ) {
457+ return isRecognizedFilterField (field , {
458+ columns: this .columns ,
459+ columnsType: this .columnsType ,
460+ defaultFilterFieldPaths: this .defaultFilterFieldPaths ,
461+ })
462+ },
463+ isCustomFilterFieldOption(option : { name? : string ; label? : string ; isCustom? : boolean } | string ) {
464+ if (typeof option === ' string' ) {
465+ return this .allFilterOptions .find ((item ) => item .name === option )?.isCustom === true
466+ }
467+
468+ if (option ?.isCustom ) return true
469+
470+ const name = option ?.name
471+ if (! name ) return false
472+
473+ return this .allFilterOptions .find ((item ) => item .name === name )?.isCustom === true
423474 },
424- mapAssign(col : { name: string ; label? : string }) {
425- return { name: col .name , label: col .label || col .name }
475+ rebuildFilterOptions() {
476+ this .allFilterOptions = buildFilterFieldOptions (this .columns , {
477+ extraDefaultPaths: this .defaultFilterFieldPaths ,
478+ customStorageKey: this .customFilterFieldsStorageKey ,
479+ })
480+ this .filterOptions = [... this .allFilterOptions ]
481+ },
482+ removeCustomFilterFieldOption(field : string ) {
483+ if (! this .customFilterFieldsStorageKey || ! field ) return
484+
485+ removeCustomFilterField (this .customFilterFieldsStorageKey , field )
486+
487+ if (this .filter .key === field ) {
488+ this .filter .key = undefined
489+ this .filter .operator = ' '
490+ this .filter .value = undefined
491+ }
492+
493+ this .rebuildFilterOptions ()
426494 },
427495 createValue(val : string , done : (newVal : string , mode : ' toggle' | ' add-unique' | ' add' ) => void ) {
428496 if (val .length > 0 ) {
429- if (this .filterOptions .find ((opt ) => opt .label === val )) {
430- // If the value already exists in options, select it
431- const existing = this .filterOptions .find ((opt ) => opt .label === val )!
497+ const existingByName = this .allFilterOptions .find ((opt ) => opt .name === val )
498+ const existingByLabel = this .allFilterOptions .find ((opt ) => opt .label === val )
499+ const existing = existingByName || existingByLabel
500+
501+ if (existing ) {
432502 done (existing .name , ' toggle' )
433- } else {
434- // Otherwise, add it to options and select it
435- const newOption = { name: val , label: val }
436- this .filterOptions . push ( newOption )
437- done ( val , ' add-unique ' )
503+ return
504+ }
505+
506+ if ( this .customFilterFieldsStorageKey ) {
507+ saveCustomFilterField ( this . customFilterFieldsStorageKey , val )
438508 }
509+
510+ this .rebuildFilterOptions ()
511+ done (val , ' add-unique' )
439512 }
440513 },
441514 filterFn(val , update ) {
442515 update (() => {
443516 if (val === ' ' ) {
444- this .filterOptions = this . columns . map ( this .mapAssign )
517+ this .filterOptions = [ ... this .allFilterOptions ]
445518 } else {
446519 const needle = val .toLowerCase ()
447- this .filterOptions = this .columns .map (this .mapAssign ).filter ((v ) => v .label .toLowerCase ().indexOf (needle ) > - 1 )
520+ this .filterOptions = this .allFilterOptions .filter (
521+ (option ) => option .label .toLowerCase ().includes (needle ) || option .name .toLowerCase ().includes (needle ),
522+ )
448523 }
449524 })
450525 },
@@ -484,12 +559,19 @@ export default defineNuxtComponent({
484559 },
485560 },
486561 mounted() {
487- this .filterOptions = this . columns . map ( this . mapAssign )
488- this .fieldType = this .columnsType . find (( col ) => col . name === this .filter . key )?. type
562+ this .rebuildFilterOptions ( )
563+ this .fieldType = resolveFilterFieldType ( this .filter . key || ' ' , this .columnsType )
489564
490- if (! this .fieldType && ! this .columnExists (this .filter .key || ' ' ) && this .filter .key ) {
565+ if (! this .fieldType && ! this .isRecognizedFilterField (this .filter .key || ' ' ) && this .filter .key ) {
491566 this .fieldType = this .comparator ?.type ? this .comparator .type [0 ] : ' text'
492567 }
493568 },
494569})
495570 </script >
571+
572+ <style lang="scss" scoped>
573+ .filter-field-path {
574+ font-size : 0.75em ;
575+ opacity : 0.72 ;
576+ }
577+ </style >
0 commit comments