fable-editor 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +175 -0
- package/dist/angular/LICENSE +21 -0
- package/dist/angular/README.md +175 -0
- package/dist/angular/angular/fable-editor.component.d.ts +29 -0
- package/dist/angular/angular/fable-editor.module.d.ts +7 -0
- package/dist/angular/angular/index.d.ts +2 -0
- package/dist/angular/core/config.d.ts +17 -0
- package/dist/angular/core/docx-import.d.ts +1 -0
- package/dist/angular/core/editor.d.ts +219 -0
- package/dist/angular/core/i18n.d.ts +139 -0
- package/dist/angular/core/icons.d.ts +2 -0
- package/dist/angular/core/index.d.ts +7 -0
- package/dist/angular/core/paste-engine.d.ts +6 -0
- package/dist/angular/core/types.d.ts +87 -0
- package/dist/angular/esm2022/angular/fable-editor.component.mjs +101 -0
- package/dist/angular/esm2022/angular/fable-editor.module.mjs +16 -0
- package/dist/angular/esm2022/angular/index.mjs +3 -0
- package/dist/angular/esm2022/core/config.mjs +122 -0
- package/dist/angular/esm2022/core/docx-import.mjs +391 -0
- package/dist/angular/esm2022/core/editor.mjs +2700 -0
- package/dist/angular/esm2022/core/i18n.mjs +278 -0
- package/dist/angular/esm2022/core/icons.mjs +89 -0
- package/dist/angular/esm2022/core/index.mjs +6 -0
- package/dist/angular/esm2022/core/paste-engine.mjs +482 -0
- package/dist/angular/esm2022/core/types.mjs +2 -0
- package/dist/angular/esm2022/fable-editor.mjs +5 -0
- package/dist/angular/esm2022/index.mjs +2 -0
- package/dist/angular/fesm2022/fable-editor.mjs +4176 -0
- package/dist/angular/fesm2022/fable-editor.mjs.map +1 -0
- package/dist/angular/index.d.ts +1 -0
- package/dist/core/config.d.ts +18 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/docx-import.d.ts +2 -0
- package/dist/core/docx-import.d.ts.map +1 -0
- package/dist/core/editor.css +1175 -0
- package/dist/core/editor.d.ts +221 -0
- package/dist/core/editor.d.ts.map +1 -0
- package/dist/core/i18n.d.ts +141 -0
- package/dist/core/i18n.d.ts.map +1 -0
- package/dist/core/icons.d.ts +3 -0
- package/dist/core/icons.d.ts.map +1 -0
- package/dist/core/index.cjs +12 -0
- package/dist/core/index.d.ts +8 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.mjs +2789 -0
- package/dist/core/paste-engine.d.ts +7 -0
- package/dist/core/paste-engine.d.ts.map +1 -0
- package/dist/core/types.d.ts +88 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/react/FableEditor.d.ts +13 -0
- package/dist/react/FableEditor.d.ts.map +1 -0
- package/dist/react/index.cjs +41 -0
- package/dist/react/index.d.ts +3 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.mjs +3471 -0
- package/package.json +105 -0
- package/style.css +1175 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 An Sajin Satheesan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# FableEditor
|
|
2
|
+
|
|
3
|
+
A rich text editor packaged as an npm library with first-class React and Angular wrappers. Built from the original standalone `fable-editor.html`/`fable-editor.js` demo, preserving the full feature set.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
| Feature | Details |
|
|
8
|
+
|---|---|
|
|
9
|
+
| Toolbar & menubar | Configurable via `toolbar`/`menubar` strings (`\|`-separated groups), or use the built-in defaults. Menus: File, Edit, View, Insert, Format, Tools, Table, Help |
|
|
10
|
+
| Tables | Resize handles, row/column insert & delete, cell/row/column/table properties, context toolbar |
|
|
11
|
+
| Paste handling | PowerPaste-style clean paste from Word / Google Docs / Excel |
|
|
12
|
+
| Internationalization | English / Arabic, automatic RTL/LTR switching |
|
|
13
|
+
| Fonts & formatting | Configurable font list (`fontFamilyFormats`), sizes, line-height, word-spacing, text/background color, change case |
|
|
14
|
+
| Custom content styling | `contentStyle` injects scoped CSS into the editable area (e.g. default font/size) |
|
|
15
|
+
| Images | Placeholder upload UI, drag-and-drop, configurable accepted file types (`imageFileTypes`), pluggable async upload handler |
|
|
16
|
+
| Documents | Import `.docx` files, source-code view, print preview |
|
|
17
|
+
| Productivity | Undo/redo, revision history, autosave draft restore, word count, special characters, page breaks |
|
|
18
|
+
| Fullscreen | Toggle fullscreen editing |
|
|
19
|
+
| Help | Built-in shortcuts/help dialog |
|
|
20
|
+
|
|
21
|
+
## Install
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install fable-editor
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
> **Note:** the package ships framework wrappers as optional peer dependencies. Install React or Angular dependencies only if you use those wrappers.
|
|
28
|
+
|
|
29
|
+
## Vanilla / Core
|
|
30
|
+
|
|
31
|
+
Import the CSS once in your app, then create an editor instance:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import 'fable-editor/style.css';
|
|
35
|
+
import { FableEditor } from 'fable-editor';
|
|
36
|
+
|
|
37
|
+
const editor = new FableEditor({
|
|
38
|
+
target: document.getElementById('editor')!,
|
|
39
|
+
language: 'en', // 'en' | 'ar'
|
|
40
|
+
height: 400,
|
|
41
|
+
initialContent: '<p><br></p>',
|
|
42
|
+
onChange: (html) => console.log(html)
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// API
|
|
46
|
+
editor.getContent();
|
|
47
|
+
editor.setContent('<p>Hello</p>');
|
|
48
|
+
editor.insertContent('<strong>bold</strong>');
|
|
49
|
+
editor.setLanguage('ar');
|
|
50
|
+
editor.focus();
|
|
51
|
+
editor.destroy();
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### `init` options
|
|
55
|
+
|
|
56
|
+
| Option | Type | Default | Description |
|
|
57
|
+
|---|---|---|---|
|
|
58
|
+
| `target` | `HTMLElement` | — | Required. Element to mount the editor into. |
|
|
59
|
+
| `language` | `'en' \| 'ar'` | `'en'` | UI language; also switches text direction. |
|
|
60
|
+
| `height` | `number` | `302` | Editable area height in px. |
|
|
61
|
+
| `initialContent` | `string` | `'<p><br></p>'` | Starting HTML content. |
|
|
62
|
+
| `menubar` | `boolean \| string` | `true` | `true`/omitted = default menu set. `false` = hidden. A string is a space-separated subset/reorder, e.g. `'file edit view insert format tools table help'`. |
|
|
63
|
+
| `toolbar` | `boolean \| string` | `true` | `true`/omitted = default toolbar. `false` = hidden. A string lays out the toolbar: `\|` separates groups, spaces separate items, e.g. `'undo redo \| bold italic'`. |
|
|
64
|
+
| `statusbar` | `boolean` | `true` | Show/hide the status bar. |
|
|
65
|
+
| `readonly` | `boolean` | `false` | Disable editing. |
|
|
66
|
+
| `fontFamilyFormats` | `[string, string][]` | built-in list | Overrides the font dropdown. Tuples of `[displayName, cssFontFamilyValue]`. |
|
|
67
|
+
| `contentStyle` | `string` | — | Custom CSS for the editable area. The literal word `body` is scoped to this editor instance; other selectors are used verbatim (your responsibility to scope). |
|
|
68
|
+
| `imageFileTypes` | `string[]` | common image MIME types | `accept` list for the native image file picker. |
|
|
69
|
+
| `imageUploadHandler` | `(file: File) => Promise<string>` | — | Resolve with a URL after uploading; omit to inline images as base64. |
|
|
70
|
+
| `onImageUploadError` | `(error, file) => void` | — | Called when `imageUploadHandler` rejects. |
|
|
71
|
+
| `draftKey` | `string` | current page path | Storage key suffix for autosaved drafts. |
|
|
72
|
+
| `onChange` / `onReady` | functions | — | Content-change and ready callbacks (usually set by the React/Angular wrapper instead). |
|
|
73
|
+
|
|
74
|
+
## React
|
|
75
|
+
|
|
76
|
+
```tsx
|
|
77
|
+
import 'fable-editor/style.css';
|
|
78
|
+
import { FableEditor } from 'fable-editor/react';
|
|
79
|
+
|
|
80
|
+
function App() {
|
|
81
|
+
const [value, setValue] = useState('<p><br></p>');
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<FableEditor
|
|
85
|
+
value={value}
|
|
86
|
+
onChange={setValue}
|
|
87
|
+
language="en"
|
|
88
|
+
height={400}
|
|
89
|
+
/>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Props: `value`, `defaultValue`, `onChange`, `language`, `height`, `menubar`, `toolbar`, `statusbar`, `readonly`, `fontFamilyFormats`, `contentStyle`, `imageFileTypes`, `imageUploadHandler`, `onImageUploadError`, `init`, `className`, `style` (see the [`init` options](#init-options) table above — every option is also a top-level prop). A ref exposes `getContent`, `setContent`, `insertContent`, `setLanguage`, `focus`, `destroy`.
|
|
95
|
+
|
|
96
|
+
## Angular
|
|
97
|
+
|
|
98
|
+
Add to `angular.json` styles:
|
|
99
|
+
|
|
100
|
+
```json
|
|
101
|
+
"styles": [
|
|
102
|
+
"src/styles.css",
|
|
103
|
+
"node_modules/fable-editor/style.css"
|
|
104
|
+
]
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Import the module:
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
import { FableEditorModule } from 'fable-editor/angular';
|
|
111
|
+
|
|
112
|
+
@NgModule({
|
|
113
|
+
imports: [FableEditorModule]
|
|
114
|
+
})
|
|
115
|
+
export class AppModule {}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Use in a template:
|
|
119
|
+
|
|
120
|
+
```html
|
|
121
|
+
<fable-editor [(ngModel)]="content" language="en" [height]="400"></fable-editor>
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Inputs: `language`, `height`, `menubar`, `toolbar`, `statusbar`, `readonly`, `init`. `menubar`/`toolbar` accept a string directly (e.g. `[toolbar]="'undo redo | bold italic'"`); `fontFamilyFormats`, `contentStyle`, `imageFileTypes`, `imageUploadHandler`, and `onImageUploadError` aren't top-level inputs — pass them via `[init]="{ contentStyle: '...' }"` (see the [`init` options](#init-options) table above). Outputs: `editorChange`, `editorReady`. Works with `ngModel` and `formControlName`.
|
|
125
|
+
|
|
126
|
+
## Development & testing
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
npm install
|
|
130
|
+
npm run build # builds core, react, and angular
|
|
131
|
+
npm test # automated core tests (Vitest + jsdom)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Manual smoke tests
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
# Vanilla demo
|
|
138
|
+
npm run demo
|
|
139
|
+
# open http://localhost:5173/demo/index.html
|
|
140
|
+
|
|
141
|
+
# React example
|
|
142
|
+
npm run demo:react
|
|
143
|
+
# open http://localhost:5173
|
|
144
|
+
|
|
145
|
+
# Angular example
|
|
146
|
+
npm run demo:angular
|
|
147
|
+
# open http://localhost:4200
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
> Run `npm run build` before the Angular demo so the library files are up to date.
|
|
151
|
+
|
|
152
|
+
## Project layout
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
src/
|
|
156
|
+
core/ # framework-agnostic editor engine
|
|
157
|
+
react/ # React wrapper
|
|
158
|
+
angular/ # Angular wrapper + module
|
|
159
|
+
index.ts # Angular library entry point
|
|
160
|
+
examples/
|
|
161
|
+
react/ # runnable React test app
|
|
162
|
+
angular/ # runnable Angular CLI test app
|
|
163
|
+
demo/
|
|
164
|
+
index.html # manual vanilla test page
|
|
165
|
+
test/
|
|
166
|
+
core.test.ts # automated core tests
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Author
|
|
170
|
+
|
|
171
|
+
An Sajin Satheesan <an.sajinsatheesan@gmail.com>
|
|
172
|
+
|
|
173
|
+
## License
|
|
174
|
+
|
|
175
|
+
MIT
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 An Sajin Satheesan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# FableEditor
|
|
2
|
+
|
|
3
|
+
A rich text editor packaged as an npm library with first-class React and Angular wrappers. Built from the original standalone `fable-editor.html`/`fable-editor.js` demo, preserving the full feature set.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
| Feature | Details |
|
|
8
|
+
|---|---|
|
|
9
|
+
| Toolbar & menubar | Configurable via `toolbar`/`menubar` strings (`\|`-separated groups), or use the built-in defaults. Menus: File, Edit, View, Insert, Format, Tools, Table, Help |
|
|
10
|
+
| Tables | Resize handles, row/column insert & delete, cell/row/column/table properties, context toolbar |
|
|
11
|
+
| Paste handling | PowerPaste-style clean paste from Word / Google Docs / Excel |
|
|
12
|
+
| Internationalization | English / Arabic, automatic RTL/LTR switching |
|
|
13
|
+
| Fonts & formatting | Configurable font list (`fontFamilyFormats`), sizes, line-height, word-spacing, text/background color, change case |
|
|
14
|
+
| Custom content styling | `contentStyle` injects scoped CSS into the editable area (e.g. default font/size) |
|
|
15
|
+
| Images | Placeholder upload UI, drag-and-drop, configurable accepted file types (`imageFileTypes`), pluggable async upload handler |
|
|
16
|
+
| Documents | Import `.docx` files, source-code view, print preview |
|
|
17
|
+
| Productivity | Undo/redo, revision history, autosave draft restore, word count, special characters, page breaks |
|
|
18
|
+
| Fullscreen | Toggle fullscreen editing |
|
|
19
|
+
| Help | Built-in shortcuts/help dialog |
|
|
20
|
+
|
|
21
|
+
## Install
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install fable-editor
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
> **Note:** the package ships framework wrappers as optional peer dependencies. Install React or Angular dependencies only if you use those wrappers.
|
|
28
|
+
|
|
29
|
+
## Vanilla / Core
|
|
30
|
+
|
|
31
|
+
Import the CSS once in your app, then create an editor instance:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import 'fable-editor/style.css';
|
|
35
|
+
import { FableEditor } from 'fable-editor';
|
|
36
|
+
|
|
37
|
+
const editor = new FableEditor({
|
|
38
|
+
target: document.getElementById('editor')!,
|
|
39
|
+
language: 'en', // 'en' | 'ar'
|
|
40
|
+
height: 400,
|
|
41
|
+
initialContent: '<p><br></p>',
|
|
42
|
+
onChange: (html) => console.log(html)
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// API
|
|
46
|
+
editor.getContent();
|
|
47
|
+
editor.setContent('<p>Hello</p>');
|
|
48
|
+
editor.insertContent('<strong>bold</strong>');
|
|
49
|
+
editor.setLanguage('ar');
|
|
50
|
+
editor.focus();
|
|
51
|
+
editor.destroy();
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### `init` options
|
|
55
|
+
|
|
56
|
+
| Option | Type | Default | Description |
|
|
57
|
+
|---|---|---|---|
|
|
58
|
+
| `target` | `HTMLElement` | — | Required. Element to mount the editor into. |
|
|
59
|
+
| `language` | `'en' \| 'ar'` | `'en'` | UI language; also switches text direction. |
|
|
60
|
+
| `height` | `number` | `302` | Editable area height in px. |
|
|
61
|
+
| `initialContent` | `string` | `'<p><br></p>'` | Starting HTML content. |
|
|
62
|
+
| `menubar` | `boolean \| string` | `true` | `true`/omitted = default menu set. `false` = hidden. A string is a space-separated subset/reorder, e.g. `'file edit view insert format tools table help'`. |
|
|
63
|
+
| `toolbar` | `boolean \| string` | `true` | `true`/omitted = default toolbar. `false` = hidden. A string lays out the toolbar: `\|` separates groups, spaces separate items, e.g. `'undo redo \| bold italic'`. |
|
|
64
|
+
| `statusbar` | `boolean` | `true` | Show/hide the status bar. |
|
|
65
|
+
| `readonly` | `boolean` | `false` | Disable editing. |
|
|
66
|
+
| `fontFamilyFormats` | `[string, string][]` | built-in list | Overrides the font dropdown. Tuples of `[displayName, cssFontFamilyValue]`. |
|
|
67
|
+
| `contentStyle` | `string` | — | Custom CSS for the editable area. The literal word `body` is scoped to this editor instance; other selectors are used verbatim (your responsibility to scope). |
|
|
68
|
+
| `imageFileTypes` | `string[]` | common image MIME types | `accept` list for the native image file picker. |
|
|
69
|
+
| `imageUploadHandler` | `(file: File) => Promise<string>` | — | Resolve with a URL after uploading; omit to inline images as base64. |
|
|
70
|
+
| `onImageUploadError` | `(error, file) => void` | — | Called when `imageUploadHandler` rejects. |
|
|
71
|
+
| `draftKey` | `string` | current page path | Storage key suffix for autosaved drafts. |
|
|
72
|
+
| `onChange` / `onReady` | functions | — | Content-change and ready callbacks (usually set by the React/Angular wrapper instead). |
|
|
73
|
+
|
|
74
|
+
## React
|
|
75
|
+
|
|
76
|
+
```tsx
|
|
77
|
+
import 'fable-editor/style.css';
|
|
78
|
+
import { FableEditor } from 'fable-editor/react';
|
|
79
|
+
|
|
80
|
+
function App() {
|
|
81
|
+
const [value, setValue] = useState('<p><br></p>');
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<FableEditor
|
|
85
|
+
value={value}
|
|
86
|
+
onChange={setValue}
|
|
87
|
+
language="en"
|
|
88
|
+
height={400}
|
|
89
|
+
/>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Props: `value`, `defaultValue`, `onChange`, `language`, `height`, `menubar`, `toolbar`, `statusbar`, `readonly`, `fontFamilyFormats`, `contentStyle`, `imageFileTypes`, `imageUploadHandler`, `onImageUploadError`, `init`, `className`, `style` (see the [`init` options](#init-options) table above — every option is also a top-level prop). A ref exposes `getContent`, `setContent`, `insertContent`, `setLanguage`, `focus`, `destroy`.
|
|
95
|
+
|
|
96
|
+
## Angular
|
|
97
|
+
|
|
98
|
+
Add to `angular.json` styles:
|
|
99
|
+
|
|
100
|
+
```json
|
|
101
|
+
"styles": [
|
|
102
|
+
"src/styles.css",
|
|
103
|
+
"node_modules/fable-editor/style.css"
|
|
104
|
+
]
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Import the module:
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
import { FableEditorModule } from 'fable-editor/angular';
|
|
111
|
+
|
|
112
|
+
@NgModule({
|
|
113
|
+
imports: [FableEditorModule]
|
|
114
|
+
})
|
|
115
|
+
export class AppModule {}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Use in a template:
|
|
119
|
+
|
|
120
|
+
```html
|
|
121
|
+
<fable-editor [(ngModel)]="content" language="en" [height]="400"></fable-editor>
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Inputs: `language`, `height`, `menubar`, `toolbar`, `statusbar`, `readonly`, `init`. `menubar`/`toolbar` accept a string directly (e.g. `[toolbar]="'undo redo | bold italic'"`); `fontFamilyFormats`, `contentStyle`, `imageFileTypes`, `imageUploadHandler`, and `onImageUploadError` aren't top-level inputs — pass them via `[init]="{ contentStyle: '...' }"` (see the [`init` options](#init-options) table above). Outputs: `editorChange`, `editorReady`. Works with `ngModel` and `formControlName`.
|
|
125
|
+
|
|
126
|
+
## Development & testing
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
npm install
|
|
130
|
+
npm run build # builds core, react, and angular
|
|
131
|
+
npm test # automated core tests (Vitest + jsdom)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Manual smoke tests
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
# Vanilla demo
|
|
138
|
+
npm run demo
|
|
139
|
+
# open http://localhost:5173/demo/index.html
|
|
140
|
+
|
|
141
|
+
# React example
|
|
142
|
+
npm run demo:react
|
|
143
|
+
# open http://localhost:5173
|
|
144
|
+
|
|
145
|
+
# Angular example
|
|
146
|
+
npm run demo:angular
|
|
147
|
+
# open http://localhost:4200
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
> Run `npm run build` before the Angular demo so the library files are up to date.
|
|
151
|
+
|
|
152
|
+
## Project layout
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
src/
|
|
156
|
+
core/ # framework-agnostic editor engine
|
|
157
|
+
react/ # React wrapper
|
|
158
|
+
angular/ # Angular wrapper + module
|
|
159
|
+
index.ts # Angular library entry point
|
|
160
|
+
examples/
|
|
161
|
+
react/ # runnable React test app
|
|
162
|
+
angular/ # runnable Angular CLI test app
|
|
163
|
+
demo/
|
|
164
|
+
index.html # manual vanilla test page
|
|
165
|
+
test/
|
|
166
|
+
core.test.ts # automated core tests
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Author
|
|
170
|
+
|
|
171
|
+
An Sajin Satheesan <an.sajinsatheesan@gmail.com>
|
|
172
|
+
|
|
173
|
+
## License
|
|
174
|
+
|
|
175
|
+
MIT
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { ElementRef, EventEmitter, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core';
|
|
2
|
+
import { EditorInitOptions, EditorLanguage, FableEditorApi } from '../core';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
export declare class FableEditorComponent implements OnInit, OnChanges, OnDestroy {
|
|
5
|
+
container: ElementRef<HTMLDivElement>;
|
|
6
|
+
language: EditorLanguage;
|
|
7
|
+
height: number;
|
|
8
|
+
menubar: boolean | string;
|
|
9
|
+
toolbar: boolean | string;
|
|
10
|
+
statusbar: boolean;
|
|
11
|
+
readonly: boolean;
|
|
12
|
+
init: Partial<Omit<EditorInitOptions, 'target'>>;
|
|
13
|
+
value: string;
|
|
14
|
+
editorChange: EventEmitter<string>;
|
|
15
|
+
editorReady: EventEmitter<FableEditorApi>;
|
|
16
|
+
private editor?;
|
|
17
|
+
private lastContent;
|
|
18
|
+
ngOnInit(): void;
|
|
19
|
+
ngOnChanges(changes: SimpleChanges): void;
|
|
20
|
+
ngOnDestroy(): void;
|
|
21
|
+
getContent(): string;
|
|
22
|
+
setContent(html: string): void;
|
|
23
|
+
insertContent(html: string): void;
|
|
24
|
+
setLanguage(lang: EditorLanguage): void;
|
|
25
|
+
focus(): void;
|
|
26
|
+
writeValue(value: string): void;
|
|
27
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<FableEditorComponent, never>;
|
|
28
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<FableEditorComponent, "fable-editor", never, { "language": { "alias": "language"; "required": false; }; "height": { "alias": "height"; "required": false; }; "menubar": { "alias": "menubar"; "required": false; }; "toolbar": { "alias": "toolbar"; "required": false; }; "statusbar": { "alias": "statusbar"; "required": false; }; "readonly": { "alias": "readonly"; "required": false; }; "init": { "alias": "init"; "required": false; }; "value": { "alias": "value"; "required": false; }; }, { "editorChange": "editorChange"; "editorReady": "editorReady"; }, never, never, true, never>;
|
|
29
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import * as i0 from "@angular/core";
|
|
2
|
+
import * as i1 from "./fable-editor.component";
|
|
3
|
+
export declare class FableEditorModule {
|
|
4
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<FableEditorModule, never>;
|
|
5
|
+
static ɵmod: i0.ɵɵNgModuleDeclaration<FableEditorModule, never, [typeof i1.FableEditorComponent], [typeof i1.FableEditorComponent]>;
|
|
6
|
+
static ɵinj: i0.ɵɵInjectorDeclaration<FableEditorModule>;
|
|
7
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Transcribed group-by-group from the editor's built-in toolbar layout so that
|
|
2
|
+
* `toolbar: true` (the default) renders identically to the hard-coded original. */
|
|
3
|
+
export declare const DEFAULT_TOOLBAR: string;
|
|
4
|
+
/** Transcribed from the editor's built-in menu keys so that `menubar: true` (the
|
|
5
|
+
* default) renders identically to the hard-coded original. */
|
|
6
|
+
export declare const DEFAULT_MENUBAR = "file edit view insert format tools table help";
|
|
7
|
+
export declare const FONTS: [string, string][];
|
|
8
|
+
export declare const SIZES: string[];
|
|
9
|
+
export declare const MIN_FONT_PX = 1;
|
|
10
|
+
export declare const MAX_FONT_PX = 100;
|
|
11
|
+
export declare const LINE_HEIGHTS: string[];
|
|
12
|
+
export declare const WORD_SPACINGS: string[];
|
|
13
|
+
export declare const BLOCKS: [string, string][];
|
|
14
|
+
/** Quick-access text color chips shown directly in the toolbar. */
|
|
15
|
+
export declare const QUICK_COLORS: string[];
|
|
16
|
+
export declare const COLORS: string[];
|
|
17
|
+
export declare const CHARS: string[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function importDocxToHtml(input: File | Blob | ArrayBuffer): Promise<string>;
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { EditorInitOptions, EditorLanguage, FableEditorApi } from './types';
|
|
2
|
+
export declare class FableEditor implements FableEditorApi {
|
|
3
|
+
private static instanceCounter;
|
|
4
|
+
private options;
|
|
5
|
+
private lang;
|
|
6
|
+
private contentClass;
|
|
7
|
+
private contentStyleEl;
|
|
8
|
+
private shell;
|
|
9
|
+
private menubar;
|
|
10
|
+
private toolbar;
|
|
11
|
+
private ed;
|
|
12
|
+
private statusbar;
|
|
13
|
+
private sbHelp;
|
|
14
|
+
private sbWords;
|
|
15
|
+
private imgInput;
|
|
16
|
+
private savedRange;
|
|
17
|
+
private foreColor;
|
|
18
|
+
private backColor;
|
|
19
|
+
private openPop;
|
|
20
|
+
private openSubEl;
|
|
21
|
+
private popAnchor;
|
|
22
|
+
private dlgOvl;
|
|
23
|
+
private tblActive;
|
|
24
|
+
private tblCorner;
|
|
25
|
+
private tblColBars;
|
|
26
|
+
private tblCtx;
|
|
27
|
+
private tblCellMark;
|
|
28
|
+
private tblOpMark;
|
|
29
|
+
private tblOpMarkKind;
|
|
30
|
+
private imgActive;
|
|
31
|
+
private imgHandles;
|
|
32
|
+
private imgCtx;
|
|
33
|
+
private imgHideTimer;
|
|
34
|
+
private phActive;
|
|
35
|
+
private phCtx;
|
|
36
|
+
private phUploadTarget;
|
|
37
|
+
private imageUploadHandler?;
|
|
38
|
+
private onImageUploadError?;
|
|
39
|
+
private contentStyle?;
|
|
40
|
+
private selCtx;
|
|
41
|
+
private docInput;
|
|
42
|
+
private revisions;
|
|
43
|
+
private revTimer;
|
|
44
|
+
private draftTimer;
|
|
45
|
+
private draftStorageKey;
|
|
46
|
+
private listeners;
|
|
47
|
+
private isDestroyed;
|
|
48
|
+
constructor(options: EditorInitOptions);
|
|
49
|
+
private t;
|
|
50
|
+
private tArr;
|
|
51
|
+
private dir;
|
|
52
|
+
private initDOM;
|
|
53
|
+
/** Rewrites the literal word `body` in a user-supplied `contentStyle` string to a
|
|
54
|
+
* selector scoped to this instance's content div. Everything else passes through
|
|
55
|
+
* verbatim, since `.ed` is a plain div in the host page (not an iframe) and an
|
|
56
|
+
* unscoped `body { ... }` rule would otherwise leak onto the host page's body. */
|
|
57
|
+
private scopeContentStyle;
|
|
58
|
+
private bindGrip;
|
|
59
|
+
private bindEvents;
|
|
60
|
+
private on;
|
|
61
|
+
private onDoc;
|
|
62
|
+
private onWin;
|
|
63
|
+
getContent(): string;
|
|
64
|
+
setContent(html: string): void;
|
|
65
|
+
insertContent(html: string): void;
|
|
66
|
+
setLanguage(lang: EditorLanguage): void;
|
|
67
|
+
focus(): void;
|
|
68
|
+
importWordFile(file: File | Blob): Promise<void>;
|
|
69
|
+
restoreDraft(): boolean;
|
|
70
|
+
getRevisions(): Array<{
|
|
71
|
+
time: number;
|
|
72
|
+
html: string;
|
|
73
|
+
}>;
|
|
74
|
+
destroy(): void;
|
|
75
|
+
private saveSel;
|
|
76
|
+
private restoreSel;
|
|
77
|
+
private exec;
|
|
78
|
+
private closestBlock;
|
|
79
|
+
private selectedBlocks;
|
|
80
|
+
private onChange;
|
|
81
|
+
private recordRevision;
|
|
82
|
+
private scheduleRevision;
|
|
83
|
+
private scheduleDraftSave;
|
|
84
|
+
private revisionDlg;
|
|
85
|
+
private closeSub;
|
|
86
|
+
private closePop;
|
|
87
|
+
private openSubFor;
|
|
88
|
+
private popup;
|
|
89
|
+
private menuItems;
|
|
90
|
+
private tbtn;
|
|
91
|
+
/** Font-size increase / decrease step button. */
|
|
92
|
+
private stepBtn;
|
|
93
|
+
/** Toolbar icon button that opens a popup menu anchored to itself. */
|
|
94
|
+
private menuTbtn;
|
|
95
|
+
/** Quick text-color chips plus a "more" chip that opens the full palette. */
|
|
96
|
+
private colorChips;
|
|
97
|
+
private group;
|
|
98
|
+
private tsel;
|
|
99
|
+
private setListStyle;
|
|
100
|
+
private listMenu;
|
|
101
|
+
private colorMenu;
|
|
102
|
+
private applyColor;
|
|
103
|
+
private syncColorSwatches;
|
|
104
|
+
private fontMenu;
|
|
105
|
+
private sizeMenu;
|
|
106
|
+
/** Font sizing needs a non-collapsed range. With a bare caret, grow the
|
|
107
|
+
selection to the word under it; in an empty spot, select a zero-width
|
|
108
|
+
space so the new size applies to what gets typed next. */
|
|
109
|
+
private ensureSizeTarget;
|
|
110
|
+
private applyFontSize;
|
|
111
|
+
private stepFontSize;
|
|
112
|
+
private transformCase;
|
|
113
|
+
private caseMenu;
|
|
114
|
+
private applyBlockStyle;
|
|
115
|
+
private currentBlockStyle;
|
|
116
|
+
private lineHeightMenu;
|
|
117
|
+
private wordSpacingMenu;
|
|
118
|
+
private closestAnchor;
|
|
119
|
+
private linkDlg;
|
|
120
|
+
private blocksMenu;
|
|
121
|
+
private blockLabel;
|
|
122
|
+
private tableGrid;
|
|
123
|
+
private buildTableGridInto;
|
|
124
|
+
private insertTable;
|
|
125
|
+
private setDir;
|
|
126
|
+
/** Splits a toolbar layout string into groups of item names: `|` separates visual
|
|
127
|
+
* groups, whitespace separates items within a group. Empty groups/tokens (e.g. from
|
|
128
|
+
* leading/trailing/doubled separators) are dropped. Pure function. */
|
|
129
|
+
private parseToolbarString;
|
|
130
|
+
/** Maps every supported toolbar item name to a builder for that one control. Each
|
|
131
|
+
* entry is a direct lift of what used to be an inline call in `buildToolbar()` — no
|
|
132
|
+
* behavior changes, just named lookup. Only `forecolor` yields multiple elements
|
|
133
|
+
* (the quick color chips); everything else yields one. */
|
|
134
|
+
private buildToolbarRegistry;
|
|
135
|
+
private buildToolbar;
|
|
136
|
+
private mItem;
|
|
137
|
+
private buildMenubar;
|
|
138
|
+
private tableOp;
|
|
139
|
+
private applyTableProps;
|
|
140
|
+
private tablePropsDlg;
|
|
141
|
+
private currentCell;
|
|
142
|
+
private toHexColor;
|
|
143
|
+
private formRow;
|
|
144
|
+
private formInput;
|
|
145
|
+
private formSelect;
|
|
146
|
+
private formBg;
|
|
147
|
+
private setOrClear;
|
|
148
|
+
private cellPropsDlg;
|
|
149
|
+
private rowPropsDlg;
|
|
150
|
+
private colPropsDlg;
|
|
151
|
+
private closeDlg;
|
|
152
|
+
private dialog;
|
|
153
|
+
private sourceDlg;
|
|
154
|
+
private helpDlg;
|
|
155
|
+
private wordCountDlg;
|
|
156
|
+
private previewDlg;
|
|
157
|
+
private charMap;
|
|
158
|
+
private printDoc;
|
|
159
|
+
private pickImage;
|
|
160
|
+
private insertImagePlaceholder;
|
|
161
|
+
private clearImgPlaceholderSel;
|
|
162
|
+
private selectImgPlaceholder;
|
|
163
|
+
private renderPhCtxButtons;
|
|
164
|
+
private renderPhCtxUrlInput;
|
|
165
|
+
private readImageFileInto;
|
|
166
|
+
/** Runs the host-supplied imageUploadHandler instead of inlining base64,
|
|
167
|
+
* so apps can route the file to S3 / Azure Blob / their own server. */
|
|
168
|
+
private uploadImageFile;
|
|
169
|
+
private replacePlaceholderWithImage;
|
|
170
|
+
private positionImgPhCtx;
|
|
171
|
+
private pickWordDoc;
|
|
172
|
+
private toggleFullscreen;
|
|
173
|
+
private clearTableHandles;
|
|
174
|
+
/** Blue outline around the cell that holds the caret, so it is clear
|
|
175
|
+
which cell / row / column the table operations will target. */
|
|
176
|
+
private positionCellMarker;
|
|
177
|
+
/** Dashed highlight over the whole row / column an operation targets,
|
|
178
|
+
shown while its button or menu item is hovered. */
|
|
179
|
+
private setOpTarget;
|
|
180
|
+
private positionOpMark;
|
|
181
|
+
/** Lowest allowed top for floating context toolbars — keeps them from
|
|
182
|
+
covering the editor menubar/toolbar when the target sits on the first line. */
|
|
183
|
+
private ctxMinTop;
|
|
184
|
+
private ctxBtn;
|
|
185
|
+
private ctxSep;
|
|
186
|
+
private buildTableCtxToolbar;
|
|
187
|
+
private positionTableHandles;
|
|
188
|
+
private selectTableForResize;
|
|
189
|
+
private withNoTextSelect;
|
|
190
|
+
private startTableCornerDrag;
|
|
191
|
+
private startColBarDrag;
|
|
192
|
+
private cancelImgHide;
|
|
193
|
+
private scheduleImgHide;
|
|
194
|
+
private clearImageHandles;
|
|
195
|
+
private selectImage;
|
|
196
|
+
private buildImageCtxToolbar;
|
|
197
|
+
private setImageAlign;
|
|
198
|
+
private applyImgTransform;
|
|
199
|
+
private rotateImage;
|
|
200
|
+
private flipImage;
|
|
201
|
+
private positionImageHandles;
|
|
202
|
+
private startImageResizeDrag;
|
|
203
|
+
private toggleBlock;
|
|
204
|
+
private clearSelToolbar;
|
|
205
|
+
private updateSelToolbar;
|
|
206
|
+
private buildSelToolbar;
|
|
207
|
+
private positionSelToolbar;
|
|
208
|
+
private repositionSelToolbar;
|
|
209
|
+
private currentBlock;
|
|
210
|
+
/** Element whose computed font reflects the current caret, falling back to the editor root before any selection lands inside it. */
|
|
211
|
+
private fontRefEl;
|
|
212
|
+
private currentFont;
|
|
213
|
+
private currentSize;
|
|
214
|
+
private refreshState;
|
|
215
|
+
private countWords;
|
|
216
|
+
private handlePaste;
|
|
217
|
+
private initUI;
|
|
218
|
+
}
|
|
219
|
+
export default FableEditor;
|