feat: add a clear button for selectors and stop create modal overflow

This commit is contained in:
tonyaellie
2025-12-22 23:24:01 +00:00
parent 37890c2a22
commit 9a9e3d462e
8 changed files with 89 additions and 16 deletions

View File

@@ -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 -->

View File

@@ -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;

View File

@@ -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"

View File

@@ -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"

View File

@@ -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);

View File

@@ -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"

View File

@@ -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);

View File

@@ -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",