applesauce-react 0.6.0 → 0.7.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/helpers/build-link-renderer.d.ts +4 -0
- package/dist/helpers/build-link-renderer.js +23 -0
- package/dist/helpers/index.d.ts +2 -0
- package/dist/helpers/index.js +2 -0
- package/dist/helpers/nast.d.ts +12 -0
- package/dist/helpers/nast.js +15 -0
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.js +2 -0
- package/dist/hooks/use-observable.js +29 -8
- package/dist/hooks/use-render-nast.d.ts +6 -0
- package/dist/hooks/use-render-nast.js +6 -0
- package/dist/hooks/use-rendered-text-content.d.ts +18 -0
- package/dist/hooks/use-rendered-text-content.js +16 -0
- package/package.json +7 -2
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import { Link } from "applesauce-content/nast";
|
|
3
|
+
export type LinkRenderer = (url: URL, node: Link) => JSX.Element | false | null;
|
|
4
|
+
export declare function buildLinkRenderer(handlers: LinkRenderer[]): import("react").NamedExoticComponent<import("./nast.js").ExtraProps<Link>>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { memo, useMemo } from "react";
|
|
3
|
+
export function buildLinkRenderer(handlers) {
|
|
4
|
+
const LinkRenderer = ({ node }) => {
|
|
5
|
+
const content = useMemo(() => {
|
|
6
|
+
try {
|
|
7
|
+
const url = new URL(node.href);
|
|
8
|
+
for (const handler of handlers) {
|
|
9
|
+
try {
|
|
10
|
+
const content = handler(url, node);
|
|
11
|
+
if (content)
|
|
12
|
+
return content;
|
|
13
|
+
}
|
|
14
|
+
catch (e) { }
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
catch (error) { }
|
|
18
|
+
return null;
|
|
19
|
+
}, [node.href, node.value]);
|
|
20
|
+
return content || _jsx(_Fragment, { children: node.value });
|
|
21
|
+
};
|
|
22
|
+
return memo(LinkRenderer);
|
|
23
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Content, ContentMap, Root } from "applesauce-content/nast";
|
|
3
|
+
type Component<ComponentProps> = React.FunctionComponent<ComponentProps> | React.ComponentClass;
|
|
4
|
+
export type ExtraProps<T extends Content> = {
|
|
5
|
+
node: T;
|
|
6
|
+
};
|
|
7
|
+
export type ComponentMap = Partial<{
|
|
8
|
+
[k in keyof ContentMap]: Component<ExtraProps<ContentMap[k]>>;
|
|
9
|
+
}>;
|
|
10
|
+
/** Render a nostr syntax tree to JSX components */
|
|
11
|
+
export declare function renderNast(root: Root, components: ComponentMap): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
/** Render a nostr syntax tree to JSX components */
|
|
3
|
+
export function renderNast(root, components) {
|
|
4
|
+
const indexes = {};
|
|
5
|
+
return (_jsx(_Fragment, { children: root.children.map((node) => {
|
|
6
|
+
indexes[node.type] = indexes[node.type] ?? 0;
|
|
7
|
+
const index = indexes[node.type];
|
|
8
|
+
indexes[node.type]++;
|
|
9
|
+
const Component = components[node.type];
|
|
10
|
+
if (!Component)
|
|
11
|
+
return null;
|
|
12
|
+
// @ts-expect-error
|
|
13
|
+
return _jsx(Component, { node: node }, node.type + "-" + index);
|
|
14
|
+
}) }));
|
|
15
|
+
}
|
package/dist/hooks/index.d.ts
CHANGED
package/dist/hooks/index.js
CHANGED
|
@@ -2,16 +2,37 @@ import { useState, useEffect } from "react";
|
|
|
2
2
|
import { isStateful } from "applesauce-core/observable";
|
|
3
3
|
/** Subscribe to the value of an observable */
|
|
4
4
|
export function useObservable(observable) {
|
|
5
|
-
const [
|
|
6
|
-
const
|
|
5
|
+
const [_count, update] = useState(0);
|
|
6
|
+
// const init = useRef(true);
|
|
7
|
+
// const value = useRef<T | undefined>(observable && isStateful(observable) ? observable.value : undefined);
|
|
8
|
+
// const prev = useRef(observable);
|
|
9
|
+
// const sub = useRef<ZenObservable.Subscription>();
|
|
10
|
+
// // This intentionally does not use useEffect
|
|
11
|
+
// // because we want the value to be returned on the first render
|
|
12
|
+
// if (!sub.current || prev.current !== observable) {
|
|
13
|
+
// prev.current = observable;
|
|
14
|
+
// if (sub.current) sub.current.unsubscribe();
|
|
15
|
+
// sub.current = observable?.subscribe((v) => {
|
|
16
|
+
// value.current = v;
|
|
17
|
+
// // only explicitly update if its not the first render
|
|
18
|
+
// if (!init.current) update(count + 1);
|
|
19
|
+
// });
|
|
20
|
+
// init.current = false;
|
|
21
|
+
// }
|
|
22
|
+
// // unsubscribe when unmount
|
|
23
|
+
// useEffect(() => {
|
|
24
|
+
// return () => {
|
|
25
|
+
// if (sub.current) sub.current.unsubscribe();
|
|
26
|
+
// };
|
|
27
|
+
// }, []);
|
|
28
|
+
// return value.current;
|
|
29
|
+
const [value, setValue] = useState(observable && isStateful(observable) ? observable.value : undefined);
|
|
7
30
|
useEffect(() => {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
update(v);
|
|
12
|
-
forceUpdate(Math.random());
|
|
31
|
+
const sub = observable?.subscribe((v) => {
|
|
32
|
+
setValue(v);
|
|
33
|
+
update((c) => c + 1);
|
|
13
34
|
});
|
|
14
|
-
return () =>
|
|
35
|
+
return () => sub?.unsubscribe();
|
|
15
36
|
}, [observable]);
|
|
16
37
|
return value;
|
|
17
38
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import { Root } from "applesauce-content/nast";
|
|
3
|
+
import { ComponentMap } from "../helpers/nast.js";
|
|
4
|
+
export { ComponentMap };
|
|
5
|
+
/** A hook to get the rendered output of a nostr syntax tree */
|
|
6
|
+
export declare function useRenderNast(root: Root | undefined, components: ComponentMap): JSX.Element | null;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import { renderNast } from "../helpers/nast.js";
|
|
3
|
+
/** A hook to get the rendered output of a nostr syntax tree */
|
|
4
|
+
export function useRenderNast(root, components) {
|
|
5
|
+
return useMemo(() => (root ? renderNast(root, components) : null), [root, Object.keys(components).join("|")]);
|
|
6
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import { getParsedTextContent } from "applesauce-content/text";
|
|
3
|
+
import { EventTemplate, NostrEvent } from "nostr-tools";
|
|
4
|
+
import { ComponentMap } from "../helpers/nast.js";
|
|
5
|
+
import { LinkRenderer } from "../helpers/build-link-renderer.js";
|
|
6
|
+
export { ComponentMap };
|
|
7
|
+
type Options = {
|
|
8
|
+
/** Override transformers */
|
|
9
|
+
transformers?: Parameters<typeof getParsedTextContent>[2];
|
|
10
|
+
/** If set will use {@link buildLinkRenderer} to render links */
|
|
11
|
+
linkRenderers?: LinkRenderer[];
|
|
12
|
+
/** Override event content */
|
|
13
|
+
content?: string;
|
|
14
|
+
/** Maximum length */
|
|
15
|
+
maxLength?: number;
|
|
16
|
+
};
|
|
17
|
+
/** Returns the parsed and render text content for an event */
|
|
18
|
+
export declare function useRenderedContent(event: NostrEvent | EventTemplate | string | undefined, components: ComponentMap, opts?: Options): JSX.Element | null;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import { getParsedTextContent } from "applesauce-content/text";
|
|
3
|
+
import { useRenderNast } from "./use-render-nast.js";
|
|
4
|
+
import { buildLinkRenderer } from "../helpers/build-link-renderer.js";
|
|
5
|
+
import { truncateContent } from "applesauce-content/nast";
|
|
6
|
+
/** Returns the parsed and render text content for an event */
|
|
7
|
+
export function useRenderedContent(event, components, opts) {
|
|
8
|
+
// if link renderers are set, override the link components
|
|
9
|
+
const _components = useMemo(() => (opts?.linkRenderers ? { ...components, link: buildLinkRenderer(opts.linkRenderers) } : components), [opts?.linkRenderers, components]);
|
|
10
|
+
// add additional transformers
|
|
11
|
+
const nast = useMemo(() => (event ? getParsedTextContent(event, opts?.content, opts?.transformers) : undefined), [event, opts?.content, opts?.transformers]);
|
|
12
|
+
let truncated = nast;
|
|
13
|
+
if (opts?.maxLength && nast)
|
|
14
|
+
truncated = truncateContent(nast, opts.maxLength);
|
|
15
|
+
return useRenderNast(truncated, _components);
|
|
16
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "applesauce-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "React hooks for applesauce",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -26,10 +26,15 @@
|
|
|
26
26
|
"./hooks/*": {
|
|
27
27
|
"import": "./dist/hooks/*.js",
|
|
28
28
|
"types": "./dist/hooks/*.d.ts"
|
|
29
|
+
},
|
|
30
|
+
"./helpers": {
|
|
31
|
+
"import": "./dist/helpers/index.js",
|
|
32
|
+
"types": "./dist/helpers/index.d.ts"
|
|
29
33
|
}
|
|
30
34
|
},
|
|
31
35
|
"dependencies": {
|
|
32
|
-
"applesauce-
|
|
36
|
+
"applesauce-content": "^0.7.0",
|
|
37
|
+
"applesauce-core": "^0.7.0",
|
|
33
38
|
"nostr-tools": "^2.7.2",
|
|
34
39
|
"react": "^18.3.1",
|
|
35
40
|
"zen-observable": "^0.10.0"
|