Skip to content

Commit

Permalink
Merge pull request #13248 from ckeditor/ck/13013
Browse files Browse the repository at this point in the history
Other (highlight): Rewrite ckeditor5-highlight to TypeScript. Closes #13013.
  • Loading branch information
arkflpc authored Jan 18, 2023
2 parents 543a5e3 + 762a20b commit 4629e92
Show file tree
Hide file tree
Showing 15 changed files with 797 additions and 3 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ packages/ckeditor5-engine/src/**/*.js
packages/ckeditor5-enter/src/**/*.js
packages/ckeditor5-essentials/src/**/*.js
packages/ckeditor5-heading/src/**/*.js
packages/ckeditor5-highlight/src/**/*.js
packages/ckeditor5-horizontal-line/src/**/*.js
packages/ckeditor5-language/src/**/*.js
packages/ckeditor5-list/src/**/*.js
Expand Down
2 changes: 1 addition & 1 deletion packages/ckeditor5-engine/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export { default as Range } from './model/range';
export { default as LiveRange } from './model/liverange';
export { default as LivePosition } from './model/liveposition';
export { default as Model } from './model/model';
export { default as TreeWalker } from './model/treewalker';
export { default as TreeWalker, type TreeWalkerValue } from './model/treewalker';
export { default as Element } from './model/element';
export { default as Position } from './model/position';
export { default as DocumentFragment } from './model/documentfragment';
Expand Down
File renamed without changes.
9 changes: 7 additions & 2 deletions packages/ckeditor5-highlight/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"ckeditor5-plugin",
"ckeditor5-dll"
],
"main": "src/index.js",
"main": "src/index.ts",
"dependencies": {
"ckeditor5": "^35.4.0"
},
Expand All @@ -28,6 +28,7 @@
"@ckeditor/ckeditor5-theme-lark": "^35.4.0",
"@ckeditor/ckeditor5-typing": "^35.4.0",
"@ckeditor/ckeditor5-utils": "^35.4.0",
"typescript": "^4.8.4",
"webpack": "^5.58.1",
"webpack-cli": "^4.9.0"
},
Expand All @@ -47,12 +48,16 @@
"files": [
"lang",
"src",
"src/**/*.js",
"src/**/*.d.ts",
"theme",
"build",
"ckeditor5-metadata.json",
"CHANGELOG.md"
],
"scripts": {
"dll:build": "webpack"
"dll:build": "webpack",
"build": "tsc -p ./tsconfig.release.json",
"postversion": "npm run build"
}
}
225 changes: 225 additions & 0 deletions packages/ckeditor5-highlight/src/highlight.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
/**
* @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/

/**
* @module highlight/highlight
*/

import { Plugin, type PluginDependencies } from 'ckeditor5/src/core';

import HighlightEditing from './highlightediting';
import HighlightUI from './highlightui';

/**
* The highlight plugin.
*
* For a detailed overview, check the {@glink features/highlight Highlight feature} documentation.
*
* This is a "glue" plugin which loads the {@link module:highlight/highlightediting~HighlightEditing} and
* {@link module:highlight/highlightui~HighlightUI} plugins.
*/
export default class Highlight extends Plugin {
/**
* @inheritDoc
*/
public static get requires(): PluginDependencies {
return [ HighlightEditing, HighlightUI ];
}

/**
* @inheritDoc
*/
public static get pluginName(): 'Highlight' {
return 'Highlight';
}
}

