chore(lite): use injection keys (#6898)

Using injection keys for `provide`/`inject` to prevent errors and code
repetition.
This commit is contained in:
Thierry Goettelmann 2023-07-11 14:56:03 +02:00 committed by GitHub
parent ce2b918a29
commit c705051a89
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 321 additions and 149 deletions

View File

@ -11,7 +11,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { type ComputedRef, computed, inject } from "vue"; import { IK_TAB_BAR_DISABLED } from "@/types/injection-keys";
import { computed, inject } from "vue";
import type { RouteLocationRaw } from "vue-router"; import type { RouteLocationRaw } from "vue-router";
import UiTab from "@/components/ui/UiTab.vue"; import UiTab from "@/components/ui/UiTab.vue";
@ -20,8 +21,8 @@ defineProps<{
disabled?: boolean; disabled?: boolean;
}>(); }>();
const isTabBarDisabled = inject<ComputedRef<boolean>>( const isTabBarDisabled = inject(
"isTabBarDisabled", IK_TAB_BAR_DISABLED,
computed(() => false) computed(() => false)
); );
</script> </script>

View File

@ -21,7 +21,8 @@
<script lang="ts" setup> <script lang="ts" setup>
import { percent } from "@/libs/utils"; import { percent } from "@/libs/utils";
import { computed, type ComputedRef, inject } from "vue"; import { IK_CHART_VALUE_FORMATTER } from "@/types/injection-keys";
import { computed, inject } from "vue";
const props = defineProps<{ const props = defineProps<{
total: number; total: number;
@ -34,9 +35,10 @@ const freePercent = computed(() =>
percent(props.total - props.used, props.total) percent(props.total - props.used, props.total)
); );
const valueFormatter = inject("valueFormatter") as ComputedRef< const valueFormatter = inject(
(value: number) => string IK_CHART_VALUE_FORMATTER,
>; computed(() => (value) => value.toString())
);
</script> </script>
<style lang="postcss" scoped> <style lang="postcss" scoped>

View File

@ -6,11 +6,11 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import UiCard from "@/components/ui/UiCard.vue";
import type { LinearChartData, ValueFormatter } from "@/types/chart";
import { IK_CHART_VALUE_FORMATTER } from "@/types/injection-keys";
import { utcFormat } from "d3-time-format"; import { utcFormat } from "d3-time-format";
import type { EChartsOption } from "echarts"; import type { EChartsOption } from "echarts";
import { computed, provide } from "vue";
import VueCharts from "vue-echarts";
import type { LinearChartData } from "@/types/chart";
import { LineChart } from "echarts/charts"; import { LineChart } from "echarts/charts";
import { import {
GridComponent, GridComponent,
@ -20,8 +20,8 @@ import {
} from "echarts/components"; } from "echarts/components";
import { use } from "echarts/core"; import { use } from "echarts/core";
import { CanvasRenderer } from "echarts/renderers"; import { CanvasRenderer } from "echarts/renderers";
import type { OptionDataValue } from "echarts/types/src/util/types"; import { computed, provide } from "vue";
import UiCard from "@/components/ui/UiCard.vue"; import VueCharts from "vue-echarts";
const Y_AXIS_MAX_VALUE = 200; const Y_AXIS_MAX_VALUE = 200;
@ -29,23 +29,23 @@ const props = defineProps<{
title?: string; title?: string;
subtitle?: string; subtitle?: string;
data: LinearChartData; data: LinearChartData;
valueFormatter?: (value: number) => string; valueFormatter?: ValueFormatter;
maxValue?: number; maxValue?: number;
}>(); }>();
const valueFormatter = computed(() => { const valueFormatter = computed<ValueFormatter>(() => {
const formatter = props.valueFormatter; const formatter = props.valueFormatter;
return (value: OptionDataValue | OptionDataValue[]) => { return (value) => {
if (formatter) { if (formatter === undefined) {
return formatter(value as number); return value.toString();
} }
return value.toString(); return formatter(value);
}; };
}); });
provide("valueFormatter", valueFormatter); provide(IK_CHART_VALUE_FORMATTER, valueFormatter);
use([ use([
CanvasRenderer, CanvasRenderer,
@ -65,7 +65,7 @@ const option = computed<EChartsOption>(() => ({
data: props.data.map((series) => series.label), data: props.data.map((series) => series.label),
}, },
tooltip: { tooltip: {
valueFormatter: valueFormatter.value, valueFormatter: (v) => valueFormatter.value(v as number),
}, },
xAxis: { xAxis: {
type: "time", type: "time",

View File

@ -19,7 +19,12 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { type HTMLAttributes, computed, inject, ref } from "vue"; import {
IK_FORM_HAS_LABEL,
IK_FORM_LABEL_DISABLED,
IK_CHECKBOX_TYPE,
} from "@/types/injection-keys";
import { type HTMLAttributes, computed, inject } from "vue";
import { faCheck, faCircle, faMinus } from "@fortawesome/free-solid-svg-icons"; import { faCheck, faCircle, faMinus } from "@fortawesome/free-solid-svg-icons";
import { useVModel } from "@vueuse/core"; import { useVModel } from "@vueuse/core";
import UiIcon from "@/components/ui/icon/UiIcon.vue"; import UiIcon from "@/components/ui/icon/UiIcon.vue";
@ -37,9 +42,15 @@ const emit = defineEmits<{
}>(); }>();
const value = useVModel(props, "modelValue", emit); const value = useVModel(props, "modelValue", emit);
const type = inject<"checkbox" | "radio" | "toggle">("inputType", "checkbox"); const type = inject(IK_CHECKBOX_TYPE, "checkbox");
const hasLabel = inject("hasLabel", false); const hasLabel = inject(
const isLabelDisabled = inject("isLabelDisabled", ref(false)); IK_FORM_HAS_LABEL,
computed(() => false)
);
const isLabelDisabled = inject(
IK_FORM_LABEL_DISABLED,
computed(() => false)
);
const icon = computed(() => { const icon = computed(() => {
if (type !== "checkbox") { if (type !== "checkbox") {
return faCircle; return faCircle;

View File

@ -48,19 +48,24 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import UiIcon from "@/components/ui/icon/UiIcon.vue";
import type { Color } from "@/types";
import {
IK_FORM_INPUT_COLOR,
IK_FORM_LABEL_DISABLED,
IK_INPUT_TYPE,
} from "@/types/injection-keys";
import type { IconDefinition } from "@fortawesome/fontawesome-common-types";
import { faAngleDown } from "@fortawesome/free-solid-svg-icons";
import { useTextareaAutosize, useVModel } from "@vueuse/core";
import { import {
type HTMLAttributes,
computed, computed,
type HTMLAttributes,
inject, inject,
nextTick, nextTick,
ref, ref,
watch, watch,
} from "vue"; } from "vue";
import type { Color } from "@/types";
import type { IconDefinition } from "@fortawesome/fontawesome-common-types";
import { faAngleDown } from "@fortawesome/free-solid-svg-icons";
import { useTextareaAutosize, useVModel } from "@vueuse/core";
import UiIcon from "@/components/ui/icon/UiIcon.vue";
defineOptions({ inheritAttrs: false }); defineOptions({ inheritAttrs: false });
@ -90,17 +95,20 @@ const value = useVModel(props, "modelValue", emit);
const isEmpty = computed( const isEmpty = computed(
() => props.modelValue == null || String(props.modelValue).trim() === "" () => props.modelValue == null || String(props.modelValue).trim() === ""
); );
const inputType = inject("inputType", "input"); const inputType = inject(IK_INPUT_TYPE, "input");
const isLabelDisabled = inject("isLabelDisabled", ref(false)); const isLabelDisabled = inject(
IK_FORM_LABEL_DISABLED,
computed(() => false)
);
const parentColor = inject( const parentColor = inject(
"color", IK_FORM_INPUT_COLOR,
computed(() => undefined) computed(() => undefined)
); );
const wrapperClass = computed(() => [ const wrapperClass = computed(() => [
`form-${inputType}`, `form-${inputType}`,
{ {
disabled: props.disabled || isLabelDisabled.value, disabled: props.disabled === true || isLabelDisabled.value,
empty: isEmpty.value, empty: isEmpty.value,
}, },
]); ]);

View File

@ -17,9 +17,14 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, provide, useSlots } from "vue";
import { faCircleExclamation } from "@fortawesome/free-solid-svg-icons";
import UiIcon from "@/components/ui/icon/UiIcon.vue"; import UiIcon from "@/components/ui/icon/UiIcon.vue";
import {
IK_FORM_HAS_LABEL,
IK_FORM_INPUT_COLOR,
IK_FORM_LABEL_DISABLED,
} from "@/types/injection-keys";
import { faCircleExclamation } from "@fortawesome/free-solid-svg-icons";
import { computed, provide, useSlots } from "vue";
const slots = useSlots(); const slots = useSlots();
@ -29,10 +34,14 @@ const props = defineProps<{
warning?: string; warning?: string;
}>(); }>();
provide("hasLabel", slots.label !== undefined);
provide( provide(
"isLabelDisabled", IK_FORM_HAS_LABEL,
computed(() => props.disabled) computed(() => slots.label !== undefined)
);
provide(
IK_FORM_LABEL_DISABLED,
computed(() => props.disabled ?? false)
); );
const hasError = computed( const hasError = computed(
@ -43,7 +52,7 @@ const hasWarning = computed(
); );
provide( provide(
"color", IK_FORM_INPUT_COLOR,
computed(() => computed(() =>
hasError.value ? "error" : hasWarning.value ? "warning" : undefined hasError.value ? "error" : hasWarning.value ? "warning" : undefined
) )

View File

@ -3,8 +3,9 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { provide } from "vue";
import FormCheckbox from "@/components/form/FormCheckbox.vue"; import FormCheckbox from "@/components/form/FormCheckbox.vue";
import { IK_CHECKBOX_TYPE } from "@/types/injection-keys";
import { provide } from "vue";
provide("inputType", "radio"); provide(IK_CHECKBOX_TYPE, "radio");
</script> </script>

View File

@ -5,10 +5,11 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { provide } from "vue";
import FormInput from "@/components/form/FormInput.vue"; import FormInput from "@/components/form/FormInput.vue";
import { IK_INPUT_TYPE } from "@/types/injection-keys";
import { provide } from "vue";
provide("inputType", "select"); provide(IK_INPUT_TYPE, "select");
</script> </script>
<style lang="postcss" scoped></style> <style lang="postcss" scoped></style>

View File

@ -3,10 +3,11 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { provide } from "vue";
import FormInput from "@/components/form/FormInput.vue"; import FormInput from "@/components/form/FormInput.vue";
import { IK_INPUT_TYPE } from "@/types/injection-keys";
import { provide } from "vue";
provide("inputType", "textarea"); provide(IK_INPUT_TYPE, "textarea");
</script> </script>
<style lang="postcss" scoped></style> <style lang="postcss" scoped></style>

View File

@ -3,8 +3,9 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { provide } from "vue";
import FormCheckbox from "@/components/form/FormCheckbox.vue"; import FormCheckbox from "@/components/form/FormCheckbox.vue";
import { IK_CHECKBOX_TYPE } from "@/types/injection-keys";
import { provide } from "vue";
provide("inputType", "toggle"); provide(IK_CHECKBOX_TYPE, "toggle");
</script> </script>

View File

@ -14,9 +14,14 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { IK_MENU_TELEPORTED } from "@/types/injection-keys"; import {
IK_CLOSE_MENU,
IK_MENU_DISABLED,
IK_MENU_HORIZONTAL,
IK_MENU_TELEPORTED,
} from "@/types/injection-keys";
import placementJs, { type Options } from "placement.js"; import placementJs, { type Options } from "placement.js";
import { inject, nextTick, provide, ref, toRef, unref, useSlots } from "vue"; import { computed, inject, nextTick, provide, ref, useSlots } from "vue";
import { onClickOutside, unrefElement, whenever } from "@vueuse/core"; import { onClickOutside, unrefElement, whenever } from "@vueuse/core";
const props = defineProps<{ const props = defineProps<{
@ -33,9 +38,18 @@ defineOptions({
const slots = useSlots(); const slots = useSlots();
const isOpen = ref(false); const isOpen = ref(false);
const menu = ref(); const menu = ref();
const isParentHorizontal = inject("isMenuHorizontal", undefined); const isParentHorizontal = inject(
provide("isMenuHorizontal", toRef(props, "horizontal")); IK_MENU_HORIZONTAL,
provide("isMenuDisabled", toRef(props, "disabled")); computed(() => false)
);
provide(
IK_MENU_HORIZONTAL,
computed(() => props.horizontal ?? false)
);
provide(
IK_MENU_DISABLED,
computed(() => props.disabled ?? false)
);
let clearClickOutsideEvent: (() => void) | undefined; let clearClickOutsideEvent: (() => void) | undefined;
const hasTrigger = useSlots().trigger !== undefined; const hasTrigger = useSlots().trigger !== undefined;
@ -50,9 +64,8 @@ whenever(
() => !isOpen.value, () => !isOpen.value,
() => clearClickOutsideEvent?.() () => clearClickOutsideEvent?.()
); );
if (slots.trigger && inject(IK_CLOSE_MENU, undefined) === undefined) {
if (slots.trigger && !inject("closeMenu", false)) { provide(IK_CLOSE_MENU, () => (isOpen.value = false));
provide("closeMenu", () => (isOpen.value = false));
} }
const open = (event: MouseEvent) => { const open = (event: MouseEvent) => {
@ -74,7 +87,7 @@ const open = (event: MouseEvent) => {
placementJs(event.currentTarget as HTMLElement, unrefElement(menu), { placementJs(event.currentTarget as HTMLElement, unrefElement(menu), {
placement: placement:
props.placement ?? props.placement ??
(unref(isParentHorizontal) !== false ? "bottom-start" : "right-start"), (isParentHorizontal.value ? "bottom-start" : "right-start"),
}); });
}); });
}; };

View File

@ -10,7 +10,7 @@
> >
<slot /> <slot />
</MenuTrigger> </MenuTrigger>
<AppMenu v-else shadow :disabled="isDisabled"> <AppMenu v-else :disabled="isDisabled" shadow>
<template #trigger="{ open, isOpen }"> <template #trigger="{ open, isOpen }">
<MenuTrigger <MenuTrigger
:active="isOpen" :active="isOpen"
@ -33,13 +33,17 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, inject, ref, unref } from "vue";
import type { IconDefinition } from "@fortawesome/fontawesome-common-types";
import { faAngleDown, faAngleRight } from "@fortawesome/free-solid-svg-icons";
import { noop } from "@vueuse/core";
import AppMenu from "@/components/menu/AppMenu.vue"; import AppMenu from "@/components/menu/AppMenu.vue";
import MenuTrigger from "@/components/menu/MenuTrigger.vue"; import MenuTrigger from "@/components/menu/MenuTrigger.vue";
import UiIcon from "@/components/ui/icon/UiIcon.vue"; import UiIcon from "@/components/ui/icon/UiIcon.vue";
import {
IK_CLOSE_MENU,
IK_MENU_DISABLED,
IK_MENU_HORIZONTAL,
} from "@/types/injection-keys";
import type { IconDefinition } from "@fortawesome/fontawesome-common-types";
import { faAngleDown, faAngleRight } from "@fortawesome/free-solid-svg-icons";
import { computed, inject, ref } from "vue";
const props = defineProps<{ const props = defineProps<{
icon?: IconDefinition; icon?: IconDefinition;
@ -48,17 +52,25 @@ const props = defineProps<{
busy?: boolean; busy?: boolean;
}>(); }>();
const isParentHorizontal = inject("isMenuHorizontal", false); const isParentHorizontal = inject(
const isMenuDisabled = inject("isMenuDisabled", false); IK_MENU_HORIZONTAL,
const isDisabled = computed(() => props.disabled || unref(isMenuDisabled)); computed(() => false)
);
const isMenuDisabled = inject(
IK_MENU_DISABLED,
computed(() => false)
);
const isDisabled = computed(
() => props.disabled === true || isMenuDisabled.value
);
const submenuIcon = computed(() => const submenuIcon = computed(() =>
unref(isParentHorizontal) ? faAngleDown : faAngleRight isParentHorizontal.value ? faAngleDown : faAngleRight
); );
const isHandlingClick = ref(false); const isHandlingClick = ref(false);
const isBusy = computed(() => isHandlingClick.value || props.busy); const isBusy = computed(() => isHandlingClick.value || props.busy === true);
const closeMenu = inject("closeMenu", noop); const closeMenu = inject(IK_CLOSE_MENU, undefined);
const handleClick = async () => { const handleClick = async () => {
if (isDisabled.value || isBusy.value) { if (isDisabled.value || isBusy.value) {
@ -68,7 +80,7 @@ const handleClick = async () => {
isHandlingClick.value = true; isHandlingClick.value = true;
try { try {
await props.onClick?.(); await props.onClick?.();
closeMenu(); closeMenu?.();
} finally { } finally {
isHandlingClick.value = false; isHandlingClick.value = false;
} }

View File

@ -3,9 +3,13 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { inject } from "vue"; import { IK_MENU_HORIZONTAL } from "@/types/injection-keys";
import { computed, inject } from "vue";
const horizontal = inject("isParentMenuHorizontal", false); const horizontal = inject(
IK_MENU_HORIZONTAL,
computed(() => false)
);
</script> </script>
<style lang="postcss" scoped> <style lang="postcss" scoped>

View File

@ -11,21 +11,19 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { IK_HOST_LAST_WEEK_STATS } from "@/types/injection-keys";
import { computed, inject } from "vue"; import { computed, inject } from "vue";
import { map } from "lodash-es"; import { map } from "lodash-es";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import LinearChart from "@/components/charts/LinearChart.vue"; import LinearChart from "@/components/charts/LinearChart.vue";
import type { FetchedStats } from "@/composables/fetch-stats.composable";
import { formatSize } from "@/libs/utils"; import { formatSize } from "@/libs/utils";
import type { HostStats } from "@/libs/xapi-stats"; import type { HostStats } from "@/libs/xapi-stats";
import type { LinearChartData } from "@/types/chart"; import type { LinearChartData } from "@/types/chart";
import { RRD_STEP_FROM_STRING } from "@/libs/xapi-stats"; import { RRD_STEP_FROM_STRING } from "@/libs/xapi-stats";
import type { XenApiHost } from "@/libs/xen-api";
const { t } = useI18n(); const { t } = useI18n();
const hostLastWeekStats = const hostLastWeekStats = inject(IK_HOST_LAST_WEEK_STATS);
inject<FetchedStats<XenApiHost, HostStats>>("hostLastWeekStats");
const data = computed<LinearChartData>(() => { const data = computed<LinearChartData>(() => {
const stats = hostLastWeekStats?.stats?.value; const stats = hostLastWeekStats?.stats?.value;

View File

@ -9,20 +9,19 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import NoDataError from "@/components/NoDataError.vue";
import UiCardTitle from "@/components/ui/UiCardTitle.vue"; import UiCardTitle from "@/components/ui/UiCardTitle.vue";
import UsageBar from "@/components/UsageBar.vue"; import UsageBar from "@/components/UsageBar.vue";
import type { Stat } from "@/composables/fetch-stats.composable";
import { getAvgCpuUsage } from "@/libs/utils"; import { getAvgCpuUsage } from "@/libs/utils";
import type { HostStats } from "@/libs/xapi-stats";
import { useHostStore } from "@/stores/host.store"; import { useHostStore } from "@/stores/host.store";
import { IK_HOST_STATS } from "@/types/injection-keys";
import { N_ITEMS } from "@/views/pool/PoolDashboardView.vue"; import { N_ITEMS } from "@/views/pool/PoolDashboardView.vue";
import { computed, type ComputedRef, inject } from "vue"; import { computed, type ComputedRef, inject } from "vue";
import NoDataError from "@/components/NoDataError.vue";
const { hasError } = useHostStore().subscribe(); const { hasError } = useHostStore().subscribe();
const stats = inject<ComputedRef<Stat<HostStats>[]>>( const stats = inject(
"hostStats", IK_HOST_STATS,
computed(() => []) computed(() => [])
); );

View File

@ -12,20 +12,18 @@
<script lang="ts" setup> <script lang="ts" setup>
import LinearChart from "@/components/charts/LinearChart.vue"; import LinearChart from "@/components/charts/LinearChart.vue";
import type { FetchedStats } from "@/composables/fetch-stats.composable";
import type { HostStats } from "@/libs/xapi-stats"; import type { HostStats } from "@/libs/xapi-stats";
import { RRD_STEP_FROM_STRING } from "@/libs/xapi-stats"; import { RRD_STEP_FROM_STRING } from "@/libs/xapi-stats";
import type { XenApiHost } from "@/libs/xen-api";
import { useHostStore } from "@/stores/host.store"; import { useHostStore } from "@/stores/host.store";
import type { LinearChartData } from "@/types/chart"; import type { LinearChartData, ValueFormatter } from "@/types/chart";
import { IK_HOST_LAST_WEEK_STATS } from "@/types/injection-keys";
import { sumBy } from "lodash-es"; import { sumBy } from "lodash-es";
import { computed, inject } from "vue"; import { computed, inject } from "vue";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
const { t } = useI18n(); const { t } = useI18n();
const hostLastWeekStats = const hostLastWeekStats = inject(IK_HOST_LAST_WEEK_STATS);
inject<FetchedStats<XenApiHost, HostStats>>("hostLastWeekStats");
const { records: hosts } = useHostStore().subscribe(); const { records: hosts } = useHostStore().subscribe();
@ -78,5 +76,5 @@ const data = computed<LinearChartData>(() => {
]; ];
}); });
const customValueFormatter = (value: number) => `${value}%`; const customValueFormatter: ValueFormatter = (value) => `${value}%`;
</script> </script>

View File

@ -9,20 +9,19 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import UiCardTitle from "@/components/ui/UiCardTitle.vue";
import { type ComputedRef, computed, inject } from "vue";
import UsageBar from "@/components/UsageBar.vue";
import type { Stat } from "@/composables/fetch-stats.composable";
import { getAvgCpuUsage } from "@/libs/utils";
import type { VmStats } from "@/libs/xapi-stats";
import { N_ITEMS } from "@/views/pool/PoolDashboardView.vue";
import NoDataError from "@/components/NoDataError.vue"; import NoDataError from "@/components/NoDataError.vue";
import UiCardTitle from "@/components/ui/UiCardTitle.vue";
import UsageBar from "@/components/UsageBar.vue";
import { getAvgCpuUsage } from "@/libs/utils";
import { useVmStore } from "@/stores/vm.store"; import { useVmStore } from "@/stores/vm.store";
import { IK_VM_STATS } from "@/types/injection-keys";
import { N_ITEMS } from "@/views/pool/PoolDashboardView.vue";
import { computed, type ComputedRef, inject } from "vue";
const { hasError } = useVmStore().subscribe(); const { hasError } = useVmStore().subscribe();
const stats = inject<ComputedRef<Stat<VmStats>[]>>( const stats = inject(
"vmStats", IK_VM_STATS,
computed(() => []) computed(() => [])
); );

View File

@ -10,19 +10,18 @@
<script lang="ts" setup> <script lang="ts" setup>
import UiCardTitle from "@/components/ui/UiCardTitle.vue"; import UiCardTitle from "@/components/ui/UiCardTitle.vue";
import { IK_HOST_STATS } from "@/types/injection-keys";
import { type ComputedRef, computed, inject } from "vue"; import { type ComputedRef, computed, inject } from "vue";
import UsageBar from "@/components/UsageBar.vue"; import UsageBar from "@/components/UsageBar.vue";
import type { Stat } from "@/composables/fetch-stats.composable";
import { formatSize, parseRamUsage } from "@/libs/utils"; import { formatSize, parseRamUsage } from "@/libs/utils";
import type { HostStats } from "@/libs/xapi-stats";
import { N_ITEMS } from "@/views/pool/PoolDashboardView.vue"; import { N_ITEMS } from "@/views/pool/PoolDashboardView.vue";
import NoDataError from "@/components/NoDataError.vue"; import NoDataError from "@/components/NoDataError.vue";
import { useHostStore } from "@/stores/host.store"; import { useHostStore } from "@/stores/host.store";
const { hasError } = useHostStore().subscribe(); const { hasError } = useHostStore().subscribe();
const stats = inject<ComputedRef<Stat<HostStats>[]>>( const stats = inject(
"hostStats", IK_HOST_STATS,
computed(() => []) computed(() => [])
); );

View File

@ -17,14 +17,12 @@
<script lang="ts" setup> <script lang="ts" setup>
import LinearChart from "@/components/charts/LinearChart.vue"; import LinearChart from "@/components/charts/LinearChart.vue";
import SizeStatsSummary from "@/components/ui/SizeStatsSummary.vue"; import SizeStatsSummary from "@/components/ui/SizeStatsSummary.vue";
import type { FetchedStats } from "@/composables/fetch-stats.composable";
import { formatSize, getHostMemory } from "@/libs/utils"; import { formatSize, getHostMemory } from "@/libs/utils";
import type { HostStats } from "@/libs/xapi-stats";
import { RRD_STEP_FROM_STRING } from "@/libs/xapi-stats"; import { RRD_STEP_FROM_STRING } from "@/libs/xapi-stats";
import type { XenApiHost } from "@/libs/xen-api";
import { useHostMetricsStore } from "@/stores/host-metrics.store"; import { useHostMetricsStore } from "@/stores/host-metrics.store";
import { useHostStore } from "@/stores/host.store"; import { useHostStore } from "@/stores/host.store";
import type { LinearChartData } from "@/types/chart"; import type { LinearChartData, ValueFormatter } from "@/types/chart";
import { IK_HOST_LAST_WEEK_STATS } from "@/types/injection-keys";
import { sumBy } from "lodash-es"; import { sumBy } from "lodash-es";
import { computed, inject } from "vue"; import { computed, inject } from "vue";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
@ -36,8 +34,7 @@ const { runningHosts } = hostStore.subscribe({ hostMetricsSubscription });
const { t } = useI18n(); const { t } = useI18n();
const hostLastWeekStats = const hostLastWeekStats = inject(IK_HOST_LAST_WEEK_STATS);
inject<FetchedStats<XenApiHost, HostStats>>("hostLastWeekStats");
const customMaxValue = computed(() => const customMaxValue = computed(() =>
sumBy( sumBy(
@ -96,5 +93,6 @@ const data = computed<LinearChartData>(() => {
]; ];
}); });
const customValueFormatter = (value: number) => String(formatSize(value)); const customValueFormatter: ValueFormatter = (value) =>
String(formatSize(value));
</script> </script>

View File

@ -9,20 +9,19 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import UiCardTitle from "@/components/ui/UiCardTitle.vue";
import { type ComputedRef, computed, inject } from "vue";
import UsageBar from "@/components/UsageBar.vue";
import type { Stat } from "@/composables/fetch-stats.composable";
import { formatSize, parseRamUsage } from "@/libs/utils";
import type { VmStats } from "@/libs/xapi-stats";
import { N_ITEMS } from "@/views/pool/PoolDashboardView.vue";
import NoDataError from "@/components/NoDataError.vue"; import NoDataError from "@/components/NoDataError.vue";
import UiCardTitle from "@/components/ui/UiCardTitle.vue";
import UsageBar from "@/components/UsageBar.vue";
import { formatSize, parseRamUsage } from "@/libs/utils";
import { useVmStore } from "@/stores/vm.store"; import { useVmStore } from "@/stores/vm.store";
import { IK_VM_STATS } from "@/types/injection-keys";
import { N_ITEMS } from "@/views/pool/PoolDashboardView.vue";
import { computed, type ComputedRef, inject } from "vue";
const { hasError } = useVmStore().subscribe(); const { hasError } = useVmStore().subscribe();
const stats = inject<ComputedRef<Stat<VmStats>[]>>( const stats = inject(
"vmStats", IK_VM_STATS,
computed(() => []) computed(() => [])
); );

View File

@ -16,9 +16,13 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, inject, unref } from "vue";
import type { IconDefinition } from "@fortawesome/fontawesome-common-types";
import UiIcon from "@/components/ui/icon/UiIcon.vue"; import UiIcon from "@/components/ui/icon/UiIcon.vue";
import {
IK_BUTTON_GROUP_BUSY,
IK_BUTTON_GROUP_DISABLED,
} from "@/types/injection-keys";
import type { IconDefinition } from "@fortawesome/fontawesome-common-types";
import { computed, inject } from "vue";
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
@ -30,11 +34,17 @@ const props = withDefaults(
{ busy: undefined, disabled: undefined } { busy: undefined, disabled: undefined }
); );
const isGroupBusy = inject("isButtonGroupBusy", false); const isGroupBusy = inject(
const isBusy = computed(() => props.busy ?? unref(isGroupBusy)); IK_BUTTON_GROUP_BUSY,
computed(() => false)
);
const isBusy = computed(() => props.busy ?? isGroupBusy.value);
const isGroupDisabled = inject("isButtonGroupDisabled", false); const isGroupDisabled = inject(
const isDisabled = computed(() => props.disabled ?? unref(isGroupDisabled)); IK_BUTTON_GROUP_DISABLED,
computed(() => false)
);
const isDisabled = computed(() => props.disabled ?? isGroupDisabled.value);
</script> </script>
<style lang="postcss" scoped> <style lang="postcss" scoped>

View File

@ -15,7 +15,14 @@
<script lang="ts" setup> <script lang="ts" setup>
import UiSpinner from "@/components/ui/UiSpinner.vue"; import UiSpinner from "@/components/ui/UiSpinner.vue";
import { computed, inject, unref } from "vue"; import {
IK_BUTTON_GROUP_BUSY,
IK_BUTTON_GROUP_COLOR,
IK_BUTTON_GROUP_DISABLED,
IK_BUTTON_GROUP_OUTLINED,
IK_BUTTON_GROUP_TRANSPARENT,
} from "@/types/injection-keys";
import { computed, inject } from "vue";
import type { Color } from "@/types"; import type { Color } from "@/types";
import type { IconDefinition } from "@fortawesome/fontawesome-common-types"; import type { IconDefinition } from "@fortawesome/fontawesome-common-types";
import UiIcon from "@/components/ui/icon/UiIcon.vue"; import UiIcon from "@/components/ui/icon/UiIcon.vue";
@ -39,17 +46,32 @@ const props = withDefaults(
} }
); );
const isGroupBusy = inject("isButtonGroupBusy", false); const isGroupBusy = inject(
const isBusy = computed(() => props.busy ?? unref(isGroupBusy)); IK_BUTTON_GROUP_BUSY,
computed(() => false)
);
const isBusy = computed(() => props.busy ?? isGroupBusy.value);
const isGroupDisabled = inject("isButtonGroupDisabled", false); const isGroupDisabled = inject(
const isDisabled = computed(() => props.disabled ?? unref(isGroupDisabled)); IK_BUTTON_GROUP_DISABLED,
computed(() => false)
);
const isDisabled = computed(() => props.disabled ?? isGroupDisabled.value);
const isGroupOutlined = inject("isButtonGroupOutlined", false); const isGroupOutlined = inject(
const isGroupTransparent = inject("isButtonGroupTransparent", false); IK_BUTTON_GROUP_OUTLINED,
computed(() => false)
);
const isGroupTransparent = inject(
IK_BUTTON_GROUP_TRANSPARENT,
computed(() => false)
);
const buttonGroupColor = inject("buttonGroupColor", "info"); const buttonGroupColor = inject(
const buttonColor = computed(() => props.color ?? unref(buttonGroupColor)); IK_BUTTON_GROUP_COLOR,
computed(() => "info")
);
const buttonColor = computed(() => props.color ?? buttonGroupColor.value);
const className = computed(() => { const className = computed(() => {
return [ return [
@ -58,8 +80,8 @@ const className = computed(() => {
busy: isBusy.value, busy: isBusy.value,
active: props.active, active: props.active,
disabled: isDisabled.value, disabled: isDisabled.value,
outlined: props.outlined ?? unref(isGroupOutlined), outlined: props.outlined ?? isGroupOutlined.value,
transparent: props.transparent ?? unref(isGroupTransparent), transparent: props.transparent ?? isGroupTransparent.value,
}, },
]; ];
}); });

View File

@ -5,8 +5,15 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, provide } from "vue";
import type { Color } from "@/types"; import type { Color } from "@/types";
import {
IK_BUTTON_GROUP_BUSY,
IK_BUTTON_GROUP_COLOR,
IK_BUTTON_GROUP_DISABLED,
IK_BUTTON_GROUP_OUTLINED,
IK_BUTTON_GROUP_TRANSPARENT,
} from "@/types/injection-keys";
import { computed, provide } from "vue";
const props = defineProps<{ const props = defineProps<{
busy?: boolean; busy?: boolean;
@ -17,23 +24,23 @@ const props = defineProps<{
merge?: boolean; merge?: boolean;
}>(); }>();
provide( provide(
"isButtonGroupBusy", IK_BUTTON_GROUP_BUSY,
computed(() => props.busy ?? false) computed(() => props.busy ?? false)
); );
provide( provide(
"isButtonGroupDisabled", IK_BUTTON_GROUP_DISABLED,
computed(() => props.disabled ?? false) computed(() => props.disabled ?? false)
); );
provide( provide(
"buttonGroupColor", IK_BUTTON_GROUP_COLOR,
computed(() => props.color ?? "info") computed(() => props.color ?? "info")
); );
provide( provide(
"isButtonGroupOutlined", IK_BUTTON_GROUP_OUTLINED,
computed(() => props.outlined ?? false) computed(() => props.outlined ?? false)
); );
provide( provide(
"isButtonGroupTransparent", IK_BUTTON_GROUP_TRANSPARENT,
computed(() => props.transparent ?? false) computed(() => props.transparent ?? false)
); );
</script> </script>

View File

@ -5,11 +5,12 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { IK_CARD_GROUP_VERTICAL } from "@/types/injection-keys";
import { inject, provide } from "vue"; import { inject, provide } from "vue";
const vertical = inject("isCardGroupVertical", false); const vertical = inject(IK_CARD_GROUP_VERTICAL, false);
provide("isCardGroupVertical", !vertical); provide(IK_CARD_GROUP_VERTICAL, !vertical);
</script> </script>
<style lang="postcss" scoped> <style lang="postcss" scoped>

View File

@ -9,7 +9,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { type ComputedRef, computed, inject } from "vue"; import { IK_TAB_BAR_DISABLED } from "@/types/injection-keys";
import { computed, inject } from "vue";
withDefaults( withDefaults(
defineProps<{ defineProps<{
@ -20,8 +21,8 @@ withDefaults(
{ tag: "span" } { tag: "span" }
); );
const isTabBarDisabled = inject<ComputedRef<boolean>>( const isTabBarDisabled = inject(
"isTabBarDisabled", IK_TAB_BAR_DISABLED,
computed(() => false) computed(() => false)
); );
</script> </script>

View File

@ -5,6 +5,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { IK_TAB_BAR_DISABLED } from "@/types/injection-keys";
import { computed, provide } from "vue"; import { computed, provide } from "vue";
const props = defineProps<{ const props = defineProps<{
@ -12,8 +13,8 @@ const props = defineProps<{
}>(); }>();
provide( provide(
"isTabBarDisabled", IK_TAB_BAR_DISABLED,
computed(() => props.disabled) computed(() => props.disabled ?? false)
); );
</script> </script>

View File

@ -5,3 +5,5 @@ export type LinearChartData = {
value: number; value: number;
}[]; }[];
}[]; }[];
export type ValueFormatter = (value: number) => string;

View File

@ -1,3 +1,72 @@
import type { InjectionKey } from "vue"; import type { FetchedStats, Stat } from "@/composables/fetch-stats.composable";
import type { HostStats, VmStats } from "@/libs/xapi-stats";
import type { XenApiHost } from "@/libs/xen-api";
import type { ValueFormatter } from "@/types/chart";
import type { ComputedRef, InjectionKey } from "vue";
export const IK_MENU_TELEPORTED = Symbol() as InjectionKey<boolean>; export const IK_MENU_TELEPORTED = Symbol() as InjectionKey<boolean>;
export const IK_TAB_BAR_DISABLED = Symbol() as InjectionKey<
ComputedRef<boolean>
>;
export const IK_CHART_VALUE_FORMATTER = Symbol() as InjectionKey<
ComputedRef<ValueFormatter>
>;
export const IK_INPUT_TYPE = Symbol() as InjectionKey<"select" | "textarea">;
export const IK_CHECKBOX_TYPE = Symbol() as InjectionKey<
"checkbox" | "radio" | "toggle"
>;
export const IK_FORM_HAS_LABEL = Symbol() as InjectionKey<ComputedRef<boolean>>;
export const IK_FORM_LABEL_DISABLED = Symbol() as InjectionKey<
ComputedRef<boolean>
>;
export const IK_FORM_INPUT_COLOR = Symbol() as InjectionKey<
ComputedRef<"success" | "error" | "warning" | "info" | undefined>
>;
export const IK_MENU_DISABLED = Symbol() as InjectionKey<ComputedRef<boolean>>;
export const IK_MENU_HORIZONTAL = Symbol() as InjectionKey<
ComputedRef<boolean>
>;
export const IK_CLOSE_MENU = Symbol() as InjectionKey<() => void>;
export const IK_HOST_STATS = Symbol() as InjectionKey<
ComputedRef<Stat<HostStats>[]>
>;
export const IK_VM_STATS = Symbol() as InjectionKey<
ComputedRef<Stat<VmStats>[]>
>;
export const IK_HOST_LAST_WEEK_STATS = Symbol() as InjectionKey<
FetchedStats<XenApiHost, HostStats>
>;
export const IK_BUTTON_GROUP_DISABLED = Symbol() as InjectionKey<
ComputedRef<boolean>
>;
export const IK_BUTTON_GROUP_BUSY = Symbol() as InjectionKey<
ComputedRef<boolean>
>;
export const IK_BUTTON_GROUP_OUTLINED = Symbol() as InjectionKey<
ComputedRef<boolean>
>;
export const IK_BUTTON_GROUP_TRANSPARENT = Symbol() as InjectionKey<
ComputedRef<boolean>
>;
export const IK_BUTTON_GROUP_COLOR = Symbol() as InjectionKey<
ComputedRef<"success" | "error" | "warning" | "info">
>;
export const IK_CARD_GROUP_VERTICAL = Symbol() as InjectionKey<boolean>;

View File

@ -33,6 +33,11 @@ export const N_ITEMS = 5;
<script lang="ts" setup> <script lang="ts" setup>
import UiCardGroup from "@/components/ui/UiCardGroup.vue"; import UiCardGroup from "@/components/ui/UiCardGroup.vue";
import { useHostMetricsStore } from "@/stores/host-metrics.store"; import { useHostMetricsStore } from "@/stores/host-metrics.store";
import {
IK_HOST_LAST_WEEK_STATS,
IK_HOST_STATS,
IK_VM_STATS,
} from "@/types/injection-keys";
import { differenceBy } from "lodash-es"; import { differenceBy } from "lodash-es";
import { provide, watch } from "vue"; import { provide, watch } from "vue";
import UiCardComingSoon from "@/components/ui/UiCardComingSoon.vue"; import UiCardComingSoon from "@/components/ui/UiCardComingSoon.vue";
@ -77,9 +82,9 @@ const hostLastWeekStats = useFetchStats<XenApiHost, HostStats>(
GRANULARITY.Hours GRANULARITY.Hours
); );
provide("hostStats", hostStats); provide(IK_HOST_STATS, hostStats);
provide("vmStats", vmStats); provide(IK_VM_STATS, vmStats);
provide("hostLastWeekStats", hostLastWeekStats); provide(IK_HOST_LAST_WEEK_STATS, hostLastWeekStats);
watch(runningHosts, (hosts, previousHosts) => { watch(runningHosts, (hosts, previousHosts) => {
// turned On // turned On