@nosto/nosto-react 0.4.1 → 0.5.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 +5 -2
- package/dist/index.d.ts +519 -0
- package/dist/index.es.js +1117 -0
- package/dist/index.umd.js +25 -0
- package/package.json +20 -13
- package/src/components/Nosto404.tsx +47 -0
- package/src/components/{Category/index.client.tsx → NostoCategory.tsx} +18 -39
- package/src/components/NostoCheckout.tsx +47 -0
- package/src/components/{Home/index.client.tsx → NostoHome.tsx} +17 -36
- package/src/components/NostoOrder.tsx +55 -0
- package/src/components/NostoOther.tsx +46 -0
- package/src/components/{Placement/index.client.tsx → NostoPlacement.tsx} +5 -8
- package/src/components/{Product/index.client.tsx → NostoProduct.tsx} +37 -81
- package/src/components/NostoProvider.tsx +220 -0
- package/src/components/{Search/index.client.tsx → NostoSearch.tsx} +18 -39
- package/src/components/{Session/index.client.tsx → NostoSession.tsx} +14 -17
- package/src/components/context.ts +55 -0
- package/src/components/index.ts +14 -0
- package/src/index.ts +3 -0
- package/src/types.ts +112 -97
- package/src/utils/compare.ts +9 -9
- package/src/utils/hooks.ts +28 -8
- package/src/utils/object.ts +10 -11
- package/src/utils/snakeize.ts +11 -11
- package/dist/index.es.client.js +0 -660
- package/dist/index.umd.client.js +0 -9
- package/src/components/Checkout/index.client.tsx +0 -66
- package/src/components/Fohofo/index.client.tsx +0 -66
- package/src/components/Order/index.client.tsx +0 -72
- package/src/components/Other/index.client.tsx +0 -64
- package/src/components/Provider/context.client.ts +0 -45
- package/src/components/Provider/index.client.tsx +0 -222
- package/src/index.client.ts +0 -33
package/src/types.ts
CHANGED
|
@@ -1,158 +1,173 @@
|
|
|
1
1
|
declare global {
|
|
2
2
|
interface Window {
|
|
3
3
|
nosto?: {
|
|
4
|
-
reload(settings: unknown): void
|
|
5
|
-
}
|
|
4
|
+
reload(settings: unknown): void
|
|
5
|
+
}
|
|
6
6
|
nostojs: {
|
|
7
|
-
(callback: (api: NostoClient) => void): void
|
|
8
|
-
q?: unknown[]
|
|
9
|
-
}
|
|
7
|
+
(callback: (api: NostoClient) => void): void
|
|
8
|
+
q?: unknown[]
|
|
9
|
+
}
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* @group Types
|
|
15
15
|
*/
|
|
16
|
-
export interface
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
viewProduct(product: string): NostoClient;
|
|
27
|
-
viewFrontPage(): NostoClient;
|
|
28
|
-
viewNotFound(): NostoClient;
|
|
29
|
-
viewOther(): NostoClient;
|
|
30
|
-
viewSearch(query: string): NostoClient;
|
|
31
|
-
viewCart(): NostoClient;
|
|
16
|
+
export interface Affinity {
|
|
17
|
+
name: string
|
|
18
|
+
score: number
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @group Types
|
|
23
|
+
*/
|
|
24
|
+
export interface SessionAction {
|
|
25
|
+
setPlacements(placements: string[]): SessionAction
|
|
32
26
|
load(): Promise<{
|
|
33
|
-
affinities: Record<
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
27
|
+
affinities: Record<string, Affinity[]>
|
|
28
|
+
geo_location?: string[]
|
|
29
|
+
page_views: number
|
|
30
|
+
recommendations: Recommendation[]
|
|
31
|
+
}>
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @group Types
|
|
36
|
+
*/
|
|
37
|
+
export interface NostoClientSession {
|
|
38
|
+
setCart(cart?: Cart): NostoClientSession
|
|
39
|
+
setCustomer(customer?: Customer): NostoClientSession
|
|
40
|
+
setResponseMode(mode: string): NostoClientSession
|
|
41
|
+
setVariation(variation?: string): NostoClientSession
|
|
42
|
+
addOrder(order: { purchase: Purchase }): SessionAction
|
|
43
|
+
viewCategory(category: string): SessionAction
|
|
44
|
+
viewProduct(product: string): SessionAction
|
|
45
|
+
viewFrontPage(): SessionAction
|
|
46
|
+
viewNotFound(): SessionAction
|
|
47
|
+
viewOther(): SessionAction
|
|
48
|
+
viewSearch(query: string): SessionAction
|
|
49
|
+
viewCart(): SessionAction
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @group Types
|
|
54
|
+
*/
|
|
55
|
+
export interface NostoClient {
|
|
56
|
+
setAutoLoad(autoload: boolean): NostoClient
|
|
57
|
+
defaultSession(): NostoClientSession
|
|
44
58
|
placements: {
|
|
45
|
-
getPlacements(): string[]
|
|
46
|
-
|
|
59
|
+
getPlacements(): string[]
|
|
60
|
+
injectCampaigns(recommendations: Record<string, Recommendation>): void
|
|
61
|
+
}
|
|
47
62
|
}
|
|
48
63
|
|
|
49
64
|
/**
|
|
50
65
|
* @group Types
|
|
51
66
|
*/
|
|
52
67
|
export interface Recommendation {
|
|
53
|
-
result_id: string
|
|
54
|
-
products: Product[]
|
|
55
|
-
result_type: string
|
|
56
|
-
title: string
|
|
57
|
-
div_id: string
|
|
58
|
-
source_product_ids: string[]
|
|
59
|
-
params: unknown
|
|
68
|
+
result_id: string
|
|
69
|
+
products: Product[]
|
|
70
|
+
result_type: string
|
|
71
|
+
title: string
|
|
72
|
+
div_id: string
|
|
73
|
+
source_product_ids: string[]
|
|
74
|
+
params: unknown
|
|
60
75
|
}
|
|
61
76
|
|
|
62
77
|
/**
|
|
63
78
|
* @group Types
|
|
64
79
|
*/
|
|
65
80
|
export interface Item {
|
|
66
|
-
name: string
|
|
67
|
-
price_currency_code: string
|
|
68
|
-
product_id: string
|
|
69
|
-
quantity: number
|
|
70
|
-
sku_id: string
|
|
71
|
-
unit_price: number
|
|
81
|
+
name: string
|
|
82
|
+
price_currency_code: string
|
|
83
|
+
product_id: string
|
|
84
|
+
quantity: number
|
|
85
|
+
sku_id: string
|
|
86
|
+
unit_price: number
|
|
72
87
|
}
|
|
73
88
|
|
|
74
89
|
/**
|
|
75
90
|
* @group Types
|
|
76
91
|
*/
|
|
77
92
|
export interface Cart {
|
|
78
|
-
items: Item[]
|
|
93
|
+
items: Item[]
|
|
79
94
|
}
|
|
80
95
|
|
|
81
96
|
/**
|
|
82
97
|
* @group Types
|
|
83
98
|
*/
|
|
84
99
|
export interface Customer {
|
|
85
|
-
customer_reference: string
|
|
86
|
-
email: string
|
|
87
|
-
first_name: string
|
|
88
|
-
last_name: string
|
|
89
|
-
newsletter: boolean
|
|
100
|
+
customer_reference: string
|
|
101
|
+
email: string
|
|
102
|
+
first_name: string
|
|
103
|
+
last_name: string
|
|
104
|
+
newsletter: boolean
|
|
90
105
|
}
|
|
91
106
|
|
|
92
107
|
/**
|
|
93
108
|
* @group Types
|
|
94
109
|
*/
|
|
95
110
|
export interface Buyer {
|
|
96
|
-
first_name: string
|
|
97
|
-
last_name: string
|
|
98
|
-
email: string
|
|
99
|
-
type: string
|
|
100
|
-
newsletter: boolean
|
|
111
|
+
first_name: string
|
|
112
|
+
last_name: string
|
|
113
|
+
email: string
|
|
114
|
+
type: string
|
|
115
|
+
newsletter: boolean
|
|
101
116
|
}
|
|
102
117
|
|
|
103
118
|
/**
|
|
104
119
|
* @group Types
|
|
105
120
|
*/
|
|
106
121
|
export interface Purchase {
|
|
107
|
-
number: string
|
|
108
|
-
info: Buyer
|
|
109
|
-
items: Item[]
|
|
122
|
+
number: string
|
|
123
|
+
info: Buyer
|
|
124
|
+
items: Item[]
|
|
110
125
|
}
|
|
111
126
|
|
|
112
127
|
/**
|
|
113
128
|
* @group Types
|
|
114
129
|
*/
|
|
115
130
|
export interface SKU {
|
|
116
|
-
id: string
|
|
117
|
-
name: string
|
|
118
|
-
price: number
|
|
119
|
-
listPrice?: number
|
|
120
|
-
url: URL
|
|
121
|
-
imageUrl: URL
|
|
122
|
-
gtin?: string
|
|
123
|
-
availability: "InStock" | "OutOfStock"
|
|
124
|
-
customFields?: { [key: string]: string }
|
|
131
|
+
id: string
|
|
132
|
+
name: string
|
|
133
|
+
price: number
|
|
134
|
+
listPrice?: number
|
|
135
|
+
url: URL
|
|
136
|
+
imageUrl: URL
|
|
137
|
+
gtin?: string
|
|
138
|
+
availability: "InStock" | "OutOfStock"
|
|
139
|
+
customFields?: { [key: string]: string }
|
|
125
140
|
}
|
|
126
141
|
|
|
127
142
|
/**
|
|
128
143
|
* @group Types
|
|
129
144
|
*/
|
|
130
145
|
export interface Product {
|
|
131
|
-
alternateImageUrls?: URL[]
|
|
132
|
-
availability: "InStock" | "OutOfStock"
|
|
133
|
-
brand?: string
|
|
134
|
-
category: string[]
|
|
135
|
-
categoryIds?: string[]
|
|
136
|
-
description: string
|
|
137
|
-
googleCategory?: string
|
|
138
|
-
condition?: string
|
|
139
|
-
gender?: string
|
|
140
|
-
ageGroup?: string
|
|
141
|
-
gtin?: string
|
|
142
|
-
imageUrl: URL
|
|
143
|
-
listPrice?: number
|
|
144
|
-
name: string
|
|
145
|
-
price: number
|
|
146
|
-
ratingValue?: number
|
|
147
|
-
reviewCount?: number
|
|
148
|
-
priceCurrencyCode: string
|
|
149
|
-
productId: string
|
|
150
|
-
tags1?: string[]
|
|
151
|
-
tags2?: string[]
|
|
152
|
-
tags3?: string[]
|
|
153
|
-
thumbUrl?: URL
|
|
154
|
-
url: URL
|
|
155
|
-
customFields?: { [key: string]: string }
|
|
156
|
-
variationId?: string
|
|
157
|
-
skus?: SKU[]
|
|
146
|
+
alternateImageUrls?: URL[]
|
|
147
|
+
availability: "InStock" | "OutOfStock"
|
|
148
|
+
brand?: string
|
|
149
|
+
category: string[]
|
|
150
|
+
categoryIds?: string[]
|
|
151
|
+
description: string
|
|
152
|
+
googleCategory?: string
|
|
153
|
+
condition?: string
|
|
154
|
+
gender?: string
|
|
155
|
+
ageGroup?: string
|
|
156
|
+
gtin?: string
|
|
157
|
+
imageUrl: URL
|
|
158
|
+
listPrice?: number
|
|
159
|
+
name: string
|
|
160
|
+
price: number
|
|
161
|
+
ratingValue?: number
|
|
162
|
+
reviewCount?: number
|
|
163
|
+
priceCurrencyCode: string
|
|
164
|
+
productId: string
|
|
165
|
+
tags1?: string[]
|
|
166
|
+
tags2?: string[]
|
|
167
|
+
tags3?: string[]
|
|
168
|
+
thumbUrl?: URL
|
|
169
|
+
url: URL
|
|
170
|
+
customFields?: { [key: string]: string }
|
|
171
|
+
variationId?: string
|
|
172
|
+
skus?: SKU[]
|
|
158
173
|
}
|
package/src/utils/compare.ts
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
import { isPlainObject } from "./object"
|
|
1
|
+
import { isPlainObject } from "./object"
|
|
2
2
|
|
|
3
3
|
export function deepCompare(a: unknown, b: unknown): boolean {
|
|
4
4
|
if (a === b) {
|
|
5
|
-
return true
|
|
5
|
+
return true
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
if (a instanceof Date && b instanceof Date) {
|
|
9
|
-
return a.getTime() === b.getTime()
|
|
9
|
+
return a.getTime() === b.getTime()
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
if (a instanceof Array && b instanceof Array) {
|
|
13
13
|
if (a.length !== b.length) {
|
|
14
|
-
return false
|
|
14
|
+
return false
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
return a.every((v, i) => deepCompare(v, b[i]))
|
|
17
|
+
return a.every((v, i) => deepCompare(v, b[i]))
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
if (isPlainObject(a) && isPlainObject(b)) {
|
|
21
|
-
const entriesA = Object.entries(a)
|
|
21
|
+
const entriesA = Object.entries(a)
|
|
22
22
|
|
|
23
23
|
if (entriesA.length !== Object.keys(b).length) {
|
|
24
|
-
return false
|
|
24
|
+
return false
|
|
25
25
|
}
|
|
26
|
-
return entriesA.every(([k, v]) => deepCompare(v, b[k]))
|
|
26
|
+
return entriesA.every(([k, v]) => deepCompare(v, b[k]))
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
return false
|
|
29
|
+
return false
|
|
30
30
|
}
|
package/src/utils/hooks.ts
CHANGED
|
@@ -1,21 +1,41 @@
|
|
|
1
|
-
import { useEffect, useRef, useMemo } from "react"
|
|
2
|
-
import {
|
|
1
|
+
import { useEffect, useRef, useMemo } from "react"
|
|
2
|
+
import { useNostoContext } from "../components/context"
|
|
3
|
+
import { deepCompare } from "./compare"
|
|
4
|
+
import { NostoClient } from "../types"
|
|
3
5
|
|
|
4
6
|
export function useDeepCompareEffect(
|
|
5
7
|
callback: Parameters<typeof useEffect>[0],
|
|
6
8
|
dependencies: Parameters<typeof useEffect>[1]
|
|
7
9
|
): ReturnType<typeof useEffect> {
|
|
8
|
-
return useEffect(callback, useDeepCompareMemoize(dependencies))
|
|
10
|
+
return useEffect(callback, useDeepCompareMemoize(dependencies))
|
|
9
11
|
}
|
|
10
12
|
|
|
11
13
|
function useDeepCompareMemoize<T>(value: T) {
|
|
12
|
-
const ref = useRef<T>(value)
|
|
13
|
-
const signalRef = useRef<number>(0)
|
|
14
|
+
const ref = useRef<T>(value)
|
|
15
|
+
const signalRef = useRef<number>(0)
|
|
14
16
|
|
|
15
17
|
if (!deepCompare(value, ref.current)) {
|
|
16
|
-
ref.current = value
|
|
17
|
-
signalRef.current += 1
|
|
18
|
+
ref.current = value
|
|
19
|
+
signalRef.current += 1
|
|
18
20
|
}
|
|
19
21
|
|
|
20
|
-
return useMemo(() => ref.current, [signalRef.current])
|
|
22
|
+
return useMemo(() => ref.current, [signalRef.current])
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function useNostoApi(
|
|
26
|
+
cb: (api: NostoClient) => void,
|
|
27
|
+
deps?: React.DependencyList,
|
|
28
|
+
flags?: { deep?: boolean }
|
|
29
|
+
): void {
|
|
30
|
+
const { clientScriptLoaded, currentVariation, responseMode } = useNostoContext()
|
|
31
|
+
const useEffectFn = flags?.deep ? useDeepCompareEffect : useEffect
|
|
32
|
+
|
|
33
|
+
useEffectFn(() => {
|
|
34
|
+
if (clientScriptLoaded) {
|
|
35
|
+
window.nostojs(api => {
|
|
36
|
+
api.defaultSession().setVariation(currentVariation).setResponseMode(responseMode)
|
|
37
|
+
cb(api)
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
}, [clientScriptLoaded, currentVariation, responseMode, ...(deps ?? [])])
|
|
21
41
|
}
|
package/src/utils/object.ts
CHANGED
|
@@ -1,20 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
value: unknown
|
|
3
|
-
): value is T {
|
|
4
|
-
const isObject = (v: unknown): v is object => String(v) === "[object Object]";
|
|
1
|
+
const isObject = (v: unknown): v is object => String(v) === "[object Object]"
|
|
5
2
|
|
|
6
|
-
|
|
3
|
+
export function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
4
|
+
if (!isObject(value)) return false
|
|
7
5
|
|
|
8
|
-
const constructor = value.constructor
|
|
9
|
-
if (constructor === undefined) return true
|
|
6
|
+
const constructor = value.constructor
|
|
7
|
+
if (constructor === undefined) return true
|
|
10
8
|
|
|
11
|
-
const prototype = constructor.prototype
|
|
12
|
-
if (!isObject(prototype)) return false
|
|
9
|
+
const prototype = constructor.prototype
|
|
10
|
+
if (!isObject(prototype)) return false
|
|
13
11
|
|
|
14
12
|
// Checks if it is not a class
|
|
13
|
+
// eslint-disable-next-line no-prototype-builtins
|
|
15
14
|
if (!prototype.hasOwnProperty("isPrototypeOf")) {
|
|
16
|
-
return false
|
|
15
|
+
return false
|
|
17
16
|
}
|
|
18
17
|
|
|
19
|
-
return true
|
|
18
|
+
return true
|
|
20
19
|
}
|
package/src/utils/snakeize.ts
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
export function snakeize<T>(obj: T): T {
|
|
2
2
|
if (!obj || typeof obj !== "object") {
|
|
3
|
-
return obj
|
|
3
|
+
return obj
|
|
4
4
|
}
|
|
5
5
|
if (isDate(obj) || isRegex(obj)) {
|
|
6
|
-
return obj
|
|
6
|
+
return obj
|
|
7
7
|
}
|
|
8
8
|
if (Array.isArray(obj)) {
|
|
9
|
-
return obj.map(snakeize) as T
|
|
9
|
+
return obj.map(snakeize) as T
|
|
10
10
|
}
|
|
11
11
|
return Object.keys(obj).reduce((acc, key) => {
|
|
12
|
-
|
|
12
|
+
const camel =
|
|
13
13
|
key[0].toLowerCase() +
|
|
14
14
|
key.slice(1).replace(/([A-Z]+)/g, (_, x) => {
|
|
15
|
-
return "_" + x.toLowerCase()
|
|
16
|
-
})
|
|
17
|
-
acc[camel as keyof typeof acc] = snakeize(obj[key as keyof typeof acc])
|
|
18
|
-
return acc
|
|
19
|
-
}, {} as T
|
|
15
|
+
return "_" + x.toLowerCase()
|
|
16
|
+
})
|
|
17
|
+
acc[camel as keyof typeof acc] = snakeize(obj[key as keyof typeof acc])
|
|
18
|
+
return acc
|
|
19
|
+
}, {}) as T
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
function isDate(obj: unknown) {
|
|
23
|
-
return Object.prototype.toString.call(obj) === "[object Date]"
|
|
23
|
+
return Object.prototype.toString.call(obj) === "[object Date]"
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
function isRegex(obj: unknown) {
|
|
27
|
-
return Object.prototype.toString.call(obj) === "[object RegExp]"
|
|
27
|
+
return Object.prototype.toString.call(obj) === "[object RegExp]"
|
|
28
28
|
}
|