UNPKG

editorjs-indent-tune

Version:
304 lines (233 loc) 12.5 kB
# EditorJS Indent Tune Indentation support for [Editor.js](https://editorjs.io) blocks. `editorjs-indent-tune` adds **indent** / **unindent controls**, keyboard shortcuts, auto-indent, visual highlighting, RTL/LTR support, and optional multi-block indentation. ![example](./assets/example1.gif) ## Instalation ### NPM ```shell npm i editorjs-indent-tune ``` ### CDN Require this script on a page with Editor.js. ```html <script src="https://cdn.jsdelivr.net/npm/editorjs-indent-tune/dist/bundle.js"><script> ``` ## Basic Usage ```js import EditorJS from '@editorjs/editorjs' import IndentTune from 'editorjs-indent-tune' const editor = new EditorJS({ tools: { indentTune: { class: IndentTune, // Recommended: pass Editor.js version for style & older compatibility version: EditorJS.version, }, }, // Apply indent tune globally tunes: ['indentTune'], }) ``` That’s it 🎉 No extra configuration is required — all other options are **fully optional** and can be added later as needed. ### What This Does - Enables indent / unindent controls - Adds keyboard shortcuts (Tab / Shift + Tab) - Works for all blocks by default - Respects built-in indent limits ## Enable / Disable Per Block If you **don’t** want indentation for a specific block, simply omit the tune from that block’s `tunes` list. ```js const editor = new EditorJS({ tools: { paragraph: { class: Paragraph, tunes: [ // intentionally no indentTune ], }, }, }) ``` ## Highlight Indent Level You can visually highlight the indent area when a block is focused. ### CSS ```css .indentHighlight { transition: background-color 0.4s ease; } [data-block-indent-wrapper][data-focused] .indentHighlight { background-color: rgba(255, 0, 0, 0.1); } ``` ### Config ```js const editor = new EditorJS({ tools: { indentTune: { class: IndentTune, config: { highlightIndent: { className: 'indentHighlight', }, }, }, }, }) ``` ## Complete Example (TypeScript) ```js import EditorJS from '@editorjs/editorjs' import IndentTune, { type IndentTuneConfig } from 'editorjs-indent-tune' const editor = new EditorJS({ tools: { paragraph: { class: Paragraph, }, indentTune: { class: IndentTune, config: { version: EditorJS.version, indentSize: 30, maxIndent: 10, minIndent: 0, multiblock: true, tuneName: 'indentTune', customBlockIndentLimits: { paragraph: { max: 5 }, }, } as IndentTuneConfig, }, }, }) ``` ## Keyboard Shortcuts By default, **EditorJS Indent Tune** enables keyboard-based indentation. ### Default Behavior | Shortcut | Action | |-|-| | `Tab` | Indent block | | `Shift + Tab` | Unindent block | ✅ Works on the currently focused block ✅ Supports multi-block selection if multiblock is enabled ✅ Automatically respects minIndent / maxIndent limits ### Custom Keyboard Handling You can override the default keyboard behavior using the `handleShortcut` option. ```ts handleShortcut?: ( e: KeyboardEvent, blockId: string ) => 'indent' | 'unindent' | 'default' | void ``` #### Interpretation | Return value | Effect | |-|-| | `'indent'` | Forces indentation | | `'unindent'` | Forces un-indentation | | `'default'` | Uses built-in behavior | | `undefined` | Uses build-in behavior | | `false` (as config value) | Disabled shortcuts completely | #### Example: Custom Shortcut Logic ```ts const editor = new EditorJS({ tools: { indentTune: { class: IndentTune, config: { handleShortcut(e, blockId) { // Use Ctrl + ] / Ctrl + [ if (e.ctrlKey && e.key === ']') return 'indent' if (e.ctrlKey && e.key === '[') return 'unindent' return 'default' }, }, }, }, }) ``` #### Notes - Auto-indent and keyboard indent respect block-specific limits - Direction (ltr / rtl) does not affect shortcut logic — only visual placement ### Using With an Alignment Tune (RTL / LTR) If your editor also supports **text alignment**, you may want indentation to react to direction changes (LTR ↔ RTL). This can be done using `directionChangeHandler`. ### Example Alignment Tune ```ts class MyAlignmentTune { private block constructor({ block }) { this.block = block } private static listeners = new Set< (blockId: string, direction: 'ltr' | 'rtl') => void >() static addChangeListener( listener: (blockId: string, direction: 'ltr' | 'rtl') => void ) { MyAlignmentTune.listeners.add(listener) } private onChange(alignment: 'left' | 'right') { const direction = alignment === 'left' ? 'ltr' : 'rtl' MyAlignmentTune.listeners.forEach(l => l(this.block.id, direction) ) } wrap(blockContent: HTMLElement) { const direction = this.data.alignment === 'left' ? 'ltr' : 'rtl' MyAlignmentTune.listeners.forEach(l => l(this.block.id, direction) ) return blockContent } } ``` ### Editor Setup ```js const editor = new EditorJS({ tools: { alignmentTune: { class: MyAlignmentTune, }, indentTune: { class: IndentTune, config: { directionChangeHandler: MyAlignmentTune.addChangeListener, }, }, }, }) ``` ✅ You’re free to implement this however you prefer — this is just one approach. ## Selecting Indent Elements (DOM) These static constants are exposed for easier quering: ```ts IndentTune.DATA_WRAPPER_NAME IndentTune.DATA_FOCUSED IndentTune.DATA_INDENT_LEVEL ``` ### Examples ```ts // Wrapper document.querySelector(`[${IndentTune.DATA_WRAPPER_NAME}]`) // Focused wrapper document.querySelector(`[${IndentTune.DATA_FOCUSED}]`) // Indent level element.getAttribute(IndentTune.DATA_INDENT_LEVEL) ``` ## Configuration Options All config fields are optional. | Field | Type | Description | Default | | ----------------------- | ---------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | | version | `string` | Recommended to pass editor version for seamless style adjustments | `2.29` | | indentSize | `number` | Size of one indent level (in pixels) | `24` | | maxIndent | `number` | The upper indent limit of any block | `8` | | minIndent | `number` | The lower indent limit of any block | `0` | | orientation | `'horizontal' \| 'vertical'` | The UI design for how you want the toolbox to be displayed | `horizontal` | | customBlockIndentLimits | `Record<string, Partial<Record<'min' \| 'max', number>>>` | A set of overrides of the indent limit for each type of block | `{}` | | multiblock | `boolean` | Marks if you can indent multiple blocks at a time | `false` | | tuneName | `string \| null` | This is required for multiblock to work | `null` | | handleShortcut | `((e:KeyboardEvent, blockId:string) => 'unindent' \| 'indent' \| 'default' \| void) \| undefined \| false` | Custom shortcut function that allows overriding the default indenting using keyboard. If set as `false` no shortcut will be applied. | `undefined` | | direction | `'ltr' \| 'rtl'` | Specify the global direction of the indents | `ltr` | | directionChangeHandler | `null \| (listener: (blockId: string, direction: TextDirection) => void): void` | If provided will be used to apply visual changes (indent direction) based on the provided change value. | `null` | | highlightIndent | `null \| { className?: string; tuneNames?: string[]; }` | If provided will display a highlight for the indent. Provide a list of `tuneNames` for which it should be applied, applies to all by default. Can be customized with custom class using `[data-focused] .myClass {}` | `null` | | autoIndent | `null \| boolean \| { tuneNames?: string[]; }` | If provided with a config object or `true` will automatically indent new blocks to same level as the previous block, if there are no indentation limit restrictions. Provide a list of `tuneNames` for which block types it should be applied, applies to all by default. | `false` |