@xwadex/fesd-next 0.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/README.md +35 -0
- package/package.json +45 -0
- package/src/fesd/components/Anchors/Anchor.Controller.tsx +101 -0
- package/src/fesd/components/Anchors/Anchor.Offseter.tsx +36 -0
- package/src/fesd/components/Anchors/Anchor.Target.tsx +97 -0
- package/src/fesd/components/Anchors/Anchor.Wrapper.tsx +160 -0
- package/src/fesd/components/Anchors/Anchors.tsx +12 -0
- package/src/fesd/components/Anchors/index.ts +2 -0
- package/src/fesd/components/Articles/Article.Basic.tsx +49 -0
- package/src/fesd/components/Articles/Article.Figure.tsx +113 -0
- package/src/fesd/components/Articles/ArticleBasic.module.scss +62 -0
- package/src/fesd/components/Articles/ArticleFigure.module.scss +82 -0
- package/src/fesd/components/Articles/index.ts +2 -0
- package/src/fesd/components/Collapse/Collapse.Body.tsx +27 -0
- package/src/fesd/components/Collapse/Collapse.Container.tsx +56 -0
- package/src/fesd/components/Collapse/Collapse.Head.tsx +39 -0
- package/src/fesd/components/Collapse/Collapse.Wrapper.tsx +46 -0
- package/src/fesd/components/Collapse/Collapse.module.scss +23 -0
- package/src/fesd/components/Collapse/Collapse.tsx +11 -0
- package/src/fesd/components/Collapse/CollapseContext.tsx +21 -0
- package/src/fesd/components/Collapse/index.ts +1 -0
- package/src/fesd/components/Modals/Modals.module.scss +84 -0
- package/src/fesd/components/Modals/Modals.tsx +103 -0
- package/src/fesd/components/Modals/Modals.types.ts +52 -0
- package/src/fesd/components/Modals/index.ts +2 -0
- package/src/fesd/components/Overlay/Overlay.module.scss +14 -0
- package/src/fesd/components/Overlay/Overlay.tsx +50 -0
- package/src/fesd/components/Overlay/Overlay.types.ts +5 -0
- package/src/fesd/components/Overlay/index.ts +2 -0
- package/src/fesd/components/index.ts +5 -0
- package/src/fesd/hooks/index.ts +2 -0
- package/src/fesd/hooks/useAnchor4/Anchor4.ts +185 -0
- package/src/fesd/hooks/useAnchor4/index.ts +2 -0
- package/src/fesd/hooks/useAnchor4/useAnchor4.tsx +88 -0
- package/src/fesd/hooks/useDragScroll4.tsx +139 -0
- package/src/fesd/hooks/useTransitionend.tsx +3 -0
- package/src/fesd/methods/Easing4/Easing4.ts +374 -0
- package/src/fesd/methods/Easing4/index.ts +2 -0
- package/src/fesd/methods/index.ts +1 -0
- package/src/fesd/tools.ts +69 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
@import "@/styles/mixins";
|
|
2
|
+
|
|
3
|
+
.root {
|
|
4
|
+
width: 100%;
|
|
5
|
+
display: flex;
|
|
6
|
+
gap: 50px;
|
|
7
|
+
|
|
8
|
+
// &[article-direction="left"] {
|
|
9
|
+
// flex-direction: row-reverse;
|
|
10
|
+
// }
|
|
11
|
+
|
|
12
|
+
figure {
|
|
13
|
+
display: flex;
|
|
14
|
+
flex-direction: column;
|
|
15
|
+
gap: 10px;
|
|
16
|
+
flex: 0 0 30%;
|
|
17
|
+
&:hover {
|
|
18
|
+
figcaption {
|
|
19
|
+
font-size: px(14);
|
|
20
|
+
opacity: 1;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
figcaption {
|
|
24
|
+
font-size: px(14);
|
|
25
|
+
opacity: 0.5;
|
|
26
|
+
transition: 0.5s;
|
|
27
|
+
opacity: 0.5;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
section {
|
|
32
|
+
flex: 1;
|
|
33
|
+
display: flex;
|
|
34
|
+
flex-direction: column;
|
|
35
|
+
gap: 30px;
|
|
36
|
+
|
|
37
|
+
&.phptos {
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
hgroup {
|
|
42
|
+
display: flex;
|
|
43
|
+
flex-direction: column;
|
|
44
|
+
gap: 10px;
|
|
45
|
+
h3 {
|
|
46
|
+
font-size: px(24);
|
|
47
|
+
font-weight: 500;
|
|
48
|
+
line-height: 1.3;
|
|
49
|
+
}
|
|
50
|
+
p {
|
|
51
|
+
font-size: px(16);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
button {
|
|
55
|
+
font-size: px(14);
|
|
56
|
+
font-weight: 400;
|
|
57
|
+
text-transform: uppercase;
|
|
58
|
+
color: #fff;
|
|
59
|
+
padding: 12px 35px;
|
|
60
|
+
background-color: #5b5b5b;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
@import "@/styles/mixins";
|
|
2
|
+
|
|
3
|
+
.root {
|
|
4
|
+
width: 100%;
|
|
5
|
+
max-width: 900px;
|
|
6
|
+
display: flex;
|
|
7
|
+
flex-direction: column;
|
|
8
|
+
gap: 50px;
|
|
9
|
+
|
|
10
|
+
&[article-width="full"] {
|
|
11
|
+
max-width: max-content;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// &[article-direction="left"] {
|
|
15
|
+
// flex-direction: row-reverse;
|
|
16
|
+
// }
|
|
17
|
+
|
|
18
|
+
.content {
|
|
19
|
+
flex: 1;
|
|
20
|
+
display: flex;
|
|
21
|
+
gap: 30px;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.photos {
|
|
25
|
+
flex: 1;
|
|
26
|
+
display: flex;
|
|
27
|
+
flex-direction: row;
|
|
28
|
+
flex-wrap: wrap;
|
|
29
|
+
gap: 50px;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
figure {
|
|
33
|
+
display: flex;
|
|
34
|
+
flex-direction: column;
|
|
35
|
+
gap: 10px;
|
|
36
|
+
flex: 1;
|
|
37
|
+
&:hover {
|
|
38
|
+
figcaption {
|
|
39
|
+
font-size: px(14);
|
|
40
|
+
opacity: 1;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
figcaption {
|
|
44
|
+
font-size: px(14);
|
|
45
|
+
opacity: 0.5;
|
|
46
|
+
transition: 0.5s;
|
|
47
|
+
opacity: 0.5;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
hgroup {
|
|
52
|
+
display: flex;
|
|
53
|
+
flex-direction: column;
|
|
54
|
+
gap: 10px;
|
|
55
|
+
h3 {
|
|
56
|
+
font-size: px(24);
|
|
57
|
+
font-weight: 500;
|
|
58
|
+
line-height: 1.3;
|
|
59
|
+
}
|
|
60
|
+
h6 {
|
|
61
|
+
font-size: px(18);
|
|
62
|
+
font-weight: 500;
|
|
63
|
+
line-height: 1.3;
|
|
64
|
+
}
|
|
65
|
+
h3 + p,
|
|
66
|
+
h6 + p {
|
|
67
|
+
margin-top: 10px;
|
|
68
|
+
}
|
|
69
|
+
p {
|
|
70
|
+
font-size: px(16);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
button {
|
|
74
|
+
font-size: px(14);
|
|
75
|
+
font-weight: 400;
|
|
76
|
+
text-transform: uppercase;
|
|
77
|
+
white-space: nowrap;
|
|
78
|
+
color: #fff;
|
|
79
|
+
padding: 12px 35px;
|
|
80
|
+
background-color: #5b5b5b;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
interface PropsType extends
|
|
4
|
+
React.HTMLAttributes<HTMLDivElement> {
|
|
5
|
+
as?: React.ElementType
|
|
6
|
+
children: React.ReactNode
|
|
7
|
+
name?: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const CollapseBody: React.FC<PropsType> = (props) => {
|
|
11
|
+
const {
|
|
12
|
+
children,
|
|
13
|
+
name,
|
|
14
|
+
as: RootComponent = "div",
|
|
15
|
+
...others
|
|
16
|
+
} = props
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<RootComponent {...others} data-collapse-content={name ?? ""}>
|
|
20
|
+
{children}
|
|
21
|
+
</RootComponent>
|
|
22
|
+
)
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
CollapseBody.displayName = "CollapseBody"
|
|
26
|
+
|
|
27
|
+
export default CollapseBody
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { useEffect, useMemo, useRef, useState } from "react"
|
|
4
|
+
import styles from "./Collapse.module.scss"
|
|
5
|
+
import { useCollapseWrapperContext, ContainerContext } from "./CollapseContext"
|
|
6
|
+
|
|
7
|
+
interface PropsType extends
|
|
8
|
+
React.HTMLAttributes<HTMLDivElement> {
|
|
9
|
+
as?: React.ElementType
|
|
10
|
+
children: React.ReactNode
|
|
11
|
+
name?: string
|
|
12
|
+
open?: boolean
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const CollapseContainer: React.FC<PropsType> = (props) => {
|
|
16
|
+
const {
|
|
17
|
+
children,
|
|
18
|
+
name,
|
|
19
|
+
as: RootComponent = "div",
|
|
20
|
+
className,
|
|
21
|
+
open = false,
|
|
22
|
+
...others
|
|
23
|
+
} = props
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
const [active, setActive] = useState(false)
|
|
27
|
+
|
|
28
|
+
const contextValue = useMemo(() => ({
|
|
29
|
+
active,
|
|
30
|
+
setActive,
|
|
31
|
+
}), [active])
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
setActive(open ?? false)
|
|
37
|
+
}, [open])
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<ContainerContext.Provider value={contextValue}>
|
|
41
|
+
<RootComponent
|
|
42
|
+
{...others}
|
|
43
|
+
className={styles.root + " " + styles.easeInOutExpo + " " + className}
|
|
44
|
+
data-collapse-active={active ? "open" : ""}
|
|
45
|
+
data-collapse-container={name ?? ""}
|
|
46
|
+
>
|
|
47
|
+
{children}, {JSON.stringify(active)}
|
|
48
|
+
</RootComponent>
|
|
49
|
+
</ContainerContext.Provider>
|
|
50
|
+
|
|
51
|
+
)
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
CollapseContainer.displayName = "CollapseContainer"
|
|
55
|
+
|
|
56
|
+
export default CollapseContainer
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { useCollapseContainerContext } from "./CollapseContext";
|
|
4
|
+
|
|
5
|
+
interface PropsType extends
|
|
6
|
+
React.HTMLAttributes<HTMLDivElement> {
|
|
7
|
+
as?: React.ElementType
|
|
8
|
+
children: React.ReactNode
|
|
9
|
+
name?: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const CollapseHead: React.FC<PropsType> = (props) => {
|
|
13
|
+
const {
|
|
14
|
+
children,
|
|
15
|
+
name,
|
|
16
|
+
as: RootComponent = "div",
|
|
17
|
+
...others
|
|
18
|
+
} = props
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
const {
|
|
22
|
+
active,
|
|
23
|
+
setActive
|
|
24
|
+
} = useCollapseContainerContext()
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<RootComponent
|
|
28
|
+
{...others}
|
|
29
|
+
data-collapse-head={name ?? ""}
|
|
30
|
+
onClick={() => setActive && setActive(prev => !prev)}
|
|
31
|
+
>
|
|
32
|
+
{children}
|
|
33
|
+
</RootComponent>
|
|
34
|
+
)
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
CollapseHead.displayName = "CollapseHead"
|
|
38
|
+
|
|
39
|
+
export default CollapseHead
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { useEffect, useMemo, useState } from "react";
|
|
4
|
+
import { WrapperContext } from "./CollapseContext";
|
|
5
|
+
|
|
6
|
+
interface PropsType extends
|
|
7
|
+
React.HTMLAttributes<HTMLDivElement> {
|
|
8
|
+
as?: React.ElementType
|
|
9
|
+
children: React.ReactNode
|
|
10
|
+
name?: string
|
|
11
|
+
openAll?: boolean
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const CollapseWrapper: React.FC<PropsType> = (props) => {
|
|
15
|
+
const {
|
|
16
|
+
children,
|
|
17
|
+
name,
|
|
18
|
+
as: RootComponent = "div",
|
|
19
|
+
openAll = false,
|
|
20
|
+
...others
|
|
21
|
+
} = props
|
|
22
|
+
|
|
23
|
+
const [activeAll, setActiveAll] = useState(false)
|
|
24
|
+
|
|
25
|
+
const contextValue = useMemo(() => ({
|
|
26
|
+
setActiveAll,
|
|
27
|
+
activeAll,
|
|
28
|
+
}), [activeAll])
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
setActiveAll(openAll)
|
|
32
|
+
}, [openAll])
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<WrapperContext.Provider value={contextValue}>
|
|
36
|
+
<div onClick={() => setActiveAll(prev => !prev)}>open all</div>
|
|
37
|
+
<RootComponent {...others} data-collapse-wrapper={name ?? ""}>
|
|
38
|
+
{children}
|
|
39
|
+
</RootComponent>
|
|
40
|
+
</WrapperContext.Provider>
|
|
41
|
+
)
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
CollapseWrapper.displayName = "CollapseWrapper"
|
|
45
|
+
|
|
46
|
+
export default CollapseWrapper
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
.root {
|
|
2
|
+
display: grid;
|
|
3
|
+
grid-template-rows: auto 0fr;
|
|
4
|
+
transition-duration: 0.5s;
|
|
5
|
+
transition-property: grid-template-rows;
|
|
6
|
+
|
|
7
|
+
&[data-collapse-active="open"] {
|
|
8
|
+
grid-template-rows: auto 1fr;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
&[data-direction="columns"] {
|
|
12
|
+
grid-template-columns: auto 0fr;
|
|
13
|
+
transition-property: grid-template-columns;
|
|
14
|
+
|
|
15
|
+
&[data-collapse-active="open"] {
|
|
16
|
+
grid-template-columns: auto 1fr;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
&.easeInOutExpo {
|
|
21
|
+
transition-timing-function: cubic-bezier(0.87, 0, 0.13, 1);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import CollapseWrapper from "./Collapse.Wrapper"
|
|
2
|
+
import CollapseBody from "./Collapse.Body"
|
|
3
|
+
import CollapseContainer from "./Collapse.Container"
|
|
4
|
+
import CollapseHead from "./Collapse.Head"
|
|
5
|
+
|
|
6
|
+
export const Collapse = Object.assign({}, {
|
|
7
|
+
Head: CollapseHead,
|
|
8
|
+
Body: CollapseBody,
|
|
9
|
+
Container: CollapseContainer,
|
|
10
|
+
Wrapper: CollapseWrapper,
|
|
11
|
+
})
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { createContext, use } from "react"
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
export interface WrapperContextType {
|
|
7
|
+
activeAll?: boolean
|
|
8
|
+
setActiveAll?: React.Dispatch<React.SetStateAction<boolean>>
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface ContainerContextType {
|
|
12
|
+
active?: boolean
|
|
13
|
+
setActive?: React.Dispatch<React.SetStateAction<boolean>>
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const WrapperContext = createContext<WrapperContextType>({})
|
|
17
|
+
export const ContainerContext = createContext<ContainerContextType>({})
|
|
18
|
+
|
|
19
|
+
export const useCollapseWrapperContext = () => use(WrapperContext)
|
|
20
|
+
export const useCollapseContainerContext = () => use(ContainerContext)
|
|
21
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./Collapse"
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
.root {
|
|
2
|
+
width: 100dvw;
|
|
3
|
+
height: 100dvh;
|
|
4
|
+
display: flex;
|
|
5
|
+
flex-direction: column;
|
|
6
|
+
position: fixed;
|
|
7
|
+
top: 0;
|
|
8
|
+
left: 0;
|
|
9
|
+
z-index: 2;
|
|
10
|
+
|
|
11
|
+
&.upperLeft {
|
|
12
|
+
justify-content: flex-start;
|
|
13
|
+
align-items: flex-start;
|
|
14
|
+
}
|
|
15
|
+
&.upper {
|
|
16
|
+
justify-content: center;
|
|
17
|
+
align-items: flex-start;
|
|
18
|
+
}
|
|
19
|
+
&.upperRight {
|
|
20
|
+
justify-content: flex-end;
|
|
21
|
+
align-items: flex-start;
|
|
22
|
+
}
|
|
23
|
+
&.left {
|
|
24
|
+
justify-content: flex-start;
|
|
25
|
+
align-items: center;
|
|
26
|
+
}
|
|
27
|
+
&.center {
|
|
28
|
+
justify-content: center;
|
|
29
|
+
align-items: center;
|
|
30
|
+
}
|
|
31
|
+
&.right {
|
|
32
|
+
justify-content: flex-end;
|
|
33
|
+
align-items: center;
|
|
34
|
+
}
|
|
35
|
+
&.lowerLeft {
|
|
36
|
+
justify-content: flex-start;
|
|
37
|
+
align-items: flex-end;
|
|
38
|
+
}
|
|
39
|
+
&.lower {
|
|
40
|
+
justify-content: center;
|
|
41
|
+
align-items: flex-end;
|
|
42
|
+
}
|
|
43
|
+
&.lowerRight {
|
|
44
|
+
justify-content: flex-end;
|
|
45
|
+
align-items: flex-end;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.container {
|
|
50
|
+
width: 100%;
|
|
51
|
+
height: 100%;
|
|
52
|
+
max-width: 990px;
|
|
53
|
+
max-height: 750px;
|
|
54
|
+
background-color: var(--base-bg-color);
|
|
55
|
+
border-radius: 3px;
|
|
56
|
+
display: flex;
|
|
57
|
+
flex-direction: column;
|
|
58
|
+
transition: 0.5s;
|
|
59
|
+
|
|
60
|
+
&.fade {
|
|
61
|
+
opacity: 0;
|
|
62
|
+
transform: translateY(2.5%);
|
|
63
|
+
}
|
|
64
|
+
&.scale {
|
|
65
|
+
opacity: 0;
|
|
66
|
+
transform: scale(0.95);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.active & {
|
|
70
|
+
&.fade {
|
|
71
|
+
opacity: 1;
|
|
72
|
+
transform: translateY(0);
|
|
73
|
+
}
|
|
74
|
+
&.scale {
|
|
75
|
+
opacity: 1;
|
|
76
|
+
transform: scale(1);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.backdrop {
|
|
82
|
+
position: fixed;
|
|
83
|
+
inset: 0;
|
|
84
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/* eslint-disable react-hooks/exhaustive-deps */
|
|
2
|
+
"use client"
|
|
3
|
+
|
|
4
|
+
import * as React from "react"
|
|
5
|
+
import { createPortal } from 'react-dom'
|
|
6
|
+
import { Overlay } from "@/fesd/components"
|
|
7
|
+
import { mergeClassName, delay } from "@/fesd/tools"
|
|
8
|
+
import { ModalsTypes } from "./Modals.types"
|
|
9
|
+
import styles from "./Modals.module.scss"
|
|
10
|
+
|
|
11
|
+
const Modals: React.FC<ModalsTypes> = (props) => {
|
|
12
|
+
|
|
13
|
+
const {
|
|
14
|
+
open = false,
|
|
15
|
+
openInBody = false,
|
|
16
|
+
transitionMode = "fade",
|
|
17
|
+
position = "center",
|
|
18
|
+
backdropClose = false,
|
|
19
|
+
ref,
|
|
20
|
+
children,
|
|
21
|
+
slots = {},
|
|
22
|
+
setClose,
|
|
23
|
+
onTransitionEnter,
|
|
24
|
+
onTransitionExit,
|
|
25
|
+
...others
|
|
26
|
+
} = props
|
|
27
|
+
|
|
28
|
+
const containerRef = React.useRef(null)
|
|
29
|
+
|
|
30
|
+
const [active, setActive] = React.useState(false)
|
|
31
|
+
const [display, setDisplay] = React.useState(false)
|
|
32
|
+
|
|
33
|
+
const Root = slots.root ?? "div"
|
|
34
|
+
const Backdrop = slots.backdrop ?? "div"
|
|
35
|
+
|
|
36
|
+
const transitionEvent = (event: string) => {
|
|
37
|
+
event == "enter" && onTransitionEnter?.()
|
|
38
|
+
event == "exit" && onTransitionExit?.()
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const setActiveHandler = async () => {
|
|
42
|
+
if (active || display) return
|
|
43
|
+
setDisplay(true)
|
|
44
|
+
await delay(100)
|
|
45
|
+
setActive(true)
|
|
46
|
+
await delay(500)
|
|
47
|
+
transitionEvent("enter")
|
|
48
|
+
}
|
|
49
|
+
const setCloseHandler = async () => {
|
|
50
|
+
if (!active || !display) return
|
|
51
|
+
setActive(false)
|
|
52
|
+
await delay(600)
|
|
53
|
+
setDisplay(false)
|
|
54
|
+
transitionEvent("exit")
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const setBackdropClose = () => {
|
|
58
|
+
if (!backdropClose) return
|
|
59
|
+
setClose()
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const rootClass = mergeClassName(
|
|
63
|
+
styles.root,
|
|
64
|
+
styles[position],
|
|
65
|
+
active ? styles.active : ""
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
const containerClass = mergeClassName(
|
|
69
|
+
styles.container,
|
|
70
|
+
styles[transitionMode],
|
|
71
|
+
styles.active
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
const RootComponent = (
|
|
75
|
+
<Root {...others} ref={ref} className={rootClass}>
|
|
76
|
+
<Backdrop className={styles.backdrop} onClick={setBackdropClose}>
|
|
77
|
+
<Overlay open={open} />
|
|
78
|
+
</Backdrop>
|
|
79
|
+
<div ref={containerRef} className={containerClass}>
|
|
80
|
+
{children}
|
|
81
|
+
<button onClick={setClose}>Close</button>
|
|
82
|
+
</div>
|
|
83
|
+
</Root>
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
React.useEffect(() => {
|
|
87
|
+
open ? setActiveHandler() : setCloseHandler()
|
|
88
|
+
}, [open])
|
|
89
|
+
|
|
90
|
+
return (
|
|
91
|
+
<React.Fragment>
|
|
92
|
+
{display && openInBody
|
|
93
|
+
? createPortal(RootComponent, document.body) as React.ReactPortal
|
|
94
|
+
: display ? RootComponent : null
|
|
95
|
+
}
|
|
96
|
+
</React.Fragment>
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
Modals.displayName = "Modals"
|
|
102
|
+
|
|
103
|
+
export default Modals
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export interface ModalsSlots {
|
|
2
|
+
root?: React.ElementType
|
|
3
|
+
backdrop?: React.ElementType
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface ModalsTypes {
|
|
7
|
+
|
|
8
|
+
ref: React.MutableRefObject<HTMLElement | null>
|
|
9
|
+
|
|
10
|
+
//A singal chil content element.
|
|
11
|
+
children: React.ReactElement
|
|
12
|
+
|
|
13
|
+
//A function called, when a transition enters
|
|
14
|
+
onTransitionEnter?: () => void
|
|
15
|
+
|
|
16
|
+
//A function called, when a transition has exited.
|
|
17
|
+
onTransitionExit?: () => void
|
|
18
|
+
|
|
19
|
+
//If "true", the component is shown.
|
|
20
|
+
//Default:"false"
|
|
21
|
+
open?: boolean
|
|
22
|
+
|
|
23
|
+
//A function called when close the component.
|
|
24
|
+
setClose: () => void
|
|
25
|
+
|
|
26
|
+
//Container position
|
|
27
|
+
//Default:"center"
|
|
28
|
+
position?: "upperLeft"
|
|
29
|
+
| "upper"
|
|
30
|
+
| "upperRight"
|
|
31
|
+
| "left"
|
|
32
|
+
| "center"
|
|
33
|
+
| "right"
|
|
34
|
+
| "lowerLeft"
|
|
35
|
+
| "lower"
|
|
36
|
+
| "lowerRight"
|
|
37
|
+
|
|
38
|
+
//Transition Mode
|
|
39
|
+
//Default:"Fade"
|
|
40
|
+
transitionMode?: "fade"
|
|
41
|
+
| "scale"
|
|
42
|
+
|
|
43
|
+
//Append Modals in Body
|
|
44
|
+
//Default: false
|
|
45
|
+
openInBody?: boolean
|
|
46
|
+
|
|
47
|
+
//A function called when click in backdrop.
|
|
48
|
+
//Default:false
|
|
49
|
+
backdropClose?: boolean
|
|
50
|
+
|
|
51
|
+
slots?: ModalsSlots
|
|
52
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { delay, mergeClassName } from "@/fesd/tools"
|
|
5
|
+
import { OverlayType } from "@/fesd/components"
|
|
6
|
+
import styles from "./Overlay.module.scss"
|
|
7
|
+
|
|
8
|
+
const Overlay: React.FC<OverlayType> = ({
|
|
9
|
+
open = false,
|
|
10
|
+
time = 500,
|
|
11
|
+
...props
|
|
12
|
+
}) => {
|
|
13
|
+
|
|
14
|
+
const [display, setDisplay] = React.useState(false)
|
|
15
|
+
const [active, setActive] = React.useState(false)
|
|
16
|
+
|
|
17
|
+
const setActiveHandler = async () => {
|
|
18
|
+
setDisplay(true)
|
|
19
|
+
await delay(100)
|
|
20
|
+
setActive(true)
|
|
21
|
+
}
|
|
22
|
+
const setCloseHandler = async () => {
|
|
23
|
+
setActive(false)
|
|
24
|
+
await delay(600)
|
|
25
|
+
setDisplay(false)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const rootClass = mergeClassName(
|
|
29
|
+
styles.root,
|
|
30
|
+
active ? styles.active : ""
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
const rootStyle = {
|
|
34
|
+
"--trans-time": String(time / 1000) + "s"
|
|
35
|
+
} as React.CSSProperties
|
|
36
|
+
|
|
37
|
+
React.useEffect(() => {
|
|
38
|
+
open ? setActiveHandler() : setCloseHandler()
|
|
39
|
+
}, [open])
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<>
|
|
43
|
+
{display && <div {...props} className={rootClass} style={rootStyle} />}
|
|
44
|
+
</>
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
Overlay.displayName = "Overlay"
|
|
50
|
+
export default Overlay
|