/**
* The highlight option descriptor. See {@link module:highlight/highlight~HighlightConfig} to learn more.
*
* ```ts
* {
* model: 'pinkMarker',
* class: 'marker-pink',
* title: 'Pink Marker',
* color: 'var(--ck-highlight-marker-pink)',
* type: 'marker'
* }
* ```
*/
export interface HighlightOption {

/**
* The user-readable title of the option.
*/
title: string;

/**
* The unique attribute value in the model.
*/
model: string;

/**
* The CSS `var()` used for the highlighter. The color is used in the user interface to represent the highlighter.
* There is a possibility to use the default color format like rgb, hex or hsl, but you need to care about the color of `<mark>`
* by adding CSS classes definition.
*/
color: string;

/**
* The CSS class used on the `<mark>` element in the view. It should match the `color` setting.
*/
class: string;

/**
* The type of highlighter:
*
* * `'marker'` &ndash; Uses the `color` as the `background-color` style,
* * `'pen'` &ndash; Uses the `color` as the font `color` style.
*/
type: 'marker' | 'pen';
}

/**
* The configuration of the {@link module:highlight/highlight~Highlight highlight feature}.
* ```ts
* ClassicEditor
* .create( editorElement, {
* highlight: ... // Highlight feature configuration.
* } )
* .then( ... )
* .catch( ... );
* ```
* See {@link module:core/editor/editorconfig~EditorConfig all editor options}.
*/
export interface HighlightConfig {

/**
* The available highlight options. The default value is:
* ```ts
* options: [
* {
* model: 'yellowMarker',
* class: 'marker-yellow',
* title: 'Yellow marker',
* color: 'var(--ck-highlight-marker-yellow)',
* type: 'marker'
* },
* {
* model: 'greenMarker',
* class: 'marker-green',
* title: 'Green marker',
* color: 'var(--ck-highlight-marker-green)',
* type: 'marker'
* },
* {
* model: 'pinkMarker',
* class: 'marker-pink',
* title: 'Pink marker',
* color: 'var(--ck-highlight-marker-pink)',
* type: 'marker'
* },
* {
* model: 'blueMarker',
* class: 'marker-blue',
* title: 'Blue marker',
* color: 'var(--ck-highlight-marker-blue)',
* type: 'marker'
* },
* {
* model: 'redPen',
* class: 'pen-red',
* title: 'Red pen',
* color: 'var(--ck-highlight-pen-red)',
* type: 'pen'
* },
* {
* model: 'greenPen',
* class: 'pen-green',
* title: 'Green pen',
* color: 'var(--ck-highlight-pen-green)',
* type: 'pen'
* }
* ]
* ```
*
* There are two types of highlighters available:
*
* * `'marker'` &ndash; Rendered as a `<mark>` element, styled with the `background-color`.
* * `'pen'` &ndash; Rendered as a `<mark>` element, styled with the font `color`.
*
* **Note**: The highlight feature provides a stylesheet with the CSS classes and corresponding colors defined
* as CSS variables.
*
* ```ts
* :root {
* --ck-highlight-marker-yellow: #fdfd77;
* --ck-highlight-marker-green: #63f963;
* --ck-highlight-marker-pink: #fc7999;
* --ck-highlight-marker-blue: #72cdfd;
* --ck-highlight-pen-red: #e91313;
* --ck-highlight-pen-green: #118800;
* }
*
* .marker-yellow { ... }
* .marker-green { ... }
* .marker-pink { ... }
* .marker-blue { ... }
* .pen-red { ... }
* .pen-green { ... }
* ```
*
* It is possible to define the `color` property directly as `rgba(R, G, B, A)`,
* `#RRGGBB[AA]` or `hsla(H, S, L, A)`. In such situation, the color will **only** apply to the UI of
* the editor and the `<mark>` elements in the content must be styled by custom classes provided by
* a dedicated stylesheet.
*
* **Note**: It is recommended for the `color` property to correspond to the class in the content
* stylesheet because it represents the highlighter in the user interface of the editor.
*
* ```ts
* ClassicEditor
* .create( editorElement, {
* highlight: {
* options: [
* {
* model: 'pinkMarker',
* class: 'marker-pink',
* title: 'Pink Marker',
* color: 'var(--ck-highlight-marker-pink)',
* type: 'marker'
* },
* {
* model: 'redPen',
* class: 'pen-red',
* title: 'Red Pen',
* color: 'var(--ck-highlight-pen-red)',
* type: 'pen'
* },
* ]
* }
* } )
* .then( ... )
* .catch( ... );
* ```
*/
options: Array<HighlightOption>;
}

