@chem-po/react-web 0.0.15 → 0.0.16
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/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/components/form/FormFooter.tsx +9 -2
- package/src/components/form/input/Editable.tsx +2 -4
- package/src/components/form/input/hooks/index.ts +1 -1
- package/src/components/form/input/hooks/useInputStyles.ts +125 -0
- package/src/components/form/input/input.tsx +4 -4
- package/src/components/form/input/multipleSelect/index.tsx +42 -8
- package/src/components/form/input/number/index.tsx +43 -39
- package/src/components/form/input/select/index.tsx +32 -18
- package/src/components/form/input/shared/InputContainer.tsx +13 -0
- package/src/components/form/input/text/index.tsx +26 -20
- package/src/components/form/input/text/textarea.tsx +0 -1
- package/src/components/form/view/file.tsx +3 -12
- package/src/components/form/view/index.tsx +4 -13
- package/src/components/form/view/multipleSelect.tsx +26 -13
- package/src/components/form/view/select.tsx +18 -8
- package/src/components/form/view/types.ts +11 -0
- package/src/components/form/input/hooks/useInputStyle.ts +0 -39
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chem-po/react-web",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.16",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
"react-window-infinite-loader": "^1.0.9",
|
|
41
41
|
"@hello-pangea/dnd": "^18.0.1",
|
|
42
42
|
"zustand": "^4.3.3",
|
|
43
|
-
"@chem-po/core": "0.0.
|
|
44
|
-
"@chem-po/react": "0.0.
|
|
43
|
+
"@chem-po/core": "0.0.16",
|
|
44
|
+
"@chem-po/react": "0.0.16"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
47
|
"react": "^19.0.0",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@types/howler": "^2.2.11",
|
|
52
|
-
"@types/react": "~19.0.
|
|
52
|
+
"@types/react": "~19.0.12",
|
|
53
53
|
"@types/react-beautiful-dnd": "^13.1.8",
|
|
54
54
|
"@types/react-transition-group": "^4.4.11",
|
|
55
55
|
"@types/react-window": "^1.8.8",
|
|
@@ -3,7 +3,13 @@ import React from 'react'
|
|
|
3
3
|
|
|
4
4
|
import { FC, PropsWithChildren } from 'react'
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
FormProps,
|
|
8
|
+
IFormElement,
|
|
9
|
+
useBorderColor,
|
|
10
|
+
useChempoForm,
|
|
11
|
+
UseFormSubmit,
|
|
12
|
+
} from '@chem-po/react'
|
|
7
13
|
import { useFormState } from 'react-hook-form'
|
|
8
14
|
import { UploadProgress } from './UploadProgress'
|
|
9
15
|
|
|
@@ -56,6 +62,7 @@ export const FormFooter = <F extends IFormElement>({
|
|
|
56
62
|
}) => {
|
|
57
63
|
const { uploads, onBack, buttonText, onSubmit } = props
|
|
58
64
|
const { isSubmitting: submitting, isValid: valid } = useFormState()
|
|
65
|
+
const borderColor = useBorderColor()
|
|
59
66
|
return (
|
|
60
67
|
<Flex w="100%" flexFlow="column">
|
|
61
68
|
<Collapse endingHeight={5} style={{ width: '100%' }} in={submitting}>
|
|
@@ -66,7 +73,7 @@ export const FormFooter = <F extends IFormElement>({
|
|
|
66
73
|
{RenderFooter ? (
|
|
67
74
|
<RenderFooter {...props} />
|
|
68
75
|
) : (
|
|
69
|
-
<HStack py={2} borderTop="1px solid" borderColor=
|
|
76
|
+
<HStack py={2} borderTop="1px solid" borderColor={borderColor} px={3} w="100%">
|
|
70
77
|
{onBack ? <CancelButton onBack={onBack}>Cancel</CancelButton> : null}
|
|
71
78
|
<SubmitButton
|
|
72
79
|
size="sm"
|
|
@@ -18,7 +18,6 @@ export const Editable = <T extends Field>({
|
|
|
18
18
|
onEditOpen,
|
|
19
19
|
}: EditableProps<CSSProperties, T>) => {
|
|
20
20
|
const {
|
|
21
|
-
formattedValue,
|
|
22
21
|
inputRef,
|
|
23
22
|
setValue,
|
|
24
23
|
isLoading,
|
|
@@ -31,7 +30,6 @@ export const Editable = <T extends Field>({
|
|
|
31
30
|
setEditHovered,
|
|
32
31
|
submit,
|
|
33
32
|
submitValue,
|
|
34
|
-
parse,
|
|
35
33
|
} = useEditable({
|
|
36
34
|
value: initValue,
|
|
37
35
|
field,
|
|
@@ -66,11 +64,11 @@ export const Editable = <T extends Field>({
|
|
|
66
64
|
{isEditing || alwaysEditing ? (
|
|
67
65
|
<StandaloneInput
|
|
68
66
|
ref={inputRef}
|
|
69
|
-
value={
|
|
67
|
+
value={value}
|
|
70
68
|
inEditable
|
|
71
69
|
field={field}
|
|
72
70
|
style={{ padding: 0, ...style }}
|
|
73
|
-
onChange={alwaysEditing ? v => submitValue(
|
|
71
|
+
onChange={alwaysEditing ? v => submitValue(v) : setValue}
|
|
74
72
|
/>
|
|
75
73
|
) : (
|
|
76
74
|
<FieldView style={style} field={field} value={value} />
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export * from './useInputImperativeHandle'
|
|
2
|
-
export * from './
|
|
2
|
+
export * from './useInputStyles'
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { InputSize } from '@chem-po/core'
|
|
2
|
+
import { useBackgroundColor, useTextColor } from '@chem-po/react'
|
|
3
|
+
import { useMemo } from 'react'
|
|
4
|
+
|
|
5
|
+
const inputStyles = {
|
|
6
|
+
input: {
|
|
7
|
+
width: '100%',
|
|
8
|
+
fontSize: '16px',
|
|
9
|
+
outline: 'none',
|
|
10
|
+
},
|
|
11
|
+
inputXSmall: {
|
|
12
|
+
padding: '4px 6px',
|
|
13
|
+
borderRadius: '3px',
|
|
14
|
+
},
|
|
15
|
+
inputSmall: {
|
|
16
|
+
padding: '6px 9px',
|
|
17
|
+
borderRadius: '4px',
|
|
18
|
+
},
|
|
19
|
+
inputMedium: {
|
|
20
|
+
padding: '8px 12px',
|
|
21
|
+
borderRadius: '6px',
|
|
22
|
+
},
|
|
23
|
+
inputLarge: {
|
|
24
|
+
padding: '10px 14px',
|
|
25
|
+
borderRadius: '8px',
|
|
26
|
+
},
|
|
27
|
+
inputTextSmall: {
|
|
28
|
+
fontSize: '14px',
|
|
29
|
+
},
|
|
30
|
+
inputTextMedium: {
|
|
31
|
+
fontSize: '16px',
|
|
32
|
+
},
|
|
33
|
+
inputTextLarge: {
|
|
34
|
+
fontSize: '18px',
|
|
35
|
+
},
|
|
36
|
+
buttonContainer: {
|
|
37
|
+
display: 'flex',
|
|
38
|
+
flexDirection: 'row' as const,
|
|
39
|
+
alignItems: 'center',
|
|
40
|
+
},
|
|
41
|
+
buttonContainerSmall: {
|
|
42
|
+
gap: '6px',
|
|
43
|
+
},
|
|
44
|
+
buttonContainerMedium: {
|
|
45
|
+
gap: '8px',
|
|
46
|
+
},
|
|
47
|
+
buttonContainerLarge: {
|
|
48
|
+
gap: '10px',
|
|
49
|
+
},
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const iconSizeMap = {
|
|
53
|
+
sm: 16,
|
|
54
|
+
md: 20,
|
|
55
|
+
lg: 24,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const clearButtonSizeMap = {
|
|
59
|
+
sm: 12,
|
|
60
|
+
md: 14,
|
|
61
|
+
lg: 16,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const inputSizeMap = {
|
|
65
|
+
sm: inputStyles.inputSmall,
|
|
66
|
+
md: inputStyles.inputMedium,
|
|
67
|
+
lg: inputStyles.inputLarge,
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const editableInputSizeMap = {
|
|
71
|
+
sm: inputStyles.inputXSmall,
|
|
72
|
+
md: inputStyles.inputSmall,
|
|
73
|
+
lg: inputStyles.inputMedium,
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const inputTextSizeMap = {
|
|
77
|
+
sm: inputStyles.inputTextSmall,
|
|
78
|
+
md: inputStyles.inputTextMedium,
|
|
79
|
+
lg: inputStyles.inputTextLarge,
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const buttonContainerSizeMap = {
|
|
83
|
+
sm: inputStyles.buttonContainerSmall,
|
|
84
|
+
md: inputStyles.buttonContainerMedium,
|
|
85
|
+
lg: inputStyles.buttonContainerLarge,
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface UseInputStyles {
|
|
89
|
+
text: React.CSSProperties
|
|
90
|
+
container: React.CSSProperties
|
|
91
|
+
size: InputSize
|
|
92
|
+
iconSize: number
|
|
93
|
+
clearButtonSize: number
|
|
94
|
+
buttonContainer: React.CSSProperties
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export const useInputStyles = (
|
|
98
|
+
inEditable: boolean | undefined,
|
|
99
|
+
size: InputSize | undefined,
|
|
100
|
+
formSize: InputSize = 'md',
|
|
101
|
+
): UseInputStyles => {
|
|
102
|
+
const formElementBg = useBackgroundColor(150)
|
|
103
|
+
const bg = useMemo(
|
|
104
|
+
() => (inEditable ? 'transparent' : formElementBg),
|
|
105
|
+
[inEditable, formElementBg],
|
|
106
|
+
)
|
|
107
|
+
const textColor = useTextColor()
|
|
108
|
+
const usedSize = size ?? formSize
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
text: { ...inputTextSizeMap[usedSize], color: textColor },
|
|
112
|
+
container: {
|
|
113
|
+
...inputStyles.input,
|
|
114
|
+
...(inEditable ? editableInputSizeMap[usedSize] : inputSizeMap[usedSize]),
|
|
115
|
+
backgroundColor: bg,
|
|
116
|
+
},
|
|
117
|
+
size: usedSize,
|
|
118
|
+
iconSize: iconSizeMap[usedSize],
|
|
119
|
+
clearButtonSize: clearButtonSizeMap[usedSize],
|
|
120
|
+
buttonContainer: {
|
|
121
|
+
...inputStyles.buttonContainer,
|
|
122
|
+
...buttonContainerSizeMap[usedSize],
|
|
123
|
+
},
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -62,7 +62,7 @@ const InputBase = <T extends Field>(props: FieldProps<T>, ref: ForwardedRef<Inpu
|
|
|
62
62
|
case 'number':
|
|
63
63
|
case 'currency':
|
|
64
64
|
case 'select':
|
|
65
|
-
return { boxShadow: `0 0 7px ${color}`, transition: 'all 300ms'
|
|
65
|
+
return { boxShadow: `0 0 7px ${color}`, transition: 'all 300ms' }
|
|
66
66
|
default:
|
|
67
67
|
return {}
|
|
68
68
|
}
|
|
@@ -83,7 +83,7 @@ const InputBase = <T extends Field>(props: FieldProps<T>, ref: ForwardedRef<Inpu
|
|
|
83
83
|
spacing={0}
|
|
84
84
|
pb={pb}
|
|
85
85
|
transition="all 500ms"
|
|
86
|
-
pt={showPlaceholder && !inEditable ?
|
|
86
|
+
pt={showPlaceholder && !inEditable ? 5 : 0}>
|
|
87
87
|
{Label && !inEditable ? (
|
|
88
88
|
typeof Label === 'string' ? (
|
|
89
89
|
<Text color="gray.800" fontSize="sm" px={2}>
|
|
@@ -93,7 +93,7 @@ const InputBase = <T extends Field>(props: FieldProps<T>, ref: ForwardedRef<Inpu
|
|
|
93
93
|
<Label />
|
|
94
94
|
)
|
|
95
95
|
) : null}
|
|
96
|
-
<Flex width="100%" borderRadius={4}
|
|
96
|
+
<Flex width="100%" borderRadius={4} overflow="hidden" {...styles}>
|
|
97
97
|
<Component ref={ref} {...props} />
|
|
98
98
|
</Flex>
|
|
99
99
|
{inEditable ? null : (
|
|
@@ -113,7 +113,7 @@ const InputBase = <T extends Field>(props: FieldProps<T>, ref: ForwardedRef<Inpu
|
|
|
113
113
|
opacity={showPlaceholder ? 1 : 0}
|
|
114
114
|
transition={`opacity 500ms ease ${showPlaceholder ? 250 : 0}ms`}
|
|
115
115
|
position="absolute"
|
|
116
|
-
top="-
|
|
116
|
+
top="-5px"
|
|
117
117
|
pointerEvents="none"
|
|
118
118
|
fontSize="sm"
|
|
119
119
|
fontFamily="fonts.heading"
|
|
@@ -1,13 +1,33 @@
|
|
|
1
1
|
import { Box, Button, Flex, Text, useColorMode } from '@chakra-ui/react'
|
|
2
|
-
import { InputRef } from '@chem-po/core'
|
|
2
|
+
import { InputRef, RenderSelectOptionProps } from '@chem-po/core'
|
|
3
3
|
import { MultipleSelectField } from '@chem-po/react'
|
|
4
|
-
import React, { forwardRef, useImperativeHandle } from 'react'
|
|
4
|
+
import React, { CSSProperties, forwardRef, useImperativeHandle, useMemo } from 'react'
|
|
5
|
+
import { useInputStyles } from '../hooks/useInputStyles'
|
|
5
6
|
import { FieldProps } from '../types'
|
|
6
7
|
|
|
8
|
+
export const getRenderSelectedOptionText = (
|
|
9
|
+
field: MultipleSelectField,
|
|
10
|
+
textStyle: CSSProperties,
|
|
11
|
+
) => {
|
|
12
|
+
return (
|
|
13
|
+
props: RenderSelectOptionProps<
|
|
14
|
+
MultipleSelectField['options'][number]['value'],
|
|
15
|
+
MultipleSelectField['options'][number]
|
|
16
|
+
>,
|
|
17
|
+
) => {
|
|
18
|
+
const { renderOption: RenderOption } = field
|
|
19
|
+
if (typeof RenderOption === 'function') {
|
|
20
|
+
return <RenderOption {...props} />
|
|
21
|
+
}
|
|
22
|
+
return <Text style={textStyle}>{props.option.label}</Text>
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
7
26
|
export const MultipleSelectComponent = forwardRef<InputRef, FieldProps<MultipleSelectField>>(
|
|
8
|
-
({ field, input, inEditable }, ref) => {
|
|
9
|
-
const { options, renderOption: RenderOption
|
|
27
|
+
({ field, input, inEditable, formSize }, ref) => {
|
|
28
|
+
const { options, renderOption: RenderOption } = field
|
|
10
29
|
const { onChange, value, onFocus, onBlur } = input
|
|
30
|
+
const { text, size } = useInputStyles(inEditable, field.size, formSize)
|
|
11
31
|
const { colorMode } = useColorMode()
|
|
12
32
|
useImperativeHandle(ref, () => ({
|
|
13
33
|
focus: () => {
|
|
@@ -18,23 +38,37 @@ export const MultipleSelectComponent = forwardRef<InputRef, FieldProps<MultipleS
|
|
|
18
38
|
},
|
|
19
39
|
}))
|
|
20
40
|
|
|
41
|
+
const RenderSelectedOptionText = useMemo(
|
|
42
|
+
() => getRenderSelectedOptionText(field, text),
|
|
43
|
+
[field, text],
|
|
44
|
+
)
|
|
21
45
|
const body = (
|
|
22
46
|
<Flex w="100%" flexFlow="row wrap">
|
|
23
47
|
{options.map(o => (
|
|
24
|
-
<Box key={
|
|
48
|
+
<Box key={o.value} p={0.5}>
|
|
25
49
|
<Button
|
|
26
50
|
w="100%"
|
|
27
51
|
minH={0}
|
|
28
52
|
size="xs"
|
|
29
53
|
p={0}
|
|
30
|
-
opacity={value?.includes(o) ? 1 : 0.7}
|
|
54
|
+
opacity={value?.includes(o.value) ? 1 : 0.7}
|
|
31
55
|
variant="unstyled"
|
|
32
56
|
onClick={e => {
|
|
33
57
|
e.stopPropagation()
|
|
34
|
-
onChange(
|
|
58
|
+
onChange(
|
|
59
|
+
value?.includes(o.value)
|
|
60
|
+
? value.filter(v => v !== o.value)
|
|
61
|
+
: [...(value ?? []), o.value],
|
|
62
|
+
)
|
|
35
63
|
}}
|
|
36
64
|
_hover={{ opacity: 0.8 }}>
|
|
37
|
-
<
|
|
65
|
+
<RenderSelectedOptionText
|
|
66
|
+
value={o.value}
|
|
67
|
+
size={size}
|
|
68
|
+
option={o}
|
|
69
|
+
colorMode={colorMode}
|
|
70
|
+
isSelected
|
|
71
|
+
/>
|
|
38
72
|
</Button>
|
|
39
73
|
</Box>
|
|
40
74
|
))}
|
|
@@ -5,60 +5,64 @@ import React, { ForwardedRef, forwardRef, useImperativeHandle, useRef, useState
|
|
|
5
5
|
import CurrencyInput from 'react-currency-input-field'
|
|
6
6
|
import { InputSlider } from '../InputSlider'
|
|
7
7
|
import { useInputImperativeHandle } from '../hooks/useInputImperativeHandle'
|
|
8
|
-
import {
|
|
8
|
+
import { useInputStyles } from '../hooks/useInputStyles'
|
|
9
|
+
import { InputContainer } from '../shared/InputContainer'
|
|
9
10
|
import { FieldProps } from '../types'
|
|
10
11
|
import './styles.css'
|
|
11
12
|
|
|
12
13
|
export const NumberComponent = forwardRef<InputRef, FieldProps<NumberField> & { prefix?: string }>(
|
|
13
|
-
(
|
|
14
|
-
|
|
14
|
+
(
|
|
15
|
+
{ input: { onChange, value, ...input }, field, formSize, prefix, inEditable, ...props },
|
|
16
|
+
ref,
|
|
17
|
+
) => {
|
|
18
|
+
const { type, defaultValue, placeholder, size } = field
|
|
15
19
|
const className = useColorModeValue('number-input', 'number-input-dark')
|
|
16
|
-
const
|
|
20
|
+
const { container, text } = useInputStyles(inEditable, size, formSize)
|
|
17
21
|
const inputRef = useInputImperativeHandle(ref)
|
|
18
22
|
const [endsWith, setEndsWith] = useState('')
|
|
19
23
|
const displayed = typeof value === 'number' && !Number.isNaN(value) ? `${value}${endsWith}` : ''
|
|
20
|
-
|
|
21
|
-
|
|
24
|
+
const body =
|
|
25
|
+
type === 'slider' ? (
|
|
22
26
|
<InputSlider
|
|
23
27
|
label={placeholder}
|
|
24
28
|
onChange={onChange}
|
|
25
29
|
value={value}
|
|
26
30
|
defaultValue={defaultValue}
|
|
27
31
|
/>
|
|
32
|
+
) : (
|
|
33
|
+
<CurrencyInput
|
|
34
|
+
className={className}
|
|
35
|
+
placeholder={field.placeholder}
|
|
36
|
+
decimalsLimit={field.precision}
|
|
37
|
+
allowDecimals={!!field.precision}
|
|
38
|
+
prefix={prefix}
|
|
39
|
+
ref={inputRef}
|
|
40
|
+
onValueChange={(v, _, values) => {
|
|
41
|
+
if (v?.endsWith('.')) setEndsWith('.')
|
|
42
|
+
else if (v?.includes('.') && v?.endsWith('0')) setEndsWith('0')
|
|
43
|
+
else setEndsWith('')
|
|
44
|
+
onChange(values?.float)
|
|
45
|
+
}}
|
|
46
|
+
value={displayed}
|
|
47
|
+
{...props}
|
|
48
|
+
style={{
|
|
49
|
+
background: 'none',
|
|
50
|
+
// height: 'auto',
|
|
51
|
+
resize: 'none',
|
|
52
|
+
fontFamily: 'Encode Sans',
|
|
53
|
+
boxSizing: 'border-box',
|
|
54
|
+
opacity: value ? 1 : 0.7,
|
|
55
|
+
width: '100%',
|
|
56
|
+
borderRadius: '4px',
|
|
57
|
+
border: 'none',
|
|
58
|
+
outline: 'none',
|
|
59
|
+
...text,
|
|
60
|
+
...props.style,
|
|
61
|
+
}}
|
|
62
|
+
{...input}
|
|
63
|
+
/>
|
|
28
64
|
)
|
|
29
|
-
}
|
|
30
|
-
return (
|
|
31
|
-
<CurrencyInput
|
|
32
|
-
className={className}
|
|
33
|
-
placeholder={field.placeholder}
|
|
34
|
-
decimalsLimit={field.precision}
|
|
35
|
-
allowDecimals={!!field.precision}
|
|
36
|
-
prefix={prefix}
|
|
37
|
-
ref={inputRef}
|
|
38
|
-
onValueChange={(v, _, values) => {
|
|
39
|
-
if (v?.endsWith('.')) setEndsWith('.')
|
|
40
|
-
else if (v?.includes('.') && v?.endsWith('0')) setEndsWith('0')
|
|
41
|
-
else setEndsWith('')
|
|
42
|
-
onChange(values?.float)
|
|
43
|
-
}}
|
|
44
|
-
value={displayed}
|
|
45
|
-
{...props}
|
|
46
|
-
style={{
|
|
47
|
-
background: 'none',
|
|
48
|
-
// height: 'auto',
|
|
49
|
-
resize: 'none',
|
|
50
|
-
fontFamily: 'Encode Sans',
|
|
51
|
-
boxSizing: 'border-box',
|
|
52
|
-
width: '100%',
|
|
53
|
-
borderRadius: '4px',
|
|
54
|
-
border: 'none',
|
|
55
|
-
outline: 'none',
|
|
56
|
-
...style,
|
|
57
|
-
...props.style,
|
|
58
|
-
}}
|
|
59
|
-
{...input}
|
|
60
|
-
/>
|
|
61
|
-
)
|
|
65
|
+
return <InputContainer style={container}>{body}</InputContainer>
|
|
62
66
|
},
|
|
63
67
|
)
|
|
64
68
|
const BaseCurrencyAmountComponent = (
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {
|
|
2
|
-
Box,
|
|
3
2
|
Button,
|
|
4
3
|
Popover,
|
|
5
4
|
PopoverArrow,
|
|
@@ -9,21 +8,20 @@ import {
|
|
|
9
8
|
Text,
|
|
10
9
|
useColorMode,
|
|
11
10
|
} from '@chakra-ui/react'
|
|
12
|
-
import { InputRef } from '@chem-po/core'
|
|
11
|
+
import { InputRef, RenderSelectOptionProps } from '@chem-po/core'
|
|
13
12
|
import { SelectField } from '@chem-po/react'
|
|
14
13
|
import React, { forwardRef, useImperativeHandle, useMemo } from 'react'
|
|
14
|
+
import { useInputStyles } from '../hooks/useInputStyles'
|
|
15
|
+
import { InputContainer } from '../shared/InputContainer'
|
|
15
16
|
import { FieldProps } from '../types'
|
|
16
17
|
|
|
17
|
-
const DefaultRenderOption = (value: any) => {
|
|
18
|
-
return <Text>{typeof value === 'string' ? value : JSON.stringify(value)}</Text>
|
|
19
|
-
}
|
|
20
|
-
|
|
21
18
|
export const SelectComponent = forwardRef<InputRef, FieldProps<SelectField>>(
|
|
22
|
-
({ field, input, meta }, ref) => {
|
|
23
|
-
const { placeholder, options, renderOption: customRender
|
|
19
|
+
({ field, input, meta, inEditable, formSize }, ref) => {
|
|
20
|
+
const { placeholder, options, renderOption: customRender } = field
|
|
24
21
|
const { onChange, value, onFocus, onBlur } = input
|
|
22
|
+
const { text, size, container } = useInputStyles(inEditable, field.size, formSize)
|
|
25
23
|
const { active } = meta
|
|
26
|
-
const selected = useMemo(() => value && options.find(o => o === value), [value, options])
|
|
24
|
+
const selected = useMemo(() => value && options.find(o => o.value === value), [value, options])
|
|
27
25
|
const { colorMode } = useColorMode()
|
|
28
26
|
useImperativeHandle(ref, () => ({
|
|
29
27
|
focus: () => {
|
|
@@ -34,47 +32,63 @@ export const SelectComponent = forwardRef<InputRef, FieldProps<SelectField>>(
|
|
|
34
32
|
},
|
|
35
33
|
}))
|
|
36
34
|
|
|
35
|
+
const DefaultRenderOption = (props: RenderSelectOptionProps) => {
|
|
36
|
+
return <Text style={text}>{props.option.label}</Text>
|
|
37
|
+
}
|
|
38
|
+
|
|
37
39
|
const RenderOption = customRender ?? DefaultRenderOption
|
|
38
40
|
|
|
39
41
|
return (
|
|
40
42
|
<Popover strategy="fixed" placement="bottom" matchWidth isOpen={active} onClose={onBlur}>
|
|
41
43
|
<PopoverTrigger>
|
|
42
44
|
<Button
|
|
43
|
-
fontWeight={500}
|
|
44
45
|
w="100%"
|
|
45
|
-
opacity={value ? 1 : 0.7}
|
|
46
46
|
onClick={e => {
|
|
47
47
|
e.stopPropagation()
|
|
48
48
|
onFocus()
|
|
49
49
|
}}
|
|
50
50
|
variant="unstyled"
|
|
51
51
|
position="relative">
|
|
52
|
-
<
|
|
52
|
+
<InputContainer style={container}>
|
|
53
53
|
{selected ? (
|
|
54
|
-
<RenderOption
|
|
54
|
+
<RenderOption
|
|
55
|
+
size={size}
|
|
56
|
+
value={selected.value}
|
|
57
|
+
option={selected}
|
|
58
|
+
colorMode={colorMode}
|
|
59
|
+
isSelected={true}
|
|
60
|
+
/>
|
|
55
61
|
) : (
|
|
56
|
-
<Text opacity={0.
|
|
62
|
+
<Text fontWeight={400} style={text} opacity={0.6}>
|
|
63
|
+
{placeholder}
|
|
64
|
+
</Text>
|
|
57
65
|
)}
|
|
58
|
-
</
|
|
66
|
+
</InputContainer>
|
|
59
67
|
</Button>
|
|
60
68
|
</PopoverTrigger>
|
|
61
69
|
<PopoverContent w="100%" overflowY="auto" maxH="300px">
|
|
62
70
|
<PopoverBody p={0}>
|
|
63
71
|
{options.map(o => (
|
|
64
72
|
<Button
|
|
65
|
-
key={
|
|
73
|
+
key={o.value}
|
|
66
74
|
w="100%"
|
|
67
75
|
variant="unstyled"
|
|
68
76
|
onClick={e => {
|
|
69
77
|
e.stopPropagation()
|
|
70
|
-
onChange(o)
|
|
78
|
+
onChange(o.value)
|
|
71
79
|
onBlur()
|
|
72
80
|
}}
|
|
73
81
|
_hover={{ bg: 'blackAlpha.100' }}
|
|
74
82
|
_dark={{
|
|
75
83
|
_hover: { bg: 'whiteAlpha.100' },
|
|
76
84
|
}}>
|
|
77
|
-
<RenderOption
|
|
85
|
+
<RenderOption
|
|
86
|
+
value={o.value}
|
|
87
|
+
option={o}
|
|
88
|
+
size={size}
|
|
89
|
+
colorMode={colorMode}
|
|
90
|
+
isSelected={o.value === value}
|
|
91
|
+
/>
|
|
78
92
|
</Button>
|
|
79
93
|
))}
|
|
80
94
|
</PopoverBody>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Flex } from '@chakra-ui/react'
|
|
2
|
+
import React, { CSSProperties, PropsWithChildren } from 'react'
|
|
3
|
+
|
|
4
|
+
export const InputContainer = ({
|
|
5
|
+
children,
|
|
6
|
+
style,
|
|
7
|
+
}: PropsWithChildren<{ style: CSSProperties }>) => {
|
|
8
|
+
return (
|
|
9
|
+
<Flex width="100%" borderRadius={4} style={style}>
|
|
10
|
+
{children}
|
|
11
|
+
</Flex>
|
|
12
|
+
)
|
|
13
|
+
}
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import { ViewIcon, ViewOffIcon } from '@chakra-ui/icons'
|
|
2
|
-
import {
|
|
2
|
+
import { IconButton, Input } from '@chakra-ui/react'
|
|
3
3
|
import { InputRef } from '@chem-po/core'
|
|
4
4
|
import { TextField } from '@chem-po/react'
|
|
5
5
|
import React, { forwardRef, useImperativeHandle, useRef, useState } from 'react'
|
|
6
|
+
import { useInputStyles } from '../hooks'
|
|
7
|
+
import { InputContainer } from '../shared/InputContainer'
|
|
6
8
|
import { FieldProps } from '../types'
|
|
7
9
|
import { TextAreaComponent } from './textarea'
|
|
8
10
|
|
|
9
11
|
export const TextComponent = forwardRef<InputRef, FieldProps<TextField>>(
|
|
10
|
-
({ input, inEditable, meta, field }, ref) => {
|
|
12
|
+
({ input, inEditable, meta, field, formSize }, ref) => {
|
|
11
13
|
const { placeholder, type } = field
|
|
12
14
|
const [isHidden, setIsHidden] = useState(type === 'password')
|
|
15
|
+
const { text, container } = useInputStyles(inEditable, field.size, formSize)
|
|
13
16
|
const { value } = input
|
|
14
17
|
const inputRef = useRef<HTMLInputElement | HTMLTextAreaElement>(null)
|
|
15
18
|
|
|
@@ -32,15 +35,18 @@ export const TextComponent = forwardRef<InputRef, FieldProps<TextField>>(
|
|
|
32
35
|
border: 'none',
|
|
33
36
|
}}
|
|
34
37
|
borderRadius={0}
|
|
35
|
-
px={inEditable ? 0 : 3}
|
|
36
38
|
background="transparent"
|
|
37
39
|
_focus={{
|
|
38
40
|
border: 'none',
|
|
41
|
+
boxShadow: 'none',
|
|
39
42
|
}}
|
|
40
|
-
py={
|
|
43
|
+
py={0}
|
|
44
|
+
px={0}
|
|
45
|
+
height="auto"
|
|
46
|
+
outline="none"
|
|
41
47
|
type={isHidden ? 'password' : 'text'}
|
|
42
|
-
height={inEditable ? 'auto' : 10}
|
|
43
48
|
placeholder={placeholder}
|
|
49
|
+
style={text}
|
|
44
50
|
{...input}
|
|
45
51
|
onChange={e => {
|
|
46
52
|
input.onChange({ target: { value: e.target.value } })
|
|
@@ -49,22 +55,22 @@ export const TextComponent = forwardRef<InputRef, FieldProps<TextField>>(
|
|
|
49
55
|
/>
|
|
50
56
|
)
|
|
51
57
|
|
|
52
|
-
return
|
|
53
|
-
<
|
|
58
|
+
return (
|
|
59
|
+
<InputContainer style={container}>
|
|
54
60
|
{body}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
61
|
+
{type === 'password' && (
|
|
62
|
+
<IconButton
|
|
63
|
+
position="absolute"
|
|
64
|
+
right={2}
|
|
65
|
+
onClick={() => setIsHidden(!isHidden)}
|
|
66
|
+
variant="ghost"
|
|
67
|
+
icon={isHidden ? <ViewIcon /> : <ViewOffIcon />}
|
|
68
|
+
aria-label={isHidden ? 'show' : 'hide'}
|
|
69
|
+
title={isHidden ? 'Show' : 'Hide'}
|
|
70
|
+
size="xs"
|
|
71
|
+
/>
|
|
72
|
+
)}
|
|
73
|
+
</InputContainer>
|
|
68
74
|
)
|
|
69
75
|
},
|
|
70
76
|
)
|
|
@@ -1,21 +1,12 @@
|
|
|
1
1
|
import { Box, Flex, Text } from '@chakra-ui/react'
|
|
2
2
|
import { ImageViewOptions } from '@chem-po/core'
|
|
3
3
|
import { FileField } from '@chem-po/react'
|
|
4
|
-
import React, {
|
|
4
|
+
import React, { useMemo } from 'react'
|
|
5
5
|
import { ExpandOnMount } from '../../box/ExpandOnMount'
|
|
6
6
|
import { FileView } from '../input/file'
|
|
7
|
+
import { FieldViewProps } from './types'
|
|
7
8
|
|
|
8
|
-
export const FileFieldView = ({
|
|
9
|
-
field,
|
|
10
|
-
value,
|
|
11
|
-
noLabel,
|
|
12
|
-
style,
|
|
13
|
-
}: {
|
|
14
|
-
field: FileField
|
|
15
|
-
value: any
|
|
16
|
-
noLabel?: boolean
|
|
17
|
-
style?: CSSProperties
|
|
18
|
-
}) => {
|
|
9
|
+
export const FileFieldView = ({ field, value, noLabel, style }: FieldViewProps<FileField>) => {
|
|
19
10
|
const { imageOptions, placeholder } = field
|
|
20
11
|
const options = useMemo<ImageViewOptions>(
|
|
21
12
|
() => ({
|