@stesura/react-wordgard 0.1.0 → 0.2.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.
Files changed (72) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +699 -237
  3. package/dist/ReactWordgard.js +2 -4
  4. package/dist/components/ChildNodeViews.js +2 -4
  5. package/dist/components/DecorationRoot.d.ts +6 -0
  6. package/dist/components/DecorationRoot.js +7 -4
  7. package/dist/components/ErrorBoundary.js +3 -5
  8. package/dist/components/SeparatorHackView.js +1 -2
  9. package/dist/components/ShapeView.js +2 -4
  10. package/dist/components/TextNodeView.js +2 -3
  11. package/dist/components/TrailingHackView.js +2 -3
  12. package/dist/components/WidgetView.js +2 -5
  13. package/dist/components/WordgardDoc.d.ts +5 -0
  14. package/dist/components/WordgardDoc.js +8 -6
  15. package/dist/components/WordgardEditor.d.ts +8 -0
  16. package/dist/components/WordgardEditor.js +10 -6
  17. package/dist/components/WordgardScope.d.ts +14 -0
  18. package/dist/components/WordgardScope.js +14 -0
  19. package/dist/components/WrapperView.js +1 -4
  20. package/dist/components/marks/MarkView.js +3 -6
  21. package/dist/components/marks/ReactMarkView.js +2 -4
  22. package/dist/components/nodes/DefaultNodeView.js +3 -7
  23. package/dist/components/nodes/DocNodeView.js +2 -3
  24. package/dist/composition.js +4 -6
  25. package/dist/contexts/DecorationContext.js +2 -5
  26. package/dist/contexts/DecorationRootContext.js +2 -4
  27. package/dist/contexts/EditorContext.d.ts +1 -0
  28. package/dist/contexts/EditorContext.js +3 -3
  29. package/dist/contexts/RenderStoreContext.js +3 -5
  30. package/dist/contexts/editorEvents.js +3 -6
  31. package/dist/decorations/ReactMarkType.d.ts +11 -0
  32. package/dist/decorations/ReactMarkType.js +6 -7
  33. package/dist/decorations/ReactShapeType.d.ts +16 -0
  34. package/dist/decorations/ReactShapeType.js +9 -11
  35. package/dist/decorations/ReactWidgetType.d.ts +10 -0
  36. package/dist/decorations/ReactWidgetType.js +6 -8
  37. package/dist/decorations/ReactWrapperType.d.ts +9 -0
  38. package/dist/decorations/ReactWrapperType.js +3 -6
  39. package/dist/decorations/iterDeco.js +3 -6
  40. package/dist/hooks/useEditorEffect.d.ts +7 -0
  41. package/dist/hooks/useEditorEffect.js +11 -7
  42. package/dist/hooks/useEditorEventCallback.d.ts +6 -0
  43. package/dist/hooks/useEditorEventCallback.js +7 -3
  44. package/dist/hooks/useEditorEventListener.d.ts +7 -0
  45. package/dist/hooks/useEditorEventListener.js +11 -11
  46. package/dist/hooks/useEditorLayoutEffect.d.ts +9 -0
  47. package/dist/hooks/useEditorLayoutEffect.js +13 -17
  48. package/dist/hooks/useEditorState.d.ts +5 -0
  49. package/dist/hooks/useEditorState.js +6 -4
  50. package/dist/hooks/useEditorStateSelector.d.ts +5 -0
  51. package/dist/hooks/useEditorStateSelector.js +5 -0
  52. package/dist/hooks/useIgnoreMutation.d.ts +5 -1
  53. package/dist/hooks/useIgnoreMutation.js +11 -10
  54. package/dist/hooks/useIsNodeSelected.d.ts +1 -0
  55. package/dist/hooks/useIsNodeSelected.js +3 -4
  56. package/dist/hooks/useMergedDOMRefs.d.ts +4 -0
  57. package/dist/hooks/useMergedDOMRefs.js +5 -4
  58. package/dist/hooks/useNodePos.d.ts +3 -0
  59. package/dist/hooks/useNodePos.js +5 -6
  60. package/dist/hooks/useSelectNode.d.ts +4 -0
  61. package/dist/hooks/useSelectNode.js +5 -4
  62. package/dist/hooks/useStopEvent.d.ts +6 -0
  63. package/dist/hooks/useStopEvent.js +8 -7
  64. package/dist/hooks/useWordgard.d.ts +1 -0
  65. package/dist/hooks/useWordgard.js +1 -2
  66. package/dist/index.d.ts +1 -0
  67. package/dist/index.js +2 -0
  68. package/dist/tile.js +3 -8
  69. package/dist/viewRoots.js +2 -3
  70. package/dist/wordgardInternals.js +2 -5
  71. package/package.json +1 -1
  72. package/skills/react-wordgard/SKILL.md +138 -10
