@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 +34 -0
- package/src/g.tsx +51 -0
- package/src/gringow-component.ts +58 -0
- package/src/gringow-event.ts +23 -0
- package/src/gringow-store.ts +83 -0
- package/src/index.ts +8 -0
- package/tsconfig.json +10 -0
- package/tsup.config.ts +21 -0
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
package/tsconfig.json
ADDED
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
|
+
})
|