diff --git a/MIGRATION.md b/MIGRATION.md new file mode 100644 index 0000000..61b8101 --- /dev/null +++ b/MIGRATION.md @@ -0,0 +1,385 @@ +

Migration guides

+ +### Navigation + +- [From v4 to v5](#4to5) +- [From v3 to v4](#3to4) +- [From v2 to v3](#2to3) +- [From v1 to v2](#1to2) + +## From v4 to v5 + +Version 5 introduces several major breaking changes that will affect all users: + +⚠️ A new configuration method has been introduced. + +⚠️ The `default` preset has been renamed to `defaultHtml`. + +To facilitate a smooth transition, guidance is provided for the following scenarios: + +- [No custom configuration](#no-custom-configuration) +- [Custom configuration](#custom-configuration) +- [Locally modified fork of the plugin](#locally-modified-fork) + +#### No custom configuration: + +If you don't have custom plugin configuration, you only need to update the existing fields to use +the new default HTML preset to avoid missing preset error. There are two options how to solve it: + +- **Option1 (Preferable)**: Update the fields in the Content-Type Builder or modify your shemas + manually to use `defaultHtml` preset instead of `default`: + +```js +// api/type_name/content-types/type_name/schema.json + +"name_of_field": { + "type": "customField", + "options": { + "preset": "default", // -> "defaultHtml" + }, + "customField": "plugin::ckeditor5.CKEditor" +}, +``` + +- **Option2**: Apply custom plugin configuration. Set the name in the `defaultHtmlPreset` to + `default` and add it to the `presets` array: + +```ts +// src/admin/app.[js|tsx] + +import { setPluginConfig, defaultHtmlPreset } from '@_sh/strapi-plugin-ckeditor'; + +export default { + register() { + defaultHtmlPreset.name = 'default'; + setPluginConfig({ presets: [defaultHtmlPreset] }); + }, +}; +``` + +#### Custom configuration: + +In addition to the steps mentioned in the previous section: + +⚠️ The `presets` property is now an array of objects of type `Preset` instead of an object. + +⚠️ The `field` property in a `Preset` has been replaced by `name` and `description`. + +⚠️ The plugin no longer exposes any CKEditor packages to the global object, you must import them +directly. + +⚠️ The provided `theme` and `presets` are no longer merged with the defaults, instead, overwrite them. + +⚠️ The style plugin and material color palette have been removed from the default preset. + +⚠️ The plugin's theme has been updated to use Strapi's theme variables. + +To transition to the new configuration method, copy and paste your configuration from the existing +configuration file into `/src/admin/app.js|tsx`: + +(Alternatively you can move you configuration file to `/src/admin/`, export the configuration, +and then import it in `admin.js|tsx`) + +**Before (v4):** + +```ts +// config/ckedtior.js + +const CKEConfig = () => ({ + dontMergePresets: true, + presets: { + default: { + editorConfig: { + toolbar: ['heading', '|', 'bold', 'italic', '|', 'strapiMediaLib', '|', 'undo', 'redo'], + }, + }, + myCustomPreset: { + field: { + key: 'myCustomPreset', + value: 'myCustomPreset', + metadatas: { + intlLabel: { + id: 'ckeditor5.preset.myCustomPreset.label', + defaultMessage: 'My custom preset', + }, + }, + }, + editorConfig: { + plugins: [ + globalThis.SH_CKE.Autoformat, + globalThis.SH_CKE.Bold, + globalThis.SH_CKE.Italic, + globalThis.SH_CKE.Essentials, + globalThis.SH_CKE.Heading, + globalThis.SH_CKE.StrapiMediaLib, + globalThis.SH_CKE.StrapiUploadAdapter, + //... + ], + toolbar: ['heading', '|', 'bold', 'italic', '|', 'strapiMediaLib', '|', 'undo', 'redo'], + //... + }, + }, + }, + // theme: {}, +}); +``` + +**Now (v5):** + +
+ app.js + +```ts +// src/admin/app.js + +import { Autoformat, Bold, Italic, Essentials, Heading } from 'ckeditor5'; + +import { + setPluginConfig, + defaultHtmlPreset, + StrapiMediaLib, + StrapiUploadAdapter, +} from '@_sh/strapi-plugin-ckeditor'; + +const CKEConfig = () => ({ + presets: [ + { + ...defaultHtmlPreset, + + /** + * If you use default preset and haven't updated your schemas to replace + * the `default` preset with `defaultHtml`, you can change `name` + * in defaultHtmlPreset to 'default' to avoid missing preset error. + */ + // name: 'default', + + editorConfig: { + ...defaultHtmlPreset.editorConfig, + toolbar: ['heading', '|', 'bold', 'italic', '|', 'strapiMediaLib', '|', 'undo', 'redo'], + }, + }, + { + name: 'myCustomPreset', + description: 'My custom preset', + editorConfig: { + licenseKey: 'GPL', + plugins: [ + Autoformat, + Bold, + Italic, + Essentials, + Heading, + StrapiMediaLib, + StrapiUploadAdapter, + //... + ], + toolbar: ['heading', '|', 'bold', 'italic', '|', 'strapiMediaLib', '|', 'undo', 'redo'], + //... + }, + }, + ], + // theme: {}, +}); + +export default { + register() { + const myConfig = CKEConfig(); + setPluginConfig(myConfig); + }, +}; +``` + +
+ +
+ app.tsx + +```ts +// src/admin/app.tsx + +import { Autoformat, Bold, Italic, Essentials, Heading } from 'ckeditor5'; + +import { + type PluginConfig, + setPluginConfig, + defaultHtmlPreset, + StrapiMediaLib, + StrapiUploadAdapter, +} from '@_sh/strapi-plugin-ckeditor'; + +const CKEConfig = (): PluginConfig => ({ + presets: [ + { + ...defaultHtmlPreset, + + /** + * If you use default preset and haven't updated your schemas to replace + * the `default` preset with `defaultHtml`, you can change `name` + * in defaultHtmlPreset to 'default' to avoid missing preset error. + */ + // name: 'default', + + editorConfig: { + ...defaultHtmlPreset.editorConfig, + toolbar: ['heading', '|', 'bold', 'italic', '|', 'strapiMediaLib', '|', 'undo', 'redo'], + }, + }, + { + name: 'myCustomPreset', + description: 'My custom preset', + editorConfig: { + licenseKey: 'GPL', + plugins: [ + Autoformat, + Bold, + Italic, + Essentials, + Heading, + StrapiMediaLib, + StrapiUploadAdapter, + //... + ], + toolbar: ['heading', '|', 'bold', 'italic', '|', 'strapiMediaLib', '|', 'undo', 'redo'], + //... + }, + }, + ], + // theme: {}, +}); + +export default { + register() { + const myConfig = CKEConfig(); + setPluginConfig(myConfig); + }, +}; +``` + +
+ +####
Locally modified fork of the plugin: + +The guidance provided in the previous two sections likely applies to your case as well. + +Additionally: + +⚠️ The plugin has been migrated to TypeScript. + +⚠️ The codebase has been significantly refactored. + +⚠️ Several new features have been introduced, such as fullscreen option. + +These changes will likely impact your setup. Please review the updated codebase. + +## From v3 to v4 + +- The new version introduces support for Strapi v5 and is incompatible with Strapi v4. You will need to update your Strapi project to version 5 before upgrading. + +- The plugin development process has changed. Please refer to the updated contribution guide for more information. + +## From v2 to v3 + +- The default editor configurations (toolbar, toolbarBalloon, blockBalloon) have been removed and now there is only one preset by default. You will need to update your fields in the Content-Type Builder. + +- Config file extension has changed from `.txt` to `.js` or `.ts` +- Configuration object properties have been renamed: + - `configsOverwrite` -> `dontMergePresets` + - `themeOverwrite` -> `dontMergeTheme` + - `configs` -> `presets` +- From v3 instead of globalThis.CKEditorConfig = {..}, the config file must define a function called CKEConfig that returns the configuration object. + +Example of the new configuration file: + +
+ ckeditor.js + +```js +const CKEConfig = () => ({ + presets: { + myCustomPreset: { + field: { + key: 'myCustomPreset', + value: 'myCustomPreset', + metadatas: { + intlLabel: { + id: 'ckeditor5.preset.myCustomPreset.label', + defaultMessage: 'My custom preset', + }, + }, + }, + editorConfig: { + plugins: [ + globalThis.SH_CKE.Autoformat, + globalThis.SH_CKE.Bold, + globalThis.SH_CKE.Italic, + globalThis.SH_CKE.Essentials, + globalThis.SH_CKE.Heading, + globalThis.SH_CKE.Image, + globalThis.SH_CKE.ImageCaption, + globalThis.SH_CKE.ImageStyle, + globalThis.SH_CKE.ImageToolbar, + globalThis.SH_CKE.ImageUpload, + globalThis.SH_CKE.Indent, + globalThis.SH_CKE.Link, + globalThis.SH_CKE.List, + globalThis.SH_CKE.Paragraph, + globalThis.SH_CKE.PasteFromOffice, + globalThis.SH_CKE.Table, + globalThis.SH_CKE.TableToolbar, + globalThis.SH_CKE.TableColumnResize, + globalThis.SH_CKE.TableCaption, + globalThis.SH_CKE.StrapiMediaLib, + globalThis.SH_CKE.StrapiUploadAdapter, + ], + toolbar: [ + 'heading', + '|', + 'bold', + 'italic', + 'link', + 'bulletedList', + 'numberedList', + '|', + 'strapiMediaLib', + 'insertTable', + '|', + 'undo', + 'redo', + ], + heading: { + options: [ + { model: 'paragraph', title: 'Paragraph', class: 'ck-heading_paragraph' }, + { model: 'heading1', view: 'h1', title: 'Heading 1', class: 'ck-heading_heading1' }, + { model: 'heading2', view: 'h2', title: 'Heading 2', class: 'ck-heading_heading2' }, + { model: 'heading3', view: 'h3', title: 'Heading 3', class: 'ck-heading_heading3' }, + { model: 'heading4', view: 'h4', title: 'Heading 4', class: 'ck-heading_heading4' }, + ], + }, + image: { + toolbar: [ + 'imageStyle:inline', + 'imageStyle:block', + 'imageStyle:side', + '|', + 'toggleImageCaption', + 'imageTextAlternative', + ], + }, + table: { + contentToolbar: ['tableColumn', 'tableRow', 'mergeTableCells', '|', 'toggleTableCaption'], + }, + }, + }, + }, +}); +``` + +
+ +## From v1 to v2 + +- You will need to update Strapi to version v4.4.x for plugin v2.0.x, or to v4.13.0+ for v2.1.x. + +- Starting with v2, the plugin uses the Custom Field API, so you'll need to manually update your schema. + +- The plugin configuration should be defined in /config/ckeditor.txt from v2 onward. [Please refer to the v2 configuration guide for details.](https://github.com/nshenderov/strapi-plugin-ckeditor/blob/e782475f54b8a50a04f55275c89ef5bf61a15745/README.md?plain=1#L54) diff --git a/README.md b/README.md index 1aeed46..0694ecc 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,41 @@

