lexical-vue
Version:
An extensible Vue 3 web text-editor based on Lexical.
215 lines (214 loc) • 10.4 kB
JavaScript
import { computed, createCommentVNode, createElementBlock, createElementVNode, defineComponent, normalizeClass, openBlock, ref, toDisplayString, useTemplateRef, watchEffect } from "vue";
const LARGE_EDITOR_STATE_SIZE = 1000;
const TreeViewCore = (()=>{
const _hoisted_1 = {
key: 0,
style: {
padding: "20px"
}
};
const _hoisted_2 = [
"max"
];
const __vine = defineComponent({
name: 'TreeViewCore',
props: {
editorState: {
required: true
},
treeTypeButtonClassName: {},
timeTravelButtonClassName: {},
timeTravelPanelButtonClassName: {},
timeTravelPanelClassName: {},
timeTravelPanelSliderClassName: {},
viewClassName: {},
generateContent: {
required: true
},
setEditorState: {
required: true
},
setEditorReadOnly: {
required: true
},
commandsLog: {}
},
setup (__props, param) {
let { expose: __expose } = param;
const props = __props;
const preRef = useTemplateRef('preRef');
const timeStampedEditorStates = ref([]);
const content = ref('');
const timeTravelEnabled = ref(false);
const showExportDOM = ref(false);
let playingIndexRef = 0;
const inputRef = useTemplateRef('inputRef');
const isPlaying = ref(false);
const isLimited = ref(false);
const showLimited = ref(false);
let lastEditorStateRef = null;
let lastCommandsLogRef = [];
let lastGenerationID = 0;
const totalEditorStates = computed(()=>timeStampedEditorStates.value.length);
const generateTree = async (exportDOM)=>{
const myID = ++lastGenerationID;
try {
const treeText = await props.generateContent(exportDOM);
if (myID === lastGenerationID) content.value = treeText;
} catch (err) {
if (myID === lastGenerationID) content.value = "Error rendering tree: ".concat(err instanceof Error ? err.message : String(err), "\n\nStack:\n").concat(err instanceof Error ? err.stack : 'No stack trace');
}
};
watchEffect(()=>{
if (!showLimited.value && props.editorState._nodeMap.size > LARGE_EDITOR_STATE_SIZE) {
isLimited.value = true;
if (!showLimited) return;
}
const shouldUpdate = lastEditorStateRef !== props.editorState || lastCommandsLogRef !== props.commandsLog;
if (shouldUpdate) {
const isEditorStateChange = lastEditorStateRef !== props.editorState;
lastEditorStateRef = props.editorState;
lastCommandsLogRef = props.commandsLog || [];
generateTree(showExportDOM.value);
if (!timeTravelEnabled.value && isEditorStateChange) timeStampedEditorStates.value = [
...timeStampedEditorStates.value,
[
Date.now(),
props.editorState
]
];
}
});
watchEffect((onInvalidate)=>{
if (isPlaying.value) {
let timeoutId;
const play = ()=>{
const currentIndex = playingIndexRef;
if (currentIndex === totalEditorStates.value - 1) {
isPlaying.value = false;
return;
}
const currentTime = timeStampedEditorStates.value[currentIndex][0];
const nextTime = timeStampedEditorStates.value[currentIndex + 1][0];
const timeDiff = nextTime - currentTime;
timeoutId = setTimeout(()=>{
playingIndexRef++;
const index = playingIndexRef;
const input = inputRef.value;
if (null !== input) input.value = String(index);
props.setEditorState(timeStampedEditorStates.value[index][1]);
play();
}, timeDiff);
};
play();
onInvalidate(()=>{
clearTimeout(timeoutId);
});
}
});
const handleExportModeToggleClick = ()=>{
generateTree(!showExportDOM.value);
showExportDOM.value = !showExportDOM.value;
};
const handleTimeTravelClick = ()=>{
props.setEditorReadOnly(true);
playingIndexRef = totalEditorStates.value - 1;
timeTravelEnabled.value = true;
};
const handlePlayPauseClick = ()=>{
if (playingIndexRef === totalEditorStates.value - 1) playingIndexRef = 1;
isPlaying.value = !isPlaying.value;
};
const handleSliderChange = (event)=>{
const target = event.target;
const editorStateIndex = Number(target.value);
const timeStampedEditorState = timeStampedEditorStates.value[editorStateIndex];
if (timeStampedEditorState) {
playingIndexRef = editorStateIndex;
props.setEditorState(timeStampedEditorState[1]);
}
};
const handleExitTimeTravel = ()=>{
props.setEditorReadOnly(false);
const index = timeStampedEditorStates.value.length - 1;
const timeStampedEditorState = timeStampedEditorStates.value[index];
props.setEditorState(timeStampedEditorState[1]);
const input = inputRef.value;
if (null !== input) input.value = String(index);
timeTravelEnabled.value = false;
isPlaying.value = false;
};
const handleShowFullTree = ()=>{
showLimited.value = true;
};
__expose({
preRef
});
return (_ctx, _cache)=>(openBlock(), createElementBlock("div", {
class: normalizeClass(_ctx.viewClassName)
}, [
!showLimited.value && isLimited.value ? (openBlock(), createElementBlock("div", _hoisted_1, [
_cache[0] || (_cache[0] = createElementVNode("span", {
style: {
"margin-right": "20px"
}
}, " Detected large EditorState, this can impact debugging performance. ", -1)),
createElementVNode("button", {
onClick: handleShowFullTree,
style: {
background: "transparent",
border: "1px solid white",
color: "white",
cursor: "pointer",
padding: "5px"
}
}, " Show full tree ")
])) : createCommentVNode("", true),
showLimited.value ? createCommentVNode("", true) : (openBlock(), createElementBlock("button", {
key: 1,
onClick: handleExportModeToggleClick,
class: normalizeClass(_ctx.treeTypeButtonClassName),
type: "button"
}, toDisplayString(showExportDOM.value ? 'Tree' : 'Export DOM'), 3)),
!timeTravelEnabled.value && (showLimited.value || !isLimited.value) && totalEditorStates.value > 2 ? (openBlock(), createElementBlock("button", {
key: 2,
onClick: handleTimeTravelClick,
class: normalizeClass(_ctx.timeTravelButtonClassName),
type: "button"
}, " Time Travel ", 2)) : createCommentVNode("", true),
showLimited.value || !isLimited.value ? (openBlock(), createElementBlock("pre", {
key: 3,
ref_key: "preRef",
ref: preRef
}, toDisplayString(content.value), 513)) : createCommentVNode("", true),
timeTravelEnabled.value && (showLimited.value || !isLimited.value) ? (openBlock(), createElementBlock("div", {
key: 4,
class: normalizeClass(_ctx.timeTravelPanelClassName)
}, [
createElementVNode("button", {
class: normalizeClass(_ctx.timeTravelPanelButtonClassName),
onClick: handlePlayPauseClick,
type: "button"
}, toDisplayString(isPlaying.value ? 'Pause' : 'Play'), 3),
createElementVNode("input", {
class: normalizeClass(_ctx.timeTravelPanelSliderClassName),
ref_key: "inputRef",
ref: inputRef,
onChange: handleSliderChange,
type: "range",
min: "1",
max: totalEditorStates.value - 1
}, null, 42, _hoisted_2),
createElementVNode("button", {
class: normalizeClass(_ctx.timeTravelPanelButtonClassName),
onClick: handleExitTimeTravel,
type: "button"
}, " Exit ", 2)
], 2)) : createCommentVNode("", true)
], 2));
}
});
__vine.__vue_vine = true;
return __vine;
})();
export { TreeViewCore };