mirror of
https://github.com/directus/directus.git
synced 2026-01-29 14:07:57 -05:00
Enable context menu for read-only fields & adds copy/paste options (#10992)
* allow viewing of raw value for read only fields * add copy value to form-field-menu * change raw value dialog title when disabled * fix notify typing * add paste option & notification for copy/paste
This commit is contained in:
@@ -1,16 +1,20 @@
|
||||
<template>
|
||||
<v-list>
|
||||
<v-list-item
|
||||
v-if="defaultValue === null || !isRequired"
|
||||
:disabled="modelValue === null"
|
||||
clickable
|
||||
@click="$emit('update:modelValue', null)"
|
||||
>
|
||||
<v-list-item-icon><v-icon name="delete_outline" /></v-list-item-icon>
|
||||
<v-list-item-content>{{ t('clear_value') }}</v-list-item-content>
|
||||
<v-list-item clickable @click="$emit('edit-raw')">
|
||||
<v-list-item-icon><v-icon name="code" /></v-list-item-icon>
|
||||
<v-list-item-content>{{ restricted ? t('view_raw_value') : t('edit_raw_value') }}</v-list-item-content>
|
||||
</v-list-item>
|
||||
<v-list-item :disabled="modelValue === null" clickable @click="$emit('copy-raw')">
|
||||
<v-list-item-icon><v-icon name="copy_outline" /></v-list-item-icon>
|
||||
<v-list-item-content>{{ t('copy_raw_value') }}</v-list-item-content>
|
||||
</v-list-item>
|
||||
<v-list-item v-if="!restricted" clickable @click="$emit('paste-raw')">
|
||||
<v-list-item-icon><v-icon name="paste_outline" /></v-list-item-icon>
|
||||
<v-list-item-content>{{ t('paste_raw_value') }}</v-list-item-content>
|
||||
</v-list-item>
|
||||
<v-divider v-if="!restricted" />
|
||||
<v-list-item
|
||||
v-if="defaultValue !== null"
|
||||
v-if="!restricted && defaultValue !== null"
|
||||
:disabled="modelValue === defaultValue"
|
||||
clickable
|
||||
@click="$emit('update:modelValue', defaultValue)"
|
||||
@@ -21,7 +25,7 @@
|
||||
<v-list-item-content>{{ t('reset_to_default') }}</v-list-item-content>
|
||||
</v-list-item>
|
||||
<v-list-item
|
||||
v-if="initialValue"
|
||||
v-if="!restricted && initialValue"
|
||||
:disabled="initialValue === undefined || modelValue === initialValue"
|
||||
clickable
|
||||
@click="$emit('unset', field)"
|
||||
@@ -31,9 +35,14 @@
|
||||
</v-list-item-icon>
|
||||
<v-list-item-content>{{ t('undo_changes') }}</v-list-item-content>
|
||||
</v-list-item>
|
||||
<v-list-item clickable @click="$emit('edit-raw')">
|
||||
<v-list-item-icon><v-icon name="code" /></v-list-item-icon>
|
||||
<v-list-item-content>{{ t('raw_value') }}</v-list-item-content>
|
||||
<v-list-item
|
||||
v-if="!restricted && (defaultValue === null || !isRequired)"
|
||||
:disabled="modelValue === null"
|
||||
clickable
|
||||
@click="$emit('update:modelValue', null)"
|
||||
>
|
||||
<v-list-item-icon><v-icon name="delete_outline" /></v-list-item-icon>
|
||||
<v-list-item-content>{{ t('clear_value') }}</v-list-item-content>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</template>
|
||||
@@ -57,8 +66,12 @@ export default defineComponent({
|
||||
type: [String, Number, Object, Array, Boolean],
|
||||
default: null,
|
||||
},
|
||||
restricted: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ['update:modelValue', 'unset', 'edit-raw'],
|
||||
emits: ['update:modelValue', 'unset', 'edit-raw', 'copy-raw', 'paste-raw'],
|
||||
setup(props) {
|
||||
const { t } = useI18n();
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
<template>
|
||||
<div :key="field.field" class="field" :class="[field.meta?.width || 'full', { invalid: validationError }]">
|
||||
<v-menu v-if="field.hideLabel !== true" placement="bottom-start" show-arrow :disabled="isDisabled">
|
||||
<v-menu v-if="field.hideLabel !== true" placement="bottom-start" show-arrow>
|
||||
<template #activator="{ toggle, active }">
|
||||
<form-field-label
|
||||
:field="field"
|
||||
:toggle="toggle"
|
||||
:active="active"
|
||||
:disabled="isDisabled"
|
||||
:batch-mode="batchMode"
|
||||
:batch-active="batchActive"
|
||||
:edited="isEdited"
|
||||
@@ -21,9 +20,12 @@
|
||||
:field="field"
|
||||
:model-value="internalValue"
|
||||
:initial-value="initialValue"
|
||||
:restricted="isDisabled"
|
||||
@update:model-value="emitValue($event)"
|
||||
@unset="$emit('unset', $event)"
|
||||
@edit-raw="showRaw = true"
|
||||
@copy-raw="copyRaw"
|
||||
@paste-raw="pasteRaw"
|
||||
/>
|
||||
</v-menu>
|
||||
<div v-else-if="['full', 'fill'].includes(field.meta && field.meta.width) === false" class="label-spacer" />
|
||||
@@ -42,9 +44,9 @@
|
||||
|
||||
<v-dialog v-model="showRaw" @esc="showRaw = false">
|
||||
<v-card>
|
||||
<v-card-title>{{ t('edit_raw_value') }}</v-card-title>
|
||||
<v-card-title>{{ isDisabled ? t('view_raw_value') : t('edit_raw_value') }}</v-card-title>
|
||||
<v-card-text>
|
||||
<v-textarea v-model="rawValue" class="raw-value" :placeholder="t('enter_raw_value')" />
|
||||
<v-textarea v-model="rawValue" :disabled="isDisabled" class="raw-value" :placeholder="t('enter_raw_value')" />
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-button @click="showRaw = false">{{ t('done') }}</v-button>
|
||||
@@ -68,6 +70,7 @@ import FormFieldLabel from './form-field-label.vue';
|
||||
import FormFieldMenu from './form-field-menu.vue';
|
||||
import FormFieldInterface from './form-field-interface.vue';
|
||||
import { getJSType } from '@/utils/get-js-type';
|
||||
import { notify } from '@/utils/notify';
|
||||
import { isEqual } from 'lodash';
|
||||
|
||||
export default defineComponent({
|
||||
@@ -146,7 +149,7 @@ export default defineComponent({
|
||||
return props.modelValue !== undefined && isEqual(props.modelValue, props.initialValue) === false;
|
||||
});
|
||||
|
||||
const { showRaw, rawValue } = useRaw();
|
||||
const { showRaw, rawValue, copyRaw, pasteRaw } = useRaw();
|
||||
|
||||
const validationMessage = computed(() => {
|
||||
if (!props.validationError) return null;
|
||||
@@ -158,7 +161,18 @@ export default defineComponent({
|
||||
}
|
||||
});
|
||||
|
||||
return { t, isDisabled, internalValue, emitValue, showRaw, rawValue, validationMessage, isEdited };
|
||||
return {
|
||||
t,
|
||||
isDisabled,
|
||||
internalValue,
|
||||
emitValue,
|
||||
showRaw,
|
||||
rawValue,
|
||||
copyRaw,
|
||||
pasteRaw,
|
||||
validationMessage,
|
||||
isEdited,
|
||||
};
|
||||
|
||||
function emitValue(value: any) {
|
||||
if (
|
||||
@@ -212,7 +226,38 @@ export default defineComponent({
|
||||
},
|
||||
});
|
||||
|
||||
return { showRaw, rawValue };
|
||||
async function copyRaw() {
|
||||
try {
|
||||
await navigator?.clipboard?.writeText(rawValue.value);
|
||||
notify({
|
||||
type: 'success',
|
||||
title: t('copy_raw_value_success'),
|
||||
});
|
||||
} catch (err: any) {
|
||||
notify({
|
||||
type: 'error',
|
||||
title: t('copy_raw_value_fail'),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function pasteRaw() {
|
||||
try {
|
||||
const pasteValue = await navigator?.clipboard?.readText();
|
||||
rawValue.value = pasteValue;
|
||||
notify({
|
||||
type: 'success',
|
||||
title: t('paste_raw_value_success'),
|
||||
});
|
||||
} catch (err: any) {
|
||||
notify({
|
||||
type: 'error',
|
||||
title: t('paste_raw_value_fail'),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return { showRaw, rawValue, copyRaw, pasteRaw };
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -289,12 +289,19 @@ foreign_key: Foreign Key
|
||||
finish_setup: Finish Setup
|
||||
dismiss: Dismiss
|
||||
raw_value: Raw value
|
||||
copy_raw_value: Copy Raw Value
|
||||
copy_raw_value_success: Copied
|
||||
copy_raw_value_fail: Copy Failed
|
||||
paste_raw_value: Paste Raw Value
|
||||
paste_raw_value_success: Pasted
|
||||
paste_raw_value_fail: Paste Failed
|
||||
view_raw_value: View Raw Value
|
||||
edit_raw_value: Edit Raw Value
|
||||
enter_raw_value: Enter raw value...
|
||||
clear_value: Clear value
|
||||
clear_value: Clear Value
|
||||
clear_changes: Clear Changes
|
||||
reset_to_default: Reset to default
|
||||
undo_changes: Undo changes
|
||||
reset_to_default: Reset to Default
|
||||
undo_changes: Undo Changes
|
||||
notifications: Notifications
|
||||
show_all_activity: Show All Activity
|
||||
page_not_found: Page Not Found
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useNotificationsStore } from '@/stores/';
|
||||
import { NotificationRaw } from '@/types';
|
||||
import { SnackbarRaw } from '@/types';
|
||||
|
||||
let store: any;
|
||||
|
||||
export function notify(notification: NotificationRaw): void {
|
||||
export function notify(notification: SnackbarRaw): void {
|
||||
if (!store) store = useNotificationsStore();
|
||||
store.add(notification);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user