77 lines
1.6 KiB
TypeScript
77 lines
1.6 KiB
TypeScript
|
import {
|
||
|
ref,
|
||
|
Ref,
|
||
|
h,
|
||
|
defineComponent,
|
||
|
onMounted,
|
||
|
onBeforeUnmount,
|
||
|
PropType,
|
||
|
watchEffect,
|
||
|
} from 'vue';
|
||
|
import { useInline } from '../inline';
|
||
|
|
||
|
export const SbContenteditable = defineComponent({
|
||
|
name: 'sb-contenteditable',
|
||
|
|
||
|
props: {
|
||
|
inputRef: {
|
||
|
type: (null as unknown) as PropType<Ref<HTMLElement|null>>,
|
||
|
default: ref(null),
|
||
|
},
|
||
|
tag: { type: String, default: 'div' },
|
||
|
|
||
|
value: { type: String, default: '' },
|
||
|
|
||
|
onValueChange: {
|
||
|
type: (null as unknown) as PropType<(value: string) => void>,
|
||
|
default: (_:string) => {},
|
||
|
},
|
||
|
},
|
||
|
|
||
|
setup(props) {
|
||
|
const {
|
||
|
registerClient,
|
||
|
unregisterClient,
|
||
|
} = useInline();
|
||
|
const onKeyup = (event: KeyboardEvent) => {
|
||
|
if (event.code !== 'Backspace' && event.code !== 'Delete') {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!props.inputRef.value) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const textContent = props.inputRef.value.textContent;
|
||
|
if (textContent === '') {
|
||
|
props.inputRef.value.innerHTML = '';
|
||
|
}
|
||
|
};
|
||
|
|
||
|
onMounted(() => {
|
||
|
props.inputRef.value?.focus();
|
||
|
registerClient(props.inputRef.value!);
|
||
|
});
|
||
|
|
||
|
watchEffect(() => {
|
||
|
if (props.inputRef.value && props.inputRef.value.innerHTML != props.value) {
|
||
|
props.inputRef.value.innerHTML = props.value;
|
||
|
}
|
||
|
});
|
||
|
|
||
|
onBeforeUnmount(() => {
|
||
|
unregisterClient(props.inputRef.value!);
|
||
|
});
|
||
|
|
||
|
return () => h(props.tag, {
|
||
|
class: 'sb-contenteditable',
|
||
|
contenteditable: 'true',
|
||
|
ref: props.inputRef,
|
||
|
onKeyup,
|
||
|
onInput: () => {
|
||
|
props.onValueChange(props.inputRef.value?.innerHTML || '');
|
||
|
},
|
||
|
});
|
||
|
},
|
||
|
});
|