forstok-ui-lib 5.0.2 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +30 -4
- package/dist/index.js +105 -84
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +96 -75
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -1
- package/src/assets/images/image-placeholder.jpg +0 -0
- package/src/assets/javascripts/function.ts +28 -0
- package/src/components/image/index.tsx +17 -0
- package/src/components/image/styles.ts +39 -0
- package/src/components/index.ts +1 -0
- package/src/components/input/index.tsx +46 -25
- package/src/components/input/ref.tsx +59 -0
- package/src/components/input/styles.ts +26 -4
- package/src/components/select/index.tsx +331 -0
- package/src/components/select/styles.ts +5 -0
- package/src/components/select/typed.ts +16 -0
- package/src/typeds/shares.typed.ts +20 -0
- package/tsconfig.json +1 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "forstok-ui-lib",
|
|
3
|
-
"version": "5.0
|
|
3
|
+
"version": "5.1.0",
|
|
4
4
|
"description": "Forstok UI Components Library",
|
|
5
5
|
"path": "dist",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
"react": "^19.0.0",
|
|
42
42
|
"react-dom": "^19.0.0",
|
|
43
43
|
"react-router-dom": "^7.1.1",
|
|
44
|
+
"react-select": "^5.10.0",
|
|
44
45
|
"rollup-plugin-dts": "^6.1.1",
|
|
45
46
|
"rollup-plugin-peer-deps-external": "^2.2.4",
|
|
46
47
|
"rollup-plugin-postcss": "^4.0.2",
|
|
Binary file
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { TMessageQuestion } from '../../components/message/typed';
|
|
2
|
+
|
|
3
|
+
export const formatNumber = (n?: string | number, flag?: boolean) => {
|
|
4
|
+
const _flag = flag === undefined ? true : flag
|
|
5
|
+
let result:string = ''
|
|
6
|
+
if (n !== '' && n !== null && n !== undefined) {
|
|
7
|
+
result = n?.toString().replaceAll(".", "")
|
|
8
|
+
if (result.length === 2 && result.substring(0, 1) === "0") {
|
|
9
|
+
result = parseInt(result).toString()
|
|
10
|
+
}
|
|
11
|
+
if (_flag) {
|
|
12
|
+
result = result.replace(/\D/g, "").replace(/\B(?=(\d{3})+(?!\d))/g, ".")
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return result
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const generateMessageQuestion = (type: string, title: string, subtitle: string, callback?: () => void, buttonSubmit?: string, cancelCallback?: () => void) => {
|
|
19
|
+
let result: TMessageQuestion = {
|
|
20
|
+
$type: type,
|
|
21
|
+
title: title,
|
|
22
|
+
subtitle: subtitle,
|
|
23
|
+
callback: callback,
|
|
24
|
+
buttonSubmit: buttonSubmit,
|
|
25
|
+
cancelCallback: cancelCallback
|
|
26
|
+
}
|
|
27
|
+
return result
|
|
28
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ImgHTMLAttributes, ReactEventHandler } from 'react';
|
|
2
|
+
import ImagePlaceholder from '../../assets/images/image-placeholder.jpg';
|
|
3
|
+
import { ImageContainer } from './styles';
|
|
4
|
+
|
|
5
|
+
type TImage = ImgHTMLAttributes<HTMLImageElement> & {
|
|
6
|
+
$mode?: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const ImageComponent = ({ $mode, ...props }: TImage) => {
|
|
10
|
+
const { width, height } = props;
|
|
11
|
+
const evError: ReactEventHandler<HTMLImageElement> = e => {
|
|
12
|
+
(e.target as HTMLImageElement).src = ImagePlaceholder;
|
|
13
|
+
}
|
|
14
|
+
return <ImageContainer $mode={$mode} width={width} height={height} onError={evError} {...props} />;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export default ImageComponent;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import styled, { css } from 'styled-components'
|
|
2
|
+
|
|
3
|
+
const storeStyles = css `
|
|
4
|
+
width: 32px;
|
|
5
|
+
height: 32px;
|
|
6
|
+
`
|
|
7
|
+
const productStyles = css`
|
|
8
|
+
background-color: var(--mt-clr-bg);
|
|
9
|
+
`
|
|
10
|
+
|
|
11
|
+
const getImageModifiedStyled = ({ $mode, width, height }: { $mode?: string, width?: string|number, height?: string|number }) => {
|
|
12
|
+
let style = ''
|
|
13
|
+
if ($mode === 'store') {
|
|
14
|
+
style += storeStyles
|
|
15
|
+
} else if ($mode === 'product') {
|
|
16
|
+
style += productStyles
|
|
17
|
+
}
|
|
18
|
+
if (width && height) {
|
|
19
|
+
style += `
|
|
20
|
+
width:${width};
|
|
21
|
+
height:${height};
|
|
22
|
+
`
|
|
23
|
+
} else if (width) {
|
|
24
|
+
style += `
|
|
25
|
+
width:${width};
|
|
26
|
+
height:${width};
|
|
27
|
+
`
|
|
28
|
+
} else if (height) {
|
|
29
|
+
style += `
|
|
30
|
+
width:${width};
|
|
31
|
+
height:${height};
|
|
32
|
+
`
|
|
33
|
+
}
|
|
34
|
+
return style
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const ImageContainer = styled.img<{ $mode?: string, width?: string|number, height?: string|number }>`
|
|
38
|
+
${getImageModifiedStyled}
|
|
39
|
+
`
|
package/src/components/index.ts
CHANGED
|
@@ -11,6 +11,7 @@ export { default as MessageComponent } from './message';
|
|
|
11
11
|
export { default as MessageQuestionComponent } from './message/question';
|
|
12
12
|
export { default as PopupComponent } from './popup';
|
|
13
13
|
export { default as ReactPortalComponent } from './portal';
|
|
14
|
+
export { default as ImageComponent } from './image';
|
|
14
15
|
|
|
15
16
|
export * from './dropdown/typed';
|
|
16
17
|
export * from './message/typed';
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { useState, useEffect, type InputHTMLAttributes, type ChangeEvent, type FocusEvent, type KeyboardEvent, type ClipboardEvent } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { formatNumber } from '../../assets/javascripts/function';
|
|
3
|
+
import { InputContainer, InputLabel, InputWrapper } from './styles';
|
|
3
4
|
import type { TEnterEvent, TKeyboadEvent, TState } from '../../typeds/base.typed';
|
|
4
5
|
|
|
5
6
|
type TInput = InputHTMLAttributes<HTMLInputElement> & {
|
|
6
|
-
mode?: string
|
|
7
|
+
$mode?: string
|
|
7
8
|
$isError?: boolean
|
|
8
9
|
$iconLeft?: boolean
|
|
9
10
|
$iconRight?: boolean
|
|
@@ -17,17 +18,20 @@ type TInput = InputHTMLAttributes<HTMLInputElement> & {
|
|
|
17
18
|
evChangeCustom? : (key: string, value: any) => void
|
|
18
19
|
evEnter?: TEnterEvent
|
|
19
20
|
evKeyUp?: TKeyboadEvent
|
|
20
|
-
|
|
21
|
+
$aliasLabel?: string
|
|
22
|
+
}
|
|
21
23
|
|
|
22
|
-
const InputComponent = ({ mode, $isError, $iconLeft,
|
|
24
|
+
const InputComponent = ({ $mode, $aliasLabel, $isError, $iconLeft, $iconRight, reset, setReset, isForceUpdate, setForceUpdate, evChange, evBlur, isField, evChangeCustom, evEnter, evKeyUp, ...props }: TInput) => {
|
|
23
25
|
const { type, name, defaultValue, defaultChecked } = props;
|
|
24
|
-
|
|
26
|
+
|
|
27
|
+
const _value = ($mode === 'currency' ? formatNumber(defaultValue?.toString() ?? '') : (defaultValue?.toString()) ?? '');
|
|
28
|
+
|
|
25
29
|
const [inputValue, evInputValue] = useState<string>(_value);
|
|
26
30
|
const [inputChecked, evInputChecked] = useState<boolean>(defaultChecked||false);
|
|
27
31
|
|
|
28
32
|
useEffect(() => {
|
|
29
33
|
if (isForceUpdate) {
|
|
30
|
-
const _value = defaultValue?.toString() ?? '';
|
|
34
|
+
const _value = ($mode === 'currency' ? formatNumber(defaultValue?.toString() ?? '') : (defaultValue?.toString()) ?? '');
|
|
31
35
|
evInputValue(_value);
|
|
32
36
|
evInputChecked(defaultChecked||false);
|
|
33
37
|
setForceUpdate && setForceUpdate(false);
|
|
@@ -37,62 +41,72 @@ const InputComponent = ({ mode, $isError, $iconLeft, $iconRight, reset, setReset
|
|
|
37
41
|
evInputChecked(false);
|
|
38
42
|
setReset && setReset(false);
|
|
39
43
|
}
|
|
40
|
-
}, [isForceUpdate, setForceUpdate, reset, setReset, defaultValue, defaultChecked, evInputChecked, mode])
|
|
44
|
+
}, [isForceUpdate, setForceUpdate, reset, setReset, defaultValue, defaultChecked, evInputChecked, $mode])
|
|
41
45
|
|
|
42
46
|
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
|
|
43
47
|
const _value = e.target.value;
|
|
44
48
|
evInputValue(_value);
|
|
45
|
-
}
|
|
49
|
+
}
|
|
46
50
|
|
|
47
51
|
const _evKeyUp = (e: KeyboardEvent<HTMLInputElement>) => {
|
|
52
|
+
if ($mode === 'currency') {
|
|
53
|
+
e.currentTarget.value = formatNumber(e.currentTarget.value);
|
|
54
|
+
}
|
|
48
55
|
if (e.key === 'Enter') {
|
|
49
56
|
e.preventDefault();
|
|
50
|
-
(isField && evEnter) && evEnter(e)
|
|
57
|
+
(isField && evEnter) && evEnter(e)
|
|
51
58
|
}
|
|
52
|
-
}
|
|
59
|
+
}
|
|
53
60
|
|
|
54
61
|
const _evKeyPress = (e: KeyboardEvent<HTMLInputElement>) => {
|
|
55
|
-
|
|
56
|
-
|
|
62
|
+
const _target = e.target as HTMLInputElement;
|
|
63
|
+
if (type === 'number' || $mode === 'currency') {
|
|
64
|
+
if(!(!(isNaN(Number(e.key)) || typeof e.key === 'undefined' || e.key === ''))) e.preventDefault();
|
|
65
|
+
}
|
|
66
|
+
if ($mode === 'currency') {
|
|
67
|
+
if ((_target.value.length === 1 && e.key === '0' && _target.value === "0") || e.key === ' ') {
|
|
57
68
|
e.preventDefault();
|
|
69
|
+
} else {
|
|
70
|
+
return true;
|
|
58
71
|
}
|
|
72
|
+
} else {
|
|
73
|
+
return true;
|
|
59
74
|
}
|
|
60
|
-
|
|
61
|
-
};
|
|
75
|
+
}
|
|
62
76
|
|
|
63
77
|
const _evBlur = (e: FocusEvent<HTMLInputElement>) => {
|
|
64
78
|
evBlur && evBlur(e);
|
|
65
79
|
(isField && evEnter) && evEnter(e);
|
|
66
80
|
if (isField && name) {
|
|
67
|
-
const value = type === 'number' ? parseInt(e.target.value) : e.target.value.trim()
|
|
81
|
+
const value = (type === 'number') ? parseInt(e.target.value) : ($mode === 'currency' ? e.target.value ? parseInt(formatNumber(e.target.value, false)) : null : e.target.value.trim())
|
|
68
82
|
evChangeCustom && evChangeCustom(name, value);
|
|
69
83
|
}
|
|
70
|
-
}
|
|
84
|
+
}
|
|
71
85
|
|
|
72
86
|
let parseProps = {...props};
|
|
73
87
|
delete parseProps.defaultValue;
|
|
74
88
|
delete parseProps.defaultChecked;
|
|
75
89
|
|
|
76
90
|
const inputEl = <InputContainer
|
|
77
|
-
mode={mode}
|
|
91
|
+
$mode={$mode}
|
|
78
92
|
$isError={$isError}
|
|
79
93
|
$iconLeft={$iconLeft}
|
|
80
94
|
$iconRight={$iconRight}
|
|
81
95
|
checked={inputChecked}
|
|
82
96
|
value={inputValue}
|
|
83
97
|
onChange={(e: ChangeEvent<HTMLInputElement>) => {
|
|
84
|
-
handleChange(e)
|
|
85
|
-
evChange && evChange(e)
|
|
98
|
+
handleChange(e)
|
|
99
|
+
evChange && evChange(e)
|
|
86
100
|
}}
|
|
87
101
|
onBlur={(e: FocusEvent<HTMLInputElement>) => {
|
|
88
|
-
handleChange(e)
|
|
89
|
-
_evBlur(e)
|
|
102
|
+
handleChange(e)
|
|
103
|
+
_evBlur(e)
|
|
90
104
|
}}
|
|
91
105
|
onKeyUp={(e: KeyboardEvent<HTMLInputElement>) => {
|
|
92
|
-
evKeyUp && evKeyUp(e)
|
|
93
|
-
_evKeyUp(e)
|
|
106
|
+
evKeyUp && evKeyUp(e)
|
|
107
|
+
_evKeyUp(e)
|
|
94
108
|
}}
|
|
95
|
-
{...(type ==='number' && { onKeyPress: _evKeyPress } )}
|
|
109
|
+
{...((type ==='number' || $mode === 'currency') && { onKeyPress: _evKeyPress } )}
|
|
96
110
|
{...(type ==='number' && { onPaste: (e: ClipboardEvent<HTMLInputElement>) => {
|
|
97
111
|
const pasteVal = (e.clipboardData || (window as any).clipboardData).getData('text')
|
|
98
112
|
const _value = pasteVal.replace(/[^0-9]/g, '')
|
|
@@ -101,9 +115,16 @@ const InputComponent = ({ mode, $isError, $iconLeft, $iconRight, reset, setReset
|
|
|
101
115
|
} })}
|
|
102
116
|
{...(type ==='number' && { onFocus: (e: FocusEvent<HTMLInputElement>) => e.target.addEventListener("wheel", function (e) { e.preventDefault() }, { passive: false })} )}
|
|
103
117
|
{...(type ==='number' && { min: "0" })}
|
|
118
|
+
{...($aliasLabel && { $aliasLabel: $aliasLabel }) }
|
|
104
119
|
{...parseProps} />
|
|
105
120
|
|
|
106
|
-
return
|
|
121
|
+
return (
|
|
122
|
+
$mode === 'currency' ? (
|
|
123
|
+
<InputWrapper>
|
|
124
|
+
{inputEl}
|
|
125
|
+
<InputLabel>{ $aliasLabel || '' }Rp</InputLabel>
|
|
126
|
+
</InputWrapper> ) : inputEl
|
|
127
|
+
)
|
|
107
128
|
}
|
|
108
129
|
|
|
109
130
|
export default InputComponent
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type KeyboardEvent, type FocusEvent, type InputHTMLAttributes, type ClipboardEvent, forwardRef, useEffect } from 'react';
|
|
2
|
+
import { formatNumber } from '../../assets/javascripts/function';
|
|
3
|
+
import { InputContainer, InputLabel, InputWrapper } from './styles';
|
|
4
|
+
|
|
5
|
+
const InputWithRefComponent = forwardRef<HTMLInputElement, InputHTMLAttributes<HTMLInputElement> & { $mode?: string, $iconLeft?: boolean, $iconRight?: boolean, $isError?: boolean }>(({ $mode, $isError, $iconLeft, $iconRight, ...props }, ref) => {
|
|
6
|
+
const { type } = props;
|
|
7
|
+
|
|
8
|
+
const _evKeyUp = (e: KeyboardEvent<HTMLInputElement>) => {
|
|
9
|
+
if ($mode === 'currency') {
|
|
10
|
+
e.currentTarget.value = formatNumber(e.currentTarget.value);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const _evKeyPress = (e: KeyboardEvent<HTMLInputElement>) => {
|
|
15
|
+
const _target = e.target as HTMLInputElement;
|
|
16
|
+
if (type === 'number' || $mode === 'currency') {
|
|
17
|
+
if(!(!(isNaN(Number(e.key)) || typeof e.key === 'undefined' || e.key === ''))) e.preventDefault();
|
|
18
|
+
}
|
|
19
|
+
if ($mode === 'currency') {
|
|
20
|
+
if ((_target.value.length === 1 && e.key === '0' && _target.value === "0") || e.key === ' ') {
|
|
21
|
+
e.preventDefault();
|
|
22
|
+
} else {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
} else {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const inputEl = <InputContainer
|
|
31
|
+
ref={ref}
|
|
32
|
+
$mode={$mode}
|
|
33
|
+
$isError={$isError}
|
|
34
|
+
$iconLeft={$iconLeft}
|
|
35
|
+
$iconRight={$iconRight}
|
|
36
|
+
onKeyUp={(e: KeyboardEvent<HTMLInputElement>) => {
|
|
37
|
+
_evKeyUp(e)
|
|
38
|
+
}}
|
|
39
|
+
{...((type ==='number' || $mode === 'currency') && { onKeyPress: _evKeyPress } )}
|
|
40
|
+
{...(type ==='number' && { onPaste: (e: ClipboardEvent<HTMLInputElement>) => {
|
|
41
|
+
const pasteVal = (e.clipboardData || (window as any).clipboardData).getData('text')
|
|
42
|
+
const _value = pasteVal.replace(/[^0-9]/g, '')
|
|
43
|
+
e.preventDefault()
|
|
44
|
+
document.execCommand('insertText', false, _value)
|
|
45
|
+
} })}
|
|
46
|
+
{...(type ==='number' && { onFocus: (e: FocusEvent<HTMLInputElement>) => e.target.addEventListener("wheel", function (e) { e.preventDefault() }, { passive: false })} )}
|
|
47
|
+
{...type ==='number' && { min: "0" }}
|
|
48
|
+
{...props} />
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
$mode === 'currency' ? (
|
|
52
|
+
<InputWrapper>
|
|
53
|
+
{inputEl}
|
|
54
|
+
<InputLabel>Rp</InputLabel>
|
|
55
|
+
</InputWrapper> ) : inputEl
|
|
56
|
+
)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
export default InputWithRefComponent
|
|
@@ -7,7 +7,8 @@ const SubmitStyles = css`
|
|
|
7
7
|
color: var(--sec-clr);
|
|
8
8
|
background-color: var(--act-clr-bg);
|
|
9
9
|
`
|
|
10
|
-
|
|
10
|
+
|
|
11
|
+
const getInputModifiedStyled = ({ width, height, $iconLeft, $iconRight, $isError, $mode, type, $aliasLabel }:{ width?: string | number, height?: string | number, $iconLeft?: boolean, $iconRight?: boolean, $isError?: boolean, $mode?: string, type?: string, $aliasLabel?:string }) => {
|
|
11
12
|
let style = ""
|
|
12
13
|
if ($iconLeft) {
|
|
13
14
|
style += `
|
|
@@ -30,7 +31,7 @@ const getInputModifiedStyled = ({ width, height, $iconLeft, $iconRight, $isError
|
|
|
30
31
|
max-height: ${height}px
|
|
31
32
|
`
|
|
32
33
|
}
|
|
33
|
-
if (mode === 'search') {
|
|
34
|
+
if ($mode === 'search') {
|
|
34
35
|
style += `
|
|
35
36
|
background-color: transparent;
|
|
36
37
|
&, &:not([type="submit"]):focus, &:active, &:active:focus {
|
|
@@ -45,11 +46,16 @@ const getInputModifiedStyled = ({ width, height, $iconLeft, $iconRight, $isError
|
|
|
45
46
|
`
|
|
46
47
|
}
|
|
47
48
|
}
|
|
48
|
-
if (mode === 'transaction') {
|
|
49
|
+
if ($mode === 'transaction') {
|
|
49
50
|
style += `
|
|
50
51
|
padding-left: 45px;
|
|
51
52
|
`
|
|
52
53
|
}
|
|
54
|
+
if ($mode === 'currency') {
|
|
55
|
+
style += `
|
|
56
|
+
padding-left: ${$aliasLabel ? '38px' : '30px'} !important;
|
|
57
|
+
`
|
|
58
|
+
}
|
|
53
59
|
if (width) {
|
|
54
60
|
style += `
|
|
55
61
|
width: ${width}px
|
|
@@ -61,10 +67,26 @@ const getInputModifiedStyled = ({ width, height, $iconLeft, $iconRight, $isError
|
|
|
61
67
|
return style;
|
|
62
68
|
}
|
|
63
69
|
|
|
64
|
-
export const InputContainer = styled.input<{ width?: string | number, height?: string | number, $iconLeft?: boolean, $iconRight?: boolean, $isError?: boolean, mode?: string, type?: string }>`
|
|
70
|
+
export const InputContainer = styled.input<{ width?: string | number, height?: string | number, $iconLeft?: boolean, $iconRight?: boolean, $isError?: boolean, $mode?: string, type?: string }>`
|
|
65
71
|
&._refQuantitiyInput {
|
|
66
72
|
width: 55px;
|
|
67
73
|
text-align: center;
|
|
68
74
|
}
|
|
69
75
|
${getInputModifiedStyled}
|
|
76
|
+
`
|
|
77
|
+
export const InputWrapper = styled.div`
|
|
78
|
+
position: relative;
|
|
79
|
+
& ._refLoadingInput {
|
|
80
|
+
position: absolute;
|
|
81
|
+
top: 50%;
|
|
82
|
+
margin-top: -5px;
|
|
83
|
+
right: 10px;
|
|
84
|
+
}
|
|
85
|
+
`
|
|
86
|
+
export const InputLabel = styled.label`
|
|
87
|
+
position: absolute;
|
|
88
|
+
left: 10px;
|
|
89
|
+
top: 50%;
|
|
90
|
+
transform: translateY(-50%);
|
|
91
|
+
color: var(--mt-clr);
|
|
70
92
|
`
|