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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forstok-ui-lib",
3
- "version": "5.0.2",
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",
@@ -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
+ `
@@ -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 { InputContainer } from './styles';
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, $iconRight, reset, setReset, isForceUpdate, setForceUpdate, evChange, evBlur, isField, evChangeCustom, evEnter, evKeyUp, ...props }: TInput) => {
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
- const _value = defaultValue?.toString() ?? '';
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
- if (type === 'number') {
56
- if(!(!(isNaN(Number(e.key)) || typeof e.key === 'undefined' || e.key === ''))) {
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
- return true;
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 inputEl;
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
- const getInputModifiedStyled = ({ width, height, $iconLeft, $iconRight, $isError, mode, type }:{ width?: string | number, height?: string | number, $iconLeft?: boolean, $iconRight?: boolean, $isError?: boolean, mode?: string, type?: string }) => {
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
  `