@rcarls/rc-markdown-editor 0.2.0 → 0.3.1
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 +7 -2
- package/dist/custom-elements.json +206 -64
- package/dist/rc-editor-toolbar-CcTLdYIt.js +5213 -0
- package/dist/{rc-editor-toolbar-B74gMsna.js.map → rc-editor-toolbar-CcTLdYIt.js.map} +1 -1
- package/dist/rc-markdown-editor-define.js +1 -1
- package/dist/rc-markdown-editor.js +1 -1
- package/dist/types/packages/rc-markdown-editor/src/formatting.d.ts +10 -0
- package/dist/types/packages/rc-markdown-editor/src/formatting.test.d.ts +1 -0
- package/dist/types/packages/rc-markdown-editor/src/rc-editor-toolbar.d.ts +11 -11
- package/dist/types/packages/rc-markdown-editor/src/rc-markdown-editor.d.ts +58 -41
- package/package.json +8 -10
- package/dist/demo.css +0 -85
- package/dist/demo.js +0 -40
- package/dist/rc-editor-toolbar-B74gMsna.js +0 -5020
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "@rcarls/rc-textarea/define";
|
|
2
|
-
import { R as o, a as t } from "./rc-editor-toolbar-
|
|
2
|
+
import { R as o, a as t } from "./rc-editor-toolbar-CcTLdYIt.js";
|
|
3
3
|
customElements.get("rc-editor-toolbar") ?? customElements.define("rc-editor-toolbar", o);
|
|
4
4
|
customElements.get("rc-markdown-editor") ?? customElements.define("rc-markdown-editor", t);
|
|
5
5
|
//# sourceMappingURL=rc-markdown-editor-define.js.map
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { Decoration } from '@rcarls/rc-textarea';
|
|
2
2
|
import { ActiveFormats } from './types.ts';
|
|
3
|
+
/**
|
|
4
|
+
* Replaces the language token on the opening fence of the code block containing
|
|
5
|
+
* `selectionStart`. Returns the updated string, or `null` if the cursor is not
|
|
6
|
+
* inside a fenced code block.
|
|
7
|
+
*
|
|
8
|
+
* @param value - Full source editor value.
|
|
9
|
+
* @param selectionStart - Cursor offset used to locate the containing fence.
|
|
10
|
+
* @param language - Language string to write (empty string removes the token).
|
|
11
|
+
*/
|
|
12
|
+
export declare function setCodeBlockLanguage(value: string, selectionStart: number, language: string): string | null;
|
|
3
13
|
/**
|
|
4
14
|
* Derives active inline and block formats from the decoration array at a given
|
|
5
15
|
* selection range in the markdown source editor. Used for toolbar state sync in
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { LitElement } from 'lit';
|
|
2
|
-
import { HeadingLevel } from './types.ts';
|
|
2
|
+
import { EditorToolbarAction, EditorToolbarActionDetail, HeadingLevel } from './types.ts';
|
|
3
3
|
/**
|
|
4
4
|
* Formatting toolbar for `<rc-markdown-editor>`. Renders into its own light DOM
|
|
5
5
|
* so the parent shadow stylesheet can reach `rc-editor-toolbar button` directly.
|
|
6
6
|
*
|
|
7
7
|
* Active-format properties are set by the parent editor and reflected as
|
|
8
|
-
* `aria-pressed` on the corresponding buttons. Heading level is shown via
|
|
9
|
-
* `<select>`. Code-block language is shown via a `<input>` when active.
|
|
8
|
+
* `aria-pressed` on the corresponding buttons. Heading level is shown via an
|
|
9
|
+
* `<rc-select>`. Code-block language is shown via a `<input>` when active.
|
|
10
10
|
*
|
|
11
11
|
* @fires rc-toolbar-action - When a formatting button or select is activated.
|
|
12
12
|
*/
|
|
@@ -18,6 +18,7 @@ export declare class RcEditorToolbar extends LitElement {
|
|
|
18
18
|
activeUnderline: boolean;
|
|
19
19
|
activeStrikethrough: boolean;
|
|
20
20
|
activeCode: boolean;
|
|
21
|
+
activeLink: boolean;
|
|
21
22
|
activeHeading: HeadingLevel | null;
|
|
22
23
|
activeBlockquote: boolean;
|
|
23
24
|
activeBulletList: boolean;
|
|
@@ -30,15 +31,14 @@ export declare class RcEditorToolbar extends LitElement {
|
|
|
30
31
|
codeLanguage: string | null;
|
|
31
32
|
/** Whether the editor is in source mode. */
|
|
32
33
|
sourceMode: boolean;
|
|
33
|
-
|
|
34
|
-
private _langInputValue;
|
|
34
|
+
protected _langInputValue: string;
|
|
35
35
|
createRenderRoot(): this;
|
|
36
36
|
updated(changed: Map<string, unknown>): void;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
37
|
+
protected _dispatch(action: EditorToolbarAction, extra?: Partial<EditorToolbarActionDetail>): void;
|
|
38
|
+
protected _onClick: (e: MouseEvent) => void;
|
|
39
|
+
protected _onHeadingChange: (e: Event) => void;
|
|
40
|
+
protected _onLangInput: (e: Event) => void;
|
|
41
|
+
protected _onLangCommit: () => void;
|
|
42
|
+
protected _onLangKeyDown: (e: KeyboardEvent) => void;
|
|
43
43
|
render(): import('lit').TemplateResult<1>;
|
|
44
44
|
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { LitElement } from 'lit';
|
|
2
|
+
import { fromMarkdown } from 'mdast-util-from-markdown';
|
|
3
|
+
import { RCTextarea, RCTextareaPluginAPI } from '@rcarls/rc-textarea';
|
|
4
|
+
import { ActiveFormats, EditorMode, EditorToolbarAction, EditorToolbarActionDetail, HeadingLevel } from './types.ts';
|
|
2
5
|
/**
|
|
3
|
-
* Markdown
|
|
6
|
+
* Rich/source Markdown editor with a formatting toolbar, backed by rc-textarea.
|
|
4
7
|
*
|
|
5
8
|
* - **Rich mode (default)**: A `contenteditable` div shows micromark-rendered
|
|
6
9
|
* HTML. DOM mutations are converted back to markdown via turndown and
|
|
@@ -18,6 +21,8 @@ import { LitElement } from 'lit';
|
|
|
18
21
|
* </rc-markdown-editor>
|
|
19
22
|
* ```
|
|
20
23
|
*
|
|
24
|
+
* @see {@link https://richardcarls.github.io/rc-webcomponents/components/rc-markdown-editor rc-markdown-editor docs}
|
|
25
|
+
*
|
|
21
26
|
* @fires rc-change - Value changed. Detail: `{ value: string }`.
|
|
22
27
|
* @fires rc-mode-change - Mode toggled. Detail: `{ mode: EditorMode }`.
|
|
23
28
|
* @fires rc-formatting-change - Active formats at cursor changed. Detail: `ActiveFormats`.
|
|
@@ -47,52 +52,64 @@ export declare class RcMarkdownEditor extends LitElement {
|
|
|
47
52
|
toolbar: boolean;
|
|
48
53
|
/** Make the editor read-only. */
|
|
49
54
|
readOnly: boolean;
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
55
|
+
protected _$richView: HTMLDivElement;
|
|
56
|
+
protected _$sourceEditor: RCTextarea;
|
|
57
|
+
protected _$toolbar?: HTMLElement;
|
|
58
|
+
protected _observer: MutationObserver | null;
|
|
59
|
+
protected _$savedRange: Range | null;
|
|
60
|
+
protected _activeFormats: ActiveFormats;
|
|
61
|
+
protected _syncTimer: ReturnType<typeof setTimeout> | null;
|
|
62
|
+
protected _pluginApi: RCTextareaPluginAPI | null;
|
|
63
|
+
protected _ignoreRichMutations: boolean;
|
|
64
|
+
protected _richViewRendered: boolean;
|
|
65
|
+
protected _cachedTree: ReturnType<typeof fromMarkdown> | null;
|
|
66
|
+
protected _firstUpdatedDone: boolean;
|
|
67
|
+
protected _$linkPopover?: HTMLDivElement;
|
|
68
|
+
protected _$linkPopoverInput?: HTMLInputElement;
|
|
69
|
+
protected _linkPopoverOpen: boolean;
|
|
70
|
+
protected _linkPopoverHref: string;
|
|
61
71
|
connectedCallback(): void;
|
|
62
72
|
disconnectedCallback(): void;
|
|
63
73
|
firstUpdated(): void;
|
|
64
74
|
updated(changed: Map<string, unknown>): void;
|
|
65
75
|
render(): import('lit').TemplateResult<1>;
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
private _applySourceFormat;
|
|
76
|
+
protected _setRichViewContent(markdown: string): void;
|
|
77
|
+
protected _renderMarkdown(markdown: string): string;
|
|
78
|
+
protected _scheduleMarkdownSync(): void;
|
|
79
|
+
protected _syncMarkdownFromRichView(): void;
|
|
80
|
+
protected _switchToSource(): void;
|
|
81
|
+
protected _switchToRich(): void;
|
|
82
|
+
protected _setupSourcePlugin(): void;
|
|
83
|
+
protected _getCodeBlockLanguage(tree: ReturnType<typeof fromMarkdown>, offset: number): string | null;
|
|
84
|
+
protected _onSourceChange: (_e: Event) => void;
|
|
85
|
+
protected _onToolbarAction: (e: CustomEvent<EditorToolbarActionDetail>) => void;
|
|
86
|
+
protected _applyRichFormat(action: EditorToolbarAction): void;
|
|
87
|
+
protected _applySourceFormat(action: EditorToolbarAction): void;
|
|
79
88
|
/** Sets the heading level on the cursor's line in source mode. null = paragraph. */
|
|
80
|
-
|
|
89
|
+
protected _setSourceHeadingLevel(level: HeadingLevel | null): void;
|
|
81
90
|
/** Updates the language of the code block at the cursor in source mode. */
|
|
82
|
-
|
|
83
|
-
/** Updates the data-lang attribute on the
|
|
84
|
-
|
|
85
|
-
/** Toggles a line prefix (e.g. `'
|
|
86
|
-
|
|
91
|
+
protected _setSourceCodeBlockLanguage(lang: string): void;
|
|
92
|
+
/** Updates the `data-lang` attribute on the `<pre>` at the cursor in rich mode. */
|
|
93
|
+
protected _setRichCodeBlockLanguage(lang: string): void;
|
|
94
|
+
/** Toggles a line prefix (e.g. `'> '`, `'- '`) on the cursor's line. */
|
|
95
|
+
protected _toggleLinePrefix(prefix: string): void;
|
|
87
96
|
/** Wraps the current selection (or cursor line) with a fenced code block. */
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
97
|
+
protected _toggleSourceCodeFence(): void;
|
|
98
|
+
protected _wrapRichSelection(tag: string): void;
|
|
99
|
+
protected _toggleRichCodeBlock(): void;
|
|
100
|
+
protected _onKeyDown: (e: KeyboardEvent) => void;
|
|
101
|
+
protected _onSelectionChange: () => void;
|
|
102
|
+
protected _pushToolbarState(): void;
|
|
103
|
+
protected _syncNativeTextarea(): void;
|
|
104
|
+
protected _connectLabel(): void;
|
|
105
|
+
protected _onRichViewClick: (e: MouseEvent) => void;
|
|
106
|
+
protected _onDocumentClick: (e: MouseEvent) => void;
|
|
107
|
+
protected _expandRichSelectionToWord(): void;
|
|
108
|
+
protected _positionLinkPopover(): void;
|
|
109
|
+
protected _applyLink: () => void;
|
|
110
|
+
protected _removeLink: () => void;
|
|
111
|
+
protected _openLinkHref: () => void;
|
|
112
|
+
protected _onLinkPopoverKeyDown: (e: KeyboardEvent) => void;
|
|
113
|
+
protected _dispatchChange(value: string): void;
|
|
114
|
+
protected _dispatchModeChange(mode: EditorMode): void;
|
|
98
115
|
}
|
package/package.json
CHANGED
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.
|
|
7
|
-
"description": "
|
|
6
|
+
"version": "0.3.1",
|
|
7
|
+
"description": "Rich/source Markdown editor with a formatting toolbar, backed by rc-textarea.",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
10
|
"url": "git+https://github.com/richardcarls/rc-webcomponents.git",
|
|
11
11
|
"directory": "packages/rc-markdown-editor"
|
|
12
12
|
},
|
|
13
|
-
"homepage": "https://github.
|
|
13
|
+
"homepage": "https://richardcarls.github.io/rc-webcomponents/components/rc-markdown-editor",
|
|
14
14
|
"license": "MIT",
|
|
15
15
|
"type": "module",
|
|
16
16
|
"files": [
|
|
@@ -37,21 +37,19 @@
|
|
|
37
37
|
],
|
|
38
38
|
"customElements": "dist/custom-elements.json",
|
|
39
39
|
"scripts": {
|
|
40
|
-
"dev": "vite",
|
|
41
40
|
"build": "tsc && vite build && cem analyze",
|
|
42
41
|
"cem:analyze": "cem analyze",
|
|
43
42
|
"preview": "vite preview",
|
|
44
|
-
"test:browser": "vitest",
|
|
45
|
-
"test:browser:chrome": "vitest --project=chromium",
|
|
46
|
-
"test:browser:firefox": "vitest --project=firefox"
|
|
47
|
-
"yalc:publish": "yalc publish --push"
|
|
43
|
+
"test:browser": "vitest --run",
|
|
44
|
+
"test:browser:chrome": "vitest --run --project=chromium",
|
|
45
|
+
"test:browser:firefox": "vitest --run --project=firefox"
|
|
48
46
|
},
|
|
49
47
|
"dependencies": {
|
|
50
|
-
"@rcarls/rc-
|
|
48
|
+
"@rcarls/rc-select": "workspace:*",
|
|
49
|
+
"@rcarls/rc-textarea": "workspace:*"
|
|
51
50
|
},
|
|
52
51
|
"devDependencies": {
|
|
53
52
|
"@custom-elements-manifest/analyzer": "0.11.0",
|
|
54
|
-
"@guanghechen/rollup-plugin-copy": "^6.0.9",
|
|
55
53
|
"@types/turndown": "^5.0.5",
|
|
56
54
|
"@vitest/browser-playwright": "4.1.5",
|
|
57
55
|
"lit": "^3.0.0",
|
package/dist/demo.css
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
*, *::before, *::after {
|
|
2
|
-
box-sizing: border-box;
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
:root {
|
|
6
|
-
color-scheme: light dark;
|
|
7
|
-
--rc-accent: Highlight; /* Chrome doesn't support AccentColor on the open web */
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
@supports (color: AccentColor) {
|
|
11
|
-
:root {
|
|
12
|
-
--rc-accent: AccentColor;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
[data-theme="light"] { color-scheme: light; }
|
|
17
|
-
[data-theme="dark"] { color-scheme: dark; }
|
|
18
|
-
|
|
19
|
-
[data-theme="solarized-light"] {
|
|
20
|
-
color-scheme: light;
|
|
21
|
-
--rc-surface: #fdf6e3;
|
|
22
|
-
--rc-text: #657b83;
|
|
23
|
-
--rc-border: 1px solid #93a1a1;
|
|
24
|
-
--rc-shadow: 0 2px 8px rgba(0, 0, 0, .12);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
[data-theme="solarized-dark"] {
|
|
28
|
-
color-scheme: dark;
|
|
29
|
-
--rc-surface: #002b36;
|
|
30
|
-
--rc-text: #839496;
|
|
31
|
-
--rc-border: 1px solid #586e75;
|
|
32
|
-
--rc-shadow: 0 2px 8px rgba(0, 0, 0, .3);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
body {
|
|
36
|
-
font-family: system-ui, sans-serif;
|
|
37
|
-
margin: 0;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
.demo-page {
|
|
41
|
-
max-width: 60rem;
|
|
42
|
-
margin: 0 auto;
|
|
43
|
-
padding: 2rem 1.5rem;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
.demo-controls {
|
|
47
|
-
display: flex;
|
|
48
|
-
align-items: center;
|
|
49
|
-
gap: 1rem;
|
|
50
|
-
margin-bottom: 2rem;
|
|
51
|
-
padding-bottom: 1rem;
|
|
52
|
-
border-bottom: 1px solid ButtonBorder;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
.demo-controls .demo-title {
|
|
56
|
-
flex: 1;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
.demo-controls h1 {
|
|
60
|
-
margin: 0 0 0.15rem;
|
|
61
|
-
font-size: 1.5rem;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
.demo-controls a {
|
|
65
|
-
font-size: 0.8rem;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
.demo-section {
|
|
69
|
-
margin-bottom: 2rem;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
.demo-section h2 {
|
|
73
|
-
margin: 0 0 0.5rem;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
.theme-picker {
|
|
77
|
-
padding: 0.35em 0.75em;
|
|
78
|
-
font-family: inherit;
|
|
79
|
-
font-size: 0.8rem;
|
|
80
|
-
cursor: pointer;
|
|
81
|
-
border: 1px solid ButtonBorder;
|
|
82
|
-
border-radius: 4px;
|
|
83
|
-
background: Canvas;
|
|
84
|
-
color: CanvasText;
|
|
85
|
-
}
|
package/dist/demo.js
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
const THEMES = ['', 'light', 'dark', 'solarized-light', 'solarized-dark'];
|
|
2
|
-
const LABELS = ['Auto', 'Light', 'Dark', 'Solarized ☀', 'Solarized ☾'];
|
|
3
|
-
|
|
4
|
-
const stored = localStorage.getItem('rc-demo-theme') ?? '';
|
|
5
|
-
applyTheme(stored);
|
|
6
|
-
|
|
7
|
-
function applyTheme(theme) {
|
|
8
|
-
if (theme) {
|
|
9
|
-
document.documentElement.dataset.theme = theme;
|
|
10
|
-
} else {
|
|
11
|
-
delete document.documentElement.dataset.theme;
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function currentIndex() {
|
|
16
|
-
const current = document.documentElement.dataset.theme ?? '';
|
|
17
|
-
const idx = THEMES.indexOf(current);
|
|
18
|
-
return idx === -1 ? 0 : idx;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function updateButtons() {
|
|
22
|
-
const label = LABELS[currentIndex()];
|
|
23
|
-
document.querySelectorAll('.theme-picker').forEach((btn) => {
|
|
24
|
-
btn.textContent = label;
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
window.cycleTheme = function () {
|
|
29
|
-
const next = THEMES[(currentIndex() + 1) % THEMES.length];
|
|
30
|
-
applyTheme(next);
|
|
31
|
-
localStorage.setItem('rc-demo-theme', next);
|
|
32
|
-
updateButtons();
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
document.addEventListener('DOMContentLoaded', () => {
|
|
36
|
-
updateButtons();
|
|
37
|
-
document.querySelectorAll('.theme-picker').forEach((btn) => {
|
|
38
|
-
btn.addEventListener('click', window.cycleTheme);
|
|
39
|
-
});
|
|
40
|
-
});
|