@graphcommerce/graphcms-ui 3.0.5 → 3.0.6

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 CHANGED
@@ -1,5 +1,14 @@
1
1
  # Change Log
2
2
 
3
+ ## 3.0.6
4
+
5
+ ### Patch Changes
6
+
7
+ - [#1322](https://github.com/graphcommerce-org/graphcommerce/pull/1322) [`ae7385b94`](https://github.com/graphcommerce-org/graphcommerce/commit/ae7385b943e591fcd6fe9e5747f22e03a73e481a) Thanks [@paales](https://github.com/paales)! - RichText component’s first and last element should have no marginTop and marginBottom respectively unless withMargin is specified
8
+
9
+ - Updated dependencies [[`5266388ea`](https://github.com/graphcommerce-org/graphcommerce/commit/5266388eaffda41592623ef7a3ddbbe03c8e0dad), [`9b35403d9`](https://github.com/graphcommerce-org/graphcommerce/commit/9b35403d9dbb2606ac7cf3bb641a0f9cc3d8a2ba), [`0298a0de1`](https://github.com/graphcommerce-org/graphcommerce/commit/0298a0de1d13e543c4124a6a099297b4e27e2b05), [`815132ea4`](https://github.com/graphcommerce-org/graphcommerce/commit/815132ea43937b4b84b59ec9974ac593cb4eb456), [`3326742a0`](https://github.com/graphcommerce-org/graphcommerce/commit/3326742a0dceb45f0cac4741ca09dc4d4f09ad90), [`7a3799bfc`](https://github.com/graphcommerce-org/graphcommerce/commit/7a3799bfc107f26aa9991a91db5f228e3476f4aa), [`9a77f88ed`](https://github.com/graphcommerce-org/graphcommerce/commit/9a77f88ed26cbecdae9a135c3cb234a5b7ecf4df), [`0eeaad304`](https://github.com/graphcommerce-org/graphcommerce/commit/0eeaad30461b1d5b486438f0287fa76d49429044), [`bc5213547`](https://github.com/graphcommerce-org/graphcommerce/commit/bc52135471479c83d989449dad24798112e898f4), [`3f1912f55`](https://github.com/graphcommerce-org/graphcommerce/commit/3f1912f553318d5888f8af2b841918ef4ae96a84), [`b6c68cda8`](https://github.com/graphcommerce-org/graphcommerce/commit/b6c68cda8836a1d0c78ef351899cec9ec1037385)]:
10
+ - @graphcommerce/next-ui@4.3.0
11
+
3
12
  ## 3.0.5
4
13
 
5
14
  ### Patch Changes
@@ -1,22 +1,59 @@
1
+ /* eslint-disable @typescript-eslint/no-use-before-define */
1
2
  import { SxProps, Theme } from '@mui/material'
2
3
  import { defaultRenderers } from './defaultRenderers'
3
4
  import { defaultSxRenderer } from './defaultSxRenderer'
4
5
  import {
5
6
  AdditionalProps,
6
7
  Renderers,
8
+ Renderer,
7
9
  SxRenderer,
8
10
  TextNode,
9
11
  ElementOrTextNode,
10
12
  ElementNode,
13
+ SimpleElement,
11
14
  } from './types'
12
15
 
13
- function 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>
16
+ const sxArr = (sxAny?: SxProps<Theme> | false) => {
17
+ if (!sxAny) return []
18
+ return Array.isArray(sxAny) ? sxAny : [sxAny]
19
+ }
20
+
21
+ function useRenderProps(
22
+ { first, last, sxRenderer }: Pick<AdditionalProps, 'first' | 'last' | 'sxRenderer'>,
23
+ type?: ElementNode['type'],
24
+ ) {
25
+ if (!type) return []
26
+ const sx: SxProps<Theme> = sxRenderer?.[type] ?? []
27
+
28
+ return [
29
+ ...sxArr(sxRenderer.all),
30
+ ...sxArr(sx),
31
+ ...sxArr(first && sxRenderer.first),
32
+ ...sxArr(last && sxRenderer.last),
33
+ ]
34
+ }
35
+
36
+ function RenderText({
37
+ text,
38
+ renderers,
39
+ sxRenderer,
40
+ first,
41
+ last,
42
+ ...textProps
43
+ }: TextNode & AdditionalProps) {
44
+ let type: 'bold' | 'italic' | 'underlined' | undefined
45
+
46
+ if (textProps.bold) type = 'bold'
47
+ if (textProps.italic) type = 'italic'
48
+ if (textProps.underlined) type = 'underlined'
18
49
 
19
- return result
50
+ const sx = useRenderProps({ first, last, sxRenderer }, type)
51
+
52
+ if (!type) return <>{text}</>
53
+
54
+ const Component: Renderer<SimpleElement> = renderers[type]
55
+
56
+ return <Component sx={sx}>{text}</Component>
20
57
  }
21
58
 
22
59
  export function isTextNode(node: ElementOrTextNode): node is TextNode {
@@ -46,29 +83,37 @@ function RenderNode(node: ElementOrTextNode & AdditionalProps) {
46
83
 
47
84
  function RenderChildren({
48
85
  childNodes,
86
+ noMargin,
49
87
  ...props
50
- }: { childNodes: ElementNode['children'] } & AdditionalProps) {
88
+ }: { childNodes: ElementNode['children']; noMargin?: boolean } & AdditionalProps) {
51
89
  return (
52
90
  <>
53
91
  {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} />
92
+ <RenderNode
93
+ {...node}
94
+ {...props}
95
+ // Since we don't know any unique identifiers of the element and since this doesn't rerender often this is fine.
96
+ // eslint-disable-next-line react/no-array-index-key
97
+ key={key}
98
+ first={noMargin && key === 0}
99
+ last={noMargin && key === childNodes.length - 1}
100
+ />
57
101
  ))}
58
102
  </>
59
103
  )
60
104
  }
61
105
 
62
106
  function RenderElement(element: ElementNode & AdditionalProps) {
63
- const { type, children, sxRenderer, renderers, ...props } = element
107
+ const { type, children, sxRenderer, renderers, first, last, ...props } = element
64
108
 
65
109
  // todo: this has the any type, could be improved
66
- const Component = renderers[type]
67
- const sx = sxRenderer?.[type] ?? []
110
+ const Component: Renderer<SimpleElement> = renderers[type]
111
+
112
+ const sx = useRenderProps({ first, last, sxRenderer }, type)
68
113
 
69
114
  if (Component) {
70
115
  return (
71
- <Component {...props} sx={[sxRenderer.all, ...(Array.isArray(sx) ? sx : [sx])]}>
116
+ <Component {...props} sx={sx}>
72
117
  <RenderChildren childNodes={children} sxRenderer={sxRenderer} renderers={renderers} />
73
118
  </Component>
74
119
  )
@@ -81,12 +126,7 @@ function RenderElement(element: ElementNode & AdditionalProps) {
81
126
  return <RenderChildren childNodes={children} sxRenderer={sxRenderer} renderers={renderers} />
82
127
  }
83
128
 
84
- export type RichTextProps = { raw: ElementNode } & {
85
- renderers?: Partial<Renderers>
86
- sxRenderer?: SxRenderer
87
- }
88
-
89
- export function mergeSxRenderer(base: SxRenderer, sxRenderer?: SxRenderer) {
129
+ function mergeSxRenderer(base: SxRenderer, sxRenderer?: SxRenderer) {
90
130
  if (!sxRenderer) return base
91
131
 
92
132
  return Object.fromEntries(
@@ -106,12 +146,38 @@ export function mergeSxRenderer(base: SxRenderer, sxRenderer?: SxRenderer) {
106
146
  ) as SxRenderer
107
147
  }
108
148
 
109
- export function RichText({ raw, sxRenderer, renderers }: RichTextProps) {
149
+ export type RichTextProps = { raw: ElementNode } & {
150
+ renderers?: Partial<Renderers>
151
+ /**
152
+ * Allows you to theme all the types of components
153
+ *
154
+ * ```tsx
155
+ * function MyComponent()f {
156
+ * return <RichText
157
+ * sxRenderer={{
158
+ * paragraph: (theme) => ({
159
+ * columnCount: { xs: 1, md: getColumnCount(props, 2) },
160
+ * columnGap: theme.spacings.md,
161
+ * }),
162
+ * //other props here
163
+ * }}
164
+ * />
165
+ * }
166
+ * ```
167
+ */
168
+ sxRenderer?: SxRenderer
169
+
170
+ /** By default the component will render the first and last element without any margins */
171
+ withMargin?: boolean
172
+ }
173
+
174
+ export function RichText({ raw, sxRenderer, renderers, withMargin = false }: RichTextProps) {
110
175
  return (
111
176
  <RenderChildren
112
177
  childNodes={raw.children}
113
178
  sxRenderer={mergeSxRenderer(defaultSxRenderer, sxRenderer)}
114
179
  renderers={{ ...defaultRenderers, ...renderers }}
180
+ noMargin={!withMargin}
115
181
  />
116
182
  )
117
183
  }
@@ -39,4 +39,7 @@ export const defaultRenderers: Renderers = {
39
39
  table_row: (props) => <Box component='tr' {...props} />,
40
40
  table_cell: (props) => <Box component='td' {...props} />,
41
41
  code: (props) => <Box component='code' {...props} />,
42
+ bold: (props) => <Box component='strong' fontWeight='bold' {...props} />,
43
+ italic: (props) => <Box component='em' fontStyle='italic' {...props} />,
44
+ underlined: (props) => <Box component='span' {...props} />,
42
45
  }
@@ -5,7 +5,12 @@ export const defaultSxRenderer: SxRenderer = {
5
5
  '&:empty': {
6
6
  display: 'none',
7
7
  },
8
- '&:last-of-type': { marginBottom: 0 },
8
+ },
9
+ first: {
10
+ marginTop: 0,
11
+ },
12
+ last: {
13
+ marginBottom: 0,
9
14
  },
10
15
  paragraph: {
11
16
  marginBottom: '1em',
@@ -18,22 +23,18 @@ export const defaultSxRenderer: SxRenderer = {
18
23
  'heading-two': {
19
24
  marginTop: '0.5em',
20
25
  marginBottom: '0.5em',
21
- '&:first-of-type': { marginTop: 0 },
22
26
  },
23
27
  'heading-three': {
24
28
  marginTop: '0.5em',
25
29
  marginBottom: '0.5em',
26
- '&:first-of-type': { marginTop: 0 },
27
30
  },
28
31
  'heading-four': {
29
32
  marginTop: '0.5em',
30
33
  marginBottom: '0.5em',
31
- '&:first-of-type': { marginTop: 0 },
32
34
  },
33
35
  'heading-five': {
34
36
  marginTop: '0.5em',
35
37
  marginBottom: '0.5em',
36
- '&:first-of-type': { marginTop: 0 },
37
38
  },
38
39
  image: {
39
40
  width: '100%',
@@ -100,4 +101,7 @@ export const defaultSxRenderer: SxRenderer = {
100
101
  link: {
101
102
  wordBreak: 'break-word',
102
103
  },
104
+ underlined: {
105
+ textDecoration: 'underline',
106
+ },
103
107
  }
@@ -22,8 +22,11 @@ type BaseElementTypes =
22
22
  | 'table_row'
23
23
  | 'table_cell'
24
24
  | 'code'
25
+ | 'bold'
26
+ | 'italic'
27
+ | 'underlined'
25
28
 
26
- type SimpleElement = {
29
+ export type SimpleElement = {
27
30
  children: ElementOrTextNode[]
28
31
  type: LiteralUnion<BaseElementTypes, string>
29
32
  }
@@ -72,8 +75,8 @@ export type ElementNode = SimpleElement | LinkElement | ImageElement | VideoElem
72
75
  export type ElementOrTextNode = ElementNode | TextNode
73
76
 
74
77
  type RendererBase = { sx?: SxProps<Theme>; children?: React.ReactNode }
75
- type Renderer<P extends ElementNode> = (
76
- props: Omit<P, 'children'> & RendererBase,
78
+ export type Renderer<P extends ElementNode> = (
79
+ props: Omit<P, 'children' | 'type'> & RendererBase,
77
80
  ) => React.ReactElement | null
78
81
 
79
82
  export type Renderers = {
@@ -86,7 +89,12 @@ export type Renderers = {
86
89
  }
87
90
 
88
91
  export type SxRenderer = {
89
- [k in keyof Renderers | 'all']?: SxProps<Theme>
92
+ [k in keyof Renderers | 'all' | 'first' | 'last']?: SxProps<Theme>
90
93
  }
91
94
 
92
- export type AdditionalProps = { renderers: Renderers; sxRenderer: SxRenderer }
95
+ export type AdditionalProps = {
96
+ renderers: Renderers
97
+ sxRenderer: SxRenderer
98
+ first?: boolean
99
+ last?: boolean
100
+ }
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@graphcommerce/graphcms-ui",
3
3
  "homepage": "https://www.graphcommerce.org/",
4
4
  "repository": "github:graphcommerce-org/graphcommerce",
5
- "version": "3.0.5",
5
+ "version": "3.0.6",
6
6
  "sideEffects": false,
7
7
  "prettier": "@graphcommerce/prettier-config-pwa",
8
8
  "eslintConfig": {
@@ -20,7 +20,7 @@
20
20
  "dependencies": {
21
21
  "@graphcommerce/graphql": "^3.0.4",
22
22
  "@graphcommerce/image": "^3.1.1",
23
- "@graphcommerce/next-ui": "^4.2.4",
23
+ "@graphcommerce/next-ui": "^4.3.0",
24
24
  "type-fest": "^2.12.0"
25
25
  },
26
26
  "peerDependencies": {