Browse Source

editor letter spacing

bianjiang 1 year ago
parent
commit
4bc6a60c69

+ 3 - 1
src/modules/editor/components/CompUI/basicUI/Text/EditorCustom.ts

@@ -14,8 +14,9 @@ import { Link } from "@ckeditor/ckeditor5-link";
 import { Paragraph } from "@ckeditor/ckeditor5-paragraph";
 import { Paragraph } from "@ckeditor/ckeditor5-paragraph";
 import Heading from "@ckeditor/ckeditor5-heading/src/heading";
 import Heading from "@ckeditor/ckeditor5-heading/src/heading";
 import LineHeight from "ckeditor5-line-height-latest/src/lineheight";
 import LineHeight from "ckeditor5-line-height-latest/src/lineheight";
-import LetterSpacing from "./ckeditor-letter-spacing";
+import LetterSpacing from "./ckeditor-letter-spacing/index";
 import { EditorConfig } from "@ckeditor/ckeditor5-core";
 import { EditorConfig } from "@ckeditor/ckeditor5-core";
+
 export class HeadlessEditor extends DecoupledEditorBase {}
 export class HeadlessEditor extends DecoupledEditorBase {}
 interface HeadlessConfig extends EditorConfig {
 interface HeadlessConfig extends EditorConfig {
   lineHeight: any;
   lineHeight: any;
@@ -64,6 +65,7 @@ HeadlessEditor.defaultConfig = {
   },
   },
   letterSpacing: {
   letterSpacing: {
     options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
     options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
+    supportAllValues: true,
   },
   },
   link: {},
   link: {},
 } as HeadlessConfig;
 } as HeadlessConfig;

+ 42 - 56
src/modules/editor/components/CompUI/basicUI/Text/ckeditor-letter-spacing/LetterSpacingCommand.ts

@@ -1,69 +1,55 @@
 import Command from "@ckeditor/ckeditor5-core/src/command";
 import Command from "@ckeditor/ckeditor5-core/src/command";
-import first from "@ckeditor/ckeditor5-utils/src/first";
-
-const Letter_Spacing = "letterSpacing";
+import { Letter_Spacing } from "./utils";
 
 
 export default class LetterSpacingCommand extends Command {
 export default class LetterSpacingCommand extends Command {
+  constructor(editor: any) {
+    super(editor);
+  }
   refresh() {
   refresh() {
-    const firstBlock = first(
-      this.editor.model.document.selection.getSelectedBlocks()
+    const model = this.editor.model;
+    const doc = model.document;
+    this.value = doc.selection.getAttribute(Letter_Spacing);
+    this.isEnabled = model.schema.checkAttributeInSelection(
+      doc.selection,
+      Letter_Spacing
     );
     );
-
-    this.isEnabled = !!firstBlock && this._canSetLetterSpacing(firstBlock);
-
-    this.value =
-      this.isEnabled && firstBlock?.hasAttribute(Letter_Spacing)
-        ? firstBlock.getAttribute(Letter_Spacing)
-        : "0";
   }
   }
 
 
   execute(options = {} as any) {
   execute(options = {} as any) {
-    const editor = this.editor;
-    const model = editor.model;
-    const doc = model.document;
-
-    console.log(model.schema.getDefinitions());
-
-    // const value = '0'
+    const model = this.editor.model;
+    const document = model.document;
+    const selection = document.selection;
     const value = options.value;
     const value = options.value;
-
-    model.change((writer) => {
-      const blocks = Array.from(doc.selection.getSelectedBlocks()).filter(
-        (block) => this._canSetLetterSpacing(block)
-      );
-      const currentLetterSpacing = blocks[0].getAttribute(Letter_Spacing);
-
-      const removeLetterSpacing =
-        /* isDefault( value ) ||  */ currentLetterSpacing === value ||
-        typeof value === "undefined";
-
-      if (removeLetterSpacing) {
-        removeLetterSpacingFromSelection(blocks, writer);
+    const batch = options.batch;
+    const updateAttribute = (writer: any) => {
+      if (selection.isCollapsed) {
+        if (value) {
+          writer.setSelectionAttribute(Letter_Spacing, value);
+        } else {
+          writer.removeSelectionAttribute(Letter_Spacing);
+        }
       } else {
       } else {
-        setLetterSpacingOnSelection(blocks, writer, value);
+        const ranges = model.schema.getValidRanges(
+          selection.getRanges(),
+          Letter_Spacing
+        );
+        for (const range of ranges) {
+          if (value) {
+            writer.setAttribute(Letter_Spacing, value, range);
+          } else {
+            writer.removeAttribute(Letter_Spacing, range);
+          }
+        }
       }
       }
-    });
-  }
-
-  _canSetLetterSpacing(block: any) {
-    return this.editor.model.schema.checkAttribute(block, Letter_Spacing);
-  }
-}
-
-function removeLetterSpacingFromSelection(blocks: any, writer: any) {
-  for (const block of blocks) {
-    console.log("removing");
-    writer.removeAttribute(Letter_Spacing, block);
-  }
-}
-
-function setLetterSpacingOnSelection(
-  blocks: any,
-  writer: any,
-  LetterSpacing: any
-) {
-  for (const block of blocks) {
-    console.log("setting", block, LetterSpacing);
-    writer.setAttribute(Letter_Spacing, LetterSpacing, block);
+    };
+    if (batch) {
+      model.enqueueChange(batch, (writer) => {
+        updateAttribute(writer);
+      });
+    } else {
+      model.change((writer) => {
+        updateAttribute(writer);
+      });
+    }
   }
   }
 }
 }

