forstok-ui-lib 3.0.1 → 4.0.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 +92 -1
- package/dist/index.js +406 -65
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +406 -65
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -2
- package/src/assets/stylesheets/shares.styles.ts +1 -1
- package/src/components/dropdown/dropdown.styles.ts +325 -0
- package/src/components/dropdown/dropdown.tsx +254 -0
- package/src/components/dropdown/dropdown.typed.ts +24 -0
- package/src/components/index.ts +9 -0
- package/src/components/message/message.styles.ts +211 -0
- package/src/components/message/message.tsx +62 -0
- package/src/components/message/message.typed.ts +18 -0
- package/src/components/message/message_question.tsx +72 -0
- package/src/components/popup/popup.styles.ts +315 -0
- package/src/components/popup/popup.tsx +92 -0
- package/src/components/popup/popup.typed.ts +33 -0
- package/src/components/portal/react_portal.tsx +37 -0
- package/src/typeds/shares.typed.ts +18 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "forstok-ui-lib",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "Forstok UI Components Library",
|
|
5
5
|
"path": "dist",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -34,11 +34,14 @@
|
|
|
34
34
|
"@rollup/plugin-typescript": "^12.1.2",
|
|
35
35
|
"@rollup/plugin-url": "^8.0.2",
|
|
36
36
|
"@svgr/rollup": "^8.1.0",
|
|
37
|
+
"@types/html-to-text": "^9.0.4",
|
|
37
38
|
"@types/react": "^19.0.2",
|
|
38
39
|
"@types/react-dom": "^19.0.2",
|
|
40
|
+
"html-to-text": "^9.0.5",
|
|
39
41
|
"react": "^19.0.0",
|
|
40
42
|
"react-dom": "^19.0.0",
|
|
41
43
|
"react-router-dom": "^7.1.1",
|
|
44
|
+
"react-tooltip": "^4.5.1",
|
|
42
45
|
"rollup-plugin-dts": "^6.1.1",
|
|
43
46
|
"rollup-plugin-peer-deps-external": "^2.2.4",
|
|
44
47
|
"rollup-plugin-postcss": "^4.0.2",
|
|
@@ -46,6 +49,7 @@
|
|
|
46
49
|
"styled-components": "^6.1.15",
|
|
47
50
|
"tslib": "^2.8.1",
|
|
48
51
|
"typescript": "^5.7.2",
|
|
49
|
-
"typescript-plugin-styled-components": "^3.0.0"
|
|
52
|
+
"typescript-plugin-styled-components": "^3.0.0",
|
|
53
|
+
"use-state-with-callback": "^3.0.2"
|
|
50
54
|
}
|
|
51
55
|
}
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
import { dropBase } from '../../assets/stylesheets/bases.styles';
|
|
3
|
+
import type { TDropdown } from './dropdown.typed';
|
|
4
|
+
|
|
5
|
+
type TDropdownChild = Pick<TDropdown, '$internalWidth' | '$openPosition' | '$placement' | '$bottom' | '$top'>
|
|
6
|
+
type TDropdownParent = Pick<TDropdown, '$openPosition' | '$placement' | '$bottom' | '$top' | '$alias' | '$externalWidth' | '$externalMinWidth' | '$area'>
|
|
7
|
+
|
|
8
|
+
const getDropDownContainerModifiedStyled = ({ $top, $bottom, $placement, $alias, $openPosition, $externalWidth, $externalMinWidth, $area }: TDropdownParent) => {
|
|
9
|
+
const axTop = $top ? (parseInt($top) + 5)+'px' : '40px'
|
|
10
|
+
let style = `
|
|
11
|
+
@keyframes droptodown {
|
|
12
|
+
90% {
|
|
13
|
+
top: ${axTop};
|
|
14
|
+
}
|
|
15
|
+
100% {
|
|
16
|
+
top: ${$top || '35px'};
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
@keyframes droptoup {
|
|
20
|
+
50% {
|
|
21
|
+
bottom: -100px;
|
|
22
|
+
}
|
|
23
|
+
50% {
|
|
24
|
+
bottom: -50px;
|
|
25
|
+
}
|
|
26
|
+
100% {
|
|
27
|
+
bottom: 0px;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
@keyframes dropgodown {
|
|
31
|
+
90% {
|
|
32
|
+
bottom: ${axTop};
|
|
33
|
+
}
|
|
34
|
+
100% {
|
|
35
|
+
bottom: ${$bottom || '35px'};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
@keyframse dropgoup {
|
|
39
|
+
50% {
|
|
40
|
+
top: -100px;
|
|
41
|
+
}
|
|
42
|
+
50% {
|
|
43
|
+
top: -50px;
|
|
44
|
+
}
|
|
45
|
+
100% {
|
|
46
|
+
top: 0px;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
& ${DropDownWrapper} {
|
|
50
|
+
display: none;
|
|
51
|
+
}
|
|
52
|
+
&.is-shown {
|
|
53
|
+
& ${DropDownControl} button {
|
|
54
|
+
box-shadow: var(--act-shd-bx);
|
|
55
|
+
}
|
|
56
|
+
@media only screen and (max-width: 767px) {
|
|
57
|
+
& ${DropDownWrapper} {
|
|
58
|
+
display: grid;
|
|
59
|
+
left: 0;
|
|
60
|
+
bottom:0;
|
|
61
|
+
${$placement === 'top' ? 'animation: dropgoup .075s;' : 'animation: droptoup .075s;'}
|
|
62
|
+
overflow-x: hidden;
|
|
63
|
+
}
|
|
64
|
+
& ${DropDownBody} {
|
|
65
|
+
._refFilterDropdown a {
|
|
66
|
+
text-align: left !important;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
@media only screen and (min-width: 768px) {
|
|
71
|
+
& ${DropDownWrapper} {
|
|
72
|
+
display: grid;
|
|
73
|
+
${$placement === 'top' ? `animation: dropgodown .075s linear; bottom: ${$bottom || '35px'};` : `animation: droptodown .075s linear; top: ${$top || '35px'};`}
|
|
74
|
+
left: ${($alias === "_dropdownFilterContainer" && $openPosition !== 'left') ? '100%': $openPosition === 'left' ? '0' : 'auto'};
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
@media only screen and (min-width: 1024px) {
|
|
78
|
+
& ${DropDownWrapper} {
|
|
79
|
+
left: ${($alias === "_dropdownFilterContainer" && $openPosition !== 'left') ? '30%': $openPosition === 'left' ? '0' : 'auto'};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
@media only screen and (min-width: 1280px) {
|
|
83
|
+
& ${DropDownWrapper} {
|
|
84
|
+
left: ${($alias === "_dropdownFilterContainer" && $openPosition !== 'left') ? '0': $openPosition === 'left' ? '0' : 'auto'};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
`
|
|
89
|
+
if ($alias === '_dropdownFilterContainer') {
|
|
90
|
+
style += `
|
|
91
|
+
margin-top: 4px;
|
|
92
|
+
${DropDownControl} {
|
|
93
|
+
margin-top: -4px;
|
|
94
|
+
}
|
|
95
|
+
`
|
|
96
|
+
}
|
|
97
|
+
if ($externalWidth) {
|
|
98
|
+
style += `
|
|
99
|
+
width: ${$externalWidth}
|
|
100
|
+
`
|
|
101
|
+
}
|
|
102
|
+
if ($externalMinWidth) {
|
|
103
|
+
style += `
|
|
104
|
+
min-width: ${$externalMinWidth}
|
|
105
|
+
`
|
|
106
|
+
}
|
|
107
|
+
if ($area === 'mobile') {
|
|
108
|
+
style += `
|
|
109
|
+
@media (min-width: 320px) and (max-width: 480px) {
|
|
110
|
+
${DropDownWrapper} {
|
|
111
|
+
width: 100%;
|
|
112
|
+
align-content: start;
|
|
113
|
+
z-index: 88;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
`
|
|
117
|
+
}
|
|
118
|
+
return style
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const getDropDownWrapperModifiedStyled = ({ $internalWidth, $openPosition, $placement, $bottom, $top }: TDropdownChild) => {
|
|
122
|
+
let style = ''
|
|
123
|
+
if ($internalWidth) {
|
|
124
|
+
style += `
|
|
125
|
+
width: ${$internalWidth};
|
|
126
|
+
`
|
|
127
|
+
} else {
|
|
128
|
+
style +=`
|
|
129
|
+
width: auto;
|
|
130
|
+
white-space: nowrap;
|
|
131
|
+
${DropDownBody} {
|
|
132
|
+
overflow: unset;
|
|
133
|
+
}
|
|
134
|
+
`
|
|
135
|
+
}
|
|
136
|
+
if ($openPosition === 'left') {
|
|
137
|
+
style += `
|
|
138
|
+
left: 0;
|
|
139
|
+
right: auto;
|
|
140
|
+
`
|
|
141
|
+
} else if ($openPosition === 'center') {
|
|
142
|
+
style += `
|
|
143
|
+
left: 50%;
|
|
144
|
+
right: auto;
|
|
145
|
+
transform: translateX(-50%);
|
|
146
|
+
`
|
|
147
|
+
}
|
|
148
|
+
if ($placement === 'top' && $bottom) {
|
|
149
|
+
style += `
|
|
150
|
+
top: unset; bottom: ${$bottom}
|
|
151
|
+
`
|
|
152
|
+
} else if ($top) {
|
|
153
|
+
style += `
|
|
154
|
+
top: ${$top};
|
|
155
|
+
`
|
|
156
|
+
}
|
|
157
|
+
return style
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const getDropDownBodyModifiedStyled = ({ $area }:{ $area?: string }) => {
|
|
161
|
+
let style = ''
|
|
162
|
+
if ($area === 'promotion') {
|
|
163
|
+
style += `
|
|
164
|
+
max-height: 347px;
|
|
165
|
+
overflow-y: auto;
|
|
166
|
+
`
|
|
167
|
+
}
|
|
168
|
+
return style
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const getDropDownActionModifiedStyled = ({ $position }:{ $position?: string }) => {
|
|
172
|
+
let style = `
|
|
173
|
+
justify-self: end;
|
|
174
|
+
`
|
|
175
|
+
if ($position === 'middle') {
|
|
176
|
+
style += `
|
|
177
|
+
width: 100%;
|
|
178
|
+
text-align: center;
|
|
179
|
+
`
|
|
180
|
+
} else if ($position === 'left') {
|
|
181
|
+
style += `
|
|
182
|
+
width: 100%;
|
|
183
|
+
`
|
|
184
|
+
} else if ($position === 'end') {
|
|
185
|
+
style += `
|
|
186
|
+
width: 100%;
|
|
187
|
+
text-align: end
|
|
188
|
+
`
|
|
189
|
+
}
|
|
190
|
+
return style
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export const DropDownWrapper = styled.section<TDropdownChild>`
|
|
194
|
+
${dropBase}
|
|
195
|
+
position: fixed;
|
|
196
|
+
width: 100vw;
|
|
197
|
+
z-index: 102;
|
|
198
|
+
display: none;
|
|
199
|
+
border-radius: 10px 10px 0px 0px;
|
|
200
|
+
> div,
|
|
201
|
+
> section {
|
|
202
|
+
padding: 1em;
|
|
203
|
+
}
|
|
204
|
+
@media only screen and (min-width: 768px) {
|
|
205
|
+
position: absolute;
|
|
206
|
+
min-width: 110px;
|
|
207
|
+
width: 100vw;
|
|
208
|
+
top: 40px;
|
|
209
|
+
right: 0;
|
|
210
|
+
z-index: 10;
|
|
211
|
+
border-radius: var(--sec-rd);
|
|
212
|
+
> div,
|
|
213
|
+
> section {
|
|
214
|
+
padding: 1em;
|
|
215
|
+
._refFilterDropdown {
|
|
216
|
+
> * {
|
|
217
|
+
margin-left: -1em;
|
|
218
|
+
margin-right: -1em;
|
|
219
|
+
&:first-child {
|
|
220
|
+
margin-top: -1em;
|
|
221
|
+
}
|
|
222
|
+
&:last-child {
|
|
223
|
+
margin-bottom: -1em;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
> article {
|
|
227
|
+
padding: 1em;
|
|
228
|
+
width: calc(100% + 2em);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
${getDropDownWrapperModifiedStyled}
|
|
233
|
+
}
|
|
234
|
+
`
|
|
235
|
+
|
|
236
|
+
export const DropDownOverlayWrapper = styled.div`
|
|
237
|
+
width: 100%;
|
|
238
|
+
height: 100%;
|
|
239
|
+
position: fixed;
|
|
240
|
+
top: 0;
|
|
241
|
+
left: 0;
|
|
242
|
+
z-index: 100;
|
|
243
|
+
background-color: rgba(0, 0, 0, .1);
|
|
244
|
+
display: none;
|
|
245
|
+
@media only screen and (min-width: 768px) {
|
|
246
|
+
display: none !important;
|
|
247
|
+
}
|
|
248
|
+
`
|
|
249
|
+
|
|
250
|
+
export const DropDownControl = styled.section`
|
|
251
|
+
display: block;
|
|
252
|
+
`
|
|
253
|
+
|
|
254
|
+
export const DropdownControlWrapper = styled.div`
|
|
255
|
+
._refDropdownControl {
|
|
256
|
+
display: none;
|
|
257
|
+
}
|
|
258
|
+
._refDropdownControlMobile {
|
|
259
|
+
display: block;
|
|
260
|
+
}
|
|
261
|
+
@media only screen and (min-width: 1024px) {
|
|
262
|
+
._refDropdownControl {
|
|
263
|
+
display: block;
|
|
264
|
+
}
|
|
265
|
+
._refDropdownControlMobile {
|
|
266
|
+
display: none;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
`
|
|
270
|
+
|
|
271
|
+
export const DropDownContainer = styled.section<{ $area?: string }>`
|
|
272
|
+
position: relative;
|
|
273
|
+
${getDropDownContainerModifiedStyled}
|
|
274
|
+
@media only screen and (min-width: 1024px) {
|
|
275
|
+
&._refContainerhover:hover {
|
|
276
|
+
${DropDownWrapper} {
|
|
277
|
+
display: grid;
|
|
278
|
+
animation: droptodown .075s linear;
|
|
279
|
+
top: '35px';
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
`
|
|
284
|
+
|
|
285
|
+
export const DropDownTitle = styled.div`
|
|
286
|
+
font-weight: 600;
|
|
287
|
+
border-bottom: 1px solid var(--sec-clr-ln);
|
|
288
|
+
`
|
|
289
|
+
|
|
290
|
+
export const DropDownSubTitle = styled.p`
|
|
291
|
+
color: var(--mt-clr);
|
|
292
|
+
margin-top: 4px;
|
|
293
|
+
margin-bottom: 0;
|
|
294
|
+
font-weight: 400;
|
|
295
|
+
line-height: 1;
|
|
296
|
+
letter-spacing: normal;
|
|
297
|
+
`
|
|
298
|
+
|
|
299
|
+
export const DropDownBody = styled.section<{ $area?: string }>`
|
|
300
|
+
overflow-y: auto;
|
|
301
|
+
max-height: 75vh;
|
|
302
|
+
overflow-x: hidden;
|
|
303
|
+
@media (max-width: 767px) {
|
|
304
|
+
max-height: 65vh;
|
|
305
|
+
}
|
|
306
|
+
${getDropDownBodyModifiedStyled}
|
|
307
|
+
`
|
|
308
|
+
|
|
309
|
+
export const DropDownAction = styled.section<{ $position?: string }>`
|
|
310
|
+
border-top: 1px solid var(--sec-clr-ln);
|
|
311
|
+
${getDropDownActionModifiedStyled}
|
|
312
|
+
`
|
|
313
|
+
|
|
314
|
+
export const DropdownPortalContainer = styled.div`
|
|
315
|
+
display: inline-block;
|
|
316
|
+
position: fixed;
|
|
317
|
+
top: 0;
|
|
318
|
+
left: 0;
|
|
319
|
+
z-index: 999;
|
|
320
|
+
&.is-shown {
|
|
321
|
+
& ${DropDownWrapper} {
|
|
322
|
+
display: block;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
`
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { useEffect, useRef, isValidElement } from 'react';
|
|
2
|
+
import ReactTooltip from 'react-tooltip';
|
|
3
|
+
import ReactPortalComponent from '../portal/react_portal';
|
|
4
|
+
import { DropDownContainer, DropDownTitle, DropDownSubTitle, DropDownWrapper, DropDownOverlayWrapper, DropDownControl, DropDownBody, DropDownAction, DropdownControlWrapper, DropdownPortalContainer } from './dropdown.styles';
|
|
5
|
+
import type { TMouseEvent } from '../../typeds/base.typed';
|
|
6
|
+
import type { TDropdown } from './dropdown.typed';
|
|
7
|
+
|
|
8
|
+
const DropDownComponent = ({ children, title, subTitle, $externalWidth, $externalMinWidth, $internalWidth, $area, $openPosition, $placement, $top, onClick, $alias, type, portalId, $bottom, ...props }: TDropdown) => {
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
ReactTooltip.rebuild() ;
|
|
11
|
+
}, [])
|
|
12
|
+
|
|
13
|
+
const buttonRef = useRef<HTMLElement>(null);
|
|
14
|
+
|
|
15
|
+
const evScrollDropdown = () => {
|
|
16
|
+
const containerEl = document.getElementsByClassName('_refContainer is-shown')[0] as HTMLElement;
|
|
17
|
+
if (containerEl) {
|
|
18
|
+
const offsetPos = containerEl.getBoundingClientRect();
|
|
19
|
+
const portalContainerRef = document.querySelector(`#${portalId} ._refDropdownPortal`) as HTMLElement;
|
|
20
|
+
if (portalContainerRef) {
|
|
21
|
+
const wrapperRef = portalContainerRef.querySelector('._refDropdownWrapper') as HTMLElement;
|
|
22
|
+
portalContainerRef.style.left = offsetPos.left + offsetPos.width - wrapperRef.getBoundingClientRect().width + 'px';
|
|
23
|
+
portalContainerRef.style.top = offsetPos.top + parseInt($top || '0') - 32 + 'px';
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const evResetDropdown = () => {
|
|
29
|
+
const containerRef = document.getElementsByClassName('_refContainer is-shown') as HTMLCollectionOf<HTMLElement>;
|
|
30
|
+
if (containerRef.length) {
|
|
31
|
+
for (let i = 0; i < containerRef.length; i++) {
|
|
32
|
+
containerRef[i].classList.remove('is-shown');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const refScrollContainerEl = document.querySelector('._refScrollContainer') as HTMLElement;
|
|
36
|
+
refScrollContainerEl.removeEventListener('scroll', evScrollDropdown, true);
|
|
37
|
+
setTimeout(() => {
|
|
38
|
+
if (portalId) {
|
|
39
|
+
const portalContainerRef = document.getElementsByClassName('_refDropdownPortal is-shown') as HTMLCollectionOf<HTMLElement>;
|
|
40
|
+
if (portalContainerRef.length) {
|
|
41
|
+
for (let i = 0; i < portalContainerRef.length; i++) {
|
|
42
|
+
portalContainerRef[i].style.left = '0';
|
|
43
|
+
portalContainerRef[i].style.top = '0';
|
|
44
|
+
portalContainerRef[i].classList.remove('is-shown');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
},10);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const evToogleDropdown: TMouseEvent = e => {
|
|
52
|
+
const currRefEl = buttonRef.current;
|
|
53
|
+
if (currRefEl) {
|
|
54
|
+
const buttonEl = (e.target as HTMLElement).closest(`.${currRefEl.classList[0]}`);
|
|
55
|
+
if (!buttonEl) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const containerEl = buttonEl.closest('._refContainer') as HTMLElement;
|
|
59
|
+
if (containerEl) {
|
|
60
|
+
const isCurOpen = containerEl.classList.contains('is-shown');
|
|
61
|
+
const offsetPos = containerEl.getBoundingClientRect();
|
|
62
|
+
|
|
63
|
+
if (!isCurOpen) {
|
|
64
|
+
evResetDropdown();
|
|
65
|
+
containerEl.classList.add('is-shown');
|
|
66
|
+
setTimeout(() => {
|
|
67
|
+
if (portalId) {
|
|
68
|
+
const portalContainerRef = document.querySelector(`#${portalId} ._refDropdownPortal`) as HTMLElement;
|
|
69
|
+
const refScrollContainerEl = document.querySelector('._refScrollContainer') as HTMLElement;
|
|
70
|
+
const wrapperRef = portalContainerRef.querySelector('._refDropdownWrapper') as HTMLElement;
|
|
71
|
+
if (portalContainerRef) {
|
|
72
|
+
if (offsetPos) {
|
|
73
|
+
if (wrapperRef) {
|
|
74
|
+
setTimeout(() => {
|
|
75
|
+
wrapperRef.style.right = 'auto';
|
|
76
|
+
portalContainerRef.style.left = offsetPos.left + offsetPos.width - wrapperRef.getBoundingClientRect().width + 'px';
|
|
77
|
+
portalContainerRef.style.top = offsetPos.top + parseInt($top || '0') - 32 + 'px';
|
|
78
|
+
portalContainerRef.style.zIndex = '8';
|
|
79
|
+
refScrollContainerEl.addEventListener('scroll', evScrollDropdown, true);
|
|
80
|
+
}, 10);
|
|
81
|
+
} else {
|
|
82
|
+
portalContainerRef.style.left = offsetPos.left + ((parseInt($internalWidth || '0') + offsetPos.width) / 2) + 'px';
|
|
83
|
+
portalContainerRef.style.top = (offsetPos.top + offsetPos.height) + 'px';
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
portalContainerRef.classList.add('is-shown');
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}, 10);
|
|
90
|
+
onClick && onClick(e);
|
|
91
|
+
} else {
|
|
92
|
+
containerEl.classList.remove('is-shown');
|
|
93
|
+
setTimeout(() => {
|
|
94
|
+
if (portalId) {
|
|
95
|
+
const portalContainerRef = document.querySelector(`#${portalId} ._refDropdownPortal`) as HTMLElement;
|
|
96
|
+
const refScrollContainerEl = document.querySelector('._refScrollContainer') as HTMLElement;
|
|
97
|
+
const wrapperRef = portalContainerRef.querySelector('._refDropdownWrapper') as HTMLElement;
|
|
98
|
+
if (portalContainerRef) {
|
|
99
|
+
portalContainerRef.classList.remove('is-shown');
|
|
100
|
+
portalContainerRef.style.left = '0px';
|
|
101
|
+
portalContainerRef.style.top = '0px';
|
|
102
|
+
if (wrapperRef) {
|
|
103
|
+
refScrollContainerEl.removeEventListener('scroll', evScrollDropdown, true);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}, 10);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const evMouseEnter: TMouseEvent = e => {
|
|
114
|
+
const currRefEl = buttonRef.current;
|
|
115
|
+
if (currRefEl) {
|
|
116
|
+
const buttonEl = (e.target as HTMLElement).closest(`.${currRefEl.classList[0]}`);
|
|
117
|
+
if (!buttonEl) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const portalContainerRefs = document.querySelectorAll(`._refDropdownPortal`) as NodeListOf<HTMLElement>;
|
|
121
|
+
for (const portalContainerRef of portalContainerRefs) {
|
|
122
|
+
portalContainerRef.classList.remove('is-shown');
|
|
123
|
+
portalContainerRef.style.left = '0';
|
|
124
|
+
portalContainerRef.style.top = '0';
|
|
125
|
+
}
|
|
126
|
+
const containerEl = buttonEl.closest('._refContainer') as HTMLElement;
|
|
127
|
+
if (containerEl) {
|
|
128
|
+
const offsetPos = containerEl.getBoundingClientRect();
|
|
129
|
+
const portalContainerRef = document.querySelector(`#${portalId} ._refDropdownPortal`) as HTMLElement;
|
|
130
|
+
if (portalContainerRef) {
|
|
131
|
+
if (offsetPos) {
|
|
132
|
+
portalContainerRef.style.left = offsetPos.left + ((parseInt($internalWidth || '0') + offsetPos.width) / 2) + 'px';
|
|
133
|
+
let currentTop = offsetPos.top + offsetPos.height;
|
|
134
|
+
const dropdownWrapperEl = portalContainerRef.querySelector('._refDropdownWrapper') as HTMLElement;
|
|
135
|
+
if (dropdownWrapperEl) {
|
|
136
|
+
dropdownWrapperEl.style.display = 'block';
|
|
137
|
+
if ((currentTop + dropdownWrapperEl.clientHeight) >= document.body.clientHeight) {
|
|
138
|
+
currentTop = offsetPos.top - dropdownWrapperEl.clientHeight - 10;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
portalContainerRef.style.top = currentTop + 'px';
|
|
142
|
+
}
|
|
143
|
+
portalContainerRef.classList.add('is-shown');
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
useEffect(() => {
|
|
150
|
+
setTimeout(() => {
|
|
151
|
+
const portalContainerRefs = document.querySelectorAll(`._refDropdownPortal._refHover`) as NodeListOf<HTMLElement>;
|
|
152
|
+
for (let portalContainerRef of portalContainerRefs) {
|
|
153
|
+
portalContainerRef && portalContainerRef.addEventListener('mouseenter', (event) => {
|
|
154
|
+
const el = event.target as HTMLElement;
|
|
155
|
+
el.classList.add('is-shown');
|
|
156
|
+
el.classList.add('unhiddenable');
|
|
157
|
+
})
|
|
158
|
+
portalContainerRef && portalContainerRef.addEventListener('mouseleave', (event) => {
|
|
159
|
+
const el = event.target as HTMLElement;
|
|
160
|
+
el.classList.remove('is-shown');
|
|
161
|
+
el.classList.remove('unhiddenable');
|
|
162
|
+
el.style.left = '0';
|
|
163
|
+
el.style.top = '0';
|
|
164
|
+
})
|
|
165
|
+
}
|
|
166
|
+
}, 1);
|
|
167
|
+
return () => {
|
|
168
|
+
const portalContainerRefs = document.querySelectorAll(`._refDropdownPortal._refHover`) as NodeListOf<HTMLElement>;
|
|
169
|
+
for (let portalContainerRef of portalContainerRefs) {
|
|
170
|
+
portalContainerRef && portalContainerRef.removeEventListener('mouseenter', (event) => {
|
|
171
|
+
const el = event.target as HTMLElement;
|
|
172
|
+
el.classList.add('is-shown');
|
|
173
|
+
el.classList.add('unhiddenable');
|
|
174
|
+
}, true)
|
|
175
|
+
portalContainerRef && portalContainerRef.removeEventListener('mouseleave', (event) => {
|
|
176
|
+
const el = event.target as HTMLElement;
|
|
177
|
+
el.classList.remove('is-shown');
|
|
178
|
+
el.classList.remove('unhiddenable');
|
|
179
|
+
el.style.left = '0';
|
|
180
|
+
el.style.top = '0';
|
|
181
|
+
}, true)
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
}, [portalId])
|
|
185
|
+
|
|
186
|
+
const evMouseLeave: TMouseEvent = e => {
|
|
187
|
+
const currRefEl = buttonRef.current;
|
|
188
|
+
if (currRefEl) {
|
|
189
|
+
const buttonEl = (e.target as HTMLElement).closest(`.${currRefEl.classList[0]}`);
|
|
190
|
+
if (!buttonEl) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
setTimeout(() => {
|
|
194
|
+
const portalContainerRef = document.querySelector(`#${portalId} ._refDropdownPortal`) as HTMLElement;
|
|
195
|
+
if (portalContainerRef && !portalContainerRef.classList.contains('unhiddenable')) {
|
|
196
|
+
portalContainerRef.classList.remove('is-shown');
|
|
197
|
+
portalContainerRef.style.left = '0';
|
|
198
|
+
portalContainerRef.style.top = '0';
|
|
199
|
+
}
|
|
200
|
+
}, 150);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const evCloseDropdown = () => {
|
|
205
|
+
evResetDropdown();
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const DropDownSubTitleEl = (subTitle) ? <DropDownSubTitle>{subTitle}</DropDownSubTitle> : null;
|
|
209
|
+
const DropDownTitleEl = (title) ? <DropDownTitle>{title} {DropDownSubTitleEl}</DropDownTitle> : null;
|
|
210
|
+
const ControlChildrenFilter = children.filter((child: any) => isValidElement(child) ? ((child.props as any)['aria-label'] === 'control') : null);
|
|
211
|
+
const DropDownControlEl = (ControlChildrenFilter && isValidElement(ControlChildrenFilter[0])) ?
|
|
212
|
+
type === 'hover' ? (
|
|
213
|
+
<DropdownControlWrapper>
|
|
214
|
+
<DropDownControl className='_refDropdownControl' {...portalId && { onMouseEnter: evMouseEnter, onMouseLeave: evMouseLeave }}>{(ControlChildrenFilter[0].props as any).children}</DropDownControl>
|
|
215
|
+
<DropDownControl className='_refDropdownControlMobile' ref={buttonRef} onClick={evToogleDropdown}>{(ControlChildrenFilter[0].props as any).children}</DropDownControl>
|
|
216
|
+
</DropdownControlWrapper>
|
|
217
|
+
) : <DropDownControl ref={buttonRef} onClick={evToogleDropdown} >{(ControlChildrenFilter[0].props as any).children}</DropDownControl>
|
|
218
|
+
: null
|
|
219
|
+
const BodyChildrenFilter = children.filter((child: any) => isValidElement(child) ? ((child.props as any)['aria-label'] === 'body') : null)
|
|
220
|
+
const DropDownBodyEl = (BodyChildrenFilter && isValidElement(BodyChildrenFilter[0])) ? <DropDownBody $area={$area}>{(BodyChildrenFilter[0].props as any).children}</DropDownBody> : null
|
|
221
|
+
const ActionChildrenFilter = children.filter((child: any) => isValidElement(child) ? ((child.props as any)['aria-label'] === 'action') : null)
|
|
222
|
+
const DropDownActionEl = (ActionChildrenFilter && isValidElement(ActionChildrenFilter[0])) ? <DropDownAction $position={(ActionChildrenFilter[0].props as any)['data-position']} className={(ActionChildrenFilter[0].props as any).className}>{(ActionChildrenFilter[0].props as any).children}</DropDownAction> : null
|
|
223
|
+
|
|
224
|
+
const dropdownWrapperEl = <DropDownWrapper
|
|
225
|
+
className='_refDropdownWrapper'
|
|
226
|
+
{...$internalWidth && { $internalWidth: $internalWidth }}
|
|
227
|
+
{...$top && { $top: $top }}
|
|
228
|
+
{...$openPosition && { $openPosition: $openPosition }}
|
|
229
|
+
{...$placement && { $placement: $placement }}
|
|
230
|
+
{...$bottom && { $bottom: $bottom }}>
|
|
231
|
+
{DropDownTitleEl}
|
|
232
|
+
{DropDownBodyEl}
|
|
233
|
+
{DropDownActionEl}
|
|
234
|
+
</DropDownWrapper>
|
|
235
|
+
return (
|
|
236
|
+
<DropDownContainer
|
|
237
|
+
className={`_refContainer ${type ? `_refContainer${type}` : ``}`}
|
|
238
|
+
{...$externalWidth && { $externalWidth: $externalWidth }}
|
|
239
|
+
{...$externalMinWidth && { $externalMinWidth: $externalMinWidth }}
|
|
240
|
+
{...$area && { $area: $area }}
|
|
241
|
+
{...$top && { $top: $top }}
|
|
242
|
+
{...$alias && { $alias: $alias }}
|
|
243
|
+
{...$openPosition && { $openPosition: $openPosition }}
|
|
244
|
+
{...$placement && { $placement: $placement }}
|
|
245
|
+
{...$bottom && { $bottom: $bottom }}
|
|
246
|
+
{...props}>
|
|
247
|
+
{DropDownControlEl}
|
|
248
|
+
<DropDownOverlayWrapper className='_refDropdownOverlay' onClick={evCloseDropdown} />
|
|
249
|
+
{ portalId ? <ReactPortalComponent wrapperId={portalId}><DropdownPortalContainer className={`_refDropdownPortal ${type === 'hover' ? '_refHover' : ''}`}>{dropdownWrapperEl}</DropdownPortalContainer></ReactPortalComponent> : dropdownWrapperEl }
|
|
250
|
+
</DropDownContainer>
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export default DropDownComponent;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
import type { TMouseEvent } from '../../typeds/base.typed'
|
|
4
|
+
|
|
5
|
+
export type TDropdown = {
|
|
6
|
+
children: ReactNode[]
|
|
7
|
+
title?: string
|
|
8
|
+
subTitle?: string
|
|
9
|
+
$externalWidth?: string
|
|
10
|
+
$externalMinWidth?: string
|
|
11
|
+
$internalWidth?: string
|
|
12
|
+
$area?: string
|
|
13
|
+
$openPosition?: string
|
|
14
|
+
$placement?: string
|
|
15
|
+
$top?: string
|
|
16
|
+
$bottom?: string
|
|
17
|
+
$alias?: string
|
|
18
|
+
type?: string
|
|
19
|
+
portalId?: string
|
|
20
|
+
onClick?: TMouseEvent
|
|
21
|
+
detail?: string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export type TCloseDropdownFunction = (currentTarget: EventTarget & HTMLElement) => void
|
package/src/components/index.ts
CHANGED
|
@@ -6,3 +6,12 @@ export { default as ButtonComponent } from './button/button';
|
|
|
6
6
|
export { default as LabelComponent } from './label/label';
|
|
7
7
|
export { default as IconComponent } from './icon/icon';
|
|
8
8
|
export { default as LoadingComponent } from './loading/loading';
|
|
9
|
+
export { default as DropdownComponent } from './dropdown/dropdown';
|
|
10
|
+
export { default as MessageComponent } from './message/message';
|
|
11
|
+
export { default as MessageQuestionComponent } from './message/message_question';
|
|
12
|
+
export { default as PopupComponent } from './popup/popup';
|
|
13
|
+
export { default as ReactPortalComponent } from './portal/react_portal';
|
|
14
|
+
|
|
15
|
+
export * from './dropdown/dropdown.typed';
|
|
16
|
+
export * from './message/message.typed';
|
|
17
|
+
export * from './popup/popup.typed';
|