Studio Developer Manual / Version 2404
Table Of Contents
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.
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.
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”.