- +

-

CKEditor 5 for Strapi

+

Strapi ➕ CKEditor

-

Integrates CKEditor 5 into your Strapi project as a fully customizable custom field. (Unofficial integration)

+

Integrates CKEditor 5 into your Strapi project as a fully customizable custom field. (Community edition)

## 👋 Get Started -* [Features](#features) -* [Installation](#installation) -* [Usage](#usage) -* [Configuration](#configuration) -* [Contributing](#contributing) -* [Migration](#migration) -* [Requirements](#requirements) +- [Features](#features) +- [Installation](#installation) +- [Usage](#usage) +- [Configuration](#configuration) +- [Contributing](#contributing) +- [Migration](#migration) +- [Requirements](#requirements) ## ✨ Features -* **Media library integration** -* **Responsive images support** -* **Strapi theme switching support** -* **Supports custom styles for the editor** -* **Supports i18n and different UI translations** -* **Allows custom editor configrations** -* **Plugins extensibility** +- **Extensive configuration** +- **Media Library integration** +- **Dark theme support** +- **Fullscreen expansion** +- **Responsive images support** +- **Automatic UI translations** +- **i18n support** +- **Self-Hosted**

- Buy Me A Coffee + Buy Me A Coffee

- ## 🔧 Installation +**Strapi v5:** Add the package to your Strapi application: @@ -42,683 +43,693 @@ Add the package to your Strapi application: yarn add @_sh/strapi-plugin-ckeditor ``` -For Strapi v4: -```bash -yarn add @_sh/strapi-plugin-ckeditor@strapi-v4-latest -``` - Then, build the app: ```bash yarn build ``` +**Strapi v4:** + +Version 3 of the plugin will remain available for Strapi v4 until March 2026. +Refer to the [**v3 installation guide**](https://github.com/nshenderov/strapi-plugin-ckeditor/blob/3.x.x/README.md#installation) +for setup instructions. + +> Note: Version 3 receives only essential bug fixes. The configuration process and available +> features in version 3 differ significantly from the latest plugin versions. + ## ✍️ Usage +The field can be fould in the Content-Type Builder under the `CUSTOM` tab: +

