|
1 | 1 | # @docs.plus/extension-indent |
2 | 2 |
|
3 | | -A professional Tiptap extension for text indentation management with customizable options. |
| 3 | +Tiptap extension that adds **line-prefix** indent and outdent: each step inserts or removes a configured string (`indentChars`, default two spaces) at line starts when the caret (or each line of a multi-line selection) sits in an allowed **textblock + parent** context (see `allowedIndentContexts` below). |
4 | 4 |
|
5 | | -## Features |
| 5 | +By default, only **`paragraph`** under **`doc`** or **`blockquote`** is allowed. Any other textblock (e.g. `heading`, `codeBlock`) is excluded until you add an explicit rule. **Tab / Shift-Tab** still run first through list and table behavior when those extensions are present (see [Keyboard](#keyboard) below). |
6 | 6 |
|
7 | | -- Indent/outdent at cursor position or for text selections |
8 | | -- Configurable indentation characters and behavior |
9 | | -- Node-type filtering for targeted indentation |
10 | | -- Tab and Shift+Tab keyboard shortcuts (configurable) |
11 | | -- Typescript support with full type definitions |
| 7 | +## Requirements |
| 8 | + |
| 9 | +Peer dependencies (your app should already include them): |
| 10 | + |
| 11 | +- `@tiptap/core` ^3.20.4 |
| 12 | +- `@tiptap/pm` ^3.20.4 |
| 13 | + |
| 14 | +Optional: `@tiptap/extension-table` for cell navigation on Tab; list extensions (`listItem` / `taskItem`) for sink/lift before literal indent. |
12 | 15 |
|
13 | 16 | ## Installation |
14 | 17 |
|
| 18 | +In this monorepo, from the root: |
| 19 | + |
15 | 20 | ```bash |
16 | | -npm install @docs.plus/extension-indent |
| 21 | +bun install |
17 | 22 | ``` |
18 | 23 |
|
19 | | -## Usage |
| 24 | +Published installs use the same package name; align TipTap versions with the peer range above. |
20 | 25 |
|
21 | | -### Basic |
| 26 | +## Usage |
22 | 27 |
|
23 | | -```js |
| 28 | +```ts |
24 | 29 | import { Editor } from '@tiptap/core' |
| 30 | +import StarterKit from '@tiptap/starter-kit' |
25 | 31 | import { Indent } from '@docs.plus/extension-indent' |
26 | 32 |
|
27 | 33 | new Editor({ |
28 | | - extensions: [ |
29 | | - // ...other extensions |
30 | | - Indent |
31 | | - ] |
| 34 | + extensions: [StarterKit, Indent.configure({ indentChars: '\t' })] |
32 | 35 | }) |
33 | 36 | ``` |
34 | 37 |
|
35 | | -### Configuration |
| 38 | +`Indent.configure({})` merges with extension defaults (see [Options](#options)). |
36 | 39 |
|
37 | | -```js |
38 | | -Indent.configure({ |
39 | | - // Character(s) for each indentation (default: ' ' - 2 spaces) |
40 | | - indentChars: ' ', // 4 spaces |
| 40 | +### `allowedIndentContexts` |
41 | 41 |
|
42 | | - // Enable/disable the extension (default: true) |
43 | | - enabled: true, |
| 42 | +Literal `indent()` / `outdent()` only run when the innermost textblock at the caret/line and its **immediate parent** match one of the rules. Each rule is `{ textblock: string, parent: string }` (TipTap / ProseMirror `NodeType.name`). |
44 | 43 |
|
45 | | - // Control which node types can be indented (default: paragraph, listItem, orderedList) |
46 | | - // Empty array allows all nodes to be indented |
47 | | - allowedNodeTypes: ['paragraph', 'listItem', 'orderedList'] |
48 | | -}) |
49 | | -``` |
| 44 | +The list is a **full** allowlist: TipTap merges `configure({ … })` into defaults — if you pass `allowedIndentContexts`, it **replaces** the default array; list every `(textblock, parent)` pair you need. |
50 | 45 |
|
51 | | -### Examples |
| 46 | +| You want | Add / use rules like | |
| 47 | +| ---------------------------------------------- | -------------------------------------------------------------------------------------------------- | |
| 48 | +| Body + blockquote paragraphs (package default) | `{ textblock: 'paragraph', parent: 'doc' }` and `{ textblock: 'paragraph', parent: 'blockquote' }` | |
| 49 | +| Body paragraphs only | Only `paragraph` + `doc` | |
| 50 | +| Blockquote paragraphs only | Only `paragraph` + `blockquote` | |
| 51 | +| List item paragraphs | `{ textblock: 'paragraph', parent: 'listItem' }` and/or `taskItem` | |
| 52 | +| Table cell paragraphs | `{ textblock: 'paragraph', parent: 'tableCell' }` (if your schema uses it) | |
| 53 | +| Headings | e.g. `{ textblock: 'heading', parent: 'doc' }` — type name is lowercase `heading`, not HTML `H1` | |
52 | 54 |
|
53 | | -```js |
54 | | -// Use tabs instead of spaces |
55 | | -Indent.configure({ |
56 | | - indentChars: '\t' |
57 | | -}) |
| 55 | +Pass **`[]`** to turn off **literal** indent/outdent everywhere (Tab can still sink/lift lists or move table cells). |
58 | 56 |
|
59 | | -// Allow indentation only for paragraphs |
60 | | -Indent.configure({ |
61 | | - allowedNodeTypes: ['paragraph'] |
62 | | -}) |
63 | | -``` |
| 57 | +**Migration:** older releases used `allowedParentTypes` (parent names for **`paragraph`** only). Replace each parent `p` with `{ textblock: 'paragraph', parent: p }`. If you used `allowedNodeTypes`, same migration. |
| 58 | + |
| 59 | +### Options |
| 60 | + |
| 61 | +| Option | Type | Default | Description | |
| 62 | +| ----------------------- | ------------------------------ | ----------------------------- | ---------------------------------------------------------------------- | |
| 63 | +| `indentChars` | `string` | `' '` | Inserted or removed per step (often `'\t'` or two spaces). | |
| 64 | +| `enabled` | `boolean` | `true` | Disable behavior without removing the extension. | |
| 65 | +| `allowedIndentContexts` | `Array<{ textblock, parent }>` | body + blockquote `paragraph` | Full allowlist of textblock + parent pairs for literal indent/outdent. | |
| 66 | + |
| 67 | +### Keyboard |
| 68 | + |
| 69 | +| Key | Order of handling | |
| 70 | +| ------------- | ----------------------------------------------------------------------------------------------------- | |
| 71 | +| **Tab** | `sinkListItem` (`listItem` / `taskItem`) → `goToNextCell` (if table extension is loaded) → `indent()` | |
| 72 | +| **Shift-Tab** | `liftListItem` → `goToPreviousCell` → `outdent()` | |
| 73 | + |
| 74 | +The extension registers with **priority `25`** so delegated commands run first when applicable. |
64 | 75 |
|
65 | 76 | ### Commands |
66 | 77 |
|
67 | | -```js |
68 | | -// Add indentation |
| 78 | +```ts |
69 | 79 | editor.commands.indent() |
70 | | - |
71 | | -// Remove indentation |
72 | 80 | editor.commands.outdent() |
73 | 81 | ``` |
74 | 82 |
|
75 | | -### Keyboard Shortcuts |
| 83 | +These respect `enabled` and the same `allowedIndentContexts` rules as the keyboard path. |
76 | 84 |
|
77 | | -Default keyboard bindings: |
| 85 | +### Multiline selections |
78 | 86 |
|
79 | | -- `Tab` - Indent |
80 | | -- `Shift+Tab` - Outdent |
| 87 | +Selected ranges are split into **visual lines** using `doc.textBetween(from, to, '\n')` (a single newline between blocks). **Every** line must match `allowedIndentContexts`; otherwise the command returns `false` and the document is unchanged. |
81 | 88 |
|
82 | | -## Development |
| 89 | +### Empty selection: outdent |
| 90 | + |
| 91 | +**Outdent** can remove: |
| 92 | + |
| 93 | +- leading `indentChars` at the **start of the current line**, or |
| 94 | +- a trailing prefix of `indentChars` **immediately before the caret** (e.g. undo a tab just inserted without moving to column 0). |
| 95 | + |
| 96 | +## Testing |
| 97 | + |
| 98 | +From `packages/extension-indent`: |
83 | 99 |
|
84 | 100 | ```bash |
85 | | -# Install dependencies |
86 | | -npm install |
| 101 | +bun run test |
| 102 | +``` |
87 | 103 |
|
88 | | -# Build |
89 | | -npm run build |
| 104 | +Jest + jsdom; config in `jest.config.cjs` (Jest stack from the monorepo root per workspace rules). Fixtures typically use `StarterKit` like a real app. |
90 | 105 |
|
91 | | -# Development with auto-rebuild |
92 | | -npm run dev |
| 106 | +End-to-end coverage lives in `packages/webapp/cypress/e2e/editor/indent/` against the production editor stack. |
93 | 107 |
|
94 | | -# Lint code |
95 | | -npm run lint |
| 108 | +## Development |
| 109 | + |
| 110 | +```bash |
| 111 | +bun install |
| 112 | +bun run build |
| 113 | +bun run dev |
| 114 | +bun run lint |
96 | 115 | ``` |
97 | 116 |
|
98 | 117 | ## License |
|
0 commit comments