diff --git a/.gitignore b/.gitignore index b3ba906684d..9759925acd8 100644 --- a/.gitignore +++ b/.gitignore @@ -60,6 +60,7 @@ packages/ckeditor5-minimap/src/**/*.js packages/ckeditor5-page-break/src/**/*.js packages/ckeditor5-paragraph/src/**/*.js packages/ckeditor5-paste-from-office/src/**/*.js +packages/ckeditor5-remove-format/src/**/*.js packages/ckeditor5-restricted-editing/src/**/*.js packages/ckeditor5-select-all/src/**/*.js packages/ckeditor5-typing/src/**/*.js diff --git a/packages/ckeditor5-remove-format/src/index.js b/packages/ckeditor5-remove-format/_src/index.js similarity index 100% rename from packages/ckeditor5-remove-format/src/index.js rename to packages/ckeditor5-remove-format/_src/index.js diff --git a/packages/ckeditor5-remove-format/src/removeformat.js b/packages/ckeditor5-remove-format/_src/removeformat.js similarity index 100% rename from packages/ckeditor5-remove-format/src/removeformat.js rename to packages/ckeditor5-remove-format/_src/removeformat.js diff --git a/packages/ckeditor5-remove-format/src/removeformatcommand.js b/packages/ckeditor5-remove-format/_src/removeformatcommand.js similarity index 100% rename from packages/ckeditor5-remove-format/src/removeformatcommand.js rename to packages/ckeditor5-remove-format/_src/removeformatcommand.js diff --git a/packages/ckeditor5-remove-format/src/removeformatediting.js b/packages/ckeditor5-remove-format/_src/removeformatediting.js similarity index 100% rename from packages/ckeditor5-remove-format/src/removeformatediting.js rename to packages/ckeditor5-remove-format/_src/removeformatediting.js diff --git a/packages/ckeditor5-remove-format/src/removeformatui.js b/packages/ckeditor5-remove-format/_src/removeformatui.js similarity index 100% rename from packages/ckeditor5-remove-format/src/removeformatui.js rename to packages/ckeditor5-remove-format/_src/removeformatui.js diff --git a/packages/ckeditor5-remove-format/package.json b/packages/ckeditor5-remove-format/package.json index 7882b6507fc..b2b4afa4d27 100644 --- a/packages/ckeditor5-remove-format/package.json +++ b/packages/ckeditor5-remove-format/package.json @@ -10,7 +10,7 @@ "ckeditor5-plugin", "ckeditor5-dll" ], - "main": "src/index.js", + "main": "src/index.ts", "dependencies": { "ckeditor5": "^36.0.1" }, @@ -30,6 +30,7 @@ "@ckeditor/ckeditor5-typing": "^36.0.1", "@ckeditor/ckeditor5-undo": "^36.0.1", "@ckeditor/ckeditor5-utils": "^36.0.1", + "typescript": "^4.8.4", "webpack": "^5.58.1", "webpack-cli": "^4.9.0" }, @@ -48,13 +49,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" } } diff --git a/packages/ckeditor5-remove-format/src/index.ts b/packages/ckeditor5-remove-format/src/index.ts new file mode 100644 index 00000000000..5ec4ab303ed --- /dev/null +++ b/packages/ckeditor5-remove-format/src/index.ts @@ -0,0 +1,12 @@ +/** + * @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 remove-format + */ + +export { default as RemoveFormat } from './removeformat'; +export { default as RemoveFormatEditing } from './removeformatediting'; +export { default as RemoveFormatUI } from './removeformatui'; diff --git a/packages/ckeditor5-remove-format/src/removeformat.ts b/packages/ckeditor5-remove-format/src/removeformat.ts new file mode 100644 index 00000000000..51cf7687c15 --- /dev/null +++ b/packages/ckeditor5-remove-format/src/removeformat.ts @@ -0,0 +1,43 @@ +/** + * @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 remove-format/removeformat + */ + +import { Plugin, type PluginDependencies } from 'ckeditor5/src/core'; + +import RemoveFormatUI from './removeformatui'; +import RemoveFormatEditing from './removeformatediting'; + +/** + * The remove format plugin. + * + * This is a "glue" plugin which loads the {@link module:remove-format/removeformatediting~RemoveFormatEditing} + * and {@link module:remove-format/removeformatui~RemoveFormatUI} plugins. + * + * For a detailed overview, check out the {@glink features/remove-format remove format} feature documentation. + */ +export default class RemoveFormat extends Plugin { + /** + * @inheritDoc + */ + public static get requires(): PluginDependencies { + return [ RemoveFormatEditing, RemoveFormatUI ]; + } + + /** + * @inheritDoc + */ + public static get pluginName(): 'RemoveFormat' { + return 'RemoveFormat'; + } +} + +declare module '@ckeditor/ckeditor5-core' { + interface PluginsMap { + [ RemoveFormat.pluginName ]: RemoveFormat; + } +} diff --git a/packages/ckeditor5-remove-format/src/removeformatcommand.ts b/packages/ckeditor5-remove-format/src/removeformatcommand.ts new file mode 100644 index 00000000000..2cdfb4a0db5 --- /dev/null +++ b/packages/ckeditor5-remove-format/src/removeformatcommand.ts @@ -0,0 +1,118 @@ +/** + * @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 remove-format/removeformatcommand + */ + +import type { DocumentSelection, Item, Schema } from 'ckeditor5/src/engine'; +import { Command } from 'ckeditor5/src/core'; +import { first } from 'ckeditor5/src/utils'; + +/** + * The remove format command. + * + * It is used by the {@link module:remove-format/removeformat~RemoveFormat remove format feature} + * to clear the formatting in the selection. + * + * ```ts + * editor.execute( 'removeFormat' ); + * ``` + */ +export default class RemoveFormatCommand extends Command { + declare public value: boolean; + + /** + * @inheritDoc + */ + public override refresh(): void { + const model = this.editor.model; + + this.isEnabled = !!first( this._getFormattingItems( model.document.selection, model.schema ) ); + } + + /** + * @inheritDoc + */ + public override execute(): void { + const model = this.editor.model; + const schema = model.schema; + + model.change( writer => { + for ( const item of this._getFormattingItems( model.document.selection, schema ) ) { + if ( item.is( 'selection' ) ) { + for ( const attributeName of this._getFormattingAttributes( item, schema ) ) { + writer.removeSelectionAttribute( attributeName ); + } + } else { + // Workaround for items with multiple removable attributes. See + // https://github.com/ckeditor/ckeditor5-remove-format/pull/1#pullrequestreview-220515609 + const itemRange = writer.createRangeOn( item ); + + for ( const attributeName of this._getFormattingAttributes( item, schema ) ) { + writer.removeAttribute( attributeName, itemRange ); + } + } + } + } ); + } + + /** + * Returns an iterable of items in a selection (including the selection itself) that have formatting model + * attributes to be removed by the feature. + * + * @param schema The schema describing the item. + */ + private* _getFormattingItems( selection: DocumentSelection, schema: Schema ) { + const itemHasRemovableFormatting = ( item: Item | DocumentSelection ) => { + return !!first( this._getFormattingAttributes( item, schema ) ); + }; + + // Check formatting on selected items that are not blocks. + for ( const curRange of selection.getRanges() ) { + for ( const item of curRange.getItems() ) { + if ( !schema.isBlock( item ) && itemHasRemovableFormatting( item ) ) { + yield item; + } + } + } + + // Check formatting from selected blocks. + for ( const block of selection.getSelectedBlocks() ) { + if ( itemHasRemovableFormatting( block ) ) { + yield block; + } + } + + // Finally the selection might be formatted as well, so make sure to check it. + if ( itemHasRemovableFormatting( selection ) ) { + yield selection; + } + } + + /** + * Returns an iterable of formatting attributes of a given model item. + * + * **Note:** Formatting items have the `isFormatting` property set to `true`. + * + * @param schema The schema describing the item. + * @returns The names of formatting attributes found in a given item. + */ + private* _getFormattingAttributes( item: Item | DocumentSelection, schema: Schema ) { + for ( const [ attributeName ] of item.getAttributes() ) { + const attributeProperties = schema.getAttributeProperties( attributeName ); + + if ( attributeProperties && attributeProperties.isFormatting ) { + yield attributeName; + } + } + } +} + +declare module '@ckeditor/ckeditor5-core' { + interface CommandsMap { + removeFormat: RemoveFormatCommand; + } +} diff --git a/packages/ckeditor5-remove-format/src/removeformatediting.ts b/packages/ckeditor5-remove-format/src/removeformatediting.ts new file mode 100644 index 00000000000..d13eb525161 --- /dev/null +++ b/packages/ckeditor5-remove-format/src/removeformatediting.ts @@ -0,0 +1,41 @@ +/** + * @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 remove-format/removeformatediting + */ + +import { Plugin } from 'ckeditor5/src/core'; + +import RemoveFormatCommand from './removeformatcommand'; + +/** + * The remove format editing plugin. + * + * It registers the {@link module:remove-format/removeformatcommand~RemoveFormatCommand removeFormat} command. + */ +export default class RemoveFormatEditing extends Plugin { + /** + * @inheritDoc + */ + public static get pluginName(): 'RemoveFormatEditing' { + return 'RemoveFormatEditing'; + } + + /** + * @inheritDoc + */ + public init(): void { + const editor = this.editor; + + editor.commands.add( 'removeFormat', new RemoveFormatCommand( editor ) ); + } +} + +declare module '@ckeditor/ckeditor5-core' { + interface PluginsMap { + [ RemoveFormatEditing.pluginName ]: RemoveFormatEditing; + } +} diff --git a/packages/ckeditor5-remove-format/src/removeformatui.ts b/packages/ckeditor5-remove-format/src/removeformatui.ts new file mode 100644 index 00000000000..06e76a9bc76 --- /dev/null +++ b/packages/ckeditor5-remove-format/src/removeformatui.ts @@ -0,0 +1,65 @@ +/** + * @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 remove-format/removeformatui + */ + +import { Plugin } from 'ckeditor5/src/core'; +import { ButtonView } from 'ckeditor5/src/ui'; + +import type RemoveFormatCommand from './removeformatcommand'; + +import removeFormatIcon from '../theme/icons/remove-format.svg'; + +const REMOVE_FORMAT = 'removeFormat'; + +/** + * The remove format UI plugin. It registers the `'removeFormat'` button which can be + * used in the toolbar. + */ +export default class RemoveFormatUI extends Plugin { + /** + * @inheritDoc + */ + public static get pluginName(): 'RemoveFormatUI' { + return 'RemoveFormatUI'; + } + + /** + * @inheritDoc + */ + public init(): void { + const editor = this.editor; + const t = editor.t; + + editor.ui.componentFactory.add( REMOVE_FORMAT, locale => { + const command: RemoveFormatCommand = editor.commands.get( REMOVE_FORMAT )!; + const view = new ButtonView( locale ); + + view.set( { + label: t( 'Remove Format' ), + icon: removeFormatIcon, + tooltip: true + } ); + + view.bind( 'isOn', 'isEnabled' ).to( command, 'value', 'isEnabled' ); + + // Execute the command. + this.listenTo( view, 'execute', () => { + editor.execute( REMOVE_FORMAT ); + editor.editing.view.focus(); + } ); + + return view; + } ); + } +} + +declare module '@ckeditor/ckeditor5-core' { + interface PluginsMap { + [ RemoveFormatUI.pluginName ]: RemoveFormatUI; + } +} diff --git a/packages/ckeditor5-remove-format/tsconfig.json b/packages/ckeditor5-remove-format/tsconfig.json new file mode 100644 index 00000000000..9d4c891939c --- /dev/null +++ b/packages/ckeditor5-remove-format/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "ckeditor5/tsconfig.json", + "include": [ + "src", + "../../typings" + ] +} diff --git a/packages/ckeditor5-remove-format/tsconfig.release.json b/packages/ckeditor5-remove-format/tsconfig.release.json new file mode 100644 index 00000000000..6d2d43909f9 --- /dev/null +++ b/packages/ckeditor5-remove-format/tsconfig.release.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.release.json", + "include": [ + "./src/", + "../../typings/" + ], + "exclude": [ + "./tests/" + ] +}