@tristan2828/ui-foundation 4.61.0 → 4.61.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.
|
@@ -814,8 +814,9 @@ export default function MilkdownEditor({ id, defaultValue, onChange, onProblem,
|
|
|
814
814
|
void editor.destroy();
|
|
815
815
|
root.replaceChildren();
|
|
816
816
|
};
|
|
817
|
-
// Made once per mount
|
|
818
|
-
//
|
|
817
|
+
// Made once per mount. A new defaultValue the editor didn't report
|
|
818
|
+
// remounts it (RichTextEditor's key), so it isn't read here.
|
|
819
|
+
// Attributes and read-only follow below.
|
|
819
820
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
820
821
|
}, []);
|
|
821
822
|
// Read-only and the ARIA attributes follow their props.
|
|
@@ -3,7 +3,11 @@ import type { HeadingFrom, HeadingLevel } from './prose-look.js';
|
|
|
3
3
|
export type RichTextEditorProps = {
|
|
4
4
|
/** The editable element's id, for a label's `htmlFor` or a test. */
|
|
5
5
|
id?: string;
|
|
6
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* The Markdown it opens with. A later value the editor didn't report
|
|
8
|
+
* (a record loaded after it mounted, a reset) replaces the text; the
|
|
9
|
+
* value it reported, passed back, changes nothing.
|
|
10
|
+
*/
|
|
7
11
|
defaultValue: string;
|
|
8
12
|
/** The Markdown to save, after every edit: untouched blocks as originally written. */
|
|
9
13
|
onChange: (markdown: string) => void;
|
|
@@ -26,13 +26,41 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
26
26
|
// With `foldKey`, the text folds by its headings (NotesOutline lists them
|
|
27
27
|
// beside it), the Markdown untouched.
|
|
28
28
|
//
|
|
29
|
+
// `defaultValue` is read when it mounts, then followed: a new value the
|
|
30
|
+
// editor didn't report itself (a form's loaded record arriving after the
|
|
31
|
+
// editor, a reset) replaces the text, as if it opened with it. Its own
|
|
32
|
+
// edits, coming back as the value, change nothing, so the caret stays
|
|
33
|
+
// where it is. A caller that never feeds `onChange` back keeps its text.
|
|
34
|
+
//
|
|
29
35
|
// The editor (Milkdown) is loaded on first use, so a page that only reads
|
|
30
36
|
// Markdown never downloads it. Until it arrives, a placeholder the size of
|
|
31
37
|
// the field shows.
|
|
32
|
-
import { lazy, Suspense } from 'react';
|
|
38
|
+
import { lazy, Suspense, useState } from 'react';
|
|
33
39
|
import { cn } from 'cn';
|
|
34
40
|
import { Skeleton } from '../ui/skeleton.js';
|
|
35
41
|
const MilkdownEditor = lazy(() => import('./milkdown-editor.js'));
|
|
42
|
+
// When the editor opens again: each time `defaultValue` changes to
|
|
43
|
+
// Markdown it didn't report. Adjusted while rendering (React's "storing
|
|
44
|
+
// information from previous renders"), so the new text never shows a
|
|
45
|
+
// frame late.
|
|
46
|
+
function useOpenings(defaultValue) {
|
|
47
|
+
const [seen, setSeen] = useState(defaultValue);
|
|
48
|
+
const [reported, setReported] = useState(defaultValue);
|
|
49
|
+
const [opening, setOpening] = useState(0);
|
|
50
|
+
if (defaultValue !== seen) {
|
|
51
|
+
setSeen(defaultValue);
|
|
52
|
+
if (defaultValue !== reported) {
|
|
53
|
+
setReported(defaultValue);
|
|
54
|
+
setOpening(opening + 1);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return { opening, report: setReported };
|
|
58
|
+
}
|
|
36
59
|
export function RichTextEditor(props) {
|
|
37
|
-
|
|
60
|
+
const { opening, report } = useOpenings(props.defaultValue);
|
|
61
|
+
const { onChange } = props;
|
|
62
|
+
return (_jsx(Suspense, { fallback: _jsxs("div", { "data-slot": "rich-text-editor", "data-state": "loading", className: cn('flex w-full flex-col', !props.inline && 'rounded-lg border border-input', props.className), children: [(props.toolbar ?? true) === true && _jsx("div", { className: "h-9 border-b border-input" }), _jsxs("div", { className: cn('flex flex-col gap-2', props.inline ? 'py-0.5' : 'min-h-24 px-2.5 py-2'), children: [_jsx(Skeleton, { className: "h-4 w-3/4" }), _jsx(Skeleton, { className: "h-4 w-1/2" })] })] }), children: _jsx(MilkdownEditor, { ...props, onChange: (markdown) => {
|
|
63
|
+
report(markdown);
|
|
64
|
+
onChange(markdown);
|
|
65
|
+
}, foldKey: props.foldable === false ? undefined : props.foldKey }, opening) }));
|
|
38
66
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tristan2828/ui-foundation",
|
|
3
|
-
"version": "4.61.
|
|
3
|
+
"version": "4.61.1",
|
|
4
4
|
"description": "Shared UI foundation for contract-first, database-backed React apps: shadcn primitives, DataTable/EntityForm composites, auth, the gateway error seam, design tokens, lint rules and the AI playbook.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|