mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
Use computed props to determine select all state, handle radio value
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch, type ComputedRef } from "vue";
|
||||
import { computed, type ComputedRef } from "vue";
|
||||
import FormDrilldownList from "./FormDrilldownList.vue";
|
||||
import type { Option } from "./types.js";
|
||||
import { getAllValues, type Option, type Value } from "./utilities";
|
||||
|
||||
export interface FormDrilldownProps {
|
||||
id: string;
|
||||
value?: string | string[];
|
||||
value?: Value;
|
||||
options: Array<Option>;
|
||||
multiple: boolean;
|
||||
}
|
||||
@@ -16,55 +16,44 @@ const props = withDefaults(defineProps<FormDrilldownProps>(), {
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "input", value: string | string[] | null): void;
|
||||
(e: "input", value: Value): void;
|
||||
}>();
|
||||
|
||||
const selectAllIndeterminate = ref(false);
|
||||
const selectAll = ref(false);
|
||||
|
||||
const hasOptions = computed(() => {
|
||||
return props.options.length > 0;
|
||||
});
|
||||
|
||||
// Determine all available values
|
||||
const allValues: ComputedRef<string[]> = computed(() => {
|
||||
let options = null;
|
||||
const values: string[] = [];
|
||||
const stack: Array<Array<Option>> = [props.options];
|
||||
while ((options = stack.pop())) {
|
||||
options.forEach((option) => {
|
||||
if (option.value) {
|
||||
values.push(option.value);
|
||||
}
|
||||
if (option.options.length > 0) {
|
||||
stack.push(option.options);
|
||||
}
|
||||
});
|
||||
}
|
||||
return values;
|
||||
return getAllValues(props.options);
|
||||
});
|
||||
|
||||
// Determine current value and set select all state
|
||||
const currentValue = computed({
|
||||
get: (): string[] => {
|
||||
if (props.value === null || props.value === "") {
|
||||
return [];
|
||||
} else if (Array.isArray(props.value)) {
|
||||
return props.value;
|
||||
} else {
|
||||
return [props.value];
|
||||
}
|
||||
},
|
||||
set: (newValue: string[]): void => {
|
||||
emit("input", newValue);
|
||||
},
|
||||
// Determine current value
|
||||
const currentValue: ComputedRef<string[]> = computed(() => {
|
||||
if (props.value === null || props.value === "") {
|
||||
return [];
|
||||
} else if (Array.isArray(props.value)) {
|
||||
return props.value;
|
||||
} else {
|
||||
return [props.value];
|
||||
}
|
||||
});
|
||||
|
||||
// Determine if select all is checked
|
||||
const selectAllChecked: ComputedRef<boolean> = computed(() => {
|
||||
return allValues.value.length === currentValue.value.length;
|
||||
});
|
||||
|
||||
// Determine if select all state undetermined
|
||||
const selectAllIndeterminate: ComputedRef<boolean> = computed(() => {
|
||||
return ![0, allValues.value.length].includes(currentValue.value.length);
|
||||
});
|
||||
|
||||
// Handle click on individual check/radio element
|
||||
function handleClick(value: string): void {
|
||||
if (props.multiple) {
|
||||
const newValue = currentValue.value.slice();
|
||||
const index = newValue.indexOf(value);
|
||||
const newValue: string[] = currentValue.value.slice();
|
||||
const index: number = newValue.indexOf(value);
|
||||
if (index !== -1) {
|
||||
newValue.splice(index, 1);
|
||||
} else {
|
||||
@@ -84,24 +73,14 @@ function handleClick(value: string): void {
|
||||
function onSelectAll(selected: boolean): void {
|
||||
emit("input", selected ? allValues.value : null);
|
||||
}
|
||||
|
||||
// Watch value and adjust select all state accordingly
|
||||
watch(
|
||||
() => props.value,
|
||||
() => {
|
||||
const length = currentValue.value.length;
|
||||
selectAll.value = allValues.value.length === length;
|
||||
selectAllIndeterminate.value = ![0, allValues.value.length].includes(length);
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="hasOptions">
|
||||
<b-form-checkbox
|
||||
v-if="props.multiple"
|
||||
v-model="selectAll"
|
||||
v-localize
|
||||
:checked="selectAllChecked"
|
||||
:indeterminate="selectAllIndeterminate"
|
||||
class="d-inline select-all-checkbox"
|
||||
@change="onSelectAll">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import FormDrilldownOption from "./FormDrilldownOption.vue";
|
||||
import type { Option } from "./types.js";
|
||||
import type { Option } from "./utilities";
|
||||
|
||||
export interface Props {
|
||||
currentValue: string[];
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from "vue";
|
||||
import { computed, ref, type ComputedRef } from "vue";
|
||||
import FormDrilldownList from "./FormDrilldownList.vue";
|
||||
import type { Option } from "./types.js";
|
||||
import type { Option } from "./utilities";
|
||||
|
||||
export interface Props {
|
||||
currentValue: string[];
|
||||
@@ -14,16 +14,26 @@ const props = defineProps<Props>();
|
||||
|
||||
const showChildren = ref(false);
|
||||
|
||||
const hasOptions = computed(() => {
|
||||
const hasOptions: ComputedRef<boolean> = computed(() => {
|
||||
return props.option.options.length > 0;
|
||||
});
|
||||
|
||||
const isChecked = computed(() => {
|
||||
// indicates if the value has been selected or not (required for checkbox display)
|
||||
const isChecked: ComputedRef<boolean> = computed(() => {
|
||||
const optionValue = props.option.value;
|
||||
return props.currentValue.indexOf(optionValue) !== -1;
|
||||
return props.currentValue.includes(optionValue);
|
||||
});
|
||||
|
||||
function toggleChildren() {
|
||||
// provides a single value, which is either null or a non-empty string (required for radio display)
|
||||
const singleValue = computed({
|
||||
get(): string | null {
|
||||
const value: string[] = props.currentValue;
|
||||
return value.length > 0 ? value[0] || null : null;
|
||||
},
|
||||
set() {},
|
||||
});
|
||||
|
||||
function toggleChildren(): void {
|
||||
showChildren.value = !showChildren.value;
|
||||
}
|
||||
</script>
|
||||
@@ -36,16 +46,16 @@ function toggleChildren() {
|
||||
</span>
|
||||
<b-form-checkbox
|
||||
v-if="multiple"
|
||||
class="d-inline drilldown-option"
|
||||
class="drilldown-option d-inline"
|
||||
:checked="isChecked"
|
||||
@change="handleClick(option.value)">
|
||||
{{ option.name }}
|
||||
</b-form-checkbox>
|
||||
<b-form-radio
|
||||
v-else
|
||||
class="d-inline"
|
||||
:selected="isChecked"
|
||||
name="selectedOption"
|
||||
v-model="singleValue"
|
||||
class="drilldown-option d-inline"
|
||||
:value="option.value"
|
||||
@change="handleClick(option.value)">
|
||||
{{ option.name }}
|
||||
</b-form-radio>
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
export interface Option {
|
||||
name: string;
|
||||
value: string;
|
||||
options: Array<Option>;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
export interface Option {
|
||||
name: string;
|
||||
value: string;
|
||||
options: Array<Option>;
|
||||
}
|
||||
|
||||
export type Value = string[] | string | null;
|
||||
|
||||
export function getAllValues(headOptions: Array<Option>): string[] {
|
||||
let options = null;
|
||||
const values: string[] = [];
|
||||
const stack: Array<Array<Option>> = [headOptions];
|
||||
while ((options = stack.pop())) {
|
||||
options.forEach((option) => {
|
||||
if (option.value) {
|
||||
values.push(option.value);
|
||||
}
|
||||
if (option.options.length > 0) {
|
||||
stack.push(option.options);
|
||||
}
|
||||
});
|
||||
}
|
||||
return values;
|
||||
}
|
||||
Reference in New Issue
Block a user