mirror of
https://github.com/sysadminsmedia/homebox.git
synced 2025-12-24 22:39:14 +01:00
feat: add a clear button for selectors and stop create modal overflow
This commit is contained in:
@@ -39,7 +39,7 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<form class="flex flex-col gap-2" @submit.prevent="create()">
|
||||
<form class="flex min-w-0 flex-col gap-2" @submit.prevent="create()">
|
||||
<LocationSelector v-model="form.location" />
|
||||
|
||||
<!-- Template Info Display - Collapsible banner with distinct styling -->
|
||||
|
||||
@@ -6,12 +6,25 @@
|
||||
<Popover v-model:open="open">
|
||||
<PopoverTrigger as-child>
|
||||
<Button :id="id" variant="outline" role="combobox" :aria-expanded="open" class="w-full justify-between">
|
||||
<span>
|
||||
<span class="truncate text-left">
|
||||
<slot name="display" v-bind="{ item: value }">
|
||||
{{ displayValue(value) || localizedPlaceholder }}
|
||||
</slot>
|
||||
</span>
|
||||
<ChevronsUpDown class="ml-2 size-4 shrink-0 opacity-50" />
|
||||
|
||||
<span class="ml-2 flex items-center">
|
||||
<button
|
||||
v-if="value"
|
||||
type="button"
|
||||
class="shrink-0 rounded p-1 hover:bg-primary/20"
|
||||
:aria-label="t('components.item.selector.clear')"
|
||||
@click.stop.prevent="clearSelection"
|
||||
>
|
||||
<X class="size-4" />
|
||||
</button>
|
||||
|
||||
<ChevronsUpDown class="ml-2 size-4 shrink-0 opacity-50" />
|
||||
</span>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent class="w-[--reka-popper-anchor-width] p-0">
|
||||
@@ -44,7 +57,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from "vue";
|
||||
import { Check, ChevronsUpDown } from "lucide-vue-next";
|
||||
import { Check, ChevronsUpDown, X } from "lucide-vue-next";
|
||||
import fuzzysort from "fuzzysort";
|
||||
import { useVModel } from "@vueuse/core";
|
||||
import { useI18n } from "vue-i18n";
|
||||
@@ -174,6 +187,12 @@
|
||||
open.value = false;
|
||||
}
|
||||
|
||||
function clearSelection() {
|
||||
value.value = null;
|
||||
search.value = "";
|
||||
open.value = false;
|
||||
}
|
||||
|
||||
const filtered = computed(() => {
|
||||
let baseItems = props.items;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<BaseModal :dialog-id="DialogID.CreateLabel" :title="$t('components.label.create_modal.title')">
|
||||
<form class="flex flex-col gap-2" @submit.prevent="create()">
|
||||
<form class="flex min-w-0 flex-col gap-2" @submit.prevent="create()">
|
||||
<FormTextField
|
||||
v-model="form.name"
|
||||
:trigger-focus="focused"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<BaseModal :dialog-id="DialogID.CreateLocation" :title="$t('components.location.create_modal.title')">
|
||||
<form class="flex flex-col gap-2" @submit.prevent="create()">
|
||||
<form class="flex min-w-0 flex-col gap-2" @submit.prevent="create()">
|
||||
<LocationSelector v-model="form.parent" />
|
||||
<FormTextField
|
||||
ref="locationNameRef"
|
||||
|
||||
@@ -7,8 +7,23 @@
|
||||
<Popover v-model:open="open">
|
||||
<PopoverTrigger as-child>
|
||||
<Button :id="id" variant="outline" role="combobox" :aria-expanded="open" class="w-full justify-between">
|
||||
{{ value && value.name ? value.name : $t("components.location.selector.select_location") }}
|
||||
<ChevronsUpDown class="ml-2 size-4 shrink-0 opacity-50" />
|
||||
<span class="min-w-0 flex-auto truncate text-left">
|
||||
{{ value && value.name ? value.name : $t("components.location.selector.select_location") }}
|
||||
</span>
|
||||
|
||||
<span class="ml-2 flex items-center">
|
||||
<button
|
||||
v-if="value"
|
||||
type="button"
|
||||
class="shrink-0 rounded p-1 hover:bg-primary/20"
|
||||
:aria-label="$t('components.location.selector.clear')"
|
||||
@click.stop.prevent="clearSelection"
|
||||
>
|
||||
<X class="size-4" />
|
||||
</button>
|
||||
|
||||
<ChevronsUpDown class="ml-2 size-4 shrink-0 opacity-50" />
|
||||
</span>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent class="w-[--reka-popper-anchor-width] p-0">
|
||||
@@ -46,7 +61,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Check, ChevronsUpDown } from "lucide-vue-next";
|
||||
import { Check, ChevronsUpDown, X } from "lucide-vue-next";
|
||||
import fuzzysort from "fuzzysort";
|
||||
import { Button } from "~/components/ui/button";
|
||||
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "~/components/ui/command";
|
||||
@@ -79,6 +94,12 @@
|
||||
open.value = false;
|
||||
}
|
||||
|
||||
function clearSelection() {
|
||||
value.value = null;
|
||||
search.value = "";
|
||||
open.value = false;
|
||||
}
|
||||
|
||||
const filteredLocations = computed(() => {
|
||||
const filtered = fuzzysort.go(search.value, locations.value, { key: "name", all: true }).map(i => i.obj);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<BaseModal :dialog-id="DialogID.CreateTemplate" :title="$t('components.template.create_modal.title')">
|
||||
<form class="flex flex-col gap-2" @submit.prevent="create()">
|
||||
<form class="flex min-w-0 flex-col gap-2" @submit.prevent="create()">
|
||||
<FormTextField
|
||||
v-model="form.name"
|
||||
:autofocus="true"
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<Separator class="my-2" />
|
||||
<h3 class="text-sm font-medium">{{ $t("components.template.form.default_item_values") }}</h3>
|
||||
<div class="grid gap-2">
|
||||
<div class="flex min-w-0 flex-col gap-2">
|
||||
<FormTextField v-model="form.defaultName" :label="$t('components.template.form.item_name')" :max-length="255" />
|
||||
<FormTextArea
|
||||
v-model="form.defaultDescription"
|
||||
|
||||
@@ -38,6 +38,14 @@
|
||||
</div>
|
||||
</CommandItem>
|
||||
</CommandGroup>
|
||||
<CommandSeparator v-if="value" />
|
||||
<CommandGroup v-if="value">
|
||||
<CommandItem v-if="value" value="clear-selection" @select="clearSelection">
|
||||
<div class="flex w-full">
|
||||
{{ $t("components.template.selector.clear") }}
|
||||
</div>
|
||||
</CommandItem>
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
@@ -79,6 +87,13 @@
|
||||
</div>
|
||||
</CommandItem>
|
||||
</CommandGroup>
|
||||
<CommandSeparator />
|
||||
<CommandItem v-if="value" value="clear-selection" @select="clearSelection">
|
||||
<X :class="cn('mr-2 h-4 w-4')" />
|
||||
<div class="flex w-full">
|
||||
<span class="text-destructive">{{ $t("components.template.selector.clear") }}</span>
|
||||
</div>
|
||||
</CommandItem>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
@@ -87,10 +102,18 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Check, ChevronsUpDown } from "lucide-vue-next";
|
||||
import { Check, ChevronsUpDown, X } from "lucide-vue-next";
|
||||
import fuzzysort from "fuzzysort";
|
||||
import { Button } from "~/components/ui/button";
|
||||
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "~/components/ui/command";
|
||||
import {
|
||||
Command,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandInput,
|
||||
CommandItem,
|
||||
CommandList,
|
||||
CommandSeparator,
|
||||
} from "~/components/ui/command";
|
||||
import { Label } from "~/components/ui/label";
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "~/components/ui/popover";
|
||||
import { cn } from "~/lib/utils";
|
||||
@@ -132,6 +155,13 @@
|
||||
open.value = false;
|
||||
}
|
||||
|
||||
function clearSelection() {
|
||||
value.value = null;
|
||||
emit("template-selected", null);
|
||||
search.value = "";
|
||||
open.value = false;
|
||||
}
|
||||
|
||||
const filteredTemplates = computed(() => {
|
||||
if (!templates.value) return [];
|
||||
const filtered = fuzzysort.go(search.value, templates.value, { key: "name", all: true }).map(i => i.obj);
|
||||
|
||||
@@ -136,7 +136,8 @@
|
||||
"no_results": "No Results Found",
|
||||
"placeholder": "Select…",
|
||||
"search_placeholder": "Type to search…",
|
||||
"searching": "Searching…"
|
||||
"searching": "Searching…",
|
||||
"clear": "Clear Item Selection"
|
||||
},
|
||||
"view": {
|
||||
"change_details": {
|
||||
@@ -221,7 +222,8 @@
|
||||
"no_location_found": "No location found",
|
||||
"parent_location": "Parent Location",
|
||||
"search_location": "Search Locations",
|
||||
"select_location": "Select a Location"
|
||||
"select_location": "Select a Location",
|
||||
"clear": "Clear Location Selection"
|
||||
},
|
||||
"tree": {
|
||||
"no_locations": "No locations available. Add new locations through the\n '<span class=\"link-primary\">'Create'</span>' button on the navigation bar."
|
||||
@@ -268,7 +270,8 @@
|
||||
"label": "Template (Optional)",
|
||||
"not_found": "No template found",
|
||||
"search": "Search templates...",
|
||||
"select": "Select template..."
|
||||
"select": "Select template...",
|
||||
"clear": "Clear Template Selection"
|
||||
},
|
||||
"toast": {
|
||||
"applied": "Template \"{name}\" applied",
|
||||
|
||||
Reference in New Issue
Block a user