declare module '@ckeditor/ckeditor5-core' {
interface PluginsMap {
[ Highlight.pluginName ]: Highlight;
}

interface EditorConfig {

/**
* The configuration of the {@link module:highlight/highlight~Highlight} feature.
*
* Read more in {@link module:highlight/highlight~HighlightConfig}.
*/
highlight?: HighlightConfig;
}
}
113 changes: 113 additions & 0 deletions packages/ckeditor5-highlight/src/highlightcommand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/**
* @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/

/**
* @module highlight/highlightcommand
*/

import { Command } from 'ckeditor5/src/core';
import type { TreeWalkerValue } from 'ckeditor5/src/engine';

/**
* The highlight command. It is used by the {@link module:highlight/highlightediting~HighlightEditing highlight feature}
* to apply the text highlighting.
*
* ```ts
* editor.execute( 'highlight', { value: 'greenMarker' } );
* ```
*
* **Note**: Executing the command without a value removes the attribute from the model. If the selection is collapsed
* inside a text with the highlight attribute, the command will remove the attribute from the entire range
* of that text.
*/
export default class HighlightCommand extends Command {
/**
* A value indicating whether the command is active. If the selection has some highlight attribute,
* it corresponds to the value of that attribute.
*
* @observable
* @readonly
*/
declare public value: string | undefined;

/**
* @inheritDoc
*/
public override refresh(): void {
const model = this.editor.model;
const doc = model.document;

this.value = doc.selection.getAttribute( 'highlight' ) as string | undefined;
this.isEnabled = model.schema.checkAttributeInSelection( doc.selection, 'highlight' );
}

/**
* Executes the command.
*
* @param options Options for the executed command.
* @param options.value The value to apply.
*
* @fires execute
*/
public override execute( options: { value?: string | null } = {} ): void {
const model = this.editor.model;
const document = model.document;
const selection = document.selection;

const highlighter = options.value;

model.change( writer => {
if ( selection.isCollapsed ) {
const position = selection.getFirstPosition()!;

// When selection is inside text with `highlight` attribute.
if ( selection.hasAttribute( 'highlight' ) ) {
// Find the full highlighted range.
const isSameHighlight = ( value: TreeWalkerValue ) => {
return value.item.hasAttribute( 'highlight' ) && value.item.getAttribute( 'highlight' ) === this.value;
};

const highlightStart = position.getLastMatchingPosition( isSameHighlight, { direction: 'backward' } );
const highlightEnd = position.getLastMatchingPosition( isSameHighlight );

const highlightRange = writer.createRange( highlightStart, highlightEnd );

// Then depending on current value...
if ( !highlighter || this.value === highlighter ) {
// ...remove attribute when passing highlighter different then current or executing "eraser".

// If we're at the end of the highlighted range, we don't want to remove highlight of the range.
if ( !position.isEqual( highlightEnd ) ) {
writer.removeAttribute( 'highlight', highlightRange );
}

writer.removeSelectionAttribute( 'highlight' );
} else {
// ...update `highlight` value.

// If we're at the end of the highlighted range, we don't want to change the highlight of the range.
if ( !position.isEqual( highlightEnd ) ) {
writer.setAttribute( 'highlight', highlighter, highlightRange );
}

writer.setSelectionAttribute( 'highlight', highlighter );
}
} else if ( highlighter ) {
writer.setSelectionAttribute( 'highlight', highlighter );
}
} else {
const ranges = model.schema.getValidRanges( selection.getRanges(), 'highlight' );

for ( const range of ranges ) {
if ( highlighter ) {
writer.setAttribute( 'highlight', highlighter, range );
} else {
writer.removeAttribute( 'highlight', range );
}
}
}
} );
}
}
Loading

0 comments on commit 4629e92

Please sign in to comment.