@@ -1,9 +1,5 @@
1
- // Named after ReactWidgetType: a React wrapper is a real Wordgard `Elt` with a
2
- // hole, the wrapper plain Wordgard draws (`Decoration.Tag.wrapper`,
3
- // `Decoration.Point.wrapper`), registered as drawn by a component, which
4
- // renders what it wraps as `children`. Registered as React shapes are (the
5
- // reserved attribute); its props are a React shape's without `contentDOMRef`:
6
- // `children` goes where the hole is.
1
+ // Named after ReactWidgetType. A React wrapper is a real Wordgard `Elt` with a hole, registered as
2
+ // React shapes are (the reserved attribute).
7
3
  import { createElement, } from "react";
8
4
  import { Elt } from "wordgard/doc";
9
5
  import { takesRef } from "../helpers/refs.js";
@@ -12,6 +8,7 @@ import { idOf, reserved } from "./ReactShapeType.js";
12
8
  const draws = new Map();
13
9
  // By component, then root element, as React shapes are.
14
10
  const wrappers = new WeakMap();
11
+ /** A wrapper, for `Decoration.Tag.wrapper` and `Decoration.Point.wrapper`. */
15
12
  export const createWrapper = (component, { element = "div" } = {}) => {
16
13
  let registered = wrappers.get(component);
17
14
  if (!registered) {
@@ -1,9 +1,6 @@
1
- // Named after react-prosemirror's iterDeco, rewritten for Wordgard: lays out a
2
- // plot's content from Wordgard's DecoIterator (copied in ./DecoIterator.ts),
3
- // the way its ContentUpdate.build does (src/editor/tile.ts in its repo), for
4
- // React to render and ReactDocTile to build tiles over. Each plot is laid out
5
- // on its own; a plot that kept its identity outside the ranges an update
6
- // redraws keeps its previous layout.
1
+ // Named after react-prosemirror's iterDeco, rewritten for Wordgard: lays out a plot's content from
2
+ // DecoIterator, as Wordgard's ContentUpdate.build does. A plot that kept its identity outside the
3
+ // redrawn ranges keeps its previous layout.
7
4
  import { Attributes, Elt, Leaf, Mark } from "wordgard/doc";
8
5
  import { Widget } from "wordgard/editor";
9
6
  import { isKept, pointMapper, samePlace, unchangedEnds } from "../changes.js";
@@ -1,3 +1,10 @@
1
1
  import { type DependencyList } from "react";
2
2
  import type { Wordgard } from "wordgard/editor";
3
+ /**
4
+ * Runs `effect` once the editor's DOM is up to date: after the flush under way or pending, else in
5
+ * a microtask, never inside React's commit. It may read layout and dispatch; what it sets shows a
6
+ * frame later. For focus, follow-up dispatches and menus. The dependencies work as React's. Don't
7
+ * read the editor's layout from a plain React effect: it can run inside Wordgard's update, before
8
+ * the editor's view of the new DOM exists.
9
+ */
3
10
  export declare const useEditorEffect: (effect: (wg: Wordgard) => void | (() => void), dependencies?: DependencyList) => void;
@@ -1,10 +1,13 @@
1
- // Named after react-prosemirror's useEditorEffect, rewritten for Wordgard: an
2
- // effect that runs once the editor's DOM is up to date, after the flush under
3
- // way or pending, else in a microtask, never inside React's commit, so it may
4
- // read the editor's layout and dispatch. A plain React effect may run inside
5
- // the flush, before the tiles over the new DOM exist.
1
+ // Named after react-prosemirror's useEditorEffect, rewritten for Wordgard.
6
2
  import { useLayoutEffect } from "react";
7
3
  import { useEditorContext } from "../contexts/EditorContext.js";
4
+ /**
5
+ * Runs `effect` once the editor's DOM is up to date: after the flush under way or pending, else in
6
+ * a microtask, never inside React's commit. It may read layout and dispatch; what it sets shows a
7
+ * frame later. For focus, follow-up dispatches and menus. The dependencies work as React's. Don't
8
+ * read the editor's layout from a plain React effect: it can run inside Wordgard's update, before
9
+ * the editor's view of the new DOM exists.
10
+ */
8
11
  export const useEditorEffect = (effect, dependencies) => {
9
12
  const { wg, afterFlush, cancel } = useEditorContext();
10
13
  useLayoutEffect(() => {
@@ -18,7 +21,8 @@ export const useEditorEffect = (effect, dependencies) => {
18
21
  if (typeof cleanup == "function")
19
22
  cleanup();
20
23
  };
21
- // The caller's dependencies, as with React's own effects.
24
+ // The caller's dependencies, as with React's own effects, and the editor:
25
+ // a WordgardScope can change it.
22
26
  // eslint-disable-next-line react-hooks/exhaustive-deps
23
- }, dependencies);
27
+ }, dependencies && [wg, ...dependencies]);
24
28
  };
