@graphcommerce/next-ui 4.14.0 → 4.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,45 @@
1
1
  # Change Log
2
2
 
3
+ ## 4.16.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#1573](https://github.com/graphcommerce-org/graphcommerce/pull/1573) [`1eb131766`](https://github.com/graphcommerce-org/graphcommerce/commit/1eb131766c32db6fcb0a8e83dba2c3d241658595) Thanks [@paales](https://github.com/paales)! - Solve issue where the products query would return multiple products while requesting a single url_key. Filter the result by findByTypename which finds the correct `typename` but also narrows the typescript type.
8
+
9
+ ### Patch Changes
10
+
11
+ - [#1573](https://github.com/graphcommerce-org/graphcommerce/pull/1573) [`87a188d6f`](https://github.com/graphcommerce-org/graphcommerce/commit/87a188d6f216b7f7b9ec95afbe74f1146cb07ce4) Thanks [@paales](https://github.com/paales)! - Sovle issue where changing images in the scroller causes issues rerendering
12
+
13
+ - Updated dependencies [[`87a188d6f`](https://github.com/graphcommerce-org/graphcommerce/commit/87a188d6f216b7f7b9ec95afbe74f1146cb07ce4)]:
14
+ - @graphcommerce/framer-scroller@2.1.27
15
+
16
+ ## 4.15.1
17
+
18
+ ### Patch Changes
19
+
20
+ - [#1570](https://github.com/graphcommerce-org/graphcommerce/pull/1570) [`a88f166f0`](https://github.com/graphcommerce-org/graphcommerce/commit/a88f166f0115c58254fe47171da51a5850658a32) Thanks [@paales](https://github.com/paales)! - Solve issue where chrome would report duplicate ids
21
+
22
+ - Updated dependencies []:
23
+ - @graphcommerce/framer-scroller@2.1.26
24
+
25
+ ## 4.15.0
26
+
27
+ ### Minor Changes
28
+
29
+ - [#1566](https://github.com/graphcommerce-org/graphcommerce/pull/1566) [`e167992df`](https://github.com/graphcommerce-org/graphcommerce/commit/e167992dfdc6964a392af719667f8a188626ab1b) Thanks [@ErwinOtten](https://github.com/ErwinOtten)! - Introduced `@graphcommerce/next-ui/navigation` component.
30
+
31
+ - Navigation is always present in the DOM
32
+ - Configurable in LayoutNavigation.tsx
33
+ - Show categories directly, or nest them in a 'products' button
34
+ - Choose prefered mouseEvent: click or hover
35
+
36
+ * [#1566](https://github.com/graphcommerce-org/graphcommerce/pull/1566) [`9c2504b4e`](https://github.com/graphcommerce-org/graphcommerce/commit/9c2504b4ed75f41d3003c4d3339814010e85e37e) Thanks [@ErwinOtten](https://github.com/ErwinOtten)! - publish navigation
37
+
38
+ ### Patch Changes
39
+
40
+ - Updated dependencies []:
41
+ - @graphcommerce/framer-scroller@2.1.25
42
+
3
43
  ## 4.14.0
4
44
 
5
45
  ### Minor Changes
@@ -211,7 +211,7 @@ export function SidebarGallery(props: SidebarGalleryProps) {
211
211
  ))}
212
212
  </Scroller>
213
213
  <MotionBox
214
- layout
214
+ layout='position'
215
215
  layoutDependency={zoomed}
216
216
  className={classes.topRight}
217
217
  sx={{
@@ -293,7 +293,7 @@ export function SidebarGallery(props: SidebarGalleryProps) {
293
293
  }}
294
294
  >
295
295
  <ScrollerDots
296
- layout
296
+ layout='position'
297
297
  layoutDependency={zoomed}
298
298
  sx={{ backgroundColor: 'background.paper', boxShadow: 6 }}
299
299
  />
@@ -328,7 +328,7 @@ export function SidebarGallery(props: SidebarGalleryProps) {
328
328
  ]}
329
329
  >
330
330
  <MotionBox
331
- layout
331
+ layout='position'
332
332
  layoutDependency={zoomed}
333
333
  className={classes.sidebar}
334
334
  sx={{
package/JsonLd/JsonLd.tsx CHANGED
@@ -1,19 +1,21 @@
1
1
  import Head from 'next/head'
2
- import { jsonLdScriptProps } from 'react-schemaorg'
3
- import { Thing, WithContext } from 'schema-dts'
2
+ import { safeJsonLdReplacer } from './safeJsonLdReplacer'
4
3
 
5
4
  export * as SchemaDts from 'schema-dts'
6
5
 
7
- export type JsonLdProps<T extends Thing> = {
8
- item: WithContext<T>
9
- }
10
-
11
- export function JsonLd<T extends Thing>(props: JsonLdProps<T>) {
6
+ export function JsonLd<T extends { '@type': string }>(props: {
7
+ item: T & { '@context': 'https://schema.org' }
8
+ }) {
12
9
  const { item } = props
13
10
 
14
11
  return (
15
12
  <Head>
16
- <script key='jsonld' {...jsonLdScriptProps<T>(item)} />
13
+ <script
14
+ key='jsonld'
15
+ type='application/ld+json'
16
+ // eslint-disable-next-line react/no-danger
17
+ dangerouslySetInnerHTML={{ __html: JSON.stringify(item, safeJsonLdReplacer) }}
18
+ />
17
19
  </Head>
18
20
  )
19
21
  }
@@ -0,0 +1,47 @@
1
+ type JsonValueScalar = string | boolean | number
2
+ type JsonValue = JsonValueScalar | Array<JsonValue> | { [key: string]: JsonValue }
3
+ type JsonReplacer = (_: string, value: JsonValue) => JsonValue | undefined
4
+
5
+ const ESCAPE_ENTITIES = Object.freeze({
6
+ '&': '&amp;',
7
+ '<': '&lt;',
8
+ '>': '&gt;',
9
+ '"': '&quot;',
10
+ "'": '&apos;',
11
+ })
12
+ const ESCAPE_REGEX = new RegExp(`[${Object.keys(ESCAPE_ENTITIES).join('')}]`, 'g')
13
+ const ESCAPE_REPLACER = (t: string): string => ESCAPE_ENTITIES[t as keyof typeof ESCAPE_ENTITIES]
14
+
15
+ // Utility: Assert never
16
+ function isNever(_: never): void {}
17
+
18
+ /**
19
+ * A replacer for JSON.stringify to strip JSON-LD of illegal HTML entities per
20
+ * https://www.w3.org/TR/json-ld11/#restrictions-for-contents-of-json-ld-script-elements
21
+ */
22
+ export const safeJsonLdReplacer: JsonReplacer = (
23
+ () =>
24
+ // Replace per https://www.w3.org/TR/json-ld11/#restrictions-for-contents-of-json-ld-script-elements
25
+ // Solution from https://stackoverflow.com/a/5499821/864313
26
+ (_: string, value: JsonValue): JsonValue | undefined => {
27
+ switch (typeof value) {
28
+ case 'object':
29
+ // Omit null values.
30
+ if (value === null) return undefined
31
+ return value // JSON.stringify will recursively call replacer.
32
+ case 'number':
33
+ case 'boolean':
34
+ case 'bigint':
35
+ return value // These values are not risky.
36
+ case 'string':
37
+ return value.replace(ESCAPE_REGEX, ESCAPE_REPLACER)
38
+ default: {
39
+ // We shouldn't expect other types.
40
+ isNever(value)
41
+
42
+ // JSON.stringify will remove this element.
43
+ return undefined
44
+ }
45
+ }
46
+ }
47
+ )()
@@ -3,7 +3,7 @@ import { LayoutOverlay, LayoutOverlayProps } from '../components/LayoutOverlay'
3
3
 
4
4
  export type LayoutOverlayState = Omit<
5
5
  LayoutOverlayProps,
6
- 'children' | 'sx' | 'sxBackdrop' | 'mdSpacingTop' | 'smSpacingTop'
6
+ 'children' | 'sx' | 'sxBackdrop' | 'mdSpacingTop' | 'smSpacingTop' | 'overlayPaneProps'
7
7
  >
8
8
 
9
9
  export function useLayoutState() {
@@ -1,6 +1,7 @@
1
1
  /* eslint-disable @typescript-eslint/no-use-before-define */
2
- import { Box, ListItemButton, styled, useEventCallback } from '@mui/material'
2
+ import { Box, ListItemButton, styled, Theme, useEventCallback, useMediaQuery } from '@mui/material'
3
3
  import PageLink from 'next/link'
4
+ import { useEffect } from 'react'
4
5
  import { IconSvg } from '../../IconSvg'
5
6
  import { extendableComponent } from '../../Styles/extendableComponent'
6
7
  import { iconChevronRight } from '../../icons'
@@ -26,7 +27,12 @@ type NavigationItemProps = NavigationNode & {
26
27
  parentPath: NavigationPath
27
28
  idx: number
28
29
  NavigationList: typeof NavigationList
29
- } & OwnerState
30
+ } & OwnerState &
31
+ mouseEventPref
32
+
33
+ export type mouseEventPref = {
34
+ mouseEvent: 'click' | 'hover'
35
+ }
30
36
 
31
37
  const componentName = 'NavigationItem'
32
38
  const parts = ['li', 'ul', 'item'] as const
@@ -39,10 +45,10 @@ const { withState } = extendableComponent<OwnerState, typeof componentName, type
39
45
  const NavigationLI = styled('li')({ display: 'contents' })
40
46
 
41
47
  export function NavigationItem(props: NavigationItemProps) {
42
- const { id, parentPath, idx, first, last, NavigationList } = props
48
+ const { id, parentPath, idx, first, last, NavigationList, mouseEvent } = props
43
49
 
44
50
  const row = idx + 1
45
- const { selected, select, hideRootOnNavigate, onClose } = useNavigation()
51
+ const { selected, select, hideRootOnNavigate, onClose, animating } = useNavigation()
46
52
 
47
53
  const itemPath = [...parentPath, id]
48
54
  const isSelected = selected.slice(0, itemPath.length).join('/') === itemPath.join('/')
@@ -59,6 +65,8 @@ export function NavigationItem(props: NavigationItemProps) {
59
65
  onClose?.(e, href)
60
66
  })
61
67
 
68
+ const isDesktop = useMediaQuery<Theme>((theme) => theme.breakpoints.up('md'))
69
+
62
70
  if (isNavigationButton(props)) {
63
71
  const { childItems, name } = props
64
72
  return (
@@ -66,18 +74,44 @@ export function NavigationItem(props: NavigationItemProps) {
66
74
  <ListItemButton
67
75
  className={classes.item}
68
76
  role='button'
69
- sx={{
70
- gridRowStart: row,
71
- gridColumnStart: column,
72
- gap: (theme) => theme.spacings.xxs,
73
- display: hideItem ? 'none' : 'flex',
74
- }}
77
+ sx={[
78
+ (theme) => ({
79
+ gridRowStart: row,
80
+ gridColumnStart: column,
81
+ gap: theme.spacings.xxs,
82
+ display: hideItem ? 'none' : 'flex',
83
+ '&.Mui-disabled': {
84
+ opacity: 1,
85
+ background: theme.palette.action.hover,
86
+ },
87
+ }),
88
+ mouseEvent === 'hover'
89
+ ? {
90
+ '&.Mui-disabled': {
91
+ cursor: 'pointer',
92
+ pointerEvents: 'auto',
93
+ },
94
+ }
95
+ : {},
96
+ ]}
75
97
  disabled={isSelected}
76
98
  tabIndex={selected.join(',').includes(parentPath.join(',')) ? undefined : -1}
77
99
  onClick={(e) => {
78
100
  e.preventDefault()
79
- if (!isSelected) select(itemPath)
101
+ if (!isSelected && animating.current === false) {
102
+ select(itemPath)
103
+ }
80
104
  }}
105
+ onMouseEnter={
106
+ itemPath.length > 1 && mouseEvent === 'hover'
107
+ ? (e) => {
108
+ if (isDesktop && animating.current === false && !isSelected) {
109
+ e.preventDefault()
110
+ setTimeout(() => select(itemPath), 0)
111
+ }
112
+ }
113
+ : undefined
114
+ }
81
115
  >
82
116
  <Box
83
117
  component='span'
@@ -93,7 +127,12 @@ export function NavigationItem(props: NavigationItemProps) {
93
127
  <IconSvg src={iconChevronRight} sx={{ flexShrink: 0 }} />
94
128
  </ListItemButton>
95
129
 
96
- <NavigationList items={childItems} selected={isSelected} parentPath={itemPath} />
130
+ <NavigationList
131
+ items={childItems}
132
+ selected={isSelected}
133
+ parentPath={itemPath}
134
+ mouseEvent={mouseEvent}
135
+ />
97
136
  </NavigationLI>
98
137
  )
99
138
  }
@@ -1,7 +1,7 @@
1
1
  import { styled } from '@mui/material'
2
2
  import { extendableComponent } from '../../Styles/extendableComponent'
3
3
  import { NavigationNode, NavigationPath } from '../hooks/useNavigation'
4
- import { NavigationItem } from './NavigationItem'
4
+ import { NavigationItem, mouseEventPref } from './NavigationItem'
5
5
 
6
6
  const NavigationUList = styled('ul')({})
7
7
 
@@ -9,7 +9,7 @@ type NavigationItemsProps = {
9
9
  parentPath?: NavigationPath
10
10
  items: NavigationNode[]
11
11
  selected?: boolean
12
- }
12
+ } & mouseEventPref
13
13
 
14
14
  type OwnerState = {
15
15
  column: number
@@ -23,7 +23,7 @@ const { withState } = extendableComponent<OwnerState, typeof name, typeof parts>
23
23
  // const parts = ['li', 'ul', 'item'] as const
24
24
 
25
25
  export function NavigationList(props: NavigationItemsProps) {
26
- const { items, parentPath = [], selected = false } = props
26
+ const { items, parentPath = [], selected = false, mouseEvent } = props
27
27
 
28
28
  return (
29
29
  <NavigationUList
@@ -43,6 +43,7 @@ export function NavigationList(props: NavigationItemsProps) {
43
43
  first={idx === 0}
44
44
  last={idx === items.length - 1}
45
45
  column={0}
46
+ mouseEvent={mouseEvent}
46
47
  />
47
48
  ))}
48
49
  </NavigationUList>
@@ -3,6 +3,7 @@ import { i18n } from '@lingui/core'
3
3
  import { Trans } from '@lingui/react'
4
4
  import { Box, Fab, SxProps, Theme, useEventCallback, useMediaQuery } from '@mui/material'
5
5
  import { m } from 'framer-motion'
6
+ import { useState } from 'react'
6
7
  import { IconSvg, useIconSvgSize } from '../../IconSvg'
7
8
  import { LayoutHeaderContent } from '../../Layout/components/LayoutHeaderContent'
8
9
  import { LayoutTitle } from '../../Layout/components/LayoutTitle'
@@ -18,14 +19,26 @@ import {
18
19
  NavigationNodeHref,
19
20
  useNavigation,
20
21
  } from '../hooks/useNavigation'
22
+ import { mouseEventPref } from './NavigationItem'
21
23
  import { NavigationList } from './NavigationList'
22
24
 
25
+ type LayoutOverlayVariant = 'left' | 'bottom' | 'right'
26
+ type LayoutOverlaySize = 'floating' | 'minimal' | 'full'
27
+ type LayoutOverlayAlign = 'start' | 'end' | 'center' | 'stretch'
28
+
23
29
  type NavigationOverlayProps = {
24
30
  active: boolean
25
31
  sx?: SxProps<Theme>
26
32
  stretchColumns?: boolean
27
- itemWidth: string
28
- }
33
+ variantSm: LayoutOverlayVariant
34
+ variantMd: LayoutOverlayVariant
35
+ sizeSm?: LayoutOverlaySize
36
+ sizeMd?: LayoutOverlaySize
37
+ justifySm?: LayoutOverlayAlign
38
+ justifyMd?: LayoutOverlayAlign
39
+ itemWidthSm?: string
40
+ itemWidthMd?: string
41
+ } & mouseEventPref
29
42
 
30
43
  function findCurrent(
31
44
  items: NavigationContextType['items'],
@@ -55,8 +68,21 @@ const parts = ['root', 'navigation', 'header', 'column'] as const
55
68
  const { classes } = extendableComponent(componentName, parts)
56
69
 
57
70
  export function NavigationOverlay(props: NavigationOverlayProps) {
58
- const { active, sx, stretchColumns, itemWidth } = props
59
- const { selected, select, items, onClose } = useNavigation()
71
+ const {
72
+ active,
73
+ sx,
74
+ stretchColumns,
75
+ variantMd,
76
+ variantSm,
77
+ justifyMd,
78
+ justifySm,
79
+ sizeMd,
80
+ sizeSm,
81
+ itemWidthSm,
82
+ itemWidthMd,
83
+ mouseEvent,
84
+ } = props
85
+ const { selected, select, items, onClose, animating } = useNavigation()
60
86
 
61
87
  const fabSize = useFabSize('responsive')
62
88
  const svgSize = useIconSvgSize('large')
@@ -74,12 +100,20 @@ export function NavigationOverlay(props: NavigationOverlayProps) {
74
100
  className={classes.root}
75
101
  active={active}
76
102
  onClosed={onClose}
77
- variantSm='left'
78
- sizeSm='floating'
79
- justifySm='start'
80
- variantMd='left'
81
- sizeMd='floating'
82
- justifyMd='start'
103
+ variantSm={variantSm}
104
+ sizeSm={sizeSm}
105
+ justifySm={justifySm}
106
+ variantMd={variantMd}
107
+ sizeMd={sizeMd}
108
+ justifyMd={justifyMd}
109
+ overlayPaneProps={{
110
+ onLayoutAnimationStart: () => {
111
+ animating.current = true
112
+ },
113
+ onLayoutAnimationComplete: () => {
114
+ animating.current = false
115
+ },
116
+ }}
83
117
  sx={{
84
118
  zIndex: 'drawer',
85
119
  '& .LayoutOverlayBase-overlayPane': {
@@ -151,14 +185,35 @@ export function NavigationOverlay(props: NavigationOverlayProps) {
151
185
  sx={(theme) => ({
152
186
  display: 'grid',
153
187
  alignItems: !stretchColumns ? 'start' : undefined,
154
-
188
+ '& .NavigationItem-item': {
189
+ // eslint-disable-next-line no-nested-ternary
190
+ width: itemWidthMd
191
+ ? selected.length >= 1
192
+ ? `calc(${itemWidthMd} + 1px)`
193
+ : itemWidthMd
194
+ : 'auto',
195
+ },
155
196
  [theme.breakpoints.down('md')]: {
197
+ width:
198
+ sizeSm !== 'floating'
199
+ ? `calc(${itemWidthSm || '100vw'} + ${selected.length}px)`
200
+ : `calc(${itemWidthSm || '100vw'} - ${theme.page.horizontal} - ${
201
+ theme.page.horizontal
202
+ })`,
203
+ minWidth: 200,
156
204
  overflow: 'hidden',
157
205
  scrollSnapType: 'x mandatory',
158
- width: `calc(${theme.spacings.md} + ${theme.spacings.md} + ${itemWidth})`,
159
- },
160
- '& .NavigationItem-item': {
161
- width: itemWidth,
206
+ '& .NavigationItem-item': {
207
+ width:
208
+ sizeSm !== 'floating'
209
+ ? `calc(${itemWidthSm || '100vw'} - ${theme.spacings.md} - ${
210
+ theme.spacings.md
211
+ } + ${selected.length}px)`
212
+ : `calc(${itemWidthSm || '100vw'} - ${theme.spacings.md} - ${
213
+ theme.spacings.md
214
+ } - ${theme.page.horizontal} - ${theme.page.horizontal})`,
215
+ minWidth: `calc(${200}px - ${theme.spacings.md} - ${theme.spacings.md})`,
216
+ },
162
217
  },
163
218
  })}
164
219
  >
@@ -228,7 +283,7 @@ export function NavigationOverlay(props: NavigationOverlayProps) {
228
283
  />
229
284
  )}
230
285
 
231
- <NavigationList items={items} selected />
286
+ <NavigationList items={items} selected mouseEvent={mouseEvent} />
232
287
  </Box>
233
288
  </Box>
234
289
  </MotionDiv>
@@ -1,6 +1,6 @@
1
1
  import { useEventCallback } from '@mui/material'
2
2
  import { MotionConfig } from 'framer-motion'
3
- import { useState, useMemo } from 'react'
3
+ import { useState, useMemo, SetStateAction, useRef } from 'react'
4
4
  import { isElement } from 'react-is'
5
5
  import {
6
6
  NavigationNode,
@@ -16,6 +16,8 @@ export type NavigationProviderProps = {
16
16
  closeAfterNavigate?: boolean
17
17
  children?: React.ReactNode
18
18
  animationDuration?: number
19
+ selected: NavigationPath
20
+ setSelected: (value: SetStateAction<NavigationPath>) => void
19
21
  onChange?: NavigationSelect
20
22
  onClose?: NavigationContextType['onClose']
21
23
  }
@@ -31,9 +33,11 @@ export function NavigationProvider(props: NavigationProviderProps) {
31
33
  animationDuration = 0.275,
32
34
  children,
33
35
  onClose: onCloseUnstable,
36
+ selected,
37
+ setSelected,
34
38
  } = props
35
39
 
36
- const [selected, setSelected] = useState<NavigationPath>([])
40
+ const animating = useRef(false)
37
41
 
38
42
  const select = useEventCallback((incomming: NavigationPath) => {
39
43
  setSelected(incomming)
@@ -50,6 +54,7 @@ export function NavigationProvider(props: NavigationProviderProps) {
50
54
  hideRootOnNavigate,
51
55
  selected,
52
56
  select,
57
+ animating,
53
58
  items: items
54
59
  .map((item, index) => (isElement(item) ? { id: item.key ?? index, component: item } : item))
55
60
  .filter(nonNullable),
@@ -1,4 +1,4 @@
1
- import { createContext, useContext } from 'react'
1
+ import { createContext, MutableRefObject, SetStateAction, useContext } from 'react'
2
2
 
3
3
  export type NavigationId = string | number
4
4
  export type NavigationPath = NavigationId[]
@@ -17,6 +17,7 @@ export type NavigationContextType = {
17
17
  items: NavigationNode[]
18
18
  hideRootOnNavigate: boolean
19
19
  onClose: NavigationOnClose
20
+ animating: MutableRefObject<boolean>
20
21
  }
21
22
 
22
23
  type NavigationNodeBase = {
@@ -5,7 +5,7 @@ import {
5
5
  useIsomorphicLayoutEffect,
6
6
  } from '@graphcommerce/framer-utils'
7
7
  import { Box, styled, SxProps, Theme, useTheme, useThemeProps } from '@mui/material'
8
- import { m, useDomEvent, useMotionValue, useTransform } from 'framer-motion'
8
+ import { m, MotionProps, useDomEvent, useMotionValue, useTransform } from 'framer-motion'
9
9
  import React, { useCallback, useEffect, useRef } from 'react'
10
10
  import { LayoutProvider } from '../../Layout/components/LayoutProvider'
11
11
  import { ExtendableComponent, extendableComponent } from '../../Styles'
@@ -40,6 +40,7 @@ export type LayoutOverlayBaseProps = {
40
40
  offsetPageY: number
41
41
  isPresent: boolean
42
42
  safeToRemove: (() => void) | null | undefined
43
+ overlayPaneProps?: MotionProps
43
44
  } & StyleProps &
44
45
  OverridableProps
45
46
 
@@ -86,6 +87,7 @@ export function OverlayBase(incommingProps: LayoutOverlayBaseProps) {
86
87
  offsetPageY,
87
88
  isPresent,
88
89
  safeToRemove,
90
+ overlayPaneProps,
89
91
  } = props
90
92
 
91
93
  const th = useTheme()
@@ -367,6 +369,7 @@ export function OverlayBase(incommingProps: LayoutOverlayBaseProps) {
367
369
  })}
368
370
  >
369
371
  <MotionDiv
372
+ {...overlayPaneProps}
370
373
  layout
371
374
  className={classes.overlayPane}
372
375
  sx={(theme) => ({
@@ -38,3 +38,10 @@ export function RenderType<
38
38
 
39
39
  return <TypeItem {...typeItemProps} __typename={__typename} />
40
40
  }
41
+
42
+ export function findByTypename<T extends TypeObject, Typename extends T['__typename']>(
43
+ type: (T | undefined | null)[] | undefined | null,
44
+ typename: Typename,
45
+ ): FilterTypeByTypename<T, Typename> {
46
+ return type?.find((item) => item?.__typename === typename) as FilterTypeByTypename<T, Typename>
47
+ }
@@ -0,0 +1,20 @@
1
+ import type { OptionalKeysOf, Simplify } from 'type-fest'
2
+
3
+ export function filterNonNullableKeys<
4
+ T extends Record<string, unknown>,
5
+ Keys extends OptionalKeysOf<T>,
6
+ >(items: (T | null | undefined)[] | null | undefined, values: Keys[]) {
7
+ if (!items) return []
8
+
9
+ type ResultWithRequired = Simplify<
10
+ Omit<T, Keys> & {
11
+ [K in Keys]: NonNullable<T[K]>
12
+ }
13
+ >
14
+
15
+ const result = items.filter(
16
+ (item) => item !== null && typeof item !== 'undefined' && values.every((v) => item?.[v]),
17
+ )
18
+
19
+ return result as ResultWithRequired[]
20
+ }
@@ -0,0 +1,3 @@
1
+ export * from './nonNullable'
2
+ export * from './filterNonNullableKeys'
3
+ export * from './RenderType'
@@ -0,0 +1,3 @@
1
+ export function nonNullable<T>(value: T): value is NonNullable<T> {
2
+ return value !== null && value !== undefined
3
+ }
@@ -100,7 +100,6 @@ export function TextInputNumber(props: TextInputNumberProps) {
100
100
  ...(Array.isArray(sx) ? sx : [sx]),
101
101
  ]}
102
102
  autoComplete='off'
103
- id='quantity-input'
104
103
  InputProps={{
105
104
  ...textFieldProps.InputProps,
106
105
  startAdornment: (
package/index.ts CHANGED
@@ -38,7 +38,7 @@ export * from './Page'
38
38
  export * from './PageLoadIndicator/PageLoadIndicator'
39
39
  export * from './PageMeta/PageMeta'
40
40
  export * from './Pagination/Pagination'
41
- export * from './RenderType/RenderType'
41
+ export * from './RenderType'
42
42
  export * from './Row'
43
43
  export * from './SectionContainer/SectionContainer'
44
44
  export * from './SectionHeader/SectionHeader'
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@graphcommerce/next-ui",
3
3
  "homepage": "https://www.graphcommerce.org/",
4
4
  "repository": "github:graphcommerce-org/graphcommerce",
5
- "version": "4.14.0",
5
+ "version": "4.16.0",
6
6
  "author": "",
7
7
  "license": "MIT",
8
8
  "sideEffects": false,
@@ -20,12 +20,11 @@
20
20
  "@emotion/server": "^11.4.0",
21
21
  "@emotion/styled": "^11.9.3",
22
22
  "@graphcommerce/framer-next-pages": "3.2.4",
23
- "@graphcommerce/framer-scroller": "2.1.24",
23
+ "@graphcommerce/framer-scroller": "2.1.27",
24
24
  "@graphcommerce/framer-utils": "3.1.4",
25
25
  "@graphcommerce/image": "3.1.7",
26
26
  "cookie": "^0.5.0",
27
27
  "react-is": "^18.2.0",
28
- "react-schemaorg": "^2.0.0",
29
28
  "schema-dts": "^1.1.0"
30
29
  },
31
30
  "peerDependencies": {