diff --git a/app/package.json b/app/package.json index 0cdfe1121d..ff0a83ee39 100644 --- a/app/package.json +++ b/app/package.json @@ -32,6 +32,7 @@ "@coremedia/ckeditor5-dom-converter": "26.0.1-rc.0", "@coremedia/ckeditor5-font-mapper": "26.0.1-rc.0", "@coremedia/ckeditor5-link-common": "26.0.1-rc.0", + "@coremedia/ckeditor5-text-direction": "workspace:*", "@coremedia/service-agent": "^2.1.2", "ckeditor5": "46.1.1", "xml-formatter": "^3.6.2" diff --git a/app/src/editors/richtext.ts b/app/src/editors/richtext.ts index 98d815df64..a8b502b4c3 100644 --- a/app/src/editors/richtext.ts +++ b/app/src/editors/richtext.ts @@ -2,29 +2,47 @@ // ImageBlockEditing: See ckeditor/ckeditor5#12027. -import { DialogVisibility } from "@coremedia/ckeditor5-dialog-visibility"; +import { Blocklist } from "@coremedia/ckeditor5-coremedia-blocklist"; +import { ContentClipboard, PasteContentPlugin } from "@coremedia/ckeditor5-coremedia-content-clipboard"; +import { Differencing } from "@coremedia/ckeditor5-coremedia-differencing"; +import { ContentImagePlugin } from "@coremedia/ckeditor5-coremedia-images"; import { ContentLinks, COREMEDIA_CONTEXT_KEY, COREMEDIA_LINK_CONFIG_KEY, LinkTarget, } from "@coremedia/ckeditor5-coremedia-link"; -import { ContentClipboard, PasteContentPlugin } from "@coremedia/ckeditor5-coremedia-content-clipboard"; -import { ContentImagePlugin } from "@coremedia/ckeditor5-coremedia-images"; -import { FontMapper as CoreMediaFontMapper } from "@coremedia/ckeditor5-font-mapper"; +import type { + LatestCoreMediaRichTextConfig, + V10CoreMediaRichTextConfig, +} from "@coremedia/ckeditor5-coremedia-richtext"; import { - COREMEDIA_MOCK_CONTENT_PLUGIN, - MockInputExamplePlugin, - MockStudioIntegration, -} from "@coremedia/ckeditor5-coremedia-studio-integration-mock"; + replaceByElementAndClassBackAndForth, + replaceElementByElementAndClass, + stripFixedAttributes, +} from "@coremedia/ckeditor5-coremedia-richtext"; +import "ckeditor5/ckeditor5.css"; import { COREMEDIA_RICHTEXT_CONFIG_KEY, COREMEDIA_RICHTEXT_SUPPORT_CONFIG_KEY, CoreMediaStudioEssentials, Strictness, } from "@coremedia/ckeditor5-coremedia-studio-essentials"; -import type { PluginConstructor } from "ckeditor5"; import { + COREMEDIA_MOCK_CONTENT_PLUGIN, + MockInputExamplePlugin, + MockStudioIntegration, +} from "@coremedia/ckeditor5-coremedia-studio-integration-mock"; +import { DataFacade } from "@coremedia/ckeditor5-data-facade"; +import type { FilterRuleSetConfiguration } from "@coremedia/ckeditor5-dataprocessor-support"; +import { DialogVisibility } from "@coremedia/ckeditor5-dialog-visibility"; +import type { RuleConfig } from "@coremedia/ckeditor5-dom-converter"; +import { FontMapper as CoreMediaFontMapper } from "@coremedia/ckeditor5-font-mapper"; +import type { LinkAttributesConfig } from "@coremedia/ckeditor5-link-common"; +import { LinkAttributes } from "@coremedia/ckeditor5-link-common"; +import type { PluginConstructor, TextPartLanguageOption } from "ckeditor5"; +import { + TextPartLanguage, Alignment, Autoformat, AutoLink, @@ -63,25 +81,9 @@ import { TableToolbar, Underline, } from "ckeditor5"; -import type { RuleConfig } from "@coremedia/ckeditor5-dom-converter"; -import type { - LatestCoreMediaRichTextConfig, - V10CoreMediaRichTextConfig, -} from "@coremedia/ckeditor5-coremedia-richtext"; -import { - replaceByElementAndClassBackAndForth, - replaceElementByElementAndClass, - stripFixedAttributes, -} from "@coremedia/ckeditor5-coremedia-richtext"; -import "ckeditor5/ckeditor5.css"; -import type { FilterRuleSetConfiguration } from "@coremedia/ckeditor5-dataprocessor-support"; -import type { LinkAttributesConfig } from "@coremedia/ckeditor5-link-common"; -import { LinkAttributes } from "@coremedia/ckeditor5-link-common"; -import { Differencing } from "@coremedia/ckeditor5-coremedia-differencing"; -import { Blocklist } from "@coremedia/ckeditor5-coremedia-blocklist"; -import { DataFacade } from "@coremedia/ckeditor5-data-facade"; -import type { CKEditorInstanceFactory } from "../CKEditorInstanceFactory"; +import { TextDirection } from "@coremedia/ckeditor5-text-direction"; import type { ApplicationState } from "../ApplicationState"; +import type { CKEditorInstanceFactory } from "../CKEditorInstanceFactory"; import { getHashParam } from "../HashParams"; import { initInputExampleContent } from "../inputExampleContents"; import { updatePreview } from "../preview"; @@ -177,9 +179,15 @@ const getRichTextConfig = ( return { strictness: Strictness.STRICT, compatibility: "latest", - rules: richTextRuleConfigurations, + rules: [...richTextRuleConfigurations], }; }; +const textPartLanguage: TextPartLanguageOption[] = [ + { title: "Arabic", languageCode: "ar" }, + { title: "English", languageCode: "en" }, + { title: "Español", languageCode: "es" }, + { title: "Français", languageCode: "fr" }, +]; export const createRichTextEditor: CKEditorInstanceFactory = async ( sourceElement: HTMLElement, state: ApplicationState, @@ -193,6 +201,13 @@ export const createRichTextEditor: CKEditorInstanceFactory = async ( return ClassicEditor.create(sourceElement, { licenseKey, placeholder: "Type your text here...", + language: { + // Language switch only applies to editor instance. + ui: uiLanguage, + // Won't change the language of content. + content: "en", + textPartLanguage, + }, plugins: [ ...imagePlugins, Alignment, @@ -231,6 +246,8 @@ export const createRichTextEditor: CKEditorInstanceFactory = async ( Superscript, Table, TableToolbar, + TextDirection, + TextPartLanguage, Underline, CoreMediaFontMapper, MockInputExamplePlugin, @@ -255,6 +272,8 @@ export const createRichTextEditor: CKEditorInstanceFactory = async ( "|", "link", "|", + "textPartLanguage", + "textDirection", "alignment", "blockQuote", "codeBlock", @@ -412,12 +431,6 @@ export const createRichTextEditor: CKEditorInstanceFactory = async ( table: { contentToolbar: ["tableColumn", "tableRow", "mergeTableCells"], }, - language: { - // Language switch only applies to editor instance. - ui: uiLanguage, - // Won't change the language of content. - content: "en", - }, autosave: { waitingTime: 1000, // in ms }, diff --git a/packages/ckeditor5-text-direction/eslint.config.mjs b/packages/ckeditor5-text-direction/eslint.config.mjs new file mode 100644 index 0000000000..5755912056 --- /dev/null +++ b/packages/ckeditor5-text-direction/eslint.config.mjs @@ -0,0 +1,23 @@ +import { dirname } from "path"; +import { fileURLToPath } from "url"; +import { defineConfig } from "eslint/config"; +import tsParser from "@typescript-eslint/parser"; +import { configs as workspaceConfig } from "../../eslint.config.mjs"; + +export default defineConfig([ + ...workspaceConfig, + { + languageOptions: { + parser: tsParser, + }, + }, + { + files: ["**/*.ts", "**/*.tsx"], + languageOptions: { + parserOptions: { + tsconfigRootDir: dirname(fileURLToPath(import.meta.url)), + project: "./tsconfig.json", + }, + }, + }, +]); diff --git a/packages/ckeditor5-text-direction/package.json b/packages/ckeditor5-text-direction/package.json new file mode 100644 index 0000000000..a54d0bc19b --- /dev/null +++ b/packages/ckeditor5-text-direction/package.json @@ -0,0 +1,65 @@ +{ + "name": "@coremedia/ckeditor5-text-direction", + "version": "26.0.1-rc.0", + "type": "module", + "description": "Plugin to handle text direction in CKEditor.", + "author": { + "name": "CoreMedia GmbH", + "email": "info@coremedia.com", + "url": "https://coremedia.com/", + "avatar": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2OTguNjY3IiBoZWlnaHQ9IjY5OC42NjciIHZlcnNpb249IjEuMCIgdmlld0JveD0iMCAwIDUyNCA1MjQiPjxwYXRoIGQ9Ik0yODIuNS42QzI0NSA0LjUgMjE1LjEgMTIuOSAxODUgMjcuOSAxMDYuNCA2Ny4yIDUzLjUgMTQyLjEgNDIuNCAyMjkuNWMtMi4xIDE3LjItMi4yIDQ4IDAgNjQuMUM0OCAzMzYuMiA2MS4zIDM3Mi4yIDg0IDQwNi4zYzYxLjkgOTMuMSAxNzUuNCAxMzYuNCAyODUgMTA4LjcgMTkuMi00LjggMzMuNy0xMC4zIDUzLjItMjAuMSAyMS0xMC41IDQyLjUtMjUuMyA1OC4zLTQwLjJsOC03LjYtMjEuNS0yMS42Yy0yMy42LTIzLjgtMjcuMS0yNi43LTM4LjYtMzIuNC0yMi40LTExLjItNDQuNy0xMS42LTcxLjctMS41LTE4LjYgNy0zMC40IDkuNC00OC43IDEwLjEtMTMgLjQtMTcuNS4yLTI3LjgtMS42LTMwLjUtNS4yLTU1LTE3LjgtNzYuOC0zOS41LTMzLjYtMzMuNi00Ny44LTgwLjQtMzguNC0xMjYuNCAxMC4yLTUwLjUgNDYuMi05MC41IDk1LjItMTA2LjEgMzEuNy0xMC4xIDY0LjItOC44IDk2LjYgMy45IDEyLjggNC45IDIyLjcgNyAzNC4xIDcgMTYuNCAwIDMxLjItNC4yIDQ1LjEtMTIuNyA1LjUtMy40IDEzLjYtMTAuNyAzMC40LTI3LjNsMjIuOC0yMi42LTkuMy04LjRjLTM5LjgtMzUuNC04NS42LTU3LTEzOC40LTY1LjEtMTIuNS0yLTQ4LjctMy4zLTU5LTIuM3oiLz48cGF0aCBkPSJNMjk2IDIxOC4xYy0yOC4zIDQuOC00NC4zIDM1LjQtMzIuMSA2MS40IDcuOSAxNyAyNy4yIDI3LjQgNDUuNCAyNC41IDI4LjktNC42IDQ1LjEtMzUuMiAzMi43LTYxLjctOC0xNy4yLTI3LjMtMjcuMy00Ni0yNC4yeiIvPjwvc3ZnPg==" + }, + "keywords": [ + "coremedia" + ], + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./dist/src/index.js", + "import": "./dist/src/index.js" + } + }, + "publishConfig": { + "directory": "dist", + "linkDirectory": false, + "exports": { + ".": { + "types": "./src/index.d.ts", + "default": "./src/index.js" + } + } + }, + "files": [ + "/src" + ], + "license": "Apache-2.0", + "devDependencies": { + "@coremedia-internal/studio-client.test-runner-helper": "0.2.3-alpha.050b44a", + "@types/node": "^22.0.0", + "ckeditor5": "46.1.1", + "copyfiles": "^2.4.1", + "expect": "^30.2.0", + "global-jsdom": "^27.0.0", + "jsdom": "^27.0.0", + "rimraf": "^6.0.1", + "ts-node": "^10.9.2", + "tsx": "^4.20.6", + "typescript": "5.4.5" + }, + "peerDependencies": { + "ckeditor5": "46.1.1" + }, + "dependencies": { + "@coremedia/ckeditor5-logging": "26.0.1-rc.0" + }, + "scripts": { + "clean": "pnpm clean:src && pnpm clean:dist", + "clean:src": "rimraf --glob \"src/**/*.@(js|js.map|d.ts|d.ts.map)\"", + "clean:dist": "rimraf ./dist", + "build": "tsc --project tsconfig.release.json && copyfiles -u 1 theme/* theme/**/* dist/theme\"", + "postbuild": "node prepare-package.cjs && copyfiles ./README.md dist", + "npm-check-updates": "npm-check-updates --upgrade", + "lint": "eslint \"**/*.{ts,tsx}\"", + "test": "test-runner-react" + } +} diff --git a/packages/ckeditor5-text-direction/prepare-package.cjs b/packages/ckeditor5-text-direction/prepare-package.cjs new file mode 100644 index 0000000000..cf14d9ab9b --- /dev/null +++ b/packages/ckeditor5-text-direction/prepare-package.cjs @@ -0,0 +1,21 @@ +#! /usr/bin/env node +"use strict"; + +const fs = require("fs"); +const path = require("path"); + +const distPath = path.resolve("dist"); +if (!fs.existsSync(distPath)) { + fs.mkdirSync(distPath); +} + +const packageJson = JSON.parse(fs.readFileSync(path.resolve("package.json"), "utf-8")); +if (typeof packageJson.publishConfig === "object") { + delete packageJson.publishConfig.directory; + delete packageJson.publishConfig.linkDirectory; + Object.assign(packageJson, packageJson.publishConfig); + delete packageJson.publishConfig; + delete packageJson.scripts; + delete packageJson.devDependencies; +} +fs.writeFileSync(path.resolve("dist/package.json"), JSON.stringify(packageJson, null, 2) + "\n"); diff --git a/packages/ckeditor5-text-direction/src/TextDirection.ts b/packages/ckeditor5-text-direction/src/TextDirection.ts new file mode 100644 index 0000000000..830d751a7d --- /dev/null +++ b/packages/ckeditor5-text-direction/src/TextDirection.ts @@ -0,0 +1,22 @@ +import { Plugin } from "ckeditor5"; +import { TextDirectionEditing } from "./TextDirectionEditing"; +import { TextDirectionUI } from "./TextDirectionUI"; + +/** + * The text direction plugin. + */ +export class TextDirection extends Plugin { + /** + * @inheritDoc + */ + static get requires() { + return [TextDirectionEditing, TextDirectionUI]; + } + + /** + * @inheritDoc + */ + static get pluginName() { + return "TextDirection"; + } +} diff --git a/packages/ckeditor5-text-direction/src/TextDirectionCommand.ts b/packages/ckeditor5-text-direction/src/TextDirectionCommand.ts new file mode 100644 index 0000000000..a35e879951 --- /dev/null +++ b/packages/ckeditor5-text-direction/src/TextDirectionCommand.ts @@ -0,0 +1,97 @@ +import type { ModelElement, ModelWriter } from "ckeditor5"; +import { Command, first } from "ckeditor5"; +import { isDefault, textDirectionAttributeName } from "./utils"; +import type { TextDirectionOption } from "./utils"; + +/** + * The text direction command plugin. + */ +export class TextDirectionCommand extends Command { + /** + * @inheritDoc + */ + override refresh() { + const editor = this.editor; + const locale = editor.locale; + const firstBlock = first(this.editor.model.document.selection.getSelectedBlocks()); + + // As first check whether to enable or disable the command as the value will always be false if the command cannot be enabled. + this.isEnabled = !!firstBlock && this._canHaveTextDirection(firstBlock); + + /** + * A value of the current block's text direction. + * + * @observable + * @readonly + * @member {String} #value + */ + if (this.isEnabled && firstBlock?.hasAttribute(textDirectionAttributeName)) { + this.value = firstBlock.getAttribute(textDirectionAttributeName); + } else { + this.value = locale.contentLanguageDirection === "rtl" ? "rtl" : "ltr"; + } + } + + /** + * Executes the command. Applies the dir `value` to the selected blocks. + * If no `value` is passed, the `value` is the default one or it is equal to the currently selected block's dir attribute, + * the command will remove the attribute from the selected blocks. + * + * @param {Object} [options] Options for the executed command. + * @param {String} [options.value] The value to apply. + * @fires execute + */ + override execute(options: { value?: TextDirectionOption }) { + const editor = this.editor; + const locale = editor.locale; + const model = editor.model; + const doc = model.document; + + const value = options.value; + + model.change((writer) => { + // Get only those blocks from selected that can have dir set + const blocks = Array.from(doc.selection.getSelectedBlocks()).filter((block) => this._canHaveTextDirection(block)); + const currentTextDirection = blocks[0].getAttribute("dir"); + + // Remove TextDirection attribute if current dir is: + // - default (should not be stored in model as it will bloat model data) + // - equal to currently set + // - or no value is passed - denotes default TextDirection. + const removeTextDirection = !value || isDefault(value, locale) || currentTextDirection === value; + + if (removeTextDirection) { + removeTextDirectionFromSelection(blocks, writer); + } else { + setTextDirectionOnSelection(blocks, writer, value); + } + }); + } + + /** + * Checks whether a block can have dir set. + * + * @private + * @param block The block to be checked. + * @returns {Boolean} + */ + _canHaveTextDirection(block: ModelElement) { + return this.editor.model.schema.checkAttribute(block, textDirectionAttributeName); + } +} + +// Removes the dir attribute from blocks. +// @private +function removeTextDirectionFromSelection(blocks: ModelElement[], writer: ModelWriter) { + for (const block of blocks) { + writer.removeAttribute(textDirectionAttributeName, block); + } +} + +// Sets the dir attribute on blocks. +// @private +function setTextDirectionOnSelection(blocks: ModelElement[], writer: ModelWriter, dirValue: TextDirectionOption) { + for (const block of blocks) { + writer.setAttribute(textDirectionAttributeName, dirValue, block); + } +} diff --git a/packages/ckeditor5-text-direction/src/TextDirectionEditing.ts b/packages/ckeditor5-text-direction/src/TextDirectionEditing.ts new file mode 100644 index 0000000000..1f4d43d9a0 --- /dev/null +++ b/packages/ckeditor5-text-direction/src/TextDirectionEditing.ts @@ -0,0 +1,53 @@ +import type { Editor } from "ckeditor5"; +import { Plugin } from "ckeditor5"; +import { TextDirectionCommand } from "./TextDirectionCommand"; +import { textDirectionAttributeName, textDirectionOptions } from "./utils"; + +/** + * The text direction editing feature. + */ +export class TextDirectionEditing extends Plugin { + /** + * @inheritDoc + */ + static get pluginName() { + return "TextDirectionEditing"; + } + + /** + * @inheritDoc + */ + constructor(editor: Editor) { + super(editor); + + editor.config.define("textDirection", { + options: [...textDirectionOptions], + }); + } + + /** + * @inheritDoc + */ + init() { + const editor = this.editor; + const schema = editor.model.schema; + + // Allow text direction attribute on all blocks. + schema.extend("$block", { allowAttributes: textDirectionAttributeName }); + editor.model.schema.setAttributeProperties(textDirectionAttributeName, { isFormatting: true }); + + // Upcast: class="dir-rtl" -> model attribute dir="rtl" + editor.conversion.for("upcast").attributeToAttribute({ + view: "dir", + model: "dir", + }); + + // Downcast: model attribute dir="rtl" -> class="dir-rtl" + editor.conversion.for("downcast").attributeToAttribute({ + model: "dir", + view: "dir", + }); + + editor.commands.add("textDirection", new TextDirectionCommand(editor)); + } +} diff --git a/packages/ckeditor5-text-direction/src/TextDirectionUI.ts b/packages/ckeditor5-text-direction/src/TextDirectionUI.ts new file mode 100644 index 0000000000..eddf22379b --- /dev/null +++ b/packages/ckeditor5-text-direction/src/TextDirectionUI.ts @@ -0,0 +1,131 @@ +import { ButtonView, Plugin, createDropdown, addToolbarToDropdown } from "ckeditor5"; +// @ts-expect-error no idea why +import ltrIcon from "../theme/icons/ltr.svg"; +// @ts-expect-error no idea why +import rtlIcon from "../theme/icons/rtl.svg"; +import type { TextDirectionOption } from "./utils"; + +const icons = new Map([ + ["ltr", ltrIcon], + ["rtl", rtlIcon], +]); + +/** + * The text direction UI plugin. + */ +export class TextDirectionUI extends Plugin { + get localizedOptionTitles() { + const t = this.editor.t; + + return { + ltr: t("Left to right"), + rtl: t("Right to left"), + }; + } + + /** + * @inheritDoc + */ + static get pluginName() { + return "TextDirectionUI"; + } + + /** + * @inheritDoc + */ + init() { + const editor = this.editor; + const componentFactory = editor.ui.componentFactory; + const t = editor.t; + const options = ["ltr", "rtl"] as const; + + options?.forEach((option) => this._addButton(option)); + + componentFactory.add("textDirection", (locale) => { + const dropdownView = createDropdown(locale); + + // Add existing text direction buttons to dropdown's toolbar. + const buttons: ButtonView[] = options?.length + ? options.map((option) => componentFactory.create(`textDirection:${option}`) as ButtonView) + : []; + addToolbarToDropdown(dropdownView, buttons); + + // Configure dropdown properties an behavior. + dropdownView.buttonView.set({ + label: t("Text direction"), + tooltip: true, + }); + + if (dropdownView.toolbarView) { + dropdownView.toolbarView.isVertical = true; + dropdownView.toolbarView.ariaLabel = t("Text direction toolbar"); + } + + dropdownView.extendTemplate({ + attributes: { + class: "ck-direction-dropdown", + }, + }); + + // The default icon depends on the direction of the content. + const defaultIcon = locale.contentLanguageDirection === "rtl" ? rtlIcon : ltrIcon; + + // Change icon to reflect current selection's text direction. + dropdownView.buttonView.bind("icon").toMany(buttons, "isOn", (...areActive) => { + // Get the index of an active button. + const index = areActive.findIndex((value) => value); + + // If none of the commands is active, display either defaultIcon or the first button's icon. + if (index < 0) { + return defaultIcon; + } + + // Return active button's icon. + return buttons[index]?.icon; + }); + + // Enable button if any of the buttons is enabled. + dropdownView + .bind("isEnabled") + .toMany(buttons, "isEnabled", (...areEnabled) => areEnabled.some((isEnabled) => isEnabled)); + + return dropdownView; + }); + } + + /** + * Helper method for initializing the button and linking it with an appropriate command. + * + * @private + * @param {String} option The name of the dir option for which the button is added. + */ + _addButton(option: TextDirectionOption) { + const editor = this.editor; + + editor.ui.componentFactory.add(`textDirection:${option}`, (locale) => { + const command = editor.commands.get("textDirection"); + const buttonView = new ButtonView(locale); + + buttonView.set({ + label: this.localizedOptionTitles[option], + icon: icons.get(option), + tooltip: true, + isToggleable: true, + }); + + if (command) { + // Bind button model to command. + buttonView.bind("isEnabled").to(command); + buttonView.bind("isOn").to(command, "value", (value) => value === option); + } + + // Execute command. + this.listenTo(buttonView, "execute", () => { + editor.execute("textDirection", { value: option }); + editor.editing.view.focus(); + }); + + return buttonView; + }); + } +} diff --git a/packages/ckeditor5-text-direction/src/index.ts b/packages/ckeditor5-text-direction/src/index.ts new file mode 100644 index 0000000000..a3281b0a20 --- /dev/null +++ b/packages/ckeditor5-text-direction/src/index.ts @@ -0,0 +1,3 @@ +export { TextDirection } from "./TextDirection"; +export { TextDirectionEditing } from "./TextDirectionEditing"; +export { TextDirectionUI } from "./TextDirectionUI"; diff --git a/packages/ckeditor5-text-direction/src/utils.ts b/packages/ckeditor5-text-direction/src/utils.ts new file mode 100644 index 0000000000..dd66d1c266 --- /dev/null +++ b/packages/ckeditor5-text-direction/src/utils.ts @@ -0,0 +1,17 @@ +import type { Locale } from "ckeditor5"; + +export type TextDirectionOption = "ltr" | "rtl"; + +export const textDirectionOptions: TextDirectionOption[] = ["ltr", "rtl"]; + +export const textDirectionAttributeName = "dir"; + +export function isDefault(dirValue: TextDirectionOption, locale: Locale) { + // Right now only LTR is supported so the 'left' value is always the default one. + + if (locale.contentLanguageDirection == "rtl") { + return dirValue === "rtl"; + } else { + return dirValue === "ltr"; + } +} diff --git a/packages/ckeditor5-text-direction/theme/icons/ltr.svg b/packages/ckeditor5-text-direction/theme/icons/ltr.svg new file mode 100644 index 0000000000..e855ee827a --- /dev/null +++ b/packages/ckeditor5-text-direction/theme/icons/ltr.svg @@ -0,0 +1,4 @@ + + + diff --git a/packages/ckeditor5-text-direction/theme/icons/rtl.svg b/packages/ckeditor5-text-direction/theme/icons/rtl.svg new file mode 100644 index 0000000000..4dd1a971dc --- /dev/null +++ b/packages/ckeditor5-text-direction/theme/icons/rtl.svg @@ -0,0 +1,4 @@ + + + diff --git a/packages/ckeditor5-text-direction/tsconfig.json b/packages/ckeditor5-text-direction/tsconfig.json new file mode 100644 index 0000000000..bc71e18f70 --- /dev/null +++ b/packages/ckeditor5-text-direction/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "types": [ + "node" + ] + }, + "include": [ + "./test", + "./src" + ] +} diff --git a/packages/ckeditor5-text-direction/tsconfig.release.json b/packages/ckeditor5-text-direction/tsconfig.release.json new file mode 100644 index 0000000000..6f82b8a4bf --- /dev/null +++ b/packages/ckeditor5-text-direction/tsconfig.release.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "exclude": [ + "./test" + ] +} diff --git a/packages/ckeditor5-text-direction/typedoc.json b/packages/ckeditor5-text-direction/typedoc.json new file mode 100644 index 0000000000..81211399b6 --- /dev/null +++ b/packages/ckeditor5-text-direction/typedoc.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://typedoc.org/schema.json", + "extends": [ + "../../typedoc.base.json" + ], + "entryPoints": [ + "./src/index.ts" + ], + "name": "ckeditor5-direction" +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4739aca4f9..2cc74fe4cf 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -128,6 +128,9 @@ importers: '@coremedia/ckeditor5-link-common': specifier: 26.0.1-rc.0 version: link:../packages/ckeditor5-link-common + '@coremedia/ckeditor5-text-direction': + specifier: workspace:* + version: link:../packages/ckeditor5-text-direction '@coremedia/service-agent': specifier: ^2.1.2 version: 2.1.2 @@ -1319,6 +1322,47 @@ importers: version: 5.4.5 publishDirectory: dist + packages/ckeditor5-text-direction: + dependencies: + '@coremedia/ckeditor5-logging': + specifier: 26.0.1-rc.0 + version: link:../ckeditor5-logging + devDependencies: + '@coremedia-internal/studio-client.test-runner-helper': + specifier: 0.2.3-alpha.050b44a + version: 0.2.3-alpha.050b44a(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@types/node': + specifier: ^22.0.0 + version: 22.13.14 + ckeditor5: + specifier: 46.1.1 + version: 46.1.1 + copyfiles: + specifier: ^2.4.1 + version: 2.4.1 + expect: + specifier: ^30.2.0 + version: 30.2.0 + global-jsdom: + specifier: ^27.0.0 + version: 27.0.0(jsdom@27.0.0(postcss@8.4.45)) + jsdom: + specifier: ^27.0.0 + version: 27.0.0(postcss@8.4.45) + rimraf: + specifier: ^6.0.1 + version: 6.0.1 + ts-node: + specifier: ^10.9.2 + version: 10.9.2(@types/node@22.13.14)(typescript@5.4.5) + tsx: + specifier: ^4.20.6 + version: 4.20.6 + typescript: + specifier: 5.4.5 + version: 5.4.5 + publishDirectory: dist + packages: '@adobe/css-tools@4.4.4': @@ -9385,8 +9429,6 @@ snapshots: '@ckeditor/ckeditor5-ui': 46.1.1 '@ckeditor/ckeditor5-utils': 46.1.1 ckeditor5: 46.1.1 - transitivePeerDependencies: - - supports-color '@ckeditor/ckeditor5-link@45.2.1': dependencies: @@ -9415,8 +9457,6 @@ snapshots: '@ckeditor/ckeditor5-widget': 46.1.1 ckeditor5: 46.1.1 es-toolkit: 1.39.5 - transitivePeerDependencies: - - supports-color '@ckeditor/ckeditor5-list@45.2.1': dependencies: