@wistia/rich-text-editor 0.0.0-stage → 0.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/README.md +148 -2
- package/dist/index.d.ts +116 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1231 -0
- package/dist/index.js.map +1 -0
- package/package.json +88 -4
package/README.md
CHANGED
|
@@ -1,3 +1,149 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @wistia/rich-text-editor
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A rich text editor for Wistia apps. It is built on [Slate](https://docs.slatejs.org/).
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
yarn add @wistia/rich-text-editor
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The package needs these peer dependencies: `react`, `react-dom`, and `styled-components` 6.
|
|
12
|
+
|
|
13
|
+
## Fonts
|
|
14
|
+
|
|
15
|
+
The editor does not load a font. Render it inside `UIProvider` from `@wistia/ui` or `VHSProvider`
|
|
16
|
+
from `@wistia/vhs`:
|
|
17
|
+
|
|
18
|
+
- `UIProvider` defines `--wui-typography-family-default`. The editor uses that font.
|
|
19
|
+
- `VHSProvider` sets the Wistia font on `body`. The editor inherits it.
|
|
20
|
+
|
|
21
|
+
Without a provider, the editor inherits the font of the page. To choose a font yourself, set
|
|
22
|
+
`--rte-font-family` on the editor or on a parent.
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
import { RichTextEditor } from '@wistia/rich-text-editor';
|
|
28
|
+
|
|
29
|
+
<RichTextEditor
|
|
30
|
+
aria-label="Description"
|
|
31
|
+
defaultValue="<p>Join us for a <strong>live tour</strong>.</p>"
|
|
32
|
+
onBlur={(nodes) => save(nodes)}
|
|
33
|
+
/>;
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Give the editor a name. Use `aria-label`, `aria-labelledby`, or a `<label for>` that points at its
|
|
37
|
+
`id`.
|
|
38
|
+
|
|
39
|
+
`defaultValue` takes an HTML string or editor nodes. The editor reads it on the first render only.
|
|
40
|
+
|
|
41
|
+
The editor reads HTML with the browser's `DOMParser`. On a server, `DOMParser` does not exist, so
|
|
42
|
+
an HTML `defaultValue` and `rteDeserializeHTML` throw there. For server rendering, give editor nodes,
|
|
43
|
+
or render the editor in the browser only.
|
|
44
|
+
|
|
45
|
+
## Forms
|
|
46
|
+
|
|
47
|
+
With `name`, the editor adds a hidden input. Its value is the content as HTML. A native form
|
|
48
|
+
submission and `FormData` include it.
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
<form onSubmit={handleSubmit}>
|
|
52
|
+
<RichTextEditor
|
|
53
|
+
aria-label="Description"
|
|
54
|
+
name="description"
|
|
55
|
+
/>
|
|
56
|
+
</form>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Inside a `FormField` from `@wistia/ui`, the field gives the editor its `id`, `name`, and
|
|
60
|
+
description. The label names the editor. A click on the label puts the cursor in the editor.
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
<Form
|
|
64
|
+
action={save}
|
|
65
|
+
aria-label="Event details"
|
|
66
|
+
>
|
|
67
|
+
<FormField
|
|
68
|
+
description="Shown on the registration page"
|
|
69
|
+
label="Description"
|
|
70
|
+
name="description"
|
|
71
|
+
>
|
|
72
|
+
<RichTextEditor />
|
|
73
|
+
</FormField>
|
|
74
|
+
</Form>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
A disabled editor does not send its content with the form.
|
|
78
|
+
|
|
79
|
+
## Controlled content
|
|
80
|
+
|
|
81
|
+
Use `value` and `onChange` to keep the content in state. The editor reads `value` on the first
|
|
82
|
+
render. After that, it reads `value` again only in two cases:
|
|
83
|
+
|
|
84
|
+
- `value` is the empty value: `[{ type: 'paragraph', children: [{ text: '' }] }]`.
|
|
85
|
+
- `useExternalValue` is `true`. Set it when code outside the editor changes the content, for
|
|
86
|
+
example a reset button.
|
|
87
|
+
|
|
88
|
+
The editor skips a `value` that matches content it sent through `onChange` in the last 10 seconds.
|
|
89
|
+
So a slow save that sends an older `onChange` value back does not undo typing or move the cursor.
|
|
90
|
+
|
|
91
|
+
## HTML
|
|
92
|
+
|
|
93
|
+
`onChange` and `onBlur` give editor nodes. Two helpers change nodes to HTML and back:
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
import { rteDeserializeHTML, rteSerializeToHTML } from '@wistia/rich-text-editor';
|
|
97
|
+
|
|
98
|
+
const html = rteSerializeToHTML(nodes);
|
|
99
|
+
const nodesAgain = rteDeserializeHTML(html);
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Formats
|
|
103
|
+
|
|
104
|
+
`enabledFormats` sets the toolbar buttons. Without it, the toolbar shows every format.
|
|
105
|
+
|
|
106
|
+
```tsx
|
|
107
|
+
<RichTextEditor
|
|
108
|
+
aria-label="Description"
|
|
109
|
+
enabledFormats={['BOLD', 'ITALIC', 'HEADING_1', 'LINK', 'BULLETED_LIST']}
|
|
110
|
+
/>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
`rteFormatTypes` lists every format: `BOLD`, `ITALIC`, `UNDERLINE`, `STRIKETHROUGH`, `HEADING_1`,
|
|
114
|
+
`HEADING_2`, `BLOCKQUOTE`, `NUMBERED_LIST`, `BULLETED_LIST`, `LINK`, and `IMAGE`. The order of the
|
|
115
|
+
list does not change the order of the toolbar.
|
|
116
|
+
|
|
117
|
+
- `LINK`: select text, then click the button. The editor asks for a URL. Links open in a new tab.
|
|
118
|
+
- `IMAGE`: click the button. The editor asks for an image URL and adds the image as its own block.
|
|
119
|
+
The URL must contain one of `jpg`, `jpeg`, `png`, `gif`, `webp`, `avif`, `svg`, `tiff`, `bmp`, or
|
|
120
|
+
`dcm`. If it does not, the editor shows a message and does not change. To remove an image, select
|
|
121
|
+
it and press Backspace or Delete.
|
|
122
|
+
|
|
123
|
+
## Custom properties
|
|
124
|
+
|
|
125
|
+
Set these CSS custom properties on the editor (`style` or `className`) or on any parent:
|
|
126
|
+
|
|
127
|
+
| Property | Default | Changes |
|
|
128
|
+
| ----------------------------- | -------------------- | ---------------------------------------------------------- |
|
|
129
|
+
| `--rte-accent-color` | `#2949e5` | links, active toolbar buttons, the selected image line |
|
|
130
|
+
| `--rte-background` | `#fff` | the background of the text area and toolbar |
|
|
131
|
+
| `--rte-border-color` | `#c4c4c8` | the outer border and the line below the toolbar |
|
|
132
|
+
| `--rte-border-radius` | `4px` | the corners of the outer border |
|
|
133
|
+
| `--rte-focus-color` | `--rte-accent-color` | the outline of the outer box while the text area has focus |
|
|
134
|
+
| `--rte-font-family` | the provider font | the font of the content |
|
|
135
|
+
| `--rte-icon-color` | `#62626c` | inactive toolbar buttons |
|
|
136
|
+
| `--rte-text-color` | `#37373c` | the content |
|
|
137
|
+
| `--rte-toolbar-button-radius` | `1000px` | the corners of toolbar buttons |
|
|
138
|
+
|
|
139
|
+
## Development
|
|
140
|
+
|
|
141
|
+
Run these commands from the root of the repository:
|
|
142
|
+
|
|
143
|
+
```sh
|
|
144
|
+
yarn storybook:rich-text-editor
|
|
145
|
+
yarn vp run @wistia/rich-text-editor#test
|
|
146
|
+
yarn vp run @wistia/rich-text-editor#typecheck
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Storybook runs at <http://127.0.0.1:6009>.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
|
|
2
|
+
import { AriaAttributes, CSSProperties, FC } from "react";
|
|
3
|
+
import { Editor } from "slate";
|
|
4
|
+
import "slate-history";
|
|
5
|
+
import { ReactEditor } from "slate-react";
|
|
6
|
+
//#region src/components/RichTextEditor/types.d.ts
|
|
7
|
+
type NodeElementObject = {
|
|
8
|
+
attributes?: Record<string, string>;
|
|
9
|
+
children?: NodeLeafObject[];
|
|
10
|
+
type?: string;
|
|
11
|
+
url?: string;
|
|
12
|
+
};
|
|
13
|
+
type NodeObject = NodeElementObject | NodeLeafObject;
|
|
14
|
+
type NodeLeafObject = {
|
|
15
|
+
attributes?: Record<string, string>;
|
|
16
|
+
bold?: boolean;
|
|
17
|
+
code?: boolean;
|
|
18
|
+
italic?: boolean;
|
|
19
|
+
parent?: NodeObject;
|
|
20
|
+
children?: NodeLeafObject[];
|
|
21
|
+
strikethrough?: boolean;
|
|
22
|
+
text?: string;
|
|
23
|
+
type?: string;
|
|
24
|
+
url?: string;
|
|
25
|
+
underline?: boolean;
|
|
26
|
+
};
|
|
27
|
+
//#endregion
|
|
28
|
+
//#region src/components/RichTextEditor/utilities/rteFormatTypes.d.ts
|
|
29
|
+
export declare const rteFormatTypes: {
|
|
30
|
+
readonly BOLD: "BOLD";
|
|
31
|
+
readonly ITALIC: "ITALIC";
|
|
32
|
+
readonly UNDERLINE: "UNDERLINE";
|
|
33
|
+
readonly STRIKETHROUGH: "STRIKETHROUGH";
|
|
34
|
+
readonly HEADING_1: "HEADING_1";
|
|
35
|
+
readonly HEADING_2: "HEADING_2";
|
|
36
|
+
readonly BLOCKQUOTE: "BLOCKQUOTE";
|
|
37
|
+
readonly NUMBERED_LIST: "NUMBERED_LIST";
|
|
38
|
+
readonly BULLETED_LIST: "BULLETED_LIST";
|
|
39
|
+
readonly LINK: "LINK";
|
|
40
|
+
readonly IMAGE: "IMAGE";
|
|
41
|
+
};
|
|
42
|
+
//#endregion
|
|
43
|
+
//#region src/components/RichTextEditor/RichTextEditor.d.ts
|
|
44
|
+
type RichTextEditorProps = AriaAttributes & {
|
|
45
|
+
/**
|
|
46
|
+
* Same as `aria-labelledby`. `aria-labelledby` wins when both are set.
|
|
47
|
+
*/
|
|
48
|
+
ariaLabelledBy?: string | undefined;
|
|
49
|
+
/**
|
|
50
|
+
* Class name for the outer box (border, toolbar, and text area)
|
|
51
|
+
*/
|
|
52
|
+
className?: string | undefined;
|
|
53
|
+
/**
|
|
54
|
+
* Test id for the outer box
|
|
55
|
+
*/
|
|
56
|
+
"data-testid"?: string;
|
|
57
|
+
/**
|
|
58
|
+
* Starting content when `value` is not set: an HTML string or editor nodes.
|
|
59
|
+
* The editor does not read it again after the first render.
|
|
60
|
+
*/
|
|
61
|
+
defaultValue?: NodeObject[] | string | undefined;
|
|
62
|
+
/**
|
|
63
|
+
* Determines whether editor is clickable/read-only
|
|
64
|
+
*/
|
|
65
|
+
disabled?: boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Formats to enable in editor, restricted to rteFormatTypes
|
|
68
|
+
*/
|
|
69
|
+
enabledFormats?: (keyof typeof rteFormatTypes)[];
|
|
70
|
+
/**
|
|
71
|
+
* Custom height of editor
|
|
72
|
+
*/
|
|
73
|
+
height?: string | undefined;
|
|
74
|
+
/**
|
|
75
|
+
* Id of the text area. A `<label for>` with this id names the text area and focuses it on click.
|
|
76
|
+
*/
|
|
77
|
+
id?: string | undefined;
|
|
78
|
+
/**
|
|
79
|
+
* Adds a hidden input with this name. Its value is the content as HTML, so a native form
|
|
80
|
+
* submission or `FormData` includes it.
|
|
81
|
+
*/
|
|
82
|
+
name?: string | undefined;
|
|
83
|
+
/**
|
|
84
|
+
* Function to be called when editor loses focus
|
|
85
|
+
*/
|
|
86
|
+
onBlur?: ((value: NodeElementObject[]) => void) | undefined;
|
|
87
|
+
/**
|
|
88
|
+
* Function to be called after editor state update
|
|
89
|
+
*/
|
|
90
|
+
onChange?: ((value: NodeElementObject[]) => void) | undefined;
|
|
91
|
+
/**
|
|
92
|
+
* Placeholder text
|
|
93
|
+
*/
|
|
94
|
+
placeholder?: string;
|
|
95
|
+
/**
|
|
96
|
+
* Inline styles for the outer box
|
|
97
|
+
*/
|
|
98
|
+
style?: CSSProperties | undefined;
|
|
99
|
+
/**
|
|
100
|
+
* Whether to update the editor when the value prop changes
|
|
101
|
+
*/
|
|
102
|
+
useExternalValue?: boolean;
|
|
103
|
+
/**
|
|
104
|
+
* Content of the editor. The editor reads it on the first render. After that, it reads it again
|
|
105
|
+
* when it is the empty value (to clear the editor), or when `useExternalValue` is true.
|
|
106
|
+
*/
|
|
107
|
+
value?: NodeObject[] | undefined;
|
|
108
|
+
};
|
|
109
|
+
export declare const RichTextEditor: FC<RichTextEditorProps>;
|
|
110
|
+
//#endregion
|
|
111
|
+
//#region src/components/RichTextEditor/serializers.d.ts
|
|
112
|
+
export declare const rteSerializeToHTML: (value: NodeObject[]) => string | null;
|
|
113
|
+
export declare const rteDeserializeHTML: (htmlString: string) => (NodeObject | string | null)[] | NodeObject | string | null | undefined;
|
|
114
|
+
//#endregion
|
|
115
|
+
export type { NodeElementObject, NodeLeafObject, NodeObject, RichTextEditorProps };
|
|
116
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/components/RichTextEditor/types.ts","../src/components/RichTextEditor/utilities/rteFormatTypes.ts","../src/components/RichTextEditor/RichTextEditor.tsx","../src/components/RichTextEditor/serializers.ts"],"mappings":";;;;;;KAIY;EACV,aAAa;EACb,WAAW;EACX;EACA;;KAGU,aAAa,oBAAoB;KAEjC;EACV,aAAa;EACb;EACA;EACA;EACA,SAAS;EACT,WAAW;EACX;EACA;EACA;EACA;EACA;;;;qBCxBW;WACF;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;;;;KCyDC,sBAAsB;;;;EAIhC;;;;EAIA;;;;EAIA;;;;;EAKA,eAAe;;;;EAIf;;;;EAIA,+BAA+B;;;;EAI/B;;;;EAIA;;;;;EAKA;;;;EAIA,WAAW,OAAO;;;;EAIlB,aAAa,OAAO;;;;EAIpB;;;;EAIA,QAAQ;;;;EAIR;;;;;EAKA,QAAQ;;qBAgBG,gBAAgB,GAAG;;;qBCXnB,qBAAsB,OAAO;qBAgP7B,qBACX,wBACE,gCAAgC"}
|