@verbaly/react 0.22.0 → 0.24.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 CHANGED
@@ -2,7 +2,7 @@
2
2
  <img src="https://raw.githubusercontent.com/AronSoto/verbaly/develop/assets/logo.png" alt="Verbaly" width="300" />
3
3
  </p>
4
4
 
5
- <p align="center"><em>React bindings for Verbaly hooks over the reactive core.</em></p>
5
+ <p align="center"><em>React bindings for Verbaly: hooks over the reactive core.</em></p>
6
6
 
7
7
  <p align="center">
8
8
  <a href="https://www.npmjs.com/package/@verbaly/react"><img src="https://img.shields.io/npm/v/@verbaly/react?logo=npm&color=cb3837" alt="npm version" /></a>
@@ -11,7 +11,7 @@
11
11
 
12
12
  ---
13
13
 
14
- React hooks for [Verbaly](https://github.com/AronSoto/verbaly) a thin layer (React 18/19) over the reactive core via `useSyncExternalStore`.
14
+ React hooks for [Verbaly](https://github.com/AronSoto/verbaly): a thin layer (React 18/19) over the reactive core via `useSyncExternalStore`.
15
15
 
16
16
  ## 🚀 Install
17
17
 
@@ -33,9 +33,9 @@ function Inbox() {
33
33
  }
34
34
  ```
35
35
 
36
- ### ✨ Rich text `<Trans>`
36
+ ### ✨ Rich text: `<Trans>`
37
37
 
38
- Write the source text in place the compiler extracts it (key, catalogs, props):
38
+ Write the source text in place and the compiler extracts it (key, catalogs, props):
39
39
 
40
40
  ```tsx
41
41
  import { Trans } from '@verbaly/react';
@@ -46,9 +46,9 @@ import { Trans } from '@verbaly/react';
46
46
  <Trans id="x7Ka9q2f" components={{ "a": <a href="/terms" /> }} />
47
47
  ```
48
48
 
49
- Runtime-first still works pass `id` (+ `values`/`components`) yourself and nothing is touched. JSX whitespace rules apply: a line break between an element and text renders no space (use `{' '}`).
49
+ Runtime-first still works: pass `id` (+ `values`/`components`) yourself and nothing is touched. Whitelisted phrasing tags in a message (`<em>`, `<code>`…) render as real elements, same whitelist as `data-verbaly-rich` (`richTags` overrides it); unknown tags unwrap to inert text. JSX whitespace rules apply: a line break between an element and text renders no space (use `{' '}`).
50
50
 
51
- Named links without custom components hrefs come from your code, never from messages (`javascript:` blocked):
51
+ Named links without custom components; hrefs come from your code, never from messages (`javascript:` blocked):
52
52
 
53
53
  ```tsx
54
54
  // message: 'Read the <docs>guide</docs>'
@@ -57,7 +57,7 @@ Named links without custom components — hrefs come from your code, never from
57
57
 
58
58
  📖 Docs: **https://verbaly-web.vercel.app/docs/frameworks**
59
59
 
60
- > ⚠️ Early development (`0.x`) API not stable yet.
60
+ > ⚠️ Early development (`0.x`): API not stable yet.
61
61
 
62
62
  ## License
63
63
 
package/dist/index.cjs CHANGED
@@ -25,12 +25,17 @@ function useVersion(instance) {
25
25
  (0, react.useSyncExternalStore)(instance.subscribe, () => instance.version, () => instance.version);
26
26
  }
27
27
  function Trans(props) {
28
- return (0, react.createElement)(react.Fragment, null, ...toNodes((0, verbaly.parseTags)(useT()(props.id, props.values)), props.components ?? {}, props.links ?? {}));
28
+ const ctx = (0, react.useContext)(VerbalyContext);
29
+ const instance = props.instance ?? ctx;
30
+ if (!instance) throw new Error("[verbaly] <Trans> requires an instance prop or a <VerbalyProvider>");
31
+ useVersion(instance);
32
+ return (0, react.createElement)(react.Fragment, null, ...toNodes((0, verbaly.parseTags)(instance.t(props.id, props.values)), props.components ?? {}, props.links ?? {}, new Set(props.richTags ?? verbaly.RICH_TAGS)));
29
33
  }
30
- function toNodes(nodes, components, links) {
34
+ const VOID_TAGS = /* @__PURE__ */ new Set(["br", "wbr"]);
35
+ function toNodes(nodes, components, links, richTags) {
31
36
  return nodes.map((node, i) => {
32
37
  if (typeof node === "string") return node;
33
- const children = toNodes(node.children, components, links);
38
+ const children = toNodes(node.children, components, links, richTags);
34
39
  const el = components[node.name];
35
40
  if (el) return (0, react.cloneElement)(el, { key: i }, ...children);
36
41
  const link = links[node.name];
@@ -38,6 +43,10 @@ function toNodes(nodes, components, links) {
38
43
  key: i,
39
44
  ...(0, verbaly.normalizeLink)(link)
40
45
  }, ...children);
46
+ if (richTags.has(node.name)) {
47
+ if (VOID_TAGS.has(node.name)) return (0, react.createElement)(node.name, { key: i });
48
+ return (0, react.createElement)(node.name, { key: i }, ...children);
49
+ }
41
50
  return (0, react.createElement)(react.Fragment, { key: i }, ...children);
42
51
  });
43
52
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["Fragment"],"sources":["../src/index.ts"],"sourcesContent":["import {\n cloneElement,\n createContext,\n createElement,\n Fragment,\n useContext,\n useSyncExternalStore,\n type ReactElement,\n type ReactNode,\n} from 'react';\nimport {\n normalizeLink,\n parseTags,\n type DictionaryInput,\n type Params,\n type RichLink,\n type TagNode,\n type TFunction,\n type Verbaly,\n} from 'verbaly';\n\nconst VerbalyContext = createContext<Verbaly | null>(null);\n\nexport interface VerbalyProviderProps<D extends DictionaryInput> {\n instance: Verbaly<D>;\n children?: ReactNode;\n}\n\nexport function VerbalyProvider<D extends DictionaryInput>(\n props: VerbalyProviderProps<D>,\n): ReactElement {\n return createElement(\n VerbalyContext.Provider,\n { value: props.instance as unknown as Verbaly },\n props.children,\n );\n}\n\nexport function useVerbaly<D extends DictionaryInput = DictionaryInput>(): Verbaly<D> {\n const instance = useContext(VerbalyContext);\n if (!instance) {\n throw new Error('[verbaly] useVerbaly requires a <VerbalyProvider>');\n }\n return instance as unknown as Verbaly<D>;\n}\n\nexport function useT<D extends DictionaryInput = DictionaryInput>(): TFunction<D> {\n const instance = useVerbaly<D>();\n useVersion(instance);\n return instance.t;\n}\n\nexport function useLocale(): [string, (locale: string) => void] {\n const instance = useVerbaly();\n useVersion(instance);\n return [instance.locale, instance.setLocale];\n}\n\nfunction useVersion<D extends DictionaryInput>(instance: Verbaly<D>): void {\n useSyncExternalStore(\n instance.subscribe,\n () => instance.version,\n () => instance.version,\n );\n}\n\nexport interface TransProps {\n id: string;\n values?: Params;\n components?: Record<string, ReactElement>;\n links?: Record<string, RichLink>;\n}\n\n// translated message + element interpolation\nexport function Trans(props: TransProps): ReactElement {\n const t = useT();\n const text = (t as unknown as (id: string, values?: Params) => string)(props.id, props.values);\n return createElement(\n Fragment,\n null,\n ...toNodes(parseTags(text), props.components ?? {}, props.links ?? {}),\n );\n}\n\nfunction toNodes(\n nodes: TagNode[],\n components: Record<string, ReactElement>,\n links: Record<string, RichLink>,\n): ReactNode[] {\n return nodes.map((node, i) => {\n if (typeof node === 'string') return node;\n const children = toNodes(node.children, components, links);\n const el = components[node.name];\n if (el) return cloneElement(el, { key: i }, ...children);\n const link = links[node.name];\n if (link !== undefined) {\n return createElement('a', { key: i, ...normalizeLink(link) }, ...children);\n }\n return createElement(Fragment, { key: i }, ...children);\n });\n}\n\nexport type { Params, TFunction, Verbaly };\n"],"mappings":";;;;AAqBA,MAAM,kBAAA,GAAA,MAAA,cAAA,CAA+C,IAAI;AAOzD,SAAgB,gBACd,OACc;CACd,QAAA,GAAA,MAAA,cAAA,CACE,eAAe,UACf,EAAE,OAAO,MAAM,SAA+B,GAC9C,MAAM,QACR;AACF;AAEA,SAAgB,aAAsE;CACpF,MAAM,YAAA,GAAA,MAAA,WAAA,CAAsB,cAAc;CAC1C,IAAI,CAAC,UACH,MAAM,IAAI,MAAM,mDAAmD;CAErE,OAAO;AACT;AAEA,SAAgB,OAAkE;CAChF,MAAM,WAAW,WAAc;CAC/B,WAAW,QAAQ;CACnB,OAAO,SAAS;AAClB;AAEA,SAAgB,YAAgD;CAC9D,MAAM,WAAW,WAAW;CAC5B,WAAW,QAAQ;CACnB,OAAO,CAAC,SAAS,QAAQ,SAAS,SAAS;AAC7C;AAEA,SAAS,WAAsC,UAA4B;CACzE,CAAA,GAAA,MAAA,qBAAA,CACE,SAAS,iBACH,SAAS,eACT,SAAS,OACjB;AACF;AAUA,SAAgB,MAAM,OAAiC;CAGrD,QAAA,GAAA,MAAA,cAAA,CACEA,MAAAA,UACA,MACA,GAAG,SAAA,GAAA,QAAA,UAAA,CALK,KACI,CAAC,CAAwD,MAAM,IAAI,MAAM,MAI7D,CAAC,GAAG,MAAM,cAAc,CAAC,GAAG,MAAM,SAAS,CAAC,CAAC,CACvE;AACF;AAEA,SAAS,QACP,OACA,YACA,OACa;CACb,OAAO,MAAM,KAAK,MAAM,MAAM;EAC5B,IAAI,OAAO,SAAS,UAAU,OAAO;EACrC,MAAM,WAAW,QAAQ,KAAK,UAAU,YAAY,KAAK;EACzD,MAAM,KAAK,WAAW,KAAK;EAC3B,IAAI,IAAI,QAAA,GAAA,MAAA,aAAA,CAAoB,IAAI,EAAE,KAAK,EAAE,GAAG,GAAG,QAAQ;EACvD,MAAM,OAAO,MAAM,KAAK;EACxB,IAAI,SAAS,KAAA,GACX,QAAA,GAAA,MAAA,cAAA,CAAqB,KAAK;GAAE,KAAK;GAAG,IAAA,GAAA,QAAA,cAAA,CAAiB,IAAI;EAAE,GAAG,GAAG,QAAQ;EAE3E,QAAA,GAAA,MAAA,cAAA,CAAqBA,MAAAA,UAAU,EAAE,KAAK,EAAE,GAAG,GAAG,QAAQ;CACxD,CAAC;AACH"}
1
+ {"version":3,"file":"index.cjs","names":["Fragment","RICH_TAGS"],"sources":["../src/index.ts"],"sourcesContent":["import {\n cloneElement,\n createContext,\n createElement,\n Fragment,\n useContext,\n useSyncExternalStore,\n type ReactElement,\n type ReactNode,\n} from 'react';\nimport {\n normalizeLink,\n parseTags,\n RICH_TAGS,\n type DictionaryInput,\n type Params,\n type RichLink,\n type TagNode,\n type TFunction,\n type Verbaly,\n} from 'verbaly';\n\nconst VerbalyContext = createContext<Verbaly | null>(null);\n\nexport interface VerbalyProviderProps<D extends DictionaryInput> {\n instance: Verbaly<D>;\n children?: ReactNode;\n}\n\nexport function VerbalyProvider<D extends DictionaryInput>(\n props: VerbalyProviderProps<D>,\n): ReactElement {\n return createElement(\n VerbalyContext.Provider,\n { value: props.instance as unknown as Verbaly },\n props.children,\n );\n}\n\nexport function useVerbaly<D extends DictionaryInput = DictionaryInput>(): Verbaly<D> {\n const instance = useContext(VerbalyContext);\n if (!instance) {\n throw new Error('[verbaly] useVerbaly requires a <VerbalyProvider>');\n }\n return instance as unknown as Verbaly<D>;\n}\n\nexport function useT<D extends DictionaryInput = DictionaryInput>(): TFunction<D> {\n const instance = useVerbaly<D>();\n useVersion(instance);\n return instance.t;\n}\n\nexport function useLocale(): [string, (locale: string) => void] {\n const instance = useVerbaly();\n useVersion(instance);\n return [instance.locale, instance.setLocale];\n}\n\nfunction useVersion<D extends DictionaryInput>(instance: Verbaly<D>): void {\n useSyncExternalStore(\n instance.subscribe,\n () => instance.version,\n () => instance.version,\n );\n}\n\nexport interface TransProps {\n id: string;\n values?: Params;\n instance?: Verbaly;\n components?: Record<string, ReactElement>;\n richTags?: string[];\n links?: Record<string, RichLink>;\n}\n\n// translated message + element interpolation\nexport function Trans(props: TransProps): ReactElement {\n const ctx = useContext(VerbalyContext);\n const instance = props.instance ?? ctx;\n if (!instance) {\n throw new Error('[verbaly] <Trans> requires an instance prop or a <VerbalyProvider>');\n }\n useVersion(instance);\n const text = (instance.t as unknown as (id: string, values?: Params) => string)(\n props.id,\n props.values,\n );\n return createElement(\n Fragment,\n null,\n ...toNodes(\n parseTags(text),\n props.components ?? {},\n props.links ?? {},\n new Set(props.richTags ?? RICH_TAGS),\n ),\n );\n}\n\n// react rejects children on void elements\nconst VOID_TAGS = new Set(['br', 'wbr']);\n\nfunction toNodes(\n nodes: TagNode[],\n components: Record<string, ReactElement>,\n links: Record<string, RichLink>,\n richTags: Set<string>,\n): ReactNode[] {\n return nodes.map((node, i) => {\n if (typeof node === 'string') return node;\n const children = toNodes(node.children, components, links, richTags);\n const el = components[node.name];\n if (el) return cloneElement(el, { key: i }, ...children);\n const link = links[node.name];\n if (link !== undefined) {\n return createElement('a', { key: i, ...normalizeLink(link) }, ...children);\n }\n if (richTags.has(node.name)) {\n if (VOID_TAGS.has(node.name)) return createElement(node.name, { key: i });\n return createElement(node.name, { key: i }, ...children);\n }\n return createElement(Fragment, { key: i }, ...children);\n });\n}\n\nexport type { Params, TFunction, Verbaly };\n"],"mappings":";;;;AAsBA,MAAM,kBAAA,GAAA,MAAA,cAAA,CAA+C,IAAI;AAOzD,SAAgB,gBACd,OACc;CACd,QAAA,GAAA,MAAA,cAAA,CACE,eAAe,UACf,EAAE,OAAO,MAAM,SAA+B,GAC9C,MAAM,QACR;AACF;AAEA,SAAgB,aAAsE;CACpF,MAAM,YAAA,GAAA,MAAA,WAAA,CAAsB,cAAc;CAC1C,IAAI,CAAC,UACH,MAAM,IAAI,MAAM,mDAAmD;CAErE,OAAO;AACT;AAEA,SAAgB,OAAkE;CAChF,MAAM,WAAW,WAAc;CAC/B,WAAW,QAAQ;CACnB,OAAO,SAAS;AAClB;AAEA,SAAgB,YAAgD;CAC9D,MAAM,WAAW,WAAW;CAC5B,WAAW,QAAQ;CACnB,OAAO,CAAC,SAAS,QAAQ,SAAS,SAAS;AAC7C;AAEA,SAAS,WAAsC,UAA4B;CACzE,CAAA,GAAA,MAAA,qBAAA,CACE,SAAS,iBACH,SAAS,eACT,SAAS,OACjB;AACF;AAYA,SAAgB,MAAM,OAAiC;CACrD,MAAM,OAAA,GAAA,MAAA,WAAA,CAAiB,cAAc;CACrC,MAAM,WAAW,MAAM,YAAY;CACnC,IAAI,CAAC,UACH,MAAM,IAAI,MAAM,oEAAoE;CAEtF,WAAW,QAAQ;CAKnB,QAAA,GAAA,MAAA,cAAA,CACEA,MAAAA,UACA,MACA,GAAG,SAAA,GAAA,QAAA,UAAA,CAPS,SAAS,EACrB,MAAM,IACN,MAAM,MAMS,CAAC,GACd,MAAM,cAAc,CAAC,GACrB,MAAM,SAAS,CAAC,GAChB,IAAI,IAAI,MAAM,YAAYC,QAAAA,SAAS,CACrC,CACF;AACF;AAGA,MAAM,4BAAY,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;AAEvC,SAAS,QACP,OACA,YACA,OACA,UACa;CACb,OAAO,MAAM,KAAK,MAAM,MAAM;EAC5B,IAAI,OAAO,SAAS,UAAU,OAAO;EACrC,MAAM,WAAW,QAAQ,KAAK,UAAU,YAAY,OAAO,QAAQ;EACnE,MAAM,KAAK,WAAW,KAAK;EAC3B,IAAI,IAAI,QAAA,GAAA,MAAA,aAAA,CAAoB,IAAI,EAAE,KAAK,EAAE,GAAG,GAAG,QAAQ;EACvD,MAAM,OAAO,MAAM,KAAK;EACxB,IAAI,SAAS,KAAA,GACX,QAAA,GAAA,MAAA,cAAA,CAAqB,KAAK;GAAE,KAAK;GAAG,IAAA,GAAA,QAAA,cAAA,CAAiB,IAAI;EAAE,GAAG,GAAG,QAAQ;EAE3E,IAAI,SAAS,IAAI,KAAK,IAAI,GAAG;GAC3B,IAAI,UAAU,IAAI,KAAK,IAAI,GAAG,QAAA,GAAA,MAAA,cAAA,CAAqB,KAAK,MAAM,EAAE,KAAK,EAAE,CAAC;GACxE,QAAA,GAAA,MAAA,cAAA,CAAqB,KAAK,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,QAAQ;EACzD;EACA,QAAA,GAAA,MAAA,cAAA,CAAqBD,MAAAA,UAAU,EAAE,KAAK,EAAE,GAAG,GAAG,QAAQ;CACxD,CAAC;AACH"}
package/dist/index.d.cts CHANGED
@@ -12,7 +12,9 @@ declare function useLocale(): [string, (locale: string) => void];
12
12
  interface TransProps {
13
13
  id: string;
14
14
  values?: Params;
15
+ instance?: Verbaly;
15
16
  components?: Record<string, ReactElement>;
17
+ richTags?: string[];
16
18
  links?: Record<string, RichLink>;
17
19
  }
18
20
  declare function Trans(props: TransProps): ReactElement;
package/dist/index.d.ts CHANGED
@@ -12,7 +12,9 @@ declare function useLocale(): [string, (locale: string) => void];
12
12
  interface TransProps {
13
13
  id: string;
14
14
  values?: Params;
15
+ instance?: Verbaly;
15
16
  components?: Record<string, ReactElement>;
17
+ richTags?: string[];
16
18
  links?: Record<string, RichLink>;
17
19
  }
18
20
  declare function Trans(props: TransProps): ReactElement;
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Fragment, cloneElement, createContext, createElement, useContext, useSyncExternalStore } from "react";
2
- import { normalizeLink, parseTags } from "verbaly";
2
+ import { RICH_TAGS, normalizeLink, parseTags } from "verbaly";
3
3
  //#region src/index.ts
4
4
  const VerbalyContext = createContext(null);
5
5
  function VerbalyProvider(props) {
@@ -24,12 +24,17 @@ function useVersion(instance) {
24
24
  useSyncExternalStore(instance.subscribe, () => instance.version, () => instance.version);
25
25
  }
26
26
  function Trans(props) {
27
- return createElement(Fragment, null, ...toNodes(parseTags(useT()(props.id, props.values)), props.components ?? {}, props.links ?? {}));
27
+ const ctx = useContext(VerbalyContext);
28
+ const instance = props.instance ?? ctx;
29
+ if (!instance) throw new Error("[verbaly] <Trans> requires an instance prop or a <VerbalyProvider>");
30
+ useVersion(instance);
31
+ return createElement(Fragment, null, ...toNodes(parseTags(instance.t(props.id, props.values)), props.components ?? {}, props.links ?? {}, new Set(props.richTags ?? RICH_TAGS)));
28
32
  }
29
- function toNodes(nodes, components, links) {
33
+ const VOID_TAGS = /* @__PURE__ */ new Set(["br", "wbr"]);
34
+ function toNodes(nodes, components, links, richTags) {
30
35
  return nodes.map((node, i) => {
31
36
  if (typeof node === "string") return node;
32
- const children = toNodes(node.children, components, links);
37
+ const children = toNodes(node.children, components, links, richTags);
33
38
  const el = components[node.name];
34
39
  if (el) return cloneElement(el, { key: i }, ...children);
35
40
  const link = links[node.name];
@@ -37,6 +42,10 @@ function toNodes(nodes, components, links) {
37
42
  key: i,
38
43
  ...normalizeLink(link)
39
44
  }, ...children);
45
+ if (richTags.has(node.name)) {
46
+ if (VOID_TAGS.has(node.name)) return createElement(node.name, { key: i });
47
+ return createElement(node.name, { key: i }, ...children);
48
+ }
40
49
  return createElement(Fragment, { key: i }, ...children);
41
50
  });
42
51
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import {\n cloneElement,\n createContext,\n createElement,\n Fragment,\n useContext,\n useSyncExternalStore,\n type ReactElement,\n type ReactNode,\n} from 'react';\nimport {\n normalizeLink,\n parseTags,\n type DictionaryInput,\n type Params,\n type RichLink,\n type TagNode,\n type TFunction,\n type Verbaly,\n} from 'verbaly';\n\nconst VerbalyContext = createContext<Verbaly | null>(null);\n\nexport interface VerbalyProviderProps<D extends DictionaryInput> {\n instance: Verbaly<D>;\n children?: ReactNode;\n}\n\nexport function VerbalyProvider<D extends DictionaryInput>(\n props: VerbalyProviderProps<D>,\n): ReactElement {\n return createElement(\n VerbalyContext.Provider,\n { value: props.instance as unknown as Verbaly },\n props.children,\n );\n}\n\nexport function useVerbaly<D extends DictionaryInput = DictionaryInput>(): Verbaly<D> {\n const instance = useContext(VerbalyContext);\n if (!instance) {\n throw new Error('[verbaly] useVerbaly requires a <VerbalyProvider>');\n }\n return instance as unknown as Verbaly<D>;\n}\n\nexport function useT<D extends DictionaryInput = DictionaryInput>(): TFunction<D> {\n const instance = useVerbaly<D>();\n useVersion(instance);\n return instance.t;\n}\n\nexport function useLocale(): [string, (locale: string) => void] {\n const instance = useVerbaly();\n useVersion(instance);\n return [instance.locale, instance.setLocale];\n}\n\nfunction useVersion<D extends DictionaryInput>(instance: Verbaly<D>): void {\n useSyncExternalStore(\n instance.subscribe,\n () => instance.version,\n () => instance.version,\n );\n}\n\nexport interface TransProps {\n id: string;\n values?: Params;\n components?: Record<string, ReactElement>;\n links?: Record<string, RichLink>;\n}\n\n// translated message + element interpolation\nexport function Trans(props: TransProps): ReactElement {\n const t = useT();\n const text = (t as unknown as (id: string, values?: Params) => string)(props.id, props.values);\n return createElement(\n Fragment,\n null,\n ...toNodes(parseTags(text), props.components ?? {}, props.links ?? {}),\n );\n}\n\nfunction toNodes(\n nodes: TagNode[],\n components: Record<string, ReactElement>,\n links: Record<string, RichLink>,\n): ReactNode[] {\n return nodes.map((node, i) => {\n if (typeof node === 'string') return node;\n const children = toNodes(node.children, components, links);\n const el = components[node.name];\n if (el) return cloneElement(el, { key: i }, ...children);\n const link = links[node.name];\n if (link !== undefined) {\n return createElement('a', { key: i, ...normalizeLink(link) }, ...children);\n }\n return createElement(Fragment, { key: i }, ...children);\n });\n}\n\nexport type { Params, TFunction, Verbaly };\n"],"mappings":";;;AAqBA,MAAM,iBAAiB,cAA8B,IAAI;AAOzD,SAAgB,gBACd,OACc;CACd,OAAO,cACL,eAAe,UACf,EAAE,OAAO,MAAM,SAA+B,GAC9C,MAAM,QACR;AACF;AAEA,SAAgB,aAAsE;CACpF,MAAM,WAAW,WAAW,cAAc;CAC1C,IAAI,CAAC,UACH,MAAM,IAAI,MAAM,mDAAmD;CAErE,OAAO;AACT;AAEA,SAAgB,OAAkE;CAChF,MAAM,WAAW,WAAc;CAC/B,WAAW,QAAQ;CACnB,OAAO,SAAS;AAClB;AAEA,SAAgB,YAAgD;CAC9D,MAAM,WAAW,WAAW;CAC5B,WAAW,QAAQ;CACnB,OAAO,CAAC,SAAS,QAAQ,SAAS,SAAS;AAC7C;AAEA,SAAS,WAAsC,UAA4B;CACzE,qBACE,SAAS,iBACH,SAAS,eACT,SAAS,OACjB;AACF;AAUA,SAAgB,MAAM,OAAiC;CAGrD,OAAO,cACL,UACA,MACA,GAAG,QAAQ,UALH,KACI,CAAC,CAAwD,MAAM,IAAI,MAAM,MAI7D,CAAC,GAAG,MAAM,cAAc,CAAC,GAAG,MAAM,SAAS,CAAC,CAAC,CACvE;AACF;AAEA,SAAS,QACP,OACA,YACA,OACa;CACb,OAAO,MAAM,KAAK,MAAM,MAAM;EAC5B,IAAI,OAAO,SAAS,UAAU,OAAO;EACrC,MAAM,WAAW,QAAQ,KAAK,UAAU,YAAY,KAAK;EACzD,MAAM,KAAK,WAAW,KAAK;EAC3B,IAAI,IAAI,OAAO,aAAa,IAAI,EAAE,KAAK,EAAE,GAAG,GAAG,QAAQ;EACvD,MAAM,OAAO,MAAM,KAAK;EACxB,IAAI,SAAS,KAAA,GACX,OAAO,cAAc,KAAK;GAAE,KAAK;GAAG,GAAG,cAAc,IAAI;EAAE,GAAG,GAAG,QAAQ;EAE3E,OAAO,cAAc,UAAU,EAAE,KAAK,EAAE,GAAG,GAAG,QAAQ;CACxD,CAAC;AACH"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import {\n cloneElement,\n createContext,\n createElement,\n Fragment,\n useContext,\n useSyncExternalStore,\n type ReactElement,\n type ReactNode,\n} from 'react';\nimport {\n normalizeLink,\n parseTags,\n RICH_TAGS,\n type DictionaryInput,\n type Params,\n type RichLink,\n type TagNode,\n type TFunction,\n type Verbaly,\n} from 'verbaly';\n\nconst VerbalyContext = createContext<Verbaly | null>(null);\n\nexport interface VerbalyProviderProps<D extends DictionaryInput> {\n instance: Verbaly<D>;\n children?: ReactNode;\n}\n\nexport function VerbalyProvider<D extends DictionaryInput>(\n props: VerbalyProviderProps<D>,\n): ReactElement {\n return createElement(\n VerbalyContext.Provider,\n { value: props.instance as unknown as Verbaly },\n props.children,\n );\n}\n\nexport function useVerbaly<D extends DictionaryInput = DictionaryInput>(): Verbaly<D> {\n const instance = useContext(VerbalyContext);\n if (!instance) {\n throw new Error('[verbaly] useVerbaly requires a <VerbalyProvider>');\n }\n return instance as unknown as Verbaly<D>;\n}\n\nexport function useT<D extends DictionaryInput = DictionaryInput>(): TFunction<D> {\n const instance = useVerbaly<D>();\n useVersion(instance);\n return instance.t;\n}\n\nexport function useLocale(): [string, (locale: string) => void] {\n const instance = useVerbaly();\n useVersion(instance);\n return [instance.locale, instance.setLocale];\n}\n\nfunction useVersion<D extends DictionaryInput>(instance: Verbaly<D>): void {\n useSyncExternalStore(\n instance.subscribe,\n () => instance.version,\n () => instance.version,\n );\n}\n\nexport interface TransProps {\n id: string;\n values?: Params;\n instance?: Verbaly;\n components?: Record<string, ReactElement>;\n richTags?: string[];\n links?: Record<string, RichLink>;\n}\n\n// translated message + element interpolation\nexport function Trans(props: TransProps): ReactElement {\n const ctx = useContext(VerbalyContext);\n const instance = props.instance ?? ctx;\n if (!instance) {\n throw new Error('[verbaly] <Trans> requires an instance prop or a <VerbalyProvider>');\n }\n useVersion(instance);\n const text = (instance.t as unknown as (id: string, values?: Params) => string)(\n props.id,\n props.values,\n );\n return createElement(\n Fragment,\n null,\n ...toNodes(\n parseTags(text),\n props.components ?? {},\n props.links ?? {},\n new Set(props.richTags ?? RICH_TAGS),\n ),\n );\n}\n\n// react rejects children on void elements\nconst VOID_TAGS = new Set(['br', 'wbr']);\n\nfunction toNodes(\n nodes: TagNode[],\n components: Record<string, ReactElement>,\n links: Record<string, RichLink>,\n richTags: Set<string>,\n): ReactNode[] {\n return nodes.map((node, i) => {\n if (typeof node === 'string') return node;\n const children = toNodes(node.children, components, links, richTags);\n const el = components[node.name];\n if (el) return cloneElement(el, { key: i }, ...children);\n const link = links[node.name];\n if (link !== undefined) {\n return createElement('a', { key: i, ...normalizeLink(link) }, ...children);\n }\n if (richTags.has(node.name)) {\n if (VOID_TAGS.has(node.name)) return createElement(node.name, { key: i });\n return createElement(node.name, { key: i }, ...children);\n }\n return createElement(Fragment, { key: i }, ...children);\n });\n}\n\nexport type { Params, TFunction, Verbaly };\n"],"mappings":";;;AAsBA,MAAM,iBAAiB,cAA8B,IAAI;AAOzD,SAAgB,gBACd,OACc;CACd,OAAO,cACL,eAAe,UACf,EAAE,OAAO,MAAM,SAA+B,GAC9C,MAAM,QACR;AACF;AAEA,SAAgB,aAAsE;CACpF,MAAM,WAAW,WAAW,cAAc;CAC1C,IAAI,CAAC,UACH,MAAM,IAAI,MAAM,mDAAmD;CAErE,OAAO;AACT;AAEA,SAAgB,OAAkE;CAChF,MAAM,WAAW,WAAc;CAC/B,WAAW,QAAQ;CACnB,OAAO,SAAS;AAClB;AAEA,SAAgB,YAAgD;CAC9D,MAAM,WAAW,WAAW;CAC5B,WAAW,QAAQ;CACnB,OAAO,CAAC,SAAS,QAAQ,SAAS,SAAS;AAC7C;AAEA,SAAS,WAAsC,UAA4B;CACzE,qBACE,SAAS,iBACH,SAAS,eACT,SAAS,OACjB;AACF;AAYA,SAAgB,MAAM,OAAiC;CACrD,MAAM,MAAM,WAAW,cAAc;CACrC,MAAM,WAAW,MAAM,YAAY;CACnC,IAAI,CAAC,UACH,MAAM,IAAI,MAAM,oEAAoE;CAEtF,WAAW,QAAQ;CAKnB,OAAO,cACL,UACA,MACA,GAAG,QACD,UARU,SAAS,EACrB,MAAM,IACN,MAAM,MAMS,CAAC,GACd,MAAM,cAAc,CAAC,GACrB,MAAM,SAAS,CAAC,GAChB,IAAI,IAAI,MAAM,YAAY,SAAS,CACrC,CACF;AACF;AAGA,MAAM,4BAAY,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;AAEvC,SAAS,QACP,OACA,YACA,OACA,UACa;CACb,OAAO,MAAM,KAAK,MAAM,MAAM;EAC5B,IAAI,OAAO,SAAS,UAAU,OAAO;EACrC,MAAM,WAAW,QAAQ,KAAK,UAAU,YAAY,OAAO,QAAQ;EACnE,MAAM,KAAK,WAAW,KAAK;EAC3B,IAAI,IAAI,OAAO,aAAa,IAAI,EAAE,KAAK,EAAE,GAAG,GAAG,QAAQ;EACvD,MAAM,OAAO,MAAM,KAAK;EACxB,IAAI,SAAS,KAAA,GACX,OAAO,cAAc,KAAK;GAAE,KAAK;GAAG,GAAG,cAAc,IAAI;EAAE,GAAG,GAAG,QAAQ;EAE3E,IAAI,SAAS,IAAI,KAAK,IAAI,GAAG;GAC3B,IAAI,UAAU,IAAI,KAAK,IAAI,GAAG,OAAO,cAAc,KAAK,MAAM,EAAE,KAAK,EAAE,CAAC;GACxE,OAAO,cAAc,KAAK,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,QAAQ;EACzD;EACA,OAAO,cAAc,UAAU,EAAE,KAAK,EAAE,GAAG,GAAG,QAAQ;CACxD,CAAC;AACH"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@verbaly/react",
3
- "version": "0.22.0",
4
- "description": "React bindings for Verbaly hooks over the reactive core.",
3
+ "version": "0.24.0",
4
+ "description": "React bindings for Verbaly: hooks over the reactive core.",
5
5
  "keywords": [
6
6
  "i18n",
7
7
  "react",
@@ -48,7 +48,7 @@
48
48
  },
49
49
  "peerDependencies": {
50
50
  "react": "^18.0.0 || ^19.0.0",
51
- "verbaly": "^0.22.0"
51
+ "verbaly": "^0.24.0"
52
52
  },
53
53
  "devDependencies": {
54
54
  "@types/react": "^19.2.17",
@@ -56,7 +56,7 @@
56
56
  "happy-dom": "^20.10.6",
57
57
  "react": "^19.2.7",
58
58
  "react-dom": "^19.2.7",
59
- "verbaly": "0.22.0"
59
+ "verbaly": "0.24.0"
60
60
  },
61
61
  "scripts": {
62
62
  "build": "tsdown",