@charcuterie/ui 3.13.0 → 3.14.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/dist/markdownEditorCodeMirror/MarkdownView.d.ts +140 -0
- package/dist/markdownEditorCodeMirror/MarkdownView.d.ts.map +1 -0
- package/dist/markdownEditorCodeMirror/MarkdownView.js +268 -0
- package/dist/markdownEditorCodeMirror/MarkdownView.js.map +1 -0
- package/dist/markdownEditorCodeMirror/index.d.ts +14 -3
- package/dist/markdownEditorCodeMirror/index.d.ts.map +1 -1
- package/dist/markdownEditorCodeMirror/index.js +12 -3
- package/dist/markdownEditorCodeMirror/index.js.map +1 -1
- package/dist/markdownEditorCodeMirror/livePreview.d.ts +42 -2
- package/dist/markdownEditorCodeMirror/livePreview.d.ts.map +1 -1
- package/dist/markdownEditorCodeMirror/livePreview.js +243 -26
- package/dist/markdownEditorCodeMirror/livePreview.js.map +1 -1
- package/dist/markdownEditorCodeMirror/livePreviewRanges.d.ts.map +1 -1
- package/dist/markdownEditorCodeMirror/livePreviewRanges.js +49 -7
- package/dist/markdownEditorCodeMirror/livePreviewRanges.js.map +1 -1
- package/dist/markdownEditorCodeMirror/safeUrls.d.ts +46 -0
- package/dist/markdownEditorCodeMirror/safeUrls.d.ts.map +1 -0
- package/dist/markdownEditorCodeMirror/safeUrls.js +123 -0
- package/dist/markdownEditorCodeMirror/safeUrls.js.map +1 -0
- package/package.json +1 -1
- package/src/MarkdownEditor/MarkdownEditor.mdx +5 -0
- package/src/markdownEditorCodeMirror/MarkdownEditorCodeMirror.mdx +11 -0
- package/src/markdownEditorCodeMirror/MarkdownView.mdx +157 -0
- package/src/markdownEditorCodeMirror/MarkdownView.stories.tsx +314 -0
- package/src/markdownEditorCodeMirror/MarkdownView.test.tsx +288 -0
- package/src/markdownEditorCodeMirror/MarkdownView.tsx +373 -0
- package/src/markdownEditorCodeMirror/index.ts +17 -2
- package/src/markdownEditorCodeMirror/livePreview.ts +329 -33
- package/src/markdownEditorCodeMirror/livePreviewRanges.ts +64 -7
- package/src/markdownEditorCodeMirror/safeUrls.test.ts +0 -0
- package/src/markdownEditorCodeMirror/safeUrls.ts +142 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { SlotProps } from "../slotProps.ts";
|
|
3
|
+
export type MarkdownViewProps = SlotProps & {
|
|
4
|
+
className?: string;
|
|
5
|
+
/**
|
|
6
|
+
* The accessible name of the region, as `aria-label`.
|
|
7
|
+
*
|
|
8
|
+
* Worth passing on anything that is not the only thing on the
|
|
9
|
+
* page. A read view is announced as an article, and "article" with
|
|
10
|
+
* no name tells a reader stepping through the page nothing about
|
|
11
|
+
* which article it is.
|
|
12
|
+
*/
|
|
13
|
+
label?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Called when the reader ticks a `- [ ]` box, with the **whole**
|
|
16
|
+
* next markdown document — the same shape the editors' `onChange`
|
|
17
|
+
* hands back, so the same autosave wires to both.
|
|
18
|
+
*
|
|
19
|
+
* **Passing it is what makes the checkboxes operable at all.**
|
|
20
|
+
* Leave it out and they render disabled: the state is still on
|
|
21
|
+
* screen, and nothing pretends the reader can change it. That is
|
|
22
|
+
* deliberate rather than defensive. Two of the three surfaces this
|
|
23
|
+
* component was built for — a rendered comment, and a repo file
|
|
24
|
+
* fetched at a commit hash — have nowhere to put a tick, and a
|
|
25
|
+
* checkbox that visibly toggles and then loses the change on
|
|
26
|
+
* reload is worse than one that says up front it cannot.
|
|
27
|
+
*
|
|
28
|
+
* The view still applies the tick to its own document
|
|
29
|
+
* immediately, so the box responds under the pointer. If the
|
|
30
|
+
* consumer rejects the change, the next `value` it passes puts it
|
|
31
|
+
* back.
|
|
32
|
+
*/
|
|
33
|
+
onToggleTask?: (nextValue: string) => void;
|
|
34
|
+
/**
|
|
35
|
+
* The markdown to render, and **controlled** — unlike either
|
|
36
|
+
* editor.
|
|
37
|
+
*
|
|
38
|
+
* The editors take `defaultValue` because reassigning their
|
|
39
|
+
* document from outside would throw away the undo history that
|
|
40
|
+
* belonged to it, which is a real cost and is why they ask for a
|
|
41
|
+
* `key` instead. A view has no undo history and no caret to
|
|
42
|
+
* protect, so it has nothing to lose by simply rendering what it
|
|
43
|
+
* is given: fetch a file at a different commit hash, pass the new
|
|
44
|
+
* string, and the page changes. No remount, no `key`, no stale
|
|
45
|
+
* document above a new title.
|
|
46
|
+
*/
|
|
47
|
+
value: string;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Rendered markdown, with no toolbar and nothing to type into.
|
|
51
|
+
*
|
|
52
|
+
* ## Why this is a component and not `isReadOnly` on the editors
|
|
53
|
+
*
|
|
54
|
+
* Both editors already have `isReadOnly`, and it means *an editor
|
|
55
|
+
* you cannot type in*: the nine formatting buttons are still in the
|
|
56
|
+
* DOM, still queryable, still in the accessibility tree, drawn in
|
|
57
|
+
* their disabled colours above a document nobody is editing. That is
|
|
58
|
+
* the right shape for a form field that is temporarily locked. It is
|
|
59
|
+
* the wrong shape for reading a file, and a `hasToolbar={false}`
|
|
60
|
+
* prop would not fix it — the type would still carry
|
|
61
|
+
* `onUploadImage`, `icons`, `placeholder` and `rawModeLabel`, none
|
|
62
|
+
* of which mean anything to a reader, and "no toolbar" would be a
|
|
63
|
+
* branch that could be got wrong rather than a thing that does not
|
|
64
|
+
* exist.
|
|
65
|
+
*
|
|
66
|
+
* So this is its own component, the way `Listbox`, `Combobox` and
|
|
67
|
+
* `Picker` are their own components rather than three modes of one.
|
|
68
|
+
* The props are the four a reader needs, and there is no code path
|
|
69
|
+
* in this file that could render a toolbar.
|
|
70
|
+
*
|
|
71
|
+
* ## Why it is in the CodeMirror subpath
|
|
72
|
+
*
|
|
73
|
+
* Because the requirement is that it renders **the same** as the
|
|
74
|
+
* editor a consumer edits the same document in — and the only way
|
|
75
|
+
* to guarantee that is to be the same code, not to be a second
|
|
76
|
+
* implementation that agrees today.
|
|
77
|
+
*
|
|
78
|
+
* A `react-markdown` reader in the main barrel was the obvious
|
|
79
|
+
* alternative and is the one that goes wrong slowly: two parsers,
|
|
80
|
+
* two table implementations, two answers to what a bare URL does,
|
|
81
|
+
* and a bug report six months later that says "the table changed
|
|
82
|
+
* when I saved it". This shares `livePreviewRanges.ts` — the 1,200
|
|
83
|
+
* lines that decide what a document *looks* like — with
|
|
84
|
+
* `MarkdownEditorCodeMirror`, and the theme with it too. There is no
|
|
85
|
+
* second renderer to keep in step.
|
|
86
|
+
*
|
|
87
|
+
* The cost is that it lives behind the same optional peers, so a
|
|
88
|
+
* consumer who wants only the reader still installs CodeMirror. That
|
|
89
|
+
* is the honest trade and it is the right way round: an app that
|
|
90
|
+
* renders markdown in three places and edits it in one should pay
|
|
91
|
+
* once.
|
|
92
|
+
*
|
|
93
|
+
* ## What differs from the editor, and why each one differs
|
|
94
|
+
*
|
|
95
|
+
* | | editor | this |
|
|
96
|
+
* | --- | --- | --- |
|
|
97
|
+
* | Toolbar | nine buttons | **none in the DOM** |
|
|
98
|
+
* | Value | `defaultValue`, uncontrolled | `value`, **controlled** |
|
|
99
|
+
* | Content role | `textbox` | **`article`** |
|
|
100
|
+
* | Headings | scaled text | scaled **and `role="heading"`** |
|
|
101
|
+
* | Links | painted span, opened by a handler | **real `<a href>`** |
|
|
102
|
+
* | Markers | revealed by the caret | never revealed |
|
|
103
|
+
* | Task boxes | always operable | **inert unless `onToggleTask`** |
|
|
104
|
+
*
|
|
105
|
+
* Every one of those follows from there being no caret. The editor
|
|
106
|
+
* paints a span instead of an anchor because an `<a href>` inside a
|
|
107
|
+
* `contenteditable` competes with the caret for the line; a document
|
|
108
|
+
* has no caret, so the link is simply a link — tabbable, activated
|
|
109
|
+
* by Enter, and copyable from the context menu. The editor reveals
|
|
110
|
+
* markup when the selection touches it because you cannot edit a URL
|
|
111
|
+
* you cannot see; there is nothing here to edit.
|
|
112
|
+
*
|
|
113
|
+
* ## Hostile markdown
|
|
114
|
+
*
|
|
115
|
+
* This renders files fetched from a git host, so the document is
|
|
116
|
+
* untrusted by default, and the answer is mostly structural rather
|
|
117
|
+
* than a sanitiser:
|
|
118
|
+
*
|
|
119
|
+
* - **Raw HTML in the source is never parsed as HTML.** There is no
|
|
120
|
+
* `innerHTML` in this subpath, no `dangerouslySetInnerHTML`, and
|
|
121
|
+
* no sanitiser to have a bug in. CodeMirror's state holds the
|
|
122
|
+
* source as *text*, and every rendered node is `createElement`
|
|
123
|
+
* plus `textContent`. A `<script>` in a fetched README is drawn
|
|
124
|
+
* as the characters `<script>`. `MarkdownView.test.tsx` asserts
|
|
125
|
+
* it against the tag most likely to be tried.
|
|
126
|
+
* - **A URL is checked before it is used as one**, because that was
|
|
127
|
+
* the one place the document reached the platform:
|
|
128
|
+
* `[click](javascript:…)` used to paint as a link and run. Both
|
|
129
|
+
* components now go through `safeUrls.ts`, which allowlists
|
|
130
|
+
* schemes the way a browser reads them.
|
|
131
|
+
* - **The reader is not a beacon.** Remote images carry
|
|
132
|
+
* `referrerpolicy="no-referrer"`, so a fetched image cannot learn
|
|
133
|
+
* which page loaded it.
|
|
134
|
+
*
|
|
135
|
+
* What is *not* claimed: this does not stop a document from linking
|
|
136
|
+
* somewhere unpleasant, and it does not fetch anything on the
|
|
137
|
+
* reader's behalf beyond the images the markdown names.
|
|
138
|
+
*/
|
|
139
|
+
export declare const MarkdownView: ({ className, label, onToggleTask, value, ...receivedSlotProps }: MarkdownViewProps) => ReactNode;
|
|
140
|
+
//# sourceMappingURL=MarkdownView.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MarkdownView.d.ts","sourceRoot":"","sources":["../../src/markdownEditorCodeMirror/MarkdownView.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGtC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAOhD,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;;;;;;;;;;;;;;;OAkBG;IACH,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAA;IAC1C;;;;;;;;;;;;OAYG;IACH,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAYD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyFG;AACH,eAAO,MAAM,YAAY,GAAI,iEAM1B,iBAAiB,KAAG,SAsMtB,CAAA"}
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useUniqueId } from "@charcuterie/logic";
|
|
3
|
+
import { markdown, markdownLanguage, } from "@codemirror/lang-markdown";
|
|
4
|
+
import { Annotation, Compartment, EditorState, } from "@codemirror/state";
|
|
5
|
+
import { EditorView } from "@codemirror/view";
|
|
6
|
+
import { useEffect, useRef } from "react";
|
|
7
|
+
import { toClassName } from "../toClassName.js";
|
|
8
|
+
import { livePreview, livePreviewOptions, } from "./livePreview.js";
|
|
9
|
+
/**
|
|
10
|
+
* Tells the update listener that a document change came from a new
|
|
11
|
+
* `value`, not from a reader.
|
|
12
|
+
*
|
|
13
|
+
* Without it, syncing the prop into the document fires
|
|
14
|
+
* `onToggleTask` with the string the consumer just passed in — a
|
|
15
|
+
* loop that looks like an autosave storm and is really an echo.
|
|
16
|
+
*/
|
|
17
|
+
const isValueSync = Annotation.define();
|
|
18
|
+
/**
|
|
19
|
+
* Rendered markdown, with no toolbar and nothing to type into.
|
|
20
|
+
*
|
|
21
|
+
* ## Why this is a component and not `isReadOnly` on the editors
|
|
22
|
+
*
|
|
23
|
+
* Both editors already have `isReadOnly`, and it means *an editor
|
|
24
|
+
* you cannot type in*: the nine formatting buttons are still in the
|
|
25
|
+
* DOM, still queryable, still in the accessibility tree, drawn in
|
|
26
|
+
* their disabled colours above a document nobody is editing. That is
|
|
27
|
+
* the right shape for a form field that is temporarily locked. It is
|
|
28
|
+
* the wrong shape for reading a file, and a `hasToolbar={false}`
|
|
29
|
+
* prop would not fix it — the type would still carry
|
|
30
|
+
* `onUploadImage`, `icons`, `placeholder` and `rawModeLabel`, none
|
|
31
|
+
* of which mean anything to a reader, and "no toolbar" would be a
|
|
32
|
+
* branch that could be got wrong rather than a thing that does not
|
|
33
|
+
* exist.
|
|
34
|
+
*
|
|
35
|
+
* So this is its own component, the way `Listbox`, `Combobox` and
|
|
36
|
+
* `Picker` are their own components rather than three modes of one.
|
|
37
|
+
* The props are the four a reader needs, and there is no code path
|
|
38
|
+
* in this file that could render a toolbar.
|
|
39
|
+
*
|
|
40
|
+
* ## Why it is in the CodeMirror subpath
|
|
41
|
+
*
|
|
42
|
+
* Because the requirement is that it renders **the same** as the
|
|
43
|
+
* editor a consumer edits the same document in — and the only way
|
|
44
|
+
* to guarantee that is to be the same code, not to be a second
|
|
45
|
+
* implementation that agrees today.
|
|
46
|
+
*
|
|
47
|
+
* A `react-markdown` reader in the main barrel was the obvious
|
|
48
|
+
* alternative and is the one that goes wrong slowly: two parsers,
|
|
49
|
+
* two table implementations, two answers to what a bare URL does,
|
|
50
|
+
* and a bug report six months later that says "the table changed
|
|
51
|
+
* when I saved it". This shares `livePreviewRanges.ts` — the 1,200
|
|
52
|
+
* lines that decide what a document *looks* like — with
|
|
53
|
+
* `MarkdownEditorCodeMirror`, and the theme with it too. There is no
|
|
54
|
+
* second renderer to keep in step.
|
|
55
|
+
*
|
|
56
|
+
* The cost is that it lives behind the same optional peers, so a
|
|
57
|
+
* consumer who wants only the reader still installs CodeMirror. That
|
|
58
|
+
* is the honest trade and it is the right way round: an app that
|
|
59
|
+
* renders markdown in three places and edits it in one should pay
|
|
60
|
+
* once.
|
|
61
|
+
*
|
|
62
|
+
* ## What differs from the editor, and why each one differs
|
|
63
|
+
*
|
|
64
|
+
* | | editor | this |
|
|
65
|
+
* | --- | --- | --- |
|
|
66
|
+
* | Toolbar | nine buttons | **none in the DOM** |
|
|
67
|
+
* | Value | `defaultValue`, uncontrolled | `value`, **controlled** |
|
|
68
|
+
* | Content role | `textbox` | **`article`** |
|
|
69
|
+
* | Headings | scaled text | scaled **and `role="heading"`** |
|
|
70
|
+
* | Links | painted span, opened by a handler | **real `<a href>`** |
|
|
71
|
+
* | Markers | revealed by the caret | never revealed |
|
|
72
|
+
* | Task boxes | always operable | **inert unless `onToggleTask`** |
|
|
73
|
+
*
|
|
74
|
+
* Every one of those follows from there being no caret. The editor
|
|
75
|
+
* paints a span instead of an anchor because an `<a href>` inside a
|
|
76
|
+
* `contenteditable` competes with the caret for the line; a document
|
|
77
|
+
* has no caret, so the link is simply a link — tabbable, activated
|
|
78
|
+
* by Enter, and copyable from the context menu. The editor reveals
|
|
79
|
+
* markup when the selection touches it because you cannot edit a URL
|
|
80
|
+
* you cannot see; there is nothing here to edit.
|
|
81
|
+
*
|
|
82
|
+
* ## Hostile markdown
|
|
83
|
+
*
|
|
84
|
+
* This renders files fetched from a git host, so the document is
|
|
85
|
+
* untrusted by default, and the answer is mostly structural rather
|
|
86
|
+
* than a sanitiser:
|
|
87
|
+
*
|
|
88
|
+
* - **Raw HTML in the source is never parsed as HTML.** There is no
|
|
89
|
+
* `innerHTML` in this subpath, no `dangerouslySetInnerHTML`, and
|
|
90
|
+
* no sanitiser to have a bug in. CodeMirror's state holds the
|
|
91
|
+
* source as *text*, and every rendered node is `createElement`
|
|
92
|
+
* plus `textContent`. A `<script>` in a fetched README is drawn
|
|
93
|
+
* as the characters `<script>`. `MarkdownView.test.tsx` asserts
|
|
94
|
+
* it against the tag most likely to be tried.
|
|
95
|
+
* - **A URL is checked before it is used as one**, because that was
|
|
96
|
+
* the one place the document reached the platform:
|
|
97
|
+
* `[click](javascript:…)` used to paint as a link and run. Both
|
|
98
|
+
* components now go through `safeUrls.ts`, which allowlists
|
|
99
|
+
* schemes the way a browser reads them.
|
|
100
|
+
* - **The reader is not a beacon.** Remote images carry
|
|
101
|
+
* `referrerpolicy="no-referrer"`, so a fetched image cannot learn
|
|
102
|
+
* which page loaded it.
|
|
103
|
+
*
|
|
104
|
+
* What is *not* claimed: this does not stop a document from linking
|
|
105
|
+
* somewhere unpleasant, and it does not fetch anything on the
|
|
106
|
+
* reader's behalf beyond the images the markdown names.
|
|
107
|
+
*/
|
|
108
|
+
export const MarkdownView = ({ className, label, onToggleTask, value, ...receivedSlotProps }) => {
|
|
109
|
+
const baseId = useUniqueId();
|
|
110
|
+
const hostRef = useRef(null);
|
|
111
|
+
const viewRef = useRef(null);
|
|
112
|
+
const optionsRef = useRef(new Compartment());
|
|
113
|
+
/**
|
|
114
|
+
* The handler reaches the view through a ref, never a closure.
|
|
115
|
+
*
|
|
116
|
+
* The `EditorView` is built once — rebuilding it on a new callback
|
|
117
|
+
* identity would drop the scroll position mid-read — so its
|
|
118
|
+
* extensions capture *this* object and the render loop keeps the
|
|
119
|
+
* object current.
|
|
120
|
+
*/
|
|
121
|
+
const handlerRef = useRef(onToggleTask);
|
|
122
|
+
handlerRef.current = onToggleTask;
|
|
123
|
+
/**
|
|
124
|
+
* Frozen at mount, and named so rather than read from the render
|
|
125
|
+
* closure, because the build-once effect below is honestly
|
|
126
|
+
* dependency-free rather than lint-suppressed.
|
|
127
|
+
*/
|
|
128
|
+
const mountRef = useRef({
|
|
129
|
+
ariaDescribedBy: receivedSlotProps["aria-describedby"],
|
|
130
|
+
id: receivedSlotProps.id ?? baseId,
|
|
131
|
+
label,
|
|
132
|
+
value,
|
|
133
|
+
});
|
|
134
|
+
useEffect(() => {
|
|
135
|
+
const host = hostRef.current;
|
|
136
|
+
if (!host) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
const view = new EditorView({
|
|
140
|
+
parent: host,
|
|
141
|
+
state: EditorState.create({
|
|
142
|
+
doc: mountRef.current.value,
|
|
143
|
+
extensions: [
|
|
144
|
+
// GFM, not strict CommonMark — the same argument as the
|
|
145
|
+
// editor's: it is what autolinks a bare URL, renders a
|
|
146
|
+
// task list, and parses a table. A reader that disagreed
|
|
147
|
+
// with the editor about any of those would be the bug this
|
|
148
|
+
// component exists to avoid.
|
|
149
|
+
markdown({ base: markdownLanguage }),
|
|
150
|
+
EditorView.lineWrapping,
|
|
151
|
+
livePreview({ hasDocumentSemantics: true }),
|
|
152
|
+
/**
|
|
153
|
+
* The one answer that can change after mount, and only
|
|
154
|
+
* that answer.
|
|
155
|
+
*
|
|
156
|
+
* A `Compartment` holding the whole `livePreview()` would
|
|
157
|
+
* reconfigure the `StateField`s inside it — raw mode,
|
|
158
|
+
* focus, the table pass — which means destroying and
|
|
159
|
+
* recreating them to change a boolean about checkboxes.
|
|
160
|
+
* Compartmenting the facet alone reconfigures one value.
|
|
161
|
+
*/
|
|
162
|
+
optionsRef.current.of(livePreviewOptions.of({
|
|
163
|
+
isTaskListInteractive: Boolean(handlerRef.current),
|
|
164
|
+
})),
|
|
165
|
+
/**
|
|
166
|
+
* `editable`, and deliberately **not**
|
|
167
|
+
* `EditorState.readOnly`.
|
|
168
|
+
*
|
|
169
|
+
* `editable` is the DOM half: it writes
|
|
170
|
+
* `contenteditable="false"`, which is what stops typing,
|
|
171
|
+
* pasting, dropping and IME, and what keeps the content
|
|
172
|
+
* out of the tab order. It is the whole of what this
|
|
173
|
+
* surface needs, because no keymap and no input handler is
|
|
174
|
+
* installed for `readOnly` to guard against.
|
|
175
|
+
*
|
|
176
|
+
* `readOnly` also makes CodeMirror write
|
|
177
|
+
* `aria-readonly="true"` onto the content element, and
|
|
178
|
+
* there is no way to unset an attribute it decides to
|
|
179
|
+
* write. On `role="article"` that attribute is one ARIA
|
|
180
|
+
* does not define, which axe reports and which is a fair
|
|
181
|
+
* report: "read-only" is a statement about a *field*, and
|
|
182
|
+
* this is not one. Nothing is lost by leaving it off, so it
|
|
183
|
+
* is left off rather than papered over.
|
|
184
|
+
*/
|
|
185
|
+
EditorView.editable.of(false),
|
|
186
|
+
EditorView.contentAttributes.of({
|
|
187
|
+
/**
|
|
188
|
+
* `article`, not the `textbox` CodeMirror writes.
|
|
189
|
+
*
|
|
190
|
+
* This is the difference between reading a document and
|
|
191
|
+
* being trapped in a text field. A screen reader entering
|
|
192
|
+
* `role="textbox"` switches to forms mode and reads the
|
|
193
|
+
* contents as one flat string — no heading navigation, no
|
|
194
|
+
* table navigation, no list structure — which for an
|
|
195
|
+
* editor is exactly right and for a reader deletes every
|
|
196
|
+
* affordance the markup was rendered for.
|
|
197
|
+
*/
|
|
198
|
+
role: "article",
|
|
199
|
+
/**
|
|
200
|
+
* CodeMirror writes `aria-multiline="true"` into the
|
|
201
|
+
* content element's attributes, and there is no way to
|
|
202
|
+
* unset one: its `updateAttrs` calls `setAttribute` with
|
|
203
|
+
* whatever it is handed, so `undefined` would arrive as
|
|
204
|
+
* the string "undefined".
|
|
205
|
+
*
|
|
206
|
+
* `false` is both the true answer — this is not a
|
|
207
|
+
* multiline textbox, because it is not a textbox — and
|
|
208
|
+
* the value axe explicitly ignores on an element carrying
|
|
209
|
+
* `contenteditable`. An attribute ARIA does not define
|
|
210
|
+
* for `article` is inert either way; this way the gate
|
|
211
|
+
* agrees.
|
|
212
|
+
*/
|
|
213
|
+
"aria-multiline": "false",
|
|
214
|
+
...(mountRef.current.label
|
|
215
|
+
? { "aria-label": mountRef.current.label }
|
|
216
|
+
: {}),
|
|
217
|
+
...(mountRef.current.ariaDescribedBy
|
|
218
|
+
? {
|
|
219
|
+
"aria-describedby": mountRef.current.ariaDescribedBy,
|
|
220
|
+
}
|
|
221
|
+
: {}),
|
|
222
|
+
id: mountRef.current.id,
|
|
223
|
+
}),
|
|
224
|
+
EditorView.updateListener.of((update) => {
|
|
225
|
+
if (!update.docChanged ||
|
|
226
|
+
update.transactions.some((transaction) => transaction.annotation(isValueSync))) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
// The only change a reader can make is a checkbox, so
|
|
230
|
+
// this is that checkbox and nothing else.
|
|
231
|
+
handlerRef.current?.(update.state.doc.toString());
|
|
232
|
+
}),
|
|
233
|
+
],
|
|
234
|
+
}),
|
|
235
|
+
});
|
|
236
|
+
viewRef.current = view;
|
|
237
|
+
return () => {
|
|
238
|
+
view.destroy();
|
|
239
|
+
viewRef.current = null;
|
|
240
|
+
};
|
|
241
|
+
}, []);
|
|
242
|
+
useEffect(() => {
|
|
243
|
+
const view = viewRef.current;
|
|
244
|
+
if (!view || view.state.doc.toString() === value) {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
view.dispatch({
|
|
248
|
+
annotations: isValueSync.of(true),
|
|
249
|
+
changes: {
|
|
250
|
+
from: 0,
|
|
251
|
+
insert: value,
|
|
252
|
+
to: view.state.doc.length,
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
}, [value]);
|
|
256
|
+
// Through a compartment rather than a rebuild, so a consumer that
|
|
257
|
+
// turns ticking on and off — a task that becomes editable — keeps
|
|
258
|
+
// its scroll position.
|
|
259
|
+
useEffect(() => {
|
|
260
|
+
viewRef.current?.dispatch({
|
|
261
|
+
effects: optionsRef.current.reconfigure(livePreviewOptions.of({
|
|
262
|
+
isTaskListInteractive: Boolean(onToggleTask),
|
|
263
|
+
})),
|
|
264
|
+
});
|
|
265
|
+
}, [onToggleTask]);
|
|
266
|
+
return (_jsx("div", { className: toClassName("text-content-primary text-md", className), ref: hostRef }));
|
|
267
|
+
};
|
|
268
|
+
//# sourceMappingURL=MarkdownView.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MarkdownView.js","sourceRoot":"","sources":["../../src/markdownEditorCodeMirror/MarkdownView.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `@charcuterie/ui/markdown-editor-codemirror` — the opt-in
|
|
3
|
-
* preview
|
|
2
|
+
* `@charcuterie/ui/markdown-editor-codemirror` — the opt-in
|
|
3
|
+
* CodeMirror markdown surface: the live-preview **editor**, and the
|
|
4
|
+
* read-only **view** that renders the same document the same way.
|
|
5
|
+
*
|
|
6
|
+
* Both are here rather than in two subpaths because there is one
|
|
7
|
+
* dependency, not two. A consumer who wants only `MarkdownView`
|
|
8
|
+
* still installs CodeMirror; splitting the entry point would suggest
|
|
9
|
+
* otherwise and would give the shared `livePreview` module two ways
|
|
10
|
+
* into the bundle.
|
|
4
11
|
*
|
|
5
12
|
* Its own entry point, and every CodeMirror package it needs is an
|
|
6
13
|
* **optional** peer, so a consumer that never imports this subpath
|
|
@@ -8,9 +15,13 @@
|
|
|
8
15
|
* `@charcuterie/ui/react-router`, and the same reason: the capacity
|
|
9
16
|
* is worth having and is not worth charging everybody for.
|
|
10
17
|
*/
|
|
11
|
-
export {
|
|
18
|
+
export type { LivePreviewOptions } from "./livePreview.ts";
|
|
19
|
+
export { livePreview, livePreviewOptions, livePreviewRawModeField, setLivePreviewRawMode, } from "./livePreview.ts";
|
|
12
20
|
export type { LivePreviewLineKind, LivePreviewMarkKind, LivePreviewRange, LivePreviewSelection, ToLivePreviewRangesOptions, } from "./livePreviewRanges.ts";
|
|
13
21
|
export { toLivePreviewRanges } from "./livePreviewRanges.ts";
|
|
14
22
|
export type { MarkdownEditorCodeMirrorProps } from "./MarkdownEditorCodeMirror.tsx";
|
|
15
23
|
export { MarkdownEditorCodeMirror } from "./MarkdownEditorCodeMirror.tsx";
|
|
24
|
+
export type { MarkdownViewProps } from "./MarkdownView.tsx";
|
|
25
|
+
export { MarkdownView } from "./MarkdownView.tsx";
|
|
26
|
+
export { toSafeImageUrl, toSafeLinkUrl, } from "./safeUrls.ts";
|
|
16
27
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/markdownEditorCodeMirror/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/markdownEditorCodeMirror/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,YAAY,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAC1D,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,uBAAuB,EACvB,qBAAqB,GACtB,MAAM,kBAAkB,CAAA;AACzB,YAAY,EACV,mBAAmB,EACnB,mBAAmB,EACnB,gBAAgB,EAChB,oBAAoB,EACpB,0BAA0B,GAC3B,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAA;AAC5D,YAAY,EAAE,6BAA6B,EAAE,MAAM,gCAAgC,CAAA;AACnF,OAAO,EAAE,wBAAwB,EAAE,MAAM,gCAAgC,CAAA;AACzE,YAAY,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EACL,cAAc,EACd,aAAa,GACd,MAAM,eAAe,CAAA"}
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `@charcuterie/ui/markdown-editor-codemirror` — the opt-in
|
|
3
|
-
* preview
|
|
2
|
+
* `@charcuterie/ui/markdown-editor-codemirror` — the opt-in
|
|
3
|
+
* CodeMirror markdown surface: the live-preview **editor**, and the
|
|
4
|
+
* read-only **view** that renders the same document the same way.
|
|
5
|
+
*
|
|
6
|
+
* Both are here rather than in two subpaths because there is one
|
|
7
|
+
* dependency, not two. A consumer who wants only `MarkdownView`
|
|
8
|
+
* still installs CodeMirror; splitting the entry point would suggest
|
|
9
|
+
* otherwise and would give the shared `livePreview` module two ways
|
|
10
|
+
* into the bundle.
|
|
4
11
|
*
|
|
5
12
|
* Its own entry point, and every CodeMirror package it needs is an
|
|
6
13
|
* **optional** peer, so a consumer that never imports this subpath
|
|
@@ -8,7 +15,9 @@
|
|
|
8
15
|
* `@charcuterie/ui/react-router`, and the same reason: the capacity
|
|
9
16
|
* is worth having and is not worth charging everybody for.
|
|
10
17
|
*/
|
|
11
|
-
export { livePreview, livePreviewRawModeField, setLivePreviewRawMode, } from "./livePreview.js";
|
|
18
|
+
export { livePreview, livePreviewOptions, livePreviewRawModeField, setLivePreviewRawMode, } from "./livePreview.js";
|
|
12
19
|
export { toLivePreviewRanges } from "./livePreviewRanges.js";
|
|
13
20
|
export { MarkdownEditorCodeMirror } from "./MarkdownEditorCodeMirror.js";
|
|
21
|
+
export { MarkdownView } from "./MarkdownView.js";
|
|
22
|
+
export { toSafeImageUrl, toSafeLinkUrl, } from "./safeUrls.js";
|
|
14
23
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/markdownEditorCodeMirror/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/markdownEditorCodeMirror/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,uBAAuB,EACvB,qBAAqB,GACtB,MAAM,kBAAkB,CAAA;AAQzB,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAA;AAE5D,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAAgC,CAAA;AAEzE,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAoB,CAAA;AACjD,OAAO,EACL,cAAc,EACd,aAAa,GACd,MAAM,eAAe,CAAA"}
|
|
@@ -25,7 +25,47 @@
|
|
|
25
25
|
* that resolves to nothing paints nothing, silently.
|
|
26
26
|
*/
|
|
27
27
|
import type { Extension } from "@codemirror/state";
|
|
28
|
-
import { StateField } from "@codemirror/state";
|
|
28
|
+
import { Facet, StateField } from "@codemirror/state";
|
|
29
|
+
/**
|
|
30
|
+
* What kind of surface this is, and what its checkboxes may do.
|
|
31
|
+
*
|
|
32
|
+
* A facet rather than closure state, for the same reason raw mode is
|
|
33
|
+
* a `StateField`: the decoration builders below are module-level
|
|
34
|
+
* singletons — one `ViewPlugin`, one `StateField` — and rebuilding
|
|
35
|
+
* them per instance would mean a second copy of the whole live
|
|
36
|
+
* preview for every surface on the page. They read the answer out of
|
|
37
|
+
* the state they were already handed.
|
|
38
|
+
*
|
|
39
|
+
* It is also what lets `MarkdownView` put the task flag in a
|
|
40
|
+
* `Compartment` and flip it without discarding the view, exactly as
|
|
41
|
+
* the editors already flip editability.
|
|
42
|
+
*/
|
|
43
|
+
export type LivePreviewOptions = {
|
|
44
|
+
/**
|
|
45
|
+
* Draw a **document** rather than an editing surface: real
|
|
46
|
+
* anchors, heading roles, and a table that is a table instead of a
|
|
47
|
+
* way back into the markdown behind it.
|
|
48
|
+
*
|
|
49
|
+
* Off by default, because the editors are the surface this module
|
|
50
|
+
* was written for and every one of these would be wrong inside a
|
|
51
|
+
* `contenteditable`.
|
|
52
|
+
*/
|
|
53
|
+
hasDocumentSemantics?: boolean;
|
|
54
|
+
/** Whether a `- [ ]` checkbox can be ticked. Defaults to `true`. */
|
|
55
|
+
isTaskListInteractive?: boolean;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* The facet itself is exported so a consumer can put **only this**
|
|
59
|
+
* in a `Compartment` and flip one answer without reconfiguring the
|
|
60
|
+
* whole extension — which would tear down and recreate the
|
|
61
|
+
* `StateField`s beside it.
|
|
62
|
+
*
|
|
63
|
+
* A later input wins per key, and a key nobody stated keeps the
|
|
64
|
+
* default. That is a merge rather than "the last one wins outright",
|
|
65
|
+
* so an override that only cares about checkboxes cannot silently
|
|
66
|
+
* turn document semantics back off.
|
|
67
|
+
*/
|
|
68
|
+
export declare const livePreviewOptions: Facet<LivePreviewOptions, Required<LivePreviewOptions>>;
|
|
29
69
|
/** Toggle the "edit Markdown" mode. */
|
|
30
70
|
export declare const setLivePreviewRawMode: import("@codemirror/state").StateEffectType<boolean>;
|
|
31
71
|
/**
|
|
@@ -36,5 +76,5 @@ export declare const setLivePreviewRawMode: import("@codemirror/state").StateEff
|
|
|
36
76
|
* without a round trip through React.
|
|
37
77
|
*/
|
|
38
78
|
export declare const livePreviewRawModeField: StateField<boolean>;
|
|
39
|
-
export declare const livePreview: () => Extension;
|
|
79
|
+
export declare const livePreview: (options?: LivePreviewOptions) => Extension;
|
|
40
80
|
//# sourceMappingURL=livePreview.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"livePreview.d.ts","sourceRoot":"","sources":["../../src/markdownEditorCodeMirror/livePreview.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAGH,OAAO,KAAK,EAEV,SAAS,EAEV,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAGL,UAAU,EACX,MAAM,mBAAmB,CAAA;AAyC1B,uCAAuC;AACvC,eAAO,MAAM,qBAAqB,sDACH,CAAA;AAE/B;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,qBAchC,CAAA;
|
|
1
|
+
{"version":3,"file":"livePreview.d.ts","sourceRoot":"","sources":["../../src/markdownEditorCodeMirror/livePreview.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAGH,OAAO,KAAK,EAEV,SAAS,EAEV,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EACL,KAAK,EAGL,UAAU,EACX,MAAM,mBAAmB,CAAA;AAyC1B;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B;;;;;;;;OAQG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAC9B,oEAAoE;IACpE,qBAAqB,CAAC,EAAE,OAAO,CAAA;CAChC,CAAA;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB,yDAmB7B,CAAA;AAOF,uCAAuC;AACvC,eAAO,MAAM,qBAAqB,sDACH,CAAA;AAE/B;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,qBAchC,CAAA;AAmkCJ,eAAO,MAAM,WAAW,GACtB,UAAS,kBAAuB,KAC/B,SA6BF,CAAA"}
|