+ 53 - 14
src/modules/editor/components/CompUI/basicUI/Text/ckeditor-letter-spacing/LetterSpacingEditing.ts

@@ -1,5 +1,10 @@
 import Plugin from "@ckeditor/ckeditor5-core/src/plugin";
 import Plugin from "@ckeditor/ckeditor5-core/src/plugin";
-import { isSupported, buildDefinition } from "./utils";
+import {
+  isSupported,
+  buildDefinition,
+  Letter_Spacing,
+  normalizeOptions,
+} from "./utils";
 import LetterSpacingCommand from "./LetterSpacingCommand";
 import LetterSpacingCommand from "./LetterSpacingCommand";
 import HeadlessEditor from "../EditorCustom";
 import HeadlessEditor from "../EditorCustom";
 
 
@@ -8,7 +13,8 @@ export default class LetterSpacingEditing extends Plugin {
     super(editor);
     super(editor);
 
 
     editor.config.define("letterSpacing", {
     editor.config.define("letterSpacing", {
-      options: [0, 1, 2, 3, 4],
+      options: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
+      supportAllValues: false,
     });
     });
   }
   }
 
 
@@ -17,23 +23,56 @@ export default class LetterSpacingEditing extends Plugin {
    */
    */
   init() {
   init() {
     const editor = this.editor;
     const editor = this.editor;
-    const schema = editor.model.schema;
-    const config: any = editor.config?.get("letterSpacing.options");
-    const enabledOptions: any =
-      config &&
-      config?.map((option: any) => String(option)).filter(isSupported); // filter
-
-    schema.extend("$block", { allowAttributes: "letterSpacing" });
-    editor.model.schema.setAttributeProperties("letterSpacing", {
+    editor.model.schema.extend("$text", {
+      allowAttributes: Letter_Spacing,
+    });
+    editor.model.schema.setAttributeProperties(Letter_Spacing, {
       isFormatting: true,
       isFormatting: true,
+      copyOnEnter: true,
     });
     });
 
 
-    const definition = buildDefinition(
-      enabledOptions /* .filter( option => !isDefault( option ) ) */
+    const supportAllValues = editor.config.get(
+      "letterSpacing.supportAllValues"
     );
     );
-    console.log(definition);
-    editor.conversion.attributeToAttribute(definition);
+    const options = normalizeOptions(
+      this.editor.config.get("letterSpacing.options")
+    ).filter((item: any) => item.model);
+    const definition = buildDefinition(options);
+
+    if (supportAllValues) {
+      this._prepareAnyValueConverters();
+    } else {
+      editor.conversion.attributeToElement(definition);
+    }
 
 
     editor.commands.add("letterSpacing", new LetterSpacingCommand(editor));
     editor.commands.add("letterSpacing", new LetterSpacingCommand(editor));
   }
   }
+  _prepareAnyValueConverters() {
+    const editor = this.editor;
+    editor.conversion.for("downcast").attributeToElement({
+      model: Letter_Spacing,
+      view: (attributeValue, { writer }) => {
+        if (!attributeValue) {
+          return;
+        }
+        return writer.createAttributeElement(
+          "span",
+          { style: "letter-spacing:" + attributeValue },
+          { priority: 7 }
+        );
+      },
+    });
+    editor.conversion.for("upcast").elementToAttribute({
+      model: {
+        key: Letter_Spacing,
+        value: (viewElement: any) => viewElement.getStyle("letter-spacing"),
+      },
+      view: {
+        name: "span",
+        styles: {
+          "letter-spacing": /.*/,
+        },
+      },
+    });
+  }
 }
 }

