fimo-react 0.21.0-experimental.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.
- package/README.md +21 -0
- package/dist/runtime/content/DataSourcesProvider.d.ts +4 -0
- package/dist/runtime/content/DataSourcesProvider.js +63 -0
- package/dist/runtime/content/RefreshContentProvider.d.ts +19 -0
- package/dist/runtime/content/RefreshContentProvider.js +71 -0
- package/dist/runtime/content/createContentClient.d.ts +7 -0
- package/dist/runtime/content/createContentClient.js +7 -0
- package/dist/runtime/content/createSingletonClient.d.ts +5 -0
- package/dist/runtime/content/createSingletonClient.js +6 -0
- package/dist/runtime/content/fixtures/Author.d.ts +48 -0
- package/dist/runtime/content/fixtures/Author.js +22 -0
- package/dist/runtime/content/fixtures/Author.types.d.ts +38 -0
- package/dist/runtime/content/fixtures/Author.types.js +1 -0
- package/dist/runtime/content/fixtures/Post.d.ts +48 -0
- package/dist/runtime/content/fixtures/Post.js +37 -0
- package/dist/runtime/content/fixtures/Post.types.d.ts +50 -0
- package/dist/runtime/content/fixtures/Post.types.js +1 -0
- package/dist/runtime/content/fixtures/SiteSettings.d.ts +42 -0
- package/dist/runtime/content/fixtures/SiteSettings.js +15 -0
- package/dist/runtime/content/fixtures/usage-contract.d.ts +3 -0
- package/dist/runtime/content/fixtures/usage-contract.js +57 -0
- package/dist/runtime/content/fixtures/virtual-fimo-config.d.ts +3 -0
- package/dist/runtime/content/fixtures/virtual-fimo-config.js +3 -0
- package/dist/runtime/content/index.d.ts +5 -0
- package/dist/runtime/content/index.js +2 -0
- package/dist/runtime/content/react-content-client.d.ts +10 -0
- package/dist/runtime/content/react-content-client.js +164 -0
- package/dist/runtime/content/react-singleton-client.d.ts +7 -0
- package/dist/runtime/content/react-singleton-client.js +37 -0
- package/dist/runtime/content/retrieve-data-sources.d.ts +29 -0
- package/dist/runtime/content/retrieve-data-sources.js +107 -0
- package/dist/runtime/content/types.d.ts +42 -0
- package/dist/runtime/content/types.js +1 -0
- package/dist/runtime/content/useQueryLocale.d.ts +2 -0
- package/dist/runtime/content/useQueryLocale.js +5 -0
- package/dist/runtime/content/vite-runtime.d.ts +3 -0
- package/dist/runtime/content/vite-runtime.js +23 -0
- package/dist/runtime/core/AppErrorBoundary.d.ts +10 -0
- package/dist/runtime/core/AppErrorBoundary.js +27 -0
- package/dist/runtime/core/ViteLifecycles.d.ts +5 -0
- package/dist/runtime/core/ViteLifecycles.js +35 -0
- package/dist/runtime/i18n-context.d.ts +9 -0
- package/dist/runtime/i18n-context.js +17 -0
- package/dist/runtime/iframe/ConsoleErrorProvider.d.ts +3 -0
- package/dist/runtime/iframe/ConsoleErrorProvider.js +103 -0
- package/dist/runtime/iframe/ScreenshotProvider.d.ts +3 -0
- package/dist/runtime/iframe/ScreenshotProvider.js +60 -0
- package/dist/runtime/index.d.ts +24 -0
- package/dist/runtime/index.js +24 -0
- package/dist/runtime/internal.d.ts +3 -0
- package/dist/runtime/internal.js +6 -0
- package/dist/runtime/primitives-server.d.ts +7 -0
- package/dist/runtime/primitives-server.js +5 -0
- package/dist/runtime/primitives.d.ts +17 -0
- package/dist/runtime/primitives.js +18 -0
- package/dist/runtime/react/primitives/contracts.d.ts +100 -0
- package/dist/runtime/react/primitives/contracts.js +1 -0
- package/dist/runtime/react/primitives/editable/Boolean.d.ts +5 -0
- package/dist/runtime/react/primitives/editable/Boolean.js +11 -0
- package/dist/runtime/react/primitives/editable/Date.d.ts +4 -0
- package/dist/runtime/react/primitives/editable/Date.js +12 -0
- package/dist/runtime/react/primitives/editable/DateTime.d.ts +4 -0
- package/dist/runtime/react/primitives/editable/DateTime.js +13 -0
- package/dist/runtime/react/primitives/editable/Image.d.ts +4 -0
- package/dist/runtime/react/primitives/editable/Image.js +10 -0
- package/dist/runtime/react/primitives/editable/Json.d.ts +5 -0
- package/dist/runtime/react/primitives/editable/Json.js +11 -0
- package/dist/runtime/react/primitives/editable/RichText.d.ts +4 -0
- package/dist/runtime/react/primitives/editable/RichText.js +11 -0
- package/dist/runtime/react/primitives/editable/Text.d.ts +20 -0
- package/dist/runtime/react/primitives/editable/Text.js +36 -0
- package/dist/runtime/react/primitives/editable/Video.d.ts +4 -0
- package/dist/runtime/react/primitives/editable/Video.js +10 -0
- package/dist/runtime/react/primitives/renderers/DateRenderer.d.ts +17 -0
- package/dist/runtime/react/primitives/renderers/DateRenderer.js +17 -0
- package/dist/runtime/react/primitives/renderers/ImageRenderer.d.ts +8 -0
- package/dist/runtime/react/primitives/renderers/ImageRenderer.js +30 -0
- package/dist/runtime/react/primitives/renderers/RichTextRenderer.d.ts +12 -0
- package/dist/runtime/react/primitives/renderers/RichTextRenderer.js +135 -0
- package/dist/runtime/react/primitives/renderers/StaticImage.d.ts +35 -0
- package/dist/runtime/react/primitives/renderers/StaticImage.js +58 -0
- package/dist/runtime/react/primitives/renderers/VideoRenderer.d.ts +8 -0
- package/dist/runtime/react/primitives/renderers/VideoRenderer.js +10 -0
- package/dist/runtime/react/useFimoLiveValue.d.ts +20 -0
- package/dist/runtime/react/useFimoLiveValue.js +43 -0
- package/dist/runtime/vite/Label.d.ts +13 -0
- package/dist/runtime/vite/Label.js +16 -0
- package/dist/runtime/vite/__fixtures__/virtual-translations.d.ts +5 -0
- package/dist/runtime/vite/__fixtures__/virtual-translations.js +6 -0
- package/dist/runtime/vite/primitives/Images.d.ts +4 -0
- package/dist/runtime/vite/primitives/Images.js +12 -0
- package/dist/runtime/vite/translations.d.ts +28 -0
- package/dist/runtime/vite/translations.js +103 -0
- package/package.json +72 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Fragment } from 'react';
|
|
3
|
+
function DefaultParagraph({ children }) {
|
|
4
|
+
return _jsx("p", { children: children });
|
|
5
|
+
}
|
|
6
|
+
function DefaultHeading({ node, children }) {
|
|
7
|
+
const level = node.attrs?.level ?? 1;
|
|
8
|
+
const Tag = `h${level}`;
|
|
9
|
+
return _jsx(Tag, { children: children });
|
|
10
|
+
}
|
|
11
|
+
function DefaultBulletList({ children }) {
|
|
12
|
+
return _jsx("ul", { children: children });
|
|
13
|
+
}
|
|
14
|
+
function DefaultOrderedList({ children }) {
|
|
15
|
+
return _jsx("ol", { children: children });
|
|
16
|
+
}
|
|
17
|
+
function DefaultListItem({ children }) {
|
|
18
|
+
return _jsx("li", { children: children });
|
|
19
|
+
}
|
|
20
|
+
function DefaultBlockquote({ children }) {
|
|
21
|
+
return _jsx("blockquote", { children: children });
|
|
22
|
+
}
|
|
23
|
+
function DefaultCodeBlock({ children }) {
|
|
24
|
+
return (_jsx("pre", { children: _jsx("code", { children: children }) }));
|
|
25
|
+
}
|
|
26
|
+
function DefaultHorizontalRule() {
|
|
27
|
+
return _jsx("hr", {});
|
|
28
|
+
}
|
|
29
|
+
function DefaultHardBreak() {
|
|
30
|
+
return _jsx("br", {});
|
|
31
|
+
}
|
|
32
|
+
function DefaultImage({ node }) {
|
|
33
|
+
const src = node.attrs?.src;
|
|
34
|
+
const alt = node.attrs?.alt ?? '';
|
|
35
|
+
return _jsx("img", { src: src, alt: alt });
|
|
36
|
+
}
|
|
37
|
+
function DefaultIframe({ node }) {
|
|
38
|
+
const src = node.attrs?.src;
|
|
39
|
+
return (_jsx("div", { style: {
|
|
40
|
+
position: 'relative',
|
|
41
|
+
width: '100%',
|
|
42
|
+
paddingBottom: '56.25%',
|
|
43
|
+
borderRadius: '0.375rem',
|
|
44
|
+
overflow: 'hidden',
|
|
45
|
+
}, children: _jsx("iframe", { src: src, style: { position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', border: 0 }, allowFullScreen: true }) }));
|
|
46
|
+
}
|
|
47
|
+
function DefaultTable({ children }) {
|
|
48
|
+
return (_jsx("table", { className: "border-border w-full border-collapse border", children: _jsx("tbody", { children: children }) }));
|
|
49
|
+
}
|
|
50
|
+
function DefaultTableRow({ children }) {
|
|
51
|
+
return _jsx("tr", { className: "border-border border-b last:border-b-0", children: children });
|
|
52
|
+
}
|
|
53
|
+
function DefaultTableCell({ children }) {
|
|
54
|
+
return _jsx("td", { className: "border-border border px-3 py-2 align-top", children: children });
|
|
55
|
+
}
|
|
56
|
+
function DefaultTableHeader({ children }) {
|
|
57
|
+
return _jsx("th", { className: "border-border bg-muted border px-3 py-2 text-left align-top font-semibold", children: children });
|
|
58
|
+
}
|
|
59
|
+
function DefaultBold({ children }) {
|
|
60
|
+
return _jsx("strong", { children: children });
|
|
61
|
+
}
|
|
62
|
+
function DefaultItalic({ children }) {
|
|
63
|
+
return _jsx("em", { children: children });
|
|
64
|
+
}
|
|
65
|
+
function DefaultUnderline({ children }) {
|
|
66
|
+
return _jsx("u", { children: children });
|
|
67
|
+
}
|
|
68
|
+
function DefaultStrike({ children }) {
|
|
69
|
+
return _jsx("s", { children: children });
|
|
70
|
+
}
|
|
71
|
+
function DefaultCode({ children }) {
|
|
72
|
+
return _jsx("code", { children: children });
|
|
73
|
+
}
|
|
74
|
+
function DefaultLink({ mark, children }) {
|
|
75
|
+
const href = mark.attrs?.href;
|
|
76
|
+
const target = mark.attrs?.target;
|
|
77
|
+
const rel = mark.attrs?.rel;
|
|
78
|
+
return (_jsx("a", { href: href, target: target, rel: rel, children: children }));
|
|
79
|
+
}
|
|
80
|
+
const DEFAULT_NODES = {
|
|
81
|
+
paragraph: DefaultParagraph,
|
|
82
|
+
heading: DefaultHeading,
|
|
83
|
+
bulletList: DefaultBulletList,
|
|
84
|
+
orderedList: DefaultOrderedList,
|
|
85
|
+
listItem: DefaultListItem,
|
|
86
|
+
blockquote: DefaultBlockquote,
|
|
87
|
+
codeBlock: DefaultCodeBlock,
|
|
88
|
+
horizontalRule: DefaultHorizontalRule,
|
|
89
|
+
hardBreak: DefaultHardBreak,
|
|
90
|
+
image: DefaultImage,
|
|
91
|
+
table: DefaultTable,
|
|
92
|
+
tableRow: DefaultTableRow,
|
|
93
|
+
tableCell: DefaultTableCell,
|
|
94
|
+
tableHeader: DefaultTableHeader,
|
|
95
|
+
iframe: DefaultIframe,
|
|
96
|
+
};
|
|
97
|
+
const DEFAULT_MARKS = {
|
|
98
|
+
bold: DefaultBold,
|
|
99
|
+
strong: DefaultBold,
|
|
100
|
+
italic: DefaultItalic,
|
|
101
|
+
em: DefaultItalic,
|
|
102
|
+
underline: DefaultUnderline,
|
|
103
|
+
strike: DefaultStrike,
|
|
104
|
+
code: DefaultCode,
|
|
105
|
+
link: DefaultLink,
|
|
106
|
+
};
|
|
107
|
+
function renderNode(node, components, key) {
|
|
108
|
+
if (node.type === 'text') {
|
|
109
|
+
let result = node.text ?? '';
|
|
110
|
+
if (node.marks) {
|
|
111
|
+
for (const mark of node.marks) {
|
|
112
|
+
const MarkComponent = components?.[mark.type] ?? DEFAULT_MARKS[mark.type];
|
|
113
|
+
if (MarkComponent) {
|
|
114
|
+
result = _jsx(MarkComponent, { mark: mark, children: result });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return _jsx(Fragment, { children: result }, key);
|
|
119
|
+
}
|
|
120
|
+
const children = node.content?.map((child, index) => renderNode(child, components, index)) ?? null;
|
|
121
|
+
if (node.type === 'doc') {
|
|
122
|
+
return _jsx(Fragment, { children: children }, key);
|
|
123
|
+
}
|
|
124
|
+
const NodeComponent = components?.[node.type] ?? DEFAULT_NODES[node.type];
|
|
125
|
+
if (NodeComponent) {
|
|
126
|
+
return (_jsx(NodeComponent, { node: node, children: children }, key));
|
|
127
|
+
}
|
|
128
|
+
return _jsx(Fragment, { children: children }, key);
|
|
129
|
+
}
|
|
130
|
+
export function RichTextRenderer({ document, source, components, elementRef, ...props }) {
|
|
131
|
+
if (!document) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
return (_jsx("div", { ref: elementRef, ...props, "data-fimo-source": source, "data-fimo-richtext": "", children: renderNode(document, components, 0) }));
|
|
135
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { StaticImageProps } from '../contracts.js';
|
|
2
|
+
export type { StaticImageProps } from '../contracts.js';
|
|
3
|
+
/**
|
|
4
|
+
* Optimized image component for non-schema images.
|
|
5
|
+
*
|
|
6
|
+
* Use `StaticImage` for images that don't come from the database —
|
|
7
|
+
* decorative images, hardcoded URLs, generated assets, or any image
|
|
8
|
+
* where you have a direct URL string rather than a `FimoMedia` value
|
|
9
|
+
* from schema hooks.
|
|
10
|
+
*
|
|
11
|
+
* Provides the same Cloudflare Image Resizing optimization as `<Image>`
|
|
12
|
+
* (responsive srcset, quality, format=auto) but without requiring a
|
|
13
|
+
* `FimoMedia` instance or database source tracking.
|
|
14
|
+
*
|
|
15
|
+
* For schema-backed media, use `<Image value={post.cover} />` instead.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* // Basic usage
|
|
19
|
+
* <StaticImage src="/images/hero.jpg" alt="Hero background" width={1200} height={630} />
|
|
20
|
+
*
|
|
21
|
+
* // With Cloudflare optimization
|
|
22
|
+
* <StaticImage src="https://...fimo.site/uploads/photo.jpg" alt="Team" width={800} fit="cover" />
|
|
23
|
+
*
|
|
24
|
+
* // Responsive mode
|
|
25
|
+
* <StaticImage
|
|
26
|
+
* src="https://...fimo.site/uploads/banner.jpg"
|
|
27
|
+
* alt="Banner"
|
|
28
|
+
* sizes="(max-width: 768px) 100vw, 50vw"
|
|
29
|
+
* />
|
|
30
|
+
*
|
|
31
|
+
* // Skip optimization
|
|
32
|
+
* <StaticImage src="/images/logo.svg" alt="Logo" unoptimized />
|
|
33
|
+
*/
|
|
34
|
+
export declare function StaticImage(props: StaticImageProps): import("react/jsx-runtime").JSX.Element | null;
|
|
35
|
+
//# sourceMappingURL=StaticImage.d.ts.map
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { resolveImagePresentation } from 'fimo/primitives';
|
|
3
|
+
/**
|
|
4
|
+
* Optimized image component for non-schema images.
|
|
5
|
+
*
|
|
6
|
+
* Use `StaticImage` for images that don't come from the database —
|
|
7
|
+
* decorative images, hardcoded URLs, generated assets, or any image
|
|
8
|
+
* where you have a direct URL string rather than a `FimoMedia` value
|
|
9
|
+
* from schema hooks.
|
|
10
|
+
*
|
|
11
|
+
* Provides the same Cloudflare Image Resizing optimization as `<Image>`
|
|
12
|
+
* (responsive srcset, quality, format=auto) but without requiring a
|
|
13
|
+
* `FimoMedia` instance or database source tracking.
|
|
14
|
+
*
|
|
15
|
+
* For schema-backed media, use `<Image value={post.cover} />` instead.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* // Basic usage
|
|
19
|
+
* <StaticImage src="/images/hero.jpg" alt="Hero background" width={1200} height={630} />
|
|
20
|
+
*
|
|
21
|
+
* // With Cloudflare optimization
|
|
22
|
+
* <StaticImage src="https://...fimo.site/uploads/photo.jpg" alt="Team" width={800} fit="cover" />
|
|
23
|
+
*
|
|
24
|
+
* // Responsive mode
|
|
25
|
+
* <StaticImage
|
|
26
|
+
* src="https://...fimo.site/uploads/banner.jpg"
|
|
27
|
+
* alt="Banner"
|
|
28
|
+
* sizes="(max-width: 768px) 100vw, 50vw"
|
|
29
|
+
* />
|
|
30
|
+
*
|
|
31
|
+
* // Skip optimization
|
|
32
|
+
* <StaticImage src="/images/logo.svg" alt="Logo" unoptimized />
|
|
33
|
+
*/
|
|
34
|
+
export function StaticImage(props) {
|
|
35
|
+
const { src, alt, quality, unoptimized = false, fit, width, height, srcSet: srcSetOverride, sizes, style, hostSuffixes, ...imgProps } = props;
|
|
36
|
+
if (!src) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
const image = resolveImagePresentation({
|
|
40
|
+
src,
|
|
41
|
+
alt,
|
|
42
|
+
quality,
|
|
43
|
+
fit,
|
|
44
|
+
unoptimized,
|
|
45
|
+
width,
|
|
46
|
+
height,
|
|
47
|
+
sizes,
|
|
48
|
+
srcSet: srcSetOverride,
|
|
49
|
+
hostSuffixes,
|
|
50
|
+
});
|
|
51
|
+
const finalStyle = style?.objectFit
|
|
52
|
+
? style
|
|
53
|
+
: {
|
|
54
|
+
...style,
|
|
55
|
+
objectFit: image.objectFit,
|
|
56
|
+
};
|
|
57
|
+
return (_jsx("img", { src: image.src, alt: image.alt, width: image.width, height: image.height, style: finalStyle, sizes: image.sizes, srcSet: image.srcSet, ...imgProps }));
|
|
58
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type RefObject } from 'react';
|
|
2
|
+
import type { VideoProps } from '../contracts.js';
|
|
3
|
+
interface VideoRendererProps extends VideoProps {
|
|
4
|
+
elementRef?: RefObject<HTMLElement | null>;
|
|
5
|
+
}
|
|
6
|
+
export declare function VideoRenderer({ value: media, children, overrideSrc, autoPlay, loop, muted, playsInline, style, elementRef, ...videoProps }: VideoRendererProps): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=VideoRenderer.d.ts.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { FIMO_SOURCE } from 'fimo/primitives';
|
|
3
|
+
export function VideoRenderer({ value: media, children, overrideSrc, autoPlay = true, loop = true, muted = true, playsInline = true, style, elementRef, ...videoProps }) {
|
|
4
|
+
const mediaSource = media[FIMO_SOURCE];
|
|
5
|
+
if (children) {
|
|
6
|
+
return (_jsx("span", { ref: elementRef, "data-fimo-source": mediaSource, children: children(media) }));
|
|
7
|
+
}
|
|
8
|
+
const finalSrc = overrideSrc ?? media.url;
|
|
9
|
+
return (_jsx("video", { ref: elementRef, src: finalSrc, width: media.width, height: media.height, autoPlay: autoPlay, loop: loop, muted: muted, playsInline: playsInline, style: style, ...videoProps, "data-fimo-source": mediaSource }));
|
|
10
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type RefObject } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* useFimoLiveValue — mirror a Fimo value into React state so live edits
|
|
4
|
+
* coming from the Fimo preview editor flow through reconciliation instead
|
|
5
|
+
* of mutating the DOM directly.
|
|
6
|
+
*
|
|
7
|
+
* In dev mode (editor): the hook subscribes to `fimo:live-edit` CustomEvents
|
|
8
|
+
* on the returned ref, tags the element with `data-fimo-live-edit` so the
|
|
9
|
+
* preview knows to dispatch events instead of falling back to raw DOM writes,
|
|
10
|
+
* and updates internal state when a mutation arrives.
|
|
11
|
+
*
|
|
12
|
+
* The hook is framework-adapter-neutral: Vite and Next.js clients render the
|
|
13
|
+
* same React primitives, and environments that never dispatch
|
|
14
|
+
* `fimo:live-edit` simply keep mirroring the external value.
|
|
15
|
+
*/
|
|
16
|
+
export declare function useFimoLiveValue<T, E extends HTMLElement = HTMLElement>(external: T): {
|
|
17
|
+
value: T;
|
|
18
|
+
ref: RefObject<E | null>;
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=useFimoLiveValue.d.ts.map
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* useFimoLiveValue — mirror a Fimo value into React state so live edits
|
|
4
|
+
* coming from the Fimo preview editor flow through reconciliation instead
|
|
5
|
+
* of mutating the DOM directly.
|
|
6
|
+
*
|
|
7
|
+
* In dev mode (editor): the hook subscribes to `fimo:live-edit` CustomEvents
|
|
8
|
+
* on the returned ref, tags the element with `data-fimo-live-edit` so the
|
|
9
|
+
* preview knows to dispatch events instead of falling back to raw DOM writes,
|
|
10
|
+
* and updates internal state when a mutation arrives.
|
|
11
|
+
*
|
|
12
|
+
* The hook is framework-adapter-neutral: Vite and Next.js clients render the
|
|
13
|
+
* same React primitives, and environments that never dispatch
|
|
14
|
+
* `fimo:live-edit` simply keep mirroring the external value.
|
|
15
|
+
*/
|
|
16
|
+
export function useFimoLiveValue(external) {
|
|
17
|
+
const ref = useRef(null);
|
|
18
|
+
const [value, setValue] = useState(external);
|
|
19
|
+
const externalRef = useRef(external);
|
|
20
|
+
if (externalRef.current !== external) {
|
|
21
|
+
externalRef.current = external;
|
|
22
|
+
setValue(external);
|
|
23
|
+
}
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
const el = ref.current;
|
|
26
|
+
if (!el) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
el.setAttribute('data-fimo-live-edit', '1');
|
|
30
|
+
const handler = (event) => {
|
|
31
|
+
const detail = event.detail;
|
|
32
|
+
if (detail !== undefined) {
|
|
33
|
+
setValue(detail);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
el.addEventListener('fimo:live-edit', handler);
|
|
37
|
+
return () => {
|
|
38
|
+
el.removeEventListener('fimo:live-edit', handler);
|
|
39
|
+
el.removeAttribute('data-fimo-live-edit');
|
|
40
|
+
};
|
|
41
|
+
}, []);
|
|
42
|
+
return { value, ref };
|
|
43
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ElementType } from 'react';
|
|
2
|
+
import type { LabelProps } from '../react/primitives/contracts.js';
|
|
3
|
+
export type { LabelProps } from '../react/primitives/contracts.js';
|
|
4
|
+
/**
|
|
5
|
+
* Render one editable label from the Vite/React Router label context.
|
|
6
|
+
*
|
|
7
|
+
* For attributes, metadata, and composed strings, use `useLabels().t()`.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* <Label value="hero.title" as="h1" />
|
|
11
|
+
*/
|
|
12
|
+
export declare function Label<T extends ElementType = 'span'>({ value, ...props }: LabelProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
//# sourceMappingURL=Label.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Text } from '../react/primitives/editable/Text.js';
|
|
3
|
+
import { useLabels } from './translations.js';
|
|
4
|
+
/**
|
|
5
|
+
* Render one editable label from the Vite/React Router label context.
|
|
6
|
+
*
|
|
7
|
+
* For attributes, metadata, and composed strings, use `useLabels().t()`.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* <Label value="hero.title" as="h1" />
|
|
11
|
+
*/
|
|
12
|
+
export function Label({ value, ...props }) {
|
|
13
|
+
const { t } = useLabels();
|
|
14
|
+
const textProps = { ...props, value: t(value) };
|
|
15
|
+
return _jsx(Text, { ...textProps });
|
|
16
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ImageProps, StaticImageProps } from '../../react/primitives/contracts.js';
|
|
2
|
+
export declare function Image(props: ImageProps): import("react/jsx-runtime").JSX.Element;
|
|
3
|
+
export declare function StaticImage(props: StaticImageProps): import("react/jsx-runtime").JSX.Element;
|
|
4
|
+
//# sourceMappingURL=Images.d.ts.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { parseImageHostSuffixes } from 'fimo/primitives';
|
|
3
|
+
import { Image as BaseImage } from '../../react/primitives/editable/Image.js';
|
|
4
|
+
import { StaticImage as BaseStaticImage } from '../../react/primitives/renderers/StaticImage.js';
|
|
5
|
+
const viteEnvironment = import.meta.env;
|
|
6
|
+
const IMAGE_HOST_SUFFIXES = parseImageHostSuffixes(viteEnvironment?.VITE_FIMO_IMAGE_HOSTS);
|
|
7
|
+
export function Image(props) {
|
|
8
|
+
return _jsx(BaseImage, { ...props, hostSuffixes: IMAGE_HOST_SUFFIXES });
|
|
9
|
+
}
|
|
10
|
+
export function StaticImage(props) {
|
|
11
|
+
return _jsx(BaseStaticImage, { ...props, hostSuffixes: IMAGE_HOST_SUFFIXES });
|
|
12
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { FimoString } from 'fimo/primitives';
|
|
2
|
+
/**
|
|
3
|
+
* Hook to provide a label function (`t`).
|
|
4
|
+
*
|
|
5
|
+
* Returns FimoString with source tracking for inline editing.
|
|
6
|
+
* Use with <Text> component for rendering, or String() for escape hatch.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* const { t } = useLabels();
|
|
10
|
+
*
|
|
11
|
+
* // With Text component (recommended)
|
|
12
|
+
* <Text value={t('welcome')} />
|
|
13
|
+
*
|
|
14
|
+
* // Escape hatch for attributes
|
|
15
|
+
* <input placeholder={String(t('email.placeholder'))} />
|
|
16
|
+
*/
|
|
17
|
+
export declare function useLabels(): {
|
|
18
|
+
t: (fullKey: string) => FimoString;
|
|
19
|
+
locale: string;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Legacy alias for older projects. Prefer useLabels() in new code.
|
|
23
|
+
*/
|
|
24
|
+
export declare function useTranslations(): {
|
|
25
|
+
t: (fullKey: string) => FimoString;
|
|
26
|
+
locale: string;
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=translations.d.ts.map
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { FimoString } from 'fimo/primitives';
|
|
2
|
+
import { useSyncExternalStore } from 'react';
|
|
3
|
+
import { defaultLocale as bundledDefaultLocale, dictionaries as bundledDictionaries, signature as translationSignature, updatedAt as bundledUpdatedAt, } from 'virtual:translations';
|
|
4
|
+
import { useLocale } from '../i18n-context.js';
|
|
5
|
+
const labelListeners = new Set();
|
|
6
|
+
let labelState = {
|
|
7
|
+
dictionaries: bundledDictionaries ?? {},
|
|
8
|
+
defaultLocale: bundledDefaultLocale || 'en',
|
|
9
|
+
signature: translationSignature,
|
|
10
|
+
updatedAt: bundledUpdatedAt ?? null,
|
|
11
|
+
};
|
|
12
|
+
function subscribeLabels(listener) {
|
|
13
|
+
labelListeners.add(listener);
|
|
14
|
+
return () => {
|
|
15
|
+
labelListeners.delete(listener);
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function getLabelSnapshot() {
|
|
19
|
+
return labelState;
|
|
20
|
+
}
|
|
21
|
+
function setLabelState(next) {
|
|
22
|
+
if (next.signature === labelState.signature) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
labelState = next;
|
|
26
|
+
for (const listener of labelListeners) {
|
|
27
|
+
listener();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function isRecord(value) {
|
|
31
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
32
|
+
}
|
|
33
|
+
function normalizeDictionaries(value) {
|
|
34
|
+
if (!isRecord(value)) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
const dictionaries = {};
|
|
38
|
+
for (const [locale, labels] of Object.entries(value)) {
|
|
39
|
+
if (!isRecord(labels)) {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
dictionaries[locale] = Object.fromEntries(Object.entries(labels).filter((entry) => typeof entry[1] === 'string'));
|
|
43
|
+
}
|
|
44
|
+
return dictionaries;
|
|
45
|
+
}
|
|
46
|
+
function applyLabelUpdate(payload) {
|
|
47
|
+
if (!isRecord(payload)) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const dictionaries = normalizeDictionaries(payload.dictionaries);
|
|
51
|
+
if (!dictionaries) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const defaultLocale = typeof payload.defaultLocale === 'string' ? payload.defaultLocale : labelState.defaultLocale;
|
|
55
|
+
const signature = typeof payload.signature === 'string' ? payload.signature : JSON.stringify({ dictionaries, defaultLocale });
|
|
56
|
+
const updatedAt = typeof payload.updatedAt === 'string' ? payload.updatedAt : null;
|
|
57
|
+
setLabelState({ dictionaries, defaultLocale, signature, updatedAt });
|
|
58
|
+
}
|
|
59
|
+
// @ts-ignore - import.meta.hot is provided by Vite during development.
|
|
60
|
+
if (import.meta.hot) {
|
|
61
|
+
const onLabelsUpdated = (payload) => {
|
|
62
|
+
applyLabelUpdate(payload);
|
|
63
|
+
};
|
|
64
|
+
// @ts-ignore - import.meta.hot is provided by Vite during development.
|
|
65
|
+
import.meta.hot.on('fimo:labels-updated', onLabelsUpdated);
|
|
66
|
+
// @ts-ignore - import.meta.hot is provided by Vite during development.
|
|
67
|
+
import.meta.hot.dispose(() => {
|
|
68
|
+
// @ts-ignore - import.meta.hot is provided by Vite during development.
|
|
69
|
+
import.meta.hot.off('fimo:labels-updated', onLabelsUpdated);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Hook to provide a label function (`t`).
|
|
74
|
+
*
|
|
75
|
+
* Returns FimoString with source tracking for inline editing.
|
|
76
|
+
* Use with <Text> component for rendering, or String() for escape hatch.
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* const { t } = useLabels();
|
|
80
|
+
*
|
|
81
|
+
* // With Text component (recommended)
|
|
82
|
+
* <Text value={t('welcome')} />
|
|
83
|
+
*
|
|
84
|
+
* // Escape hatch for attributes
|
|
85
|
+
* <input placeholder={String(t('email.placeholder'))} />
|
|
86
|
+
*/
|
|
87
|
+
export function useLabels() {
|
|
88
|
+
const localeContext = useLocale();
|
|
89
|
+
const labels = useSyncExternalStore(subscribeLabels, getLabelSnapshot, getLabelSnapshot);
|
|
90
|
+
const t = (fullKey) => {
|
|
91
|
+
const locale = localeContext.locale || labels.defaultLocale || 'en';
|
|
92
|
+
const dictionary = labels.dictionaries[locale] ?? labels.dictionaries[labels.defaultLocale] ?? {};
|
|
93
|
+
const text = dictionary[fullKey] ?? '';
|
|
94
|
+
return new FimoString(text, `__labels.${locale}.${fullKey}.value`);
|
|
95
|
+
};
|
|
96
|
+
return { t, locale: localeContext.locale };
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Legacy alias for older projects. Prefer useLabels() in new code.
|
|
100
|
+
*/
|
|
101
|
+
export function useTranslations() {
|
|
102
|
+
return useLabels();
|
|
103
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "fimo-react",
|
|
3
|
+
"version": "0.21.0-experimental.1",
|
|
4
|
+
"description": "Fimo React runtime: content clients, labels, and source-tracked primitives.",
|
|
5
|
+
"files": [
|
|
6
|
+
"dist/",
|
|
7
|
+
"!dist/**/*.d.ts.map",
|
|
8
|
+
"README.md"
|
|
9
|
+
],
|
|
10
|
+
"type": "module",
|
|
11
|
+
"sideEffects": false,
|
|
12
|
+
"exports": {
|
|
13
|
+
"./package.json": "./package.json",
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/runtime/index.d.ts",
|
|
16
|
+
"import": "./dist/runtime/index.js"
|
|
17
|
+
},
|
|
18
|
+
"./primitives": {
|
|
19
|
+
"types": "./dist/runtime/primitives.d.ts",
|
|
20
|
+
"import": "./dist/runtime/primitives.js"
|
|
21
|
+
},
|
|
22
|
+
"./primitives/server": {
|
|
23
|
+
"types": "./dist/runtime/primitives-server.d.ts",
|
|
24
|
+
"import": "./dist/runtime/primitives-server.js"
|
|
25
|
+
},
|
|
26
|
+
"./content": {
|
|
27
|
+
"types": "./dist/runtime/content/index.d.ts",
|
|
28
|
+
"import": "./dist/runtime/content/index.js"
|
|
29
|
+
},
|
|
30
|
+
"./internal": {
|
|
31
|
+
"types": "./dist/runtime/internal.d.ts",
|
|
32
|
+
"import": "./dist/runtime/internal.js"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "tsc -b tsconfig.json",
|
|
40
|
+
"check:types": "tsc -b tsconfig.json --noEmit",
|
|
41
|
+
"clean": "rm -rf dist tsconfig.tsbuildinfo",
|
|
42
|
+
"test": "vitest run",
|
|
43
|
+
"test:watch": "vitest"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@fimo/tsconfig": "0.21.0-experimental.1",
|
|
47
|
+
"@tanstack/react-query": "^5.80.6",
|
|
48
|
+
"@types/node": "^22.15.29",
|
|
49
|
+
"@types/react": "^19.2.14",
|
|
50
|
+
"@types/react-dom": "^19.2.3",
|
|
51
|
+
"fimo": "0.21.0-experimental.1",
|
|
52
|
+
"react": "^19.2.4",
|
|
53
|
+
"react-dom": "^19.2.4",
|
|
54
|
+
"typescript": "7.0.2",
|
|
55
|
+
"vite": "^8.0.13",
|
|
56
|
+
"vitest": "^4.1.6"
|
|
57
|
+
},
|
|
58
|
+
"peerDependencies": {
|
|
59
|
+
"@tanstack/react-query": "^5.80.6",
|
|
60
|
+
"fimo": ">=0.14.0",
|
|
61
|
+
"react": "^18.2.0 || ^19.0.0",
|
|
62
|
+
"react-dom": "^18.2.0 || ^19.0.0"
|
|
63
|
+
},
|
|
64
|
+
"peerDependenciesMeta": {
|
|
65
|
+
"@tanstack/react-query": {
|
|
66
|
+
"optional": true
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
"engines": {
|
|
70
|
+
"node": ">=20.12.0"
|
|
71
|
+
}
|
|
72
|
+
}
|