@itsammarb/mention-editor 1.0.4 → 1.0.5
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 +35 -27
- package/dist/MentionEditor.d.ts +3 -1
- package/dist/MentionEditor.js +15 -2
- package/dist/MentionMenu.d.ts +2 -1
- package/dist/MentionMenu.js +13 -1
- package/dist/colors.d.ts +9 -0
- package/dist/colors.js +39 -0
- package/dist/index.d.ts +1 -1
- package/dist/types.d.ts +13 -0
- package/llms.txt +5 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -60,6 +60,7 @@ Hello <@a1b2c3d4-0000-0000-0000-000000000001>, welcome.
|
|
|
60
60
|
| `placeholder` | `string` | `undefined` (no placeholder) | Shown only when the document is fully empty (a single empty paragraph) — see [Behavior notes](#behavior-notes). |
|
|
61
61
|
| `rows` | `number` | `undefined` (intrinsic `min-h-11`, ~44px) | Sets `min-height` to `rows * 1.5em`, textarea-`rows`-equivalent. The editor still grows taller than this if content wraps to more lines. |
|
|
62
62
|
| `className` | `string` | `undefined` | Extra class name(s) appended to the root container, after the built-in ones — use this for layout (width, margin) or to override the built-in border/background. |
|
|
63
|
+
| `colors` | `MentionEditorColors` | `undefined` (built-in defaults) | Per-instance color overrides — see [Theme colors](#theme-colors). Omitted keys keep their default. |
|
|
63
64
|
|
|
64
65
|
## Exported utilities
|
|
65
66
|
|
|
@@ -70,6 +71,7 @@ Everything importable from `@itsammarb/mention-editor`:
|
|
|
70
71
|
| `MentionEditor` | component | The editor itself; see [Props](#props). |
|
|
71
72
|
| `MentionEditorProps` | type | Prop types for `MentionEditor`. |
|
|
72
73
|
| `MentionFieldOption` | type | Shape of an entry in `fields`: `{ id: string; label: string }`. |
|
|
74
|
+
| `MentionEditorColors` | type | Shape of the `colors` prop — see [Theme colors](#theme-colors). |
|
|
73
75
|
| `serialize(nodes: Descendant[]): string` | function | Converts a Slate `Descendant[]` tree to the wire-format string. Useful if you need to inspect/generate content outside the component (e.g. server-side). |
|
|
74
76
|
| `deserialize(value: string, fields?: MentionFieldOption[]): Descendant[]` | function | Inverse of `serialize`. `fields` resolves each mention's label; an id not found in `fields` still renders, falling back to the raw id as its label. |
|
|
75
77
|
| `serializeToDiscordMarkup` | function | Deprecated alias of `serialize`, kept for backwards compatibility with the pre-rename API. Prefer `serialize`. |
|
|
@@ -95,37 +97,43 @@ Override any of these by targeting the class directly in your own CSS, or via `c
|
|
|
95
97
|
|
|
96
98
|
### Theme colors
|
|
97
99
|
|
|
98
|
-
|
|
100
|
+
Pass a `colors` prop to override any of the built-in colors, per instance — no external CSS needed:
|
|
99
101
|
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
102
|
+
```tsx
|
|
103
|
+
<MentionEditor
|
|
104
|
+
value={value}
|
|
105
|
+
fields={fields}
|
|
106
|
+
onChange={setValue}
|
|
107
|
+
colors={{
|
|
108
|
+
mentionColor: '#16a34a', // green mention text/underline
|
|
109
|
+
mentionBg: '#dcfce7', // optional pill-style highlight behind a mention
|
|
110
|
+
borderColor: '#db2777',
|
|
111
|
+
menuHighlightBg: '#fde68a', // selected/hovered suggestion row
|
|
112
|
+
}}
|
|
113
|
+
/>
|
|
108
114
|
```
|
|
109
115
|
|
|
110
|
-
|
|
116
|
+
Omitted keys keep their built-in default, and different instances on the same page can have entirely different `colors` without affecting each other — including each instance's own suggestion menu, even though the menu renders through a React portal into `document.body` (a sibling of the editor in the real DOM, not a descendant): `MentionEditor` forwards `colors` to it directly rather than relying on CSS inheritance.
|
|
117
|
+
|
|
118
|
+
| `colors` key | Controls | Light default | Dark default |
|
|
111
119
|
| --- | --- | --- | --- |
|
|
112
|
-
|
|
|
113
|
-
|
|
|
114
|
-
|
|
|
115
|
-
|
|
|
116
|
-
|
|
|
117
|
-
|
|
|
118
|
-
|
|
|
119
|
-
|
|
|
120
|
-
|
|
|
121
|
-
|
|
|
122
|
-
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
**
|
|
120
|
+
| `bg` | Editor container background | white | neutral-900 |
|
|
121
|
+
| `textColor` | Editor body text color | gray-900 | gray-100 |
|
|
122
|
+
| `borderColor` | Container border (normal state) | gray-300 | neutral-700 |
|
|
123
|
+
| `borderColorError` | Container border when `isError` | red-500 | red-500 (same in both) |
|
|
124
|
+
| `placeholderColor` | Placeholder text color | gray-400 | neutral-500 |
|
|
125
|
+
| `mentionColor` | A mention's text/underline color | blue-600 | blue-400 |
|
|
126
|
+
| `mentionBg` | Background behind a mention (e.g. a pill highlight) | transparent | transparent |
|
|
127
|
+
| `menuBg` | Suggestion menu background | white | neutral-800 |
|
|
128
|
+
| `menuBorderColor` | Suggestion menu border | gray-300 | neutral-700 |
|
|
129
|
+
| `menuTextColor` | Suggestion menu row text | gray-900 | gray-100 |
|
|
130
|
+
| `menuHighlightBg` | Selected/hovered suggestion row background | indigo-50 | neutral-700 |
|
|
131
|
+
|
|
132
|
+
A `colors` value applies in both light and dark mode (the fallback is what differs by scheme, not your override) — if you want genuinely different colors per scheme, read `window.matchMedia('(prefers-color-scheme: dark)')` (or your app's own dark-mode state) and pass a different `colors` object accordingly.
|
|
133
|
+
|
|
134
|
+
**Dark mode**: the built-in dark defaults (used when `colors` doesn't set a given key) respond to the OS/browser's `prefers-color-scheme: dark`, *not* a manually-toggled `.dark` class (e.g. from `next-themes` or a similar library). If your app drives dark mode via a class rather than OS preference, either pass `colors` explicitly (bypassing the scheme-based default entirely), or drive it from your own dark-mode state as described above.
|
|
135
|
+
|
|
136
|
+
**Advanced — global CSS override**: under the hood, `colors` works by setting CSS custom properties (`--mention-editor-*`) inline. You can also set these directly in your own global CSS (`:root { --mention-editor-mention-color: ...; }`) as a page-wide fallback for instances that don't pass `colors` at all — an inline `colors` value always takes precedence over a global CSS one. Global CSS declarations must be scoped to `:root`/`html`/`body` (not `.mention-editor`) for the same portal reason as above.
|
|
129
137
|
|
|
130
138
|
If your own app happens to use Tailwind and you want to reuse its utility classes against this component's internal DOM (beyond what `className` on the root reaches), you can optionally point your app's Tailwind config/`@source` at `node_modules/@itsammarb/mention-editor/dist` — but this is purely an opt-in extra, not required for the component to work or look right.
|
|
131
139
|
|
package/dist/MentionEditor.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { MentionFieldOption } from './types';
|
|
2
|
+
import { MentionFieldOption, MentionEditorColors } from './types';
|
|
3
3
|
export interface MentionEditorProps {
|
|
4
4
|
/** Wire-format string, e.g. `"Hello <@a1b2c3d4-...>, pay rent."` */
|
|
5
5
|
value: string;
|
|
@@ -13,5 +13,7 @@ export interface MentionEditorProps {
|
|
|
13
13
|
/** Approximate visible height, textarea-`rows`-equivalent. */
|
|
14
14
|
rows?: number;
|
|
15
15
|
className?: string;
|
|
16
|
+
/** Per-instance color overrides; omitted keys keep the built-in default. */
|
|
17
|
+
colors?: MentionEditorColors;
|
|
16
18
|
}
|
|
17
19
|
export declare function MentionEditor(props: MentionEditorProps): React.JSX.Element;
|
package/dist/MentionEditor.js
CHANGED
|
@@ -8,9 +8,19 @@ const slate_react_1 = require("slate-react");
|
|
|
8
8
|
const slate_history_1 = require("slate-history");
|
|
9
9
|
const types_1 = require("./types");
|
|
10
10
|
const serialize_1 = require("./serialize");
|
|
11
|
+
const colors_1 = require("./colors");
|
|
11
12
|
const MentionMenu_1 = require("./MentionMenu");
|
|
13
|
+
const EDITOR_COLOR_KEYS = [
|
|
14
|
+
'bg',
|
|
15
|
+
'textColor',
|
|
16
|
+
'borderColor',
|
|
17
|
+
'borderColorError',
|
|
18
|
+
'placeholderColor',
|
|
19
|
+
'mentionColor',
|
|
20
|
+
'mentionBg',
|
|
21
|
+
];
|
|
12
22
|
function MentionEditor(props) {
|
|
13
|
-
const { value, fields, onChange, dir, disabled = false, isError = false, placeholder, rows, className, } = props;
|
|
23
|
+
const { value, fields, onChange, dir, disabled = false, isError = false, placeholder, rows, className, colors, } = props;
|
|
14
24
|
// `generation` forces a full remount of the Slate editor instance whenever the
|
|
15
25
|
// `value` prop changes for a reason *other* than our own onChange echoing back
|
|
16
26
|
// down (e.g. the consumer swaps to editing a different record). See the
|
|
@@ -149,7 +159,10 @@ function MentionEditor(props) {
|
|
|
149
159
|
.filter(Boolean)
|
|
150
160
|
.join(' ');
|
|
151
161
|
const editableStyle = rows ? { minHeight: `${rows * 1.5}em` } : undefined;
|
|
152
|
-
|
|
162
|
+
// Set on the root: cascades naturally to Editable/mention/placeholder since
|
|
163
|
+
// they're real DOM descendants (unlike the portaled menu -- see MentionMenu).
|
|
164
|
+
const rootStyle = (0, colors_1.colorsToCssVars)(colors, EDITOR_COLOR_KEYS);
|
|
165
|
+
return ((0, jsx_runtime_1.jsx)(slate_react_1.Slate, { editor: editor, value: slateValue, onChange: handleChange, children: (0, jsx_runtime_1.jsxs)("div", { className: rootClassName, style: rootStyle, children: [(0, jsx_runtime_1.jsx)(slate_react_1.Editable, { className: "mention-editor__editable min-h-11 px-3 py-2 text-base leading-6 outline-none text-(--mention-editor-text-color,var(--color-gray-900)) dark:text-(--mention-editor-text-color,var(--color-gray-100))", style: editableStyle, dir: dir, readOnly: disabled, onKeyDown: handleKeyDown, placeholder: placeholder, renderElement: (elementProps) => (0, jsx_runtime_1.jsx)(Element, { ...elementProps }), renderPlaceholder: renderPlaceholder }), target && ((0, jsx_runtime_1.jsx)(MentionMenu_1.MentionMenu, { fields: filteredFields, selectedIndex: index, onSelect: selectField, onHover: setIndex, targetRect: menuTargetRect, colors: colors }))] }) }, generation));
|
|
153
166
|
}
|
|
154
167
|
// slate-react's default placeholder renders at a fixed dim-black opacity,
|
|
155
168
|
// which reads poorly in dark mode. Overriding it lets the placeholder use
|
package/dist/MentionMenu.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { MentionFieldOption } from './types';
|
|
2
|
+
import { MentionFieldOption, MentionEditorColors } from './types';
|
|
3
3
|
interface MentionMenuProps {
|
|
4
4
|
fields: MentionFieldOption[];
|
|
5
5
|
selectedIndex: number;
|
|
6
6
|
onSelect: (field: MentionFieldOption) => void;
|
|
7
7
|
onHover: (index: number) => void;
|
|
8
8
|
targetRect: DOMRect | null;
|
|
9
|
+
colors?: MentionEditorColors;
|
|
9
10
|
}
|
|
10
11
|
export declare const MentionMenu: React.FC<MentionMenuProps>;
|
|
11
12
|
export {};
|
package/dist/MentionMenu.js
CHANGED
|
@@ -3,7 +3,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.MentionMenu = void 0;
|
|
4
4
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
5
|
const react_dom_1 = require("react-dom");
|
|
6
|
-
const
|
|
6
|
+
const colors_1 = require("./colors");
|
|
7
|
+
const MENU_COLOR_KEYS = [
|
|
8
|
+
'menuBg',
|
|
9
|
+
'menuBorderColor',
|
|
10
|
+
'menuTextColor',
|
|
11
|
+
'menuHighlightBg',
|
|
12
|
+
];
|
|
13
|
+
const MentionMenu = ({ fields, selectedIndex, onSelect, onHover, targetRect, colors, }) => {
|
|
7
14
|
if (!targetRect || fields.length === 0)
|
|
8
15
|
return null;
|
|
9
16
|
// Rendered into document.body via a portal and positioned with `fixed` using
|
|
@@ -13,10 +20,15 @@ const MentionMenu = ({ fields, selectedIndex, onSelect, onHover, targetRect, })
|
|
|
13
20
|
// another modal: portaling to <body> means there's no transformed/positioned
|
|
14
21
|
// ancestor between the menu and the viewport to throw off `fixed` coordinates.
|
|
15
22
|
// The caller (MentionEditor) keeps `targetRect` fresh across scroll/resize.
|
|
23
|
+
//
|
|
24
|
+
// `colors` has to be applied here too, not just on `.mention-editor` -- this
|
|
25
|
+
// element is a portal child of `document.body`, a sibling of `.mention-editor`
|
|
26
|
+
// in the real DOM, so it wouldn't inherit color overrides set there.
|
|
16
27
|
return (0, react_dom_1.createPortal)((0, jsx_runtime_1.jsx)("div", { className: "mention-editor__menu z-9999 w-70 max-h-75 overflow-y-auto rounded-md border py-1 shadow-lg border-(--mention-editor-menu-border-color,var(--color-gray-300)) dark:border-(--mention-editor-menu-border-color,var(--color-neutral-700)) bg-(--mention-editor-menu-bg,var(--color-white)) dark:bg-(--mention-editor-menu-bg,var(--color-neutral-800))", style: {
|
|
17
28
|
position: 'fixed',
|
|
18
29
|
top: targetRect.bottom + 8,
|
|
19
30
|
left: targetRect.left,
|
|
31
|
+
...(0, colors_1.colorsToCssVars)(colors, MENU_COLOR_KEYS),
|
|
20
32
|
}, children: fields.map((field, i) => ((0, jsx_runtime_1.jsx)("div", { className: 'mention-editor__menu-item cursor-pointer px-3 py-2' +
|
|
21
33
|
' text-(--mention-editor-menu-text-color,var(--color-gray-900))' +
|
|
22
34
|
' dark:text-(--mention-editor-menu-text-color,var(--color-gray-100))' +
|
package/dist/colors.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CSSProperties } from 'react';
|
|
2
|
+
import { MentionEditorColors } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Builds an inline `style` object setting only the CSS custom properties for
|
|
5
|
+
* the given keys that are actually present in `colors` -- omitted keys are
|
|
6
|
+
* left unset entirely, so the built-in fallback in the compiled CSS still
|
|
7
|
+
* applies for them.
|
|
8
|
+
*/
|
|
9
|
+
export declare const colorsToCssVars: (colors: MentionEditorColors | undefined, keys: (keyof MentionEditorColors)[]) => CSSProperties;
|
package/dist/colors.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.colorsToCssVars = void 0;
|
|
4
|
+
// Maps each `colors` prop key to the CSS custom property the compiled
|
|
5
|
+
// Tailwind classes read via `var(--x, <built-in fallback>)`. Setting the
|
|
6
|
+
// property inline (rather than requiring a consumer to write global CSS)
|
|
7
|
+
// wins over the fallback automatically -- and, being inline, wins regardless
|
|
8
|
+
// of stylesheet load order, since there's no competing external rule to race.
|
|
9
|
+
const COLOR_VARS = {
|
|
10
|
+
bg: '--mention-editor-bg',
|
|
11
|
+
textColor: '--mention-editor-text-color',
|
|
12
|
+
borderColor: '--mention-editor-border-color',
|
|
13
|
+
borderColorError: '--mention-editor-border-color-error',
|
|
14
|
+
placeholderColor: '--mention-editor-placeholder-color',
|
|
15
|
+
mentionColor: '--mention-editor-mention-color',
|
|
16
|
+
mentionBg: '--mention-editor-mention-bg',
|
|
17
|
+
menuBg: '--mention-editor-menu-bg',
|
|
18
|
+
menuBorderColor: '--mention-editor-menu-border-color',
|
|
19
|
+
menuTextColor: '--mention-editor-menu-text-color',
|
|
20
|
+
menuHighlightBg: '--mention-editor-menu-highlight-bg',
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Builds an inline `style` object setting only the CSS custom properties for
|
|
24
|
+
* the given keys that are actually present in `colors` -- omitted keys are
|
|
25
|
+
* left unset entirely, so the built-in fallback in the compiled CSS still
|
|
26
|
+
* applies for them.
|
|
27
|
+
*/
|
|
28
|
+
const colorsToCssVars = (colors, keys) => {
|
|
29
|
+
if (!colors)
|
|
30
|
+
return {};
|
|
31
|
+
const style = {};
|
|
32
|
+
for (const key of keys) {
|
|
33
|
+
const value = colors[key];
|
|
34
|
+
if (value)
|
|
35
|
+
style[COLOR_VARS[key]] = value;
|
|
36
|
+
}
|
|
37
|
+
return style;
|
|
38
|
+
};
|
|
39
|
+
exports.colorsToCssVars = colorsToCssVars;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { MentionEditor } from './MentionEditor';
|
|
2
2
|
export type { MentionEditorProps } from './MentionEditor';
|
|
3
3
|
export { serialize, deserialize, serializeToDiscordMarkup } from './serialize';
|
|
4
|
-
export type { MentionFieldOption } from './types';
|
|
4
|
+
export type { MentionFieldOption, MentionEditorColors } from './types';
|
|
5
5
|
export { INITIAL_VALUE, MENTION_OPEN, MENTION_CLOSE } from './types';
|
package/dist/types.d.ts
CHANGED
|
@@ -27,3 +27,16 @@ declare module 'slate' {
|
|
|
27
27
|
export declare const INITIAL_VALUE: Descendant[];
|
|
28
28
|
export declare const MENTION_OPEN = "<@";
|
|
29
29
|
export declare const MENTION_CLOSE = ">";
|
|
30
|
+
export interface MentionEditorColors {
|
|
31
|
+
bg?: string;
|
|
32
|
+
textColor?: string;
|
|
33
|
+
borderColor?: string;
|
|
34
|
+
borderColorError?: string;
|
|
35
|
+
placeholderColor?: string;
|
|
36
|
+
mentionColor?: string;
|
|
37
|
+
mentionBg?: string;
|
|
38
|
+
menuBg?: string;
|
|
39
|
+
menuBorderColor?: string;
|
|
40
|
+
menuTextColor?: string;
|
|
41
|
+
menuHighlightBg?: string;
|
|
42
|
+
}
|
package/llms.txt
CHANGED
|
@@ -45,10 +45,11 @@ function Example() {
|
|
|
45
45
|
| `placeholder` | `string` | no | Shows only when the doc is exactly one empty paragraph. |
|
|
46
46
|
| `rows` | `number` | no | `min-height = rows * 1.5em`, textarea-rows-equivalent; grows taller if content wraps further. |
|
|
47
47
|
| `className` | `string` | no | Appended to the root container's class list. |
|
|
48
|
+
| `colors` | `MentionEditorColors` | no | Per-instance color overrides; see Theme colors below. Omitted keys keep the default. |
|
|
48
49
|
|
|
49
50
|
## Other exports
|
|
50
51
|
|
|
51
|
-
`MentionEditorProps` (type), `MentionFieldOption` (type), `INITIAL_VALUE` (empty-doc Slate value), `MENTION_OPEN` / `MENTION_CLOSE` (the `'<@'` / `'>'` delimiter constants), `serializeToDiscordMarkup` (deprecated alias of `serialize`).
|
|
52
|
+
`MentionEditorProps` (type), `MentionFieldOption` (type), `MentionEditorColors` (type), `INITIAL_VALUE` (empty-doc Slate value), `MENTION_OPEN` / `MENTION_CLOSE` (the `'<@'` / `'>'` delimiter constants), `serializeToDiscordMarkup` (deprecated alias of `serialize`).
|
|
52
53
|
|
|
53
54
|
## Behavior that is fixed, not configurable via props
|
|
54
55
|
|
|
@@ -64,13 +65,11 @@ function Example() {
|
|
|
64
65
|
|
|
65
66
|
`.mention-editor` (root), `.mention-editor__editable`, `.mention-editor__paragraph`, `.mention-editor__mention`, `.mention-editor__menu`, `.mention-editor__menu-item`.
|
|
66
67
|
|
|
67
|
-
## Theme colors
|
|
68
|
+
## Theme colors
|
|
68
69
|
|
|
69
|
-
|
|
70
|
+
Use the `colors` prop, not CSS, to retheme -- e.g. `colors={{ mentionColor: '#16a34a', mentionBg: '#dcfce7', borderColor: '#db2777', menuHighlightBg: '#fde68a' }}`. Keys: `bg`, `textColor`, `borderColor`, `borderColorError`, `placeholderColor`, `mentionColor`, `mentionBg`, `menuBg`, `menuBorderColor`, `menuTextColor`, `menuHighlightBg`. Omitted keys keep the built-in default (which itself differs light vs. dark, following OS `prefers-color-scheme`). Each editor instance's `colors` is independent, and is correctly forwarded to that instance's own suggestion menu even though the menu renders through a React portal into `document.body`.
|
|
70
71
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
Each has a light/dark default baked in as a `var(--x, fallback)` on the relevant utility class (no competing `:root` declaration in this package's own CSS), so setting one always wins regardless of stylesheet load order, and applies in both light and dark mode unless you scope your own override inside a `prefers-color-scheme` media query.
|
|
72
|
+
Advanced: `colors` works by setting `--mention-editor-*` CSS custom properties inline; you can also set these directly in your own global CSS at `:root`/`html`/`body` scope (not `.mention-editor` -- the portaled menu sits outside it) as a page-wide fallback. An inline `colors` value always wins over a global CSS one.
|
|
74
73
|
|
|
75
74
|
## Full docs
|
|
76
75
|
|