mirror of
https://github.com/haexhub/haex-hub.git
synced 2025-12-17 06:30:50 +01:00
implemented search
This commit is contained in:
44
src/components/haex/pass/dialog/deleteItem.vue
Normal file
44
src/components/haex/pass/dialog/deleteItem.vue
Normal file
@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<UiDialogConfirm
|
||||
v-model:open="showConfirmDeleteDialog"
|
||||
:confirm-label="final ? t('final.label') : t('label')"
|
||||
:title="final ? t('final.title') : t('title', { itemName })"
|
||||
@abort="$emit('abort')"
|
||||
@confirm="$emit('confirm')"
|
||||
>
|
||||
{{
|
||||
final ? t('final.question', { itemName }) : t('question', { itemName })
|
||||
}}
|
||||
</UiDialogConfirm>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const { t } = useI18n()
|
||||
|
||||
const showConfirmDeleteDialog = defineModel<boolean>('open')
|
||||
defineProps<{ final?: boolean; itemName?: string | null }>()
|
||||
|
||||
defineEmits(['confirm', 'abort'])
|
||||
</script>
|
||||
|
||||
<i18n lang="yaml">
|
||||
de:
|
||||
title: Eintrag löschen
|
||||
question: Soll der Eintrag "{itemName}" in den Papierkorb verschoben werden?
|
||||
label: Verschieben
|
||||
|
||||
final:
|
||||
title: Eintrag endgültig löschen
|
||||
question: Soll der Eintrag "{itemName}" endgültig gelöscht werden?
|
||||
label: Löschen
|
||||
|
||||
en:
|
||||
title: Delete Entry
|
||||
question: Should the “{itemName}” entry be moved to the recycle bin?
|
||||
label: Move
|
||||
|
||||
final:
|
||||
title: Delete entry permanently
|
||||
question: Should the entry “{itemName}” be permanently deleted?
|
||||
label: Delete
|
||||
</i18n>
|
||||
47
src/components/haex/pass/dialog/unsavedChanges.vue
Normal file
47
src/components/haex/pass/dialog/unsavedChanges.vue
Normal file
@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<UiDialogConfirm
|
||||
:confirm-label="t('label')"
|
||||
:title="t('title')"
|
||||
@abort="$emit('abort')"
|
||||
@confirm="onConfirm"
|
||||
v-model:open="showUnsavedChangesDialog"
|
||||
>
|
||||
{{ t('question') }}
|
||||
</UiDialogConfirm>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const { t } = useI18n()
|
||||
|
||||
const showUnsavedChangesDialog = defineModel<boolean>('open')
|
||||
const ignoreChanges = defineModel<boolean>('ignoreChanges')
|
||||
const { hasChanges } = defineProps<{ hasChanges: boolean }>()
|
||||
|
||||
const emit = defineEmits(['confirm', 'abort'])
|
||||
|
||||
const onConfirm = () => {
|
||||
ignoreChanges.value = true
|
||||
emit('confirm')
|
||||
}
|
||||
|
||||
onBeforeRouteLeave(() => {
|
||||
if (hasChanges && !ignoreChanges.value) {
|
||||
showUnsavedChangesDialog.value = true
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
</script>
|
||||
|
||||
<i18n lang="yaml">
|
||||
de:
|
||||
title: Nicht gespeicherte Änderungen
|
||||
question: Sollen die Änderungen verworfen werden?
|
||||
label: Verwerfen
|
||||
|
||||
en:
|
||||
title: Unsaved changes
|
||||
question: Should the changes be discarded?
|
||||
label: discard
|
||||
</i18n>
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="breadcrumbs sticky top-0">
|
||||
<div class="breadcrumbs">
|
||||
<ul>
|
||||
<li>
|
||||
<NuxtLinkLocale :to="{ name: 'passwordGroupItems' }">
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<div class="p-1">
|
||||
<UiCard
|
||||
v-if="group"
|
||||
:title="mode === 'create' ? t('title.create') : t('title.edit')"
|
||||
:title="mode === 'edit' ? t('title.edit') : t('title.create')"
|
||||
icon="mdi:folder-plus-outline"
|
||||
@close="$emit('close')"
|
||||
body-class="px-0"
|
||||
@ -12,9 +12,9 @@
|
||||
@submit.prevent="$emit('submit')"
|
||||
>
|
||||
<UiInput
|
||||
:check-input="check"
|
||||
:label="t('name')"
|
||||
:placeholder="t('name')"
|
||||
:read_only
|
||||
autofocus
|
||||
v-model="group.name"
|
||||
ref="nameRef"
|
||||
@ -23,9 +23,9 @@
|
||||
|
||||
<UiInput
|
||||
v-model="group.description"
|
||||
:check-input="check"
|
||||
:label="t('description')"
|
||||
:placeholder="t('description')"
|
||||
:read_only
|
||||
@keyup.enter="$emit('submit')"
|
||||
/>
|
||||
|
||||
@ -33,12 +33,16 @@
|
||||
<UiSelectIcon
|
||||
v-model="group.icon"
|
||||
default-icon="mdi:folder-outline"
|
||||
:read_only
|
||||
/>
|
||||
|
||||
<UiSelectColor v-model="group.color" />
|
||||
<UiSelectColor
|
||||
v-model="group.color"
|
||||
:read_only
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap justify-end gap-4">
|
||||
<!-- <div class="flex flex-wrap justify-end gap-4">
|
||||
<UiButton
|
||||
class="btn-error btn-outline flex-1"
|
||||
@click="$emit('close')"
|
||||
@ -54,7 +58,7 @@
|
||||
{{ mode === 'create' ? t('create') : t('save') }}
|
||||
<Icon name="mdi:check" />
|
||||
</UiButton>
|
||||
</div>
|
||||
</div> -->
|
||||
</form>
|
||||
</UiCard>
|
||||
</div>
|
||||
@ -64,13 +68,14 @@
|
||||
import type { SelectHaexPasswordsGroups } from '~~/src-tauri/database/schemas/vault'
|
||||
|
||||
const group = defineModel<SelectHaexPasswordsGroups | null>()
|
||||
defineEmits(['close', 'submit', 'back'])
|
||||
defineProps<{ mode: 'create' | 'edit' }>()
|
||||
const { read_only = false } = defineProps<{
|
||||
read_only?: boolean
|
||||
mode: 'create' | 'edit'
|
||||
}>()
|
||||
defineEmits(['close', 'submit'])
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const check = ref<boolean>(false)
|
||||
|
||||
const nameRef = useTemplateRef('nameRef')
|
||||
onStartTyping(() => {
|
||||
nameRef.value?.inputRef?.focus()
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
:with-copy-button
|
||||
:read_only
|
||||
v-model.trim="itemDetails.username"
|
||||
@keyup.enter="$emit('submit')"
|
||||
/>
|
||||
|
||||
<UiInputPassword
|
||||
@ -33,6 +34,7 @@
|
||||
:read_only
|
||||
:with-copy-button
|
||||
v-model.trim="itemDetails.password"
|
||||
@keyup.enter="$emit('submit')"
|
||||
>
|
||||
<template #append>
|
||||
<UiDialogPasswordGenerator
|
||||
@ -51,6 +53,7 @@
|
||||
:read_only
|
||||
:with-copy-button
|
||||
v-model="itemDetails.url"
|
||||
@keyup.enter="$emit('submit')"
|
||||
/>
|
||||
|
||||
<UiSelectIcon
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="p-1">
|
||||
<UiCard
|
||||
body-class="rounded overflow-auto px-0 h-full"
|
||||
body-class="rounded overflow-auto p-0 h-full"
|
||||
@close="onClose"
|
||||
>
|
||||
<div class="">
|
||||
|
||||
102
src/components/haex/pass/menu/bottom.vue
Normal file
102
src/components/haex/pass/menu/bottom.vue
Normal file
@ -0,0 +1,102 @@
|
||||
<template>
|
||||
<div
|
||||
class="fixed bottom-4 flex justify-between transition-all pointer-events-none right-0 sm:items-center items-end"
|
||||
:class="[isVisible ? 'left-15 ' : 'left-0']"
|
||||
>
|
||||
<div class="flex items-center justify-center flex-1">
|
||||
<UiButton
|
||||
v-show="showCloseButton"
|
||||
:tooltip="t('abort')"
|
||||
@click="$emit('close')"
|
||||
class="btn-accent btn-square"
|
||||
>
|
||||
<Icon name="mdi:close" />
|
||||
</UiButton>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<UiButton
|
||||
v-show="showEditButton"
|
||||
:tooltip="t('edit')"
|
||||
@click="$emit('edit')"
|
||||
class="btn-xl btn-square btn-primary"
|
||||
>
|
||||
<Icon
|
||||
name="mdi:pencil-outline"
|
||||
class="size-11 shrink-0"
|
||||
/>
|
||||
</UiButton>
|
||||
|
||||
<UiButton
|
||||
v-show="showReadonlyButton"
|
||||
:tooltip="t('readonly')"
|
||||
class="btn-xl btn-square btn-primary"
|
||||
@click="$emit('readonly')"
|
||||
>
|
||||
<Icon
|
||||
name="mdi:pencil-off-outline"
|
||||
class="size-11 shrink-0"
|
||||
/>
|
||||
</UiButton>
|
||||
|
||||
<UiButton
|
||||
v-show="showSaveButton"
|
||||
:tooltip="t('save')"
|
||||
class="btn-xl btn-square btn-primary motion-duration-2000"
|
||||
:class="{ 'motion-preset-pulse-sm': hasChanges }"
|
||||
@click="$emit('save')"
|
||||
>
|
||||
<Icon
|
||||
name="mdi:content-save-outline"
|
||||
class="size-11 shrink-0"
|
||||
/>
|
||||
</UiButton>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-center flex-1">
|
||||
<UiButton
|
||||
v-show="showDeleteButton"
|
||||
:tooltip="t('delete')"
|
||||
class="btn-square btn-error"
|
||||
@click="$emit('delete')"
|
||||
>
|
||||
<Icon
|
||||
name="mdi:trash-outline"
|
||||
class="shrink-0"
|
||||
/>
|
||||
</UiButton>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const { isVisible } = storeToRefs(useSidebarStore())
|
||||
const { t } = useI18n()
|
||||
|
||||
defineProps<{
|
||||
showCloseButton?: boolean
|
||||
showDeleteButton?: boolean
|
||||
showEditButton?: boolean
|
||||
showReadonlyButton?: boolean
|
||||
showSaveButton?: boolean
|
||||
hasChanges?: boolean
|
||||
}>()
|
||||
|
||||
defineEmits(['close', 'edit', 'readonly', 'save', 'delete'])
|
||||
</script>
|
||||
|
||||
<i18n lang="yaml">
|
||||
de:
|
||||
save: Speichern
|
||||
abort: Abbrechen
|
||||
edit: Bearbeiten
|
||||
readonly: Lesemodus
|
||||
delete: Löschen
|
||||
|
||||
en:
|
||||
save: Save
|
||||
abort: Abort
|
||||
edit: Edit
|
||||
readonly: Read Mode
|
||||
delete: Delete
|
||||
</i18n>
|
||||
@ -1,5 +1,8 @@
|
||||
<template>
|
||||
<div v-if="menuItems?.length">
|
||||
<div
|
||||
v-if="menuItems?.length"
|
||||
class="h-full"
|
||||
>
|
||||
<ul
|
||||
class="flex flex-col w-full h-full gap-y-2 first:rounded-t-md last:rounded-b-md p-1"
|
||||
ref="listRef"
|
||||
@ -40,7 +43,7 @@
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="flex justify-center items-center px-20 h-full"
|
||||
class="flex justify-center items-center px-20 flex-1 bg-red-100"
|
||||
>
|
||||
<UiIconNoData class="text-primary size-24 shrink-0" />
|
||||
<!-- <p>{{ t('empty') }}</p> -->
|
||||
@ -78,6 +81,7 @@ watch(selectedItems, () => {
|
||||
|
||||
const localePath = useLocalePath()
|
||||
const { ctrl } = useMagicKeys()
|
||||
const { search } = storeToRefs(useSearchStore())
|
||||
|
||||
const onClickItemAsync = async (item: IPasswordMenuItem) => {
|
||||
currentSelectedItem.value = null
|
||||
@ -91,6 +95,7 @@ const onClickItemAsync = async (item: IPasswordMenuItem) => {
|
||||
|
||||
if (!selectedItems.value.size) longPressedHook.value = false
|
||||
} else {
|
||||
search.value = ''
|
||||
if (item.type === 'group')
|
||||
await navigateTo(
|
||||
localePath({
|
||||
|
||||
@ -13,53 +13,55 @@
|
||||
</slot>
|
||||
</button>
|
||||
|
||||
<Teleport to="body">
|
||||
<div
|
||||
:id
|
||||
ref="modalRef"
|
||||
class="overlay modal overlay-open:opacity-100 overlay-open:duration-300 hidden modal-middle p-0 xs:p-2 --prevent-on-load-init pointer-events-auto max-w-none"
|
||||
role="dialog"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div class="hidden">
|
||||
<Teleport to="body">
|
||||
<div
|
||||
class="overlay-animation-target overlay-open:duration-300 overlay-open:opacity-100 transition-all ease-out modal-dialog"
|
||||
:id
|
||||
ref="modalRef"
|
||||
class="overlay modal overlay-open:opacity-100 overlay-open:duration-300 hidden modal-middle p-0 xs:p-2 --prevent-on-load-init pointer-events-auto max-w-none"
|
||||
role="dialog"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div class="modal-content justify-between">
|
||||
<div class="modal-header py-0 sm:py-4">
|
||||
<div
|
||||
v-if="title || $slots.title"
|
||||
class="modal-title py-4 break-all"
|
||||
>
|
||||
<slot name="title">
|
||||
{{ title }}
|
||||
</slot>
|
||||
<div
|
||||
class="overlay-animation-target overlay-open:duration-300 overlay-open:opacity-100 transition-all ease-out modal-dialog"
|
||||
>
|
||||
<div class="modal-content justify-between">
|
||||
<div class="modal-header py-0 sm:py-4">
|
||||
<div
|
||||
v-if="title || $slots.title"
|
||||
class="modal-title py-4 break-all"
|
||||
>
|
||||
<slot name="title">
|
||||
{{ title }}
|
||||
</slot>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-text btn-circle btn-sm absolute end-3 top-3"
|
||||
:aria-label="t('close')"
|
||||
tabindex="1"
|
||||
@click="open = false"
|
||||
>
|
||||
<Icon
|
||||
name="mdi:close"
|
||||
size="18"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-text btn-circle btn-sm absolute end-3 top-3"
|
||||
:aria-label="t('close')"
|
||||
tabindex="1"
|
||||
@click="open = false"
|
||||
>
|
||||
<Icon
|
||||
name="mdi:close"
|
||||
size="18"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body text-sm sm:text-base grow mt-0 pt-0">
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<div class="modal-body text-sm sm:text-base grow mt-0 pt-0">
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<div class="modal-footer flex-col sm:flex-row">
|
||||
<slot name="buttons" />
|
||||
<div class="modal-footer flex-col sm:flex-row">
|
||||
<slot name="buttons" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Teleport>
|
||||
</Teleport>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
@ -32,7 +32,7 @@
|
||||
>
|
||||
<li
|
||||
:is="itemIs"
|
||||
@click="$emit('select', item)"
|
||||
@click="read_only ? '' : $emit('select', item)"
|
||||
class="dropdown-item"
|
||||
v-for="item in items"
|
||||
>
|
||||
@ -59,6 +59,7 @@ const { itemIs = 'li', offset = '[--offset:0]' } = defineProps<{
|
||||
itemIs?: string
|
||||
activatorClass?: string
|
||||
offset?: string
|
||||
read_only?: boolean
|
||||
}>()
|
||||
|
||||
defineEmits<{ select: [T] }>()
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
:rules
|
||||
:type="type"
|
||||
:with-copy-button
|
||||
@keyup="(e) => $emit('keyup', e)"
|
||||
>
|
||||
<template #append>
|
||||
<slot name="append" />
|
||||
@ -38,6 +39,10 @@ defineProps<{
|
||||
withCopyButton?: boolean
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
keyup: [KeyboardEvent]
|
||||
}>()
|
||||
|
||||
const type = ref<'password' | 'text'>('password')
|
||||
|
||||
const tooglePasswordType = () => {
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
:rules
|
||||
:with-copy-button
|
||||
v-model.trim="value"
|
||||
@keyup="(e) => $emit('keyup', e)"
|
||||
>
|
||||
<template #append>
|
||||
<UiButton
|
||||
@ -38,6 +39,10 @@ defineProps({
|
||||
withCopyButton: Boolean,
|
||||
read_only: Boolean,
|
||||
})
|
||||
|
||||
defineEmits<{
|
||||
keyup: [KeyboardEvent]
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<i18n lang="json">
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
:items="icons"
|
||||
class="btn"
|
||||
@select="(newIcon) => (iconName = newIcon)"
|
||||
:read_only
|
||||
>
|
||||
<template #activator>
|
||||
<Icon :name="iconName ? iconName : defaultIcon || icons.at(0)" />
|
||||
@ -13,7 +14,7 @@
|
||||
<li
|
||||
class="dropdown-item"
|
||||
v-for="item in items"
|
||||
@click="iconName = item"
|
||||
@click="read_only ? '' : (iconName = item)"
|
||||
>
|
||||
<Icon
|
||||
:name="item"
|
||||
@ -73,5 +74,5 @@ const icons = [
|
||||
|
||||
const iconName = defineModel<string | undefined | null>()
|
||||
|
||||
defineProps<{ defaultIcon?: string }>()
|
||||
defineProps<{ defaultIcon?: string; read_only?: boolean }>()
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user