+ 0 - 112
src/modules/editor/components/CompUI/basicUI/Text/ckeditor-letter-spacing/LetterSpacingUI.ts

@@ -1,112 +0,0 @@
-// import Plugin from "@ckeditor/ckeditor5-core/src/plugin";
-// import Model from "@ckeditor/ckeditor5-ui/src/model";
-// import Collection from "@ckeditor/ckeditor5-utils/src/collection";
-
-// import {
-//   createDropdown,
-//   addListToDropdown,
-// } from "@ckeditor/ckeditor5-ui/src/dropdown/utils";
-
-// import { isSupported, normalizeOptions, optionsKey } from "./utils";
-
-// export default class LetterSpacingUI extends Plugin {
-//   init() {
-//     const editor = this.editor;
-//     const t = editor.t;
-
-//     const options = this._getLocalizedOptions();
-
-//     const command = editor.commands.get("lineHeight");
-
-//     // Register UI component.
-//     editor.ui.componentFactory.add("lineHeight", (locale) => {
-//       const dropdownView = createDropdown(locale);
-//       addListToDropdown(dropdownView, _prepareListOptions(options, command));
-
-//       // Create dropdown model.
-//       dropdownView.buttonView.set({
-//         // label: 'Zeilenhöhe',
-//         label: t("Line Height"),
-//         icon:
-//           editor.config.get("lineHeight.icon") ||
-//           '<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1"  width="24" height="24" viewBox="0 0 24 24"><path fill="#000000" d="M10,13H22V11H10M10,19H22V17H10M10,7H22V5H10M6,7H8.5L5,3.5L1.5,7H4V17H1.5L5,20.5L8.5,17H6V7Z" /></svg>',
-//         tooltip: true,
-//       });
-
-//       dropdownView.extendTemplate({
-//         attributes: {
-//           class: ["p0thi-ckeditor5-lineHeight-dropdown"],
-//         },
-//       });
-
-//       dropdownView.bind("isEnabled").to(command);
-
-//       // Execute command when an item from the dropdown is selected.
-//       this.listenTo(dropdownView, "execute", (evt) => {
-//         editor.execute(evt.source.commandName, {
-//           value: evt.source.commandParam,
-//         });
-//         editor.editing.view.focus();
-//       });
-
-//       return dropdownView;
-//     });
-//   }
-
-//   _getLocalizedOptions() {
-//     const editor = this.editor;
-//     const t = editor.t;
-
-//     const localizedTitles = {
-//       // Default: 'Standard'
-//       Default: t("Default"),
-//     };
-
-//     const options = normalizeOptions(
-//       editor.config
-//         .get("lineHeight.options")
-//         .filter((option) => isSupported(option))
-//     );
-
-//     return options.map((option: any) => {
-//       const title = localizedTitles[option.title];
-
-//       if (title && title != option.title) {
-//         // Clone the option to avoid altering the original `namedPresets` from `./utils.js`.
-//         option = Object.assign({}, option, { title });
-//       }
-
-//       return option;
-//     });
-//   }
-// }
-
-// function _prepareListOptions(options: optionsKey, command: any) {
-//   const itemDefinitions = new Collection();
-
-//   for (const option of options) {
-//     const def = {
-//       type: "button",
-//       model: new Model({
-//         commandName: "lineHeight",
-//         commandParam: option.model,
-//         label: option.title,
-//         class: "p0thi-ckeditor5-lineHeight-dropdown",
-//         withText: true,
-//       }),
-//     };
-
-//     if (option.view && option.view.classes) {
-//       def.model.set("class", `${def.model.class} ${option.view.classes}`);
-//     }
-
-//     def.model.bind("isOn").to(command, "value", (value) => {
-//       const newValue = value ? parseFloat(value) : value;
-//       return newValue === option.model;
-//     });
-
-//     itemDefinitions.add(def);
-//   }
-
-//   return itemDefinitions;
-// }

