@graphcommerce/hygraph-dynamic-rows-ui 8.1.0-canary.9 → 9.0.0-canary.101
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 +201 -2
- package/components/PropertyPicker.tsx +51 -75
- package/components/Setup.tsx +45 -23
- package/components/setup.module.css +67 -12
- package/lib/getFieldPaths.ts +66 -0
- package/package.json +21 -21
- package/pages/property-picker.tsx +3 -4
- package/pages/setup.tsx +2 -2
- package/lib/createOptionsFromInterfaceObject.ts +0 -42
- package/lib/createRecursiveIntrospectionQuery.ts +0 -13
- package/lib/fetchGraphQLInterface.ts +0 -14
- package/lib/index.ts +0 -4
- package/lib/objectifyGraphQLInterface.ts +0 -62
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,205 @@
|
|
|
1
1
|
# @graphcommerce/hygraph-dynamic-rows-ui
|
|
2
2
|
|
|
3
|
+
## 9.0.0-canary.101
|
|
4
|
+
|
|
5
|
+
## 9.0.0-canary.100
|
|
6
|
+
|
|
7
|
+
## 9.0.0-canary.99
|
|
8
|
+
|
|
9
|
+
## 9.0.0-canary.98
|
|
10
|
+
|
|
11
|
+
## 9.0.0-canary.97
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- [#2410](https://github.com/graphcommerce-org/graphcommerce/pull/2410) [`2a425b3`](https://github.com/graphcommerce-org/graphcommerce/commit/2a425b323ddaf0918c549e93b598888db7328d66) - Allow attributes with deprecationReasons (e.g. custom attributes) as PropertyPicker value ([@carlocarels90](https://github.com/carlocarels90))
|
|
16
|
+
|
|
17
|
+
## 9.0.0-canary.96
|
|
18
|
+
|
|
19
|
+
## 9.0.0-canary.95
|
|
20
|
+
|
|
21
|
+
## 9.0.0-canary.94
|
|
22
|
+
|
|
23
|
+
## 9.0.0-canary.93
|
|
24
|
+
|
|
25
|
+
## 9.0.0-canary.92
|
|
26
|
+
|
|
27
|
+
## 9.0.0-canary.91
|
|
28
|
+
|
|
29
|
+
## 9.0.0-canary.90
|
|
30
|
+
|
|
31
|
+
## 9.0.0-canary.89
|
|
32
|
+
|
|
33
|
+
## 9.0.0-canary.88
|
|
34
|
+
|
|
35
|
+
## 9.0.0-canary.87
|
|
36
|
+
|
|
37
|
+
## 9.0.0-canary.86
|
|
38
|
+
|
|
39
|
+
## 9.0.0-canary.85
|
|
40
|
+
|
|
41
|
+
## 9.0.0-canary.84
|
|
42
|
+
|
|
43
|
+
## 9.0.0-canary.83
|
|
44
|
+
|
|
45
|
+
## 9.0.0-canary.82
|
|
46
|
+
|
|
47
|
+
## 9.0.0-canary.81
|
|
48
|
+
|
|
49
|
+
## 9.0.0-canary.80
|
|
50
|
+
|
|
51
|
+
## 9.0.0-canary.79
|
|
52
|
+
|
|
53
|
+
## 9.0.0-canary.78
|
|
54
|
+
|
|
55
|
+
## 9.0.0-canary.77
|
|
56
|
+
|
|
57
|
+
## 9.0.0-canary.76
|
|
58
|
+
|
|
59
|
+
## 9.0.0-canary.75
|
|
60
|
+
|
|
61
|
+
## 9.0.0-canary.74
|
|
62
|
+
|
|
63
|
+
## 9.0.0-canary.73
|
|
64
|
+
|
|
65
|
+
## 9.0.0-canary.72
|
|
66
|
+
|
|
67
|
+
## 9.0.0-canary.71
|
|
68
|
+
|
|
69
|
+
## 9.0.0-canary.70
|
|
70
|
+
|
|
71
|
+
### Patch Changes
|
|
72
|
+
|
|
73
|
+
- [#2347](https://github.com/graphcommerce-org/graphcommerce/pull/2347) [`7fa50a2`](https://github.com/graphcommerce-org/graphcommerce/commit/7fa50a2f21ee9edbc67d06d7694316f101f9415f) - Resolve issue where the dynamic rows UI wouldn’t load any definitions ([@paales](https://github.com/paales))
|
|
74
|
+
|
|
75
|
+
## 9.0.0-canary.69
|
|
76
|
+
|
|
77
|
+
## 9.0.0-canary.68
|
|
78
|
+
|
|
79
|
+
## 9.0.0-canary.67
|
|
80
|
+
|
|
81
|
+
## 9.0.0-canary.66
|
|
82
|
+
|
|
83
|
+
## 9.0.0-canary.65
|
|
84
|
+
|
|
85
|
+
## 9.0.0-canary.64
|
|
86
|
+
|
|
87
|
+
## 9.0.0-canary.63
|
|
88
|
+
|
|
89
|
+
## 9.0.0-canary.62
|
|
90
|
+
|
|
91
|
+
## 9.0.0-canary.61
|
|
92
|
+
|
|
93
|
+
## 9.0.0-canary.60
|
|
94
|
+
|
|
95
|
+
## 9.0.0-canary.59
|
|
96
|
+
|
|
97
|
+
## 9.0.0-canary.58
|
|
98
|
+
|
|
99
|
+
## 9.0.0-canary.57
|
|
100
|
+
|
|
101
|
+
## 9.0.0-canary.56
|
|
102
|
+
|
|
103
|
+
## 9.0.0-canary.55
|
|
104
|
+
|
|
105
|
+
## 9.0.0-canary.54
|
|
106
|
+
|
|
107
|
+
## 8.1.0-canary.53
|
|
108
|
+
|
|
109
|
+
## 8.1.0-canary.52
|
|
110
|
+
|
|
111
|
+
## 8.1.0-canary.51
|
|
112
|
+
|
|
113
|
+
## 8.1.0-canary.50
|
|
114
|
+
|
|
115
|
+
## 8.1.0-canary.49
|
|
116
|
+
|
|
117
|
+
## 8.1.0-canary.48
|
|
118
|
+
|
|
119
|
+
## 8.1.0-canary.47
|
|
120
|
+
|
|
121
|
+
## 8.1.0-canary.46
|
|
122
|
+
|
|
123
|
+
### Patch Changes
|
|
124
|
+
|
|
125
|
+
- [#2316](https://github.com/graphcommerce-org/graphcommerce/pull/2316) [`28d4708`](https://github.com/graphcommerce-org/graphcommerce/commit/28d470861a10a38690d565d236ce573905038d2b) - fix hygraph studio bugs in Dynamic Row UI module. Style config screen DR UI app. ([@JoshuaS98](https://github.com/JoshuaS98))
|
|
126
|
+
|
|
127
|
+
## 8.1.0-canary.45
|
|
128
|
+
|
|
129
|
+
## 8.1.0-canary.44
|
|
130
|
+
|
|
131
|
+
## 8.1.0-canary.43
|
|
132
|
+
|
|
133
|
+
## 8.1.0-canary.42
|
|
134
|
+
|
|
135
|
+
### Patch Changes
|
|
136
|
+
|
|
137
|
+
- [#2308](https://github.com/graphcommerce-org/graphcommerce/pull/2308) [`8faa5ac`](https://github.com/graphcommerce-org/graphcommerce/commit/8faa5ac618ecfdacd6d5eb751b4110c423aef97f) - Added Draft Mode support. When enabled it will be shown. ([@paales](https://github.com/paales))
|
|
138
|
+
|
|
139
|
+
## 8.1.0-canary.41
|
|
140
|
+
|
|
141
|
+
## 8.1.0-canary.40
|
|
142
|
+
|
|
143
|
+
## 8.1.0-canary.39
|
|
144
|
+
|
|
145
|
+
## 8.1.0-canary.38
|
|
146
|
+
|
|
147
|
+
## 8.1.0-canary.37
|
|
148
|
+
|
|
149
|
+
## 8.1.0-canary.36
|
|
150
|
+
|
|
151
|
+
## 8.1.0-canary.35
|
|
152
|
+
|
|
153
|
+
## 8.1.0-canary.34
|
|
154
|
+
|
|
155
|
+
## 8.1.0-canary.33
|
|
156
|
+
|
|
157
|
+
## 8.1.0-canary.32
|
|
158
|
+
|
|
159
|
+
## 8.1.0-canary.31
|
|
160
|
+
|
|
161
|
+
## 8.1.0-canary.30
|
|
162
|
+
|
|
163
|
+
## 8.1.0-canary.29
|
|
164
|
+
|
|
165
|
+
## 8.1.0-canary.28
|
|
166
|
+
|
|
167
|
+
## 8.1.0-canary.27
|
|
168
|
+
|
|
169
|
+
## 8.1.0-canary.26
|
|
170
|
+
|
|
171
|
+
## 8.1.0-canary.25
|
|
172
|
+
|
|
173
|
+
## 8.1.0-canary.24
|
|
174
|
+
|
|
175
|
+
## 8.1.0-canary.23
|
|
176
|
+
|
|
177
|
+
## 8.1.0-canary.22
|
|
178
|
+
|
|
179
|
+
## 8.1.0-canary.21
|
|
180
|
+
|
|
181
|
+
## 8.1.0-canary.20
|
|
182
|
+
|
|
183
|
+
## 8.1.0-canary.19
|
|
184
|
+
|
|
185
|
+
## 8.1.0-canary.18
|
|
186
|
+
|
|
187
|
+
## 8.1.0-canary.17
|
|
188
|
+
|
|
189
|
+
## 8.1.0-canary.16
|
|
190
|
+
|
|
191
|
+
## 8.1.0-canary.15
|
|
192
|
+
|
|
193
|
+
## 8.1.0-canary.14
|
|
194
|
+
|
|
195
|
+
## 8.1.0-canary.13
|
|
196
|
+
|
|
197
|
+
## 8.1.0-canary.12
|
|
198
|
+
|
|
199
|
+
## 8.1.0-canary.11
|
|
200
|
+
|
|
201
|
+
## 8.1.0-canary.10
|
|
202
|
+
|
|
3
203
|
## 8.1.0-canary.9
|
|
4
204
|
|
|
5
205
|
## 8.1.0-canary.8
|
|
@@ -92,8 +292,7 @@
|
|
|
92
292
|
|
|
93
293
|
### Minor Changes
|
|
94
294
|
|
|
95
|
-
- [#2100](https://github.com/graphcommerce-org/graphcommerce/pull/2100) [`4df891a`](https://github.com/graphcommerce-org/graphcommerce/commit/4df891a4c18b29cc52447eab3a97c66948b6c18f) - Add Dynamic Row UI for property UI field through a custom Hygraph application
|
|
96
|
-
([@JoshuaS98](https://github.com/JoshuaS98))
|
|
295
|
+
- [#2100](https://github.com/graphcommerce-org/graphcommerce/pull/2100) [`4df891a`](https://github.com/graphcommerce-org/graphcommerce/commit/4df891a4c18b29cc52447eab3a97c66948b6c18f) - Add Dynamic Row UI for property UI field through a custom Hygraph application ([@JoshuaS98](https://github.com/JoshuaS98))
|
|
97
296
|
|
|
98
297
|
## 8.0.0-canary.100
|
|
99
298
|
|
|
@@ -1,91 +1,59 @@
|
|
|
1
|
-
import { ApolloClient, InMemoryCache } from '@apollo/client'
|
|
2
|
-
import { useFieldExtension } from '@hygraph/app-sdk-react'
|
|
3
|
-
// eslint-disable-next-line @typescript-eslint/no-restricted-imports
|
|
1
|
+
import { ApolloClient, gql, InMemoryCache, useQuery } from '@apollo/client'
|
|
2
|
+
import { FieldExtensionProps, useFieldExtension } from '@hygraph/app-sdk-react'
|
|
4
3
|
import { TextField } from '@mui/material'
|
|
4
|
+
import { getIntrospectionQuery, IntrospectionQuery } from 'graphql'
|
|
5
5
|
import { useEffect, useMemo, useState } from 'react'
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
import { getFieldPaths } from '../lib/getFieldPaths'
|
|
7
|
+
|
|
8
|
+
function useClient(extension: FieldExtensionProps['extension']) {
|
|
9
|
+
return useMemo(
|
|
10
|
+
() =>
|
|
11
|
+
new ApolloClient({
|
|
12
|
+
uri:
|
|
13
|
+
typeof extension.config.backend === 'string'
|
|
14
|
+
? extension.config.backend
|
|
15
|
+
: 'https://graphcommerce.vercel.app/api/graphql', // fallback on the standard GraphCommerce Schema
|
|
16
|
+
cache: new InMemoryCache(),
|
|
17
|
+
}),
|
|
18
|
+
[extension.config.backend],
|
|
19
|
+
)
|
|
20
|
+
}
|
|
9
21
|
|
|
10
22
|
export function PropertyPicker() {
|
|
11
|
-
const
|
|
23
|
+
const fieldExtension = useFieldExtension()
|
|
24
|
+
|
|
25
|
+
const { value, onChange, extension } = fieldExtension
|
|
12
26
|
const [localValue, setLocalValue] = useState<string | undefined | null>(
|
|
13
27
|
typeof value === 'string' ? value : undefined,
|
|
14
28
|
)
|
|
15
|
-
|
|
29
|
+
|
|
30
|
+
const client = useClient(extension)
|
|
31
|
+
const { data, loading, error } = useQuery<IntrospectionQuery>(gql(getIntrospectionQuery()), {
|
|
32
|
+
client,
|
|
33
|
+
})
|
|
34
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
35
|
+
const schema = data?.__schema
|
|
16
36
|
|
|
17
37
|
useEffect(() => {
|
|
18
38
|
onChange(localValue).catch((err) => err)
|
|
19
39
|
}, [localValue, onChange])
|
|
20
40
|
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
})
|
|
29
|
-
return fetchGraphQLInterface(client)
|
|
30
|
-
}, [extension.config.backend])
|
|
31
|
-
|
|
32
|
-
// Prepare options
|
|
33
|
-
const numberOptions = useMemo(
|
|
34
|
-
() =>
|
|
35
|
-
createOptionsFromInterfaceObject(
|
|
36
|
-
objectifyGraphQLInterface(fields, 'number', ['ProductInterface']),
|
|
37
|
-
),
|
|
38
|
-
[fields],
|
|
39
|
-
)
|
|
40
|
-
const textOptions = useMemo(
|
|
41
|
-
() =>
|
|
42
|
-
createOptionsFromInterfaceObject(
|
|
43
|
-
objectifyGraphQLInterface(fields, 'text', ['ProductInterface']),
|
|
44
|
-
),
|
|
45
|
-
[fields],
|
|
46
|
-
)
|
|
47
|
-
const allOptions = useMemo(
|
|
48
|
-
() => ({
|
|
49
|
-
text: [...textOptions, { label: 'url', id: 'url' }].sort((a, b) => {
|
|
50
|
-
if (!a.label.includes('.') && !b.label.includes('.')) {
|
|
51
|
-
return a.label.localeCompare(b.label)
|
|
52
|
-
}
|
|
53
|
-
if (a.label.includes('.')) {
|
|
54
|
-
return 1
|
|
55
|
-
}
|
|
56
|
-
return -1
|
|
57
|
-
}),
|
|
58
|
-
number: [...numberOptions, { label: 'url', id: 'url' }],
|
|
59
|
-
}),
|
|
60
|
-
[numberOptions, textOptions],
|
|
61
|
-
)
|
|
62
|
-
|
|
63
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
64
|
-
// @ts-ignore - outdated types from @hygraph/app-sdk-react
|
|
65
|
-
const fieldType = field.parent.apiId ?? 'ConditionText'
|
|
66
|
-
const options = fieldType === 'ConditionNumber' ? allOptions.number : allOptions.text
|
|
67
|
-
|
|
68
|
-
if (!fields) {
|
|
69
|
-
Promise.resolve(graphQLInterfaceQuery)
|
|
70
|
-
.then((res) => {
|
|
71
|
-
const newFields: __Field[] = res?.data.__type?.fields
|
|
72
|
-
|
|
73
|
-
setFields(newFields)
|
|
74
|
-
})
|
|
75
|
-
.catch((err) => err)
|
|
76
|
-
|
|
77
|
-
return <div>Loading fields...</div>
|
|
78
|
-
}
|
|
79
|
-
if (options.length < 1) return <div>No properties available</div>
|
|
80
|
-
if (options.length > 10000) return <div>Too many properties to display</div>
|
|
41
|
+
const fieldPaths = useMemo(() => {
|
|
42
|
+
if (!schema) return []
|
|
43
|
+
return getFieldPaths(schema, ['ProductInterface'])
|
|
44
|
+
.sort((a, b) => a.depth() - b.depth())
|
|
45
|
+
.map((fp) => fp.stringify())
|
|
46
|
+
.filter<string>((v) => v !== undefined)
|
|
47
|
+
}, [schema])
|
|
81
48
|
|
|
82
49
|
return (
|
|
83
50
|
<TextField
|
|
84
51
|
id='property-selector'
|
|
85
|
-
select
|
|
52
|
+
select={!!fieldPaths.length}
|
|
53
|
+
variant='outlined'
|
|
54
|
+
size='small'
|
|
86
55
|
SelectProps={{
|
|
87
56
|
native: true,
|
|
88
|
-
variant: 'outlined',
|
|
89
57
|
}}
|
|
90
58
|
value={localValue}
|
|
91
59
|
onChange={(v) => {
|
|
@@ -95,6 +63,7 @@ export function PropertyPicker() {
|
|
|
95
63
|
fullWidth
|
|
96
64
|
sx={{
|
|
97
65
|
mt: '4px',
|
|
66
|
+
fontSize: '0.8em',
|
|
98
67
|
'& .MuiInputBase-root': {
|
|
99
68
|
borderRadius: { xs: '2px!important' },
|
|
100
69
|
},
|
|
@@ -119,11 +88,18 @@ export function PropertyPicker() {
|
|
|
119
88
|
},
|
|
120
89
|
}}
|
|
121
90
|
>
|
|
122
|
-
{
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
91
|
+
{fieldPaths.length > 0 ? (
|
|
92
|
+
<>
|
|
93
|
+
<option value='url'>url</option>
|
|
94
|
+
{fieldPaths.map((fp) => (
|
|
95
|
+
<option key={fp} value={fp}>
|
|
96
|
+
{fp}
|
|
97
|
+
</option>
|
|
98
|
+
))}
|
|
99
|
+
</>
|
|
100
|
+
) : (
|
|
101
|
+
<>{loading ? 'Loading..' : error?.message}</>
|
|
102
|
+
)}
|
|
127
103
|
</TextField>
|
|
128
104
|
)
|
|
129
105
|
}
|
package/components/Setup.tsx
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { useApp, Wrapper } from '@hygraph/app-sdk-react'
|
|
2
|
+
import Image from 'next/image'
|
|
2
3
|
import { useState } from 'react'
|
|
3
4
|
import styles from './setup.module.css'
|
|
4
5
|
|
|
5
6
|
function Install() {
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
const { updateInstallation, installation, showToast } = useApp()
|
|
8
|
+
|
|
8
9
|
const installed = installation.status === 'COMPLETED'
|
|
9
10
|
const [gqlUri, setGqlUri] = useState('')
|
|
10
11
|
|
|
11
|
-
const changedUri = extension.config.backend !== gqlUri
|
|
12
|
-
|
|
13
12
|
const saveOnClick = () =>
|
|
14
13
|
updateInstallation({
|
|
15
14
|
config: { backend: gqlUri },
|
|
@@ -64,10 +63,8 @@ function Install() {
|
|
|
64
63
|
|
|
65
64
|
let buttonText: string
|
|
66
65
|
let buttonAction: typeof uninstallOnClick | typeof installOnClick
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
buttonAction = saveOnClick
|
|
70
|
-
} else if (installed) {
|
|
66
|
+
|
|
67
|
+
if (installed) {
|
|
71
68
|
buttonText = 'Disable app'
|
|
72
69
|
buttonAction = uninstallOnClick
|
|
73
70
|
} else {
|
|
@@ -78,17 +75,30 @@ function Install() {
|
|
|
78
75
|
return (
|
|
79
76
|
<>
|
|
80
77
|
<>
|
|
81
|
-
<span>
|
|
78
|
+
<span>
|
|
79
|
+
<strong>GraphQL API URI</strong>
|
|
80
|
+
</span>
|
|
82
81
|
<input
|
|
83
82
|
name='gql-uri'
|
|
84
|
-
|
|
83
|
+
className={styles.textInput}
|
|
84
|
+
defaultValue={(installation.config.backend as string) ?? ''}
|
|
85
85
|
onChange={(e) => setGqlUri(e.target.value)}
|
|
86
86
|
/>
|
|
87
87
|
</>
|
|
88
88
|
|
|
89
|
-
<
|
|
90
|
-
{
|
|
91
|
-
|
|
89
|
+
<div className={styles.buttonsContainer}>
|
|
90
|
+
<button type='button' className={styles.button} onClick={saveOnClick} data-save-button>
|
|
91
|
+
Save
|
|
92
|
+
</button>
|
|
93
|
+
<button
|
|
94
|
+
type='button'
|
|
95
|
+
className={styles.button}
|
|
96
|
+
onClick={buttonAction}
|
|
97
|
+
aria-disabled={installed}
|
|
98
|
+
>
|
|
99
|
+
{buttonText}
|
|
100
|
+
</button>
|
|
101
|
+
</div>
|
|
92
102
|
</>
|
|
93
103
|
)
|
|
94
104
|
}
|
|
@@ -96,16 +106,28 @@ function Install() {
|
|
|
96
106
|
export function Page() {
|
|
97
107
|
return (
|
|
98
108
|
<div className={styles.container}>
|
|
99
|
-
<
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
+
<div className={styles.contentWrapper}>
|
|
110
|
+
<div className={styles.logoWrapper}>
|
|
111
|
+
<Image
|
|
112
|
+
src='https://www.graphcommerce.org/_next/static/media/graphcommerce.a8fe7e28.svg'
|
|
113
|
+
layout='fill'
|
|
114
|
+
alt='GraphCommerce logo'
|
|
115
|
+
/>
|
|
116
|
+
</div>
|
|
117
|
+
|
|
118
|
+
<h1 className={styles.title}>
|
|
119
|
+
Dynamic Rows Property Selector <span className={styles.author}>by Joshua Bolk</span>
|
|
120
|
+
</h1>
|
|
121
|
+
<p className={styles.description}>
|
|
122
|
+
Enhance your content management experience with Dynamic Rows, specifically designed to
|
|
123
|
+
integrate seamlessly with our Dynamic Row module. It features an intuitive property picker
|
|
124
|
+
field, allowing for effortless selection and organization of properties to customize your
|
|
125
|
+
content layout. Enable the app and put your graphQL URI to get started.
|
|
126
|
+
</p>
|
|
127
|
+
<Wrapper>
|
|
128
|
+
<Install />
|
|
129
|
+
</Wrapper>
|
|
130
|
+
</div>
|
|
109
131
|
</div>
|
|
110
132
|
)
|
|
111
133
|
}
|
|
@@ -1,11 +1,28 @@
|
|
|
1
1
|
.container {
|
|
2
|
-
display: flex;
|
|
3
|
-
flex-direction: column;
|
|
4
|
-
align-items: center;
|
|
5
|
-
justify-content: center;
|
|
6
2
|
height: 100%;
|
|
7
3
|
max-width: 1200px;
|
|
8
|
-
font-family: '
|
|
4
|
+
font-family: 'Roboto', sans-serif;
|
|
5
|
+
line-height: 1.3;
|
|
6
|
+
font-size: 18px;
|
|
7
|
+
background-color: aliceblue;
|
|
8
|
+
padding: 32px 24px;
|
|
9
|
+
border-radius: 8px;
|
|
10
|
+
margin: 0 auto;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.contentWrapper {
|
|
14
|
+
max-width: 700px;
|
|
15
|
+
display: flex;
|
|
16
|
+
flex-direction: column;
|
|
17
|
+
align-items: left;
|
|
18
|
+
margin: 0 auto;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.logoWrapper {
|
|
22
|
+
position: relative;
|
|
23
|
+
height: 60px;
|
|
24
|
+
width: 50%;
|
|
25
|
+
margin-bottom: 32px;
|
|
9
26
|
}
|
|
10
27
|
|
|
11
28
|
.title {
|
|
@@ -15,6 +32,12 @@
|
|
|
15
32
|
margin-bottom: 16px;
|
|
16
33
|
}
|
|
17
34
|
|
|
35
|
+
.author {
|
|
36
|
+
font-size: 12px;
|
|
37
|
+
font-weight: 300;
|
|
38
|
+
line-height: 16px;
|
|
39
|
+
}
|
|
40
|
+
|
|
18
41
|
.desciption {
|
|
19
42
|
font-size: 14px;
|
|
20
43
|
font-weight: 300;
|
|
@@ -40,19 +63,51 @@
|
|
|
40
63
|
text-decoration: none;
|
|
41
64
|
font-weight: 500;
|
|
42
65
|
border: 0px;
|
|
43
|
-
margin: 16px
|
|
66
|
+
margin: 16px 0 0 0;
|
|
44
67
|
border-radius: 4px;
|
|
45
|
-
font-size:
|
|
68
|
+
font-size: 18px;
|
|
46
69
|
line-height: 16px;
|
|
47
|
-
height:
|
|
70
|
+
height: 40px;
|
|
48
71
|
min-width: 24px;
|
|
49
|
-
padding-left:
|
|
50
|
-
padding-right:
|
|
72
|
+
padding-left: 16px;
|
|
73
|
+
padding-right: 16px;
|
|
51
74
|
color: rgb(255, 255, 255);
|
|
52
|
-
background-color:
|
|
75
|
+
background-color: #2563eb;
|
|
76
|
+
transition: background-color 0.2s ease 0s;
|
|
53
77
|
}
|
|
54
78
|
|
|
55
79
|
.button:hover {
|
|
56
80
|
cursor: pointer;
|
|
57
|
-
background-color:
|
|
81
|
+
background-color: #1d4ed8;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.button[aria-disabled='true'] {
|
|
85
|
+
background-color: rgb(200, 200, 200);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.button[aria-disabled='true']:hover {
|
|
89
|
+
background-color: rgb(192, 192, 192);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.button[aria-disabled='false'] {
|
|
93
|
+
background-color: #16a34a;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.button[aria-disabled='false']:hover {
|
|
97
|
+
background-color: #15803d;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.buttonsContainer {
|
|
101
|
+
display: flex;
|
|
102
|
+
align-items: center;
|
|
103
|
+
justify-content: space-between;
|
|
104
|
+
margin-top: 8px;
|
|
105
|
+
column-gap: 24px;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.textInput {
|
|
109
|
+
display: inline;
|
|
110
|
+
margin: 24px 0;
|
|
111
|
+
font-size: 18px;
|
|
112
|
+
padding: 12px 6px;
|
|
58
113
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { IntrospectionField, IntrospectionOutputTypeRef, IntrospectionSchema } from 'graphql'
|
|
2
|
+
|
|
3
|
+
function getType(type: IntrospectionOutputTypeRef) {
|
|
4
|
+
switch (type.kind) {
|
|
5
|
+
case 'NON_NULL':
|
|
6
|
+
case 'LIST':
|
|
7
|
+
return getType(type.ofType)
|
|
8
|
+
default:
|
|
9
|
+
return type
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
class FieldPath {
|
|
14
|
+
constructor(
|
|
15
|
+
public field: IntrospectionField,
|
|
16
|
+
private prev: FieldPath | undefined,
|
|
17
|
+
) {}
|
|
18
|
+
|
|
19
|
+
stringify(filter?: string[]): string | undefined {
|
|
20
|
+
if (this.field.type.kind === 'SCALAR' && filter && !filter.includes(this.field.type.name)) {
|
|
21
|
+
return undefined
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const prevStr = this.prev?.stringify(filter)
|
|
25
|
+
return prevStr ? `${prevStr}.${this.field.name}` : this.field.name
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
depth = () => (this?.prev?.depth() ?? 0) + 1
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function getFieldPaths(
|
|
32
|
+
schema: IntrospectionSchema,
|
|
33
|
+
types: string[],
|
|
34
|
+
prevPath: FieldPath | undefined = undefined,
|
|
35
|
+
): FieldPath[] {
|
|
36
|
+
const typeName = types[types.length - 1]
|
|
37
|
+
|
|
38
|
+
const paths: FieldPath[] = []
|
|
39
|
+
const type = schema.types.find((t) => t.name === typeName)
|
|
40
|
+
|
|
41
|
+
if (!type) return paths
|
|
42
|
+
|
|
43
|
+
if ((prevPath?.depth() ?? 0) > 3) return paths
|
|
44
|
+
|
|
45
|
+
if (type.kind === 'OBJECT' || type.kind === 'INTERFACE') {
|
|
46
|
+
type.fields.forEach((field) => {
|
|
47
|
+
const t = getType(field.type)
|
|
48
|
+
|
|
49
|
+
if (!types.includes(t.name)) {
|
|
50
|
+
const newTypes = [...types, t.name]
|
|
51
|
+
|
|
52
|
+
const newPath = new FieldPath(field, prevPath)
|
|
53
|
+
|
|
54
|
+
if (t.kind === 'OBJECT' || t.kind === 'INTERFACE') {
|
|
55
|
+
paths.push(...getFieldPaths(schema, newTypes, newPath))
|
|
56
|
+
} else if (t.kind === 'SCALAR' || t.kind === 'ENUM') {
|
|
57
|
+
paths.push(newPath)
|
|
58
|
+
} else if (t.kind === 'UNION') {
|
|
59
|
+
// not supported currently
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return paths
|
|
66
|
+
}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@graphcommerce/hygraph-dynamic-rows-ui",
|
|
3
3
|
"homepage": "https://www.graphcommerce.org/",
|
|
4
4
|
"repository": "github:graphcommerce-org/graphcommerce",
|
|
5
|
-
"version": "
|
|
5
|
+
"version": "9.0.0-canary.101",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"type": "commonjs",
|
|
8
8
|
"prettier": "@graphcommerce/prettier-config-pwa",
|
|
@@ -16,29 +16,29 @@
|
|
|
16
16
|
"dev": "next dev"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@apollo/client": "~3.8
|
|
20
|
-
"@graphcommerce/next-config": "
|
|
21
|
-
"@hygraph/app-sdk-react": "^0.0.
|
|
22
|
-
"@lingui/core": "
|
|
23
|
-
"@lingui/macro": "
|
|
24
|
-
"@lingui/react": "
|
|
25
|
-
"@mui/material": "5.
|
|
19
|
+
"@apollo/client": "~3.11.8",
|
|
20
|
+
"@graphcommerce/next-config": "9.0.0-canary.101",
|
|
21
|
+
"@hygraph/app-sdk-react": "^0.0.5",
|
|
22
|
+
"@lingui/core": "4.11.4",
|
|
23
|
+
"@lingui/macro": "4.11.4",
|
|
24
|
+
"@lingui/react": "4.11.4",
|
|
25
|
+
"@mui/material": "5.16.7",
|
|
26
26
|
"cross-env": "^7.0.3",
|
|
27
|
-
"dotenv": "16.
|
|
28
|
-
"graphql": "^16.
|
|
29
|
-
"next": "14.
|
|
30
|
-
"react": "^18.
|
|
31
|
-
"react-dom": "^18.
|
|
32
|
-
"webpack": "5.
|
|
27
|
+
"dotenv": "16.4.5",
|
|
28
|
+
"graphql": "^16.9.0",
|
|
29
|
+
"next": "14.2.14",
|
|
30
|
+
"react": "^18.3.1",
|
|
31
|
+
"react-dom": "^18.3.1",
|
|
32
|
+
"webpack": "~5.93.0"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@graphcommerce/eslint-config-pwa": "
|
|
36
|
-
"@graphcommerce/prettier-config-pwa": "
|
|
37
|
-
"@graphcommerce/typescript-config-pwa": "
|
|
38
|
-
"@types/react-is": "^18.
|
|
35
|
+
"@graphcommerce/eslint-config-pwa": "9.0.0-canary.101",
|
|
36
|
+
"@graphcommerce/prettier-config-pwa": "9.0.0-canary.101",
|
|
37
|
+
"@graphcommerce/typescript-config-pwa": "9.0.0-canary.101",
|
|
38
|
+
"@types/react-is": "^18.3.0",
|
|
39
39
|
"babel-plugin-macros": "^3.1.0",
|
|
40
|
-
"eslint": "8
|
|
41
|
-
"prettier": "3.
|
|
42
|
-
"typescript": "5.
|
|
40
|
+
"eslint": "^8",
|
|
41
|
+
"prettier": "3.3.3",
|
|
42
|
+
"typescript": "5.6.2"
|
|
43
43
|
}
|
|
44
44
|
}
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { Wrapper } from '@hygraph/app-sdk-react'
|
|
2
|
-
import React from 'react'
|
|
3
|
-
import { PropertyPicker } from '
|
|
2
|
+
import React, { useEffect } from 'react'
|
|
3
|
+
import { PropertyPicker } from '../components/PropertyPicker'
|
|
4
4
|
|
|
5
5
|
export default function DRPropertyPicker() {
|
|
6
6
|
const fieldContainer = React.useRef<HTMLDivElement | null>(null)
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
useEffect(() => {
|
|
9
9
|
/**
|
|
10
10
|
* Some styling is being undone here to resolve conflicts between Hygraph App SDK and CssAndFramerMotionProvider.
|
|
11
11
|
*/
|
|
12
|
-
|
|
13
12
|
const frameBox1 = fieldContainer?.current?.parentElement
|
|
14
13
|
if (frameBox1) {
|
|
15
14
|
frameBox1.style.position = 'static'
|
package/pages/setup.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from 'react'
|
|
1
|
+
import React, { useEffect } from 'react'
|
|
2
2
|
import { Page } from '..'
|
|
3
3
|
|
|
4
4
|
export default function Setup() {
|
|
@@ -8,7 +8,7 @@ export default function Setup() {
|
|
|
8
8
|
* This is a hack to fix the height of the iframe, which was malfunctioning because of a conflict
|
|
9
9
|
* with FramerNextPages
|
|
10
10
|
*/
|
|
11
|
-
|
|
11
|
+
useEffect(() => {
|
|
12
12
|
const framerParent = appContainer?.current?.parentElement
|
|
13
13
|
if (framerParent) {
|
|
14
14
|
framerParent.style.position = 'static'
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { ProductProperty } from '../types'
|
|
2
|
-
|
|
3
|
-
export const createOptionsFromInterfaceObject = (
|
|
4
|
-
obj: object,
|
|
5
|
-
path = '',
|
|
6
|
-
inputs: ProductProperty[] = [],
|
|
7
|
-
parent = '',
|
|
8
|
-
): ProductProperty[] => {
|
|
9
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
10
|
-
/** Keep count of the current path and parent */
|
|
11
|
-
const currentPath: string = path ? `${path}.${key}` : key
|
|
12
|
-
const currentParent: string = parent ? `${parent}/` : ''
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* If the value is a string, number or boolean, add it to the inputs array. If the value is an
|
|
16
|
-
* array, recurse on the first item. If the value is an object, recurse on all it's keys.
|
|
17
|
-
*/
|
|
18
|
-
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
19
|
-
inputs.push({
|
|
20
|
-
label: currentPath,
|
|
21
|
-
id: currentPath,
|
|
22
|
-
})
|
|
23
|
-
} else if (Array.isArray(value) && value.length > 0) {
|
|
24
|
-
createOptionsFromInterfaceObject(
|
|
25
|
-
value[0] as object,
|
|
26
|
-
`${currentPath}[0]`,
|
|
27
|
-
inputs,
|
|
28
|
-
`${currentParent}${key}`,
|
|
29
|
-
)
|
|
30
|
-
} else if (typeof value === 'object' && value !== null) {
|
|
31
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
32
|
-
createOptionsFromInterfaceObject(
|
|
33
|
-
value as object,
|
|
34
|
-
currentPath,
|
|
35
|
-
inputs,
|
|
36
|
-
`${currentParent}${key}`,
|
|
37
|
-
)
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return inputs
|
|
42
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export const createRecursiveIntrospectionQuery = (type: string, depth: number) => {
|
|
2
|
-
let baseQuery = `__type(name: "${type}") { name fields { name `
|
|
3
|
-
let endQuery = ' } }'
|
|
4
|
-
|
|
5
|
-
for (let i = 0; i < depth; i++) {
|
|
6
|
-
baseQuery += 'type { name ofType { name fields { name isDeprecated '
|
|
7
|
-
endQuery += ' } } }'
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const result = baseQuery + endQuery
|
|
11
|
-
|
|
12
|
-
return result
|
|
13
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { ApolloClient, NormalizedCacheObject, gql } from '@apollo/client'
|
|
2
|
-
import { createRecursiveIntrospectionQuery } from './createRecursiveIntrospectionQuery'
|
|
3
|
-
|
|
4
|
-
export const fetchGraphQLInterface = (client: ApolloClient<NormalizedCacheObject>) => {
|
|
5
|
-
const introspectionQuery = createRecursiveIntrospectionQuery('ProductInterface', 4)
|
|
6
|
-
|
|
7
|
-
return client.query({
|
|
8
|
-
query: gql`
|
|
9
|
-
query getSchema {
|
|
10
|
-
${introspectionQuery}
|
|
11
|
-
}
|
|
12
|
-
`,
|
|
13
|
-
})
|
|
14
|
-
}
|
package/lib/index.ts
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { __Field } from '../types'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* In this function we create an object from the GraphQL interface.
|
|
5
|
-
* We need this so we can map out the properties of an interface that is needed
|
|
6
|
-
* for the Dynamic Rows Autocomplete.
|
|
7
|
-
* @param fields - The GraphQL interface object that is read from the schema.
|
|
8
|
-
* @returns
|
|
9
|
-
*/
|
|
10
|
-
export const objectifyGraphQLInterface = (
|
|
11
|
-
fields: __Field[] | null,
|
|
12
|
-
conditionType: 'text' | 'number' | 'all',
|
|
13
|
-
skip: string[],
|
|
14
|
-
): object => {
|
|
15
|
-
let objectifiedInterface: object = {}
|
|
16
|
-
|
|
17
|
-
if (!fields) return objectifiedInterface
|
|
18
|
-
|
|
19
|
-
for (const [, value] of Object.entries(fields)) {
|
|
20
|
-
const nestedFields = value?.type?.ofType?.fields
|
|
21
|
-
const { isDeprecated } = value
|
|
22
|
-
const typeOf = value?.type?.name
|
|
23
|
-
const typeName = value?.type?.ofType?.name ?? ''
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* With typevalue we can know of which type a property is, so we for example can determine to to hide string values in ConditionNumbers.
|
|
27
|
-
*/
|
|
28
|
-
let typeValue: 'number' | 'text' | 'boolean'
|
|
29
|
-
switch (typeOf) {
|
|
30
|
-
case 'Float' || 'Int':
|
|
31
|
-
typeValue = 'number'
|
|
32
|
-
break
|
|
33
|
-
case 'Boolean':
|
|
34
|
-
typeValue = 'text' // Seperate booleans are not supported yet.
|
|
35
|
-
break
|
|
36
|
-
default:
|
|
37
|
-
typeValue = 'text'
|
|
38
|
-
break
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (skip.includes(typeName) || isDeprecated || !value?.name) {
|
|
42
|
-
// do nothing
|
|
43
|
-
} else if (nestedFields) {
|
|
44
|
-
objectifiedInterface = {
|
|
45
|
-
...objectifiedInterface,
|
|
46
|
-
[value.name]: objectifyGraphQLInterface(nestedFields, conditionType, [...skip, typeName]),
|
|
47
|
-
}
|
|
48
|
-
} else if (typeOf && conditionType === 'all') {
|
|
49
|
-
objectifiedInterface = {
|
|
50
|
-
...objectifiedInterface,
|
|
51
|
-
[value.name]: typeValue,
|
|
52
|
-
}
|
|
53
|
-
} else if (conditionType === typeValue) {
|
|
54
|
-
objectifiedInterface = {
|
|
55
|
-
...objectifiedInterface,
|
|
56
|
-
[value.name]: typeValue,
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return objectifiedInterface
|
|
62
|
-
}
|