mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
update list input
* add focus after add entry or when deleting last entry * handle escape key * add dark mode * update actions that will close the dropdown * fix issue with empty array value * update tags
This commit is contained in:
parent
9400784fe3
commit
6cbb2c3a38
6 changed files with 114 additions and 52 deletions
|
|
@ -217,11 +217,11 @@ body {
|
|||
}
|
||||
|
||||
.input-list-add {
|
||||
@apply transition absolute top-1 right-9 cursor-pointer transition-transform h-6 w-6 disabled:cursor-not-allowed disabled:opacity-50 disabled:dark:opacity-75;
|
||||
@apply transition absolute top-1 right-9 cursor-pointer transition-transform h-6 w-6 disabled:cursor-not-allowed disabled:opacity-50 disabled:dark:opacity-50;
|
||||
}
|
||||
|
||||
.input-list-svg {
|
||||
@apply absolute top-1.5 right-2 pointer-events-none transition-transform h-6 w-6 fill-gray-600 dark:fill-gray-500;
|
||||
@apply absolute top-1.5 right-2 pointer-events-none transition-transform h-6 w-6 stroke-gray-600 dark:stroke-gray-500;
|
||||
}
|
||||
|
||||
.checkbox-container {
|
||||
|
|
@ -273,34 +273,47 @@ body {
|
|||
@apply flex max-h-[200px] overflow-x-hidden overflow-y-auto flex-col w-fit mt-2;
|
||||
}
|
||||
|
||||
.open.select-dropdown-container {
|
||||
@apply absolute z-[2000];
|
||||
}
|
||||
|
||||
.open.select-dropdown-container {
|
||||
animation: dropOpen 0.1s linear;
|
||||
}
|
||||
|
||||
.close.select-dropdown-container {
|
||||
@apply hidden;
|
||||
.list-dropdown-container {
|
||||
@apply flex max-h-[200px] overflow-x-hidden overflow-y-auto flex-col w-fit mt-2;
|
||||
}
|
||||
|
||||
.select-dropdown-btn {
|
||||
@apply outline-offset-[-4px] border-b border-l border-r border-gray-300 hover:brightness-90 bg-white text-gray-700 my-0 relative px-2 py-2 text-left align-middle transition-all rounded-none cursor-pointer leading-normal text-sm ease-in tracking-normal dark:border-slate-600 dark:bg-slate-700 dark:text-gray-300;
|
||||
}
|
||||
|
||||
.list-dropdown-btn {
|
||||
@apply outline-offset-[-4px] border-b border-l border-r border-gray-300 hover:brightness-90 bg-white text-gray-700 my-0 relative px-2 py-2 flex justify-between align-middle transition-all rounded-none cursor-pointer leading-normal text-sm ease-in tracking-normal dark:border-slate-600 dark:bg-slate-700 dark:text-gray-300;
|
||||
}
|
||||
|
||||
.active.select-dropdown-btn {
|
||||
@apply dark:border-slate-600 dark:bg-slate-600 bg-gray-200 font-semibold dark:text-gray-100;
|
||||
}
|
||||
|
||||
.first.select-dropdown-btn {
|
||||
.first.select-dropdown-btn,
|
||||
.first.list-dropdown-btn {
|
||||
@apply border-t rounded-t;
|
||||
}
|
||||
|
||||
.last.select-dropdown-btn {
|
||||
.last.select-dropdown-btn,
|
||||
.last.list-dropdown-btn {
|
||||
@apply rounded-b;
|
||||
}
|
||||
|
||||
.open.select-dropdown-container,
|
||||
.open.list-dropdown-container {
|
||||
@apply absolute z-[2000];
|
||||
}
|
||||
|
||||
.open.select-dropdown-container,
|
||||
.open.list-dropdown-container{
|
||||
animation: dropOpen 0.1s linear;
|
||||
}
|
||||
|
||||
.close.select-dropdown-container,
|
||||
.close.list-dropdown-container {
|
||||
@apply hidden;
|
||||
}
|
||||
|
||||
.input-title {
|
||||
@apply transition duration-300 ease-in-out dark:opacity-90 text-sm font-bold m-0 dark:text-gray-300;
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -276,6 +276,11 @@ function closeScroll(e) {
|
|||
select.isOpen = false;
|
||||
}
|
||||
|
||||
function closeEscape(e) {
|
||||
if (e.key !== "Escape") return;
|
||||
select.isOpen = false;
|
||||
}
|
||||
|
||||
// Check after a key is pressed if the current active element is the select button
|
||||
// If not close the select
|
||||
function closeTab(e) {
|
||||
|
|
@ -291,13 +296,15 @@ function closeTab(e) {
|
|||
watch(select, () => {
|
||||
if (select.isOpen) {
|
||||
inputEl.value.focus();
|
||||
window.addEventListener("click", closeOutside);
|
||||
window.addEventListener("scroll", closeScroll, true);
|
||||
window.addEventListener("click", closeOutside);
|
||||
window.addEventListener("keydown", closeTab);
|
||||
window.addEventListener("keydown", closeEscape);
|
||||
} else {
|
||||
window.removeEventListener("click", closeOutside);
|
||||
window.removeEventListener("scroll", closeScroll, true);
|
||||
window.removeEventListener("click", closeOutside);
|
||||
window.removeEventListener("keydown", closeTab);
|
||||
window.removeEventListener("keydown", closeEscape);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -226,12 +226,13 @@ function openSelect() {
|
|||
}, 10);
|
||||
}
|
||||
|
||||
function closeSelect() {
|
||||
inp.isOpen = false;
|
||||
}
|
||||
|
||||
// Close select when clicked outside logic
|
||||
function closeOutside(e) {
|
||||
if (
|
||||
e.target.hasAttribute("data-select-item") ||
|
||||
e.target.hasAttribute("data-delete-entry")
|
||||
)
|
||||
return;
|
||||
try {
|
||||
if (e.target !== inputEl.value && e.target !== inputEl.value) {
|
||||
inp.isOpen = false;
|
||||
|
|
@ -254,13 +255,23 @@ function closeScroll(e) {
|
|||
inp.isOpen = false;
|
||||
}
|
||||
|
||||
function closeEscape(e) {
|
||||
if (e.key !== "Escape") return;
|
||||
inp.isOpen = false;
|
||||
}
|
||||
|
||||
// Check after a key is pressed if the current active element is the select button
|
||||
// If not close the select
|
||||
function closeTab(e) {
|
||||
if (e.key !== "Tab" && e.key !== "Shift-Tab") return;
|
||||
setTimeout(() => {
|
||||
const activeEl = document.activeElement;
|
||||
if (activeEl.closest("[data-select-dropdown]") !== selectDropdown.value)
|
||||
if (
|
||||
activeEl.closest("[data-select-dropdown]") !== selectDropdown.value &&
|
||||
activeEl
|
||||
?.closest("[data-input-container]")
|
||||
?.querySelector("[data-toggle-dropdown]") !== inputEl.value
|
||||
)
|
||||
return (inp.isOpen = false);
|
||||
}, 10);
|
||||
}
|
||||
|
|
@ -276,9 +287,9 @@ function addEntry(e) {
|
|||
)
|
||||
return;
|
||||
|
||||
inp.value = `${inp.enterValue}${props.separator}${inp.value}`;
|
||||
console.log(inp.value);
|
||||
inp.value = `${inp.enterValue}${props.separator}${inp.value}`.trim();
|
||||
inp.enterValue = "";
|
||||
inputEl.value.focus();
|
||||
}
|
||||
|
||||
// Case the entry is focus and value is valid, add it to the list
|
||||
|
|
@ -286,19 +297,25 @@ function deleteValue(value) {
|
|||
inp.value = inp.value
|
||||
.split(props.separator)
|
||||
.filter((val) => val !== value)
|
||||
.join(props.separator);
|
||||
.join(props.separator)
|
||||
.trim();
|
||||
|
||||
// Case no item anymore, focus on main input
|
||||
if (!inp.value) inputEl.value.focus();
|
||||
}
|
||||
|
||||
// Close select dropdown when clicked outside element
|
||||
watch(inp, () => {
|
||||
if (inp.isOpen) {
|
||||
window.addEventListener("click", closeOutside);
|
||||
window.addEventListener("scroll", closeScroll, true);
|
||||
window.addEventListener("click", closeOutside);
|
||||
window.addEventListener("keydown", closeEscape);
|
||||
window.addEventListener("keydown", closeTab);
|
||||
window.addEventListener("keydown", addEntry);
|
||||
} else {
|
||||
window.removeEventListener("click", closeOutside);
|
||||
window.removeEventListener("scroll", closeScroll, true);
|
||||
window.removeEventListener("click", closeOutside);
|
||||
window.removeEventListener("keydown", closeEscape);
|
||||
window.removeEventListener("keydown", closeTab);
|
||||
window.removeEventListener("keydown", addEntry);
|
||||
}
|
||||
|
|
@ -339,7 +356,7 @@ const emits = defineEmits(["inp"]);
|
|||
|
||||
<!--custom-->
|
||||
<div class="relative">
|
||||
<div class="input-regular-container">
|
||||
<div data-input-container class="input-regular-container">
|
||||
<input
|
||||
data-toggle-dropdown
|
||||
:aria-controls="`${inp.id}-custom`"
|
||||
|
|
@ -349,6 +366,7 @@ const emits = defineEmits(["inp"]);
|
|||
ref="inputEl"
|
||||
@input="
|
||||
(e) => {
|
||||
openSelect();
|
||||
inp.enterValue = e.target.value;
|
||||
$emit('inp', inp.value);
|
||||
}
|
||||
|
|
@ -378,6 +396,7 @@ const emits = defineEmits(["inp"]);
|
|||
type="text"
|
||||
/>
|
||||
<button
|
||||
:tabindex="props.tabId"
|
||||
data-add-entry
|
||||
@click.prevent="(e) => addEntry(e)"
|
||||
:disabled="
|
||||
|
|
@ -412,16 +431,15 @@ const emits = defineEmits(["inp"]);
|
|||
</svg>
|
||||
</div>
|
||||
<!-- dropdown-->
|
||||
<div
|
||||
<ul
|
||||
data-select-dropdown
|
||||
:aria-hidden="inp.isOpen ? 'false' : 'true'"
|
||||
:aria-expanded="inp.isOpen ? 'true' : 'false'"
|
||||
ref="selectDropdown"
|
||||
role="listbox"
|
||||
:style="{ width: selectWidth }"
|
||||
:id="`${inp.id}-custom`"
|
||||
:class="[inp.isOpen ? 'open' : 'close']"
|
||||
class="select-dropdown-container"
|
||||
class="list-dropdown-container"
|
||||
:aria-description="$t('inp_select_dropdown_desc')"
|
||||
>
|
||||
<ErrorDropdown
|
||||
|
|
@ -430,25 +448,42 @@ const emits = defineEmits(["inp"]);
|
|||
:isValue="!!inp.value"
|
||||
:isValueTaken="inp.isEnterMatching"
|
||||
/>
|
||||
<button
|
||||
@click="deleteValue(value)"
|
||||
v-if="inp.isValid && !inp.isEnterMatching && inp.isEnterValid"
|
||||
role="option"
|
||||
:tabindex="inp.isOpen ? props.tabId : '-1'"
|
||||
v-for="(value, id) in inp.values"
|
||||
:class="[
|
||||
id === 0 ? 'first' : '',
|
||||
id === inp.values.length - 1 ? 'last' : '',
|
||||
'select-dropdown-btn',
|
||||
]"
|
||||
data-select-item
|
||||
:data-setting-id="inp.id"
|
||||
:data-setting-value="value"
|
||||
:aria-controls="`${inp.id}-text`"
|
||||
<template
|
||||
v-if="
|
||||
inp.isValid &&
|
||||
!inp.isEnterMatching &&
|
||||
inp.isEnterValid &&
|
||||
inp.values.length >= 0 &&
|
||||
inp.values[0]
|
||||
"
|
||||
>
|
||||
{{ value }}
|
||||
</button>
|
||||
</div>
|
||||
<li
|
||||
:tabindex="inp.isOpen ? props.tabId : '-1'"
|
||||
v-for="(value, id) in inp.values"
|
||||
:class="[
|
||||
id === 0 ? 'first' : '',
|
||||
id === inp.values.length - 1 ? 'last' : '',
|
||||
'list-dropdown-btn',
|
||||
]"
|
||||
data-select-item
|
||||
:data-setting-id="inp.id"
|
||||
:data-setting-value="value"
|
||||
:aria-controls="`${inp.id}-text`"
|
||||
>
|
||||
<span>
|
||||
{{ value }}
|
||||
</span>
|
||||
<button
|
||||
data-delete-entry
|
||||
:tabindex="inp.isOpen ? props.tabId : '-1'"
|
||||
data-is="input"
|
||||
@click="deleteValue(value)"
|
||||
>
|
||||
<Icons :iconName="'trash'" />
|
||||
</button>
|
||||
</li>
|
||||
</template>
|
||||
</ul>
|
||||
<ErrorField
|
||||
v-if="!inp.isOpen"
|
||||
:errorClass="'input'"
|
||||
|
|
|
|||
|
|
@ -257,6 +257,11 @@ function closeScroll(e) {
|
|||
select.isOpen = false;
|
||||
}
|
||||
|
||||
function closeEscape(e) {
|
||||
if (e.key !== "Escape") return;
|
||||
select.isOpen = false;
|
||||
}
|
||||
|
||||
// Check after a key is pressed if the current active element is the select button
|
||||
// If not close the select
|
||||
function closeTab(e) {
|
||||
|
|
@ -271,13 +276,15 @@ function closeTab(e) {
|
|||
// Close select dropdown when clicked outside element
|
||||
watch(select, () => {
|
||||
if (select.isOpen) {
|
||||
window.addEventListener("click", closeOutside);
|
||||
window.addEventListener("scroll", closeScroll, true);
|
||||
window.addEventListener("click", closeOutside);
|
||||
window.addEventListener("keydown", closeTab);
|
||||
window.addEventListener("keydown", closeEscape);
|
||||
} else {
|
||||
window.removeEventListener("click", closeOutside);
|
||||
window.removeEventListener("scroll", closeScroll, true);
|
||||
window.removeEventListener("click", closeOutside);
|
||||
window.removeEventListener("keydown", closeTab);
|
||||
window.removeEventListener("keydown", closeEscape);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import DashboardLayout from "@components/Dashboard/Layout.vue";
|
|||
|
||||
const list = {
|
||||
id: "test-input",
|
||||
value: "yes no maybe",
|
||||
value: "yes no maybe I don't know can you repeat the question",
|
||||
name: "test-list",
|
||||
label: "Test list",
|
||||
inpType: "list",
|
||||
|
|
|
|||
Loading…
Reference in a new issue