LCOV - code coverage report
Current view: top level - src/components/SSVGPropEdit - RelationCodeEditor.vue.js (source / functions) Hit Total Coverage
Test: lcov.info Lines: 17 37 45.9 %
Date: 2025-06-29 02:18:36 Functions: 7 11 63.6 %

          Line data    Source code
       1             : // @ts-check
       2             : 
       3             : import Vue from "vue";
       4             : import { mapState } from "vuex";
       5             : import { get, map, noop } from "lodash";
       6             : 
       7             : import { xml } from "@codemirror/lang-xml";
       8             : import { Compartment, StateEffect } from "@codemirror/state";
       9             : import { foldAll } from "@codemirror/language";
      10             : import { closeBrackets, insertCompletionText } from "@codemirror/autocomplete";
      11             : import { linter, openLintPanel } from "@codemirror/lint";
      12             : 
      13             : import CMColorPlugin from "../../utils/CMColorPlugin";
      14             : import { makeSelectorPlugin, selectorTheme } from "../../utils/CMSelectorPlugin";
      15             : import CodeMirrorMixin from "../CodeMirrorMixin";
      16             : import { bracketConf } from "../CodeMirrorMixinInit";
      17             : import { ssvgElements, ssvgLint, ssvgPrettyPrint } from "../../utils/SSVGLint";
      18             : import SVGSelector from "../SVGSelector.vue";
      19             : 
      20             : /**
      21             :  * @typedef {import("@codemirror/autocomplete").Completion} Completion
      22             :  * @typedef {import("@codemirror/view").EditorView} EditorView
      23             :  *
      24             :  * @typedef {{ syntax: Compartment, schema: typeof schema }} Opts
      25             :  * @typedef {{ cm: HTMLElement, selector: V.Instance<typeof SVGSelector> }} Refs
      26             :  */
      27             : 
      28           1 : const schema = [
      29             :   { ...ssvgElements.relation, top: true },
      30             :   { ...ssvgElements.transform, top: true },
      31             :   { ...ssvgElements.direct, top: true },
      32             :   { ...ssvgElements.transition }
      33             : ];
      34           1 : const languageCompartment = new Compartment();
      35             : 
      36           1 : const component = /** @type {V.Constructor<Opts, Refs>}*/(Vue).extend({
      37             :   name: "SSVGCodeEditor",
      38             :   components: { SVGSelector },
      39             :   mixins: [ CodeMirrorMixin({
      40             :     extensions: [
      41           0 :       linter((view) => ssvgLint(view.state?.doc.toString())),
      42             :       languageCompartment.of([]),
      43             :       CMColorPlugin
      44             :     ]
      45             :   }) ],
      46             :   /** @return {{ isSelecting: boolean }} */
      47             :   data() {
      48           4 :     return { isSelecting: false };
      49             :   },
      50             :   computed: {
      51             :     .../** @type {{ relation(): Element|null }} */(mapState("selection", [ "relation" ])),
      52             :     .../** @type {{ svgSelectors(): any }} */(mapState("engine", {
      53           4 :       svgSelectors: (state) => get(state, [ "svgSelectors" ])
      54             :     })),
      55             :     /** @return {string} */
      56             :     currentContent() {
      57           5 :       return (this.relation) ? ssvgPrettyPrint(this.relation.outerHTML) : "";
      58             :     }
      59             :   },
      60             :   watch: {
      61           2 :     currentContent(val) { this.setContent(val); }
      62             :   },
      63             :   mounted() {
      64           4 :     const cm = this.getEditor();
      65           4 :     cm?.dispatch({ effects: bracketConf.reconfigure(closeBrackets()) });
      66           4 :     cm?.dispatch({
      67             :       effects: StateEffect.appendConfig.of([
      68             :         makeSelectorPlugin(this.getSelector), selectorTheme
      69             :       ])
      70             :     });
      71             : 
      72           4 :     this.$options.schema = schema;
      73           4 :     this.updateLanguage();
      74           4 :     this.setContent(this.currentContent);
      75           4 :     foldAll(cm);
      76             :   },
      77             :   methods: {
      78             :     updateLanguage() {
      79           5 :       const attributes = [
      80             :         {
      81             :           name: "query-selector", values: [
      82             :             { label: "select", detail: "on document", apply: this.updateSelector, type: "selector", boost: 1 },
      83          18 :             ...map(this.svgSelectors, (s) => ({ label: s, type: "text" }))
      84             :           ]
      85             :         }
      86             :       ];
      87           5 :       this.getEditor().dispatch({ effects: languageCompartment.reconfigure(xml({ elements: this.$options.schema, attributes })) });
      88             :     },
      89             :     /** @returns {Promise<string>} */
      90             :     async getSelector() {
      91           0 :       this.isSelecting = true;
      92           0 :       let selector = "";
      93           0 :       try {
      94           0 :         selector = await this.$refs.selector?.select();
      95           0 :         if (!selector) {
      96           0 :           throw new Error("No selector found");
      97             :         }
      98             :       }
      99           0 :       catch (e) { console.log("getSelector error", e); }
     100           0 :       this.isSelecting = false;
     101           0 :       return selector;
     102             :     },
     103             :     /**
     104             :      * @param {EditorView} view
     105             :      * @param {Completion} completion
     106             :      * @param {number} from
     107             :      * @param {number} to
     108             :      */
     109             :     async updateSelector(view, completion, from, to) {
     110           0 :       let selector = await this.getSelector();
     111           0 :       if (selector) {
     112           0 :         selector = `"${selector}"`;
     113           0 :         view.dispatch(insertCompletionText(view.state, selector, from, to));
     114             :       }
     115             :     },
     116             :     async doApply() {
     117           0 :       if (!this.relation) { /* noop */ }
     118           0 :       else if (this.$refs.cm?.getElementsByClassName("cm-lint-marker-error").length > 0) {
     119           0 :         openLintPanel(this.getEditor());
     120           0 :         return false;
     121             :       }
     122             :       else {
     123           0 :         await this.$store.dispatch("replace", this.editValue).catch(noop);
     124           0 :         return true;
     125             :       }
     126             :     }
     127             :   }
     128             : });
     129             : export default component;

Generated by: LCOV version 1.16