@graphcommerce/ecommerce-ui 9.0.3 → 9.0.4-canary.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 +4 -8
- package/components/ComposedSubmitButton/ComposedSubmitButton.tsx +1 -1
- package/components/ComposedSubmitButton/ComposedSubmitLinkOrButton.tsx +1 -1
- package/components/FormComponents/CheckboxButtonGroup.tsx +35 -47
- package/components/FormComponents/CheckboxElement.tsx +18 -8
- package/components/FormComponents/InputBaseElement.tsx +27 -18
- package/components/FormComponents/NumberFieldElement.tsx +15 -8
- package/components/FormComponents/RadioButtonGroup.tsx +32 -43
- package/components/FormComponents/SelectElement.tsx +21 -75
- package/components/FormComponents/SliderElement.tsx +32 -16
- package/components/FormComponents/SwitchElement.tsx +20 -14
- package/components/FormComponents/TextFieldElement.tsx +28 -21
- package/components/FormComponents/index.ts +0 -1
- package/components/FormComponents/types.ts +26 -0
- package/components/PreviewMode/LightTooltip.tsx +1 -1
- package/components/PreviewMode/PreviewMode.tsx +2 -138
- package/components/PreviewMode/PreviewModeDisabled.tsx +56 -0
- package/components/PreviewMode/PreviewModeEnabled.tsx +98 -0
- package/components/PreviewMode/previewModeDefaults.ts +1 -1
- package/package.json +7 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
# @graphcommerce/ecommerce-ui
|
|
2
2
|
|
|
3
|
-
## 9.0.
|
|
3
|
+
## 9.0.4-canary.1
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
## 9.0.2
|
|
8
|
-
|
|
9
|
-
## 9.0.2-canary.0
|
|
5
|
+
### Patch Changes
|
|
10
6
|
|
|
11
|
-
|
|
7
|
+
- [#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))
|
|
12
8
|
|
|
13
|
-
## 9.0.
|
|
9
|
+
## 9.0.4-canary.0
|
|
14
10
|
|
|
15
11
|
## 9.0.0
|
|
16
12
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ButtonProps } from '@graphcommerce/next-ui'
|
|
2
|
-
import { Button,
|
|
2
|
+
import { Button, iconChevronRight, IconSvg } 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 { IconSvg, LinkOrButton
|
|
2
|
+
import { iconChevronRight, IconSvg, LinkOrButton } 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
|
|
1
|
+
import type { 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 { CheckboxProps } from '@mui/material'
|
|
@@ -11,27 +11,33 @@ import {
|
|
|
11
11
|
FormLabel,
|
|
12
12
|
useTheme,
|
|
13
13
|
} from '@mui/material'
|
|
14
|
+
import type { BaseControllerProps } from './types'
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
type OptionBase = { id: string | number; label: string | number }
|
|
17
|
+
|
|
18
|
+
type AdditionalProps<TOption extends OptionBase = OptionBase> = {
|
|
19
|
+
options: TOption[]
|
|
18
20
|
helperText?: string
|
|
19
21
|
required?: boolean
|
|
20
22
|
label?: string
|
|
21
|
-
labelKey?:
|
|
22
|
-
valueKey?:
|
|
23
|
-
|
|
24
|
-
onChange?: Function
|
|
25
|
-
returnObject?: boolean
|
|
23
|
+
labelKey?: keyof TOption
|
|
24
|
+
valueKey?: keyof TOption
|
|
25
|
+
onChange?: (value: (string | number)[]) => void
|
|
26
26
|
disabled?: boolean
|
|
27
27
|
row?: boolean
|
|
28
28
|
checkboxColor?: CheckboxProps['color']
|
|
29
|
-
}
|
|
29
|
+
}
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
export type CheckboxButtonGroupProps<
|
|
32
|
+
TFieldValues extends FieldValues = FieldValues,
|
|
33
|
+
TOption extends OptionBase = OptionBase,
|
|
34
|
+
> = BaseControllerProps<TFieldValues> & AdditionalProps<TOption>
|
|
35
|
+
|
|
36
|
+
type CheckboxButtonGroupComponent = <TFieldValues extends FieldValues>(
|
|
33
37
|
props: CheckboxButtonGroupProps<TFieldValues>,
|
|
34
|
-
)
|
|
38
|
+
) => React.ReactNode
|
|
39
|
+
|
|
40
|
+
function CheckboxButtonGroupBase(props: CheckboxButtonGroupProps) {
|
|
35
41
|
const {
|
|
36
42
|
helperText,
|
|
37
43
|
options,
|
|
@@ -40,7 +46,6 @@ export function CheckboxButtonGroup<TFieldValues extends FieldValues>(
|
|
|
40
46
|
required,
|
|
41
47
|
labelKey = 'label',
|
|
42
48
|
valueKey = 'id',
|
|
43
|
-
returnObject,
|
|
44
49
|
disabled,
|
|
45
50
|
row,
|
|
46
51
|
control,
|
|
@@ -65,57 +70,37 @@ export function CheckboxButtonGroup<TFieldValues extends FieldValues>(
|
|
|
65
70
|
|
|
66
71
|
const parsedHelperText = error ? error.message : helperText
|
|
67
72
|
|
|
68
|
-
const handleChange = (
|
|
69
|
-
|
|
70
|
-
const newArray
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
76
|
-
newArray.push(returnObject ? options.find((i) => i[valueKey] === index) : index)
|
|
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)
|
|
77
80
|
} else {
|
|
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
|
-
)
|
|
81
|
+
newArray.splice(index, 1)
|
|
83
82
|
}
|
|
84
|
-
|
|
83
|
+
|
|
85
84
|
onChange(newArray)
|
|
86
|
-
|
|
87
|
-
rest.onChange(newArray)
|
|
88
|
-
}
|
|
85
|
+
rest.onChange?.(newArray)
|
|
89
86
|
}
|
|
90
87
|
|
|
91
88
|
return (
|
|
92
89
|
<FormControl error={invalid} required={required}>
|
|
93
90
|
{label && <FormLabel error={invalid}>{label}</FormLabel>}
|
|
94
91
|
<FormGroup row={row}>
|
|
95
|
-
{options.map((option
|
|
92
|
+
{options.map((option) => {
|
|
96
93
|
const optionKey = option[valueKey]
|
|
97
|
-
|
|
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
|
|
94
|
+
const isChecked = (value as (string | number)[]).includes(optionKey)
|
|
107
95
|
return (
|
|
108
96
|
<FormControlLabel
|
|
109
97
|
control={
|
|
110
98
|
<Checkbox
|
|
111
|
-
sx={{
|
|
112
|
-
color: invalid ? theme.palette.error.main : undefined,
|
|
113
|
-
}}
|
|
99
|
+
sx={{ color: invalid ? theme.palette.error.main : undefined }}
|
|
114
100
|
color={checkboxColor || 'primary'}
|
|
115
101
|
value={optionKey}
|
|
116
102
|
checked={isChecked}
|
|
117
103
|
disabled={disabled}
|
|
118
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
119
104
|
onChange={() => handleChange(optionKey)}
|
|
120
105
|
/>
|
|
121
106
|
}
|
|
@@ -129,3 +114,6 @@ export function CheckboxButtonGroup<TFieldValues extends FieldValues>(
|
|
|
129
114
|
</FormControl>
|
|
130
115
|
)
|
|
131
116
|
}
|
|
117
|
+
|
|
118
|
+
/** @public */
|
|
119
|
+
export const CheckboxButtonGroup = CheckboxButtonGroupBase as CheckboxButtonGroupComponent
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { 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,18 +16,23 @@ import {
|
|
|
16
16
|
FormHelperText,
|
|
17
17
|
useForkRef,
|
|
18
18
|
} from '@mui/material'
|
|
19
|
+
import type { FieldElementProps } from './types'
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
type AdditionalProps = {
|
|
21
22
|
label?: FormControlLabelProps['label']
|
|
22
23
|
helperText?: string
|
|
23
24
|
sx?: SxProps<Theme>
|
|
24
25
|
formControl?: Omit<FormControlProps<'div'>, 'required' | 'error'>
|
|
25
|
-
}
|
|
26
|
+
}
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
export type CheckboxElementProps<TFieldValues extends FieldValues = FieldValues> =
|
|
29
|
+
FieldElementProps<TFieldValues, CheckboxProps> & AdditionalProps
|
|
30
|
+
|
|
31
|
+
type CheckboxElementComponent = <TFieldValues extends FieldValues>(
|
|
29
32
|
props: CheckboxElementProps<TFieldValues>,
|
|
30
|
-
)
|
|
33
|
+
) => React.ReactNode
|
|
34
|
+
|
|
35
|
+
function CheckboxElementBase(props: CheckboxElementProps): JSX.Element {
|
|
31
36
|
const {
|
|
32
37
|
name,
|
|
33
38
|
rules = {},
|
|
@@ -48,7 +53,7 @@ export function CheckboxElement<TFieldValues extends FieldValues>(
|
|
|
48
53
|
}
|
|
49
54
|
|
|
50
55
|
const {
|
|
51
|
-
field: { value, onChange, ref,
|
|
56
|
+
field: { value, onChange, ref, onBlur },
|
|
52
57
|
fieldState: { invalid, error },
|
|
53
58
|
} = useController({
|
|
54
59
|
name,
|
|
@@ -69,7 +74,9 @@ export function CheckboxElement<TFieldValues extends FieldValues>(
|
|
|
69
74
|
control={
|
|
70
75
|
<Checkbox
|
|
71
76
|
{...rest}
|
|
72
|
-
{
|
|
77
|
+
onBlur={onBlur}
|
|
78
|
+
disabled={disabled}
|
|
79
|
+
name={name}
|
|
73
80
|
inputRef={useForkRef(ref, rest.inputRef)}
|
|
74
81
|
color={rest.color || 'primary'}
|
|
75
82
|
sx={{
|
|
@@ -87,3 +94,6 @@ export function CheckboxElement<TFieldValues extends FieldValues>(
|
|
|
87
94
|
</FormControl>
|
|
88
95
|
)
|
|
89
96
|
}
|
|
97
|
+
|
|
98
|
+
/** @public */
|
|
99
|
+
export const CheckboxElement = CheckboxElementBase as CheckboxElementComponent
|
|
@@ -1,24 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
1
|
+
import type { FieldValues } from '@graphcommerce/react-hook-form'
|
|
2
|
+
import { useController } from '@graphcommerce/react-hook-form'
|
|
3
3
|
import { i18n } from '@lingui/core'
|
|
4
|
-
import {
|
|
4
|
+
import type { InputBaseProps } from '@mui/material'
|
|
5
|
+
import { InputBase } from '@mui/material'
|
|
5
6
|
import React from 'react'
|
|
7
|
+
import type { FieldElementProps } from './types'
|
|
6
8
|
|
|
7
|
-
export type InputBaseElementProps<
|
|
8
|
-
InputBaseProps
|
|
9
|
-
'name' | 'defaultValue'
|
|
10
|
-
> & {
|
|
11
|
-
showValid?: boolean
|
|
12
|
-
} & UseControllerProps<T>
|
|
9
|
+
export type InputBaseElementProps<TFieldValues extends FieldValues = FieldValues> =
|
|
10
|
+
FieldElementProps<TFieldValues, InputBaseProps>
|
|
13
11
|
|
|
14
12
|
type InputBaseElementComponent = <TFieldValues extends FieldValues>(
|
|
15
13
|
props: InputBaseElementProps<TFieldValues> & { ref?: React.Ref<HTMLInputElement> },
|
|
16
|
-
) =>
|
|
14
|
+
) => React.ReactNode
|
|
17
15
|
|
|
18
|
-
|
|
19
|
-
HTMLInputElement,
|
|
20
|
-
|
|
21
|
-
>((props: InputBaseElementProps<FieldValues>, ref: React.Ref<HTMLInputElement>): JSX.Element => {
|
|
16
|
+
function InputBaseElementBase(
|
|
17
|
+
props: InputBaseElementProps & { ref?: React.Ref<HTMLInputElement> },
|
|
18
|
+
): JSX.Element {
|
|
22
19
|
const {
|
|
23
20
|
type,
|
|
24
21
|
required,
|
|
@@ -27,19 +24,26 @@ export const InputBaseElement = React.forwardRef<
|
|
|
27
24
|
defaultValue,
|
|
28
25
|
rules = {},
|
|
29
26
|
shouldUnregister,
|
|
30
|
-
showValid,
|
|
31
27
|
disabled,
|
|
28
|
+
ref,
|
|
32
29
|
...rest
|
|
33
30
|
} = props
|
|
34
31
|
|
|
35
|
-
if (required && !rules
|
|
32
|
+
if (required && !rules.required) {
|
|
36
33
|
rules.required = i18n._(/* i18n */ 'This field is required')
|
|
37
34
|
}
|
|
38
35
|
|
|
39
36
|
const {
|
|
40
37
|
field,
|
|
41
38
|
fieldState: { error },
|
|
42
|
-
} = useController({
|
|
39
|
+
} = useController({
|
|
40
|
+
name,
|
|
41
|
+
control,
|
|
42
|
+
rules,
|
|
43
|
+
defaultValue,
|
|
44
|
+
shouldUnregister,
|
|
45
|
+
disabled,
|
|
46
|
+
})
|
|
43
47
|
|
|
44
48
|
return (
|
|
45
49
|
<InputBase
|
|
@@ -51,4 +55,9 @@ export const InputBaseElement = React.forwardRef<
|
|
|
51
55
|
error={Boolean(error) || rest.error}
|
|
52
56
|
/>
|
|
53
57
|
)
|
|
54
|
-
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** @public */
|
|
61
|
+
export const InputBaseElement = React.forwardRef<HTMLInputElement, InputBaseElementProps>(
|
|
62
|
+
(props, ref) => InputBaseElementBase({ ...props, ref }),
|
|
63
|
+
) as InputBaseElementComponent
|
|
@@ -5,20 +5,25 @@ import {
|
|
|
5
5
|
IconSvg,
|
|
6
6
|
responsiveVal,
|
|
7
7
|
} from '@graphcommerce/next-ui'
|
|
8
|
-
import type {
|
|
9
|
-
import {
|
|
8
|
+
import type { FieldValues } from '@graphcommerce/react-hook-form'
|
|
9
|
+
import { 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'
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
TextFieldProps,
|
|
16
|
-
'type' | 'defaultValue'
|
|
17
|
-
> & {
|
|
15
|
+
type AdditionalProps = {
|
|
18
16
|
DownProps?: IconButtonProps
|
|
19
17
|
UpProps?: IconButtonProps
|
|
20
18
|
sx?: SxProps<Theme>
|
|
21
|
-
}
|
|
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
|
|
22
27
|
|
|
23
28
|
type OwnerState = { size?: 'small' | 'medium' }
|
|
24
29
|
const componentName = 'TextInputNumber'
|
|
@@ -29,7 +34,7 @@ const { withState } = extendableComponent<OwnerState, typeof componentName, type
|
|
|
29
34
|
)
|
|
30
35
|
|
|
31
36
|
/** @public */
|
|
32
|
-
|
|
37
|
+
function NumberFieldElementBase(props: NumberFieldElementProps) {
|
|
33
38
|
const {
|
|
34
39
|
DownProps = {},
|
|
35
40
|
UpProps = {},
|
|
@@ -182,3 +187,5 @@ export function NumberFieldElement<T extends FieldValues>(props: NumberFieldElem
|
|
|
182
187
|
/>
|
|
183
188
|
)
|
|
184
189
|
}
|
|
190
|
+
|
|
191
|
+
export const NumberFieldElement = NumberFieldElementBase as NumberFieldElementComponent
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FieldValues
|
|
1
|
+
import type { 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 {
|
|
@@ -11,27 +11,33 @@ import {
|
|
|
11
11
|
useTheme,
|
|
12
12
|
} from '@mui/material'
|
|
13
13
|
import type { ChangeEvent } from 'react'
|
|
14
|
+
import type { BaseControllerProps } from './types'
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
type OptionBase = { id: string | number; label: string | number }
|
|
17
|
+
|
|
18
|
+
type AdditionalProps<TOption extends OptionBase = OptionBase> = {
|
|
19
|
+
options: TOption[]
|
|
18
20
|
helperText?: string
|
|
19
21
|
required?: boolean
|
|
20
22
|
label?: string
|
|
21
|
-
labelKey?:
|
|
22
|
-
valueKey?:
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
26
|
-
onChange?: (value: any) => void
|
|
27
|
-
returnObject?: boolean
|
|
23
|
+
labelKey?: keyof TOption
|
|
24
|
+
valueKey?: keyof TOption
|
|
25
|
+
onChange?: (value: string | number) => void
|
|
26
|
+
disabled?: boolean
|
|
28
27
|
row?: boolean
|
|
29
|
-
|
|
28
|
+
emptyOptionLabel?: string
|
|
29
|
+
}
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
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>(
|
|
33
37
|
props: RadioButtonGroupProps<TFieldValues>,
|
|
34
|
-
)
|
|
38
|
+
) => React.ReactNode
|
|
39
|
+
|
|
40
|
+
function RadioButtonGroupBase(props: RadioButtonGroupProps): JSX.Element {
|
|
35
41
|
const {
|
|
36
42
|
helperText,
|
|
37
43
|
options,
|
|
@@ -41,7 +47,6 @@ export function RadioButtonGroup<TFieldValues extends FieldValues>(
|
|
|
41
47
|
valueKey = 'id',
|
|
42
48
|
required,
|
|
43
49
|
emptyOptionLabel,
|
|
44
|
-
returnObject,
|
|
45
50
|
row,
|
|
46
51
|
control,
|
|
47
52
|
defaultValue,
|
|
@@ -65,26 +70,16 @@ export function RadioButtonGroup<TFieldValues extends FieldValues>(
|
|
|
65
70
|
|
|
66
71
|
const parsedHelperText = error ? error.message : helperText
|
|
67
72
|
|
|
68
|
-
const
|
|
69
|
-
const radioValue =
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
: radioValue
|
|
73
|
-
// setValue(name, returnValue, { shouldValidate: true })
|
|
74
|
-
onChange(returnValue)
|
|
75
|
-
if (typeof rest.onChange === 'function') {
|
|
76
|
-
rest.onChange(returnValue)
|
|
77
|
-
}
|
|
73
|
+
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
|
|
74
|
+
const radioValue = event.target.value
|
|
75
|
+
onChange(radioValue)
|
|
76
|
+
rest.onChange?.(radioValue)
|
|
78
77
|
}
|
|
79
78
|
|
|
80
79
|
return (
|
|
81
|
-
<FormControl error={invalid}>
|
|
82
|
-
{label &&
|
|
83
|
-
|
|
84
|
-
{label}
|
|
85
|
-
</FormLabel>
|
|
86
|
-
)}
|
|
87
|
-
<RadioGroup onChange={onRadioChange} name={name} row={row} value={value || ''}>
|
|
80
|
+
<FormControl error={invalid} required={required}>
|
|
81
|
+
{label && <FormLabel error={invalid}>{label}</FormLabel>}
|
|
82
|
+
<RadioGroup onChange={handleChange} name={name} row={row} value={value ?? ''}>
|
|
88
83
|
{emptyOptionLabel && (
|
|
89
84
|
<FormControlLabel
|
|
90
85
|
control={
|
|
@@ -101,15 +96,6 @@ export function RadioButtonGroup<TFieldValues extends FieldValues>(
|
|
|
101
96
|
)}
|
|
102
97
|
{options.map((option) => {
|
|
103
98
|
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
|
-
)
|
|
113
99
|
return (
|
|
114
100
|
<FormControlLabel
|
|
115
101
|
control={
|
|
@@ -117,7 +103,7 @@ export function RadioButtonGroup<TFieldValues extends FieldValues>(
|
|
|
117
103
|
sx={{
|
|
118
104
|
color: invalid ? theme.palette.error.main : undefined,
|
|
119
105
|
}}
|
|
120
|
-
checked={
|
|
106
|
+
checked={value === optionKey}
|
|
121
107
|
/>
|
|
122
108
|
}
|
|
123
109
|
value={optionKey}
|
|
@@ -131,3 +117,6 @@ export function RadioButtonGroup<TFieldValues extends FieldValues>(
|
|
|
131
117
|
</FormControl>
|
|
132
118
|
)
|
|
133
119
|
}
|
|
120
|
+
|
|
121
|
+
/** @public */
|
|
122
|
+
export const RadioButtonGroup = RadioButtonGroupBase as RadioButtonGroupComponent
|
|
@@ -1,87 +1,31 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type { ControllerProps, FieldValues } from '@graphcommerce/react-hook-form'
|
|
3
|
-
import { useController } from '@graphcommerce/react-hook-form'
|
|
4
|
-
import { i18n } from '@lingui/core'
|
|
1
|
+
import type { FieldValues } from '@graphcommerce/react-hook-form'
|
|
5
2
|
import type { TextFieldProps } from '@mui/material'
|
|
6
|
-
import { MenuItem
|
|
3
|
+
import { MenuItem } from '@mui/material'
|
|
4
|
+
import { TextFieldElement, type TextFieldElementProps } from './TextFieldElement'
|
|
7
5
|
|
|
8
6
|
type OptionBase = { id: string | number; label: string | number }
|
|
9
7
|
|
|
10
|
-
|
|
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'>
|
|
8
|
+
type AdditionalProps<O extends OptionBase = OptionBase> = { options: O[] }
|
|
19
9
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
|
10
|
+
export type SelectElementProps<
|
|
11
|
+
TFieldValues extends FieldValues = FieldValues,
|
|
12
|
+
TOption extends OptionBase = OptionBase,
|
|
13
|
+
> = Omit<TextFieldElementProps<TFieldValues>, 'select'> & AdditionalProps<TOption>
|
|
36
14
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
15
|
+
type SelectElementComponent = <TFieldValues extends FieldValues>(
|
|
16
|
+
props: SelectElementProps<TFieldValues>,
|
|
17
|
+
) => React.ReactNode
|
|
40
18
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
} =
|
|
45
|
-
name,
|
|
46
|
-
rules,
|
|
47
|
-
control,
|
|
48
|
-
defaultValue,
|
|
49
|
-
disabled,
|
|
50
|
-
shouldUnregister,
|
|
51
|
-
})
|
|
19
|
+
/** @public */
|
|
20
|
+
function SelectElementBase(props: SelectElementProps): JSX.Element {
|
|
21
|
+
const { options } = props as AdditionalProps
|
|
22
|
+
const { SelectProps } = props as TextFieldProps
|
|
52
23
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
rest.InputLabelProps = rest.InputLabelProps || {}
|
|
56
|
-
rest.InputLabelProps.shrink = true
|
|
57
|
-
}
|
|
24
|
+
const isNativeSelect = !!SelectProps?.native
|
|
25
|
+
const ChildComponent = isNativeSelect ? 'option' : MenuItem
|
|
58
26
|
|
|
59
27
|
return (
|
|
60
|
-
<
|
|
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
|
-
>
|
|
28
|
+
<TextFieldElement {...props} select>
|
|
85
29
|
{/* eslint-disable-next-line jsx-a11y/control-has-associated-label */}
|
|
86
30
|
{isNativeSelect && <option />}
|
|
87
31
|
{options.map((item) => (
|
|
@@ -89,6 +33,8 @@ export function SelectElement<TFieldValues extends FieldValues, O extends Option
|
|
|
89
33
|
{item.label}
|
|
90
34
|
</ChildComponent>
|
|
91
35
|
))}
|
|
92
|
-
</
|
|
36
|
+
</TextFieldElement>
|
|
93
37
|
)
|
|
94
38
|
}
|
|
39
|
+
|
|
40
|
+
export const SelectElement = SelectElementBase as SelectElementComponent
|
|
@@ -1,28 +1,41 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { 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'
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
type AdditionalProps = {
|
|
8
10
|
label?: string
|
|
9
11
|
required?: boolean
|
|
10
12
|
formControlProps?: FormControlProps
|
|
11
|
-
}
|
|
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
|
|
12
38
|
|
|
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>) {
|
|
26
39
|
if (required && !rules.required) {
|
|
27
40
|
rules.required = i18n._(/* i18n */ 'This field is required')
|
|
28
41
|
}
|
|
@@ -53,3 +66,6 @@ export function SliderElement<TFieldValues extends FieldValues>({
|
|
|
53
66
|
</FormControl>
|
|
54
67
|
)
|
|
55
68
|
}
|
|
69
|
+
|
|
70
|
+
/** @public */
|
|
71
|
+
export const SliderElement = SliderElementBase as SliderElementComponent
|
|
@@ -1,22 +1,25 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { FieldValues } from '@graphcommerce/react-hook-form'
|
|
2
2
|
import { useController } from '@graphcommerce/react-hook-form'
|
|
3
|
-
import type { FormControlLabelProps } from '@mui/material'
|
|
3
|
+
import type { FormControlLabelProps, SwitchProps } from '@mui/material'
|
|
4
4
|
import { FormControlLabel, Switch } from '@mui/material'
|
|
5
|
+
import React from 'react'
|
|
6
|
+
import type { FieldElementProps } from './types'
|
|
5
7
|
|
|
6
|
-
type
|
|
8
|
+
type AdditionalProps = Omit<FormControlLabelProps, 'control'>
|
|
7
9
|
|
|
8
|
-
export type SwitchElementProps<
|
|
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
|
|
9
22
|
|
|
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>) {
|
|
20
23
|
const { field } = useController({
|
|
21
24
|
name,
|
|
22
25
|
control,
|
|
@@ -28,3 +31,6 @@ export function SwitchElement<TFieldValues extends FieldValues>({
|
|
|
28
31
|
|
|
29
32
|
return <FormControlLabel control={<Switch {...field} checked={!!field.value} />} {...other} />
|
|
30
33
|
}
|
|
34
|
+
|
|
35
|
+
/** @public */
|
|
36
|
+
export const SwitchElement = SwitchElementBase as SwitchElementComponent
|
|
@@ -1,32 +1,35 @@
|
|
|
1
1
|
/* eslint-disable no-nested-ternary */
|
|
2
2
|
import { InputCheckmark } from '@graphcommerce/next-ui'
|
|
3
|
-
import type { FieldValues
|
|
3
|
+
import type { FieldValues } 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'
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
> &
|
|
14
|
-
|
|
15
|
-
|
|
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
|
|
16
19
|
|
|
17
20
|
/** @public */
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
...rest
|
|
29
|
-
|
|
21
|
+
function TextFieldElementBase(props: TextFieldElementProps): JSX.Element {
|
|
22
|
+
const {
|
|
23
|
+
name,
|
|
24
|
+
control,
|
|
25
|
+
defaultValue,
|
|
26
|
+
rules = {},
|
|
27
|
+
shouldUnregister,
|
|
28
|
+
disabled,
|
|
29
|
+
} = props as BaseControllerProps
|
|
30
|
+
const { showValid } = props as ShowValidProps
|
|
31
|
+
const { type, required, ...rest } = props as TextFieldProps
|
|
32
|
+
|
|
30
33
|
if (required && !rules.required) {
|
|
31
34
|
rules.required = i18n._(/* i18n */ 'This field is required')
|
|
32
35
|
}
|
|
@@ -39,7 +42,7 @@ export function TextFieldElement<TFieldValues extends FieldValues>({
|
|
|
39
42
|
}
|
|
40
43
|
|
|
41
44
|
const {
|
|
42
|
-
field: { onChange, ref, value = '',
|
|
45
|
+
field: { onChange, ref, value = '', onBlur },
|
|
43
46
|
fieldState: { error },
|
|
44
47
|
} = useController({ name, control, rules, defaultValue, shouldUnregister, disabled })
|
|
45
48
|
|
|
@@ -58,7 +61,9 @@ export function TextFieldElement<TFieldValues extends FieldValues>({
|
|
|
58
61
|
return (
|
|
59
62
|
<TextField
|
|
60
63
|
{...rest}
|
|
61
|
-
{
|
|
64
|
+
onBlur={onBlur}
|
|
65
|
+
name={name}
|
|
66
|
+
disabled={disabled}
|
|
62
67
|
value={value}
|
|
63
68
|
inputProps={{ ...rest.inputProps, onAnimationStart }}
|
|
64
69
|
onChange={(ev) => {
|
|
@@ -83,3 +88,5 @@ export function TextFieldElement<TFieldValues extends FieldValues>({
|
|
|
83
88
|
/>
|
|
84
89
|
)
|
|
85
90
|
}
|
|
91
|
+
|
|
92
|
+
export const TextFieldElement = TextFieldElementBase as TextFieldElementComponent
|
|
@@ -2,7 +2,6 @@ export * from './ActionCardListForm'
|
|
|
2
2
|
export * from './CheckboxButtonGroup'
|
|
3
3
|
export * from './CheckboxElement'
|
|
4
4
|
export * from './EmailElement'
|
|
5
|
-
export * from './MultiSelectElement'
|
|
6
5
|
export * from './NumberFieldElement'
|
|
7
6
|
export * from './PasswordElement'
|
|
8
7
|
export * from './PasswordRepeatElement'
|
|
@@ -0,0 +1,26 @@
|
|
|
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,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { styled, Tooltip, tooltipClasses } from '@mui/material'
|
|
2
2
|
|
|
3
3
|
export const LightTooltip = styled<typeof Tooltip>(({ className, ...props }) => (
|
|
4
4
|
<Tooltip {...props} classes={{ popper: className }} />
|
|
@@ -1,20 +1,6 @@
|
|
|
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'
|
|
13
1
|
import { useRouter } from 'next/router'
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import { PreviewModeActions } from './PreviewModeActions'
|
|
17
|
-
import { PreviewModeToolbar } from './PreviewModeToolbar'
|
|
2
|
+
import { PreviewModeDisabled } from './PreviewModeDisabled'
|
|
3
|
+
import { PreviewModeEnabled } from './PreviewModeEnabled'
|
|
18
4
|
|
|
19
5
|
function getPreviewUrl() {
|
|
20
6
|
const url = new URL(window.location.href)
|
|
@@ -23,128 +9,6 @@ function getPreviewUrl() {
|
|
|
23
9
|
return url
|
|
24
10
|
}
|
|
25
11
|
|
|
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
|
-
|
|
148
12
|
export function PreviewMode() {
|
|
149
13
|
const router = useRouter()
|
|
150
14
|
|
|
@@ -0,0 +1,56 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
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
|
+
}
|
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.
|
|
5
|
+
"version": "9.0.4-canary.1",
|
|
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.
|
|
16
|
-
"@graphcommerce/graphql": "^9.0.
|
|
17
|
-
"@graphcommerce/next-ui": "^9.0.
|
|
18
|
-
"@graphcommerce/prettier-config-pwa": "^9.0.
|
|
19
|
-
"@graphcommerce/react-hook-form": "^9.0.
|
|
20
|
-
"@graphcommerce/typescript-config-pwa": "^9.0.
|
|
15
|
+
"@graphcommerce/eslint-config-pwa": "^9.0.4-canary.1",
|
|
16
|
+
"@graphcommerce/graphql": "^9.0.4-canary.1",
|
|
17
|
+
"@graphcommerce/next-ui": "^9.0.4-canary.1",
|
|
18
|
+
"@graphcommerce/prettier-config-pwa": "^9.0.4-canary.1",
|
|
19
|
+
"@graphcommerce/react-hook-form": "^9.0.4-canary.1",
|
|
20
|
+
"@graphcommerce/typescript-config-pwa": "^9.0.4-canary.1",
|
|
21
21
|
"@lingui/core": "^4.2.1",
|
|
22
22
|
"@lingui/macro": "^4.2.1",
|
|
23
23
|
"@lingui/react": "^4.2.1",
|