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:
Azri Kahar
2022-01-12 23:11:24 +08:00
committed by GitHub
parent d71e5664d8
commit a4f31697f4
4 changed files with 91 additions and 26 deletions

View File

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

View File

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

View File

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

View File

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