close

Filter

loading table of contents...

Studio Developer Manual / Version 2406.0

Table Of Contents

10.3.10 Link Editing

In this section you will get some more details on customizing CKEditor 5 regarding required customizations for link editing behavior based on the Link feature of CKEditor 5. For general information, like, how to configure https:// as default protocol for links, please have a look at the corresponding feature documentation.

This section is recommended to be read, if you are about to implement support for editing custom attributes, that are bound to links.

The Section “Custom Link Attributes” describes an assistive plugin LinkAttributes that helps to manage link-related attributes. The plugin is bundled in npm package @coremedia/​ckeditor5-link-common. For more details regarding the plugins consult CoreMedia CKEditor 5 Plugin: LinkAttributes.

The Section “Handle Artificial xlink:role” describes how to deal with so-called artificial xlink:role attributes. Short: How to use xlink:role to store information, that shall not be represented within the target attribute within CKEditor 5 model and view layers.

Default Plugins

The default CKEditor 5 configuration ckeditorDefault.ts (@coremedia-blueprint/​studio-client.​ckeditor5) contains these main plugins provided by CoreMedia:

  • ContentLinks

  • LinkTarget

They are responsible to enable linking to content items as well as to provide support for target attribute editing. For details see Section 10.2.9, “Link Plugins”.

Custom Link Attributes

Along with the plugins listed in Section “Default Plugins” ckeditorDefault.ts also refers to a plugin called LinkAttributes. This plugin may be important, when it is about adding support for additional attributes bound to links.

This plugin can be summarized as to integrate into typical link attribute editing and clean-up behavior as implemented by CKEditor 5 Link feature. This includes the so-called two-step-caret-movement (see TwoStepCaretMovement) as well as removing all link-related attributes on removing links.

Note

Prefer CKEditor 5 API, if available

If CKSource provides an API for CKEditor 5 to register such link-related attributes, it is preferred to use that one, as it is assumed to cover more use-cases.

The plugin provides two configuration layers:

  • CKEditor 5 instance configuration

  • configuration API suitable for use in custom plugins

CKEditor 5 Instance Configuration: ckeditorDefault.ts ships with a configuration for attributes, that are part of CoreMedia Rich Text 1.0, but not yet covered by corresponding editing features.

import { LinkAttributesConfig } from "@coremedia/ckeditor5-link-common";

export const linkAttributesConfig: LinkAttributesConfig = {
  attributes: [
    {
      view: "title",
      model: "linkTitle",
    },
    {
      view: "data-xlink-actuate",
      model: "linkActuate",
    },
  ],
};

Example 10.14. LinkAttributes Configuration


Example 10.14, “LinkAttributes Configuration” shows a configuration for attributes title and data-xlink-actuate, that are a result of the default data-processing for CoreMedia Rich Text 1.0 (see Section 10.2.10, “Rich Text Plugin”) of attributes xlink:title and xlink:actuate.

import Link from '@ckeditor/ckeditor5-link/src/link';
import { LinkAttributes, linkAttributesConfig } from 
  "@coremedia/ckeditor5-link-common";

return ClassicEditor.create(domElement, {
  /* ... */
  plugins: [
    /* ... */
    Link,
    LinkAttributes,
    /* ... */
  ],
  link: {
    defaultProtocol: 'https://',
    ...linkAttributesConfig,
  },
});

/* ... */

Example 10.15. LinkAttributes Configuration Usage


Example 10.15, “LinkAttributes Configuration Usage” demonstrates the integration into ckeditorDefault.ts merging the configuration with the configuration of the CKEditor 5 Link feature. Plugin LinkAttributes will parse this configuration and trigger corresponding configuration for two-step-caret-movement and link-attribute clean-up.

import Plugin from "@ckeditor/ckeditor5-core/src/plugin";
import { getLinkAttributes, LinkAttributes } from
  "@coremedia/ckeditor5-link-common";

export class MyLinkTitleEditing extends Plugin {
  static readonly pluginName: string = "MyLinkTitleEditing";

  static readonly requires = [LinkAttributes, /* ... */];

