@graphcommerce/ecommerce-ui 9.0.4-canary.8 → 9.0.4

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,30 +1,18 @@
1
1
  # @graphcommerce/ecommerce-ui
2
2
 
3
- ## 9.0.4-canary.8
3
+ ## 9.0.4
4
4
 
5
- ## 9.0.4-canary.7
5
+ ## 9.0.3
6
6
 
7
- ## 9.0.4-canary.6
7
+ ## 9.0.3-canary.0
8
8
 
9
- ## 9.0.4-canary.5
9
+ ## 9.0.2
10
10
 
11
- ## 9.0.4-canary.4
11
+ ## 9.0.2-canary.0
12
12
 
13
- ### Patch Changes
14
-
15
- - [`026114e`](https://github.com/graphcommerce-org/graphcommerce/commit/026114ece325d8f675ba8820c6b63345e48532fa) - Sovle issue where showValid would be forwarded to the textField element ([@paales](https://github.com/paales))
16
-
17
- ## 9.0.4-canary.3
18
-
19
- ## 9.0.4-canary.2
20
-
21
- ## 9.0.4-canary.1
22
-
23
- ### Patch Changes
24
-
25
- - [#2470](https://github.com/graphcommerce-org/graphcommerce/pull/2470) [`910e6aa`](https://github.com/graphcommerce-org/graphcommerce/commit/910e6aab024a925bb042cf17968b2ab826f97d88) - Refactor the FormComponents for better TypeScript checking performance. ([@paales](https://github.com/paales))
13
+ ## 9.0.1
26
14
 
27
- ## 9.0.4-canary.0
15
+ ## 9.0.1-canary.1
28
16
 
29
17
  ## 9.0.0
30
18
 
@@ -1,5 +1,5 @@
1
1
  import type { ButtonProps } from '@graphcommerce/next-ui'
2
- import { Button, iconChevronRight, IconSvg } from '@graphcommerce/next-ui'
2
+ import { Button, IconSvg, iconChevronRight } from '@graphcommerce/next-ui'
3
3
  import type { ComposedSubmitRenderComponentProps } from '@graphcommerce/react-hook-form'
4
4
  import { forwardRef } from 'react'
5
5
 
@@ -1,5 +1,5 @@
1
1
  import type { LinkOrButtonProps } from '@graphcommerce/next-ui'
2
- import { iconChevronRight, IconSvg, LinkOrButton } from '@graphcommerce/next-ui'
2
+ import { IconSvg, LinkOrButton, iconChevronRight } from '@graphcommerce/next-ui'
3
3
  import type { ComposedSubmitRenderComponentProps } from '@graphcommerce/react-hook-form'
4
4
  import { forwardRef } from 'react'
5
5
 
@@ -1,4 +1,4 @@
1
- import type { FieldValues } from '@graphcommerce/react-hook-form'
1
+ import type { FieldValues, UseControllerProps } from '@graphcommerce/react-hook-form'
2
2
  import { useController } from '@graphcommerce/react-hook-form'
3
3
  import { i18n } from '@lingui/core'
4
4
  import type { CheckboxProps } from '@mui/material'
@@ -11,33 +11,27 @@ import {
11
11
  FormLabel,
12
12
  useTheme,
13
13
  } from '@mui/material'
14
- import type { BaseControllerProps } from './types'
15
14
 
16
- type OptionBase = { id: string | number; label: string | number }
17
-
18
- type AdditionalProps<TOption extends OptionBase = OptionBase> = {
19
- options: TOption[]
15
+ export type CheckboxButtonGroupProps<T extends FieldValues> = {
16
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
+ options: { id: string | number; label: string }[] | any[]
20
18
  helperText?: string
21
19
  required?: boolean
22
20
  label?: string
23
- labelKey?: keyof TOption
24
- valueKey?: keyof TOption
25
- onChange?: (value: (string | number)[]) => void
21
+ labelKey?: string
22
+ valueKey?: string
23
+ // eslint-disable-next-line @typescript-eslint/ban-types
24
+ onChange?: Function
25
+ returnObject?: boolean
26
26
  disabled?: boolean
27
27
  row?: boolean
28
28
  checkboxColor?: CheckboxProps['color']
29
- }
30
-
31
- export type CheckboxButtonGroupProps<
32
- TFieldValues extends FieldValues = FieldValues,
33
- TOption extends OptionBase = OptionBase,
34
- > = BaseControllerProps<TFieldValues> & AdditionalProps<TOption>
29
+ } & UseControllerProps<T>
35
30
 
36
- type CheckboxButtonGroupComponent = <TFieldValues extends FieldValues>(
31
+ /** @public */
32
+ export function CheckboxButtonGroup<TFieldValues extends FieldValues>(
37
33
  props: CheckboxButtonGroupProps<TFieldValues>,
38
- ) => React.ReactNode
39
-
40
- function CheckboxButtonGroupBase(props: CheckboxButtonGroupProps) {
34
+ ): JSX.Element {
41
35
  const {
42
36
  helperText,
43
37
  options,
@@ -46,6 +40,7 @@ function CheckboxButtonGroupBase(props: CheckboxButtonGroupProps) {
46
40
  required,
47
41
  labelKey = 'label',
48
42
  valueKey = 'id',
43
+ returnObject,
49
44
  disabled,
50
45
  row,
51
46
  control,
@@ -70,37 +65,57 @@ function CheckboxButtonGroupBase(props: CheckboxButtonGroupProps) {
70
65
 
71
66
  const parsedHelperText = error ? error.message : helperText
72
67
 
73
- const handleChange = (optionKey: string | number) => {
74
- const currentValue = value as (string | number)[]
75
- const newArray = [...currentValue]
76
-
77
- const index = currentValue.indexOf(optionKey)
78
- if (index === -1) {
79
- newArray.push(optionKey)
68
+ const handleChange = (index: number | string) => {
69
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
70
+ const newArray: (string | number)[] | any[] = [...value]
71
+ const exists =
72
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
73
+ value.findIndex((i: any) => (returnObject ? i[valueKey] === index : i === index)) === -1
74
+ if (exists) {
75
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
76
+ newArray.push(returnObject ? options.find((i) => i[valueKey] === index) : index)
80
77
  } else {
81
- newArray.splice(index, 1)
78
+ newArray.splice(
79
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
80
+ value.findIndex((i: any) => (returnObject ? i[valueKey] === index : i === index)),
81
+ 1,
82
+ )
82
83
  }
83
-
84
+ // setValue(name, newArray, { shouldValidate: true })
84
85
  onChange(newArray)
85
- rest.onChange?.(newArray)
86
+ if (typeof rest.onChange === 'function') {
87
+ rest.onChange(newArray)
88
+ }
86
89
  }
87
90
 
88
91
  return (
89
92
  <FormControl error={invalid} required={required}>
90
93
  {label && <FormLabel error={invalid}>{label}</FormLabel>}
91
94
  <FormGroup row={row}>
92
- {options.map((option) => {
95
+ {options.map((option: any) => {
93
96
  const optionKey = option[valueKey]
94
- const isChecked = (value as (string | number)[]).includes(optionKey)
97
+ if (!optionKey) {
98
+ console.error(
99
+ `CheckboxButtonGroup: valueKey ${valueKey} does not exist on option`,
100
+ option,
101
+ )
102
+ }
103
+ const isChecked =
104
+ value.findIndex((item) =>
105
+ returnObject ? item[valueKey] === optionKey : item === optionKey,
106
+ ) !== -1
95
107
  return (
96
108
  <FormControlLabel
97
109
  control={
98
110
  <Checkbox
99
- sx={{ color: invalid ? theme.palette.error.main : undefined }}
111
+ sx={{
112
+ color: invalid ? theme.palette.error.main : undefined,
113
+ }}
100
114
  color={checkboxColor || 'primary'}
101
115
  value={optionKey}
102
116
  checked={isChecked}
103
117
  disabled={disabled}
118
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
104
119
  onChange={() => handleChange(optionKey)}
105
120
  />
106
121
  }
@@ -114,6 +129,3 @@ function CheckboxButtonGroupBase(props: CheckboxButtonGroupProps) {
114
129
  </FormControl>
115
130
  )
116
131
  }
117
-
118
- /** @public */
119
- export const CheckboxButtonGroup = CheckboxButtonGroupBase as CheckboxButtonGroupComponent
@@ -1,4 +1,4 @@
1
- import type { FieldValues } from '@graphcommerce/react-hook-form'
1
+ import type { ControllerProps, FieldValues } from '@graphcommerce/react-hook-form'
2
2
  import { useController } from '@graphcommerce/react-hook-form'
3
3
  import { i18n } from '@lingui/core'
4
4
  import type {
@@ -16,23 +16,18 @@ import {
16
16
  FormHelperText,
17
17
  useForkRef,
18
18
  } from '@mui/material'
19
- import type { FieldElementProps } from './types'
20
19
 
21
- type AdditionalProps = {
20
+ export type CheckboxElementProps<T extends FieldValues> = Omit<CheckboxProps, 'name'> & {
22
21
  label?: FormControlLabelProps['label']
23
22
  helperText?: string
24
23
  sx?: SxProps<Theme>
25
24
  formControl?: Omit<FormControlProps<'div'>, 'required' | 'error'>
26
- }
27
-
28
- export type CheckboxElementProps<TFieldValues extends FieldValues = FieldValues> =
29
- FieldElementProps<TFieldValues, CheckboxProps> & AdditionalProps
25
+ } & Omit<ControllerProps<T>, 'render'>
30
26
 
31
- type CheckboxElementComponent = <TFieldValues extends FieldValues>(
27
+ /** @public */
28
+ export function CheckboxElement<TFieldValues extends FieldValues>(
32
29
  props: CheckboxElementProps<TFieldValues>,
33
- ) => React.ReactNode
34
-
35
- function CheckboxElementBase(props: CheckboxElementProps): JSX.Element {
30
+ ): JSX.Element {
36
31
  const {
37
32
  name,
38
33
  rules = {},
@@ -53,7 +48,7 @@ function CheckboxElementBase(props: CheckboxElementProps): JSX.Element {
53
48
  }
54
49
 
55
50
  const {
56
- field: { value, onChange, ref, onBlur },
51
+ field: { value, onChange, ref, ...field },
57
52
  fieldState: { invalid, error },
58
53
  } = useController({
59
54
  name,
@@ -74,9 +69,7 @@ function CheckboxElementBase(props: CheckboxElementProps): JSX.Element {
74
69
  control={
75
70
  <Checkbox
76
71
  {...rest}
77
- onBlur={onBlur}
78
- disabled={disabled}
79
- name={name}
72
+ {...field}
80
73
  inputRef={useForkRef(ref, rest.inputRef)}
81
74
  color={rest.color || 'primary'}
82
75
  sx={{
@@ -94,6 +87,3 @@ function CheckboxElementBase(props: CheckboxElementProps): JSX.Element {
94
87
  </FormControl>
95
88
  )
96
89
  }
97
-
98
- /** @public */
99
- export const CheckboxElement = CheckboxElementBase as CheckboxElementComponent
@@ -1,21 +1,24 @@
1
- import type { FieldValues } from '@graphcommerce/react-hook-form'
2
- import { useController } from '@graphcommerce/react-hook-form'
1
+ /* eslint-disable no-nested-ternary */
2
+ import { FieldValues, UseControllerProps, useController } from '@graphcommerce/react-hook-form'
3
3
  import { i18n } from '@lingui/core'
4
- import type { InputBaseProps } from '@mui/material'
5
- import { InputBase } from '@mui/material'
4
+ import { InputBase, InputBaseProps } from '@mui/material'
6
5
  import React from 'react'
7
- import type { FieldElementProps } from './types'
8
6
 
9
- export type InputBaseElementProps<TFieldValues extends FieldValues = FieldValues> =
10
- FieldElementProps<TFieldValues, InputBaseProps>
7
+ export type InputBaseElementProps<T extends FieldValues = FieldValues> = Omit<
8
+ InputBaseProps,
9
+ 'name' | 'defaultValue'
10
+ > & {
11
+ showValid?: boolean
12
+ } & UseControllerProps<T>
11
13
 
12
14
  type InputBaseElementComponent = <TFieldValues extends FieldValues>(
13
15
  props: InputBaseElementProps<TFieldValues> & { ref?: React.Ref<HTMLInputElement> },
14
- ) => React.ReactNode
16
+ ) => JSX.Element
15
17
 
16
- function InputBaseElementBase(
17
- props: InputBaseElementProps & { ref?: React.Ref<HTMLInputElement> },
18
- ): JSX.Element {
18
+ export const InputBaseElement = React.forwardRef<
19
+ HTMLInputElement,
20
+ InputBaseElementProps<FieldValues>
21
+ >((props: InputBaseElementProps<FieldValues>, ref: React.Ref<HTMLInputElement>): JSX.Element => {
19
22
  const {
20
23
  type,
21
24
  required,
@@ -24,26 +27,19 @@ function InputBaseElementBase(
24
27
  defaultValue,
25
28
  rules = {},
26
29
  shouldUnregister,
30
+ showValid,
27
31
  disabled,
28
- ref,
29
32
  ...rest
30
33
  } = props
31
34
 
32
- if (required && !rules.required) {
35
+ if (required && !rules?.required) {
33
36
  rules.required = i18n._(/* i18n */ 'This field is required')
34
37
  }
35
38
 
36
39
  const {
37
40
  field,
38
41
  fieldState: { error },
39
- } = useController({
40
- name,
41
- control,
42
- rules,
43
- defaultValue,
44
- shouldUnregister,
45
- disabled,
46
- })
42
+ } = useController({ name, control, rules, defaultValue, shouldUnregister, disabled })
47
43
 
48
44
  return (
49
45
  <InputBase
@@ -55,9 +51,4 @@ function InputBaseElementBase(
55
51
  error={Boolean(error) || rest.error}
56
52
  />
57
53
  )
58
- }
59
-
60
- /** @public */
61
- export const InputBaseElement = React.forwardRef<HTMLInputElement, InputBaseElementProps>(
62
- (props, ref) => InputBaseElementBase({ ...props, ref }),
63
- ) as InputBaseElementComponent
54
+ }) as InputBaseElementComponent
@@ -5,25 +5,20 @@ import {
5
5
  IconSvg,
6
6
  responsiveVal,
7
7
  } from '@graphcommerce/next-ui'
8
- import type { FieldValues } from '@graphcommerce/react-hook-form'
9
- import { useController } from '@graphcommerce/react-hook-form'
8
+ import type { ControllerProps, FieldValues } from '@graphcommerce/react-hook-form'
9
+ import { Controller, useController } from '@graphcommerce/react-hook-form'
10
10
  import { i18n } from '@lingui/core'
11
11
  import type { IconButtonProps, SxProps, TextFieldProps, Theme } from '@mui/material'
12
12
  import { Fab, TextField, useForkRef } from '@mui/material'
13
- import type { FieldElementProps } from './types'
14
13
 
15
- type AdditionalProps = {
14
+ export type NumberFieldElementProps<T extends FieldValues = FieldValues> = Omit<
15
+ TextFieldProps,
16
+ 'type' | 'defaultValue'
17
+ > & {
16
18
  DownProps?: IconButtonProps
17
19
  UpProps?: IconButtonProps
18
20
  sx?: SxProps<Theme>
19
- }
20
-
21
- export type NumberFieldElementProps<TFieldValues extends FieldValues = FieldValues> =
22
- FieldElementProps<TFieldValues, Omit<TextFieldProps, 'type'>> & AdditionalProps
23
-
24
- type NumberFieldElementComponent = <TFieldValues extends FieldValues>(
25
- props: NumberFieldElementProps<TFieldValues>,
26
- ) => React.ReactNode
21
+ } & Omit<ControllerProps<T>, 'render'>
27
22
 
28
23
  type OwnerState = { size?: 'small' | 'medium' }
29
24
  const componentName = 'TextInputNumber'
@@ -34,7 +29,7 @@ const { withState } = extendableComponent<OwnerState, typeof componentName, type
34
29
  )
35
30
 
36
31
  /** @public */
37
- function NumberFieldElementBase(props: NumberFieldElementProps) {
32
+ export function NumberFieldElement<T extends FieldValues>(props: NumberFieldElementProps<T>) {
38
33
  const {
39
34
  DownProps = {},
40
35
  UpProps = {},
@@ -187,5 +182,3 @@ function NumberFieldElementBase(props: NumberFieldElementProps) {
187
182
  />
188
183
  )
189
184
  }
190
-
191
- export const NumberFieldElement = NumberFieldElementBase as NumberFieldElementComponent
@@ -1,4 +1,4 @@
1
- import type { FieldValues } from '@graphcommerce/react-hook-form'
1
+ import type { FieldValues, UseControllerProps } from '@graphcommerce/react-hook-form'
2
2
  import { useController } from '@graphcommerce/react-hook-form'
3
3
  import { i18n } from '@lingui/core'
4
4
  import {
@@ -11,33 +11,27 @@ import {
11
11
  useTheme,
12
12
  } from '@mui/material'
13
13
  import type { ChangeEvent } from 'react'
14
- import type { BaseControllerProps } from './types'
15
14
 
16
- type OptionBase = { id: string | number; label: string | number }
17
-
18
- type AdditionalProps<TOption extends OptionBase = OptionBase> = {
19
- options: TOption[]
15
+ export type RadioButtonGroupProps<T extends FieldValues> = {
16
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
+ options: { label: string; id: string | number }[] | any[]
20
18
  helperText?: string
21
19
  required?: boolean
22
20
  label?: string
23
- labelKey?: keyof TOption
24
- valueKey?: keyof TOption
25
- onChange?: (value: string | number) => void
26
- disabled?: boolean
21
+ labelKey?: string
22
+ valueKey?: string
23
+ type?: 'number' | 'string'
24
+ emptyOptionLabel?: 'string'
25
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
26
+ onChange?: (value: any) => void
27
+ returnObject?: boolean
27
28
  row?: boolean
28
- emptyOptionLabel?: string
29
- }
29
+ } & UseControllerProps<T>
30
30
 
31
- export type RadioButtonGroupProps<
32
- TFieldValues extends FieldValues = FieldValues,
33
- TOption extends OptionBase = OptionBase,
34
- > = BaseControllerProps<TFieldValues> & AdditionalProps<TOption>
35
-
36
- type RadioButtonGroupComponent = <TFieldValues extends FieldValues>(
31
+ /** @public */
32
+ export function RadioButtonGroup<TFieldValues extends FieldValues>(
37
33
  props: RadioButtonGroupProps<TFieldValues>,
38
- ) => React.ReactNode
39
-
40
- function RadioButtonGroupBase(props: RadioButtonGroupProps): JSX.Element {
34
+ ): JSX.Element {
41
35
  const {
42
36
  helperText,
43
37
  options,
@@ -47,6 +41,7 @@ function RadioButtonGroupBase(props: RadioButtonGroupProps): JSX.Element {
47
41
  valueKey = 'id',
48
42
  required,
49
43
  emptyOptionLabel,
44
+ returnObject,
50
45
  row,
51
46
  control,
52
47
  defaultValue,
@@ -70,16 +65,26 @@ function RadioButtonGroupBase(props: RadioButtonGroupProps): JSX.Element {
70
65
 
71
66
  const parsedHelperText = error ? error.message : helperText
72
67
 
73
- const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
74
- const radioValue = event.target.value
75
- onChange(radioValue)
76
- rest.onChange?.(radioValue)
68
+ const onRadioChange = (event: ChangeEvent<HTMLInputElement>) => {
69
+ const radioValue = (event.target as HTMLInputElement).value
70
+ const returnValue = returnObject
71
+ ? options.find((items) => items[valueKey] === radioValue)
72
+ : radioValue
73
+ // setValue(name, returnValue, { shouldValidate: true })
74
+ onChange(returnValue)
75
+ if (typeof rest.onChange === 'function') {
76
+ rest.onChange(returnValue)
77
+ }
77
78
  }
78
79
 
79
80
  return (
80
- <FormControl error={invalid} required={required}>
81
- {label && <FormLabel error={invalid}>{label}</FormLabel>}
82
- <RadioGroup onChange={handleChange} name={name} row={row} value={value ?? ''}>
81
+ <FormControl error={invalid}>
82
+ {label && (
83
+ <FormLabel required={required} error={invalid}>
84
+ {label}
85
+ </FormLabel>
86
+ )}
87
+ <RadioGroup onChange={onRadioChange} name={name} row={row} value={value || ''}>
83
88
  {emptyOptionLabel && (
84
89
  <FormControlLabel
85
90
  control={
@@ -96,6 +101,15 @@ function RadioButtonGroupBase(props: RadioButtonGroupProps): JSX.Element {
96
101
  )}
97
102
  {options.map((option) => {
98
103
  const optionKey = option[valueKey]
104
+ if (!optionKey) {
105
+ console.error(
106
+ `CheckboxButtonGroup: valueKey ${valueKey} does not exist on option`,
107
+ option,
108
+ )
109
+ }
110
+ const isChecked = !!(
111
+ value && (returnObject ? value[valueKey] === optionKey : value === optionKey)
112
+ )
99
113
  return (
100
114
  <FormControlLabel
101
115
  control={
@@ -103,7 +117,7 @@ function RadioButtonGroupBase(props: RadioButtonGroupProps): JSX.Element {
103
117
  sx={{
104
118
  color: invalid ? theme.palette.error.main : undefined,
105
119
  }}
106
- checked={value === optionKey}
120
+ checked={isChecked}
107
121
  />
108
122
  }
109
123
  value={optionKey}
@@ -117,6 +131,3 @@ function RadioButtonGroupBase(props: RadioButtonGroupProps): JSX.Element {
117
131
  </FormControl>
118
132
  )
119
133
  }
120
-
121
- /** @public */
122
- export const RadioButtonGroup = RadioButtonGroupBase as RadioButtonGroupComponent
@@ -1,31 +1,87 @@
1
- import type { FieldValues } from '@graphcommerce/react-hook-form'
1
+ import { InputCheckmark } from '@graphcommerce/next-ui'
2
+ import type { ControllerProps, FieldValues } from '@graphcommerce/react-hook-form'
3
+ import { useController } from '@graphcommerce/react-hook-form'
4
+ import { i18n } from '@lingui/core'
2
5
  import type { TextFieldProps } from '@mui/material'
3
- import { MenuItem } from '@mui/material'
4
- import { TextFieldElement, type TextFieldElementProps } from './TextFieldElement'
6
+ import { MenuItem, TextField, useForkRef } from '@mui/material'
5
7
 
6
8
  type OptionBase = { id: string | number; label: string | number }
7
9
 
8
- type AdditionalProps<O extends OptionBase = OptionBase> = { options: O[] }
10
+ export type SelectElementProps<T extends FieldValues, O extends OptionBase> = Omit<
11
+ TextFieldProps,
12
+ 'name' | 'type' | 'onChange' | 'defaultValue'
13
+ > & {
14
+ options?: O[]
15
+ type?: 'string' | 'number'
16
+ onChange?: (value: string | number) => void
17
+ showValid?: boolean
18
+ } & Omit<ControllerProps<T>, 'render'>
9
19
 
10
- export type SelectElementProps<
11
- TFieldValues extends FieldValues = FieldValues,
12
- TOption extends OptionBase = OptionBase,
13
- > = Omit<TextFieldElementProps<TFieldValues>, 'select'> & AdditionalProps<TOption>
20
+ /** @public */
21
+ export function SelectElement<TFieldValues extends FieldValues, O extends OptionBase>({
22
+ name,
23
+ required,
24
+ options = [],
25
+ type,
26
+ control,
27
+ defaultValue,
28
+ rules = {},
29
+ showValid,
30
+ disabled,
31
+ shouldUnregister,
32
+ ...rest
33
+ }: SelectElementProps<TFieldValues, O>): JSX.Element {
34
+ const isNativeSelect = !!rest.SelectProps?.native
35
+ const ChildComponent = isNativeSelect ? 'option' : MenuItem
14
36
 
15
- type SelectElementComponent = <TFieldValues extends FieldValues>(
16
- props: SelectElementProps<TFieldValues>,
17
- ) => React.ReactNode
37
+ if (required && !rules.required) {
38
+ rules.required = i18n._(/* i18n */ 'This field is required')
39
+ }
18
40
 
19
- /** @public */
20
- function SelectElementBase(props: SelectElementProps): JSX.Element {
21
- const { options } = props as AdditionalProps
22
- const { SelectProps } = props as TextFieldProps
41
+ const {
42
+ field: { onChange, value, ref, ...field },
43
+ fieldState: { invalid, error },
44
+ } = useController({
45
+ name,
46
+ rules,
47
+ control,
48
+ defaultValue,
49
+ disabled,
50
+ shouldUnregister,
51
+ })
23
52
 
24
- const isNativeSelect = !!SelectProps?.native
25
- const ChildComponent = isNativeSelect ? 'option' : MenuItem
53
+ // handle shrink on number input fields
54
+ if (type === 'number' && typeof value !== 'undefined') {
55
+ rest.InputLabelProps = rest.InputLabelProps || {}
56
+ rest.InputLabelProps.shrink = true
57
+ }
26
58
 
27
59
  return (
28
- <TextFieldElement {...props} select>
60
+ <TextField
61
+ {...rest}
62
+ value={value ?? ''}
63
+ {...field}
64
+ inputRef={useForkRef(ref, rest.inputRef)}
65
+ onChange={(event) => {
66
+ let item: number | string | O | undefined = event.target.value
67
+ if (type === 'number') item = Number(item)
68
+ rest.onChange?.(item)
69
+ onChange(item)
70
+ }}
71
+ select
72
+ required={required}
73
+ error={invalid}
74
+ helperText={error ? error.message : rest.helperText}
75
+ InputProps={{
76
+ ...rest.InputProps,
77
+ endAdornment:
78
+ showValid && value && !error ? (
79
+ <InputCheckmark show={!error} />
80
+ ) : (
81
+ rest.InputProps?.endAdornment
82
+ ),
83
+ }}
84
+ >
29
85
  {/* eslint-disable-next-line jsx-a11y/control-has-associated-label */}
30
86
  {isNativeSelect && <option />}
31
87
  {options.map((item) => (
@@ -33,8 +89,6 @@ function SelectElementBase(props: SelectElementProps): JSX.Element {
33
89
  {item.label}
34
90
  </ChildComponent>
35
91
  ))}
36
- </TextFieldElement>
92
+ </TextField>
37
93
  )
38
94
  }
39
-
40
- export const SelectElement = SelectElementBase as SelectElementComponent
@@ -1,41 +1,28 @@
1
- import type { FieldValues } from '@graphcommerce/react-hook-form'
1
+ import type { ControllerProps, FieldValues } from '@graphcommerce/react-hook-form'
2
2
  import { useController } from '@graphcommerce/react-hook-form'
3
3
  import { i18n } from '@lingui/core'
4
4
  import type { FormControlProps, SliderProps } from '@mui/material'
5
5
  import { FormControl, FormHelperText, FormLabel, Slider } from '@mui/material'
6
- import React from 'react'
7
- import type { FieldElementProps } from './types'
8
6
 
9
- type AdditionalProps = {
7
+ export type SliderElementProps<T extends FieldValues> = Omit<SliderProps, 'control'> & {
10
8
  label?: string
11
9
  required?: boolean
12
10
  formControlProps?: FormControlProps
13
- }
14
-
15
- export type SliderElementProps<TFieldValues extends FieldValues = FieldValues> = FieldElementProps<
16
- TFieldValues,
17
- Omit<SliderProps, 'name'>
18
- > &
19
- AdditionalProps
20
-
21
- type SliderElementComponent = <TFieldValues extends FieldValues>(
22
- props: SliderElementProps<TFieldValues>,
23
- ) => React.ReactNode
24
-
25
- function SliderElementBase(props: SliderElementProps): JSX.Element {
26
- const {
27
- name,
28
- control,
29
- label,
30
- rules = {},
31
- required,
32
- formControlProps,
33
- defaultValue,
34
- disabled,
35
- shouldUnregister,
36
- ...other
37
- } = props
11
+ } & Omit<ControllerProps<T>, 'render'>
38
12
 
13
+ /** @public */
14
+ export function SliderElement<TFieldValues extends FieldValues>({
15
+ name,
16
+ control,
17
+ label,
18
+ rules = {},
19
+ required,
20
+ formControlProps,
21
+ defaultValue,
22
+ disabled,
23
+ shouldUnregister,
24
+ ...other
25
+ }: SliderElementProps<TFieldValues>) {
39
26
  if (required && !rules.required) {
40
27
  rules.required = i18n._(/* i18n */ 'This field is required')
41
28
  }
@@ -66,6 +53,3 @@ function SliderElementBase(props: SliderElementProps): JSX.Element {
66
53
  </FormControl>
67
54
  )
68
55
  }
69
-
70
- /** @public */
71
- export const SliderElement = SliderElementBase as SliderElementComponent
@@ -1,25 +1,22 @@
1
- import type { FieldValues } from '@graphcommerce/react-hook-form'
1
+ import type { ControllerProps, FieldValues } from '@graphcommerce/react-hook-form'
2
2
  import { useController } from '@graphcommerce/react-hook-form'
3
- import type { FormControlLabelProps, SwitchProps } from '@mui/material'
3
+ import type { FormControlLabelProps } from '@mui/material'
4
4
  import { FormControlLabel, Switch } from '@mui/material'
5
- import React from 'react'
6
- import type { FieldElementProps } from './types'
7
5
 
8
- type AdditionalProps = Omit<FormControlLabelProps, 'control'>
6
+ type IProps = Omit<FormControlLabelProps, 'control'>
9
7
 
10
- export type SwitchElementProps<TFieldValues extends FieldValues = FieldValues> = FieldElementProps<
11
- TFieldValues,
12
- SwitchProps
13
- > &
14
- AdditionalProps
15
-
16
- type SwitchElementComponent = <TFieldValues extends FieldValues>(
17
- props: SwitchElementProps<TFieldValues>,
18
- ) => React.ReactNode
19
-
20
- function SwitchElementBase(props: SwitchElementProps): JSX.Element {
21
- const { name, control, defaultValue, disabled, shouldUnregister, rules, ...other } = props
8
+ export type SwitchElementProps<T extends FieldValues> = IProps & Omit<ControllerProps<T>, 'render'>
22
9
 
10
+ /** @public */
11
+ export function SwitchElement<TFieldValues extends FieldValues>({
12
+ name,
13
+ control,
14
+ defaultValue,
15
+ disabled,
16
+ shouldUnregister,
17
+ rules,
18
+ ...other
19
+ }: SwitchElementProps<TFieldValues>) {
23
20
  const { field } = useController({
24
21
  name,
25
22
  control,
@@ -31,6 +28,3 @@ function SwitchElementBase(props: SwitchElementProps): JSX.Element {
31
28
 
32
29
  return <FormControlLabel control={<Switch {...field} checked={!!field.value} />} {...other} />
33
30
  }
34
-
35
- /** @public */
36
- export const SwitchElement = SwitchElementBase as SwitchElementComponent
@@ -1,37 +1,32 @@
1
1
  /* eslint-disable no-nested-ternary */
2
2
  import { InputCheckmark } from '@graphcommerce/next-ui'
3
- import type { FieldValues } from '@graphcommerce/react-hook-form'
3
+ import type { FieldValues, UseControllerProps } from '@graphcommerce/react-hook-form'
4
4
  import { emailPattern, useController } from '@graphcommerce/react-hook-form'
5
5
  import { i18n } from '@lingui/core'
6
6
  import type { TextFieldProps } from '@mui/material'
7
7
  import { TextField, useForkRef } from '@mui/material'
8
8
  import React, { useState } from 'react'
9
- import type { BaseControllerProps, FieldElementProps } from './types'
10
9
 
11
- type ShowValidProps = { showValid?: boolean }
12
-
13
- export type TextFieldElementProps<TFieldValues extends FieldValues = FieldValues> =
14
- FieldElementProps<TFieldValues, TextFieldProps> & ShowValidProps
15
-
16
- type TextFieldElementComponent = <TFieldValues extends FieldValues>(
17
- props: TextFieldElementProps<TFieldValues>,
18
- ) => React.ReactNode
10
+ export type TextFieldElementProps<T extends FieldValues = FieldValues> = Omit<
11
+ TextFieldProps,
12
+ 'name' | 'defaultValue'
13
+ > & {
14
+ showValid?: boolean
15
+ } & UseControllerProps<T>
19
16
 
20
17
  /** @public */
21
- function TextFieldElementBase(props: TextFieldElementProps): JSX.Element {
22
- const {
23
- name,
24
- control,
25
- defaultValue,
26
- rules = {},
27
- shouldUnregister,
28
- disabled,
29
- type,
30
- required,
31
- showValid,
32
- ...rest
33
- } = props as TextFieldProps & ShowValidProps & BaseControllerProps
34
-
18
+ export function TextFieldElement<TFieldValues extends FieldValues>({
19
+ type,
20
+ required,
21
+ name,
22
+ control,
23
+ defaultValue,
24
+ rules = {},
25
+ shouldUnregister,
26
+ showValid,
27
+ disabled,
28
+ ...rest
29
+ }: TextFieldElementProps<TFieldValues>): JSX.Element {
35
30
  if (required && !rules.required) {
36
31
  rules.required = i18n._(/* i18n */ 'This field is required')
37
32
  }
@@ -44,7 +39,7 @@ function TextFieldElementBase(props: TextFieldElementProps): JSX.Element {
44
39
  }
45
40
 
46
41
  const {
47
- field: { onChange, ref, value = '', onBlur },
42
+ field: { onChange, ref, value = '', ...field },
48
43
  fieldState: { error },
49
44
  } = useController({ name, control, rules, defaultValue, shouldUnregister, disabled })
50
45
 
@@ -63,9 +58,7 @@ function TextFieldElementBase(props: TextFieldElementProps): JSX.Element {
63
58
  return (
64
59
  <TextField
65
60
  {...rest}
66
- onBlur={onBlur}
67
- name={name}
68
- disabled={disabled}
61
+ {...field}
69
62
  value={value}
70
63
  inputProps={{ ...rest.inputProps, onAnimationStart }}
71
64
  onChange={(ev) => {
@@ -90,5 +83,3 @@ function TextFieldElementBase(props: TextFieldElementProps): JSX.Element {
90
83
  />
91
84
  )
92
85
  }
93
-
94
- export const TextFieldElement = TextFieldElementBase as TextFieldElementComponent
@@ -2,6 +2,7 @@ export * from './ActionCardListForm'
2
2
  export * from './CheckboxButtonGroup'
3
3
  export * from './CheckboxElement'
4
4
  export * from './EmailElement'
5
+ export * from './MultiSelectElement'
5
6
  export * from './NumberFieldElement'
6
7
  export * from './PasswordElement'
7
8
  export * from './PasswordRepeatElement'
@@ -1,4 +1,4 @@
1
- import { styled, Tooltip, tooltipClasses } from '@mui/material'
1
+ import { Tooltip, styled, tooltipClasses } from '@mui/material'
2
2
 
3
3
  export const LightTooltip = styled<typeof Tooltip>(({ className, ...props }) => (
4
4
  <Tooltip {...props} classes={{ popper: className }} />
@@ -1,6 +1,20 @@
1
+ import type { PreviewData } from '@graphcommerce/graphql'
2
+ import {
3
+ iconChevronRight,
4
+ iconClose,
5
+ iconContrast,
6
+ iconInfo,
7
+ iconRefresh,
8
+ IconSvg,
9
+ MessageSnackbar,
10
+ } from '@graphcommerce/next-ui'
11
+ import { FormAutoSubmit, FormPersist, FormProvider, useForm } from '@graphcommerce/react-hook-form'
12
+ import { Box, IconButton } from '@mui/material'
1
13
  import { useRouter } from 'next/router'
2
- import { PreviewModeDisabled } from './PreviewModeDisabled'
3
- import { PreviewModeEnabled } from './PreviewModeEnabled'
14
+ import { TextFieldElement } from '../FormComponents'
15
+ import { LightTooltip } from './LightTooltip'
16
+ import { PreviewModeActions } from './PreviewModeActions'
17
+ import { PreviewModeToolbar } from './PreviewModeToolbar'
4
18
 
5
19
  function getPreviewUrl() {
6
20
  const url = new URL(window.location.href)
@@ -9,6 +23,128 @@ function getPreviewUrl() {
9
23
  return url
10
24
  }
11
25
 
26
+ function PreviewModeEnabled() {
27
+ const form = useForm<{ secret: string; previewData: PreviewData }>({})
28
+
29
+ const submit = form.handleSubmit((formValues) => {
30
+ const url = getPreviewUrl()
31
+ url.searchParams.append('action', 'update')
32
+
33
+ Object.entries(formValues).forEach(([key, value]) => {
34
+ url.searchParams.append(key, JSON.stringify(value))
35
+ })
36
+
37
+ window.location.href = url.toString()
38
+ })
39
+
40
+ const exitHandler = form.handleSubmit(() => {
41
+ const url = getPreviewUrl()
42
+ url.searchParams.append('action', 'exit')
43
+
44
+ window.location.href = url.toString()
45
+ })
46
+
47
+ const revalidateHandler = form.handleSubmit((formValues) => {
48
+ const url = getPreviewUrl()
49
+ Object.entries(formValues).forEach(([key, value]) => {
50
+ // eslint-disable-next-line @typescript-eslint/no-base-to-string
51
+ url.searchParams.append(key, `${value}`)
52
+ })
53
+ url.searchParams.append('action', 'revalidate')
54
+ window.location.href = url.toString()
55
+ })
56
+
57
+ return (
58
+ <FormProvider {...form}>
59
+ <MessageSnackbar
60
+ variant='pill'
61
+ severity='warning'
62
+ disableBackdropClick
63
+ disableClose
64
+ open
65
+ icon={iconContrast}
66
+ onClose={() => {}}
67
+ action={
68
+ <>
69
+ <PreviewModeActions />
70
+ <LightTooltip title='Revalidate / Regenerate Page' placement='top'>
71
+ <IconButton color='secondary' type='submit' onClick={revalidateHandler}>
72
+ <IconSvg src={iconRefresh} />
73
+ </IconButton>
74
+ </LightTooltip>
75
+ <LightTooltip title='Stop preview mode' placement='top'>
76
+ <IconButton color='secondary' type='submit' onClick={exitHandler}>
77
+ <IconSvg src={iconClose} />
78
+ </IconButton>
79
+ </LightTooltip>
80
+ </>
81
+ }
82
+ >
83
+ <Box sx={{ display: 'grid', gridAutoFlow: 'column', placeItems: 'center', gap: 4 }}>
84
+ <Box sx={{ display: 'flex', placeItems: 'center' }}>
85
+ Preview Mode{' '}
86
+ <LightTooltip title='You are currently viewing the website in Preview Mode (caches are disabled).'>
87
+ <IconButton size='small'>
88
+ <IconSvg src={iconInfo} />
89
+ </IconButton>
90
+ </LightTooltip>
91
+ </Box>
92
+ <PreviewModeToolbar />
93
+ </Box>
94
+ </MessageSnackbar>
95
+ <FormPersist form={form} name='PreviewModePreviewData' />
96
+ <FormAutoSubmit control={form.control} submit={submit} />
97
+ </FormProvider>
98
+ )
99
+ }
100
+
101
+ function PreviewModeDisabled() {
102
+ const form = useForm<{ secret: string }>({
103
+ defaultValues: {
104
+ secret:
105
+ process.env.NODE_ENV === 'development'
106
+ ? (import.meta.graphCommerce.previewSecret ?? '')
107
+ : '',
108
+ },
109
+ })
110
+
111
+ const submit = form.handleSubmit((formValues) => {
112
+ const url = getPreviewUrl()
113
+ url.searchParams.append('action', 'enable')
114
+
115
+ Object.entries(formValues).forEach(([key, value]) => {
116
+ url.searchParams.append(key, typeof value === 'string' ? value : JSON.stringify(value))
117
+ })
118
+
119
+ window.location.href = url.toString()
120
+ })
121
+
122
+ return (
123
+ <FormProvider {...form}>
124
+ <MessageSnackbar
125
+ variant='pill'
126
+ severity='warning'
127
+ disableBackdropClick
128
+ disableClose
129
+ open
130
+ icon={iconContrast}
131
+ onClose={() => {}}
132
+ action={
133
+ <IconButton color='secondary' type='submit' onClick={submit}>
134
+ <IconSvg src={iconChevronRight} />
135
+ </IconButton>
136
+ }
137
+ >
138
+ <Box sx={{ display: 'grid', gridAutoFlow: 'column', placeItems: 'center', gap: 4 }}>
139
+ <Box sx={{ display: 'flex', placeItems: 'center' }}>Preview Mode</Box>
140
+ <TextFieldElement control={form.control} name='secret' label='Secret' />
141
+ </Box>
142
+ </MessageSnackbar>
143
+ <FormPersist form={form} name='PreviewModePreviewData' />
144
+ </FormProvider>
145
+ )
146
+ }
147
+
12
148
  export function PreviewMode() {
13
149
  const router = useRouter()
14
150
 
@@ -1,5 +1,5 @@
1
1
  import type { PreviewData } from '@graphcommerce/graphql'
2
2
 
3
3
  export function previewModeDefaults(): PreviewData {
4
- return {} as PreviewData
4
+ return {}
5
5
  }
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@graphcommerce/ecommerce-ui",
3
3
  "homepage": "https://www.graphcommerce.org/",
4
4
  "repository": "github:graphcommerce-org/graphcommerce",
5
- "version": "9.0.4-canary.8",
5
+ "version": "9.0.4",
6
6
  "sideEffects": false,
7
7
  "prettier": "@graphcommerce/prettier-config-pwa",
8
8
  "eslintConfig": {
@@ -12,12 +12,12 @@
12
12
  }
13
13
  },
14
14
  "peerDependencies": {
15
- "@graphcommerce/eslint-config-pwa": "^9.0.4-canary.8",
16
- "@graphcommerce/graphql": "^9.0.4-canary.8",
17
- "@graphcommerce/next-ui": "^9.0.4-canary.8",
18
- "@graphcommerce/prettier-config-pwa": "^9.0.4-canary.8",
19
- "@graphcommerce/react-hook-form": "^9.0.4-canary.8",
20
- "@graphcommerce/typescript-config-pwa": "^9.0.4-canary.8",
15
+ "@graphcommerce/eslint-config-pwa": "^9.0.4",
16
+ "@graphcommerce/graphql": "^9.0.4",
17
+ "@graphcommerce/next-ui": "^9.0.4",
18
+ "@graphcommerce/prettier-config-pwa": "^9.0.4",
19
+ "@graphcommerce/react-hook-form": "^9.0.4",
20
+ "@graphcommerce/typescript-config-pwa": "^9.0.4",
21
21
  "@lingui/core": "^4.2.1",
22
22
  "@lingui/macro": "^4.2.1",
23
23
  "@lingui/react": "^4.2.1",
@@ -1,26 +0,0 @@
1
- import type {
2
- Control,
3
- FieldPath,
4
- FieldPathValue,
5
- FieldValues,
6
- RegisterOptions,
7
- } from '@graphcommerce/react-hook-form'
8
-
9
- export type BaseControllerProps<TFieldValues extends FieldValues = FieldValues> = {
10
- name: FieldPath<TFieldValues>
11
- control?: Control<TFieldValues>
12
- rules?: Omit<
13
- RegisterOptions<NoInfer<TFieldValues>, FieldPath<TFieldValues>>,
14
- 'valueAsNumber' | 'valueAsDate' | 'setValueAs' | 'disabled'
15
- >
16
- shouldUnregister?: boolean
17
- defaultValue?: FieldPathValue<NoInfer<TFieldValues>, FieldPath<TFieldValues>>
18
- disabled?: boolean
19
- }
20
-
21
- type BaseControllerPropsKeys = keyof BaseControllerProps
22
-
23
- export type FieldElementProps<
24
- TFieldValues extends FieldValues,
25
- BaseTypes = Record<string, unknown>,
26
- > = BaseControllerProps<TFieldValues> & Omit<BaseTypes, BaseControllerPropsKeys>
@@ -1,56 +0,0 @@
1
- import { iconChevronRight, iconContrast, IconSvg, MessageSnackbar } from '@graphcommerce/next-ui'
2
- import { FormPersist, FormProvider, useForm } from '@graphcommerce/react-hook-form'
3
- import { Box, IconButton } from '@mui/material'
4
- import { TextFieldElement } from '../FormComponents'
5
-
6
- function getPreviewUrl() {
7
- const url = new URL(window.location.href)
8
- url.pathname = '/api/preview'
9
- ;[...url.searchParams.entries()].forEach(([key]) => url.searchParams.delete(key))
10
- return url
11
- }
12
-
13
- const secret =
14
- process.env.NODE_ENV === 'development' ? (import.meta.graphCommerce.previewSecret ?? '') : ''
15
-
16
- type FormValues = { secret: string }
17
-
18
- export function PreviewModeDisabled() {
19
- const form = useForm<FormValues>({ defaultValues: { secret } })
20
-
21
- const submit = form.handleSubmit((formValues) => {
22
- const url = getPreviewUrl()
23
- url.searchParams.append('action', 'enable')
24
-
25
- Object.entries(formValues).forEach(([key, value]) => {
26
- url.searchParams.append(key, typeof value === 'string' ? value : JSON.stringify(value))
27
- })
28
-
29
- window.location.href = url.toString()
30
- })
31
-
32
- return (
33
- <FormProvider<FormValues> {...form}>
34
- <MessageSnackbar
35
- variant='pill'
36
- severity='warning'
37
- disableBackdropClick
38
- disableClose
39
- open
40
- icon={iconContrast}
41
- onClose={() => {}}
42
- action={
43
- <IconButton color='secondary' type='submit' onClick={submit}>
44
- <IconSvg src={iconChevronRight} />
45
- </IconButton>
46
- }
47
- >
48
- <Box sx={{ display: 'grid', gridAutoFlow: 'column', placeItems: 'center', gap: 4 }}>
49
- <Box sx={{ display: 'flex', placeItems: 'center' }}>Preview Mode</Box>
50
- <TextFieldElement control={form.control} name='secret' label='Secret' />
51
- </Box>
52
- </MessageSnackbar>
53
- <FormPersist form={form} name='PreviewModePreviewData' />
54
- </FormProvider>
55
- )
56
- }
@@ -1,98 +0,0 @@
1
- import type { PreviewData } from '@graphcommerce/graphql'
2
- import {
3
- iconClose,
4
- iconContrast,
5
- iconInfo,
6
- iconRefresh,
7
- IconSvg,
8
- MessageSnackbar,
9
- } from '@graphcommerce/next-ui'
10
- import { FormAutoSubmit, FormPersist, FormProvider, useForm } from '@graphcommerce/react-hook-form'
11
- import { Box, IconButton } from '@mui/material'
12
- import { LightTooltip } from './LightTooltip'
13
- import { PreviewModeActions } from './PreviewModeActions'
14
- import { PreviewModeToolbar } from './PreviewModeToolbar'
15
-
16
- function getPreviewUrl() {
17
- const url = new URL(window.location.href)
18
- url.pathname = '/api/preview'
19
- ;[...url.searchParams.entries()].forEach(([key]) => url.searchParams.delete(key))
20
- return url
21
- }
22
-
23
- type FormValues = { secret: string; previewData: PreviewData }
24
-
25
- export function PreviewModeEnabled() {
26
- const form = useForm<FormValues>({})
27
-
28
- const submit = form.handleSubmit((formValues) => {
29
- const url = getPreviewUrl()
30
- url.searchParams.append('action', 'update')
31
-
32
- Object.entries(formValues).forEach(([key, value]) => {
33
- url.searchParams.append(key, JSON.stringify(value))
34
- })
35
-
36
- window.location.href = url.toString()
37
- })
38
-
39
- const exitHandler = form.handleSubmit(() => {
40
- const url = getPreviewUrl()
41
- url.searchParams.append('action', 'exit')
42
-
43
- window.location.href = url.toString()
44
- })
45
-
46
- const revalidateHandler = form.handleSubmit((formValues) => {
47
- const url = getPreviewUrl()
48
- Object.entries(formValues).forEach(([key, value]) => {
49
- // eslint-disable-next-line @typescript-eslint/no-base-to-string
50
- url.searchParams.append(key, `${value}`)
51
- })
52
- url.searchParams.append('action', 'revalidate')
53
- window.location.href = url.toString()
54
- })
55
-
56
- return (
57
- <FormProvider<FormValues> {...form}>
58
- <MessageSnackbar
59
- variant='pill'
60
- severity='warning'
61
- disableBackdropClick
62
- disableClose
63
- open
64
- icon={iconContrast}
65
- onClose={() => {}}
66
- action={
67
- <>
68
- <PreviewModeActions />
69
- <LightTooltip title='Revalidate / Regenerate Page' placement='top'>
70
- <IconButton color='secondary' type='submit' onClick={revalidateHandler}>
71
- <IconSvg src={iconRefresh} />
72
- </IconButton>
73
- </LightTooltip>
74
- <LightTooltip title='Stop preview mode' placement='top'>
75
- <IconButton color='secondary' type='submit' onClick={exitHandler}>
76
- <IconSvg src={iconClose} />
77
- </IconButton>
78
- </LightTooltip>
79
- </>
80
- }
81
- >
82
- <Box sx={{ display: 'grid', gridAutoFlow: 'column', placeItems: 'center', gap: 4 }}>
83
- <Box sx={{ display: 'flex', placeItems: 'center' }}>
84
- Preview Mode{' '}
85
- <LightTooltip title='You are currently viewing the website in Preview Mode (caches are disabled).'>
86
- <IconButton size='small'>
87
- <IconSvg src={iconInfo} />
88
- </IconButton>
89
- </LightTooltip>
90
- </Box>
91
- <PreviewModeToolbar />
92
- </Box>
93
- </MessageSnackbar>
94
- <FormPersist form={form} name='PreviewModePreviewData' />
95
- <FormAutoSubmit control={form.control} submit={submit} />
96
- </FormProvider>
97
- )
98
- }