mirror of
https://github.com/photoprism/photoprism.git
synced 2025-12-11 16:24:11 +01:00
People: Adjust menuProps and add focus trap to PConfirmDialog #5307
Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
@@ -2,24 +2,28 @@
|
|||||||
<v-dialog
|
<v-dialog
|
||||||
ref="dialog"
|
ref="dialog"
|
||||||
:model-value="visible"
|
:model-value="visible"
|
||||||
|
:close-delay="0"
|
||||||
|
:open-delay="0"
|
||||||
persistent
|
persistent
|
||||||
|
scrim
|
||||||
max-width="360"
|
max-width="360"
|
||||||
class="p-dialog p-confirm-dialog"
|
class="p-dialog p-confirm-dialog"
|
||||||
retain-focus
|
@keyup.esc.exact="close"
|
||||||
@keydown.esc.exact.stop.prevent="close"
|
@keyup.enter.exact="confirm"
|
||||||
@keydown.enter.exact.stop.prevent="confirm"
|
|
||||||
@after-enter="afterEnter"
|
@after-enter="afterEnter"
|
||||||
|
@after-leave="afterLeave"
|
||||||
|
@focusout="onFocusOut"
|
||||||
>
|
>
|
||||||
<v-card ref="content" tabindex="1">
|
<v-card ref="content" tabindex="0">
|
||||||
<v-card-title class="d-flex justify-start align-center ga-3">
|
<v-card-title class="d-flex justify-start align-center ga-3">
|
||||||
<v-icon :icon="icon" :size="iconSize" color="primary"></v-icon>
|
<v-icon :icon="icon" :size="iconSize" color="primary"></v-icon>
|
||||||
<div class="text-subtitle-1">{{ text ? text : $gettext(`Are you sure?`) }}</div>
|
<div class="text-subtitle-1">{{ text ? text : $gettext(`Are you sure?`) }}</div>
|
||||||
</v-card-title>
|
</v-card-title>
|
||||||
<v-card-actions class="action-buttons">
|
<v-card-actions class="action-buttons">
|
||||||
<v-btn variant="flat" color="button" class="action-cancel action-close" @click.stop="close">
|
<v-btn variant="flat" tabindex="0" color="button" class="action-cancel action-close" @click.stop="close">
|
||||||
{{ $gettext(`Cancel`) }}
|
{{ $gettext(`Cancel`) }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn color="highlight" variant="flat" class="action-confirm" @click.stop="confirm">
|
<v-btn color="highlight" tabindex="0" variant="flat" class="action-confirm" @click.stop="confirm">
|
||||||
{{ action ? action : $gettext(`Yes`) }}
|
{{ action ? action : $gettext(`Yes`) }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</v-card-actions>
|
</v-card-actions>
|
||||||
@@ -55,18 +59,31 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {};
|
return {};
|
||||||
},
|
},
|
||||||
watch: {
|
|
||||||
visible(show) {
|
|
||||||
if (show) {
|
|
||||||
this.$nextTick(() => this.$view.enter(this, this.$refs?.content, ".action-confirm"));
|
|
||||||
} else {
|
|
||||||
this.$view.leave(this);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
afterEnter() {
|
afterEnter() {
|
||||||
this.$nextTick(() => this.$view.enter(this, this.$refs?.content, ".action-confirm"));
|
this.$view.enter(this);
|
||||||
|
},
|
||||||
|
afterLeave() {
|
||||||
|
this.$view.leave(this);
|
||||||
|
},
|
||||||
|
onFocusOut(ev) {
|
||||||
|
if (!this.$view.isActive(this)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const el = this.$refs.content?.$el;
|
||||||
|
|
||||||
|
if (!ev || !ev.target || !(ev.target instanceof HTMLElement) || !(el instanceof HTMLElement)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const next = ev.relatedTarget;
|
||||||
|
const leavingDialog = !next || !(next instanceof Node) || !el.contains(next);
|
||||||
|
|
||||||
|
if (leavingDialog) {
|
||||||
|
el.focus();
|
||||||
|
ev.preventDefault();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
close() {
|
close() {
|
||||||
this.$emit("close");
|
this.$emit("close");
|
||||||
|
|||||||
@@ -79,10 +79,9 @@
|
|||||||
item-title="Name"
|
item-title="Name"
|
||||||
item-value="Name"
|
item-value="Name"
|
||||||
:disabled="busy"
|
:disabled="busy"
|
||||||
|
:menu-props="menuProps"
|
||||||
return-object
|
return-object
|
||||||
hide-no-data
|
hide-no-data
|
||||||
:menu-props="menuProps"
|
|
||||||
:menu="openMenuId === m.UID"
|
|
||||||
hide-details
|
hide-details
|
||||||
single-line
|
single-line
|
||||||
open-on-clear
|
open-on-clear
|
||||||
@@ -90,15 +89,9 @@
|
|||||||
prepend-inner-icon="mdi-account-plus"
|
prepend-inner-icon="mdi-account-plus"
|
||||||
density="comfortable"
|
density="comfortable"
|
||||||
class="input-name pa-0 ma-0 text-selectable"
|
class="input-name pa-0 ma-0 text-selectable"
|
||||||
@blur="
|
|
||||||
() => {
|
|
||||||
onSetName(m, 'blur');
|
|
||||||
onUpdateMenu(m, false);
|
|
||||||
}
|
|
||||||
"
|
|
||||||
@update:menu="(val) => onUpdateMenu(m, val)"
|
|
||||||
@update:model-value="(person) => onSetPerson(m, person)"
|
@update:model-value="(person) => onSetPerson(m, person)"
|
||||||
@keyup.enter="onSetName(m, 'enter')"
|
@blur="(ev) => onSetName(m, ev)"
|
||||||
|
@keyup.enter="(ev) => onSetName(m, ev)"
|
||||||
>
|
>
|
||||||
</v-combobox>
|
</v-combobox>
|
||||||
</v-card-actions>
|
</v-card-actions>
|
||||||
@@ -147,9 +140,15 @@ export default {
|
|||||||
text: this.$gettext("Add person?"),
|
text: this.$gettext("Add person?"),
|
||||||
},
|
},
|
||||||
menuProps: {
|
menuProps: {
|
||||||
closeOnClick: false,
|
|
||||||
closeOnContentClick: true,
|
|
||||||
openOnClick: true,
|
openOnClick: true,
|
||||||
|
openOnFocus: true,
|
||||||
|
closeOnBack: true,
|
||||||
|
closeOnContentClick: true,
|
||||||
|
persistent: false,
|
||||||
|
scrim: true,
|
||||||
|
openDelay: 0,
|
||||||
|
closeDelay: 0,
|
||||||
|
opacity: 0,
|
||||||
density: "compact",
|
density: "compact",
|
||||||
maxHeight: 300,
|
maxHeight: 300,
|
||||||
scrollStrategy: "reposition",
|
scrollStrategy: "reposition",
|
||||||
@@ -161,7 +160,6 @@ export default {
|
|||||||
|
|
||||||
return v.length <= this.$config.get("clip") || this.$gettext("Name too long");
|
return v.length <= this.$config.get("clip") || this.$gettext("Name too long");
|
||||||
},
|
},
|
||||||
openMenuId: "",
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -331,7 +329,7 @@ export default {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
onSetName(model, trigger) {
|
onSetName(model, ev) {
|
||||||
if (this.busy || !model) {
|
if (this.busy || !model) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -366,7 +364,8 @@ export default {
|
|||||||
|
|
||||||
model.Name = name;
|
model.Name = name;
|
||||||
model.SubjUID = "";
|
model.SubjUID = "";
|
||||||
if (trigger === "enter") {
|
|
||||||
|
if (ev && ev.key === "Enter" && !ev.isComposing && !ev.repeat) {
|
||||||
this.setName(model);
|
this.setName(model);
|
||||||
} else {
|
} else {
|
||||||
this.confirm.visible = true;
|
this.confirm.visible = true;
|
||||||
@@ -385,19 +384,6 @@ export default {
|
|||||||
this.confirm.model.SubjUID = "";
|
this.confirm.model.SubjUID = "";
|
||||||
}
|
}
|
||||||
this.confirm.visible = false;
|
this.confirm.visible = false;
|
||||||
this.openMenuId = "";
|
|
||||||
},
|
|
||||||
getModelKey(model) {
|
|
||||||
return model?.UID || model?.ID || "";
|
|
||||||
},
|
|
||||||
onUpdateMenu(model, open) {
|
|
||||||
const key = this.getModelKey(model);
|
|
||||||
if (!key) return;
|
|
||||||
if (open) {
|
|
||||||
this.openMenuId = key;
|
|
||||||
} else if (this.openMenuId === key) {
|
|
||||||
this.openMenuId = "";
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
setName(model) {
|
setName(model) {
|
||||||
if (this.busy || !model) {
|
if (this.busy || !model) {
|
||||||
|
|||||||
@@ -85,8 +85,8 @@
|
|||||||
single-line
|
single-line
|
||||||
density="comfortable"
|
density="comfortable"
|
||||||
class="input-name pa-0 ma-0"
|
class="input-name pa-0 ma-0"
|
||||||
@blur="onSetName(m, 'blur')"
|
@blur="(ev) => onSetName(m, ev)"
|
||||||
@keyup.enter="onSetName(m, 'enter')"
|
@keyup.enter="(ev) => onSetName(m, ev)"
|
||||||
></v-text-field>
|
></v-text-field>
|
||||||
<v-combobox
|
<v-combobox
|
||||||
v-else
|
v-else
|
||||||
@@ -95,10 +95,9 @@
|
|||||||
item-title="Name"
|
item-title="Name"
|
||||||
item-value="Name"
|
item-value="Name"
|
||||||
:readonly="readonly"
|
:readonly="readonly"
|
||||||
|
:menu-props="menuProps"
|
||||||
return-object
|
return-object
|
||||||
hide-no-data
|
hide-no-data
|
||||||
:menu-props="menuProps"
|
|
||||||
:menu="openMenuId === m.ID"
|
|
||||||
hide-details
|
hide-details
|
||||||
single-line
|
single-line
|
||||||
open-on-clear
|
open-on-clear
|
||||||
@@ -107,15 +106,9 @@
|
|||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
density="comfortable"
|
density="comfortable"
|
||||||
class="input-name pa-0 ma-0 text-selectable"
|
class="input-name pa-0 ma-0 text-selectable"
|
||||||
@blur="
|
|
||||||
() => {
|
|
||||||
onSetName(m, 'blur');
|
|
||||||
onUpdateMenu(m, false);
|
|
||||||
}
|
|
||||||
"
|
|
||||||
@update:menu="(val) => onUpdateMenu(m, val)"
|
|
||||||
@update:model-value="(person) => onSetPerson(m, person)"
|
@update:model-value="(person) => onSetPerson(m, person)"
|
||||||
@keyup.enter="onSetName(m, 'enter')"
|
@blur="(ev) => onSetName(m, ev)"
|
||||||
|
@keyup.enter="(ev) => onSetName(m, ev)"
|
||||||
>
|
>
|
||||||
</v-combobox>
|
</v-combobox>
|
||||||
</v-card-actions>
|
</v-card-actions>
|
||||||
@@ -201,9 +194,15 @@ export default {
|
|||||||
text: this.$gettext("Add person?"),
|
text: this.$gettext("Add person?"),
|
||||||
},
|
},
|
||||||
menuProps: {
|
menuProps: {
|
||||||
closeOnClick: false,
|
|
||||||
closeOnContentClick: true,
|
|
||||||
openOnClick: true,
|
openOnClick: true,
|
||||||
|
openOnFocus: true,
|
||||||
|
closeOnBack: true,
|
||||||
|
closeOnContentClick: true,
|
||||||
|
persistent: false,
|
||||||
|
scrim: true,
|
||||||
|
openDelay: 0,
|
||||||
|
closeDelay: 0,
|
||||||
|
opacity: 0,
|
||||||
density: "compact",
|
density: "compact",
|
||||||
maxHeight: 300,
|
maxHeight: 300,
|
||||||
scrollStrategy: "reposition",
|
scrollStrategy: "reposition",
|
||||||
@@ -215,7 +214,6 @@ export default {
|
|||||||
|
|
||||||
return v.length <= this.$config.get("clip") || this.$gettext("Text too long");
|
return v.length <= this.$config.get("clip") || this.$gettext("Text too long");
|
||||||
},
|
},
|
||||||
openMenuId: "",
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -653,7 +651,7 @@ export default {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
onSetName(model, trigger) {
|
onSetName(model, ev) {
|
||||||
if (this.busy || !model) {
|
if (this.busy || !model) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -690,7 +688,7 @@ export default {
|
|||||||
model.SubjUID = "";
|
model.SubjUID = "";
|
||||||
|
|
||||||
if (model.Name) {
|
if (model.Name) {
|
||||||
if (trigger === "enter") {
|
if (ev && ev.key === "Enter" && !ev.isComposing && !ev.repeat) {
|
||||||
this.setName(model, model.Name);
|
this.setName(model, model.Name);
|
||||||
} else {
|
} else {
|
||||||
this.confirm.visible = true;
|
this.confirm.visible = true;
|
||||||
@@ -715,19 +713,6 @@ export default {
|
|||||||
this.confirm.model.SubjUID = "";
|
this.confirm.model.SubjUID = "";
|
||||||
}
|
}
|
||||||
this.confirm.visible = false;
|
this.confirm.visible = false;
|
||||||
this.openMenuId = "";
|
|
||||||
},
|
|
||||||
getModelKey(model) {
|
|
||||||
return model?.ID || model?.UID || "";
|
|
||||||
},
|
|
||||||
onUpdateMenu(model, open) {
|
|
||||||
const key = this.getModelKey(model);
|
|
||||||
if (!key) return;
|
|
||||||
if (open) {
|
|
||||||
this.openMenuId = key;
|
|
||||||
} else if (this.openMenuId === key) {
|
|
||||||
this.openMenuId = "";
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
setName(model, newName) {
|
setName(model, newName) {
|
||||||
if (this.busy || !model || !newName || newName.trim() === "") {
|
if (this.busy || !model || !newName || newName.trim() === "") {
|
||||||
|
|||||||
Reference in New Issue
Block a user