- +

+**Presets** + +A preset is a set of settings that define the editor's features and appearence. You +can specify a dedicated preset for each field. The available presets can be customized through the +[configuration](#configuration). + +

+ +

+ +The plugin provides two presets out of the box: `Default HTML editor` and `Default Markdown editor`, +each has different output format: HTML and Markdown, respectively. Both presets include +an extensive set of non-premium CKEditor plugins. +
- Default preset: - + Default HTML editor: + +

+ +

+ +

+ +

+

- +

-## ⚙️ Configuration +
+ Default Markdown editor: +

+ +

-> It is highly recommended to explore [**the official CKEditor5 documentation**](https://ckeditor.com/docs/ckeditor5/latest/features/index.html) and [**the Strapi Custom Field API**](https://docs.strapi.io/developer-docs/latest/development/custom-fields.html#registering-a-custom-field-on-the-server) -> -> To display content from external sources, such as images or videos, in your admin panel, you need to configure your `middlewares.js` file. [**Check the official documentation for details.**](https://docs.strapi.io/dev-docs/configurations/middlewares#security) +

+ +

-The plugin configuration should be defined in `your-app/config/ckeditor.js` or in `ckeditor.ts` if you are using TypeScript in your project. +
-By default, the plugin provides one default preset that can be modified in the config file, and you can expand the preset set as needed. +**Responsive images** -The plugin exposes all default CKEditor packages to the global variable `SH_CKE`. +The plugin generates `srcset` attribute for inserted images if the image has any `formats` other than `thumbnail`. -The config file must define a function called `CKEConfig` that returns the configuration object. +**UI language**: If you don't specify the UI language in the editor configuration, the plugin will +default to the Strapi admin's preferred language. If no preference is set, English will be used as a fallback. -The structure of the configuration object is as follows: +**Content language**: i18n for the editor's content language can be enabled by checking the +`Enable localization for this field` option in the Advanced Settings tab. -```js -{ - dontMergePresets:bool, - dontMergeTheme:bool, - presets:{ - default:{ - field:{...}, - styles:string, - editorConfig:{...}, - }, - ... - }, - theme:{ - common:string, - light:string, - dark:string, - additional:string, - } -} -``` +## ⚙️ Configuration -Every preset in the `presets` object contains three properties: +The plugin configuration must be defined on the front-end. The plugin provides a `setPluginConfig` +function, which accepts a plugin configuration (`PluginConfig`) that can include an array of presets +and a theme object: -1. `field (object)` Registers this configuration version in the Admin panel. -2. `styles (string)` Styles applied to the editor in this version. -3. `editorConfig (object)` The CKEditor configuration. +```ts +import { setPluginConfig } from '@_sh/strapi-plugin-ckeditor'; -The `theme` object is used to modify the default global theme of the plugin. -It contains four properties: +function setPluginConfig(pluginConfig: { + presets: Preset[] | undefined; + theme: Theme | undefined; +}): void; +``` -1. `common (string)` Styles applied to the editor to ensure proper appearance of the input. -2. `light (string)` Styles applied to the editor when Strapi is in light mode. -3. `dark (string)` Styles applied to the editor when Strapi is in dark mode. -4. `additional (string)` Additional styles to further enhance the editors appearance. +**Key points:** -By default, everything defined in the user’s configuration is merged with the -default configuration object. These two properties allow you to prevent -this behavior: +- The function must be invoked before the admin panel's bootstrap lifecycle function. The general recommendation is to call it inside the admin panel's register lifecycle function. +- Provided properties will overwrite the default configuration values. +- The configuration becomes immutable after the first invocation, preventing further modifications. -1. `dontMergePresets (bool)` -2. `dontMergeTheme (bool)` +**Plugin configuration object** includes optional `presets` and `theme` properties: -Since Strapi uses i18n for translation, the `ignorei18n` property is added to the -`editorConfig.language` object. This property allows you to manually set the -content language, bypassing i18n. The language object looks like this: +```ts +type PluginConfig = { + /** + * Presets are sets of settings that define the editor's features and appearance. + */ + presets?: Preset[]; + /** + * Styles applied globally to every editor instance. + */ + theme?: Theme; +}; +``` -```js -language:{ - ignorei18n (bool), - ui (string), - content (string) -} +`presets` is an array of objects of type `Preset`. Each preset includes the following properties: + +```ts +type Preset = { + /** + * Preset name, displayed in the schema. + */ + name: string; + /** + * Preset description, displayed in the Content-Type Builder. + */ + description: string; + /** + * CKEditor configuration object. + * + * @see {@link https://ckeditor.com/docs/ckeditor5/latest/getting-started/setup/configuration.html | CKEditor documentation} + */ + editorConfig: EditorConfig; + /** + * Additional styles applied to the editor instance after the theme styles. + */ + styles?: EditorStyles; +}; ``` -The language determination follows this logic: -- Plugin UI language: -`language.ui || strapi admin preferred language || 'en'` +`theme` object defines a plugin CSS styles applied to every editor instance. It includes the +following properties: + +```ts +/** + * The `common` styles are applied first, followed by `light` or `dark` styles + * according to the preferences, and finally `additional` styles. + */ +type Theme = { + /** + * Core styles. + */ + common?: EditorStyles; + /** + * Styles apllied in light mode. + */ + light?: EditorStyles; + /** + * Styles apllied in dark mode. + */ + dark?: EditorStyles; + /** + * Additional styles that complement the theme. + */ + additional?: EditorStyles; +}; +``` -- Content language: -`(ignorei18n ? language.content : i18n) || language.ui` +```ts +/** + * Represents styles that can be applied to an editor instance. + * Can be a plain CSS string or an array of interpolations for dynamic styling. + */ +export type EditorStyles = string | Interpolation[]; +``` -### Configuration examples +**Default presets and theme** -Adding a new preset: +To simplify the process of defining a new preset, the plugin exports default presets and +a default theme, which can be used as a base in custom configurations: -
- ckeditor.js +```ts +import { + defaultTheme, + defaultHtmlPreset, + defaultMarkdownPreset, +} from '@_sh/strapi-plugin-ckeditor'; +``` -```js -const CKEConfig = () => ({ - presets: { - markdown:{ - field: { - key: "markdown", - value: "markdown", - metadatas: { - intlLabel: { - id: "ckeditor.preset.markdown.label", - defaultMessage: "Markdown output", - }, - }, - }, - editorConfig:{ - placeholder: 'Markdown editor', - plugins: [ - globalThis.SH_CKE.Essentials, - globalThis.SH_CKE.Autoformat, - globalThis.SH_CKE.BlockQuote, - globalThis.SH_CKE.Bold, - globalThis.SH_CKE.Heading, - globalThis.SH_CKE.Image, - globalThis.SH_CKE.ImageCaption, - globalThis.SH_CKE.ImageStyle, - globalThis.SH_CKE.ImageToolbar, - globalThis.SH_CKE.ImageUpload, - globalThis.SH_CKE.Indent, - globalThis.SH_CKE.Italic, - globalThis.SH_CKE.Link, - globalThis.SH_CKE.List, - globalThis.SH_CKE.MediaEmbed, - globalThis.SH_CKE.Paragraph, - globalThis.SH_CKE.Table, - globalThis.SH_CKE.TableToolbar, - globalThis.SH_CKE.SourceEditing, - globalThis.SH_CKE.StrapiMediaLib, - globalThis.SH_CKE.StrapiUploadAdapter, - globalThis.SH_CKE.Markdown, - globalThis.SH_CKE.Code, - globalThis.SH_CKE.CodeBlock, - globalThis.SH_CKE.TodoList, - globalThis.SH_CKE.Strikethrough, - globalThis.SH_CKE.HorizontalLine - ], - toolbar: { - items: [ - 'heading', - '|', - 'bold', - 'italic', - 'strikethrough', - 'link', - '|', - 'bulletedList', - 'numberedList', - 'todoList', - '|', - 'code', - 'codeBlock', - '|', - 'uploadImage', - 'strapiMediaLib', - 'blockQuote', - 'horizontalLine', - '-', - 'sourceEditing', - '|', - 'outdent', - 'indent', - '|', - 'undo', - 'redo' - ], - shouldNotGroupWhenFull: true - }, - image: { - toolbar: [ 'imageStyle:inline', 'imageStyle:block', 'imageStyle:side', '|', 'toggleImageCaption', 'imageTextAlternative' ] - }, - codeBlock: { - languages: [ - { language: 'css', label: 'CSS' }, - { language: 'html', label: 'HTML' }, - { language: 'javascript', label: 'JavaScript' }, - { language: 'php', label: 'PHP' } - ] - }, - } - }, - } -}) +**Integration with Strapi's Media Library** + +To integrate CKEditor with the Strapi's Media Library, the plugin provides two CKEditor plugins +that can be included in your presets without additional configuration: + +```ts +import { StrapiMediaLib, StrapiUploadAdapter } from '@_sh/strapi-plugin-ckeditor'; ``` -
+**Available type definitions** ---- +The following type definitions are available for use: -Changing toolbar buttons, modifying the plugin list, adding styles to the default preset: +```ts +import type { + PluginConfig, + Preset, + EditorConfig, + Theme, + EditorStyles, +} from '@_sh/strapi-plugin-ckeditor'; +```
- ckeditor.js - -```js -const CKEConfig = () => ({ - presets:{ - default:{ - styles:` - --ck-focus-ring:3px dashed #5CB176; - - .ck.ck-content.ck-editor__editable { - &.ck-focused:not(.ck-editor__nested-editable) { - border: var(--ck-focus-ring) !important; - } - } - .ck.ck-content.ck-editor__editable.ck-rounded-corners.ck-editor__editable_inline.ck-blurred{ - min-height: 400px; - max-height: 400px; - } - .ck.ck-content.ck-editor__editable.ck-rounded-corners.ck-editor__editable_inline.ck-focused{ - min-height: 400px; - max-height: 1700px; - } - `, - editorConfig:{ - plugins: [ - globalThis.SH_CKE.Bold, - globalThis.SH_CKE.Italic, - globalThis.SH_CKE.Essentials, - globalThis.SH_CKE.Heading, - globalThis.SH_CKE.Image, - globalThis.SH_CKE.ImageCaption, - globalThis.SH_CKE.ImageStyle, - globalThis.SH_CKE.ImageToolbar, - globalThis.SH_CKE.Link, - globalThis.SH_CKE.List, - globalThis.SH_CKE.Paragraph, - globalThis.SH_CKE.StrapiMediaLib, - globalThis.SH_CKE.StrapiUploadAdapter, - ], - toolbar: [ - 'heading', - '|', - 'bold', 'italic', 'link', 'bulletedList', 'numberedList', - '|', - 'strapiMediaLib', 'insertTable', - '|', - 'undo', 'redo' - ] - } - } - } -}) + Type definitions + +```ts +/** + * Plugin configuration object. + */ +export type PluginConfig = { + /** + * Presets are sets of settings that define the editor's features and appearance. + */ + presets?: Preset[]; + /** + * Styles applied globally to every editor instance. + */ + theme?: Theme; +}; ``` -
+```ts +/** + * Preset is a set of settings that define the editor's features and appearance. + */ +export type Preset = { + /** + * Preset name, displayed in the schema. + */ + name: string; + /** + * Preset description, displayed in the Content-Type Builder. + */ + description: string; + /** + * Additional styles applied to the editor instance after the theme styles. + */ + styles?: EditorStyles; + /** + * CKEditor configuration object. + * + * @see {@link https://ckeditor.com/docs/ckeditor5/latest/getting-started/setup/configuration.html | CKEditor documentation} + */ + editorConfig: EditorConfig; +}; +``` ---- +```ts +/** + * CKEditor configuration object. + * + * @see {@link https://ckeditor.com/docs/ckeditor5/latest/getting-started/setup/configuration.html | CKEditor documentation} + */ +export type EditorConfig = CKE5EditorConfig; +``` -> 📂 Default preset: [**admin/src/components/Input/presets/default.js**](https://github.com/nshenderov/strapi-plugin-ckeditor/blob/master/admin/src/components/Input/presets/default.js) -> -> 📂 Default theme: [**admin/src/components/Input/theme**](https://github.com/nshenderov/strapi-plugin-ckeditor/blob/master/admin/src/components/Input/theme) +```ts +/** + * Styles applied globally to every editor instance. + * + * @remarks + * + * The `common` styles are applied first, followed by `light` or `dark` styles + * according to the preferences, and finally `additional` styles. + */ +export type Theme = { + /** + * Core styles. + */ + common?: EditorStyles; + /** + * Styles apllied in light mode. + */ + light?: EditorStyles; + /** + * Styles apllied in dark mode. + */ + dark?: EditorStyles; + /** + * Additional styles that complement the theme. + */ + additional?: EditorStyles; +}; +``` -### Adding plugins +```ts +/** + * Represents styles that can be applied to an editor instance. + * Can be a plain CSS string or an array of interpolations for dynamic styling. + */ +export type EditorStyles = string | Interpolation[]; +``` -Your plugin must be available in the `global` object. + -**Example of adding the Timestamp plugin:** +### Configuration examples: -1. Create the plugin: +
+ Adding a new preset [JS] ```js -// your-app/src/admin/timestamp.js - -class Timestamp extends globalThis.SH_CKE.Plugin { - init() { - const editor = this.editor; - // The button must be registered among the UI components of the editor - // to be displayed in the toolbar. - editor.ui.componentFactory.add( 'timestamp', () => { - // The button will be an instance of ButtonView. - const button = new globalThis.SH_CKE.ButtonView(); - - button.set( { - label: 'Timestamp', - withText: true - } ); - - // Execute a callback function when the button is clicked. - button.on( 'execute', () => { - const now = new Date(); - - // Change the model using the model writer. - editor.model.change( writer => { - - // Insert the text at the user's current position. - editor.model.insertContent( writer.createText( now.toString() ) ); - } ); - } ); - - return button; - } ); - } -} - -globalThis.Timestamp = Timestamp; -``` +// src/admin/app.js + +import { + Bold, + Italic, + Essentials, + Heading, + Image, + ImageCaption, + ImageStyle, + ImageToolbar, + ImageUpload, + Link, + List, + Paragraph, +} from 'ckeditor5'; + +import { setPluginConfig, StrapiMediaLib, StrapiUploadAdapter } from '@_sh/strapi-plugin-ckeditor'; + +const myCustomPreset = { + name: 'myCustomPreset', + description: 'myCustomPreset description', + editorConfig: { + licenseKey: 'GPL', + plugins: [ + Bold, + Italic, + Essentials, + Heading, + Image, + ImageCaption, + ImageStyle, + ImageToolbar, + ImageUpload, + Link, + List, + Paragraph, + StrapiMediaLib, + StrapiUploadAdapter, + ], + toolbar: [ + 'heading', + '|', + 'bold', + 'italic', + 'link', + 'bulletedList', + 'numberedList', + '|', + 'strapiMediaLib', + '|', + 'undo', + 'redo', + ], + }, +}; -2. Import the plugin: +const myConfig = { + /** + * Note: Since the provided `presets` include only `myCustomPreset` + * this configuration will overwrite default presets. + */ + presets: [myCustomPreset], +}; -```js -// your-app/src/admin/app.js +export default { + register() { + setPluginConfig(myConfig); + }, +}; +``` -import './timestamp.js' +
-const config = {}; +
+ Adding a new preset [TS] + +```ts +// src/admin/app.tsx + +import { + Bold, + Italic, + Essentials, + Heading, + Image, + ImageCaption, + ImageStyle, + ImageToolbar, + ImageUpload, + Link, + List, + Paragraph, +} from 'ckeditor5'; + +import { + type PluginConfig, + type Preset, + setPluginConfig, + StrapiMediaLib, + StrapiUploadAdapter, +} from '@_sh/strapi-plugin-ckeditor'; + +const myCustomPreset: Preset = { + name: 'myCustomPreset', + description: 'myCustomPreset description', + editorConfig: { + licenseKey: 'GPL', + plugins: [ + Bold, + Italic, + Essentials, + Heading, + Image, + ImageCaption, + ImageStyle, + ImageToolbar, + ImageUpload, + Link, + List, + Paragraph, + StrapiMediaLib, + StrapiUploadAdapter, + ], + toolbar: [ + 'heading', + '|', + 'bold', + 'italic', + 'link', + 'bulletedList', + 'numberedList', + '|', + 'strapiMediaLib', + '|', + 'undo', + 'redo', + ], + }, +}; -const bootstrap = (app) => {}; +const myConfig: PluginConfig = { + /** + * Note that since provided `presets` includes only `myCustomPreset` + * this configuration will overwrite default presets. + */ + presets: [myCustomPreset], +}; export default { - config, - bootstrap, + register() { + setPluginConfig(myConfig); + }, }; - ``` -3. Add the new plugin to your preset: +
- ckeditor.js + Default presets modification [TS] + +```ts +// src/admin/app.tsx + +import { css } from 'styled-components'; + +import { + type PluginConfig, + type Preset, + setPluginConfig, + defaultHtmlPreset, + defaultMarkdownPreset, +} from '@_sh/strapi-plugin-ckeditor'; + +const defaultHtml: Preset = { + ...defaultHtmlPreset, + description: 'Modified default HTML editor', + styles: ` + .ck { + color: red; + } + `, + editorConfig: { + ...defaultHtmlPreset.editorConfig, + placeholder: 'Modified default HTML editor', + toolbar: [ + 'heading', + '|', + 'bold', + 'italic', + 'link', + 'bulletedList', + 'numberedList', + '|', + 'strapiMediaLib', + 'insertTable', + '|', + 'undo', + 'redo', + ], + }, +}; -```js -// your-app/config/ckeditor.js - -const CKEConfig = () => ({ - presets: { - myCustomPreset:{ - field: { - key: "myCustomPreset", - value: "myCustomPreset", - metadatas: { - intlLabel: { - id: "ckeditor5.preset.myCustomPreset.label", - defaultMessage: "My custom preset", - }, - }, - }, - editorConfig:{ - plugins: [ - globalThis.Timestamp, - globalThis.SH_CKE.Autoformat, - globalThis.SH_CKE.Bold, - globalThis.SH_CKE.Italic, - globalThis.SH_CKE.Essentials, - globalThis.SH_CKE.Heading, - globalThis.SH_CKE.Image, - globalThis.SH_CKE.ImageCaption, - globalThis.SH_CKE.ImageStyle, - globalThis.SH_CKE.ImageToolbar, - globalThis.SH_CKE.ImageUpload, - globalThis.SH_CKE.Indent, - globalThis.SH_CKE.Link, - globalThis.SH_CKE.List, - globalThis.SH_CKE.Paragraph, - globalThis.SH_CKE.PasteFromOffice, - globalThis.SH_CKE.Table, - globalThis.SH_CKE.TableToolbar, - globalThis.SH_CKE.TableColumnResize, - globalThis.SH_CKE.TableCaption, - globalThis.SH_CKE.StrapiMediaLib, - globalThis.SH_CKE.StrapiUploadAdapter, - ], - toolbar: [ - 'timestamp', - 'heading', - '|', - 'bold', 'italic', 'link', 'bulletedList', 'numberedList', - '|', - 'strapiMediaLib', 'insertTable', - '|', - 'undo', 'redo' - ], - - // ... - } - }, - } -}) +const defaultMarkdown: Preset = { + ...defaultMarkdownPreset, + description: 'Modified default Markdown editor', + styles: ` + .ck { + --ck-editor-max-width: 1500px; + --ck-editor-min-height: 700px; + --ck-editor-max-height: 700px; + } + + .ck.ck-editor__main { + border: 3px dashed ${({ theme }) => theme.colors.warning500}; + } + `, +}; + +const myConfig: PluginConfig = { + presets: [defaultHtml, defaultMarkdown], +}; + +export default { + register() { + setPluginConfig(myConfig); + }, +}; ``` -
+ +
+ Modifying theme [TS] + +```ts +// src/admin/app.tsx + +import { css } from 'styled-components'; + +import { type PluginConfig, setPluginConfig, defaultTheme } from '@_sh/strapi-plugin-ckeditor'; + +const myConfig: PluginConfig = { + theme: { + ...defaultTheme, + additional: css` + .ck { + --ck-editor-max-width: 1500px; + --ck-editor-min-height: 700px; + --ck-editor-max-height: 700px; + } + + .ck.ck-editor__main { + border: 3px dashed ${({ theme }) => theme.colors.warning500}; + } + `, + }, +}; -4. Then rebuild the application: -```bash -yarn build +export default { + register() { + setPluginConfig(myConfig); + }, +}; ``` -**💡 Alternatively, you can define your plugin like this:** +
- ckeditor.js + Adding Timestamp plugin [JS] -```js -const CKEConfig = () => { - class Timestamp extends globalThis.SH_CKE.Plugin { - init() { - const editor = this.editor; - // The button must be registered among the UI components of the editor - // to be displayed in the toolbar. - editor.ui.componentFactory.add("timestamp", () => { - // The button will be an instance of ButtonView. - const button = new globalThis.SH_CKE.ButtonView(); - - button.set({ - label: "Timestamp", - withText: true, - }); +```ts +// src/admin/app.js - // Execute a callback function when the button is clicked. - button.on("execute", () => { - const now = new Date(); +import { Plugin, ButtonView } from 'ckeditor5'; - // Change the model using the model writer. - editor.model.change((writer) => { - // Insert the text at the user's current position. - editor.model.insertContent(writer.createText(now.toString())); - }); - }); +import { setPluginConfig, defaultHtmlPreset } from '@_sh/strapi-plugin-ckeditor'; - return button; +class Timestamp extends Plugin { + init() { + const editor = this.editor; + editor.ui.componentFactory.add('timestamp', () => { + const button = new ButtonView(); + button.set({ + label: 'timestamp', + withText: true, }); - } - } - - return { - presets: { - myCustomPreset:{ - field: { - key: "myCustomPreset", - value: "myCustomPreset", - metadatas: { - intlLabel: { - id: "ckeditor5.preset.myCustomPreset.label", - defaultMessage: "My custom preset", - }, - }, - }, - editorConfig:{ - plugins: [ - Timestamp, - globalThis.SH_CKE.Autoformat, - globalThis.SH_CKE.Bold, - globalThis.SH_CKE.Italic, - globalThis.SH_CKE.Essentials, - globalThis.SH_CKE.Heading, - globalThis.SH_CKE.Image, - globalThis.SH_CKE.ImageCaption, - globalThis.SH_CKE.ImageStyle, - globalThis.SH_CKE.ImageToolbar, - globalThis.SH_CKE.ImageUpload, - globalThis.SH_CKE.Indent, - globalThis.SH_CKE.Link, - globalThis.SH_CKE.List, - globalThis.SH_CKE.Paragraph, - globalThis.SH_CKE.PasteFromOffice, - globalThis.SH_CKE.Table, - globalThis.SH_CKE.TableToolbar, - globalThis.SH_CKE.TableColumnResize, - globalThis.SH_CKE.TableCaption, - globalThis.SH_CKE.StrapiMediaLib, - globalThis.SH_CKE.StrapiUploadAdapter, - ], - toolbar: [ - 'timestamp', - 'heading', - '|', - 'bold', 'italic', 'link', 'bulletedList', 'numberedList', - '|', - 'strapiMediaLib', 'insertTable', - '|', - 'undo', 'redo' - ], - - ... - } - }, + button.on('execute', () => { + const now = new Date(); + editor.model.change(writer => { + editor.model.insertContent(writer.createText(now.toString())); + }); + }); + return button; + }); } } + +export default { + register() { + defaultHtmlPreset.editorConfig.plugins.push(Timestamp); + defaultHtmlPreset.editorConfig.toolbar.unshift('timestamp'); + setPluginConfig({ presets: [defaultHtmlPreset] }); + }, +}; ``` -
+ +
+📂 Default HTML preset: [**admin/src/config/htmlPreset.js**](https://github.com/nshenderov/strapi-plugin-ckeditor/blob/master/admin/src/config/htmlPreset.js) -## 🛠 Contributing +📂 Default Markdown preset: [**admin/src/config/markdownPreset.js**](https://github.com/nshenderov/strapi-plugin-ckeditor/blob/master/admin/src/config/markdownPreset.js) +📂 Default theme: [**admin/src/theme**](https://github.com/nshenderov/strapi-plugin-ckeditor/blob/master/admin/src/theme) -This section explains how to set up your environment if you want to contribute to this package. +> 📌 It is highly recommended to explore [**the official CKEditor5 documentation**](https://ckeditor.com/docs/ckeditor5/latest/getting-started/setup/configuration.html). -[Strapi Plugin SDK docs](https://docs.strapi.io/dev-docs/plugins/development/create-a-plugin#linking-the-plugin-to-your-project) +> 💡 To display content from external sources, such as images or videos, in your admin panel, +> you need to configure your `middlewares.js` file. +> [**Check the documentation for details.**](https://docs.strapi.io/dev-docs/configurations/middlewares#security) -Clone the repository: +## 🛠 Contributing -```bash -git clone https://github.com/nshenderov/strapi-plugin-ckeditor.git ./strapi-plugin-ckeditor -``` -Navigate to the downloaded folder: +Feel free to [fork the repository](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo) +and open a pull request, any help is appreciated. -```bash -cd ./strapi-plugin-ckeditor -``` +Follow these steps to set up a plugin development environment: + +1. Clone the repository. + +2. Read [the Strapi Plugin SDK documentation](https://docs.strapi.io/dev-docs/plugins/development/create-a-plugin#linking-the-plugin-to-your-project). -Install dependencies: +3. Navigate to the cloned plugin folder and install dependencies, run: ```bash yarn install ``` -Link the plugin to your project: +4. Link the plugin to your project: -In the plugin directory, run: -```bash -yarn watch:link -``` + - In the plugin folder, run: -Open new terminal window and in your project directory, run: -```bash -yarn dlx yalc add --link @_sh/strapi-plugin-ckeditor && yarn install -``` + ```bash + yarn watch:link + ``` + + - Open a new terminal, navigate to your Strapi project directory, and run: -Rebuild the project and start the server: + ```bash + yarn dlx yalc add --link @_sh/strapi-plugin-ckeditor && yarn install + ``` + +5. Rebuild the project and start the server: ```bash yarn build yarn develop ``` - ## ✈️ Migration -### From v3 to v4 - -- The new version introduces support for Strapi v5 and is incompatible with Strapi v4. You will need to update your Strapi project to version 5 before upgrading. - -- The plugin development process has changed. Please refer to the updated contribution guide for more information. - -### From v2 to v3 - -- The default editor configurations (toolbar, toolbarBalloon, blockBalloon) have been removed and now there is only one preset by default. You will need to update your fields in the Content-Type Builder. - -- Config file extension has changed from `.txt` to `.js` or `.ts` -- Configuration object properties have been renamed: - - `configsOverwrite` -> `dontMergePresets` - - `themeOverwrite` -> `dontMergeTheme` - - `configs` -> `presets` -- From v3 instead of globalThis.CKEditorConfig = {..}, the config file must define a function called CKEConfig that returns the configuration object. - -Example of the new configuration file: - -
- ckeditor.js - -```js -const CKEConfig = () => ({ - presets:{ - myCustomPreset:{ - field: { - key: "myCustomPreset", - value: "myCustomPreset", - metadatas: { - intlLabel: { - id: "ckeditor5.preset.myCustomPreset.label", - defaultMessage: "My custom preset", - }, - }, - }, - editorConfig:{ - plugins: [ - globalThis.SH_CKE.Autoformat, - globalThis.SH_CKE.Bold, - globalThis.SH_CKE.Italic, - globalThis.SH_CKE.Essentials, - globalThis.SH_CKE.Heading, - globalThis.SH_CKE.Image, - globalThis.SH_CKE.ImageCaption, - globalThis.SH_CKE.ImageStyle, - globalThis.SH_CKE.ImageToolbar, - globalThis.SH_CKE.ImageUpload, - globalThis.SH_CKE.Indent, - globalThis.SH_CKE.Link, - globalThis.SH_CKE.List, - globalThis.SH_CKE.Paragraph, - globalThis.SH_CKE.PasteFromOffice, - globalThis.SH_CKE.Table, - globalThis.SH_CKE.TableToolbar, - globalThis.SH_CKE.TableColumnResize, - globalThis.SH_CKE.TableCaption, - globalThis.SH_CKE.StrapiMediaLib, - globalThis.SH_CKE.StrapiUploadAdapter, - ], - toolbar: [ - 'heading', - '|', - 'bold', 'italic', 'link', 'bulletedList', 'numberedList', - '|', - 'strapiMediaLib', 'insertTable', - '|', - 'undo', 'redo' - ], - heading: { - options: [ - { model: 'paragraph', title: 'Paragraph', class: 'ck-heading_paragraph' }, - { model: 'heading1', view: 'h1', title: 'Heading 1', class: 'ck-heading_heading1' }, - { model: 'heading2', view: 'h2', title: 'Heading 2', class: 'ck-heading_heading2' }, - { model: 'heading3', view: 'h3', title: 'Heading 3', class: 'ck-heading_heading3' }, - { model: 'heading4', view: 'h4', title: 'Heading 4', class: 'ck-heading_heading4' }, - ] - }, - image: { - toolbar: [ - 'imageStyle:inline', - 'imageStyle:block', - 'imageStyle:side', - '|', - 'toggleImageCaption', - 'imageTextAlternative' - ] - }, - table: { - contentToolbar: [ - 'tableColumn', - 'tableRow', - 'mergeTableCells', - '|', - 'toggleTableCaption' - ] - } - } - } - } -}) -``` - -
- -### From v1 to v2 - -- You will need to update Strapi to version v4.4.x for plugin v2.0.x, or to v4.13.0+ for v2.1.x. - -- Starting with v2, the plugin uses the Custom Field API, so you'll need to manually update your schema. - -- The plugin configuration should be defined in /config/ckeditor.txt from v2 onward. [Please refer to the v2 configuration guide for details.](https://github.com/nshenderov/strapi-plugin-ckeditor/blob/e782475f54b8a50a04f55275c89ef5bf61a15745/README.md?plain=1#L54) +To upgrade to the latest version, follow the dedicated [migration guide](https://github.com/nshenderov/strapi-plugin-ckeditor/blob/master/MIGRATION.md) +for detailed instructions. ## ⚠️ Requirements - -**v4.x.x** +**v5.x.x** Strapi **>= 5.0.0** -Node **>= 18.0.0 <= 20.x.x** +Node **>= 18.0.0 <= 22.x.x** -___ +--- **v3.x.x** Strapi **>= 4.13.0 < 5.0.0** -Node **>= 18.0.0 <= 20.x.x** \ No newline at end of file +Node **>= 18.0.0 <= 20.x.x** diff --git a/assets/v5-def-html-dark.png b/assets/v5-def-html-dark.png new file mode 100644 index 0000000..b4317dc Binary files /dev/null and b/assets/v5-def-html-dark.png differ diff --git a/assets/v5-def-html-light-full.png b/assets/v5-def-html-light-full.png new file mode 100644 index 0000000..675eb42 Binary files /dev/null and b/assets/v5-def-html-light-full.png differ diff --git a/assets/v5-def-html-light.png b/assets/v5-def-html-light.png new file mode 100644 index 0000000..bfea5f6 Binary files /dev/null and b/assets/v5-def-html-light.png differ diff --git a/assets/v5-def-mrk-dark.png b/assets/v5-def-mrk-dark.png new file mode 100644 index 0000000..9cb33e8 Binary files /dev/null and b/assets/v5-def-mrk-dark.png differ diff --git a/assets/v5-def-mrk-light.png b/assets/v5-def-mrk-light.png new file mode 100644 index 0000000..dd5b966 Binary files /dev/null and b/assets/v5-def-mrk-light.png differ diff --git a/assets/v5-head.png b/assets/v5-head.png new file mode 100644 index 0000000..d877857 Binary files /dev/null and b/assets/v5-head.png differ diff --git a/assets/v5-usage-guide-1.png b/assets/v5-usage-guide-1.png new file mode 100644 index 0000000..8a98277 Binary files /dev/null and b/assets/v5-usage-guide-1.png differ diff --git a/assets/v5-usage-guide-2.png b/assets/v5-usage-guide-2.png new file mode 100644 index 0000000..c8b60e5 Binary files /dev/null and b/assets/v5-usage-guide-2.png differ diff --git a/assets/v5-usage-guide-3.png b/assets/v5-usage-guide-3.png new file mode 100644 index 0000000..2177ac3 Binary files /dev/null and b/assets/v5-usage-guide-3.png differ