  init(): void {
    const { editor } = this;

    getLinkAttributes(editor)?
      .registerAttribute({ view: "title", model: "linkTitle" });
  }
}

Example 10.16. LinkAttributes at Plugin Initialization


Example 10.16, “LinkAttributes at Plugin Initialization” is a typical usage from within plugins. Here, a plugin provides capabilities for link title editing. It is recommended to move the configuration for this attribute to the plugin initialization then.

Thus, if you have any custom attribute to edit, that is only valid in context of links, we recommend using LinkAttributes to register this attribute, or, as alternative, carefully review and adapt behaviors as can be found in sources of the CKEditor 5 Link plugin.

Handle Artificial xlink:role

In this section you will learn how to deal with so-called artificial xlink:role attributes, that should not be represented as target in the CKEditor 5 model and view layers. You will learn, how to override this behavior to store the value in any other attribute.

Note

Artificial xlink:role Attribute

We call an xlink:role attribute artificial when it is non-empty for any other value of xlink:show than "other".

It is artificial in that sense that the typical transformation applied to CoreMedia Rich Text 1.0 will use the value of xlink:role to render the HTML target when xlink:show is set to "other". In other cases the value of xlink:role is typically ignored, its use is not clearly defined. And we call this usage artificial.

To deal with artificial xlink:role states, you may add a data-processing rule with at least "high" priority, that processes the xlink:role attribute before the default processing. For toData processing this is best done in prepare step and for toView processing in imported step. For details find corresponding references in Section 10.2.10, “Rich Text Plugin”.

Convenience API: For convenience, if not even recommended, as we ensure correct processing order, you may use a factory method for a data-processing rule called mapArtificialXLinkRole that ships with @coremedia/ckeditor5-coremedia-richtext.

A simple example, assuming usage from within a custom plugin, is shown in Example 10.17, “Example Usage of mapArtificialXLinkRole”. If adding the generated rule, exactly as shown in the example, it will activate a mode that can be described as: Remove any artificial xlink:role attribute. In any other case than having xlink:show set to "other", it will just strip xlink:role from the data and consequently will not add when transforming the view back to the data.

import Plugin
  from "@ckeditor/ckeditor5-core/src/plugin";
import { mapArtificialXLinkRole }
  from "@coremedia/ckeditor5-coremedia-richtext";

export class ArtificialRoleToClass extends Plugin {
  static readonly pluginName: string = "ArtificialRoleToClass";

  init(): void {
    const { editor: {data: { processor } } } = this;

    if (isRichTextDataProcessor(processor)) {
      processor.addRules([
        mapArtificialXLinkRole(/* ... config ... */);
      ]);
    }
  }
}

Example 10.17. Example Usage of mapArtificialXLinkRole


If you want to store it in some other attribute instead, your configuration of mapArtificialXLinkRole may be similar as shown in Example 10.18, “Example Configuration of mapArtificialXLinkRole”. Here, the artificial xlink:role is stored as additional class attribute value within CKEditor 5 model and view layers and later restored from class attribute in toData processing.

{
  toView: (element, role) => {
    const sanitizedRole = role.replaceAll(/\s/g, "_");
    element.classList.add(`role_${sanitizedRole}`);
  },
  toData: (element) => {
    const matcher = /^role_(\S*)$/;
    const matchedClasses: string[] = [];
    let role: string | undefined;
    for (const cls of element.classList) {
      const match = cls.match(matcher);
      if (match) {
        const [matchedCls, matchedRole] = match;
        role = matchedRole;
        matchedClasses.push(matchedCls);
      }
    }
    // Clean-up any matched classes and possibly left-over `class=""`.
    element.classList.remove(...matchedClasses);
    if (element.classList.length === 0) {
      element.removeAttribute("class");
    }
    return role;
  },
}

Example 10.18. Example Configuration of mapArtificialXLinkRole


If instead, you store the state in some different attribute (e.g., within the anchor's download attribute), ensure to register the corresponding attribute as belonging to the link. For details, see Section “Custom Link Attributes”.

Search Results

Table Of Contents
warning

Your Internet Explorer is no longer supported.

Please use Mozilla Firefox, Google Chrome, or Microsoft Edge.