@graphcommerce/graphcms-ui 2.106.38 → 3.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/CHANGELOG.md +92 -86
- package/components/Asset/Asset.graphql +7 -0
- package/components/Asset/index.tsx +34 -0
- package/components/RichText/RichText.tsx +117 -0
- package/components/RichText/defaultRenderers.tsx +42 -0
- package/components/RichText/defaultSxRenderer.ts +103 -0
- package/{RichText → components/RichText}/getNodeLength.tsx +3 -2
- package/components/RichText/index.ts +2 -0
- package/components/RichText/types.ts +92 -0
- package/index.ts +2 -1
- package/package.json +14 -18
- package/RichText/RichTextColumns.tsx +0 -22
- package/RichText/RichTextDoubleSpread.tsx +0 -18
- package/RichText/RichTextHeadingStrongStroked.tsx +0 -22
- package/RichText/RichTextHero.tsx +0 -27
- package/RichText/RichTextParagraphStrongStroked.tsx +0 -23
- package/RichText/RichTextQuote.tsx +0 -21
- package/RichText/RichTextSpread.tsx +0 -14
- package/RichText/index.tsx +0 -325
- package/RichText/useRichTextStyles.tsx +0 -118
package/CHANGELOG.md
CHANGED
|
@@ -1,158 +1,164 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
|
-
|
|
6
|
-
## [2.106.12](https://github.com/ho-nl/m2-pwa/compare/@graphcommerce/graphcms-ui@2.106.11...@graphcommerce/graphcms-ui@2.106.12) (2021-12-17)
|
|
3
|
+
## 3.0.1
|
|
7
4
|
|
|
5
|
+
### Patch Changes
|
|
8
6
|
|
|
9
|
-
|
|
7
|
+
- [`0cbaa878b`](https://github.com/ho-nl/m2-pwa/commit/0cbaa878b8a844d5abbeb1797b625a33130e6514)
|
|
8
|
+
Thanks [@paales](https://github.com/paales)! - Added homepage and repository package.json files,
|
|
9
|
+
so that the packages link to back to the website and repository
|
|
10
|
+
- Updated dependencies
|
|
11
|
+
[[`0cbaa878b`](https://github.com/ho-nl/m2-pwa/commit/0cbaa878b8a844d5abbeb1797b625a33130e6514)]:
|
|
12
|
+
- @graphcommerce/graphql@3.0.1
|
|
13
|
+
- @graphcommerce/image@3.0.1
|
|
14
|
+
- @graphcommerce/next-ui@4.0.1
|
|
10
15
|
|
|
11
|
-
|
|
16
|
+
## 3.0.0
|
|
12
17
|
|
|
18
|
+
### Major Changes
|
|
13
19
|
|
|
20
|
+
- [#1258](https://github.com/ho-nl/m2-pwa/pull/1258)
|
|
21
|
+
[`ad36382a4`](https://github.com/ho-nl/m2-pwa/commit/ad36382a4d55d83d9e47b7eb6a02671a2a631a05)
|
|
22
|
+
Thanks [@paales](https://github.com/paales)! - Upgraded to Material UI 5
|
|
14
23
|
|
|
24
|
+
### Patch Changes
|
|
15
25
|
|
|
26
|
+
- Updated dependencies
|
|
27
|
+
[[`ad36382a4`](https://github.com/ho-nl/m2-pwa/commit/ad36382a4d55d83d9e47b7eb6a02671a2a631a05)]:
|
|
28
|
+
- @graphcommerce/graphql@3.0.0
|
|
29
|
+
- @graphcommerce/image@3.0.0
|
|
30
|
+
- @graphcommerce/next-ui@4.0.0
|
|
16
31
|
|
|
17
|
-
|
|
32
|
+
All notable changes to this project will be documented in this file. See
|
|
33
|
+
[Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
18
34
|
|
|
35
|
+
## [2.106.12](https://github.com/ho-nl/m2-pwa/compare/@graphcommerce/graphcms-ui@2.106.11...@graphcommerce/graphcms-ui@2.106.12) (2021-12-17)
|
|
19
36
|
|
|
20
37
|
### Bug Fixes
|
|
21
38
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
* render bug stroke variable fonts ([582de18](https://github.com/ho-nl/m2-pwa/commit/582de187800ee9c53718bf43a39ca77398d21b91)), closes [/github.com/rsms/inter/issues/292#issuecomment-674993644](https://github.com//github.com/rsms/inter/issues/292/issues/issuecomment-674993644)
|
|
39
|
+
- make sure we're able to render table_header_cell and do not throw when rendering unknown elements
|
|
40
|
+
in production
|
|
41
|
+
([f7b7972](https://github.com/ho-nl/m2-pwa/commit/f7b797272f5765801c3f2e217fa9905f65dbe6d6))
|
|
26
42
|
|
|
43
|
+
# [2.106.0](https://github.com/ho-nl/m2-pwa/compare/@graphcommerce/graphcms-ui@2.105.4...@graphcommerce/graphcms-ui@2.106.0) (2021-12-01)
|
|
27
44
|
|
|
28
|
-
###
|
|
29
|
-
|
|
30
|
-
* breakpointVal ([0294503](https://github.com/ho-nl/m2-pwa/commit/029450343051cf6995babad9f9b42c7e6ad1094e))
|
|
31
|
-
* responsiveTyp ([6108b61](https://github.com/ho-nl/m2-pwa/commit/6108b6148e76ddbbe2db1614f10aaf88423db5ca))
|
|
32
|
-
|
|
45
|
+
### Bug Fixes
|
|
33
46
|
|
|
47
|
+
- build ([72d9353](https://github.com/ho-nl/m2-pwa/commit/72d935376a1967cc976c20b984ef15a862e5d0f4))
|
|
48
|
+
- fontWeights
|
|
49
|
+
([7172527](https://github.com/ho-nl/m2-pwa/commit/71725272fe9f0b854d918ae357a668f641bfe8e5))
|
|
50
|
+
- hero text spacing
|
|
51
|
+
([79dd6aa](https://github.com/ho-nl/m2-pwa/commit/79dd6aa2fe576104ebbbdd092f6b415d319dec48))
|
|
52
|
+
- render bug stroke variable fonts
|
|
53
|
+
([582de18](https://github.com/ho-nl/m2-pwa/commit/582de187800ee9c53718bf43a39ca77398d21b91)),
|
|
54
|
+
closes
|
|
55
|
+
[/github.com/rsms/inter/issues/292#issuecomment-674993644](https://github.com//github.com/rsms/inter/issues/292/issues/issuecomment-674993644)
|
|
34
56
|
|
|
57
|
+
### Features
|
|
35
58
|
|
|
59
|
+
- breakpointVal
|
|
60
|
+
([0294503](https://github.com/ho-nl/m2-pwa/commit/029450343051cf6995babad9f9b42c7e6ad1094e))
|
|
61
|
+
- responsiveTyp
|
|
62
|
+
([6108b61](https://github.com/ho-nl/m2-pwa/commit/6108b6148e76ddbbe2db1614f10aaf88423db5ca))
|
|
36
63
|
|
|
37
64
|
# [2.105.0](https://github.com/ho-nl/m2-pwa/compare/@graphcommerce/graphcms-ui@2.104.29...@graphcommerce/graphcms-ui@2.105.0) (2021-11-12)
|
|
38
65
|
|
|
39
|
-
|
|
40
66
|
### Features
|
|
41
67
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
68
|
+
- added tons of translations
|
|
69
|
+
([9bb0ac7](https://github.com/ho-nl/m2-pwa/commit/9bb0ac709b58df6ea6141e92e4923a5ca9ae2963))
|
|
47
70
|
|
|
48
71
|
## [2.104.29](https://github.com/ho-nl/m2-pwa/compare/@graphcommerce/graphcms-ui@2.104.28...@graphcommerce/graphcms-ui@2.104.29) (2021-11-12)
|
|
49
72
|
|
|
50
|
-
|
|
51
73
|
### Bug Fixes
|
|
52
74
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
75
|
+
- must have smaller sizes for mobile
|
|
76
|
+
([34cf81e](https://github.com/ho-nl/m2-pwa/commit/34cf81eed6c5996f70be8b2c10888e44fad4d57f))
|
|
58
77
|
|
|
59
78
|
## [2.104.18](https://github.com/ho-nl/m2-pwa/compare/@graphcommerce/graphcms-ui@2.104.17...@graphcommerce/graphcms-ui@2.104.18) (2021-11-04)
|
|
60
79
|
|
|
61
|
-
|
|
62
80
|
### Bug Fixes
|
|
63
81
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
82
|
+
- remove hardcoded fontSize
|
|
83
|
+
([e4e09e1](https://github.com/ho-nl/m2-pwa/commit/e4e09e11baeb8edeff634550b8cdb88571d96911))
|
|
69
84
|
|
|
70
85
|
## [2.104.16](https://github.com/ho-nl/m2-pwa/compare/@graphcommerce/graphcms-ui@2.104.15...@graphcommerce/graphcms-ui@2.104.16) (2021-11-03)
|
|
71
86
|
|
|
72
|
-
|
|
73
87
|
### Bug Fixes
|
|
74
88
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
89
|
+
- various accessibility improvements
|
|
90
|
+
([47481a9](https://github.com/ho-nl/m2-pwa/commit/47481a9a882ba87968de6dd797557b0b275d75fb))
|
|
80
91
|
|
|
81
92
|
## [2.104.9](https://github.com/ho-nl/m2-pwa/compare/@graphcommerce/graphcms-ui@2.104.8...@graphcommerce/graphcms-ui@2.104.9) (2021-11-02)
|
|
82
93
|
|
|
83
|
-
|
|
84
94
|
### Bug Fixes
|
|
85
95
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
96
|
+
- color from palette
|
|
97
|
+
([283cca1](https://github.com/ho-nl/m2-pwa/commit/283cca169ec00cbac8b973e14e6b77a443e26c99))
|
|
98
|
+
- darkMode
|
|
99
|
+
([c7573de](https://github.com/ho-nl/m2-pwa/commit/c7573de6bb80643b26931c35ac61735539e7fbf0))
|
|
92
100
|
|
|
93
101
|
## [2.104.3](https://github.com/ho-nl/m2-pwa/compare/@graphcommerce/graphcms-ui@2.104.2...@graphcommerce/graphcms-ui@2.104.3) (2021-10-28)
|
|
94
102
|
|
|
95
|
-
|
|
96
103
|
### Bug Fixes
|
|
97
104
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
105
|
+
- default heading margins for CMS content
|
|
106
|
+
([ac7c24f](https://github.com/ho-nl/m2-pwa/commit/ac7c24f731abf9573ad93d534d84b9f7c1b16a2b))
|
|
103
107
|
|
|
104
108
|
# [2.104.0](https://github.com/ho-nl/m2-pwa/compare/@graphcommerce/graphcms-ui@2.103.30...@graphcommerce/graphcms-ui@2.104.0) (2021-10-27)
|
|
105
109
|
|
|
106
|
-
|
|
107
110
|
### Features
|
|
108
111
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
112
|
+
- **nextjs:** upgraded to nextjs 12
|
|
113
|
+
([9331bc8](https://github.com/ho-nl/m2-pwa/commit/9331bc801f6419522115cc47d291d49d608d5a90))
|
|
114
114
|
|
|
115
115
|
## [2.103.1](https://github.com/ho-nl/m2-pwa/compare/@graphcommerce/graphcms-ui@2.103.0...@graphcommerce/graphcms-ui@2.103.1) (2021-09-27)
|
|
116
116
|
|
|
117
117
|
**Note:** Version bump only for package @graphcommerce/graphcms-ui
|
|
118
118
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
119
|
# 2.103.0 (2021-09-27)
|
|
124
120
|
|
|
125
|
-
|
|
126
121
|
### Bug Fixes
|
|
127
122
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
123
|
+
- ignore md files from triggering version updates
|
|
124
|
+
([4f98392](https://github.com/ho-nl/m2-pwa/commit/4f9839250b3a32d3070da5290e5efcc5e2243fba))
|
|
125
|
+
- implement next-ui barrel imports
|
|
126
|
+
([75bea70](https://github.com/ho-nl/m2-pwa/commit/75bea703dba898f18a2a1dfa3243ebd0a4e6f0e1))
|
|
127
|
+
- narrow quote on mobile
|
|
128
|
+
([e06ccc2](https://github.com/ho-nl/m2-pwa/commit/e06ccc25048124431dcdb786f1719f688a5e429c))
|
|
129
|
+
- remove conflicting files
|
|
130
|
+
([0c17ae4](https://github.com/ho-nl/m2-pwa/commit/0c17ae46be62b775ac83b35f11c532ce2d9401a3))
|
|
131
|
+
- since all links are of next/link we need to add passHref for custom components
|
|
132
|
+
([16fb931](https://github.com/ho-nl/m2-pwa/commit/16fb93100d367203ea79bb4f93357221253f2ecd))
|
|
134
133
|
|
|
135
134
|
### Features
|
|
136
135
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
136
|
+
- created stacked-pages package
|
|
137
|
+
([d86008e](https://github.com/ho-nl/m2-pwa/commit/d86008ee659ccb25b194a41d624b394a1ddbd088))
|
|
138
|
+
- **image:** introduced completely rewritten Image component
|
|
139
|
+
([e3413b3](https://github.com/ho-nl/m2-pwa/commit/e3413b3a57392d6571ea64cb8d9c8dca05ea31df))
|
|
140
|
+
- implemented the ability to load graphcms content by URL
|
|
141
|
+
([99bc970](https://github.com/ho-nl/m2-pwa/commit/99bc970fc46d40b7efa99a71819529aeaa206bb4))
|
|
142
|
+
- introduced graphcms-ui package to quickly build pages
|
|
143
|
+
([04d89c3](https://github.com/ho-nl/m2-pwa/commit/04d89c3fecf25f64923caf437eeb4b73f8887102))
|
|
144
|
+
- introduces framer-next-pages and framer-sheet to next-ui and soxbase package
|
|
145
|
+
([e04ad8a](https://github.com/ho-nl/m2-pwa/commit/e04ad8a94cd1fd5a7c5575c9db7916b6e8a88f16))
|
|
146
|
+
- next.js 11
|
|
147
|
+
([7d61407](https://github.com/ho-nl/m2-pwa/commit/7d614075a778f488045034f74be4f75b93f63c43))
|
|
148
|
+
- **playwright:** added new playwright package to enable browser testing
|
|
149
|
+
([6f49ec7](https://github.com/ho-nl/m2-pwa/commit/6f49ec7595563775b96ebf21c27e39da1282e8d9))
|
|
150
|
+
- renamed all packages to use [@graphcommerce](https://github.com/graphcommerce) instead of
|
|
151
|
+
[@reachdigital](https://github.com/reachdigital)
|
|
152
|
+
([491e4ce](https://github.com/ho-nl/m2-pwa/commit/491e4cec9a2686472dac36b79f999257c0811ffe))
|
|
153
|
+
- upgrade to node 14
|
|
154
|
+
([d079a75](https://github.com/ho-nl/m2-pwa/commit/d079a751e9bfd8dc7f5009d2c9f31c336a0c96ab))
|
|
155
|
+
- upgraded to nextjs 11
|
|
156
|
+
([0053beb](https://github.com/ho-nl/m2-pwa/commit/0053beb7ef597c190add7264256a0eaec35868da))
|
|
148
157
|
|
|
149
158
|
### Reverts
|
|
150
159
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
160
|
+
- Revert "chore: upgrade @apollo/client"
|
|
161
|
+
([55ff24e](https://github.com/ho-nl/m2-pwa/commit/55ff24ede0e56c85b8095edadadd1ec5e0b1b8d2))
|
|
156
162
|
|
|
157
163
|
# Change Log
|
|
158
164
|
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { removeNull } from '@graphcommerce/graphql'
|
|
2
|
+
import { Image, ImageProps } from '@graphcommerce/image'
|
|
3
|
+
import { AssetFragment } from './Asset.gql'
|
|
4
|
+
|
|
5
|
+
export type { AssetFragment } from './Asset.gql'
|
|
6
|
+
|
|
7
|
+
type ImageAsset = Omit<AssetFragment, 'width' | 'height'> & {
|
|
8
|
+
width: number
|
|
9
|
+
height: number
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function isImage(asset: AssetFragment): asset is ImageAsset {
|
|
13
|
+
return !!(asset.width && asset.height)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
type AssetProps = {
|
|
17
|
+
asset: AssetFragment
|
|
18
|
+
} & Omit<ImageProps, 'src' | 'width' | 'height' | 'alt'>
|
|
19
|
+
|
|
20
|
+
export function Asset(props: AssetProps) {
|
|
21
|
+
const { asset, ...imgProps } = props
|
|
22
|
+
|
|
23
|
+
if (isImage(asset)) {
|
|
24
|
+
const { url, height, mimeType, size, width, ...assetProps } = removeNull(asset)
|
|
25
|
+
return <Image src={url} height={height} width={width} {...imgProps} {...assetProps} />
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (asset.mimeType === 'video/mp4') {
|
|
29
|
+
return <video src={asset.url} autoPlay muted loop playsInline disableRemotePlayback />
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (process.env.NODE_ENV !== 'production') return <div>{asset.mimeType} not supported</div>
|
|
33
|
+
return null
|
|
34
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { SxProps, Theme } from '@mui/material'
|
|
2
|
+
import { defaultRenderers } from './defaultRenderers'
|
|
3
|
+
import { defaultSxRenderer } from './defaultSxRenderer'
|
|
4
|
+
import {
|
|
5
|
+
AdditionalProps,
|
|
6
|
+
Renderers,
|
|
7
|
+
SxRenderer,
|
|
8
|
+
TextNode,
|
|
9
|
+
ElementOrTextNode,
|
|
10
|
+
ElementNode,
|
|
11
|
+
} from './types'
|
|
12
|
+
|
|
13
|
+
const RenderText = ({ classes, text, ...textProps }: TextNode) => {
|
|
14
|
+
let result = <>{text}</>
|
|
15
|
+
if (textProps.bold) result = <strong>{result}</strong>
|
|
16
|
+
if (textProps.italic) result = <em>{result}</em>
|
|
17
|
+
if (textProps.underlined) result = <em>{result}</em>
|
|
18
|
+
|
|
19
|
+
return result
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function isTextNode(node: ElementOrTextNode): node is TextNode {
|
|
23
|
+
return (node as TextNode).text !== undefined
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function isElementNode(node: ElementOrTextNode): node is ElementNode {
|
|
27
|
+
return (node as ElementNode).children !== undefined
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function RenderNode(node: ElementOrTextNode & AdditionalProps) {
|
|
31
|
+
if (isTextNode(node)) {
|
|
32
|
+
return <RenderText {...node} />
|
|
33
|
+
}
|
|
34
|
+
if (isElementNode(node)) {
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
36
|
+
return <RenderElement {...node} />
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
40
|
+
console.error(node)
|
|
41
|
+
throw Error(`RichText: Node not recognized`)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return null
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function RenderChildren({
|
|
48
|
+
childNodes,
|
|
49
|
+
...props
|
|
50
|
+
}: { childNodes: ElementNode['children'] } & AdditionalProps) {
|
|
51
|
+
return (
|
|
52
|
+
<>
|
|
53
|
+
{childNodes.map((node, key) => (
|
|
54
|
+
// Since we don't know any unique identifiers of the element and since this doesn't rerender often this is fine.
|
|
55
|
+
// eslint-disable-next-line react/no-array-index-key
|
|
56
|
+
<RenderNode {...node} {...props} key={key} />
|
|
57
|
+
))}
|
|
58
|
+
</>
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function RenderElement(element: ElementNode & AdditionalProps) {
|
|
63
|
+
const { type, children, sxRenderer, renderers, ...props } = element
|
|
64
|
+
|
|
65
|
+
// todo: this has the any type, could be improved
|
|
66
|
+
const Component = renderers[type]
|
|
67
|
+
const sx = sxRenderer?.[type] ?? []
|
|
68
|
+
|
|
69
|
+
if (Component) {
|
|
70
|
+
return (
|
|
71
|
+
<Component {...props} sx={[sxRenderer.all, ...(Array.isArray(sx) ? sx : [sx])]}>
|
|
72
|
+
<RenderChildren childNodes={children} sxRenderer={sxRenderer} renderers={renderers} />
|
|
73
|
+
</Component>
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
78
|
+
console.error(element)
|
|
79
|
+
throw Error(`RichText: Unknown Element: ${element.type}`)
|
|
80
|
+
}
|
|
81
|
+
return <RenderChildren childNodes={children} sxRenderer={sxRenderer} renderers={renderers} />
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export type RichTextProps = { raw: ElementNode } & {
|
|
85
|
+
renderers?: Partial<Renderers>
|
|
86
|
+
sxRenderer?: SxRenderer
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function mergeSxRenderer(base: SxRenderer, sxRenderer?: SxRenderer) {
|
|
90
|
+
if (!sxRenderer) return base
|
|
91
|
+
|
|
92
|
+
return Object.fromEntries(
|
|
93
|
+
Object.entries<SxProps<Theme>>(base).map(([key, sx]) => {
|
|
94
|
+
const sxOverride: SxProps<Theme> = sxRenderer?.[key]
|
|
95
|
+
|
|
96
|
+
return sxOverride
|
|
97
|
+
? [
|
|
98
|
+
key,
|
|
99
|
+
[
|
|
100
|
+
...(Array.isArray(sx) ? sx : [sx]),
|
|
101
|
+
...(Array.isArray(sxOverride) ? sxOverride : [sxOverride]),
|
|
102
|
+
],
|
|
103
|
+
]
|
|
104
|
+
: [key, sx]
|
|
105
|
+
}),
|
|
106
|
+
) as SxRenderer
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function RichText({ raw, sxRenderer, renderers }: RichTextProps) {
|
|
110
|
+
return (
|
|
111
|
+
<RenderChildren
|
|
112
|
+
childNodes={raw.children}
|
|
113
|
+
sxRenderer={mergeSxRenderer(defaultSxRenderer, sxRenderer)}
|
|
114
|
+
renderers={{ ...defaultRenderers, ...renderers }}
|
|
115
|
+
/>
|
|
116
|
+
)
|
|
117
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Box, Typography, Link } from '@mui/material'
|
|
2
|
+
import PageLink from 'next/link'
|
|
3
|
+
import { Asset } from '../Asset'
|
|
4
|
+
import { Renderers } from './types'
|
|
5
|
+
|
|
6
|
+
export const defaultRenderers: Renderers = {
|
|
7
|
+
'heading-one': (props) => <Typography variant='h1' {...props} />,
|
|
8
|
+
'heading-two': (props) => <Typography variant='h2' {...props} />,
|
|
9
|
+
'heading-three': (props) => <Typography variant='h3' {...props} />,
|
|
10
|
+
'heading-four': (props) => <Typography variant='h4' {...props} />,
|
|
11
|
+
'heading-five': (props) => <Typography variant='h5' {...props} />,
|
|
12
|
+
'heading-six': (props) => <Typography variant='h6' {...props} />,
|
|
13
|
+
paragraph: (props) => <Typography variant='body1' gutterBottom {...props} />,
|
|
14
|
+
'bulleted-list': (props) => <Box component='ul' {...props} />,
|
|
15
|
+
'numbered-list': (props) => <Box component='ol' {...props} />,
|
|
16
|
+
'list-item': (props) => <Box component='li' {...props} />,
|
|
17
|
+
'list-item-child': (props) => <Box component='span' {...props} />,
|
|
18
|
+
'block-quote': (props) => <Box component='blockquote' {...props} />,
|
|
19
|
+
iframe: (props) => (
|
|
20
|
+
// todo add security attributes to iframe
|
|
21
|
+
// todo make iframe responsive (generic IFrame component?)
|
|
22
|
+
<Box component='iframe' title='embedded content' loading='lazy' {...props} />
|
|
23
|
+
),
|
|
24
|
+
image: ({ src, width, height, title, mimeType }) => (
|
|
25
|
+
<Asset asset={{ url: src, alt: title, width, height, mimeType }} />
|
|
26
|
+
),
|
|
27
|
+
video: ({ src, width, height, title, mimeType }) => (
|
|
28
|
+
<Asset asset={{ url: src, alt: title, width, height, mimeType }} />
|
|
29
|
+
),
|
|
30
|
+
link: ({ href, ...props }) => (
|
|
31
|
+
<PageLink href={href} passHref>
|
|
32
|
+
<Link underline='hover' {...props} />
|
|
33
|
+
</PageLink>
|
|
34
|
+
),
|
|
35
|
+
table: (props) => <Box component='table' {...props} />,
|
|
36
|
+
table_head: (props) => <Box component='thead' {...props} />,
|
|
37
|
+
table_header_cell: (props) => <Box component='th' {...props} />,
|
|
38
|
+
table_body: (props) => <Box component='tbody' {...props} />,
|
|
39
|
+
table_row: (props) => <Box component='tr' {...props} />,
|
|
40
|
+
table_cell: (props) => <Box component='td' {...props} />,
|
|
41
|
+
code: (props) => <Box component='code' {...props} />,
|
|
42
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { SxRenderer } from './types'
|
|
2
|
+
|
|
3
|
+
export const defaultSxRenderer: SxRenderer = {
|
|
4
|
+
all: {
|
|
5
|
+
'&:empty': {
|
|
6
|
+
display: 'none',
|
|
7
|
+
},
|
|
8
|
+
'&:last-of-type': { marginBottom: 0 },
|
|
9
|
+
},
|
|
10
|
+
paragraph: {
|
|
11
|
+
marginBottom: '1em',
|
|
12
|
+
wordBreak: 'break-word',
|
|
13
|
+
},
|
|
14
|
+
'heading-one': {
|
|
15
|
+
marginTop: '0.5em',
|
|
16
|
+
marginBottom: '0.5em',
|
|
17
|
+
},
|
|
18
|
+
'heading-two': {
|
|
19
|
+
marginTop: '0.5em',
|
|
20
|
+
marginBottom: '0.5em',
|
|
21
|
+
'&:first-of-type': { marginTop: 0 },
|
|
22
|
+
},
|
|
23
|
+
'heading-three': {
|
|
24
|
+
marginTop: '0.5em',
|
|
25
|
+
marginBottom: '0.5em',
|
|
26
|
+
'&:first-of-type': { marginTop: 0 },
|
|
27
|
+
},
|
|
28
|
+
'heading-four': {
|
|
29
|
+
marginTop: '0.5em',
|
|
30
|
+
marginBottom: '0.5em',
|
|
31
|
+
'&:first-of-type': { marginTop: 0 },
|
|
32
|
+
},
|
|
33
|
+
'heading-five': {
|
|
34
|
+
marginTop: '0.5em',
|
|
35
|
+
marginBottom: '0.5em',
|
|
36
|
+
'&:first-of-type': { marginTop: 0 },
|
|
37
|
+
},
|
|
38
|
+
image: {
|
|
39
|
+
width: '100%',
|
|
40
|
+
height: 'auto',
|
|
41
|
+
},
|
|
42
|
+
video: {
|
|
43
|
+
width: '100%',
|
|
44
|
+
height: 'auto',
|
|
45
|
+
},
|
|
46
|
+
'block-quote': (theme) => ({
|
|
47
|
+
paddingLeft: theme.spacings.sm,
|
|
48
|
+
margin: `${theme.spacings.md} 0`,
|
|
49
|
+
}),
|
|
50
|
+
'bulleted-list': {
|
|
51
|
+
marginBottom: '1em',
|
|
52
|
+
},
|
|
53
|
+
'numbered-list': {
|
|
54
|
+
marginBottom: '1em',
|
|
55
|
+
},
|
|
56
|
+
code: {
|
|
57
|
+
width: 'fit-content',
|
|
58
|
+
maxWidth: '100%',
|
|
59
|
+
padding: 5,
|
|
60
|
+
overflow: 'scroll',
|
|
61
|
+
},
|
|
62
|
+
table: (theme) => ({
|
|
63
|
+
display: 'table',
|
|
64
|
+
width: '100%',
|
|
65
|
+
borderSpacing: '2px',
|
|
66
|
+
borderCollapse: 'collapse',
|
|
67
|
+
marginTop: theme.spacings.md,
|
|
68
|
+
marginBottom: theme.spacings.sm,
|
|
69
|
+
'& thead, tbody': {
|
|
70
|
+
'& td': {
|
|
71
|
+
padding: '10px 20px',
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
'& thead': {
|
|
75
|
+
'& tr': {
|
|
76
|
+
'& td': {
|
|
77
|
+
'& p': {
|
|
78
|
+
fontWeight: theme.typography.fontWeightBold,
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
'& tbody': {
|
|
84
|
+
display: 'table-row-group',
|
|
85
|
+
verticalAlign: 'center',
|
|
86
|
+
borderColor: 'inherit',
|
|
87
|
+
'& tr': {
|
|
88
|
+
'&:nth-of-type(odd)': {
|
|
89
|
+
background: theme.palette.background.paper,
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
'& td': {
|
|
93
|
+
[theme.breakpoints.up('sm')]: {
|
|
94
|
+
minWidth: '150px',
|
|
95
|
+
},
|
|
96
|
+
'& p': {},
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
}),
|
|
100
|
+
link: {
|
|
101
|
+
wordBreak: 'break-word',
|
|
102
|
+
},
|
|
103
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { isElementNode, isTextNode
|
|
1
|
+
import { isElementNode, isTextNode } from './RichText'
|
|
2
|
+
import { ElementOrTextNode } from './types'
|
|
2
3
|
|
|
3
|
-
export
|
|
4
|
+
export function getNodeLength(node: ElementOrTextNode): number {
|
|
4
5
|
if (isElementNode(node))
|
|
5
6
|
return node.children.map(getNodeLength).reduce<number>((prev, curr) => prev + curr, 0)
|
|
6
7
|
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { SxProps, Theme } from '@mui/material'
|
|
2
|
+
import { LiteralUnion } from 'type-fest'
|
|
3
|
+
|
|
4
|
+
type BaseElementTypes =
|
|
5
|
+
| 'heading-one'
|
|
6
|
+
| 'heading-two'
|
|
7
|
+
| 'heading-three'
|
|
8
|
+
| 'heading-four'
|
|
9
|
+
| 'heading-five'
|
|
10
|
+
| 'heading-six'
|
|
11
|
+
| 'paragraph'
|
|
12
|
+
| 'numbered-list'
|
|
13
|
+
| 'bulleted-list'
|
|
14
|
+
| 'block-quote'
|
|
15
|
+
| 'paragraph'
|
|
16
|
+
| 'list-item'
|
|
17
|
+
| 'list-item-child'
|
|
18
|
+
| 'table'
|
|
19
|
+
| 'table_head'
|
|
20
|
+
| 'table_header_cell'
|
|
21
|
+
| 'table_body'
|
|
22
|
+
| 'table_row'
|
|
23
|
+
| 'table_cell'
|
|
24
|
+
| 'code'
|
|
25
|
+
|
|
26
|
+
type SimpleElement = {
|
|
27
|
+
children: ElementOrTextNode[]
|
|
28
|
+
type: LiteralUnion<BaseElementTypes, string>
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type TextNode = {
|
|
32
|
+
text: string
|
|
33
|
+
bold?: true
|
|
34
|
+
italic?: true
|
|
35
|
+
underlined?: true
|
|
36
|
+
[key: string]: unknown
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
type LinkElement = {
|
|
40
|
+
type: 'link'
|
|
41
|
+
children: ElementOrTextNode[]
|
|
42
|
+
href: string
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
type ImageElement = {
|
|
46
|
+
type: 'image'
|
|
47
|
+
children: ElementOrTextNode[]
|
|
48
|
+
src: string
|
|
49
|
+
title: string
|
|
50
|
+
width: number
|
|
51
|
+
height: number
|
|
52
|
+
mimeType: string
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
type VideoElement = {
|
|
56
|
+
type: 'image'
|
|
57
|
+
children: ElementOrTextNode[]
|
|
58
|
+
src: string
|
|
59
|
+
title: string
|
|
60
|
+
width: number
|
|
61
|
+
height: number
|
|
62
|
+
mimeType: string
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
type IframeElement = {
|
|
66
|
+
type: 'iframe'
|
|
67
|
+
children: ElementOrTextNode[]
|
|
68
|
+
src: string
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export type ElementNode = SimpleElement | LinkElement | ImageElement | VideoElement | IframeElement
|
|
72
|
+
export type ElementOrTextNode = ElementNode | TextNode
|
|
73
|
+
|
|
74
|
+
type RendererBase = { sx?: SxProps<Theme>; children?: React.ReactNode }
|
|
75
|
+
type Renderer<P extends ElementNode> = (
|
|
76
|
+
props: Omit<P, 'children'> & RendererBase,
|
|
77
|
+
) => React.ReactElement | null
|
|
78
|
+
|
|
79
|
+
export type Renderers = {
|
|
80
|
+
[k in BaseElementTypes]: Renderer<SimpleElement>
|
|
81
|
+
} & {
|
|
82
|
+
link: Renderer<LinkElement>
|
|
83
|
+
image: Renderer<ImageElement>
|
|
84
|
+
video: Renderer<VideoElement>
|
|
85
|
+
iframe: Renderer<IframeElement>
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export type SxRenderer = {
|
|
89
|
+
[k in keyof Renderers | 'all']?: SxProps<Theme>
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export type AdditionalProps = { renderers: Renderers; sxRenderer: SxRenderer }
|
package/index.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export
|
|
1
|
+
export * from './components/RichText'
|
|
2
|
+
export * from './components/Asset'
|
package/package.json
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@graphcommerce/graphcms-ui",
|
|
3
|
-
"
|
|
3
|
+
"homepage": "https://www.graphcommerce.org/",
|
|
4
|
+
"repository": "github:graphcommerce-org/graphcommerce",
|
|
5
|
+
"version": "3.0.1",
|
|
4
6
|
"sideEffects": false,
|
|
5
7
|
"prettier": "@graphcommerce/prettier-config-pwa",
|
|
6
|
-
"browserslist": [
|
|
7
|
-
"extends @graphcommerce/browserslist-config-pwa"
|
|
8
|
-
],
|
|
9
8
|
"eslintConfig": {
|
|
10
9
|
"extends": "@graphcommerce/eslint-config-pwa",
|
|
11
10
|
"parserOptions": {
|
|
@@ -13,22 +12,19 @@
|
|
|
13
12
|
}
|
|
14
13
|
},
|
|
15
14
|
"devDependencies": {
|
|
16
|
-
"@graphcommerce/
|
|
17
|
-
"@graphcommerce/
|
|
18
|
-
"@graphcommerce/
|
|
19
|
-
"@
|
|
20
|
-
"@playwright/test": "^1.17.1"
|
|
15
|
+
"@graphcommerce/eslint-config-pwa": "^4.0.1",
|
|
16
|
+
"@graphcommerce/prettier-config-pwa": "^4.0.1",
|
|
17
|
+
"@graphcommerce/typescript-config-pwa": "^4.0.1",
|
|
18
|
+
"@playwright/test": "^1.18.1"
|
|
21
19
|
},
|
|
22
20
|
"dependencies": {
|
|
23
|
-
"@
|
|
24
|
-
"@graphcommerce/image": "^
|
|
25
|
-
"@graphcommerce/next-ui": "^
|
|
26
|
-
"@material
|
|
27
|
-
"
|
|
28
|
-
"next": "^12.0.7",
|
|
21
|
+
"@graphcommerce/graphql": "^3.0.1",
|
|
22
|
+
"@graphcommerce/image": "^3.0.1",
|
|
23
|
+
"@graphcommerce/next-ui": "^4.0.1",
|
|
24
|
+
"@mui/material": "^5.4.1",
|
|
25
|
+
"next": "^12.0.10",
|
|
29
26
|
"react": "^17.0.2",
|
|
30
27
|
"react-dom": "^17.0.2",
|
|
31
|
-
"type-fest": "^2.
|
|
32
|
-
}
|
|
33
|
-
"gitHead": "110ddb032ee3ce6e363b9f5f7d21af71fb51b527"
|
|
28
|
+
"type-fest": "^2.11.2"
|
|
29
|
+
}
|
|
34
30
|
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { UseStyles } from '@graphcommerce/next-ui'
|
|
2
|
-
import { makeStyles, Theme } from '@material-ui/core'
|
|
3
|
-
import React from 'react'
|
|
4
|
-
import RichText, { RichTextProps } from '.'
|
|
5
|
-
|
|
6
|
-
type StyleProps = { columnCount: number }
|
|
7
|
-
|
|
8
|
-
const useStyles = makeStyles(({ spacings, breakpoints }: Theme) => ({
|
|
9
|
-
paragraph: {
|
|
10
|
-
[breakpoints.up('md')]: {
|
|
11
|
-
columnCount: ({ columnCount }: StyleProps) => columnCount,
|
|
12
|
-
columnGap: spacings.md,
|
|
13
|
-
},
|
|
14
|
-
},
|
|
15
|
-
}))
|
|
16
|
-
|
|
17
|
-
type RichTextColumnsProps = UseStyles<typeof useStyles> & StyleProps & RichTextProps
|
|
18
|
-
|
|
19
|
-
export default function RichTextColumns(props: RichTextColumnsProps) {
|
|
20
|
-
const classes = useStyles(props)
|
|
21
|
-
return <RichText classes={classes} {...props} />
|
|
22
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { withStyles, Theme } from '@material-ui/core'
|
|
2
|
-
import RichText from '.'
|
|
3
|
-
|
|
4
|
-
const RichTextDoubleSpread = withStyles((theme: Theme) => ({
|
|
5
|
-
h2: theme.typography.h4,
|
|
6
|
-
paragraph: {
|
|
7
|
-
[theme.breakpoints.up('sm')]: {
|
|
8
|
-
columnCount: 2,
|
|
9
|
-
columnGap: theme.spacings.md,
|
|
10
|
-
},
|
|
11
|
-
[theme.breakpoints.up('lg')]: {
|
|
12
|
-
columnCount: 3,
|
|
13
|
-
columnGap: theme.spacings.md,
|
|
14
|
-
},
|
|
15
|
-
},
|
|
16
|
-
}))(RichText)
|
|
17
|
-
|
|
18
|
-
export default RichTextDoubleSpread
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { breakpointVal } from '@graphcommerce/next-ui'
|
|
2
|
-
import { Theme, withStyles } from '@material-ui/core'
|
|
3
|
-
import RichText from '.'
|
|
4
|
-
|
|
5
|
-
const RichTextHeadingStrongStroked = withStyles((theme: Theme) => ({
|
|
6
|
-
h2: {
|
|
7
|
-
...theme.typography.h1,
|
|
8
|
-
textTransform: 'uppercase',
|
|
9
|
-
color: theme.palette.text.primary,
|
|
10
|
-
[theme.breakpoints.up('sm')]: {
|
|
11
|
-
...breakpointVal('fontSize', 36, 82, theme.breakpoints.values),
|
|
12
|
-
},
|
|
13
|
-
'& strong': {
|
|
14
|
-
// https://github.com/rsms/inter/issues/292#issuecomment-674993644
|
|
15
|
-
color: theme.palette.background.default,
|
|
16
|
-
textShadow: `1.2px 0 0 ${theme.palette.text.primary},0 1.2px 0 ${theme.palette.text.primary},-1.2px 0 0 ${theme.palette.text.primary},0 -1.2px 0 ${theme.palette.text.primary}`,
|
|
17
|
-
},
|
|
18
|
-
[theme.breakpoints.up('md')]: {},
|
|
19
|
-
},
|
|
20
|
-
}))(RichText)
|
|
21
|
-
|
|
22
|
-
export default RichTextHeadingStrongStroked
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { breakpointVal } from '@graphcommerce/next-ui'
|
|
2
|
-
import { Theme, withStyles } from '@material-ui/core'
|
|
3
|
-
import RichText from '.'
|
|
4
|
-
|
|
5
|
-
const RichTextHero = withStyles((theme: Theme) => ({
|
|
6
|
-
h1: {
|
|
7
|
-
...theme.typography.h1,
|
|
8
|
-
textTransform: 'uppercase',
|
|
9
|
-
maxWidth: '70%',
|
|
10
|
-
textAlign: 'center',
|
|
11
|
-
margin: 0,
|
|
12
|
-
marginBottom: theme.spacings.md,
|
|
13
|
-
[theme.breakpoints.up('sm')]: {
|
|
14
|
-
...breakpointVal('fontSize', 36, 82, theme.breakpoints.values),
|
|
15
|
-
},
|
|
16
|
-
[theme.breakpoints.up('md')]: {
|
|
17
|
-
textAlign: 'left',
|
|
18
|
-
maxWidth: '100%',
|
|
19
|
-
},
|
|
20
|
-
'& strong': {
|
|
21
|
-
WebkitTextFillColor: 'transparent',
|
|
22
|
-
WebkitTextStroke: `1.2px #fff`,
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
}))(RichText)
|
|
26
|
-
|
|
27
|
-
export default RichTextHero
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { Theme, withStyles } from '@material-ui/core'
|
|
2
|
-
import RichText from '.'
|
|
3
|
-
|
|
4
|
-
const RichTextParagraphStrongStroked = withStyles((theme: Theme) => ({
|
|
5
|
-
paragraph: {
|
|
6
|
-
...theme.typography.body2,
|
|
7
|
-
textTransform: 'uppercase',
|
|
8
|
-
maxWidth: '100%',
|
|
9
|
-
fontWeight: 600,
|
|
10
|
-
textAlign: 'left',
|
|
11
|
-
[theme.breakpoints.up('md')]: {
|
|
12
|
-
...theme.typography.h3,
|
|
13
|
-
fontWeight: 600,
|
|
14
|
-
maxWidth: '100%',
|
|
15
|
-
},
|
|
16
|
-
'& strong': {
|
|
17
|
-
color: 'transparent',
|
|
18
|
-
WebkitTextStroke: '1.2px #fff',
|
|
19
|
-
},
|
|
20
|
-
},
|
|
21
|
-
}))(RichText)
|
|
22
|
-
|
|
23
|
-
export default RichTextParagraphStrongStroked
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { Theme, withStyles } from '@material-ui/core'
|
|
2
|
-
import RichText from '.'
|
|
3
|
-
|
|
4
|
-
const RichTextQuote = withStyles((theme: Theme) => ({
|
|
5
|
-
paragraph: {
|
|
6
|
-
...theme.typography.h4,
|
|
7
|
-
fontWeight: 600,
|
|
8
|
-
'@supports (font-variation-settings: normal)': {
|
|
9
|
-
fontVariationSettings: "'wght' 620",
|
|
10
|
-
},
|
|
11
|
-
textTransform: 'uppercase',
|
|
12
|
-
maxWidth: '60%',
|
|
13
|
-
textAlign: 'center',
|
|
14
|
-
margin: '0 auto',
|
|
15
|
-
[theme.breakpoints.up('lg')]: {
|
|
16
|
-
maxWidth: '80%',
|
|
17
|
-
},
|
|
18
|
-
},
|
|
19
|
-
}))(RichText)
|
|
20
|
-
|
|
21
|
-
export default RichTextQuote
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { withStyles, Theme } from '@material-ui/core'
|
|
2
|
-
import RichText from '.'
|
|
3
|
-
|
|
4
|
-
const RichTextSpread = withStyles((theme: Theme) => ({
|
|
5
|
-
h2: theme.typography.h4,
|
|
6
|
-
paragraph: {
|
|
7
|
-
[theme.breakpoints.up('md')]: {
|
|
8
|
-
columnCount: 2,
|
|
9
|
-
columnGap: theme.spacings.md,
|
|
10
|
-
},
|
|
11
|
-
},
|
|
12
|
-
}))(RichText)
|
|
13
|
-
|
|
14
|
-
export default RichTextSpread
|
package/RichText/index.tsx
DELETED
|
@@ -1,325 +0,0 @@
|
|
|
1
|
-
import { Image } from '@graphcommerce/image'
|
|
2
|
-
import { Link, Typography } from '@material-ui/core'
|
|
3
|
-
import PageLink from 'next/link'
|
|
4
|
-
import React from 'react'
|
|
5
|
-
import useRichTextStyles, { UseRichTextStyles } from './useRichTextStyles'
|
|
6
|
-
|
|
7
|
-
export interface Element {
|
|
8
|
-
children: Node[]
|
|
9
|
-
type:
|
|
10
|
-
| 'heading-one'
|
|
11
|
-
| 'heading-two'
|
|
12
|
-
| 'heading-three'
|
|
13
|
-
| 'heading-four'
|
|
14
|
-
| 'heading-five'
|
|
15
|
-
| 'heading-six'
|
|
16
|
-
| 'paragraph'
|
|
17
|
-
| 'numbered-list'
|
|
18
|
-
| 'bulleted-list'
|
|
19
|
-
| 'block-quote'
|
|
20
|
-
| 'paragraph'
|
|
21
|
-
| 'list-item'
|
|
22
|
-
| 'list-item-child'
|
|
23
|
-
| 'table'
|
|
24
|
-
| 'table_head'
|
|
25
|
-
| 'table_header_cell'
|
|
26
|
-
| 'table_body'
|
|
27
|
-
| 'table_row'
|
|
28
|
-
| 'table_cell'
|
|
29
|
-
| string
|
|
30
|
-
[key: string]: unknown
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export interface Text {
|
|
34
|
-
text: string
|
|
35
|
-
bold?: true
|
|
36
|
-
italic?: true
|
|
37
|
-
underlined?: true
|
|
38
|
-
[key: string]: unknown
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
interface LinkElement extends Element {
|
|
42
|
-
type: 'link'
|
|
43
|
-
href: string
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
interface ImageElement extends Element {
|
|
47
|
-
type: 'image'
|
|
48
|
-
src: string
|
|
49
|
-
title: string
|
|
50
|
-
width: number
|
|
51
|
-
height: number
|
|
52
|
-
mimeType: string
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
interface VideoElement extends Element {
|
|
56
|
-
type: 'image'
|
|
57
|
-
src: string
|
|
58
|
-
title: string
|
|
59
|
-
width: number
|
|
60
|
-
height: number
|
|
61
|
-
mimeType: string
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
interface LinkElement extends Element {
|
|
65
|
-
type: 'link'
|
|
66
|
-
href: string
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
interface IframeElement extends Element {
|
|
70
|
-
type: 'iframe'
|
|
71
|
-
src: string
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export type ElementNode =
|
|
75
|
-
| Element
|
|
76
|
-
| LinkElement
|
|
77
|
-
| ImageElement
|
|
78
|
-
| VideoElement
|
|
79
|
-
| IframeElement
|
|
80
|
-
| LinkElement
|
|
81
|
-
|
|
82
|
-
export type Node = ElementNode | Text
|
|
83
|
-
|
|
84
|
-
const RenderText = ({ classes, text, ...textProps }: Text & Required<UseRichTextStyles>) => {
|
|
85
|
-
let result = <>{text}</>
|
|
86
|
-
if (textProps.bold) result = <strong>{result}</strong>
|
|
87
|
-
if (textProps.italic) result = <em>{result}</em>
|
|
88
|
-
if (textProps.underlined) result = <em>{result}</em>
|
|
89
|
-
|
|
90
|
-
return result
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export function isTextNode(node: Node): node is Text {
|
|
94
|
-
return (node as Text).text !== undefined
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export function isElementNode(node: Node): node is ElementNode {
|
|
98
|
-
return (node as ElementNode).children !== undefined
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function RenderNode({ classes, ...node }: Node & Required<UseRichTextStyles>) {
|
|
102
|
-
if (isTextNode(node)) {
|
|
103
|
-
return <RenderText {...node} classes={classes} />
|
|
104
|
-
}
|
|
105
|
-
if (isElementNode(node)) {
|
|
106
|
-
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
107
|
-
return <RenderElement {...node} classes={classes} />
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
console.error(node)
|
|
111
|
-
throw Error(`RichText: Node not recognized`)
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function RenderChildren({ children, classes }: { children: Node[] } & Required<UseRichTextStyles>) {
|
|
115
|
-
return (
|
|
116
|
-
<>
|
|
117
|
-
{children.map((node, key) => (
|
|
118
|
-
// Since we don't know any unique identifiers of the element and since this doesn't rerender often this is fine.
|
|
119
|
-
// eslint-disable-next-line react/no-array-index-key
|
|
120
|
-
<RenderNode {...node} key={key} classes={classes} />
|
|
121
|
-
))}
|
|
122
|
-
</>
|
|
123
|
-
)
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
function RenderElement({ classes, ...element }: ElementNode & Required<UseRichTextStyles>) {
|
|
127
|
-
const {
|
|
128
|
-
root,
|
|
129
|
-
asset,
|
|
130
|
-
h1,
|
|
131
|
-
h2,
|
|
132
|
-
h3,
|
|
133
|
-
h4,
|
|
134
|
-
h5,
|
|
135
|
-
h6,
|
|
136
|
-
paragraph,
|
|
137
|
-
ul,
|
|
138
|
-
ol,
|
|
139
|
-
blockQuote,
|
|
140
|
-
code,
|
|
141
|
-
iframe,
|
|
142
|
-
aspectContainer,
|
|
143
|
-
table,
|
|
144
|
-
link,
|
|
145
|
-
} = classes
|
|
146
|
-
|
|
147
|
-
switch (element.type) {
|
|
148
|
-
case 'heading-one':
|
|
149
|
-
return (
|
|
150
|
-
<Typography variant='h1' classes={{ root, h1 }}>
|
|
151
|
-
<RenderChildren {...element} classes={classes} />
|
|
152
|
-
</Typography>
|
|
153
|
-
)
|
|
154
|
-
case 'heading-two':
|
|
155
|
-
return (
|
|
156
|
-
<Typography variant='h2' classes={{ root, h2 }}>
|
|
157
|
-
<RenderChildren {...element} classes={classes} />
|
|
158
|
-
</Typography>
|
|
159
|
-
)
|
|
160
|
-
case 'heading-three':
|
|
161
|
-
return (
|
|
162
|
-
<Typography variant='h3' classes={{ root, h3 }}>
|
|
163
|
-
<RenderChildren {...element} classes={classes} />
|
|
164
|
-
</Typography>
|
|
165
|
-
)
|
|
166
|
-
case 'heading-four':
|
|
167
|
-
return (
|
|
168
|
-
<Typography variant='h4' classes={{ root, h4 }}>
|
|
169
|
-
<RenderChildren {...element} classes={classes} />
|
|
170
|
-
</Typography>
|
|
171
|
-
)
|
|
172
|
-
case 'heading-five':
|
|
173
|
-
return (
|
|
174
|
-
<Typography variant='h5' classes={{ root, h5 }}>
|
|
175
|
-
<RenderChildren {...element} classes={classes} />
|
|
176
|
-
</Typography>
|
|
177
|
-
)
|
|
178
|
-
case 'heading-six':
|
|
179
|
-
return (
|
|
180
|
-
<Typography variant='h6' classes={{ root, h6 }}>
|
|
181
|
-
<RenderChildren {...element} classes={classes} />
|
|
182
|
-
</Typography>
|
|
183
|
-
)
|
|
184
|
-
case 'paragraph':
|
|
185
|
-
return element.children[0].code ? (
|
|
186
|
-
<pre className={classes.code}>
|
|
187
|
-
<code>
|
|
188
|
-
<RenderChildren {...element} classes={classes} />
|
|
189
|
-
</code>
|
|
190
|
-
</pre>
|
|
191
|
-
) : (
|
|
192
|
-
<Typography variant='body1' paragraph classes={{ root, paragraph }}>
|
|
193
|
-
<RenderChildren {...element} classes={classes} />
|
|
194
|
-
</Typography>
|
|
195
|
-
)
|
|
196
|
-
|
|
197
|
-
case 'bulleted-list':
|
|
198
|
-
return (
|
|
199
|
-
<Typography component='ul' classes={{ root: ul }}>
|
|
200
|
-
<RenderChildren {...element} classes={classes} />
|
|
201
|
-
</Typography>
|
|
202
|
-
)
|
|
203
|
-
case 'numbered-list':
|
|
204
|
-
return (
|
|
205
|
-
<Typography component='ol' classes={{ root: ol }}>
|
|
206
|
-
<RenderChildren {...element} classes={classes} />
|
|
207
|
-
</Typography>
|
|
208
|
-
)
|
|
209
|
-
case 'list-item':
|
|
210
|
-
return (
|
|
211
|
-
<li>
|
|
212
|
-
<RenderChildren {...element} classes={classes} />
|
|
213
|
-
</li>
|
|
214
|
-
)
|
|
215
|
-
case 'list-item-child':
|
|
216
|
-
return <RenderChildren {...element} classes={classes} />
|
|
217
|
-
case 'block-quote':
|
|
218
|
-
return (
|
|
219
|
-
<Typography component='blockquote' classes={{ root: blockQuote }}>
|
|
220
|
-
<RenderChildren {...element} classes={classes} />
|
|
221
|
-
</Typography>
|
|
222
|
-
)
|
|
223
|
-
case 'iframe':
|
|
224
|
-
// eslint-disable-next-line no-case-declarations
|
|
225
|
-
const iframeElement = element as IframeElement
|
|
226
|
-
// todo(paales) add security attributes to iframe
|
|
227
|
-
// todo(paales) make iframe responsive
|
|
228
|
-
return (
|
|
229
|
-
<div className={aspectContainer}>
|
|
230
|
-
<iframe
|
|
231
|
-
src={iframeElement.src}
|
|
232
|
-
title='embedded content'
|
|
233
|
-
className={iframe}
|
|
234
|
-
loading='lazy'
|
|
235
|
-
/>
|
|
236
|
-
</div>
|
|
237
|
-
)
|
|
238
|
-
case 'image':
|
|
239
|
-
// eslint-disable-next-line no-case-declarations
|
|
240
|
-
const imageElement = element as ImageElement
|
|
241
|
-
return (
|
|
242
|
-
<Image
|
|
243
|
-
src={imageElement.src}
|
|
244
|
-
width={imageElement.width}
|
|
245
|
-
height={imageElement.height}
|
|
246
|
-
alt={imageElement.title}
|
|
247
|
-
className={asset}
|
|
248
|
-
/>
|
|
249
|
-
)
|
|
250
|
-
case 'video':
|
|
251
|
-
// eslint-disable-next-line no-case-declarations
|
|
252
|
-
const videoElement = element as VideoElement
|
|
253
|
-
return (
|
|
254
|
-
<div>IMPLEMENT VIDEO</div>
|
|
255
|
-
// <Asset
|
|
256
|
-
// asset={{ url: videoElement.src, mimeType: videoElement.mimeType }}
|
|
257
|
-
// autoPlay
|
|
258
|
-
// loop
|
|
259
|
-
// muted
|
|
260
|
-
// playsInline
|
|
261
|
-
// width={380}
|
|
262
|
-
// className={asset}
|
|
263
|
-
// />
|
|
264
|
-
)
|
|
265
|
-
case 'link':
|
|
266
|
-
// eslint-disable-next-line no-case-declarations
|
|
267
|
-
const linkElement = element as LinkElement
|
|
268
|
-
return (
|
|
269
|
-
<PageLink href={linkElement.href} passHref>
|
|
270
|
-
<Link classes={{ root: link }}>
|
|
271
|
-
<RenderChildren {...element} classes={classes} />
|
|
272
|
-
</Link>
|
|
273
|
-
</PageLink>
|
|
274
|
-
)
|
|
275
|
-
case 'table':
|
|
276
|
-
return (
|
|
277
|
-
<table className={table}>
|
|
278
|
-
<RenderChildren {...element} classes={classes} />
|
|
279
|
-
</table>
|
|
280
|
-
)
|
|
281
|
-
case 'table_head':
|
|
282
|
-
return (
|
|
283
|
-
<thead>
|
|
284
|
-
<RenderChildren {...element} classes={classes} />
|
|
285
|
-
</thead>
|
|
286
|
-
)
|
|
287
|
-
case 'table_header_cell':
|
|
288
|
-
return (
|
|
289
|
-
<th>
|
|
290
|
-
<RenderChildren {...element} classes={classes} />
|
|
291
|
-
</th>
|
|
292
|
-
)
|
|
293
|
-
case 'table_body':
|
|
294
|
-
return (
|
|
295
|
-
<tbody>
|
|
296
|
-
<RenderChildren {...element} classes={classes} />
|
|
297
|
-
</tbody>
|
|
298
|
-
)
|
|
299
|
-
case 'table_row':
|
|
300
|
-
return (
|
|
301
|
-
<tr>
|
|
302
|
-
<RenderChildren {...element} classes={classes} />
|
|
303
|
-
</tr>
|
|
304
|
-
)
|
|
305
|
-
case 'table_cell':
|
|
306
|
-
return (
|
|
307
|
-
<td>
|
|
308
|
-
<RenderChildren {...element} classes={classes} />
|
|
309
|
-
</td>
|
|
310
|
-
)
|
|
311
|
-
default:
|
|
312
|
-
if (process.env.NODE_ENV !== 'production') {
|
|
313
|
-
console.error(element)
|
|
314
|
-
throw Error(`RichText: Unknown Element: ${element.type}`)
|
|
315
|
-
}
|
|
316
|
-
return <RenderChildren {...element} classes={classes} />
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
export type RichTextProps = { raw: ElementNode } & UseRichTextStyles
|
|
321
|
-
|
|
322
|
-
export default function RichText({ raw, ...props }: RichTextProps) {
|
|
323
|
-
const classes = useRichTextStyles(props)
|
|
324
|
-
return <RenderChildren classes={classes} {...raw} />
|
|
325
|
-
}
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
import { UseStyles, responsiveVal } from '@graphcommerce/next-ui'
|
|
2
|
-
import { makeStyles, Theme } from '@material-ui/core'
|
|
3
|
-
|
|
4
|
-
const useRichTextStyles = makeStyles(
|
|
5
|
-
(theme: Theme) => ({
|
|
6
|
-
root: {
|
|
7
|
-
'&:empty': {
|
|
8
|
-
display: 'none',
|
|
9
|
-
},
|
|
10
|
-
'&:last-child': {
|
|
11
|
-
marginBottom: 0,
|
|
12
|
-
},
|
|
13
|
-
},
|
|
14
|
-
paragraph: {
|
|
15
|
-
marginBottom: '1em',
|
|
16
|
-
wordBreak: 'break-word',
|
|
17
|
-
},
|
|
18
|
-
h1: {
|
|
19
|
-
marginTop: '0.5em',
|
|
20
|
-
marginBottom: '0.5em',
|
|
21
|
-
},
|
|
22
|
-
h2: {
|
|
23
|
-
marginTop: '0.5em',
|
|
24
|
-
marginBottom: '0.5em',
|
|
25
|
-
'&:first-child': { marginTop: 0 },
|
|
26
|
-
},
|
|
27
|
-
h3: {
|
|
28
|
-
marginTop: '0.5em',
|
|
29
|
-
marginBottom: '0.5em',
|
|
30
|
-
'&:first-child': { marginTop: 0 },
|
|
31
|
-
},
|
|
32
|
-
h4: {
|
|
33
|
-
marginTop: responsiveVal(11, 30),
|
|
34
|
-
marginBottom: responsiveVal(11, 30),
|
|
35
|
-
'&:first-child': { marginTop: 0 },
|
|
36
|
-
},
|
|
37
|
-
h5: {
|
|
38
|
-
marginTop: responsiveVal(7, 20),
|
|
39
|
-
marginBottom: responsiveVal(7, 20),
|
|
40
|
-
'&:first-child': { marginTop: 0 },
|
|
41
|
-
},
|
|
42
|
-
h6: {
|
|
43
|
-
'&:first-of-type': {
|
|
44
|
-
marginTop: 0,
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
|
-
asset: {
|
|
48
|
-
width: '100%',
|
|
49
|
-
height: 'auto',
|
|
50
|
-
},
|
|
51
|
-
blockQuote: {
|
|
52
|
-
paddingLeft: theme.spacings.sm,
|
|
53
|
-
margin: `${theme.spacings.md} 0`,
|
|
54
|
-
},
|
|
55
|
-
ol: {
|
|
56
|
-
marginBottom: '1em',
|
|
57
|
-
},
|
|
58
|
-
ul: {
|
|
59
|
-
marginBottom: '1em',
|
|
60
|
-
},
|
|
61
|
-
strong: {},
|
|
62
|
-
italic: {},
|
|
63
|
-
underlined: {},
|
|
64
|
-
code: {
|
|
65
|
-
width: 'fit-content',
|
|
66
|
-
maxWidth: '100%',
|
|
67
|
-
padding: 5,
|
|
68
|
-
overflow: 'scroll',
|
|
69
|
-
},
|
|
70
|
-
iframe: {},
|
|
71
|
-
aspectContainer: {},
|
|
72
|
-
table: {
|
|
73
|
-
display: 'table',
|
|
74
|
-
width: '100%',
|
|
75
|
-
borderSpacing: '2px',
|
|
76
|
-
borderCollapse: 'collapse',
|
|
77
|
-
marginTop: theme.spacings.md,
|
|
78
|
-
marginBottom: theme.spacings.sm,
|
|
79
|
-
'& thead, tbody': {
|
|
80
|
-
'& td': {
|
|
81
|
-
padding: '10px 20px',
|
|
82
|
-
},
|
|
83
|
-
},
|
|
84
|
-
'& thead': {
|
|
85
|
-
'& tr': {
|
|
86
|
-
'& td': {
|
|
87
|
-
'& p': {
|
|
88
|
-
fontWeight: theme.typography.fontWeightBold,
|
|
89
|
-
},
|
|
90
|
-
},
|
|
91
|
-
},
|
|
92
|
-
},
|
|
93
|
-
'& tbody': {
|
|
94
|
-
display: 'table-row-group',
|
|
95
|
-
verticalAlign: 'center',
|
|
96
|
-
borderColor: 'inherit',
|
|
97
|
-
'& tr': {
|
|
98
|
-
'&:nth-child(odd)': {
|
|
99
|
-
background: theme.palette.background.paper,
|
|
100
|
-
},
|
|
101
|
-
},
|
|
102
|
-
'& td': {
|
|
103
|
-
[theme.breakpoints.up('sm')]: {
|
|
104
|
-
minWidth: '150px',
|
|
105
|
-
},
|
|
106
|
-
'& p': {},
|
|
107
|
-
},
|
|
108
|
-
},
|
|
109
|
-
},
|
|
110
|
-
link: {
|
|
111
|
-
wordBreak: 'break-word',
|
|
112
|
-
},
|
|
113
|
-
}),
|
|
114
|
-
{ name: 'RichText' },
|
|
115
|
-
)
|
|
116
|
-
export type UseRichTextStyles = UseStyles<typeof useRichTextStyles>
|
|
117
|
-
|
|
118
|
-
export default useRichTextStyles
|