@gringow/gringow-react 0.0.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/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@gringow/gringow-react",
3
+ "version": "0.0.1",
4
+ "description": "React bindings for Gringow AI-powered translation tool",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.mjs",
11
+ "require": "./dist/index.js"
12
+ }
13
+ },
14
+ "author": "Renato Gaspar <rntgspr@gmail.com>",
15
+ "license": "MIT",
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "devDependencies": {
20
+ "tsup": "8.5.0",
21
+ "typescript": "5.8.3"
22
+ },
23
+ "dependencies": {
24
+ "@gringow/gringow": "0.0.9",
25
+ "react": "19.1.0",
26
+ "react-dom": "19.1.0"
27
+ },
28
+ "scripts": {
29
+ "build": "rm -rf ./dist/* ./tsconfig.tsbuildinfo && tsup",
30
+ "watch": "pnpm run build && tsup --watch --config ../tsup.config.ts",
31
+ "__postinstall": "pnpm run build",
32
+ "test": "echo \"Warning: no test specified\" && exit 0"
33
+ }
34
+ }
package/src/g.tsx ADDED
@@ -0,0 +1,51 @@
1
+ import { createCacheId, flatTemplateString } from '@gringow/gringow/browser'
2
+ import React from 'react'
3
+
4
+ import { GringowComponent } from './gringow-component'
5
+ import { GringowStore } from './gringow-store'
6
+
7
+ import type { GringowCache } from '@gringow/gringow'
8
+
9
+ const ELEMENT_NAME = 'g-gringow'
10
+
11
+ export function g(strings: TemplateStringsArray, ...values: Array<unknown>): React.ReactNode {
12
+ const [elementShow, setElementShow] = React.useState(false)
13
+ const [cache, setCache] = React.useState<GringowCache | undefined>()
14
+ const ref = React.useRef<GringowComponent>(null)
15
+
16
+ const flatten = flatTemplateString(strings, values)
17
+ const cacheId = createCacheId(flatten)
18
+
19
+ React.useEffect(() => {
20
+ React.startTransition(async () => {
21
+ await GringowStore.fetchCache()
22
+ setCache(GringowStore.cache)
23
+ })
24
+ }, [])
25
+
26
+ React.useEffect(() => {
27
+ if (!window.customElements.get(ELEMENT_NAME)) {
28
+ window.customElements.define(ELEMENT_NAME, GringowComponent)
29
+ }
30
+ })
31
+
32
+ React.useEffect(() => {
33
+ if (ref.current) {
34
+ ref.current.lang = GringowStore.defaultLanguage
35
+ ref.current.cacheId = cacheId
36
+ ref.current.cache = cache[cacheId]
37
+ ref.current.flatten = flatten
38
+ if (!elementShow) setElementShow(true)
39
+ }
40
+ })
41
+
42
+ return (
43
+ <React.Suspense fallback={flatten}>
44
+ {Boolean(cache) && (
45
+ <g-gringow ref={ref} id={cacheId}>
46
+ <g-outlet slot={cacheId}>{flatten}</g-outlet>
47
+ </g-gringow>
48
+ )}
49
+ </React.Suspense>
50
+ )
51
+ }
@@ -0,0 +1,58 @@
1
+ import { LanguageChangeEvent } from './gringow-event'
2
+ import { GringowStore } from './gringow-store'
3
+
4
+ import type { GringowCacheItem } from '@gringow/gringow'
5
+
6
+ type GringowElement = Element & { cache: GringowCacheItem; cacheId: string }
7
+
8
+ class GringowComponent extends HTMLElement {
9
+ public shadowRoot: ShadowRoot
10
+ private cacheId: string
11
+ private cache: GringowCacheItem
12
+ private flatten: string
13
+ private message: string
14
+ private gringowElement: GringowElement
15
+
16
+ private _lang: string = GringowStore.defaultLanguage
17
+
18
+ get lang() {
19
+ return this._lang
20
+ }
21
+
22
+ set lang(value: string) {
23
+ this._lang = value ?? GringowStore.defaultLanguage
24
+ this.updateMessage(this._lang)
25
+ }
26
+
27
+ constructor() {
28
+ super()
29
+ this.initializeShadowRoot()
30
+
31
+ window.addEventListener(LanguageChangeEvent.EVENT_NAME, this.languageChangedListener.bind(this))
32
+ }
33
+
34
+ private languageChangedListener(event: LanguageChangeEvent) {
35
+ if (this.lang !== event.detail.lang) {
36
+ this.lang = event.detail.lang
37
+ }
38
+
39
+ this.updateMessage(event.detail.lang)
40
+ }
41
+
42
+ private updateMessage(lang: string) {
43
+ this.gringowElement = this.shadowRoot.host as GringowElement
44
+ this.cacheId = this.gringowElement.cacheId
45
+ this.cache = this.gringowElement.cache
46
+
47
+ this.message = this.cache?.translation?.[lang] ?? this.cache?.original ?? this.flatten
48
+
49
+ this.shadowRoot.innerHTML = `<slot>${this.message}</slot>`
50
+ }
51
+
52
+ private initializeShadowRoot() {
53
+ this.shadowRoot = this.attachShadow({ mode: 'open' })
54
+ this.updateMessage(this.lang)
55
+ }
56
+ }
57
+
58
+ export { GringowComponent }
@@ -0,0 +1,23 @@
1
+ export interface LanguageChangeEventDetail {
2
+ lang: string
3
+ timestamp?: number
4
+ }
5
+
6
+ export class LanguageChangeEvent extends CustomEvent<LanguageChangeEventDetail> {
7
+ static readonly EVENT_NAME = 'language-change'
8
+
9
+ constructor(detail: LanguageChangeEventDetail) {
10
+ super(LanguageChangeEvent.EVENT_NAME, {
11
+ detail: {
12
+ lang: detail.lang ?? 'en-US',
13
+ timestamp: detail.timestamp || Date.now(),
14
+ },
15
+ bubbles: true,
16
+ cancelable: true,
17
+ })
18
+ }
19
+
20
+ static create(lang: string): LanguageChangeEvent {
21
+ return new LanguageChangeEvent({ lang })
22
+ }
23
+ }
@@ -0,0 +1,83 @@
1
+ import { GRINGOW_IO_URL } from '@gringow/gringow/browser'
2
+
3
+ import type { GringowCache } from '@gringow/gringow'
4
+
5
+ class GringowStoreSingleton {
6
+ private static instance: GringowStoreSingleton | null = null
7
+ #cache: GringowCache | null = null
8
+ #cacheUrl: string
9
+ #gringowIoToken: string
10
+ #defaultLanguage: string
11
+
12
+ private constructor() {}
13
+
14
+ public static getInstance(): GringowStoreSingleton {
15
+ if (!GringowStoreSingleton.instance) {
16
+ GringowStoreSingleton.instance = new GringowStoreSingleton()
17
+ }
18
+ return GringowStoreSingleton.instance
19
+ }
20
+
21
+ public set defaultLanguage(value: string) {
22
+ this.#defaultLanguage = value ?? 'en-US'
23
+ }
24
+
25
+ public get defaultLanguage() {
26
+ return this.#defaultLanguage
27
+ }
28
+
29
+ public set cacheUrl(value: string) {
30
+ this.#cacheUrl = value
31
+ this.fetchCache()
32
+ }
33
+
34
+ public get cacheUrl() {
35
+ if (!this.#cacheUrl) {
36
+ throw new Error('Cache URL is not set')
37
+ }
38
+
39
+ return this.#cacheUrl
40
+ }
41
+
42
+ public set gringowIoToken(value: string) {
43
+ this.#gringowIoToken = value
44
+ this.#cacheUrl = `${GRINGOW_IO_URL}?token=${this.#gringowIoToken}`
45
+ this.fetchCache()
46
+ }
47
+
48
+ public get gringowIoToken() {
49
+ return this.#gringowIoToken
50
+ }
51
+
52
+ public get cache() {
53
+ return this.#cache
54
+ }
55
+
56
+ public set cache(value: GringowCache) {
57
+ this.#cache = value
58
+ }
59
+
60
+ public async fetchCache(force: boolean = false): Promise<GringowCache> {
61
+ if (!this.#cacheUrl) {
62
+ throw new Error('Cache URL is not set')
63
+ }
64
+
65
+ if (!this.cache || force) {
66
+ try {
67
+ const response = await fetch(this.#cacheUrl)
68
+ if (!response.ok) {
69
+ throw new Error('Failed to fetch gringow cache')
70
+ }
71
+
72
+ this.cache = await response.json()
73
+ } catch (error) {
74
+ throw new Error('Failed to fetch gringow cache', { cause: error })
75
+ }
76
+ }
77
+
78
+ return this.cache
79
+ }
80
+ }
81
+
82
+ const GringowStore = GringowStoreSingleton.getInstance()
83
+ export { GringowStore }
package/src/index.ts ADDED
@@ -0,0 +1,8 @@
1
+ const gringowReact = {}
2
+
3
+ export default gringowReact
4
+
5
+ export * from './g'
6
+ export * from './gringow-component'
7
+ export * from './gringow-event'
8
+ export * from './gringow-store'
package/tsconfig.json ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "compilerOptions": {
3
+ "module": "ESNext",
4
+ "moduleResolution": "bundler",
5
+ "target": "ESNext",
6
+ "lib": ["ESNext", "DOM"],
7
+ "jsx": "react-jsx",
8
+ "outDir": "./dist"
9
+ }
10
+ }
package/tsup.config.ts ADDED
@@ -0,0 +1,21 @@
1
+ import { defineConfig } from 'tsup'
2
+
3
+ export default defineConfig({
4
+ entry: ['src/**/*.ts'],
5
+ // format: ["cjs", "esm"],
6
+ format: ['esm'],
7
+ dts: true,
8
+ splitting: false,
9
+ sourcemap: false,
10
+ clean: true,
11
+ treeshake: 'safest',
12
+ minify: false,
13
+ outDir: 'dist',
14
+ target: 'esnext',
15
+ outExtension({ format }) {
16
+ return {
17
+ js: format === 'cjs' ? '.js' : '.mjs',
18
+ }
19
+ },
20
+ onSuccess: 'tsc-alias --verbose',
21
+ })