Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite language to TypeScript #13156

Merged
merged 4 commits into from
Dec 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ packages/ckeditor5-engine/src/**/*.js
packages/ckeditor5-enter/src/**/*.js
packages/ckeditor5-essentials/src/**/*.js
packages/ckeditor5-horizontal-line/src/**/*.js
packages/ckeditor5-language/src/**/*.js
packages/ckeditor5-list/src/**/*.js
packages/ckeditor5-page-break/src/**/*.js
packages/ckeditor5-paragraph/src/**/*.js
Expand Down
7 changes: 6 additions & 1 deletion packages/ckeditor5-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@ export { default as Context } from './context';
export { default as ContextPlugin } from './contextplugin';

export { default as Editor, type EditorReadyEvent } from './editor/editor';
export type { EditorConfig, ToolbarConfig, ToolbarConfigItem } from './editor/editorconfig';
export type {
EditorConfig,
LanguageConfig,
ToolbarConfig,
ToolbarConfigItem
} from './editor/editorconfig';

export { default as attachToForm } from './editor/utils/attachtoform';
export { default as DataApiMixin } from './editor/utils/dataapimixin';
Expand Down
10 changes: 7 additions & 3 deletions packages/ckeditor5-language/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 @@ -22,6 +22,7 @@
"@ckeditor/ckeditor5-paragraph": "^35.4.0",
"@ckeditor/ckeditor5-theme-lark": "^35.4.0",
"@ckeditor/ckeditor5-ui": "^35.4.0",
"typescript": "^4.8.4",
"webpack": "^5.58.1",
"webpack-cli": "^4.9.0"
},
Expand All @@ -40,13 +41,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"
}
}
12 changes: 12 additions & 0 deletions packages/ckeditor5-language/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/

/**
* @module language
*/

export { default as TextPartLanguage } from './textpartlanguage';
export { default as TextPartLanguageEditing } from './textpartlanguageediting';
export { default as TextPartLanguageUI } from './textpartlanguageui';
106 changes: 106 additions & 0 deletions packages/ckeditor5-language/src/textpartlanguage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/**
* @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/

/**
* @module language/textpartlanguage
*/

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

import TextPartLanguageEditing from './textpartlanguageediting';
import TextPartLanguageUI from './textpartlanguageui';

/**
* The text part language feature.
*
* This feature allows setting a language of the document's text part to support
* [WCAG 3.1.2 Language of Parts](https://www.w3.org/TR/UNDERSTANDING-WCAG20/meaning-other-lang-id.html) specification.
*
* To change the editor's UI language, refer to the {@glink features/ui-language Setting the UI language} guide.
*
* For more information about this feature, check the {@glink api/language package page} as well as the {@glink features/language
* Text part language} feature guide.
*
* This is a "glue" plugin which loads the
* {@link module:language/textpartlanguageediting~TextPartLanguageEditing text part language editing feature}
* and the {@link module:language/textpartlanguageui~TextPartLanguageUI text part language UI feature}.
*/
export default class TextPartLanguage extends Plugin {
/**
* @inheritDoc
*/
public static get requires(): PluginDependencies {
return [ TextPartLanguageEditing, TextPartLanguageUI ];
}

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

/**
* The text part language feature option descriptor.
*/
export interface TextPartLanguageOption {

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

/**
* The language code in the ISO 639 format.
*/
languageCode: string;

/**
* The language text direction. Automatically detected if omitted.
*/
textDirection?: LanguageDirection;
}

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

interface LanguageConfig {

/**
* The available {@link module:language/textpartlanguage~TextPartLanguage}
* options that allow setting the language of parts of the content.
*
* This configuration option is available only with the {@glink api/language text part language feature} enabled.
*
* Refer to [WCAG 3.1.2 Language of Parts](https://www.w3.org/TR/UNDERSTANDING-WCAG20/meaning-other-lang-id.html) specification
* to learn more.
*
* To change the editor's UI language, refer to the {@glink features/ui-language Setting the UI language} guide.
*
* The default value is:
*
* ```ts
* const config = [
* { title: 'Arabic', languageCode: 'ar' },
* { title: 'French', languageCode: 'fr' },
* { title: 'Spanish', languageCode: 'es' }
* ];
* ```
*
* The `title` property will be used by the text part language dropdown to render available options.
*
* The `languageCode` property is used for the `lang` attribute in [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) format.
*
* You can also specify the optional `textDirection` property indicating the reading direction of the language.
* Correct values are `ltr` and `rtl`. When the `textDirection` property is missing, the text part language feature will
* specify the text direction by itself.
*/
textPartLanguage?: Array<TextPartLanguageOption>;
}
}
126 changes: 126 additions & 0 deletions packages/ckeditor5-language/src/textpartlanguagecommand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/**
* @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/

/**
* @module language/textpartlanguagecommand
*/

