forstok-ui-lib 3.0.1 → 4.0.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.
@@ -0,0 +1,211 @@
1
+ import styled, { css } from 'styled-components';
2
+ import { dropBase } from '../../assets/stylesheets/bases.styles';
3
+ import IconCheck from '../../assets/images/icons/check-white.svg';
4
+ import IconWarning from '../../assets/images/icons/warning.svg';
5
+ import IconWarningWhite from '../../assets/images/icons/warning-white.svg';
6
+
7
+ const RightButtonStyles = css`
8
+ button {
9
+ position: absolute;
10
+ right: 5px;
11
+ top: 50%;
12
+ transform: translateY(-50%);
13
+ }
14
+ `
15
+ const AsideButtonStyles = css`
16
+ aside {
17
+ display: grid;
18
+ grid-auto-flow: column;
19
+ width: 100%;
20
+ justify-content: end;
21
+ grid-gap: 8px;
22
+ padding-top: 15px;
23
+ }
24
+ `
25
+
26
+ const getMessageModifiedStyled = ({ $isOpen }:{ $isOpen: boolean }) => {
27
+ if ($isOpen) {
28
+ return `
29
+ & ${MessageOverlay}, & ${MessageContainer} {
30
+ display: block;
31
+ }
32
+ `
33
+ }
34
+ }
35
+ const getMessageContentModifiedStyled = ({ $type }:{ $type?: string }) => {
36
+ let style = ``;
37
+ switch ($type) {
38
+ case 'success' :
39
+ style += `
40
+ background-color: #21BA45;
41
+ i {
42
+ border: 2px solid #fff;
43
+ border-radius: 50%;
44
+ &:before {
45
+ width: .75em;
46
+ height: 1em;
47
+ top: 50%;
48
+ left: 50%;
49
+ transform: translate(-50%, -50%);
50
+ content: url(${IconCheck});
51
+ }
52
+ }
53
+ ${RightButtonStyles}
54
+ `
55
+ break
56
+ case 'failed' :
57
+ style += `
58
+ background-color: #dd2926;
59
+ i {
60
+ &:before {
61
+ width: 1.5em;
62
+ height: 1em;
63
+ top: 0;
64
+ left: 0;
65
+ content: url(${IconWarningWhite});
66
+ }
67
+ }
68
+ ${RightButtonStyles}
69
+ `
70
+ break
71
+ case 'warning' :
72
+ style += `
73
+ background-color: #e1ad01;
74
+ i {
75
+ &:before {
76
+ width: 1.5em;
77
+ height: 1em;
78
+ top: 0;
79
+ left: 0;
80
+ content: url(${IconWarningWhite});
81
+ }
82
+ }
83
+ ${RightButtonStyles}
84
+ `
85
+ break
86
+ case 'confirm' :
87
+ style += `
88
+ padding: 20px 15px;
89
+ grid-template-columns: 21px calc(100vw - 99px);
90
+ color: var(--pri-clr);
91
+ i {
92
+ align-self: start;
93
+ &:before {
94
+ width: 21px;
95
+ height: 21px;
96
+ content: url(${IconWarning});
97
+ }
98
+ }
99
+ span {
100
+ display: block;
101
+ margin-bottom: 5px;
102
+ line-height: 20px;
103
+ }
104
+ ${AsideButtonStyles}
105
+ @media only screen and (min-width: 768px) {
106
+ padding: 20px 30px;
107
+ grid-template-columns: 21px 508px;
108
+ }
109
+ `
110
+ break
111
+ default:
112
+ break
113
+ }
114
+ return style;
115
+ }
116
+
117
+ export const Message = styled.div<{ $isOpen: boolean }>`
118
+ ${getMessageModifiedStyled}
119
+ `
120
+ export const MessageOverlay = styled.div<{ $type?: string }>`
121
+ display: none;
122
+ width: 100%;
123
+ height: 100%;
124
+ position: fixed;
125
+ top: 0;
126
+ left: 0;
127
+ z-index: 101;
128
+ overflow: hidden;
129
+ ${({ $type }:{ $type?: string }) => {
130
+ if ($type === 'confirm') {
131
+ return css`
132
+ background-color: var(--ov-clr-bg);
133
+ `
134
+ }
135
+ }}
136
+ `
137
+ export const MessageWrapper = styled.section<{ $isOpen: boolean }>`
138
+ max-width: 320px;
139
+ height: auto;
140
+ max-height: 100%;
141
+ transform: translate(-50%);
142
+ left: 50%;
143
+ top: 0;
144
+ opacity: 0;
145
+ background-color: var(--pri-clr-bg);
146
+ border-radius: var(--ter-rd);
147
+ overflow: hidden;
148
+ transition: opacity .4s;
149
+ width: auto;
150
+ min-width: 600px;
151
+ padding: 0;
152
+ position: relative;
153
+ display: inline-grid;
154
+ grid-auto-flow: row;
155
+ @media only screen and (min-width: 768px) {
156
+ min-width: 600px;
157
+ }
158
+ ${({ $isOpen }:{ $isOpen: boolean }) => {
159
+ if ($isOpen) {
160
+ return css`
161
+ opacity: 1;
162
+ `
163
+ }
164
+ }}
165
+ `
166
+ export const MessageContainer = styled.section<{ $type?: string }>`
167
+ display: none;
168
+ position: fixed;
169
+ top: 5%;
170
+ left: 50%;
171
+ z-index: 105;
172
+ transform: translate(-50%);
173
+ ${({ $type }:{ $type?: string }) => {
174
+ if ($type === 'confirm') {
175
+ return css`
176
+ top: 0;
177
+ & ${MessageWrapper} {
178
+ ${dropBase}
179
+ border: none;
180
+ border-radius: 0 0px 10px 10px;
181
+ min-width: auto;
182
+ max-width: 600px;
183
+ width: calc(100vw - 35px);
184
+ }
185
+ `
186
+ }
187
+ }}
188
+ `
189
+ export const MessageContent = styled.section<{ $type?: string }>`
190
+ width: 100%;
191
+ display: inline-grid;
192
+ grid-auto-flow: column;
193
+ grid-template-columns: 21px 1fr;
194
+ padding: 15px 35px 15px 15px;
195
+ vertical-align: middle;
196
+ align-items: center;
197
+ grid-gap: 10px;
198
+ justify-content: start;
199
+ color: var(--act-clr);
200
+ line-height: 16px;
201
+ i {
202
+ float: right;
203
+ position: relative;
204
+ width: 21px;
205
+ height: 21px;
206
+ &::before {
207
+ position: absolute;
208
+ }
209
+ }
210
+ ${getMessageContentModifiedStyled}
211
+ `
@@ -0,0 +1,62 @@
1
+ import { useCallback, useEffect } from 'react';
2
+ import { useStateWithCallbackLazy } from 'use-state-with-callback';
3
+ import ButtonComponent from '../button/button';
4
+ import { Message, MessageOverlay, MessageContainer, MessageWrapper, MessageContent } from './message.styles';
5
+ import type { TMessage } from './message.typed';
6
+
7
+ const MessageComponent = ({ timer=500, $type, message, callback }: TMessage) => {
8
+ const [isOpen, setOpen] = useStateWithCallbackLazy<boolean>(false);
9
+ const [isOpenContainer, setOpenContainer] = useStateWithCallbackLazy<boolean>(false);
10
+ const [isClosing, setClosing] = useStateWithCallbackLazy<boolean>(false);
11
+
12
+ const evCloseMessage = useCallback(() => {
13
+ setClosing(!isClosing, () => {
14
+ const bodyEl = document.getElementsByTagName("BODY")[0] as HTMLBodyElement;
15
+ bodyEl.classList.remove("is-immuned");
16
+ callback && callback();
17
+ })
18
+ }, [callback, setClosing, isClosing])
19
+
20
+ const CloseCallback = useCallback(() => {
21
+ evCloseMessage();
22
+ }, [evCloseMessage])
23
+
24
+ useEffect(() => {
25
+ const _timer = timer ? timer : 2000;
26
+ const overlayEl = document.querySelector('._refMessageOverlay') as HTMLDivElement;
27
+ if (!isOpen && !isClosing) {
28
+ setOpen(!isOpen, () => {
29
+ overlayEl && overlayEl.addEventListener('click', CloseCallback);
30
+ setTimeout(() => {
31
+ setOpenContainer(!isOpenContainer, () => {
32
+ setTimeout(() => !isOpenContainer && evCloseMessage() , _timer);
33
+ });
34
+ }, 1);
35
+ });
36
+ } else if (isOpen && isClosing) {
37
+ setOpen && setOpen(!isOpen, () => {});
38
+ setOpenContainer && setOpenContainer(!isOpenContainer, () => {});
39
+ overlayEl && overlayEl.removeEventListener('click', CloseCallback);
40
+ }
41
+ return () => {
42
+ overlayEl && overlayEl.removeEventListener('click', CloseCallback);
43
+ }
44
+ },[isOpen, isOpenContainer, timer, evCloseMessage, setOpen, setOpenContainer, CloseCallback, isClosing])
45
+
46
+ return (
47
+ <Message $isOpen={isOpen}>
48
+ <MessageOverlay className='_refMessageOverlay' />
49
+ <MessageContainer>
50
+ <MessageWrapper $isOpen={isOpenContainer}>
51
+ <MessageContent $type={$type}>
52
+ <i />
53
+ {message}
54
+ <ButtonComponent $mode='small-white-round-close' onClick={evCloseMessage} />
55
+ </MessageContent>
56
+ </MessageWrapper>
57
+ </MessageContainer>
58
+ </Message>
59
+ )
60
+ }
61
+
62
+ export default MessageComponent
@@ -0,0 +1,18 @@
1
+ import { ReactNode } from 'react';
2
+
3
+ export type TMessage = {
4
+ timer?: number
5
+ $type: string
6
+ message: string | ReactNode
7
+ callback?: () => void
8
+ }
9
+ export type TMessageQuestion = {
10
+ $type: string
11
+ title: string
12
+ subtitle: string
13
+ callback?: () => void
14
+ buttonSubmit?: string
15
+ cancelCallback?: () => void
16
+ }
17
+ export type TMessageFunction = ({ timer, $type, message, callback }: TMessage) => void
18
+ export type TMessageQuestionFunction =({ $type, title, subtitle, callback, buttonSubmit }: TMessageQuestion) => void
@@ -0,0 +1,72 @@
1
+ import { useCallback, useEffect } from 'react';
2
+ import { htmlToText } from 'html-to-text';
3
+ import { useStateWithCallbackLazy } from 'use-state-with-callback';
4
+ import ButtonComponent from '../button/button';
5
+ import TextComponent from '../text/text';
6
+ import { Message, MessageOverlay, MessageContainer, MessageWrapper, MessageContent } from './message.styles'
7
+ import type { TMessageQuestion } from './message.typed'
8
+
9
+ const MessageQuestionComponent = ({ $type, title, subtitle, callback, buttonSubmit, cancelCallback }: TMessageQuestion) => {
10
+ const [isOpen, setOpen] = useStateWithCallbackLazy<boolean>(false);
11
+ const [isOpenContainer, setOpenContainer] = useStateWithCallbackLazy<boolean>(false);
12
+ const [isClosing, setClosing] = useStateWithCallbackLazy<boolean>(false);
13
+
14
+ const evCloseMessage = useCallback(() => {
15
+ setClosing(!isClosing, () => {
16
+ cancelCallback && cancelCallback();
17
+ const bodyEl = document.getElementsByTagName("BODY")[0] as HTMLBodyElement;
18
+ bodyEl.classList.remove("is-immuned");
19
+ })
20
+ },[cancelCallback, isClosing, setClosing])
21
+
22
+ const evConfirmButton = () => {
23
+ setClosing(!isClosing, () => {
24
+ const bodyEl = document.getElementsByTagName("BODY")[0] as HTMLBodyElement;
25
+ bodyEl.classList.remove("is-immuned");
26
+ callback && callback();
27
+ })
28
+ }
29
+
30
+ const CloseCallback = useCallback(() => {
31
+ evCloseMessage();
32
+ }, [evCloseMessage])
33
+
34
+ useEffect(() => {
35
+ const overlayEl = document.querySelector('._refQuestionMessageOverlay') as HTMLDivElement;
36
+ if (!isOpen && !isClosing) {
37
+ setOpen(!isOpen, () => {
38
+ setTimeout(() => {
39
+ setOpenContainer(!isOpenContainer, () => {});
40
+ overlayEl && overlayEl.addEventListener('click', CloseCallback);
41
+ }, 1);
42
+ });
43
+ } else if (isOpen && isClosing) {
44
+ setOpen(!isOpen, () => {});
45
+ setOpenContainer(!isOpenContainer, () => {});
46
+ overlayEl && overlayEl.removeEventListener('click', CloseCallback);
47
+ }
48
+ },[isOpen, isOpenContainer, setOpen, setOpenContainer, CloseCallback, isClosing])
49
+
50
+ return (
51
+ <Message $isOpen={isOpen}>
52
+ <MessageOverlay className='_refQuestionMessageOverlay' $type={$type}></MessageOverlay>
53
+ <MessageContainer $type={$type}>
54
+ <MessageWrapper $isOpen={isOpenContainer}>
55
+ <MessageContent $type={$type}>
56
+ <i></i>
57
+ <div>
58
+ <TextComponent $elipsis={false} style={{ fontWeight: 600, fontSize: '15px' }}>{title}</TextComponent>
59
+ <TextComponent $color='grey' $elipsis={false}>{htmlToText(subtitle)}</TextComponent>
60
+ <aside>
61
+ <ButtonComponent $mode='white' $size='small' onClick={evCloseMessage}>Cancel</ButtonComponent>
62
+ <ButtonComponent $mode='red' $size='small' onClick={evConfirmButton}>{buttonSubmit || 'Confirm'}</ButtonComponent>
63
+ </aside>
64
+ </div>
65
+ </MessageContent>
66
+ </MessageWrapper>
67
+ </MessageContainer>
68
+ </Message>
69
+ )
70
+ }
71
+
72
+ export default MessageQuestionComponent
@@ -0,0 +1,315 @@
1
+ import styled, { css } from 'styled-components';
2
+ import { dropBase } from '../../assets/stylesheets/bases.styles';
3
+
4
+ type TPopupParam = {
5
+ $isOpen?: boolean
6
+ $withHeader?: boolean
7
+ $mode?: string | null
8
+ $width?: string | number
9
+ $height?: string | number
10
+ }
11
+
12
+ const popupWrapperStyles = css`
13
+ transform: translate(-50%);
14
+ top: -100%;
15
+ left: 50%;
16
+ background-color: var(--pri-clr-bg);
17
+ min-width: 100%;
18
+ max-width: 100%;
19
+ @media (min-width: 768px) {
20
+ max-width: 700px;
21
+ min-width: auto;
22
+ }
23
+ @media (min-width: 1024px) {
24
+ max-width: 1000px;
25
+ }
26
+ `
27
+
28
+ const getPopupModifiedStyled = ({ $isOpen }:Pick<TPopupParam, '$isOpen'>) => {
29
+ if ($isOpen) {
30
+ return `
31
+ & ${PopupOverlay}, & ${PopupContainer} {
32
+ display: block;
33
+ }
34
+ `
35
+ }
36
+ }
37
+ const getPopupWrapperModifiedStyled = ({ $isOpen, $withHeader, $width }:Omit<TPopupParam, '$height' | '$mode'>) => {
38
+ let style = ``
39
+ if ($isOpen) {
40
+ style += `
41
+ top: 0;
42
+ transition: top .5s;
43
+ `
44
+ }
45
+ if ($withHeader) {
46
+ style += `
47
+ padding-top: 52px;
48
+ `
49
+ }
50
+ if ($width === '1200px') {
51
+ style += `
52
+ width: 100%;
53
+ @media only screen and (min-width: 1024px) {
54
+ max-width: 1200px;
55
+ width: 100%;
56
+ }
57
+ @media only screen and (min-width: 1366px) {
58
+ width: ${$width};
59
+ }
60
+ `
61
+ }
62
+ else if ($width) {
63
+ style += `
64
+ width: 100%;
65
+ @media only screen and (min-width: 1024px) {
66
+ width: ${$width};
67
+ }
68
+ `
69
+ }
70
+ return style
71
+ }
72
+ const getPopupHeaderWrapperModifiedStyled = ({ $width, $mode, $isOpen }:Pick<TPopupParam, '$width' | '$mode' | '$isOpen'>) => {
73
+ let style = ``
74
+ if ($mode === 'question') {
75
+ style +=`
76
+ ${PopupHeader} {
77
+ font-size: 15px;
78
+ line-height: 18px;
79
+ padding: 0;
80
+ margin-bottom: 8px;
81
+ letter-spacing: normal;
82
+ }
83
+ ${PopupFooter} {
84
+ padding: 12px 0 8px;
85
+ grid-template-columns: none;
86
+ aside {
87
+ &:last-child {
88
+ justify-content: start;
89
+ }
90
+ a { height: 28px; }
91
+ }
92
+ }
93
+ > aside {
94
+ font-size: 14px;
95
+ color: var(--hd-clr);
96
+ line-height: 1.4;
97
+ }
98
+ `
99
+ }
100
+ else {
101
+ style += popupWrapperStyles + `
102
+ position: fixed;
103
+ z-index: 10;
104
+ border-bottom: 1px solid var(--sec-clr-ln);
105
+ button[mode=round-close] {
106
+ position: absolute;
107
+ top: 12px;
108
+ right: 0px;
109
+ }
110
+ @media only screen and (min-width: 768px) {
111
+ button[mode=round-close] {
112
+ top: 16px;
113
+ right: 20px;
114
+ }
115
+ }
116
+ `
117
+ if ($isOpen) {
118
+ style += `
119
+ top: 0;
120
+ transition: top .5s;
121
+ `
122
+ }
123
+ }
124
+
125
+ if ($width === '1200px') {
126
+ style += `
127
+ width: 100%;
128
+ @media only screen and (min-width: 1024px) {
129
+ max-width: 1200px;
130
+ width: 100%;
131
+ }
132
+ @media only screen and (min-width: 1366px) {
133
+ width: ${$width};
134
+ }
135
+ `
136
+ }
137
+ else if ($width) {
138
+ style += `
139
+ width: 100%;
140
+ @media only screen and (min-width: 1024px) {
141
+ width: ${$width};
142
+ }
143
+ `
144
+ }
145
+ return style
146
+ }
147
+ const getPopupBodyModifiedStyled = ({ $mode, $height, $width }:Pick<TPopupParam, '$width' | '$mode' | '$height'>) => {
148
+ let style = ``
149
+ if ($mode === 'clear') {
150
+ style += `
151
+ display: block;
152
+ padding: 0;
153
+ @media only screen and (min-width: 768px) {
154
+ padding: 0;
155
+ }
156
+ `
157
+ }
158
+ if ($height) {
159
+ style += `
160
+ min-height: auto;
161
+ height: auto;
162
+ max-height: calc(100vh - 176px);
163
+ overflow: auto;
164
+ @media (min-width: 768px) {
165
+ max-height: ${$height};
166
+ }
167
+ `
168
+ }
169
+ if ($width === '1200px') {
170
+ style += `
171
+ width: 100%;
172
+ @media only screen and (min-width: 1024px) {
173
+ max-width: 1200px;
174
+ width: 100%;
175
+ }
176
+ @media only screen and (min-width: 1366px) {
177
+ width: ${$width};
178
+ }
179
+ `
180
+ }
181
+ else if ($width) {
182
+ style += `
183
+ width: 100%;
184
+ @media only screen and (min-width: 1024px) {
185
+ width: ${$width};
186
+ }
187
+ }
188
+ `
189
+ }
190
+ return style
191
+ }
192
+
193
+ export const PopupPanel = styled.div<{ $isOpen: boolean }>`
194
+ ${getPopupModifiedStyled}
195
+ `
196
+ export const PopupOverlay = styled.div`
197
+ display: none;
198
+ width: 100%;
199
+ height: 100%;
200
+ position: fixed;
201
+ top: 0;
202
+ left: 0;
203
+ z-index: 100;
204
+ background-color: var(--ov-clr-bg);
205
+ `
206
+ export const PopupContainer = styled.section`
207
+ display: none;
208
+ position: fixed;
209
+ width: 100%;
210
+ height: 100%;
211
+ top: 0;
212
+ left: 0;
213
+ z-index: 101;
214
+ overflow: auto;
215
+ `
216
+ export const PopupWrapper = styled.section<{ $width?: string | number, $isOpen: boolean, $withHeader?: boolean }>`
217
+ ${dropBase}
218
+ ${popupWrapperStyles}
219
+ height: auto;
220
+ overflow: hidden;
221
+ transition: top .5s;
222
+ width: auto;
223
+ padding: 0;
224
+ position: relative;
225
+ display: inline-grid;
226
+ grid-auto-flow: row;
227
+ margin-bottom: 52px;
228
+ border: none;
229
+ border-radius: 0 0px 10px 10px;
230
+ ${getPopupWrapperModifiedStyled}
231
+ `
232
+ export const PopupHeaderWrapper = styled.div<{ $width?: string | number, $mode?: string | null , $isOpen?: boolean }>`
233
+ ${getPopupHeaderWrapperModifiedStyled}
234
+ `
235
+ export const PopupHeader = styled.h3`
236
+ font-size: 16px;
237
+ padding: 11px 10px;
238
+ color: var(--hd-clr);
239
+ i[data-tip] {
240
+ &:before {
241
+ display: inline-block;
242
+ }
243
+ }
244
+ @media only screen and (min-width: 768px) {
245
+ font-size: 20px;
246
+ padding: 11px 30px;
247
+ }
248
+ `
249
+ export const PopupBody = styled.div<{ $mode?: string | null, $width?: string | number, $height?: string | number }>`
250
+ display: inline-grid;
251
+ justify-content: normal;
252
+ align-content: start;
253
+ padding: 20px 10px;
254
+ grid-gap: 1em;
255
+ min-height: 5vh;
256
+ max-height: calc(100vh - 170px);
257
+ height: auto;
258
+ overflow: auto;
259
+ overflow-x: hidden;
260
+ border-bottom: 1px solid var(--sec-clr-ln);
261
+ @media only screen and (min-width: 768px) {
262
+ padding: 20px 30px;
263
+ }
264
+ ${getPopupBodyModifiedStyled}
265
+ `
266
+ export const PopupFooter = styled.div<{ $isLeft?: boolean }>`
267
+ display: inline-grid;
268
+ grid-auto-flow: column;
269
+ grid-template-columns: min-content auto;
270
+ background-color: var(--pri-clr-bg);
271
+ padding: 15px 30px;
272
+ aside {
273
+ display: grid;
274
+ grid-auto-flow: column;
275
+ grid-gap: 8px;
276
+ &:first-child {
277
+ justify-content: start;
278
+ align-items: center;
279
+ }
280
+ &:last-child {
281
+ justify-content: end;
282
+ @media only screen and (max-width: 767px) {
283
+ &[data-type="shipping"] {
284
+ grid-template-columns: 1fr 1fr;
285
+ grid-template-rows: 1fr 1fr;
286
+ > button {
287
+ width: max-content;
288
+ }
289
+ }
290
+ }
291
+ }
292
+ }
293
+ ${({ $isLeft }: { $isLeft?: boolean }) => {
294
+ if ($isLeft) {
295
+ return css`
296
+ grid-template-columns: 40% 60%;
297
+ `
298
+ }
299
+ }}
300
+ `
301
+ export const PopupQuestionWrapper = styled.section<{ $width?: string | number }>`
302
+ padding: 20px 24px;
303
+ display: grid;
304
+ grid-auto-flow: column;
305
+ grid-template-columns: 30px auto;
306
+ grid-gap: 20px;
307
+ align-content: start;
308
+ ${({ $width }: { $width?: string | number }) => {
309
+ if ($width) {
310
+ return css`
311
+ max-width: ${$width};
312
+ `
313
+ }
314
+ }}
315
+ `