@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,185 @@
|
|
|
1
|
+
/* 2024.06.10 */
|
|
2
|
+
"use client"
|
|
3
|
+
|
|
4
|
+
import { Easing4, Easing4Type } from "@/fesd/methods"
|
|
5
|
+
import { debug, isElement, delay as timeout } from "@/fesd/tools"
|
|
6
|
+
|
|
7
|
+
export type EventCallback = (
|
|
8
|
+
event: string,
|
|
9
|
+
state: boolean,
|
|
10
|
+
options?: Anchor4OptionsType
|
|
11
|
+
) => void
|
|
12
|
+
|
|
13
|
+
export interface Anchor4OptionsType {
|
|
14
|
+
target?: string
|
|
15
|
+
scroller: string
|
|
16
|
+
offseter?: string
|
|
17
|
+
offset?: number
|
|
18
|
+
easing?: Easing4Type
|
|
19
|
+
duration?: number
|
|
20
|
+
center?: boolean
|
|
21
|
+
delay?: number
|
|
22
|
+
direction?: "scrollLeft" | "scrollTop"
|
|
23
|
+
onceEvent?: boolean
|
|
24
|
+
onBeforeScroll?: EventCallback
|
|
25
|
+
onAfterScroll?: EventCallback
|
|
26
|
+
onScrolling?: EventCallback
|
|
27
|
+
debug?: boolean
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface Anchor4Type extends Anchor4OptionsType {
|
|
31
|
+
targetEl?: Element | null
|
|
32
|
+
scrollerEl?: Element | string | null
|
|
33
|
+
offseterEls?: NodeListOf<Element> | null
|
|
34
|
+
eventCalled?: boolean
|
|
35
|
+
setScrolling?: React.Dispatch<boolean>
|
|
36
|
+
setEventCalled?: React.Dispatch<boolean>
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const Anchor4 = async (options: Anchor4Type) => {
|
|
40
|
+
const {
|
|
41
|
+
targetEl,
|
|
42
|
+
scrollerEl = "body",
|
|
43
|
+
offseterEls,
|
|
44
|
+
|
|
45
|
+
eventCalled = false,
|
|
46
|
+
setScrolling,
|
|
47
|
+
setEventCalled,
|
|
48
|
+
|
|
49
|
+
offset = 0,
|
|
50
|
+
easing = "easeInOutExpo",
|
|
51
|
+
duration = 1000,
|
|
52
|
+
onceEvent = false,
|
|
53
|
+
center = false,
|
|
54
|
+
delay,
|
|
55
|
+
direction = "scrollTop",
|
|
56
|
+
onBeforeScroll,
|
|
57
|
+
onAfterScroll,
|
|
58
|
+
onScrolling,
|
|
59
|
+
debug: debugMode = false,
|
|
60
|
+
} = options
|
|
61
|
+
|
|
62
|
+
// Init Options
|
|
63
|
+
const isScrollBody = scrollerEl == "body"
|
|
64
|
+
const scrollerElement = isScrollBody ? document.scrollingElement : scrollerEl
|
|
65
|
+
const scrollerContainer = isScrollBody ? document.body : scrollerElement
|
|
66
|
+
|
|
67
|
+
if (!isElement(scrollerElement) || !isElement(scrollerContainer)) {
|
|
68
|
+
throw new Error('Anchor4, scrollerElement or scrollerContainer is not Element');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const isScrollTop = direction == "scrollTop"
|
|
72
|
+
const property = isScrollTop ? "height" : "width"
|
|
73
|
+
const padding = isScrollTop ? "padding-top" : "padding-left"
|
|
74
|
+
const rect = isScrollTop ? "top" : "left"
|
|
75
|
+
|
|
76
|
+
const targetElRect = targetEl ? targetEl.getBoundingClientRect()[rect] : 0
|
|
77
|
+
|
|
78
|
+
const scrollerElRect = scrollerElement.getBoundingClientRect()
|
|
79
|
+
const scrollerElProperty = scrollerElRect[property]
|
|
80
|
+
const scrollerElPadding = getContainerPadding(scrollerContainer, padding)
|
|
81
|
+
const scrollerElOffset = isScrollBody ? 0 : scrollerElRect[rect]
|
|
82
|
+
const scrollerElCenterOffset = center ? (scrollerElProperty - scrollerElPadding) * 0.5 : 0
|
|
83
|
+
|
|
84
|
+
const offseterElsProperty = offseterEls ? offsetTargetsProperty(offseterEls, property) : 0
|
|
85
|
+
const offsetValue = scrollerElOffset
|
|
86
|
+
+ scrollerElCenterOffset
|
|
87
|
+
+ offseterElsProperty
|
|
88
|
+
+ offset
|
|
89
|
+
|
|
90
|
+
const startScrollValue = scrollerElement[direction]
|
|
91
|
+
const distanceValue = targetEl
|
|
92
|
+
? targetElRect - offsetValue
|
|
93
|
+
: targetElRect - offsetValue - startScrollValue
|
|
94
|
+
|
|
95
|
+
const endScrollValue = startScrollValue + distanceValue
|
|
96
|
+
const totalScrollValue = scrollerElement.scrollHeight - scrollerElement.clientHeight
|
|
97
|
+
|
|
98
|
+
// Check FinalDistanceValue
|
|
99
|
+
let finalDistanceValue = Math.round(distanceValue)
|
|
100
|
+
|
|
101
|
+
if (distanceValue > 0 && endScrollValue >= totalScrollValue)
|
|
102
|
+
finalDistanceValue = totalScrollValue - startScrollValue
|
|
103
|
+
|
|
104
|
+
if (distanceValue < 0 && endScrollValue <= 0)
|
|
105
|
+
finalDistanceValue = 0 - startScrollValue
|
|
106
|
+
|
|
107
|
+
if (finalDistanceValue == 0) return
|
|
108
|
+
|
|
109
|
+
// Scroll Process
|
|
110
|
+
let requestID: number[] = []
|
|
111
|
+
let currentTime: number
|
|
112
|
+
|
|
113
|
+
const setScrollElScrolling = (isScrolling: boolean) => isScrolling
|
|
114
|
+
? scrollerContainer.setAttribute("anchor-scrolling", "")
|
|
115
|
+
: scrollerContainer.removeAttribute("anchor-scrolling")
|
|
116
|
+
|
|
117
|
+
const startScrollAnimation = (state: boolean) => state
|
|
118
|
+
? requestID.push(requestAnimationFrame(scrollAnimationHandler))
|
|
119
|
+
: requestID.length && requestID.forEach((id: number) => cancelAnimationFrame(id))
|
|
120
|
+
|
|
121
|
+
const endScrollAnimation = () => {
|
|
122
|
+
startScrollAnimation(false)
|
|
123
|
+
setScrollElScrolling(false)
|
|
124
|
+
|
|
125
|
+
if (!eventCalled) onAfterScroll?.("onAfterScroll", false, options)
|
|
126
|
+
if (!eventCalled) onScrolling?.("onScrollEnd", false, options)
|
|
127
|
+
if (onceEvent) setEventCalled?.(true)
|
|
128
|
+
|
|
129
|
+
setScrolling?.(false)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const containerScrollHandler = (time: number) => scrollerElement[direction] = Math.round(
|
|
133
|
+
Easing4[easing](time, startScrollValue, Math.round(finalDistanceValue), duration)
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
const scrollAnimationHandler = (requestTime: number) => {
|
|
137
|
+
currentTime = currentTime ?? requestTime
|
|
138
|
+
const time = requestTime - currentTime
|
|
139
|
+
|
|
140
|
+
if (time < duration) startScrollAnimation(true)
|
|
141
|
+
if (time >= duration) endScrollAnimation()
|
|
142
|
+
containerScrollHandler(time)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const Anchor4Active = () => {
|
|
146
|
+
setScrollElScrolling(true)
|
|
147
|
+
startScrollAnimation(true)
|
|
148
|
+
|
|
149
|
+
if (!eventCalled) onScrolling?.("onScrolling", true, options)
|
|
150
|
+
setScrolling?.(true)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// if (debugMode) debug(target ?? "Null", {
|
|
154
|
+
// rect,
|
|
155
|
+
// padding,
|
|
156
|
+
// property,
|
|
157
|
+
// targetElRect,
|
|
158
|
+
// scrollerElProperty,
|
|
159
|
+
// scrollerElPadding,
|
|
160
|
+
// scrollerElCenterOffset,
|
|
161
|
+
// offseterElsProperty,
|
|
162
|
+
// offsetValue,
|
|
163
|
+
// startScrollValue,
|
|
164
|
+
// distanceValue,
|
|
165
|
+
// options: options,
|
|
166
|
+
// },
|
|
167
|
+
// "Anchor4",
|
|
168
|
+
// hooksName
|
|
169
|
+
// )
|
|
170
|
+
|
|
171
|
+
if (!eventCalled) onBeforeScroll?.("onBeforeScroll", true, options)
|
|
172
|
+
if (delay) await timeout(delay)
|
|
173
|
+
|
|
174
|
+
Anchor4Active()
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export const getContainerPadding = (scrollerEl: Element | HTMLElement, padding: string) =>
|
|
178
|
+
parseInt(getComputedStyle(scrollerEl)[padding as any])
|
|
179
|
+
|
|
180
|
+
export const offsetTargetsProperty = (offsetEls: NodeListOf<Element>, property: "width" | "height") => {
|
|
181
|
+
return offsetEls && offsetEls.length ? [...offsetEls]
|
|
182
|
+
.map(element => element.getBoundingClientRect()[property])
|
|
183
|
+
.reduce((value, current) => value + current, 0) : 0
|
|
184
|
+
}
|
|
185
|
+
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/* 2024.06.10 */
|
|
2
|
+
"use client"
|
|
3
|
+
|
|
4
|
+
import * as React from "react"
|
|
5
|
+
import { Anchor4, Anchor4OptionsType } from "./Anchor4"
|
|
6
|
+
import { query, queryAll } from "@/fesd/tools"
|
|
7
|
+
|
|
8
|
+
export interface useAnchor4Type extends
|
|
9
|
+
Anchor4OptionsType {
|
|
10
|
+
as?: React.ElementType
|
|
11
|
+
href?: string
|
|
12
|
+
hash?: boolean
|
|
13
|
+
history?: boolean
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface useAnchor4SettingType {
|
|
17
|
+
targetAttr?: string
|
|
18
|
+
scrollerAttr?: string
|
|
19
|
+
offseterAttr?: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const useAnchor4 = (setting: useAnchor4SettingType = {}) => {
|
|
23
|
+
const {
|
|
24
|
+
targetAttr = "anchor-target",
|
|
25
|
+
scrollerAttr = "anchor-scroller",
|
|
26
|
+
offseterAttr = "anchor-offseter"
|
|
27
|
+
} = setting
|
|
28
|
+
|
|
29
|
+
const [scrolling, setScrolling] = React.useState(false)
|
|
30
|
+
const [eventCalled, setEventCalled] = React.useState(false)
|
|
31
|
+
|
|
32
|
+
const getTarget = (target: string) =>
|
|
33
|
+
query(`[${targetAttr}="${target}"]`)
|
|
34
|
+
|
|
35
|
+
const getScroller = (scroller: string) =>
|
|
36
|
+
query(`[${scrollerAttr}="${scroller}"]`)
|
|
37
|
+
|
|
38
|
+
const getOffseter = (offseter: string) =>
|
|
39
|
+
queryAll(`[${offseterAttr}="${offseter}"]`)
|
|
40
|
+
|
|
41
|
+
const getElements = (options: useAnchor4Type) => {
|
|
42
|
+
const { target, scroller, offseter } = options
|
|
43
|
+
|
|
44
|
+
const scrollerEl = scroller == "body" ? scroller : scroller
|
|
45
|
+
? getScroller(scroller) : undefined
|
|
46
|
+
|
|
47
|
+
const targetEl = target
|
|
48
|
+
? getTarget(target) : undefined
|
|
49
|
+
|
|
50
|
+
const offseterEls = offseter
|
|
51
|
+
? getOffseter(offseter) : undefined
|
|
52
|
+
|
|
53
|
+
return { targetEl, scrollerEl, offseterEls }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const scrollToAnchor = async (options: useAnchor4Type) => {
|
|
57
|
+
if (scrolling) return
|
|
58
|
+
|
|
59
|
+
const {
|
|
60
|
+
targetEl,
|
|
61
|
+
scrollerEl,
|
|
62
|
+
offseterEls
|
|
63
|
+
} = getElements(options)
|
|
64
|
+
|
|
65
|
+
Anchor4({
|
|
66
|
+
...options,
|
|
67
|
+
setScrolling,
|
|
68
|
+
setEventCalled,
|
|
69
|
+
eventCalled,
|
|
70
|
+
targetEl,
|
|
71
|
+
scrollerEl,
|
|
72
|
+
offseterEls,
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
scrolling,
|
|
78
|
+
eventCalled,
|
|
79
|
+
scrollToAnchor,
|
|
80
|
+
methods: {
|
|
81
|
+
getTarget,
|
|
82
|
+
getScroller,
|
|
83
|
+
getOffseter,
|
|
84
|
+
getElements,
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
};
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/* eslint-disable react-hooks/exhaustive-deps */
|
|
2
|
+
/* 2024.06.10 */
|
|
3
|
+
"use client"
|
|
4
|
+
|
|
5
|
+
import { useEffect, useRef } from 'react';
|
|
6
|
+
|
|
7
|
+
interface useDragScroll4Type {
|
|
8
|
+
current: HTMLElement | null,
|
|
9
|
+
active: boolean
|
|
10
|
+
chilPointer?: boolean
|
|
11
|
+
cursorGrab?: boolean
|
|
12
|
+
className?: string
|
|
13
|
+
attributeKey?: string
|
|
14
|
+
attributeValue?: string
|
|
15
|
+
onBeforeDrag?: (e: MouseEvent) => void,
|
|
16
|
+
onAfterDrag?: (e: MouseEvent) => void,
|
|
17
|
+
onDraging?: (e: MouseEvent) => void,
|
|
18
|
+
debug?: boolean
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function useDragScroll4(options: useDragScroll4Type) {
|
|
22
|
+
|
|
23
|
+
const {
|
|
24
|
+
current,
|
|
25
|
+
active = false,
|
|
26
|
+
chilPointer = true,
|
|
27
|
+
cursorGrab = true,
|
|
28
|
+
attributeKey: attrKey = "drag-scrolling",
|
|
29
|
+
attributeValue: attrValue = "",
|
|
30
|
+
className,
|
|
31
|
+
onBeforeDrag,
|
|
32
|
+
onAfterDrag,
|
|
33
|
+
onDraging,
|
|
34
|
+
debug,
|
|
35
|
+
} = options
|
|
36
|
+
|
|
37
|
+
const mouseDown = useRef(false)
|
|
38
|
+
const clientX = useRef(0)
|
|
39
|
+
const clientY = useRef(0)
|
|
40
|
+
|
|
41
|
+
const setCursorGrab = (element: HTMLElement, state: string) => {
|
|
42
|
+
if (state) element.style.cursor = active ? state : ""
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const setDragScrolling = (element: HTMLElement, state: string) => {
|
|
46
|
+
if (state == "add") element.setAttribute(attrKey, attrValue)
|
|
47
|
+
if (state == "remove") element.removeAttribute(attrKey)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const setChilPointer = (nodes: HTMLCollection, state: string) => {
|
|
51
|
+
[...nodes].forEach((node: any) => node.style.pointerEvents = state)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const setClassName = (element: HTMLElement, classname: string, action: string) => {
|
|
55
|
+
if (!element || !classname || !action) return
|
|
56
|
+
if (action == "add") element.classList.add(classname)
|
|
57
|
+
if (action == "remove") element.classList.remove(classname)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const mousedown = (e: MouseEvent) => {
|
|
61
|
+
e.preventDefault();
|
|
62
|
+
|
|
63
|
+
if (!current) return
|
|
64
|
+
if (e.button === 2) return
|
|
65
|
+
|
|
66
|
+
mouseDown.current = true
|
|
67
|
+
clientX.current = e.clientX
|
|
68
|
+
clientY.current = e.clientY
|
|
69
|
+
onBeforeDrag?.(e)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const mousemove = (e: MouseEvent) => {
|
|
73
|
+
const container = current
|
|
74
|
+
|
|
75
|
+
if (!mouseDown.current) return
|
|
76
|
+
if (!container) return
|
|
77
|
+
if (
|
|
78
|
+
Math.abs(clientX.current - e.clientX) > 10 ||
|
|
79
|
+
Math.abs(clientY.current - e.clientY) > 10
|
|
80
|
+
) {
|
|
81
|
+
if (className) setClassName(container, className, "add")
|
|
82
|
+
if (cursorGrab) setCursorGrab(container, "grabbing")
|
|
83
|
+
if (chilPointer) setChilPointer(container.children, "none")
|
|
84
|
+
|
|
85
|
+
container.scrollTop -= e.clientY - clientY.current
|
|
86
|
+
container.scrollLeft -= e.clientX - clientX.current
|
|
87
|
+
setDragScrolling(container, "add")
|
|
88
|
+
onDraging?.(e)
|
|
89
|
+
}
|
|
90
|
+
clientX.current = e.clientX
|
|
91
|
+
clientY.current = e.clientY
|
|
92
|
+
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const mouseup = (e: MouseEvent) => {
|
|
96
|
+
e.preventDefault();
|
|
97
|
+
|
|
98
|
+
const container = current
|
|
99
|
+
|
|
100
|
+
if (!mouseDown.current) return
|
|
101
|
+
if (!container) return
|
|
102
|
+
|
|
103
|
+
if (className) setClassName(container, className, "remove")
|
|
104
|
+
if (chilPointer) setChilPointer(container.children, "auto")
|
|
105
|
+
|
|
106
|
+
mouseDown.current = false
|
|
107
|
+
setDragScrolling(container, "remove")
|
|
108
|
+
onAfterDrag?.(e)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
useEffect(() => {
|
|
112
|
+
if (!active) return
|
|
113
|
+
if (!current) return
|
|
114
|
+
|
|
115
|
+
const container = current
|
|
116
|
+
|
|
117
|
+
current.setAttribute("drag4-scroll", "")
|
|
118
|
+
|
|
119
|
+
if (cursorGrab) setCursorGrab(container, "grab")
|
|
120
|
+
window.addEventListener("mousemove", mousemove)
|
|
121
|
+
window.addEventListener("mouseup", mouseup)
|
|
122
|
+
// window.addEventListener("touchmove", mousemove)
|
|
123
|
+
// window.addEventListener("touchend", mouseup)
|
|
124
|
+
|
|
125
|
+
container.addEventListener("mousedown", mousedown)
|
|
126
|
+
// container.addEventListener("touchstart", mousedown)
|
|
127
|
+
|
|
128
|
+
return () => {
|
|
129
|
+
window.removeEventListener("mousemove", mousemove)
|
|
130
|
+
window.removeEventListener("mouseup", mouseup)
|
|
131
|
+
// window.removeEventListener("touchmove", mousemove)
|
|
132
|
+
// window.removeEventListener("touchend", mouseup)
|
|
133
|
+
|
|
134
|
+
container.removeEventListener("mousedown", mousedown)
|
|
135
|
+
// container.removeEventListener("touchstart", mousedown)
|
|
136
|
+
}
|
|
137
|
+
}, [current, active])
|
|
138
|
+
|
|
139
|
+
}
|