Skip to content

Commit

Permalink
use FilterMenu in ToolSearch and WorkflowBox
Browse files Browse the repository at this point in the history
Add functionailty for 3 types of menus. E.g.s:
- `HistoryPanel` has default `linked` menu
  i.e.: `filters` object is reactive to `filterText`
- `ToolSearch` has `separate` menu (not reactive,
  since `filterText` is for side panel search and menu
  is for advanced search in center panel)
- `WorkflowBox` has a `standalone` menu (no `filterText`
  • Loading branch information
ahmedhamidawan committed Aug 28, 2023
1 parent c1a9161 commit 2f8d07a
Show file tree
Hide file tree
Showing 13 changed files with 371 additions and 388 deletions.
146 changes: 62 additions & 84 deletions client/src/components/Common/FilterMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
import { capitalize, kebabCase } from "lodash";
import { computed, type Ref, ref, watch } from "vue";
import type { Alias, ValidFilter } from "@/utils/filtering";
import type Filtering from "@/utils/filtering";
import { getOperatorForAlias } from "@/utils/filtering";
import { type Alias, getOperatorForAlias } from "@/utils/filtering";
import FilterMenuRadio from "@/components/Common/FilterMenuRadio.vue";
import DelayedInput from "@/components/Common/DelayedInput.vue";
import FilterMenuBoolean from "@/components/Common/FilterMenuBoolean.vue";
interface BackendFilterError {
err_msg: string;
Expand All @@ -22,48 +22,46 @@ interface BackendFilterError {
const props = withDefaults(
defineProps<{
/** A name for the menu */
name?: string;
/** A placeholder for the main search input */
placeholder?: string;
/** The `Filtering` class to use */
filterClass: Filtering<any>;
filterText: string;
/** The current filter text in the main field */
filterText?: string;
/** Whether a help `<template>` has been provided for the `<slot>` */
hasHelp?: boolean;
/** Whether to replace default Cancel (toggle) button with Clear */
hasClearBtn?: boolean;
/** Triggers the loading icon */
loading?: boolean;
/** Default `linked`: filters react to current `filterText` */
menuType?: "linked" | "separate" | "standalone";
/** A `BackendFilterError` if provided */
searchError?: BackendFilterError;
/** Whether the advanced menu is currently expanded */
showAdvanced?: boolean;
}>(),
{
name: "Menu",
placeholder: "search for items",
filterText: "",
menuType: "linked",
showAdvanced: false,
searchError: undefined,
}
);
const emit = defineEmits<{
(e: "update:show-advanced", showAdvanced: boolean): void;
(e: "update:filter-text", filter: string): void;
(e: "show-help", filter: string, show: boolean): void;
(e: "update:show-advanced", showAdvanced: boolean): void;
(e: "on-search", filters: Record<string, string | boolean>, filterText?: string): void;
}>();
const defaultBoolOptions = [
{ text: "Yes", value: true },
{ text: "No", value: false },
];
const localFilterText = computed({
get: () => {
return props.filterText;
},
set: (newVal: any) => {
if (newVal !== props.filterText) {
updateFilterText(newVal as string);
}
},
});
const validFilters = computed(() => props.filterClass.validFilters);
const filters = computed(() => Object.fromEntries(props.filterClass.getFiltersForText(localFilterText.value)));
const filters = computed(() => Object.fromEntries(props.filterClass.getFiltersForText(props.filterText)));
const identifier = kebabCase(props.name);
Expand Down Expand Up @@ -145,11 +143,18 @@ function onOption(filter: string, value: any) {
}
function onSearch() {
onToggle();
if (props.menuType === "linked") {
onToggle();
}
Object.keys(dateFilters.value).forEach((key) => {
onOption(key, dateFilters.value[key]);
});
updateFilterText(props.filterClass.getFilterText(filters.value));
const newFilterText = props.filterClass.getFilterText(filters.value);
if (props.menuType !== "linked") {
emit("on-search", filters.value, newFilterText);
} else {
updateFilterText(newFilterText);
}
}
function onToggle() {
Expand All @@ -163,76 +168,49 @@ function updateFilterText(newFilterText: string) {

<template>
<div>
<b-input-group>
<b-form-input
:id="`${identifier}-filter`"
v-model="localFilterText"
size="sm"
:class="filterText && 'font-weight-bold'"
:placeholder="props.placeholder"
data-description="filter text input"
debounce="400"
@keyup.esc="updateFilterText('')" />
<b-input-group-append>
<b-button
:id="`${identifier}-advanced-filter-toggle`"
v-b-tooltip.hover
aria-haspopup="true"
size="sm"
:pressed="showAdvanced"
:variant="showAdvanced ? 'info' : 'secondary'"
data-description="show advanced filter toggle"
title="Show Advanced Filter"
aria-label="Show advanced filter"
@click="onToggle">
<icon v-if="showAdvanced" icon="angle-double-up" />
<icon v-else icon="angle-double-down" />
</b-button>
<b-button
v-b-tooltip.hover
aria-haspopup="true"
size="sm"
title="Clear Filters (esc)"
aria-label="Clear filters"
data-description="clear filters"
@click="updateFilterText('')">
<icon icon="times" />
</b-button>
</b-input-group-append>
</b-input-group>
<!-- eslint-disable-next-line vuejs-accessibility/no-static-element-interactions -->
<DelayedInput
v-if="props.menuType !== 'standalone'"
v-show="props.menuType == 'linked' || (props.menuType == 'separate' && !props.showAdvanced)"
:class="props.filterText && 'font-weight-bold'"
:query="props.filterText"
:delay="400"
:loading="props.loading"
:show-advanced="props.showAdvanced"
enable-advanced
:placeholder="props.placeholder"
@change="updateFilterText"
@onToggle="onToggle" />
<b-button
v-if="props.menuType == 'separate' && props.showAdvanced"
v-b-tooltip.hover.noninteractive
class="w-100"
aria-haspopup="true"
size="sm"
:pressed="props.showAdvanced"
:variant="props.showAdvanced ? 'info' : 'secondary'"
:title="`Toggle Advanced Search`"
data-description="reset query"
@click="onToggle">
<icon fixed-width icon="angle-double-up" />
</b-button>
<div
v-if="showAdvanced"
v-if="props.menuType == 'standalone' || props.showAdvanced"
class="mt-2"
data-description="advanced filters"
@keyup.enter="onSearch"
@keyup.esc="onToggle">
<div v-for="filter in Object.keys(validFilters)" :key="filter">
<span v-if="validFilters[filter]?.menuItem">
<!-- is a Radio filter -->
<span v-if="validFilters[filter]?.type == 'Radio'">
<!-- is a Boolean filter -->
<span v-if="validFilters[filter]?.type == Boolean">
<small>{{ validFilters[filter]?.placeholder }}:</small>
<FilterMenuRadio
v-if="validFilters[filter]?.options"
:filter="filter"
:options="validFilters[filter]?.options"
<FilterMenuBoolean
:name="filter"
:filter="validFilters[filter]"
:settings="filters"
@change="onOption" />
</span>

<!-- is a Boolean filter -->
<span v-else-if="validFilters[filter]?.type == Boolean">
<small>{{ validFilters[filter]?.placeholder }}:</small>
<b-form-group class="m-0">
<b-form-radio-group
v-model="filters[filter]"
:options="defaultBoolOptions"
size="sm"
buttons
:data-description="`filter ${filter}`" />
</b-form-group>
</span>

<!-- is a Ranged filter -->
<span v-else-if="validFilters[filter]?.isRangeInput" class="m-0">
<small>Filter by {{ validFilters[filter]?.placeholder }}:</small>
Expand Down Expand Up @@ -380,7 +358,7 @@ function updateFilterText(newFilterText: string) {
<icon icon="search" />
<span v-localize>Search</span>
</b-button>
<b-button size="sm" @click="onToggle">
<b-button v-if="props.menuType !== 'standalone'" size="sm" @click="onToggle">
<icon icon="redo" />
<span v-localize>Cancel</span>
</b-button>
Expand Down
52 changes: 52 additions & 0 deletions client/src/components/Common/FilterMenuBoolean.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<script setup lang="ts">
/**
* REFACTOR CODE HERE, `ANY` FILTER SHOULD BE SET TO UNDEFINED ...
*/
import { computed } from "vue";
const props = defineProps({
filter: { type: Object, required: true },
name: { type: String, required: true },
settings: { type: Object, required: true },
});
const boolType = computed(() => props.filter.boolType || "default");
const options =
boolType.value == "default"
? [
{ text: "Any", value: "any" },
{ text: "Yes", value: true },
{ text: "No", value: false },
]
: [
{ text: "Yes", value: true },
{ text: "No", value: "any" },
];
const emit = defineEmits<{
(e: "change", name: string, value: boolean | string | undefined): void;
}>();
const value = computed({
get: () => {
const value = props.settings[props.name];
return value !== undefined ? value : "any";
},
set: (newVal: boolean | string | undefined) => {
const value = newVal !== null ? newVal : "any";
emit("change", props.name, value);
},
});
</script>

<template>
<b-form-group class="m-0">
<b-form-radio-group
v-model="value"
:options="options"
size="sm"
buttons
:data-description="`filter ${props.name}`" />
</b-form-group>
</template>
42 changes: 0 additions & 42 deletions client/src/components/Common/FilterMenuRadio.vue

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
placeholder="search datasets"
:filter-class="filterClass"
:filter-text.sync="filterText"
:loading="loading"
:search-error="searchError"
:show-advanced.sync="showAdvanced" />
<section v-if="!showAdvanced">
Expand Down
17 changes: 6 additions & 11 deletions client/src/components/History/HistoryFilters.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,8 @@ import Filtering, { compare, contains, equals, expandNameTag, toBool, toDate } f

const excludeStates = ["empty", "failed", "upload"];
const states = Object.keys(STATES).filter((state) => !excludeStates.includes(state));
const options = [
{ text: "Any", value: "any" },
{ text: "Yes", value: true },
{ text: "No", value: false },
];

export const validFilters = {
const validFilters = {
name: { placeholder: "name", type: String, handler: contains("name"), menuItem: true },
extension: { placeholder: "extension", type: String, handler: equals("extension"), menuItem: true },
tag: { placeholder: "tag", type: String, handler: contains("tags", "tag", expandNameTag), menuItem: true },
Expand Down Expand Up @@ -38,16 +33,16 @@ export const validFilters = {
create_time_le: { handler: compare("create_time", "le", toDate), menuItem: false },
deleted: {
placeholder: "Deleted",
type: "Radio",
options: options,
type: Boolean,
handler: equals("deleted", "deleted", toBool),
default: false,
menuItem: true,
},
visible: {
placeholder: "Visible",
type: "Radio",
options: options,
type: Boolean,
handler: equals("visible", "visible", toBool),
default: true,
menuItem: true,
},
update_time: { handler: compare("update_time", "le", toDate), menuItem: false },
Expand All @@ -57,4 +52,4 @@ export const validFilters = {
update_time_lt: { handler: compare("update_time", "lt", toDate), menuItem: false },
};

export const HistoryFilters = new Filtering(validFilters, true);
export const HistoryFilters = new Filtering(validFilters);
3 changes: 2 additions & 1 deletion client/src/components/History/HistoryPublishedList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const validFilters = {
update_time_le: { handler: compare("update_time", "le", toDate), menuItem: false },
};
const filters = new Filtering(validFilters, false);
const filters = new Filtering(validFilters);
const limit = ref(50);
const offset = ref(0);
Expand Down Expand Up @@ -105,6 +105,7 @@ watch([filterText, sortBy, sortDesc], () => {
:placeholder="'Search by name or use the advanced filtering options' | localize"
:filter-class="filters"
:filter-text.sync="filterText"
:loading="loading"
:show-advanced.sync="showAdvanced" />

<b-alert v-if="noResults" variant="info" show>
Expand Down
3 changes: 2 additions & 1 deletion client/src/components/History/Modals/SelectorModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const validFilters = {
update_time_ge: { handler: compare("update_time", "ge", toDate), menuItem: false },
update_time_le: { handler: compare("update_time", "le", toDate), menuItem: false },
};
const HistoriesFilters = new Filtering(validFilters, false);
const HistoriesFilters = new Filtering(validFilters);
type AdditionalOptions = "set-current" | "multi" | "center";
type HistorySummary = components["schemas"]["HistorySummary"];
Expand Down Expand Up @@ -233,6 +233,7 @@ async function loadMore(noScroll = false) {
placeholder="search datasets"
:filter-class="HistoriesFilters"
:filter-text.sync="filter"
:loading="busy"
:show-advanced.sync="showAdvanced" />
</BFormGroup>

Expand Down
Loading

0 comments on commit 2f8d07a

Please sign in to comment.