@portabletext/astro 0.0.0 → 0.2.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/CHANGELOG.md +17 -0
- package/LICENSE +15 -0
- package/README.md +338 -27
- package/components/Block.astro +36 -0
- package/components/HardBreak.astro +7 -0
- package/components/List.astro +24 -0
- package/components/ListItem.astro +9 -0
- package/components/Mark.astro +32 -0
- package/components/PortableText.astro +483 -0
- package/components/Text.astro +9 -0
- package/components/UnknownBlock.astro +7 -0
- package/components/UnknownList.astro +7 -0
- package/components/UnknownListItem.astro +7 -0
- package/components/UnknownMark.astro +7 -0
- package/components/UnknownType.astro +21 -0
- package/lib/astro.d.ts +9 -0
- package/lib/components.ts +4 -0
- package/lib/context.ts +21 -0
- package/lib/index.ts +2 -0
- package/lib/internal.ts +183 -0
- package/lib/types.ts +469 -0
- package/lib/utils.d.ts +26 -0
- package/lib/utils.ts +3 -0
- package/lib/warnings.ts +42 -0
- package/package.json +89 -7
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
---
|
|
2
|
+
import {
|
|
3
|
+
isPortableTextBlock,
|
|
4
|
+
isPortableTextListItemBlock,
|
|
5
|
+
isPortableTextToolkitList,
|
|
6
|
+
isPortableTextToolkitSpan,
|
|
7
|
+
isPortableTextToolkitTextNode,
|
|
8
|
+
nestLists,
|
|
9
|
+
buildMarksTree,
|
|
10
|
+
LIST_NEST_MODE_HTML,
|
|
11
|
+
} from "@portabletext/toolkit";
|
|
12
|
+
|
|
13
|
+
import type {
|
|
14
|
+
Component,
|
|
15
|
+
Context,
|
|
16
|
+
MissingComponentHandler,
|
|
17
|
+
NodeType,
|
|
18
|
+
PortableTextComponents,
|
|
19
|
+
PortableTextProps,
|
|
20
|
+
Props as ComponentProps,
|
|
21
|
+
RenderOptions,
|
|
22
|
+
TypedObject,
|
|
23
|
+
} from "../lib/types";
|
|
24
|
+
|
|
25
|
+
import {
|
|
26
|
+
isComponent,
|
|
27
|
+
isSlotName,
|
|
28
|
+
mergeComponents,
|
|
29
|
+
setNodeComponents,
|
|
30
|
+
getNodeComponents,
|
|
31
|
+
toSlotName,
|
|
32
|
+
} from "../lib/internal";
|
|
33
|
+
import type { SlotNodeType } from "../lib/internal";
|
|
34
|
+
|
|
35
|
+
import {
|
|
36
|
+
getWarningMessage,
|
|
37
|
+
ignoredChildrenWarning,
|
|
38
|
+
printWarning,
|
|
39
|
+
unknownSlotWarning,
|
|
40
|
+
} from "../lib/warnings";
|
|
41
|
+
import { key as contextRef } from "../lib/context";
|
|
42
|
+
|
|
43
|
+
import Block from "./Block.astro";
|
|
44
|
+
import HardBreak from "./HardBreak.astro";
|
|
45
|
+
import List from "./List.astro";
|
|
46
|
+
import ListItem from "./ListItem.astro";
|
|
47
|
+
import Mark from "./Mark.astro";
|
|
48
|
+
import Text from "./Text.astro";
|
|
49
|
+
import UnknownBlock from "./UnknownBlock.astro";
|
|
50
|
+
import UnknownList from "./UnknownList.astro";
|
|
51
|
+
import UnknownListItem from "./UnknownListItem.astro";
|
|
52
|
+
import UnknownMark from "./UnknownMark.astro";
|
|
53
|
+
import UnknownType from "./UnknownType.astro";
|
|
54
|
+
|
|
55
|
+
export type Props = PortableTextProps;
|
|
56
|
+
|
|
57
|
+
const {
|
|
58
|
+
value,
|
|
59
|
+
components: componentOverrides = {},
|
|
60
|
+
listNestingMode = LIST_NEST_MODE_HTML,
|
|
61
|
+
onMissingComponent = true,
|
|
62
|
+
} = Astro.props;
|
|
63
|
+
|
|
64
|
+
const components = mergeComponents(
|
|
65
|
+
{
|
|
66
|
+
type: {},
|
|
67
|
+
unknownType: UnknownType,
|
|
68
|
+
block: {
|
|
69
|
+
h1: Block,
|
|
70
|
+
h2: Block,
|
|
71
|
+
h3: Block,
|
|
72
|
+
h4: Block,
|
|
73
|
+
h5: Block,
|
|
74
|
+
h6: Block,
|
|
75
|
+
blockquote: Block,
|
|
76
|
+
normal: Block,
|
|
77
|
+
},
|
|
78
|
+
unknownBlock: UnknownBlock,
|
|
79
|
+
list: {
|
|
80
|
+
bullet: List,
|
|
81
|
+
number: List,
|
|
82
|
+
menu: List,
|
|
83
|
+
},
|
|
84
|
+
unknownList: UnknownList,
|
|
85
|
+
listItem: {
|
|
86
|
+
bullet: ListItem,
|
|
87
|
+
number: ListItem,
|
|
88
|
+
menu: ListItem,
|
|
89
|
+
},
|
|
90
|
+
unknownListItem: UnknownListItem,
|
|
91
|
+
mark: {
|
|
92
|
+
code: Mark,
|
|
93
|
+
em: Mark,
|
|
94
|
+
link: Mark,
|
|
95
|
+
"strike-through": Mark,
|
|
96
|
+
strong: Mark,
|
|
97
|
+
underline: Mark,
|
|
98
|
+
},
|
|
99
|
+
unknownMark: UnknownMark,
|
|
100
|
+
text: Text,
|
|
101
|
+
hardBreak: HardBreak,
|
|
102
|
+
},
|
|
103
|
+
componentOverrides
|
|
104
|
+
) as PortableTextComponents;
|
|
105
|
+
|
|
106
|
+
const noop = () => {};
|
|
107
|
+
|
|
108
|
+
const missingComponentHandler = ((
|
|
109
|
+
handler: unknown
|
|
110
|
+
): MissingComponentHandler => {
|
|
111
|
+
if (typeof handler === "function") {
|
|
112
|
+
return handler as MissingComponentHandler;
|
|
113
|
+
}
|
|
114
|
+
return !handler ? noop : printWarning;
|
|
115
|
+
})(onMissingComponent);
|
|
116
|
+
|
|
117
|
+
const asComponentProps = <T extends TypedObject>(
|
|
118
|
+
node: T,
|
|
119
|
+
index: number,
|
|
120
|
+
isInline: boolean
|
|
121
|
+
): ComponentProps<T> => ({
|
|
122
|
+
node,
|
|
123
|
+
index,
|
|
124
|
+
isInline,
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
const provideComponent = (
|
|
128
|
+
nodeType: NodeType,
|
|
129
|
+
type: string,
|
|
130
|
+
fallbackComponent: Component
|
|
131
|
+
): Component => {
|
|
132
|
+
const component: Component | undefined = ((entry) => {
|
|
133
|
+
return entry[type as keyof typeof entry] || entry;
|
|
134
|
+
})(components[nodeType]);
|
|
135
|
+
|
|
136
|
+
if (isComponent(component)) {
|
|
137
|
+
return component;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
missingComponentHandler(getWarningMessage(nodeType, type), {
|
|
141
|
+
nodeType,
|
|
142
|
+
type,
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
return fallbackComponent;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
type RunArgs = {
|
|
149
|
+
nodeType: SlotNodeType;
|
|
150
|
+
/** Block style, list type, mark type or custom `_type`, when there is one */
|
|
151
|
+
type?: string;
|
|
152
|
+
// Loosely typed: each `RenderOptions` entry narrows `node` to its own type
|
|
153
|
+
handler: ((handlerProps: any) => any) | undefined;
|
|
154
|
+
/** Called only if something actually asks for the node's `Component` */
|
|
155
|
+
resolveComponent: () => Component;
|
|
156
|
+
props: ComponentProps<TypedObject>;
|
|
157
|
+
children?: unknown;
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
// The local render function will override these options
|
|
161
|
+
let fallbackRenderOptions: Required<RenderOptions> | undefined;
|
|
162
|
+
|
|
163
|
+
const portableTextRender = (options: RenderOptions, isInline?: boolean) => {
|
|
164
|
+
if (!fallbackRenderOptions) {
|
|
165
|
+
throw new Error(
|
|
166
|
+
"[PortableText portableTextRender] fallbackRenderOptions is undefined"
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const renderChildren = (children?: TypedObject[], inline = false) => {
|
|
171
|
+
return children?.map(portableTextRender(options, inline)) ?? [];
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const renderOptions = { ...fallbackRenderOptions, ...options };
|
|
175
|
+
|
|
176
|
+
return function renderNode(node: TypedObject, index: number): any {
|
|
177
|
+
function run({
|
|
178
|
+
nodeType,
|
|
179
|
+
type,
|
|
180
|
+
handler,
|
|
181
|
+
resolveComponent,
|
|
182
|
+
props,
|
|
183
|
+
children,
|
|
184
|
+
}: RunArgs) {
|
|
185
|
+
let component: Component | undefined;
|
|
186
|
+
|
|
187
|
+
const handlerProps = {
|
|
188
|
+
// Resolved lazily. A slot rendering its own markup never asks for
|
|
189
|
+
// `Component`, and so must not trigger a missing component warning.
|
|
190
|
+
get Component() {
|
|
191
|
+
return (component ??= resolveComponent());
|
|
192
|
+
},
|
|
193
|
+
props,
|
|
194
|
+
children,
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
// A slot always wins. `renderOptions` may have been overridden by a component
|
|
198
|
+
// from the `components` prop calling `render` from `usePortableText`, which
|
|
199
|
+
// must not take the slot away from the author of this `PortableText`.
|
|
200
|
+
const slotRenderer = provideSlot(nodeType, type);
|
|
201
|
+
|
|
202
|
+
if (slotRenderer) {
|
|
203
|
+
return slotRenderer([handlerProps]);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (!isComponent(handler)) {
|
|
207
|
+
throw new Error(
|
|
208
|
+
`[PortableText render] No handler found for node type ${node._type}.`
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return handler(handlerProps);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (isPortableTextToolkitList(node)) {
|
|
216
|
+
const listType = node.listItem;
|
|
217
|
+
const UnknownComponent = components.unknownList ?? UnknownList;
|
|
218
|
+
setNodeComponents(node, List, UnknownComponent);
|
|
219
|
+
|
|
220
|
+
return run({
|
|
221
|
+
nodeType: "list",
|
|
222
|
+
type: listType,
|
|
223
|
+
handler: renderOptions.list,
|
|
224
|
+
resolveComponent: () =>
|
|
225
|
+
provideComponent("list", listType, UnknownComponent),
|
|
226
|
+
props: asComponentProps(node, index, false),
|
|
227
|
+
children: renderChildren(node.children, false),
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (isPortableTextListItemBlock(node)) {
|
|
232
|
+
const { listItem, ...blockNode } = node;
|
|
233
|
+
const isStyled = node.style && node.style !== "normal";
|
|
234
|
+
// Apply block style if defined (and not "normal"), otherwise render with marks.
|
|
235
|
+
node.children = isStyled
|
|
236
|
+
? renderNode(blockNode, index)
|
|
237
|
+
: buildMarksTree(node);
|
|
238
|
+
|
|
239
|
+
const UnknownComponent = components.unknownListItem ?? UnknownListItem;
|
|
240
|
+
setNodeComponents(node, ListItem, UnknownComponent);
|
|
241
|
+
|
|
242
|
+
return run({
|
|
243
|
+
nodeType: "listItem",
|
|
244
|
+
type: listItem,
|
|
245
|
+
handler: renderOptions.listItem,
|
|
246
|
+
resolveComponent: () =>
|
|
247
|
+
provideComponent("listItem", listItem, UnknownComponent),
|
|
248
|
+
props: asComponentProps(node, index, false),
|
|
249
|
+
children: isStyled
|
|
250
|
+
? node.children
|
|
251
|
+
: renderChildren(node.children, true),
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (isPortableTextToolkitSpan(node)) {
|
|
256
|
+
const markType = node.markType;
|
|
257
|
+
const UnknownComponent = components.unknownMark ?? UnknownMark;
|
|
258
|
+
setNodeComponents(node, Mark, UnknownComponent);
|
|
259
|
+
|
|
260
|
+
return run({
|
|
261
|
+
nodeType: "mark",
|
|
262
|
+
type: markType,
|
|
263
|
+
handler: renderOptions.mark,
|
|
264
|
+
resolveComponent: () =>
|
|
265
|
+
provideComponent("mark", markType, UnknownComponent),
|
|
266
|
+
props: asComponentProps(node, index, true),
|
|
267
|
+
children: renderChildren(node.children, true),
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (isPortableTextBlock(node)) {
|
|
272
|
+
node.style ??= "normal"; /* Make sure style has been set */
|
|
273
|
+
node.children = buildMarksTree(node);
|
|
274
|
+
|
|
275
|
+
const style = node.style;
|
|
276
|
+
const UnknownComponent = components.unknownBlock ?? UnknownBlock;
|
|
277
|
+
setNodeComponents(node, Block, UnknownComponent);
|
|
278
|
+
|
|
279
|
+
return run({
|
|
280
|
+
nodeType: "block",
|
|
281
|
+
type: style,
|
|
282
|
+
handler: renderOptions.block,
|
|
283
|
+
resolveComponent: () =>
|
|
284
|
+
provideComponent("block", style, UnknownComponent),
|
|
285
|
+
props: asComponentProps(node, index, false),
|
|
286
|
+
children: renderChildren(node.children, true),
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (isPortableTextToolkitTextNode(node)) {
|
|
291
|
+
const isHardBreak = "\n" === node.text;
|
|
292
|
+
const props = asComponentProps(node, index, true);
|
|
293
|
+
|
|
294
|
+
if (isHardBreak) {
|
|
295
|
+
return run({
|
|
296
|
+
nodeType: "hardBreak",
|
|
297
|
+
handler: renderOptions.hardBreak,
|
|
298
|
+
resolveComponent: () =>
|
|
299
|
+
isComponent(components.hardBreak)
|
|
300
|
+
? components.hardBreak
|
|
301
|
+
: HardBreak,
|
|
302
|
+
props,
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
return run({
|
|
307
|
+
nodeType: "text",
|
|
308
|
+
handler: renderOptions.text,
|
|
309
|
+
resolveComponent: () =>
|
|
310
|
+
isComponent(components.text) ? components.text : Text,
|
|
311
|
+
props,
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Custom type
|
|
316
|
+
const customType = node._type;
|
|
317
|
+
const UnknownComponent = components.unknownType ?? UnknownType;
|
|
318
|
+
|
|
319
|
+
return run({
|
|
320
|
+
nodeType: "type",
|
|
321
|
+
type: customType,
|
|
322
|
+
handler: renderOptions.type,
|
|
323
|
+
resolveComponent: () =>
|
|
324
|
+
provideComponent("type", customType, UnknownComponent),
|
|
325
|
+
props: asComponentProps(
|
|
326
|
+
node,
|
|
327
|
+
index,
|
|
328
|
+
isInline ?? false /* default to block */
|
|
329
|
+
),
|
|
330
|
+
});
|
|
331
|
+
};
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
(globalThis as any)[contextRef] = (
|
|
335
|
+
node: TypedObject & { children?: TypedObject[] }
|
|
336
|
+
): Context => ({
|
|
337
|
+
getDefaultComponent: provideDefaultComponent.bind(null, node),
|
|
338
|
+
getUnknownComponent: provideUnknownComponent.bind(null, node),
|
|
339
|
+
render: (options) => node.children?.map(portableTextRender(options)),
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
// Returns the `default` component related to the passed in node
|
|
343
|
+
const provideDefaultComponent = (node: TypedObject) => {
|
|
344
|
+
const DefaultComponent = getNodeComponents(node)?.Default;
|
|
345
|
+
if (DefaultComponent) return DefaultComponent;
|
|
346
|
+
|
|
347
|
+
// Cache missed use manual lookup
|
|
348
|
+
if (import.meta.env.DEV) {
|
|
349
|
+
// oxlint-disable-next-line no-console
|
|
350
|
+
console.warn(
|
|
351
|
+
`[@portabletext/astro] Cache missed for default component on node type "${node._type}". Ensure you are passing in the original "node" prop.`
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (isPortableTextToolkitList(node)) return List;
|
|
356
|
+
if (isPortableTextListItemBlock(node)) return ListItem;
|
|
357
|
+
if (isPortableTextToolkitSpan(node)) return Mark;
|
|
358
|
+
if (isPortableTextBlock(node)) return Block;
|
|
359
|
+
|
|
360
|
+
if (isPortableTextToolkitTextNode(node)) {
|
|
361
|
+
return "\n" === node.text ? HardBreak : Text;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return UnknownType;
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
// Returns the `unknown` component related to the passed in node
|
|
368
|
+
const provideUnknownComponent = (node: TypedObject) => {
|
|
369
|
+
const UnknownComponent = getNodeComponents(node)?.Unknown;
|
|
370
|
+
if (UnknownComponent) return UnknownComponent;
|
|
371
|
+
|
|
372
|
+
// Cache missed use manual lookup
|
|
373
|
+
if (import.meta.env.DEV) {
|
|
374
|
+
// oxlint-disable-next-line no-console
|
|
375
|
+
console.warn(
|
|
376
|
+
`[@portabletext/astro] Cache missed for unknown component on node type "${node._type}". Ensure you are passing in the original "node" prop.`
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (isPortableTextToolkitList(node)) {
|
|
381
|
+
return components.unknownList ?? UnknownList;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (isPortableTextListItemBlock(node)) {
|
|
385
|
+
return components.unknownListItem ?? UnknownListItem;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if (isPortableTextToolkitSpan(node)) {
|
|
389
|
+
return components.unknownMark ?? UnknownMark;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
if (isPortableTextBlock(node)) {
|
|
393
|
+
return components.unknownBlock ?? UnknownBlock;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (!isPortableTextToolkitTextNode(node)) {
|
|
397
|
+
return components.unknownType ?? UnknownType;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
throw new Error(
|
|
401
|
+
`[PortableText getUnknownComponent] Unable to provide component with node type ${node._type}`
|
|
402
|
+
);
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
// Make sure we have an array of blocks
|
|
406
|
+
const blocks = Array.isArray(value) ? value : value ? [value] : [];
|
|
407
|
+
const nodes = nestLists(blocks, listNestingMode);
|
|
408
|
+
|
|
409
|
+
const render = (options: NonNullable<typeof fallbackRenderOptions>) => {
|
|
410
|
+
fallbackRenderOptions = options;
|
|
411
|
+
return portableTextRender(options);
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
// Create a slot renderer for a specific slot
|
|
415
|
+
const createSlotRenderer = (slotName: string) =>
|
|
416
|
+
Astro.slots.render.bind(Astro.slots, slotName);
|
|
417
|
+
|
|
418
|
+
type SlotRenderer = ReturnType<typeof createSlotRenderer>;
|
|
419
|
+
|
|
420
|
+
const slotRenderers = new Map<string, SlotRenderer | undefined>();
|
|
421
|
+
|
|
422
|
+
const provideSlotRenderer = (slotName: string): SlotRenderer | undefined => {
|
|
423
|
+
if (!slotRenderers.has(slotName)) {
|
|
424
|
+
slotRenderers.set(
|
|
425
|
+
slotName,
|
|
426
|
+
Astro.slots.has(slotName) ? createSlotRenderer(slotName) : undefined
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
return slotRenderers.get(slotName);
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Resolves the slot to render a node with.
|
|
435
|
+
* A slot scoped to a type, e.g. `block:h1`, takes precedence over `block`.
|
|
436
|
+
*/
|
|
437
|
+
const provideSlot = (
|
|
438
|
+
nodeType: SlotNodeType,
|
|
439
|
+
type?: string
|
|
440
|
+
): SlotRenderer | undefined =>
|
|
441
|
+
(type ? provideSlotRenderer(toSlotName(nodeType, type)) : undefined) ??
|
|
442
|
+
provideSlotRenderer(nodeType);
|
|
443
|
+
|
|
444
|
+
// A slot targeting an unrecognised node type never renders, so let the author
|
|
445
|
+
// know rather than silently ignoring it.
|
|
446
|
+
if (import.meta.env.DEV) {
|
|
447
|
+
for (const slotName of Object.keys(Astro.slots)) {
|
|
448
|
+
if (isSlotName(slotName)) continue;
|
|
449
|
+
|
|
450
|
+
printWarning(
|
|
451
|
+
"default" === slotName
|
|
452
|
+
? ignoredChildrenWarning()
|
|
453
|
+
: unknownSlotWarning(slotName)
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// Slots are resolved by `portableTextRender`, so every node type falls back to
|
|
459
|
+
// simply rendering its resolved component.
|
|
460
|
+
type RenderNode = (
|
|
461
|
+
props: Parameters<NonNullable<RenderOptions[keyof RenderOptions]>>[0]
|
|
462
|
+
) => any;
|
|
463
|
+
---
|
|
464
|
+
|
|
465
|
+
{
|
|
466
|
+
(() => {
|
|
467
|
+
const renderNode: RenderNode = ({ Component, props, children }) => (
|
|
468
|
+
<Component {...(props as any)}>{children}</Component>
|
|
469
|
+
);
|
|
470
|
+
|
|
471
|
+
return nodes.map(
|
|
472
|
+
render({
|
|
473
|
+
type: renderNode,
|
|
474
|
+
block: renderNode,
|
|
475
|
+
list: renderNode,
|
|
476
|
+
listItem: renderNode,
|
|
477
|
+
mark: renderNode,
|
|
478
|
+
text: renderNode,
|
|
479
|
+
hardBreak: renderNode,
|
|
480
|
+
})
|
|
481
|
+
);
|
|
482
|
+
})()
|
|
483
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { TypedObject, Props as $ } from "../lib/types";
|
|
3
|
+
import { getWarningMessage } from "../lib/warnings";
|
|
4
|
+
|
|
5
|
+
export type Props = $<TypedObject>;
|
|
6
|
+
|
|
7
|
+
const { node, isInline } = Astro.props;
|
|
8
|
+
const warning = getWarningMessage("type", node._type);
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
{
|
|
12
|
+
isInline ? (
|
|
13
|
+
<span style="display:none" data-portabletext-unknown="type">
|
|
14
|
+
{warning}
|
|
15
|
+
</span>
|
|
16
|
+
) : (
|
|
17
|
+
<div style="display:none" data-portabletext-unknown="type">
|
|
18
|
+
{warning}
|
|
19
|
+
</div>
|
|
20
|
+
)
|
|
21
|
+
}
|
package/lib/astro.d.ts
ADDED
package/lib/context.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type {TypedObject} from '@portabletext/types'
|
|
2
|
+
|
|
3
|
+
import type {Context} from './types'
|
|
4
|
+
|
|
5
|
+
export const key = Symbol('@portabletext/astro')
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* This function returns rendering utility functions within a Portable Text tree. It should
|
|
9
|
+
* only be used within an Astro component that has been passed into the PortableText `components` prop.
|
|
10
|
+
* It follows a naming convention similar to React hooks, though it is not a hook as such.
|
|
11
|
+
*
|
|
12
|
+
* @param node - The Portable Text node that was passed into the Astro component
|
|
13
|
+
* @returns Rendering utility functions
|
|
14
|
+
*/
|
|
15
|
+
export function usePortableText(node: TypedObject) {
|
|
16
|
+
if (!(key in globalThis)) {
|
|
17
|
+
throw new Error(`PortableText "context" has not been initialised`)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return (globalThis as any)[key](node) as Context
|
|
21
|
+
}
|
package/lib/index.ts
ADDED