component2.tsx 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. import { useEditor } from "@/modules/editor";
  2. import { css } from "@linaria/core";
  3. import { nextTick } from "process";
  4. import { defineComponent, onMounted, onUnmounted, reactive, ref } from "vue";
  5. import { string } from "vue-types";
  6. import { useCompData } from ".";
  7. import { View } from "../View";
  8. import HeadlessEditor from "./EditorCustom";
  9. export const Component = defineComponent({
  10. props: {
  11. compId: string().def(""),
  12. },
  13. setup(props) {
  14. const comp = useCompData(props.compId);
  15. const { store, actions, controls } = useEditor();
  16. const state = reactive({
  17. editableId: "",
  18. });
  19. if (store.isEditMode) {
  20. actions.on("textFocus", function (compId, focus) {
  21. if (compId != props.compId) return;
  22. if (focus) {
  23. state.editableId = "" + Date.now();
  24. return;
  25. }
  26. state.editableId = "";
  27. });
  28. }
  29. return () => (
  30. <View
  31. class={[textStyle]}
  32. compId={props.compId}
  33. onDblclick={() => {
  34. if (store.isEditMode) {
  35. state.editableId = "" + Date.now();
  36. }
  37. }}
  38. >
  39. {state.editableId ? (
  40. <EditorComp
  41. compId={props.compId}
  42. key={state.editableId}
  43. onLost={() => {
  44. state.editableId = "";
  45. controls.textEditorCtrl.setCurrEditor(null);
  46. }}
  47. />
  48. ) : (
  49. <div
  50. innerHTML={comp.value}
  51. id={`editor_${props.compId}`}
  52. class={[textStyle, store.isEditMode && `pointer-events-none`]}
  53. />
  54. )}
  55. </View>
  56. );
  57. },
  58. });
  59. const EditorComp = defineComponent({
  60. props: {
  61. compId: string().isRequired,
  62. },
  63. emits: ["lost"],
  64. setup(props, { emit }) {
  65. let editorRefVal = ref<HeadlessEditor>().value;
  66. let editorDomRef=ref<HTMLElement>()
  67. const comp = useCompData(props.compId);
  68. const { store, actions, helper, controls } = useEditor();
  69. let blurCanceler: any = null;
  70. onUnmounted(() => {
  71. blurCanceler?.();
  72. });
  73. const preHeight = ref<number>(0);
  74. const initHeight = () => {
  75. const dom: HTMLElement | null = document.querySelector(
  76. `#editor_${props.compId}`
  77. );
  78. if (!dom) {
  79. return false;
  80. }
  81. const h = helper.pxToDesignSize(dom.clientHeight);
  82. const isChange = Math.abs(preHeight.value - h) > 1;
  83. preHeight.value = h;
  84. actions.updateCompData(comp, "layout.size.1", preHeight.value);
  85. helper.extendStreamCard(store.currStreamCardId);
  86. if (isChange) {
  87. actions.selectObjs([]);
  88. setTimeout(() => {
  89. actions.selectObjs([props.compId]);
  90. }, 0);
  91. }
  92. };
  93. function isInCkBodyWrapper(dom: HTMLElement) {
  94. const out = { in: false, stop: true };
  95. if (editorRefVal) {
  96. const in1 =
  97. editorRefVal.ui.view.toolbar.element?.contains(dom) ||
  98. editorRefVal.ui.view.editable.element?.contains(dom);
  99. if (in1) {
  100. out.in = true;
  101. return out;
  102. }
  103. const ckBodyWrapper = document.querySelector(".ck-body-wrapper");
  104. if (ckBodyWrapper === dom || ckBodyWrapper?.contains(dom)) {
  105. out.in = true;
  106. return out;
  107. }
  108. }
  109. let n = 0;
  110. let curr: any = dom;
  111. do {
  112. if (
  113. curr.id == "text_toolform" ||
  114. curr.id == "toolbar" ||
  115. curr.classList.contains("editor_text_color") ||
  116. curr.classList.contains("ant-modal-input") ||
  117. curr.classList.contains("ant-select-dropdown")
  118. ) {
  119. out.in = true;
  120. out.stop = false;
  121. return out;
  122. }
  123. curr = curr.parentElement;
  124. n += 1;
  125. if (n > 10) break;
  126. } while (curr);
  127. return out;
  128. }
  129. function blurHandle() {
  130. function blur(e: MouseEvent) {
  131. const target = e.target as HTMLElement;
  132. if (!editorRefVal) return;
  133. const test = isInCkBodyWrapper(target);
  134. if (test.in) {
  135. if (test.stop) {
  136. e.stopPropagation();
  137. }
  138. return;
  139. }
  140. actions.submitUpdate();
  141. emit("lost");
  142. }
  143. document.addEventListener("mousedown", blur, {
  144. capture: true,
  145. });
  146. return () => {
  147. document.removeEventListener("mousedown", blur, { capture: true });
  148. };
  149. }
  150. const initEditor = async () => {
  151. // const dom: HTMLElement | null = document.querySelector(
  152. // `#editor_${props.compId}`
  153. // );
  154. if (!editorDomRef.value) {
  155. return;
  156. }
  157. editorRefVal = await HeadlessEditor.create(editorDomRef.value);
  158. editorRefVal.setData(comp.value);
  159. editorRefVal.focus();
  160. const range = document.createRange();
  161. range.selectNodeContents(editorDomRef.value);
  162. const selection = window.getSelection();
  163. selection?.removeAllRanges();
  164. selection?.addRange(range);
  165. editorRefVal.model.document.on("change:data", () => {
  166. const value = editorRefVal?.getData();
  167. if (comp.value !== value) {
  168. actions.updateCompData(comp, "value", value);
  169. nextTick(() => {
  170. const element = editorRefVal?.ui.view.editable.element || null;
  171. if (!element) {
  172. return;
  173. }
  174. const h = helper.pxToDesignSize(element.clientHeight);
  175. const isChange = Math.abs(preHeight.value - h) > 1;
  176. preHeight.value = h;
  177. actions.updateCompData(comp, "layout.size.1", preHeight.value);
  178. helper.extendStreamCard(store.currStreamCardId);
  179. if (isChange) {
  180. actions.selectObjs([]);
  181. setTimeout(() => {
  182. actions.selectObjs([props.compId]);
  183. }, 0);
  184. }
  185. });
  186. }
  187. });
  188. controls.textEditorCtrl.setCurrEditor(editorRefVal);
  189. };
  190. onMounted(() => {
  191. initEditor();
  192. blurCanceler = blurHandle();
  193. nextTick(() => {
  194. initHeight();
  195. });
  196. });
  197. return () => {
  198. return <div class={textStyle} ref={editorDomRef} ></div>;
  199. };
  200. },
  201. });
  202. const textStyle = css`
  203. font-size: 14px;
  204. width: 100%;
  205. color: #666;
  206. word-break: break-all;
  207. p {
  208. margin: 0;
  209. }
  210. .ck.ck-editor__editable_inline {
  211. cursor: text;
  212. overflow: hidden;
  213. border: none !important;
  214. > :last-child,
  215. > :first-child {
  216. margin-top: 0;
  217. margin-bottom: 0;
  218. }
  219. padding: 0 !important;
  220. }
  221. `;