@@ -1,2 +1,8 @@
1
1
  import type { Wordgard } from "wordgard/editor";
2
+ /**
3
+ * A stable function calling the latest `callback` with the editor first, for event handlers. There,
4
+ * `wg.state` is the latest state, dispatches not yet drawn included. See the `SaveButton` in
5
+ * [Accessing the state outside the
6
+ * editor](https://github.com/stesura/react-wordgard#accessing-the-state-outside-the-editor).
7
+ */
2
8
  export declare const useEditorEventCallback: <Args extends unknown[], Result>(callback: (wg: Wordgard, ...args: Args) => Result) => (...args: Args) => Result;
@@ -1,8 +1,12 @@
1
- // Named after react-prosemirror's useEditorEventCallback, rewritten for
2
- // Wordgard: a stable function calling the latest `callback` with the editor,
3
- // for event handlers.
1
+ // Named after react-prosemirror's useEditorEventCallback, rewritten for Wordgard.
4
2
  import { useCallback, useLayoutEffect, useRef } from "react";
5
3
  import { useEditorContext } from "../contexts/EditorContext.js";
4
+ /**
5
+ * A stable function calling the latest `callback` with the editor first, for event handlers. There,
6
+ * `wg.state` is the latest state, dispatches not yet drawn included. See the `SaveButton` in
7
+ * [Accessing the state outside the
8
+ * editor](https://github.com/stesura/react-wordgard#accessing-the-state-outside-the-editor).
9
+ */
6
10
  export const useEditorEventCallback = (callback) => {
7
11
  const { wg } = useEditorContext();
8
12
  const latest = useRef(callback);
@@ -1,3 +1,10 @@
1
1
  import type { Wordgard } from "wordgard/editor";
2
2
  import { type EditorEvent } from "../contexts/editorEvents.js";
3
+ /**
4
+ * Listens to the editor's DOM events of `type`, before Wordgard's handlers and keymaps. Returning
5
+ * `true` handles the event: Wordgard calls `preventDefault` and skips its own handling. The event
6
+ * types are listed under [Limits](https://github.com/stesura/react-wordgard#limits). Key events
7
+ * during a composition don't come; handling `input`, `beforeinput` or a composition event skips
8
+ * Wordgard's own handling of it, so leave typing to Wordgard.
9
+ */
3
10
  export declare const useEditorEventListener: <Type extends EditorEvent>(type: Type, handler: (wg: Wordgard, event: HTMLElementEventMap[Type]) => boolean | void) => void;
@@ -1,18 +1,18 @@
1
- // Named after react-prosemirror's useEditorEventListener, rewritten for
2
- // Wordgard: `handler` gets the editor's DOM events of `type` before Wordgard's
3
- // own handlers and keymaps, as react-prosemirror's `handleDOMEvents` came
4
- // before plugins: ReactWordgard installs a Wordgard.domEventHandler for each
5
- // type, at the highest precedence. Returning true says it handled the event:
6
- // Wordgard then calls `preventDefault` and goes no further. The latest
7
- // registered runs first, as in react-prosemirror. Unlike there, key events
8
- // during a composition don't come (Wordgard ignores them first), and handling
9
- // `input`, `beforeinput` or a composition event skips Wordgard's own tracking
10
- // of it. Among `prec.highest` handlers, these come first in an editor made
11
- // from `config`, after the state's own in one made from a `state`.
1
+ // Named after react-prosemirror's useEditorEventListener, rewritten for Wordgard: ReactWordgard
2
+ // installs a `prec.highest` Wordgard.domEventHandler per type, first in an editor made from
3
+ // `config`, after the state's own in one made from a `state`. The latest registered runs first, as
4
+ // in react-prosemirror.
12
5
  import { useLayoutEffect } from "react";
13
6
  import { useEditorContext } from "../contexts/EditorContext.js";
14
7
  import { listenersOf } from "../contexts/editorEvents.js";
15
8
  import { useEditorEventCallback } from "./useEditorEventCallback.js";
9
+ /**
10
+ * Listens to the editor's DOM events of `type`, before Wordgard's handlers and keymaps. Returning
11
+ * `true` handles the event: Wordgard calls `preventDefault` and skips its own handling. The event
12
+ * types are listed under [Limits](https://github.com/stesura/react-wordgard#limits). Key events
13
+ * during a composition don't come; handling `input`, `beforeinput` or a composition event skips
14
+ * Wordgard's own handling of it, so leave typing to Wordgard.
15
+ */
16
16
  export const useEditorEventListener = (type, handler) => {
17
17
  const { wg } = useEditorContext();
18
18
  const listener = useEditorEventCallback(handler);
@@ -1,6 +1,15 @@
1
1
  import type { Wordgard } from "wordgard/editor";
2
+ /** The `read` and `write` pair `useEditorLayoutEffect` takes. */
2
3
  export type EditorLayoutEffect<T> = {
3
4
  read: (wg: Wordgard) => T | undefined;
4
5
  write: (value: T, wg: Wordgard) => void;
5
6
  };
7
+ /**
8
+ * Runs inside every editor update, without re-rendering: `read` in Wordgard's read phase, then
9
+ * `write` with its value in the write phase, on the same paint as the document. For what moves with
10
+ * the text (markers in the margin, pagination). It can't dispatch: Wordgard throws during its
11
+ * update. A `read` returning `undefined` skips its write, and when no read asks for a write, none
12
+ * is scheduled, which spares Wordgard measuring again. Reads run in the order the effects were
13
+ * registered.
14
+ */
6
15
  export declare const useEditorLayoutEffect: <T>(measure: EditorLayoutEffect<T>) => void;
@@ -1,22 +1,18 @@
1
- // The other half of react-prosemirror's useEditorEffect, which ran inside
2
- // React's commit: what it set showed on the same paint. Here React commits
3
- // inside Wordgard's flush, so useEditorEffect runs after it, and this hook
4
- // runs in it, as React's useLayoutEffect to useEffect. It measures the
5
- // editor's layout and writes from it on the same paint as the document update:
6
- // `read` runs in every flush's read phase, `write` with its result in the
7
- // write phase after (Wordgard's scheduleDOMRead and scheduleDOMWrite), React
8
- // state it sets committing there too. A write that changes the size of the
9
- // editor's content makes Wordgard update and measure again within the flush,
10
- // so pagination can settle there. A `read` returning undefined skips its write;
11
- // with no write to do, none is scheduled, which spares Wordgard measuring
12
- // again. Reads run in registration order: an engine whose parts must run in
13
- // another order registers one effect and drives them. Neither may dispatch
14
- // (Wordgard throws during
15
- // its flush): dispatch from useEditorEffect or an event handler. Writes into
16
- // the document's own DOM are foreign edits to Wordgard: write inside widgets,
17
- // outside the editor, or through decorations.
1
+ // The other half of react-prosemirror's useEditorEffect, which ran inside React's commit. Here
2
+ // React commits inside Wordgard's flush: useEditorEffect runs after it, this hook in it, through
3
+ // Wordgard's scheduleDOMRead and scheduleDOMWrite. A write resizing the content makes Wordgard
4
+ // measure again within the flush, so pagination settles there. Writes into the document's own DOM
5
+ // are foreign edits to Wordgard: write inside widgets, outside the editor, or through decorations.
18
6
  import { useLayoutEffect, useRef } from "react";
19
7
  import { useEditorContext } from "../contexts/EditorContext.js";
8
+ /**
9
+ * Runs inside every editor update, without re-rendering: `read` in Wordgard's read phase, then
10
+ * `write` with its value in the write phase, on the same paint as the document. For what moves with
11
+ * the text (markers in the margin, pagination). It can't dispatch: Wordgard throws during its
12
+ * update. A `read` returning `undefined` skips its write, and when no read asks for a write, none
13
+ * is scheduled, which spares Wordgard measuring again. Reads run in the order the effects were
14
+ * registered.
15
+ */
20
16
  export const useEditorLayoutEffect = (measure) => {
21
17
  const { wg, measures, requestMeasure } = useEditorContext();
22
18
  const latest = useRef(measure);
@@ -1 +1,6 @@
1
+ /**
2
+ * The state as of the editor's last flush, rendered in the same React commit as the document. The
3
+ * component re-renders on every flush: for a part of the state, use `useEditorStateSelector`; in a
4
+ * handler, `wg.state` is the latest.
5
+ */
1
6
  export declare const useEditorState: () => import("wordgard/state").GardState;
@@ -1,9 +1,11 @@
1
- // Named after react-prosemirror's useEditorState, rewritten for Wordgard: the
2
- // state the editor's DOM shows, as of its last flush. Re-renders on every
3
- // state change, in the same commit as the document; `wg.state` is ahead of it
4
- // between a dispatch and the flush.
1
+ // Named after react-prosemirror's useEditorState, rewritten for Wordgard.
5
2
  import { useSyncExternalStore } from "react";
6
3
  import { useEditorContext } from "../contexts/EditorContext.js";
4
+ /**
5
+ * The state as of the editor's last flush, rendered in the same React commit as the document. The
6
+ * component re-renders on every flush: for a part of the state, use `useEditorStateSelector`; in a
7
+ * handler, `wg.state` is the latest.
8
+ */
7
9
  export const useEditorState = () => {
8
10
  const { store } = useEditorContext();
9
11
  return useSyncExternalStore(store.subscribe, store.getState);
@@ -1,2 +1,7 @@
1
1
  import type { GardState } from "wordgard/state";
2
+ /**
3
+ * A part of the last flush's state, like Redux's `useSelector`: the component re-renders only when
4
+ * the result changes, by `Object.is` or by `isEqual`. A selector building a new object or array
5
+ * each time re-renders on every flush unless you pass `isEqual`.
6
+ */
2
7
  export declare const useEditorStateSelector: <Result>(selector: (state: GardState) => Result, isEqual?: (a: Result, b: Result) => boolean) => Result;
@@ -1,5 +1,10 @@
1
1
  import { useSyncExternalStoreWithSelector } from "use-sync-external-store/with-selector";
2
2
  import { useEditorContext } from "../contexts/EditorContext.js";
3
+ /**
4
+ * A part of the last flush's state, like Redux's `useSelector`: the component re-renders only when
5
+ * the result changes, by `Object.is` or by `isEqual`. A selector building a new object or array
6
+ * each time re-renders on every flush unless you pass `isEqual`.
7
+ */
3
8
  export const useEditorStateSelector = (selector, isEqual) => {
4
9
  const { store } = useEditorContext();
5
10
  return useSyncExternalStoreWithSelector(store.subscribe, store.getState, store.getState, selector, isEqual);
@@ -1,2 +1,6 @@
1
1
  import type { Wordgard } from "wordgard/editor";
2
- export declare const useIgnoreMutation: (ignoreMutation: (wg: Wordgard, mutation: MutationRecord) => boolean) => void;
2
+ /**
3
+ * Wordgard doesn't read the DOM mutations `ignore` returns `true` for, typing aside (Wordgard takes
4
+ * it from `beforeinput`). Mutations in chrome and React widgets never reach Wordgard anyway.
5
+ */
6
+ export declare const useIgnoreMutation: (ignore: (wg: Wordgard, mutation: MutationRecord) => boolean) => void;
@@ -1,18 +1,19 @@
1
- // Named after react-prosemirror's useIgnoreMutation, rewritten for Wordgard:
2
- // when `ignoreMutation` returns true for a DOM mutation inside the React
3
- // decoration (its content included), Wordgard doesn't read it. Mutations in
4
- // the decoration's chrome never reach Wordgard anyway (`sortRecords`). Typing
5
- // isn't stopped: Wordgard takes it from `beforeinput`, not the mutation.
1
+ // Named after react-prosemirror's useIgnoreMutation, rewritten for Wordgard. Chrome mutations are
2
+ // dropped before, by `sortRecords`.
6
3
  import { useLayoutEffect } from "react";
7
4
  import { useDecorationContext } from "../contexts/DecorationContext.js";
8
5
  import { useEditorEventCallback } from "./useEditorEventCallback.js";
9
- export const useIgnoreMutation = (ignoreMutation) => {
6
+ /**
7
+ * Wordgard doesn't read the DOM mutations `ignore` returns `true` for, typing aside (Wordgard takes
8
+ * it from `beforeinput`). Mutations in chrome and React widgets never reach Wordgard anyway.
9
+ */
10
+ export const useIgnoreMutation = (ignore) => {
10
11
  const { handlers } = useDecorationContext("useIgnoreMutation");
11
- const ignore = useEditorEventCallback(ignoreMutation);
12
+ const callback = useEditorEventCallback(ignore);
12
13
  useLayoutEffect(() => {
13
- handlers.ignoreMutation.add(ignore);
14
+ handlers.ignoreMutation.add(callback);
14
15
  return () => {
15
- handlers.ignoreMutation.delete(ignore);
16
+ handlers.ignoreMutation.delete(callback);
16
17
  };
17
- }, [handlers, ignore]);
18
+ }, [handlers, callback]);
18
19
  };
@@ -1 +1,2 @@
1
+ /** Whether the node the decoration draws is the editor's node selection. */
1
2
  export declare const useIsNodeSelected: () => boolean;
@@ -1,6 +1,5 @@
1
- // Named after react-prosemirror's useIsNodeSelected, rewritten for Wordgard:
2
- // whether the node the React decoration a component is drawn in draws is the
3
- // node selection, read from the class Wordgard draws it with
4
- // (`wg-selected-node`), so the component renders with it, without an effect.
1
+ // Named after react-prosemirror's useIsNodeSelected, rewritten for Wordgard: read from the
2
+ // `wg-selected-node` class Wordgard draws, so the component renders with it, without an effect.
5
3
  import { useDecorationContext } from "../contexts/DecorationContext.js";
4
+ /** Whether the node the decoration draws is the editor's node selection. */
6
5
  export const useIsNodeSelected = () => useDecorationContext("useIsNodeSelected").selected;
@@ -1,2 +1,6 @@
1
1
  import { type Ref, type RefCallback } from "react";
2
+ /**
3
+ * One ref setting several, as for a root that's also the content element (`ref` and
4
+ * `contentDOMRef`).
5
+ */
2
6
  export declare const useMergedDOMRefs: <Value>(...refs: (Ref<Value> | undefined)[]) => RefCallback<Value>;
@@ -1,8 +1,9 @@
1
- // Adapted from react-prosemirror's useMergedDOMRefs (src/refs.ts there),
2
- // without its typecast: one ref callback setting each of `refs`, as a React
3
- // decoration's component gives its root both the forwarded `ref` and
4
- // `contentDOMRef`, or a measuring ref.
1
+ // Adapted from react-prosemirror's useMergedDOMRefs (src/refs.ts there), without its typecast.
5
2
  import { useCallback } from "react";
3
+ /**
4
+ * One ref setting several, as for a root that's also the content element (`ref` and
5
+ * `contentDOMRef`).
6
+ */
6
7
  export const useMergedDOMRefs = (...refs) => useCallback((value) => {
7
8
  for (const ref of refs) {
8
9
  if (typeof ref == "function")
@@ -1 +1,4 @@
1
+ /**
2
+ * A function giving the position of what the decoration draws, to call in handlers and effects.
3
+ */
1
4
  export declare const useNodePos: () => () => number;
@@ -1,8 +1,7 @@
1
- // Named after react-prosemirror's useNodePos, rewritten for Wordgard: the
2
- // position of what the React decoration a component is drawn in draws (a
3
- // shape's, wrapper's or widget's node, or where a mark or point widget
4
- // starts), read from its tile. Returns a stable function, for event handlers
5
- // and effects, rather than the position: during render the tile may not be
6
- // built yet.
1
+ // Named after react-prosemirror's useNodePos, rewritten for Wordgard: read from the decoration's
2
+ // tile. A function rather than the position: during render, the tile may not be built yet.
7
3
  import { useDecorationContext } from "../contexts/DecorationContext.js";
4
+ /**
5
+ * A function giving the position of what the decoration draws, to call in handlers and effects.
6
+ */
8
7
  export const useNodePos = () => useDecorationContext("useNodePos").handlers.getPos;
@@ -1,2 +1,6 @@
1
1
  import type { Wordgard } from "wordgard/editor";
2
+ /**
3
+ * Calls `selectNode` when the node becomes the node selection, and `deselectNode` when it stops
4
+ * being it, once the editor's DOM is up to date (as `useEditorEffect`).
5
+ */
2
6
  export declare const useSelectNode: (selectNode: (wg: Wordgard) => void, deselectNode?: (wg: Wordgard) => void) => void;
@@ -1,12 +1,13 @@
1
- // Named after react-prosemirror's useSelectNode, rewritten for Wordgard:
2
- // `selectNode` runs once the node the React decoration draws becomes the node
3
- // selection, `deselectNode` once it stops being it, after the flush that draws
4
- // the change, as ProseMirror's NodeView.selectNode and deselectNode.
1
+ // Named after react-prosemirror's useSelectNode, rewritten for Wordgard.
5
2
  import { useRef } from "react";
6
3
  import { useEditorEffect } from "./useEditorEffect.js";
7
4
  import { useEditorEventCallback } from "./useEditorEventCallback.js";
8
5
  import { useIsNodeSelected } from "./useIsNodeSelected.js";
9
6
  const noop = () => { };
7
+ /**
8
+ * Calls `selectNode` when the node becomes the node selection, and `deselectNode` when it stops
9
+ * being it, once the editor's DOM is up to date (as `useEditorEffect`).
10
+ */
10
11
  export const useSelectNode = (selectNode, deselectNode = noop) => {
11
12
  const selected = useIsNodeSelected();
12
13
  const select = useEditorEventCallback(selectNode);
@@ -1,2 +1,8 @@
1
1
  import type { Wordgard } from "wordgard/editor";
2
+ /**
3
+ * Asked for events in the decoration's chrome (its root and the elements around its content
4
+ * element, unless the root is the content element) and in React widgets; never for those in its
5
+ * content. For an event it stops, Wordgard's handlers and keymaps are skipped and the browser's
6
+ * default goes ahead: for a control in the chrome, such as an input.
7
+ */
2
8
  export declare const useStopEvent: (stopEvent: (wg: Wordgard, event: Event) => boolean) => void;
@@ -1,13 +1,14 @@
1
- // Named after react-prosemirror's useStopEvent, rewritten for Wordgard: when
2
- // `stopEvent` returns true, Wordgard doesn't handle the event, nor do its
3
- // handlers and keymaps, and the browser's default goes ahead. It's consulted
4
- // through widget types' `propagateEvent`, for events in a React decoration's
5
- // chrome or in a React widget, its own or a nested one's, and through its own
6
- // elements' tiles (tile.ts `ownTile`), for events on them; not for those in
7
- // its content.
1
+ // Named after react-prosemirror's useStopEvent, rewritten for Wordgard: consulted through widget
2
+ // types' `propagateEvent`, and through the decoration's own elements' tiles (tile.ts `ownTile`).
8
3
  import { useLayoutEffect } from "react";
9
4
  import { useDecorationContext } from "../contexts/DecorationContext.js";
10
5
  import { useEditorEventCallback } from "./useEditorEventCallback.js";
6
+ /**
7
+ * Asked for events in the decoration's chrome (its root and the elements around its content
8
+ * element, unless the root is the content element) and in React widgets; never for those in its
9
+ * content. For an event it stops, Wordgard's handlers and keymaps are skipped and the browser's
10
+ * default goes ahead: for a control in the chrome, such as an input.
11
+ */
11
12
  export const useStopEvent = (stopEvent) => {
12
13
  const { handlers } = useDecorationContext("useStopEvent");
13
14
  const stop = useEditorEventCallback(stopEvent);
@@ -1 +1,2 @@
1
+ /** The editor. */
1
2
  export declare const useWordgard: () => import("wordgard/editor").Wordgard;
@@ -1,4 +1,3 @@
1
- // The editor a component is drawn in, or under (WordgardEditor): what a
2
- // widget's `render` gets.
3
1
  import { useEditorContext } from "../contexts/EditorContext.js";
2
+ /** The editor. */
4
3
  export const useWordgard = () => useEditorContext().wg;
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export { DecorationRoot, type DecorationRootProps } from "./components/DecorationRoot.js";
2
2
  export { WordgardDoc } from "./components/WordgardDoc.js";
3
3
  export { WordgardEditor } from "./components/WordgardEditor.js";
4
+ export { WordgardScope } from "./components/WordgardScope.js";
4
5
  export { type MarkComponentProps, type MarkShapeOptions, createMarkShape, } from "./decorations/ReactMarkType.js";
5
6
  export { type ReactShape, type ShapeComponentProps, createShape, } from "./decorations/ReactShapeType.js";
6
7
  export { type WidgetTypeSpec, type WidgetComponentProps, createWidgetType, } from "./decorations/ReactWidgetType.js";
package/dist/index.js CHANGED
@@ -1,6 +1,8 @@
1
+ "use client";
1
2
  export { DecorationRoot } from "./components/DecorationRoot.js";
2
3
  export { WordgardDoc } from "./components/WordgardDoc.js";
3
4
  export { WordgardEditor } from "./components/WordgardEditor.js";
5
+ export { WordgardScope } from "./components/WordgardScope.js";
4
6
  export { createMarkShape, } from "./decorations/ReactMarkType.js";
5
7
  export { createShape, } from "./decorations/ReactShapeType.js";
6
8
  export { createWidgetType, } from "./decorations/ReactWidgetType.js";
package/dist/tile.js CHANGED
@@ -1,11 +1,6 @@
1
- // Takes the role of Wordgard's own DocTile (src/editor/tile.ts in its repo):
2
- // React draws the document and the tiles are built over React's DOM, as
3
- // react-prosemirror's view descriptions are built over the DOM its components
4
- // render. Native widgets aren't React's: their DOM is rendered, reused,
5
- // connected and placed here, following Wordgard's ContentUpdate;
6
- // `setUneditable` is copied from it. React widgets are drawn by React, and
7
- // connected here. React commits into a root of its own, or, for an editor
8
- // WordgardEditor hosts, a portal in the app's tree (WordgardDoc).
1
+ // Takes the role of Wordgard's DocTile (src/editor/tile.ts in its repo): the tiles are built over
2
+ // React's DOM, as react-prosemirror's view descriptions are. Native widgets' DOM is placed here,
3
+ // following Wordgard's ContentUpdate (`setUneditable` is copied from it).
9
4
  import { createElement } from "react";
10
5
  import { flushSync } from "react-dom";
11
6
  import { createRoot } from "react-dom/client";
package/dist/viewRoots.js CHANGED
@@ -1,6 +1,5 @@
1
- // The elements React drew for node views and mark wrappers: what a container's
2
- // content is made of, so a React shape's chrome beside its content (in the
3
- // same element) can be told apart from it.
1
+ // The elements React drew for node views and mark wrappers, to tell a React shape's chrome from its
2
+ // content.
4
3
  export const viewRoots = new WeakSet();
5
4
  export const viewRoot = (dom) => {
6
5
  if (dom)
@@ -1,8 +1,5 @@
1
- // The one place that depends on Wordgard internals missing from its typings
2
- // (src/editor/tile.ts, decoration.ts and editor.ts, and src/doc/node.ts in its
3
- // repo, at the pinned version). The declarations below are trusted, not
4
- // checked: re-read them against Wordgard's source after `npm run
5
- // update-wordgard`.
1
+ // The one place depending on Wordgard internals missing from its typings. The declarations are
2
+ // trusted, not checked: re-read them against Wordgard's source after `npm run update-wordgard`.
6
3
  import { Elt } from "wordgard/doc";
7
4
  import { Decoration, Widget, Wordgard } from "wordgard/editor";
8
5
  import { basicSchema } from "wordgard/schema";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stesura/react-wordgard",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "A React view for the Wordgard rich text editor",
5
5
  "keywords": [
6
6
  "react",