component2.tsx 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  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 } = 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. }}
  46. />
  47. ) : (
  48. <div
  49. innerHTML={comp.value}
  50. class={[textStyle, store.isEditMode && `pointer-events-none`]}
  51. />
  52. )}
  53. </View>
  54. );
  55. },
  56. });
  57. const EditorComp = defineComponent({
  58. props: {
  59. compId: string().isRequired,
  60. },
  61. emits: ["lost"],
  62. setup(props, { emit }) {
  63. let editorRefVal = ref<HeadlessEditor>().value;
  64. const comp = useCompData(props.compId);
  65. const { store, actions, helper, controls } = useEditor();
  66. let blurCanceler: any = null;
  67. onUnmounted(() => {
  68. blurCanceler?.();
  69. });
  70. const preHeight = ref<number>(0);
  71. const initHeight = () => {
  72. const dom: HTMLElement | null = document.querySelector(
  73. `#editor_${props.compId}`
  74. );
  75. if (!dom) {
  76. return false;
  77. }
  78. const h = helper.pxToDesignSize(dom.clientHeight);
  79. const isChange = Math.abs(preHeight.value - h) > 1;
  80. preHeight.value = h;
  81. actions.updateCompData(comp, "layout.size.1", preHeight.value);
  82. helper.extendStreamCard(store.currStreamCardId);
  83. if (isChange) {
  84. actions.selectObjs([]);
  85. setTimeout(() => {
  86. actions.selectObjs([props.compId]);
  87. }, 0);
  88. }
  89. };
  90. function isInCkBodyWrapper(dom: HTMLElement) {
  91. const out = { in: false, stop: true };
  92. if (editorRefVal) {
  93. const in1 =
  94. editorRefVal.ui.view.toolbar.element?.contains(dom) ||
  95. editorRefVal.ui.view.editable.element?.contains(dom);
  96. if (in1) {
  97. out.in = true;
  98. return out;
  99. }
  100. const ckBodyWrapper = document.querySelector(".ck-body-wrapper");
  101. if (ckBodyWrapper === dom || ckBodyWrapper?.contains(dom)) {
  102. out.in = true;
  103. return out;
  104. }
  105. }
  106. let n = 0;
  107. let curr: any = dom;
  108. do {
  109. if (
  110. curr.id == "toptoolbar" ||
  111. curr.classList.contains("pcr-app") ||
  112. curr.classList.contains("editor_toolbar_drop") ||
  113. curr.classList.contains("ant-select-dropdown")
  114. ) {
  115. out.in = true;
  116. out.stop = false;
  117. return out;
  118. }
  119. curr = curr.parentElement;
  120. n += 1;
  121. if (n > 10) break;
  122. } while (curr);
  123. return out;
  124. }
  125. function blurHandle() {
  126. function blur(e: MouseEvent) {
  127. const target = e.target as HTMLElement;
  128. if (!editorRefVal) return;
  129. const test = isInCkBodyWrapper(target);
  130. if (test.in) {
  131. if (test.stop) {
  132. e.stopPropagation();
  133. }
  134. return;
  135. }
  136. actions.submitUpdate();
  137. emit("lost");
  138. }
  139. document.addEventListener("mousedown", blur, {
  140. capture: true,
  141. });
  142. return () => {
  143. document.removeEventListener("mousedown", blur, { capture: true });
  144. };
  145. }
  146. const initEditor = async () => {
  147. const dom: HTMLElement | null = document.querySelector(
  148. `#editor_${props.compId}`
  149. );
  150. if (!dom) {
  151. return;
  152. }
  153. editorRefVal = await HeadlessEditor.create(dom);
  154. editorRefVal.setData(comp.value);
  155. editorRefVal.focus();
  156. const range = document.createRange();
  157. range.selectNodeContents(dom);
  158. const selection = window.getSelection();
  159. selection?.removeAllRanges();
  160. selection?.addRange(range);
  161. editorRefVal.model.document.on("change:data", () => {
  162. const value = editorRefVal?.getData();
  163. if (comp.value !== value) {
  164. actions.updateCompData(comp, "value", value);
  165. nextTick(() => {
  166. const element = editorRefVal?.ui.view.editable.element || null;
  167. if (!element) {
  168. return;
  169. }
  170. const h = helper.pxToDesignSize(element.clientHeight);
  171. const isChange = Math.abs(preHeight.value - h) > 1;
  172. preHeight.value = h;
  173. actions.updateCompDatas(
  174. comp,
  175. ["value", "layout.size.1"],
  176. [value, preHeight.value]
  177. );
  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.textEditor = editorRefVal;
  189. };
  190. onMounted(() => {
  191. initEditor();
  192. blurCanceler = blurHandle();
  193. nextTick(() => {
  194. initHeight();
  195. });
  196. });
  197. return () => {
  198. return <div class={textStyle} id={`editor_${props.compId}`}></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. `;