|
|
|
<script setup lang="ts">
|
|
|
|
import {
|
|
|
|
Modal as AModal,
|
|
|
|
ActiveCellInj,
|
|
|
|
EditModeInj,
|
|
|
|
IsFormInj,
|
|
|
|
ReadonlyInj,
|
|
|
|
computed,
|
|
|
|
inject,
|
|
|
|
ref,
|
|
|
|
useSelectedCellKeyupListener,
|
|
|
|
useVModel,
|
|
|
|
watch,
|
|
|
|
} from '#imports'
|
|
|
|
|
|
|
|
interface Props {
|
|
|
|
modelValue: string | Record<string, any> | undefined
|
|
|
|
}
|
|
|
|
|
|
|
|
interface Emits {
|
|
|
|
(event: 'update:modelValue', model: string): void
|
|
|
|
}
|
|
|
|
|
|
|
|
const props = defineProps<Props>()
|
|
|
|
|
|
|
|
const emits = defineEmits<Emits>()
|
|
|
|
|
|
|
|
const editEnabled = inject(EditModeInj, ref(false))
|
|
|
|
|
|
|
|
const active = inject(ActiveCellInj, ref(false))
|
|
|
|
|
|
|
|
const isForm = inject(IsFormInj, ref(false))
|
|
|
|
|
|
|
|
const readonly = inject(ReadonlyInj)
|
|
|
|
|
|
|
|
const vModel = useVModel(props, 'modelValue', emits)
|
|
|
|
|
|
|
|
const localValueState = ref<string | undefined>()
|
|
|
|
|
|
|
|
let error = $ref<string | undefined>()
|
|
|
|
|
|
|
|
let isExpanded = $ref(false)
|
|
|
|
|
|
|
|
const localValue = computed<string | Record<string, any> | undefined>({
|
|
|
|
get: () => localValueState.value,
|
|
|
|
set: (val: undefined | string | Record<string, any>) => {
|
|
|
|
localValueState.value = typeof val === 'object' ? JSON.stringify(val, null, 2) : val
|
|
|
|
/** if form and not expanded then sync directly */
|
|
|
|
if (isForm.value && !isExpanded) {
|
|
|
|
vModel.value = val
|
|
|
|
}
|
|
|
|
},
|
|
|
|
})
|
|
|
|
|
|
|
|
const clear = () => {
|
|
|
|
error = undefined
|
|
|
|
|
|
|
|
isExpanded = false
|
|
|
|
|
|
|
|
editEnabled.value = false
|
|
|
|
|
|
|
|
localValue.value = vModel.value
|
|
|
|
}
|
|
|
|
|
|
|
|
const formatJson = (json: string) => {
|
|
|
|
try {
|
|
|
|
return JSON.stringify(JSON.parse(json), null, 2)
|
|
|
|
} catch (e) {
|
|
|
|
return json
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const onSave = () => {
|
|
|
|
isExpanded = false
|
|
|
|
|
|
|
|
editEnabled.value = false
|
|
|
|
|
|
|
|
localValue.value = localValue ? formatJson(localValue.value as string) : localValue
|
|
|
|
|
|
|
|
vModel.value = localValue.value
|
|
|
|
}
|
|
|
|
|
|
|
|
watch(
|
|
|
|
vModel,
|
|
|
|
(val) => {
|
|
|
|
localValue.value = val
|
|
|
|
},
|
|
|
|
{ immediate: true },
|
|
|
|
)
|
|
|
|
|
|
|
|
watch(localValue, (val) => {
|
|
|
|
try {
|
|
|
|
JSON.parse(val as string)
|
|
|
|
|
|
|
|
error = undefined
|
|
|
|
} catch (e: any) {
|
|
|
|
error = e
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
watch(editEnabled, () => {
|
|
|
|
isExpanded = false
|
|
|
|
|
|
|
|
localValue.value = vModel.value
|
|
|
|
})
|
|
|
|
|
|
|
|
useSelectedCellKeyupListener(active, (e) => {
|
|
|
|
switch (e.key) {
|
|
|
|
case 'Enter':
|
|
|
|
e.stopPropagation()
|
|
|
|
if (e.shiftKey) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if (editEnabled.value) {
|
|
|
|
onSave()
|
|
|
|
} else {
|
|
|
|
editEnabled.value = true
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
})
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<template>
|
|
|
|
<component :is="isExpanded ? AModal : 'div'" v-model:visible="isExpanded" :closable="false" centered :footer="null">
|
|
|
|
<div v-if="editEnabled && !readonly" class="flex flex-col w-full">
|
|
|
|
<div class="flex flex-row justify-between pt-1 pb-2">
|
|
|
|
<a-button type="text" size="small" @click="isExpanded = !isExpanded">
|
|
|
|
<CilFullscreenExit v-if="isExpanded" class="h-2.5" />
|
|
|
|
|
|
|
|
<CilFullscreen v-else class="h-2.5" />
|
|
|
|
</a-button>
|
|
|
|
|
|
|
|
<div v-if="!isForm || isExpanded" class="flex flex-row">
|
|
|
|
<a-button type="text" size="small" :onclick="clear"><div class="text-xs">Cancel</div></a-button>
|
|
|
|
|
|
|
|
<a-button type="primary" size="small" :disabled="!!error || localValue === vModel">
|
|
|
|
<div class="text-xs" @click="onSave">Save</div>
|
|
|
|
</a-button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<LazyMonacoEditor
|
|
|
|
:model-value="localValue || ''"
|
|
|
|
class="min-w-full w-80"
|
|
|
|
:class="{ 'expanded-editor': isExpanded, 'editor': !isExpanded }"
|
|
|
|
:hide-minimap="true"
|
|
|
|
:disable-deep-compare="true"
|
|
|
|
@update:model-value="localValue = $event"
|
|
|
|
/>
|
|
|
|
|
|
|
|
<span v-if="error" class="text-xs w-full py-1 text-red-500">
|
|
|
|
{{ error.toString() }}
|
|
|
|
</span>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<span v-else>{{ vModel }}</span>
|
|
|
|
</component>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
|
.expanded-editor {
|
|
|
|
min-height: min(600px, 80vh);
|
|
|
|
}
|
|
|
|
|
|
|
|
.editor {
|
|
|
|
min-height: min(200px, 10vh);
|
|
|
|
}
|
|
|
|
</style>
|