@graphcommerce/hygraph-dynamic-rows 6.2.0-canary.8
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/CHANGELOG.md +7 -0
- package/graphql/AllDynamicRows.graphql +11 -0
- package/graphql/Conditions.graphql +29 -0
- package/graphql/DynamicRow.graphql +19 -0
- package/graphql/DynamicRows.graphql +5 -0
- package/graphql/index.ts +4 -0
- package/lib/hygraphDynamicRows.ts +145 -0
- package/next-env.d.ts +4 -0
- package/package.json +31 -0
- package/plugins/hygraphDynamicRowsPageContent.ts +19 -0
- package/tsconfig.json +5 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# @graphcommerce/hygraph-dynamic-rows
|
|
2
|
+
|
|
3
|
+
## 6.2.0-canary.8
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#1912](https://github.com/graphcommerce-org/graphcommerce/pull/1912) [`a43d389e9`](https://github.com/graphcommerce-org/graphcommerce/commit/a43d389e956fe69b73238b12c98c781b7044e4bb) - Added dynamic rows feature and better performance for Hygraph ([@JoshuaS98](https://github.com/JoshuaS98))
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
fragment ConditionNumber on ConditionNumber {
|
|
2
|
+
__typename
|
|
3
|
+
property
|
|
4
|
+
operator
|
|
5
|
+
numberValue: value
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
fragment ConditionText on ConditionText {
|
|
9
|
+
__typename
|
|
10
|
+
property
|
|
11
|
+
stringValue: value
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
fragment ConditionOr on ConditionOr {
|
|
15
|
+
__typename
|
|
16
|
+
conditions {
|
|
17
|
+
...ConditionNumber
|
|
18
|
+
...ConditionText
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
fragment ConditionAnd on ConditionAnd {
|
|
23
|
+
__typename
|
|
24
|
+
conditions {
|
|
25
|
+
...ConditionNumber
|
|
26
|
+
...ConditionText
|
|
27
|
+
...ConditionOr
|
|
28
|
+
}
|
|
29
|
+
}
|
package/graphql/index.ts
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* - Boven de product description zetten? in rowrenderer zetten
|
|
3
|
+
* - Hoe gaan we dit optioneel maken?
|
|
4
|
+
* - Hoe gaan we dit upgradebaar maken? management sdk
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { HygraphPagesQuery } from '@graphcommerce/graphcms-ui'
|
|
8
|
+
import { ApolloClient, NormalizedCacheObject } from '@graphcommerce/graphql'
|
|
9
|
+
import {
|
|
10
|
+
AllDynamicRowsDocument,
|
|
11
|
+
ConditionTextFragment,
|
|
12
|
+
ConditionNumberFragment,
|
|
13
|
+
ConditionOrFragment,
|
|
14
|
+
ConditionAndFragment,
|
|
15
|
+
DynamicRowsDocument,
|
|
16
|
+
} from '../graphql'
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* This generally works the same way as lodash get, however, when encountering an array it will
|
|
20
|
+
* return all values.
|
|
21
|
+
*/
|
|
22
|
+
function getByPath(
|
|
23
|
+
value: unknown,
|
|
24
|
+
query: string | Array<string | number>,
|
|
25
|
+
): (undefined | string | number | bigint)[] {
|
|
26
|
+
const splitQuery = Array.isArray(query)
|
|
27
|
+
? query
|
|
28
|
+
: query
|
|
29
|
+
.replace(/(\[(\d)\])/g, '.$2')
|
|
30
|
+
.replace(/^\./, '')
|
|
31
|
+
.split('.')
|
|
32
|
+
|
|
33
|
+
if (!splitQuery.length || splitQuery[0] === undefined)
|
|
34
|
+
return [value as undefined | string | number | bigint]
|
|
35
|
+
|
|
36
|
+
const key = splitQuery[0]
|
|
37
|
+
|
|
38
|
+
if (Array.isArray(value)) {
|
|
39
|
+
return value.map((item) => getByPath(item, splitQuery)).flat(1000)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (
|
|
43
|
+
typeof value !== 'object' ||
|
|
44
|
+
value === null ||
|
|
45
|
+
!(key in value) ||
|
|
46
|
+
(value as Record<string | number, unknown>)[key] === undefined
|
|
47
|
+
) {
|
|
48
|
+
return [undefined]
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const res = getByPath((value as Record<string | number, unknown>)[key], splitQuery.slice(1))
|
|
52
|
+
return Array.isArray(res) ? res : [res]
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** A recursive match function that is able to match a condition against the requested conditions. */
|
|
56
|
+
function matchCondition(
|
|
57
|
+
condition:
|
|
58
|
+
| ConditionTextFragment
|
|
59
|
+
| ConditionNumberFragment
|
|
60
|
+
| ConditionOrFragment
|
|
61
|
+
| ConditionAndFragment,
|
|
62
|
+
obj: object,
|
|
63
|
+
) {
|
|
64
|
+
if (condition.__typename === 'ConditionOr')
|
|
65
|
+
return condition.conditions.some((c) => matchCondition(c, obj))
|
|
66
|
+
|
|
67
|
+
if (condition.__typename === 'ConditionAnd')
|
|
68
|
+
return condition.conditions.every((c) => matchCondition(c, obj))
|
|
69
|
+
|
|
70
|
+
if (condition.__typename === 'ConditionNumber') {
|
|
71
|
+
return getByPath(obj, condition.property).some((v) => {
|
|
72
|
+
const value = Number(v)
|
|
73
|
+
if (!value || Number.isNaN(value)) return false
|
|
74
|
+
|
|
75
|
+
if (condition.operator === 'EQUAL') return value === condition.numberValue
|
|
76
|
+
if (condition.operator === 'LTE') return value <= condition.numberValue
|
|
77
|
+
if (condition.operator === 'GTE') return value >= condition.numberValue
|
|
78
|
+
return false
|
|
79
|
+
})
|
|
80
|
+
}
|
|
81
|
+
if (condition.__typename === 'ConditionText') {
|
|
82
|
+
return getByPath(obj, condition.property).some((value) => value === condition.stringValue)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return false
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Fetch the page content for the given urls.
|
|
90
|
+
*
|
|
91
|
+
* - Uses an early bailout to check to reduce hygraph calls.
|
|
92
|
+
* - Implements an alias sytem to merge the content of multiple pages.
|
|
93
|
+
*/
|
|
94
|
+
export async function hygraphDynamicRows(
|
|
95
|
+
client: ApolloClient<NormalizedCacheObject>,
|
|
96
|
+
pageQuery: Promise<{ data: HygraphPagesQuery }>,
|
|
97
|
+
url: string,
|
|
98
|
+
cached: boolean,
|
|
99
|
+
additionalProperties?: Promise<object> | object,
|
|
100
|
+
): Promise<{ data: HygraphPagesQuery }> {
|
|
101
|
+
const alwaysCache = process.env.NODE_ENV !== 'development' ? 'cache-first' : undefined
|
|
102
|
+
const fetchPolicy = cached ? alwaysCache : undefined
|
|
103
|
+
|
|
104
|
+
const allRoutes = await client.query({ query: AllDynamicRowsDocument, fetchPolicy: alwaysCache })
|
|
105
|
+
|
|
106
|
+
// Get the required rowIds from the conditions
|
|
107
|
+
const properties = { ...(await additionalProperties), url }
|
|
108
|
+
|
|
109
|
+
const rowIds = allRoutes.data.dynamicRows
|
|
110
|
+
.filter((availableDynamicRow) =>
|
|
111
|
+
availableDynamicRow.conditions.some((condition) => matchCondition(condition, properties)),
|
|
112
|
+
)
|
|
113
|
+
.map((row) => row.id)
|
|
114
|
+
|
|
115
|
+
const dynamicRows =
|
|
116
|
+
rowIds.length !== 0
|
|
117
|
+
? client.query({ query: DynamicRowsDocument, variables: { rowIds }, fetchPolicy })
|
|
118
|
+
: undefined
|
|
119
|
+
|
|
120
|
+
const [pageResult, dynamicResult] = await Promise.all([pageQuery, dynamicRows])
|
|
121
|
+
|
|
122
|
+
// Create a copy of the content array.
|
|
123
|
+
const content = [...(pageResult.data.pages[0]?.content ?? [])]
|
|
124
|
+
|
|
125
|
+
dynamicResult?.data.dynamicRows.forEach((dynamicRow) => {
|
|
126
|
+
const { placement, target, row } = dynamicRow
|
|
127
|
+
if (!row) return
|
|
128
|
+
|
|
129
|
+
if (!target) {
|
|
130
|
+
if (placement === 'BEFORE') content.unshift(row)
|
|
131
|
+
else content.push(row)
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const targetIdx = content.findIndex((c) => c.id === target.id)
|
|
136
|
+
if (placement === 'BEFORE') content.splice(targetIdx, 0, row)
|
|
137
|
+
if (placement === 'AFTER') content.splice(targetIdx + 1, 0, row)
|
|
138
|
+
if (placement === 'REPLACE') content.splice(targetIdx, 1, row)
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
if (!content.length) return pageResult
|
|
142
|
+
|
|
143
|
+
// Return the merged page result.
|
|
144
|
+
return { data: { ...pageResult.data, pages: [{ ...pageResult.data.pages[0], content }] } }
|
|
145
|
+
}
|
package/next-env.d.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@graphcommerce/hygraph-dynamic-rows",
|
|
3
|
+
"homepage": "https://www.graphcommerce.org/",
|
|
4
|
+
"repository": "github:graphcommerce-org/graphcommerce",
|
|
5
|
+
"version": "6.2.0-canary.8",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"prettier": "@graphcommerce/prettier-config-pwa",
|
|
8
|
+
"eslintConfig": {
|
|
9
|
+
"extends": "@graphcommerce/eslint-config-pwa",
|
|
10
|
+
"parserOptions": {
|
|
11
|
+
"project": "./tsconfig.json"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@graphcommerce/eslint-config-pwa": "6.2.0-canary.8",
|
|
16
|
+
"@graphcommerce/prettier-config-pwa": "6.2.0-canary.8",
|
|
17
|
+
"@graphcommerce/typescript-config-pwa": "6.2.0-canary.8"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@graphcommerce/graphql": "6.2.0-canary.8",
|
|
21
|
+
"@graphcommerce/image": "6.2.0-canary.8",
|
|
22
|
+
"@graphcommerce/next-ui": "6.2.0-canary.8",
|
|
23
|
+
"@graphcommerce/graphcms-ui": "6.2.0-canary.8"
|
|
24
|
+
},
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"@mui/material": "^5.10.16",
|
|
27
|
+
"next": "^13.2.0",
|
|
28
|
+
"react": "^18.2.0",
|
|
29
|
+
"react-dom": "^18.2.0"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { hygraphPageContent } from '@graphcommerce/graphcms-ui'
|
|
2
|
+
import type { MethodPlugin } from '@graphcommerce/next-config'
|
|
3
|
+
import { hygraphDynamicRows } from '../lib/hygraphDynamicRows'
|
|
4
|
+
|
|
5
|
+
export const func = 'hygraphPageContent'
|
|
6
|
+
export const exported = '@graphcommerce/graphcms-ui'
|
|
7
|
+
|
|
8
|
+
const hygraphDynamicRowsPageContent: MethodPlugin<typeof hygraphPageContent> = (
|
|
9
|
+
prev,
|
|
10
|
+
client,
|
|
11
|
+
url,
|
|
12
|
+
additionalProperties,
|
|
13
|
+
cached = false,
|
|
14
|
+
) => {
|
|
15
|
+
const pageQuery = prev(client, url, additionalProperties, cached)
|
|
16
|
+
return hygraphDynamicRows(client, pageQuery, url, cached, additionalProperties)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const plugin = hygraphDynamicRowsPageContent
|