@foir/sdk 0.1.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/README.md +110 -0
- package/dist/auth.d.ts +305 -0
- package/dist/auth.js +309 -0
- package/dist/chunk-ADO5FJNM.js +30 -0
- package/dist/client.d.ts +83 -0
- package/dist/client.js +81 -0
- package/dist/codegen/block-registry.d.ts +27 -0
- package/dist/codegen/block-registry.js +35 -0
- package/dist/codegen/block-scaffold.d.ts +28 -0
- package/dist/codegen/block-scaffold.js +45 -0
- package/dist/codegen/index.d.ts +65 -0
- package/dist/codegen/index.js +80 -0
- package/dist/content.d.ts +96 -0
- package/dist/content.js +161 -0
- package/dist/index.d.ts +394 -0
- package/dist/index.js +476 -0
- package/dist/media.d.ts +234 -0
- package/dist/media.js +399 -0
- package/dist/realtime.d.ts +111 -0
- package/dist/realtime.js +168 -0
- package/dist/server.d.ts +457 -0
- package/dist/server.js +296 -0
- package/dist/shared-DkWlEd2Z.d.ts +18 -0
- package/package.json +136 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { CodegenConfig } from '@graphql-codegen/cli';
|
|
2
|
+
export { B as BlockPluginConfig } from '../shared-DkWlEd2Z.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* `@foir/sdk/codegen` — the Foir graphql-codegen preset.
|
|
6
|
+
*
|
|
7
|
+
* `foirCodegen()` returns a complete `@graphql-codegen/client-preset` config so
|
|
8
|
+
* a consumer's `codegen.ts` is ~3 lines instead of the ~109 every storefront
|
|
9
|
+
* hand-copies. It standardizes the Foir conventions (custom scalars,
|
|
10
|
+
* `enumValues: 'keep'`, the `X-Foir-Schema-Channel: draft` introspection header)
|
|
11
|
+
* AND wires the block-registry plugin so the exhaustive `BlockRegistry` type is
|
|
12
|
+
* regenerated alongside the typed documents.
|
|
13
|
+
*
|
|
14
|
+
* `foirScaffold()` is the separate one-shot that writes the ContentRenderer
|
|
15
|
+
* shell (run once; the dev owns the output).
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
interface FoirCodegenOptions {
|
|
19
|
+
/** Foir public API GraphQL endpoint. */
|
|
20
|
+
apiUrl: string;
|
|
21
|
+
/**
|
|
22
|
+
* Public (`pk_`) API key. Reads the published schema. Used as the fallback
|
|
23
|
+
* when no preview key is set.
|
|
24
|
+
*/
|
|
25
|
+
apiKey?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Preview (`sk_`) key with `drafts:read`. Reads the DRAFT schema channel so
|
|
28
|
+
* freshly pushed (un-promoted) model changes are visible to codegen.
|
|
29
|
+
* Strongly preferred — a `pk_` key only sees the published schema.
|
|
30
|
+
*/
|
|
31
|
+
previewKey?: string;
|
|
32
|
+
/** Output directory for the generated client types. Default `./app/gql/foir/`. */
|
|
33
|
+
outputDir?: string;
|
|
34
|
+
/** Operation document glob(s). Default `app/graphql/foir/**\/*.graphql`. */
|
|
35
|
+
documents?: string | string[];
|
|
36
|
+
/** Block union name(s) for the registry. Default `['Block']`. */
|
|
37
|
+
blockUnions?: string[];
|
|
38
|
+
/** Generated registry file path. Default `<outputDir>block-registry.ts`. */
|
|
39
|
+
registryFile?: string;
|
|
40
|
+
/** Extra/override custom scalars merged onto the Foir defaults. */
|
|
41
|
+
scalars?: Record<string, unknown>;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Build the continuous Foir codegen config: typed documents + the exhaustive
|
|
45
|
+
* block registry. Drop it into `codegen.ts` and run `graphql-codegen`.
|
|
46
|
+
*/
|
|
47
|
+
declare function foirCodegen(options: FoirCodegenOptions): CodegenConfig;
|
|
48
|
+
interface FoirScaffoldOptions extends FoirCodegenOptions {
|
|
49
|
+
/** Output path for the scaffold. Default `<outputDir>ContentRenderer.tsx`. */
|
|
50
|
+
scaffoldFile?: string;
|
|
51
|
+
/** Render-context type threaded to every block component. Default `unknown`. */
|
|
52
|
+
ctxType?: string;
|
|
53
|
+
/** Optional ctx-type import line, e.g. `import type { Ctx } from '~/ctx'`. */
|
|
54
|
+
ctxImport?: string;
|
|
55
|
+
/** Import path for the generated registry type. Default `./block-registry`. */
|
|
56
|
+
registryImport?: string;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Build the one-shot scaffold config — writes a ContentRenderer shell with a
|
|
60
|
+
* typed stub per block type. Run once (`graphql-codegen --config scaffold.ts`),
|
|
61
|
+
* then own and implement the output.
|
|
62
|
+
*/
|
|
63
|
+
declare function foirScaffold(options: FoirScaffoldOptions): CodegenConfig;
|
|
64
|
+
|
|
65
|
+
export { type FoirCodegenOptions, type FoirScaffoldOptions, foirCodegen, foirScaffold };
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_UNIONS
|
|
3
|
+
} from "../chunk-ADO5FJNM.js";
|
|
4
|
+
|
|
5
|
+
// src/codegen/preset.ts
|
|
6
|
+
var DEFAULT_OUTPUT_DIR = "./app/gql/foir/";
|
|
7
|
+
var DEFAULT_DOCUMENTS = "app/graphql/foir/**/*.graphql";
|
|
8
|
+
var FOIR_SCALARS = {
|
|
9
|
+
JSON: { input: "unknown", output: "{ [key: string]: unknown }" },
|
|
10
|
+
DateTime: "string"
|
|
11
|
+
};
|
|
12
|
+
function normalizeDir(dir) {
|
|
13
|
+
return dir.endsWith("/") ? dir : `${dir}/`;
|
|
14
|
+
}
|
|
15
|
+
function schemaEntry(apiUrl, key) {
|
|
16
|
+
return [
|
|
17
|
+
{
|
|
18
|
+
[apiUrl]: {
|
|
19
|
+
headers: {
|
|
20
|
+
"x-api-key": key ?? "",
|
|
21
|
+
// Read the draft channel so freshly pushed changes are visible
|
|
22
|
+
// (silently ignored by public keys, which can't see drafts).
|
|
23
|
+
"X-Foir-Schema-Channel": "draft"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
];
|
|
28
|
+
}
|
|
29
|
+
function foirCodegen(options) {
|
|
30
|
+
const previewKey = options.previewKey ?? options.apiKey;
|
|
31
|
+
const outputDir = normalizeDir(options.outputDir ?? DEFAULT_OUTPUT_DIR);
|
|
32
|
+
const documents = options.documents ?? DEFAULT_DOCUMENTS;
|
|
33
|
+
const blockUnions = options.blockUnions ?? DEFAULT_UNIONS;
|
|
34
|
+
const registryFile = options.registryFile ?? `${outputDir}block-registry.ts`;
|
|
35
|
+
return {
|
|
36
|
+
schema: schemaEntry(options.apiUrl, previewKey),
|
|
37
|
+
documents,
|
|
38
|
+
ignoreNoDocuments: true,
|
|
39
|
+
generates: {
|
|
40
|
+
[outputDir]: {
|
|
41
|
+
preset: "client",
|
|
42
|
+
presetConfig: { fragmentMasking: false },
|
|
43
|
+
config: {
|
|
44
|
+
useTypeImports: true,
|
|
45
|
+
namingConvention: { enumValues: "keep" },
|
|
46
|
+
scalars: { ...FOIR_SCALARS, ...options.scalars ?? {} }
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
[registryFile]: {
|
|
50
|
+
plugins: ["@foir/sdk/codegen/block-registry"],
|
|
51
|
+
config: { unions: blockUnions }
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function foirScaffold(options) {
|
|
57
|
+
const previewKey = options.previewKey ?? options.apiKey;
|
|
58
|
+
const outputDir = normalizeDir(options.outputDir ?? DEFAULT_OUTPUT_DIR);
|
|
59
|
+
const scaffoldFile = options.scaffoldFile ?? `${outputDir}ContentRenderer.tsx`;
|
|
60
|
+
const blockUnions = options.blockUnions ?? DEFAULT_UNIONS;
|
|
61
|
+
return {
|
|
62
|
+
schema: schemaEntry(options.apiUrl, previewKey),
|
|
63
|
+
ignoreNoDocuments: true,
|
|
64
|
+
generates: {
|
|
65
|
+
[scaffoldFile]: {
|
|
66
|
+
plugins: ["@foir/sdk/codegen/block-scaffold"],
|
|
67
|
+
config: {
|
|
68
|
+
unions: blockUnions,
|
|
69
|
+
ctxType: options.ctxType,
|
|
70
|
+
ctxImport: options.ctxImport,
|
|
71
|
+
registryImport: options.registryImport
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
export {
|
|
78
|
+
foirCodegen,
|
|
79
|
+
foirScaffold
|
|
80
|
+
};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { ReactNode, JSX } from 'react';
|
|
2
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* createContentRenderer — the generic content-dispatch primitive.
|
|
6
|
+
*
|
|
7
|
+
* It does NOT know any concrete block types. Given a registry that maps each
|
|
8
|
+
* block `__typename` to a component, it renders a `flexible` field's block
|
|
9
|
+
* array: looks each block up by `__typename`, renders unknown types as null,
|
|
10
|
+
* keys by the block `_id`, assigns LCP `priority` to the first block that
|
|
11
|
+
* actually renders, and threads a consumer-defined `Ctx` to every component.
|
|
12
|
+
*
|
|
13
|
+
* The "switch" is a typed object lookup. Exhaustiveness is enforced where the
|
|
14
|
+
* registry is BUILT — against the exhaustive `BlockRegistry` type the codegen
|
|
15
|
+
* plugin (EID-92) generates per project, so omitting a block type is a compile
|
|
16
|
+
* error there, not a runtime surprise here. The consumer can supply different
|
|
17
|
+
* registries per surface (e.g. web vs email).
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/** Minimal shape every block in a `flexible` field carries. */
|
|
21
|
+
interface BlockLike {
|
|
22
|
+
__typename: string;
|
|
23
|
+
_id?: string | null;
|
|
24
|
+
}
|
|
25
|
+
/** Props a block component receives from the content renderer. */
|
|
26
|
+
interface BlockComponentProps<TBlock, TCtx = unknown> {
|
|
27
|
+
block: TBlock;
|
|
28
|
+
/** Consumer-defined render context (host product, locale, etc.). */
|
|
29
|
+
ctx: TCtx;
|
|
30
|
+
/** True only for the first block that renders — the LCP candidate. */
|
|
31
|
+
priority: boolean;
|
|
32
|
+
}
|
|
33
|
+
/** A component that renders one block type. */
|
|
34
|
+
type BlockComponent<TBlock, TCtx = unknown> = (props: BlockComponentProps<TBlock, TCtx>) => ReactNode;
|
|
35
|
+
/**
|
|
36
|
+
* A registry mapping `__typename` → block component. In practice this is the
|
|
37
|
+
* exhaustive `BlockRegistry` type the codegen plugin emits for the project;
|
|
38
|
+
* here it's the structural lower bound the renderer needs.
|
|
39
|
+
*/
|
|
40
|
+
type BlockRegistry<TCtx = unknown> = Record<string, BlockComponent<any, TCtx>>;
|
|
41
|
+
interface ContentRendererProps<TCtx> {
|
|
42
|
+
blocks: readonly BlockLike[] | null | undefined;
|
|
43
|
+
/** Consumer-defined context passed to every block component. */
|
|
44
|
+
ctx: TCtx;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Build a `<ContentRenderer>` bound to a block registry. Returns a component
|
|
48
|
+
* that takes the `flexible` field's `blocks` array plus a `ctx`.
|
|
49
|
+
*/
|
|
50
|
+
declare function createContentRenderer<TCtx = unknown>(registry: BlockRegistry<TCtx>): (props: ContentRendererProps<TCtx>) => ReactNode;
|
|
51
|
+
|
|
52
|
+
/** A resolved typography token from the project's design tokens. */
|
|
53
|
+
interface TypographyToken {
|
|
54
|
+
/** Token key referenced by a Lexical node's `style`. */
|
|
55
|
+
name: string;
|
|
56
|
+
/** Rendered HTML element (paragraph / h1–h6 / blockquote). */
|
|
57
|
+
tag?: string;
|
|
58
|
+
value: {
|
|
59
|
+
fontFamily: string[];
|
|
60
|
+
fontSize: string | number;
|
|
61
|
+
fontWeight: string | number;
|
|
62
|
+
lineHeight: string | number;
|
|
63
|
+
letterSpacing: string | number;
|
|
64
|
+
textTransform?: string;
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Provide the project's resolved typography tokens to every nested
|
|
69
|
+
* `<RichTextRenderer>` so callers don't thread them through each usage. A
|
|
70
|
+
* `tokens` prop on the renderer still takes precedence.
|
|
71
|
+
*/
|
|
72
|
+
declare function RichTextTypographyProvider({ tokens, children, }: {
|
|
73
|
+
tokens: TypographyToken[];
|
|
74
|
+
children: ReactNode;
|
|
75
|
+
}): react_jsx_runtime.JSX.Element;
|
|
76
|
+
/** Read the injected typography tokens (null when no provider is mounted). */
|
|
77
|
+
declare function useRichTextTypography(): TypographyToken[] | null;
|
|
78
|
+
interface RichtextValueLike {
|
|
79
|
+
html?: string | null;
|
|
80
|
+
json?: unknown;
|
|
81
|
+
}
|
|
82
|
+
interface RichTextRendererProps {
|
|
83
|
+
value: RichtextValueLike | null | undefined;
|
|
84
|
+
className?: string;
|
|
85
|
+
/**
|
|
86
|
+
* Resolved typography tokens. Overrides any provided via
|
|
87
|
+
* `<RichTextTypographyProvider>`. When neither is set, paragraphs render
|
|
88
|
+
* with `fallbackTag` and no inline typography.
|
|
89
|
+
*/
|
|
90
|
+
tokens?: TypographyToken[];
|
|
91
|
+
/** Tag used for any paragraph with no token style and no Lexical tag. */
|
|
92
|
+
fallbackTag?: keyof JSX.IntrinsicElements;
|
|
93
|
+
}
|
|
94
|
+
declare function RichTextRenderer({ value, className, tokens, fallbackTag, }: RichTextRendererProps): react_jsx_runtime.JSX.Element | null;
|
|
95
|
+
|
|
96
|
+
export { type BlockComponent, type BlockComponentProps, type BlockLike, type BlockRegistry, type ContentRendererProps, RichTextRenderer, type RichTextRendererProps, RichTextTypographyProvider, type TypographyToken, createContentRenderer, useRichTextTypography };
|
package/dist/content.js
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
// src/content/content-renderer.tsx
|
|
2
|
+
import { Fragment } from "react";
|
|
3
|
+
import { Fragment as Fragment2, jsx } from "react/jsx-runtime";
|
|
4
|
+
function createContentRenderer(registry) {
|
|
5
|
+
return function ContentRenderer({ blocks, ctx }) {
|
|
6
|
+
if (!blocks?.length) return null;
|
|
7
|
+
let priorityAssigned = false;
|
|
8
|
+
return /* @__PURE__ */ jsx(Fragment2, { children: blocks.map((block, i) => {
|
|
9
|
+
const Component = registry[block.__typename];
|
|
10
|
+
if (!Component) return null;
|
|
11
|
+
const priority = !priorityAssigned;
|
|
12
|
+
priorityAssigned = true;
|
|
13
|
+
return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(Component, { block, ctx, priority }) }, block._id ?? i);
|
|
14
|
+
}) });
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// src/content/richtext.tsx
|
|
19
|
+
import {
|
|
20
|
+
createContext,
|
|
21
|
+
createElement,
|
|
22
|
+
useContext
|
|
23
|
+
} from "react";
|
|
24
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
25
|
+
function safeHref(url) {
|
|
26
|
+
if (!url) return "#";
|
|
27
|
+
const trimmed = url.trim();
|
|
28
|
+
if (trimmed.startsWith("/") || trimmed.startsWith("#") || trimmed.startsWith("?") || trimmed.startsWith("./") || trimmed.startsWith("../")) {
|
|
29
|
+
return trimmed;
|
|
30
|
+
}
|
|
31
|
+
const normalized = Array.from(trimmed).filter((ch) => ch.charCodeAt(0) > 32).join("").toLowerCase();
|
|
32
|
+
const schemeMatch = normalized.match(/^([a-z][a-z0-9+.-]*):/);
|
|
33
|
+
if (!schemeMatch) return trimmed;
|
|
34
|
+
const allowed = ["http", "https", "mailto", "tel"];
|
|
35
|
+
return allowed.includes(schemeMatch[1]) ? trimmed : "#";
|
|
36
|
+
}
|
|
37
|
+
var TypographyContext = createContext(null);
|
|
38
|
+
function RichTextTypographyProvider({
|
|
39
|
+
tokens,
|
|
40
|
+
children
|
|
41
|
+
}) {
|
|
42
|
+
return /* @__PURE__ */ jsx2(TypographyContext.Provider, { value: tokens, children });
|
|
43
|
+
}
|
|
44
|
+
function useRichTextTypography() {
|
|
45
|
+
return useContext(TypographyContext);
|
|
46
|
+
}
|
|
47
|
+
var TEXT_FORMAT_BOLD = 1;
|
|
48
|
+
var TEXT_FORMAT_ITALIC = 2;
|
|
49
|
+
var TEXT_FORMAT_STRIKETHROUGH = 4;
|
|
50
|
+
var TEXT_FORMAT_UNDERLINE = 8;
|
|
51
|
+
var TEXT_FORMAT_CODE = 16;
|
|
52
|
+
function RichTextRenderer({
|
|
53
|
+
value,
|
|
54
|
+
className,
|
|
55
|
+
tokens,
|
|
56
|
+
fallbackTag = "p"
|
|
57
|
+
}) {
|
|
58
|
+
const contextTokens = useRichTextTypography();
|
|
59
|
+
const typography = tokens ?? contextTokens ?? [];
|
|
60
|
+
if (!value) return null;
|
|
61
|
+
if (!value.json) {
|
|
62
|
+
if (value.html) {
|
|
63
|
+
return /* @__PURE__ */ jsx2(
|
|
64
|
+
"div",
|
|
65
|
+
{
|
|
66
|
+
className,
|
|
67
|
+
dangerouslySetInnerHTML: { __html: value.html }
|
|
68
|
+
}
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
const root = value.json && typeof value.json === "object" ? value.json.root ?? null : null;
|
|
74
|
+
if (!root || !Array.isArray(root.children) || root.children.length === 0) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
const tokenMap = /* @__PURE__ */ new Map();
|
|
78
|
+
for (const t of typography) {
|
|
79
|
+
tokenMap.set(t.name, t);
|
|
80
|
+
}
|
|
81
|
+
return /* @__PURE__ */ jsx2("div", { className, children: root.children.map(
|
|
82
|
+
(node, i) => renderBlockNode(node, i, tokenMap, fallbackTag)
|
|
83
|
+
) });
|
|
84
|
+
}
|
|
85
|
+
function renderBlockNode(node, key, tokens, fallbackTag) {
|
|
86
|
+
const type = node.type;
|
|
87
|
+
if (type === "paragraph" || type === "heading" || type === "quote") {
|
|
88
|
+
const styleKey = typeof node.style === "string" && node.style.length > 0 ? node.style : null;
|
|
89
|
+
const token = styleKey ? tokens.get(styleKey) ?? null : null;
|
|
90
|
+
const tag = pickTag(node, token, fallbackTag);
|
|
91
|
+
const styleProps = tokenToCss(token, node.format);
|
|
92
|
+
const children = (node.children ?? []).map(
|
|
93
|
+
(child, i) => renderInlineNode(child, i)
|
|
94
|
+
);
|
|
95
|
+
return createElement(tag, { key, style: styleProps }, ...children);
|
|
96
|
+
}
|
|
97
|
+
if (type === "list") {
|
|
98
|
+
const ordered = node.listType === "number";
|
|
99
|
+
const ListTag = ordered ? "ol" : "ul";
|
|
100
|
+
return /* @__PURE__ */ jsx2(ListTag, { children: (node.children ?? []).map((child, i) => /* @__PURE__ */ jsx2("li", { children: (child.children ?? []).map((c, j) => renderInlineNode(c, j)) }, i)) }, key);
|
|
101
|
+
}
|
|
102
|
+
if (Array.isArray(node.children)) {
|
|
103
|
+
return /* @__PURE__ */ jsx2("div", { children: node.children.map((child, i) => renderInlineNode(child, i)) }, key);
|
|
104
|
+
}
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
function renderInlineNode(node, key) {
|
|
108
|
+
if (node.type === "text") {
|
|
109
|
+
const formatBits = typeof node.format === "number" ? node.format : 0;
|
|
110
|
+
let el = node.text ?? "";
|
|
111
|
+
if (formatBits & TEXT_FORMAT_CODE) el = /* @__PURE__ */ jsx2("code", { children: el });
|
|
112
|
+
if (formatBits & TEXT_FORMAT_BOLD) el = /* @__PURE__ */ jsx2("strong", { children: el });
|
|
113
|
+
if (formatBits & TEXT_FORMAT_ITALIC) el = /* @__PURE__ */ jsx2("em", { children: el });
|
|
114
|
+
if (formatBits & TEXT_FORMAT_UNDERLINE) el = /* @__PURE__ */ jsx2("u", { children: el });
|
|
115
|
+
if (formatBits & TEXT_FORMAT_STRIKETHROUGH) el = /* @__PURE__ */ jsx2("s", { children: el });
|
|
116
|
+
return /* @__PURE__ */ jsx2("span", { children: el }, key);
|
|
117
|
+
}
|
|
118
|
+
if (node.type === "linebreak") return /* @__PURE__ */ jsx2("br", {}, key);
|
|
119
|
+
if (node.type === "link") {
|
|
120
|
+
const href = safeHref(node.url);
|
|
121
|
+
const target = node.target;
|
|
122
|
+
const rel = target && target !== "_self" ? "noopener noreferrer" : void 0;
|
|
123
|
+
return /* @__PURE__ */ jsx2("a", { href, target, rel, children: (node.children ?? []).map((c, i) => renderInlineNode(c, i)) }, key);
|
|
124
|
+
}
|
|
125
|
+
if (Array.isArray(node.children)) {
|
|
126
|
+
return /* @__PURE__ */ jsx2("span", { children: node.children.map((c, i) => renderInlineNode(c, i)) }, key);
|
|
127
|
+
}
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
function pickTag(node, token, fallback) {
|
|
131
|
+
if (token?.tag) return token.tag;
|
|
132
|
+
if (node.type === "heading" && typeof node.tag === "string") {
|
|
133
|
+
return node.tag;
|
|
134
|
+
}
|
|
135
|
+
if (node.type === "quote") return "blockquote";
|
|
136
|
+
return fallback;
|
|
137
|
+
}
|
|
138
|
+
function tokenToCss(token, paragraphFormat) {
|
|
139
|
+
const style = {};
|
|
140
|
+
if (token) {
|
|
141
|
+
const v = token.value;
|
|
142
|
+
style.fontFamily = v.fontFamily.join(", ");
|
|
143
|
+
style.fontSize = v.fontSize;
|
|
144
|
+
style.fontWeight = v.fontWeight;
|
|
145
|
+
style.lineHeight = v.lineHeight;
|
|
146
|
+
style.letterSpacing = v.letterSpacing;
|
|
147
|
+
if (v.textTransform) {
|
|
148
|
+
style.textTransform = v.textTransform;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (typeof paragraphFormat === "string" && paragraphFormat !== "") {
|
|
152
|
+
style.textAlign = paragraphFormat;
|
|
153
|
+
}
|
|
154
|
+
return Object.keys(style).length > 0 ? style : void 0;
|
|
155
|
+
}
|
|
156
|
+
export {
|
|
157
|
+
RichTextRenderer,
|
|
158
|
+
RichTextTypographyProvider,
|
|
159
|
+
createContentRenderer,
|
|
160
|
+
useRichTextTypography
|
|
161
|
+
};
|