import type { LanguageDirection } from 'ckeditor5/src/utils';
import { Command } from 'ckeditor5/src/core';
import { stringifyLanguageAttribute } from './utils';

/**
* The text part language command plugin.
*/
export default class TextPartLanguageCommand extends Command {
/**
* If the selection starts in a language attribute, the value is set to
* the value of that language in a format:
*
* ```
* <languageCode>:<textDirection>
* ```
*
* * `languageCode` - The language code used for the `lang` attribute in the [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1)
* format.
* * `textDirection` - One of the following values: `rtl` or `ltr`, indicating the reading direction of the language.
*
* See the {@link module:core/editor/editorconfig~LanguageConfig#textPartLanguage text part language configuration}
* for more information about language properties.
*
* It is set to `false` otherwise.
*
* @observable
* @readonly
*/
declare public value: false | string;

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

this.value = this._getValueFromFirstAllowedNode();
this.isEnabled = model.schema.checkAttributeInSelection( doc.selection, 'language' );
}

/**
* Executes the command. Applies the attribute to the selection or removes it from the selection.
*
* If `languageCode` is set to `false` or a `null` value, it will remove attributes. Otherwise, it will set
* the attribute in the `{@link #value value}` format.
*
* The execution result differs, depending on the {@link module:engine/model/document~Document#selection}:
*
* * If the selection is on a range, the command applies the attribute to all nodes in that range
* (if they are allowed to have this attribute by the {@link module:engine/model/schema~Schema schema}).
* * If the selection is collapsed in a non-empty node, the command applies the attribute to the
* {@link module:engine/model/document~Document#selection} itself (note that typed characters copy attributes from the selection).
* * If the selection is collapsed in an empty node, the command applies the attribute to the parent node of the selection (note
* that the selection inherits all attributes from a node if it is in an empty node).
*
* @fires execute
* @param options Command options.
* @param options.languageCode The language code to be applied to the model.
* @param options.textDirection The language text direction.
*/
public override execute(
{ languageCode, textDirection }: { languageCode?: string | false; textDirection?: LanguageDirection } = {}
): void {
const model = this.editor.model;
const doc = model.document;
const selection = doc.selection;

const value = languageCode ? stringifyLanguageAttribute( languageCode, textDirection ) : false;

model.change( writer => {
if ( selection.isCollapsed ) {
if ( value ) {
writer.setSelectionAttribute( 'language', value );
} else {
writer.removeSelectionAttribute( 'language' );
}
} else {
const ranges = model.schema.getValidRanges( selection.getRanges(), 'language' );

for ( const range of ranges ) {
if ( value ) {
writer.setAttribute( 'language', value, range );
} else {
writer.removeAttribute( 'language', range );
}
}
}
} );
}

/**
* Returns the attribute value of the first node in the selection that allows the attribute.
* For a collapsed selection it returns the selection attribute.
*
* @returns The attribute value.
*/
private _getValueFromFirstAllowedNode(): false | string {
const model = this.editor.model;
const schema = model.schema;
const selection = model.document.selection;

if ( selection.isCollapsed ) {
return selection.getAttribute( 'language' ) as string || false;
}

for ( const range of selection.getRanges() ) {
for ( const item of range.getItems() ) {
if ( schema.checkAttribute( item, 'language' ) ) {
return item.getAttribute( 'language' ) as string || false;
}
}
}

return false;
}
}
Loading