Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 0 additions & 61 deletions src/components/cylc/TaskFilter.vue

This file was deleted.

187 changes: 171 additions & 16 deletions src/components/cylc/ViewToolbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,24 +30,83 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
class="control"
:data-cy="`control-${iControl.key}`"
>
<v-btn
@click="iControl.callback"
v-bind="btnProps"
:disabled="iControl.disabled"
:aria-checked="iControl.value"
:color="iControl.value ? 'blue' : undefined"
role="switch"
<!-- menu component (to support dropdowns) -->
<v-menu
eager
:close-on-content-click="false"
>
<v-icon>{{ iControl.icon }}</v-icon>
<v-tooltip>{{ iControl.title }}</v-tooltip>
</v-btn>
<template v-slot:activator="{ props }">
<!-- inputs -->
<v-text-field
v-if="iControl.action === 'input'"
class="input"
v-bind="iControl.props"
clearable
:prepend-inner-icon="iControl.icon"
@update:modelValue="iControl.callback"
@focus="autoResizeInput"
@blur="autoResizeInput"
/>

<!-- buttons -->
<v-btn
v-else
class="control-btn"
v-bind="{...$attrs, ...props, ...btnProps}"
@click="(e) => {iControl.action === 'menu' ? null : iControl.callback(e)}"
:disabled="iControl.disabled"
:aria-checked="iControl.value"
:color="isSet(iControl.value) ? 'blue' : undefined"
role="switch"
density="compact"
>
<v-icon>{{ iControl.icon[iControl.value] || iControl.icon }}</v-icon>
<v-tooltip>{{ iControl.title }}</v-tooltip>
</v-btn>
</template>

<!-- dropdowns -->
<v-treeview
v-if="iControl.action === 'menu'"
v-bind="iControl.props"
@update:activated="iControl.callback"
color="blue"
density="compact"
>
<!-- task icons (for task state filters) -->
<template
v-slot:prepend="{ item }"
v-if="iControl.props['task-state-icons']"
>
<Task :task="item.props" />
</template>
<!-- disable expansion until parent active (for task state filters) -->
<template
v-slot:toggle="{ props: toggleProps, isActive, isOpen }"
v-if="iControl.props['task-state-icons']"
>
<v-icon
:icon="isOpen ? $options.icons.mdiChevronUp : $options.icons.mdiChevronDown"
:disabled="!isActive && !isOpen"
v-bind="toggleProps"
/>
</template>
</v-treeview>
</v-menu>
</div>
</div>
</div>
</template>

<script>
import { btnProps } from '@/utils/viewToolbar'
import { btnProps, taskStateItems } from '@/utils/viewToolbar'
import Task from '@/components/cylc/Task.vue'
import {
mdiChevronDown,
mdiChevronUp,
mdiFilter,
mdiMagnify,
} from '@mdi/js'

export default {
name: 'ViewToolbar',
Expand All @@ -56,10 +115,20 @@ export default {
'setOption'
],

components: {
Task
},

icons: {
mdiChevronDown,
mdiChevronUp,
mdiMagnify,
},

props: {
groups: {
required: true,
type: Array
type: Array,
/*
groups: [
{
Expand All @@ -70,21 +139,40 @@ export default {
{
// display name
title: String,

// unique key:
// * Provided with "setOption" events.
// * Used by enableIf/disableIf
// * Added to the control's class list for testing.
key: String

// icon for the control:
// * Either an icon.
// * Or a mapping of state to an icon.
// NOTE: this is autopopulated for action="taskStateFilter | taskIDFilter"
icon: Icon | Object[key, Icon]

// action to perform when clicked:
// Generic actions:
// * toggle - toggle true/false
// * callback - call the provided callback
// * menu - open a menu (provide props: {items} in v-treeview format)
// Specialised actions:
// * taskIDFilter - Search box for task IDs
// * taskStateFilter - open a task state filter menu
action: String

// for use with action='callback'
callback: Fuction

// props to be set on the control
props: Object

// list of keys
// only enable this control if all of the listed controls have
// truthy values
enableIf

// list of keys
// disable this control if any of the listed controls have
// truthy values
Expand All @@ -111,6 +199,7 @@ export default {
let iControl
let callback // callback to fire when control is activated
let disabled // true if control should not be enabled
let props
const values = this.getValues()
for (const group of this.groups) {
iGroup = {
Expand All @@ -120,15 +209,43 @@ export default {
for (const control of group.controls) {
callback = null
disabled = false
props = control.props || {}

// set callback
switch (control.action) {
case 'toggle':
case 'toggle': // toggle button
callback = (e) => this.toggle(control, e)
break
case 'callback':
case 'callback': // button which actions a callback
callback = (e) => this.call(control, e)
break
case 'taskIDFilter': // specialised "input" for filtering tasks
callback = (value) => this.set(control, value)
control.icon = mdiMagnify
control.action = 'input'
props = {
placeholder: 'Search',
...props,
}
break
case 'input': // text input
callback = (value) => this.set(control, value)
break
case 'taskStateFilter': // specialised "menu" for filtering tasks
control.action = 'menu'
control.icon = mdiFilter
props = {
items: taskStateItems,
'indent-lines': true,
activatable: true,
'active-strategy': 'independent',
'item-value': 'value',
'task-state-icons': true, // flag to enable special slots
...props,

}
callback = (value) => this.set(control, value)
break
}

// set disabled
Expand All @@ -147,6 +264,7 @@ export default {

iControl = {
...control,
props,
callback,
disabled
}
Expand Down Expand Up @@ -186,6 +304,29 @@ export default {
}
return vars
},
set (control, value) {
// update the value
this.$emit('setOption', control.key, value)
},
autoResizeInput (e) {
// enlarge a text input when focused or containing text
if (e.type === 'focus') {
e.target.classList.add('expanded')
} else {
if (e.target.value) {
e.target.classList.add('expanded')
} else {
e.target.classList.remove('expanded')
}
}
},
isSet (value) {
// determine if a control is active or not
if (Array.isArray(value)) {
return value.length
}
return value
}
}
}
</script>
Expand All @@ -204,11 +345,25 @@ export default {
// place a divider between groups
content: '';
height: 70%;
width: 2px;
background: rgb(0, 0, 0, 0.22);
width: 0.15em;
border-radius: 0.15em;
background: rgb(0, 0, 0, 0.18);
// put a bit of space between the groups
margin: 0 $spacing;
}

// pack buttons more tightly than the vuetify default
.control-btn {
margin: 0.4em 0.25em 0.4em 0.25em;
}

// auto expand/collapse the search bar
.input {
width: 8em;
}
.input:has(input.expanded) {
width: 20em;
}
}
}
</style>
Loading
Loading