+ 16 - 15
src/modules/editor/components/CompUI/basicUI/Text/ckeditor-letter-spacing/utils.ts

@@ -1,33 +1,35 @@
+export const Letter_Spacing = "letterSpacing";
+
 export function isSupported(option: any) {
 export function isSupported(option: any) {
   // return supportedOptions.includes( option );
   // return supportedOptions.includes( option );
   return /^\d(.\d+)?$/gm.test(String(option));
   return /^\d(.\d+)?$/gm.test(String(option));
 }
 }
 export type optionsKey = Record<string, any>;
 export type optionsKey = Record<string, any>;
+
 export function buildDefinition(options: any) {
 export function buildDefinition(options: any) {
   const definition = {
   const definition = {
     model: {
     model: {
       key: "letterSpacing",
       key: "letterSpacing",
-      values: options.slice(),
+      values: [] as any,
     },
     },
     view: {} as optionsKey,
     view: {} as optionsKey,
+    upcastAlso: {} as optionsKey,
   };
   };
-
   for (const option of options) {
   for (const option of options) {
-    definition.view[option] = {
-      key: "style",
-      value: {
-        "letter-spacing": option,
-      },
-    };
+    definition.model.values.push(option.model);
+    definition.view[option.model] = option.view;
+    if (option.upcastAlso) {
+      definition.upcastAlso[option.model] = option.upcastAlso;
+    }
   }
   }
-
   return definition;
   return definition;
 }
 }
 export function normalizeOptions(configuredOptions: any) {
 export function normalizeOptions(configuredOptions: any) {
   return configuredOptions
   return configuredOptions
-    .map(optionDefinition)
-    .filter((option: any) => !!option);
+    .map((item: any) => optionDefinition(item))
+    .filter((option: any) => option !== undefined);
 }
 }
+
 function optionDefinition(option: any) {
 function optionDefinition(option: any) {
   if (typeof option === "object") {
   if (typeof option === "object") {
     return option;
     return option;
@@ -49,16 +51,15 @@ function optionDefinition(option: any) {
   return generatePixelPreset(sizePreset);
   return generatePixelPreset(sizePreset);
 }
 }
 
 
-function generatePixelPreset(size: any) {
+function generatePixelPreset(size: number) {
   const sizeName = String(size);
   const sizeName = String(size);
-
   return {
   return {
     title: sizeName,
     title: sizeName,
-    model: size,
+    model: `${size}px`,
     view: {
     view: {
       name: "span",
       name: "span",
       styles: {
       styles: {
-        "letter-spacing": sizeName,
+        "letter-spacing": `${size}px`,
       },
       },
       priority: 5,
       priority: 5,
     },
     },