@byeolnaerim/flex-layout 0.0.3 → 0.0.5

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.
Files changed (43) hide show
  1. package/dist/FlexLayoutSplitScreenDragBox-eCtq4kLd.d.cts +31 -0
  2. package/dist/FlexLayoutSplitScreenDragBox-eCtq4kLd.d.ts +31 -0
  3. package/dist/components.cjs +3048 -0
  4. package/dist/components.cjs.map +1 -0
  5. package/dist/{index.css → components.css} +1 -1
  6. package/dist/components.d.cts +122 -0
  7. package/dist/components.d.ts +122 -0
  8. package/dist/components.js +3036 -0
  9. package/dist/components.js.map +1 -0
  10. package/dist/hooks.cjs +425 -0
  11. package/dist/hooks.cjs.map +1 -0
  12. package/dist/hooks.d.cts +37 -0
  13. package/dist/hooks.d.ts +37 -0
  14. package/dist/hooks.js +409 -0
  15. package/dist/hooks.js.map +1 -0
  16. package/dist/index.cjs +0 -3460
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.cts +1 -417
  19. package/dist/index.d.ts +1 -417
  20. package/dist/index.js +0 -3402
  21. package/dist/index.js.map +1 -1
  22. package/dist/providers.cjs +411 -0
  23. package/dist/providers.cjs.map +1 -0
  24. package/dist/providers.d.cts +54 -0
  25. package/dist/providers.d.ts +54 -0
  26. package/dist/providers.js +402 -0
  27. package/dist/providers.js.map +1 -0
  28. package/dist/store.cjs +204 -0
  29. package/dist/store.cjs.map +1 -0
  30. package/dist/store.d.cts +67 -0
  31. package/dist/store.d.ts +67 -0
  32. package/dist/store.js +182 -0
  33. package/dist/store.js.map +1 -0
  34. package/dist/useDrag-CYQnhUFk.d.cts +108 -0
  35. package/dist/useDrag-DR01Ob3s.d.ts +108 -0
  36. package/dist/utils.cjs +209 -0
  37. package/dist/utils.cjs.map +1 -0
  38. package/dist/utils.d.cts +28 -0
  39. package/dist/utils.d.ts +28 -0
  40. package/dist/utils.js +197 -0
  41. package/dist/utils.js.map +1 -0
  42. package/package.json +44 -5
  43. /package/dist/{index.css.map → components.css.map} +0 -0
package/dist/hooks.js ADDED
@@ -0,0 +1,409 @@
1
+ import equal from 'fast-deep-equal';
2
+ import { useRef, useEffect, useState, useCallback } from 'react';
3
+ import { Subject, BehaviorSubject, map, distinctUntilChanged } from 'rxjs';
4
+
5
+ // src/flex-layout/hooks/useDrag.ts
6
+
7
+ // src/flex-layout/utils/FlexLayoutUtils.ts
8
+ var lastTouchEvent;
9
+ function getClientXy(event) {
10
+ let clientX;
11
+ let clientY;
12
+ if (window.MouseEvent && event instanceof window.MouseEvent) {
13
+ clientX = event.clientX;
14
+ clientY = event.clientY;
15
+ } else if (window.TouchEvent && event instanceof window.TouchEvent) {
16
+ const _event = event.touches.length == 0 ? lastTouchEvent : event;
17
+ clientX = _event.touches[0].clientX;
18
+ clientY = _event.touches[0].clientY;
19
+ lastTouchEvent = event;
20
+ } else {
21
+ return;
22
+ }
23
+ return { clientX, clientY };
24
+ }
25
+
26
+ // src/flex-layout/hooks/useDrag.ts
27
+ var dragState = new Subject();
28
+ var filterChildren = (obj) => {
29
+ const { children, ...rest } = obj || {};
30
+ return rest;
31
+ };
32
+ var useDragCapture = (targetRef) => {
33
+ const stateRef = useRef(null);
34
+ const forceUpdate = useRef(0);
35
+ useEffect(() => {
36
+ const subscription = dragState.pipe(
37
+ map((value) => {
38
+ if (!targetRef || !targetRef.current) return null;
39
+ const { x, y } = value;
40
+ const rect = targetRef.current.getBoundingClientRect();
41
+ const {
42
+ width,
43
+ height,
44
+ x: rectX,
45
+ y: rectY,
46
+ right,
47
+ bottom
48
+ } = rect;
49
+ let isOver = false;
50
+ if (x < rectX || x > right || y < rectY || y > bottom) {
51
+ isOver = true;
52
+ }
53
+ const leftBoundary = rectX + width * 0.2;
54
+ const rightBoundary = right - width * 0.2;
55
+ const topBoundary = rectY + height * 0.2;
56
+ const bottomBoundary = bottom - height * 0.2;
57
+ let position = "centerBoundary";
58
+ if (x < leftBoundary) {
59
+ position = "leftBoundary";
60
+ } else if (x > rightBoundary) {
61
+ position = "rightBoundary";
62
+ } else if (y < topBoundary) {
63
+ position = "topBoundary";
64
+ } else if (y > bottomBoundary) {
65
+ position = "bottomBoundary";
66
+ }
67
+ return {
68
+ positionName: position,
69
+ isOver,
70
+ ...value
71
+ };
72
+ }),
73
+ distinctUntilChanged(
74
+ (prev, curr) => equal(filterChildren(prev), filterChildren(curr))
75
+ )
76
+ ).subscribe({
77
+ next: (value) => {
78
+ if (value && !equal(
79
+ filterChildren(stateRef.current),
80
+ filterChildren(value)
81
+ )) {
82
+ stateRef.current = value;
83
+ forceUpdate.current++;
84
+ }
85
+ },
86
+ error: (err) => console.error(err)
87
+ });
88
+ return () => subscription.unsubscribe();
89
+ }, [targetRef]);
90
+ const [, rerender] = useState({});
91
+ useEffect(() => {
92
+ const interval = setInterval(() => {
93
+ rerender({});
94
+ }, 50);
95
+ return () => clearInterval(interval);
96
+ }, []);
97
+ return stateRef.current;
98
+ };
99
+ var dropMovementEventSubject = new Subject();
100
+ var allSplitScreenCount = new BehaviorSubject(0);
101
+ var useDragEvents = ({
102
+ isBlockingActiveInput = false
103
+ }) => {
104
+ const dragResumeTimer = useRef(null);
105
+ const scrollThreshold = 10;
106
+ const isScrolling = useRef(false);
107
+ const isPending = useRef(false);
108
+ const isMouseDown = useRef(false);
109
+ const isDragging = useRef(false);
110
+ const touchStartX = useRef(0);
111
+ const touchStartY = useRef(0);
112
+ const handleStart = useCallback(
113
+ ({
114
+ event: _event,
115
+ dragStartCallback
116
+ }) => {
117
+ const event = _event instanceof Event ? _event : _event.nativeEvent;
118
+ if (dragResumeTimer.current) {
119
+ clearTimeout(dragResumeTimer.current);
120
+ dragResumeTimer.current = null;
121
+ }
122
+ if (event.target.contentEditable === "true" || isBlockingActiveInput && document.activeElement === event.target) {
123
+ return;
124
+ }
125
+ if (event.cancelable) {
126
+ event.preventDefault();
127
+ }
128
+ isPending.current = true;
129
+ isMouseDown.current = true;
130
+ if (event instanceof globalThis.TouchEvent) {
131
+ const touch = event.touches[0];
132
+ touchStartX.current = touch.clientX;
133
+ touchStartY.current = touch.clientY;
134
+ } else if (event instanceof globalThis.MouseEvent) {
135
+ touchStartX.current = event.clientX;
136
+ touchStartY.current = event.clientY;
137
+ }
138
+ setTimeout(() => {
139
+ if (!isPending.current || isScrolling.current) return;
140
+ isPending.current = false;
141
+ isDragging.current = true;
142
+ const xy = getClientXy(event);
143
+ if (!xy) return;
144
+ const { clientX, clientY } = xy;
145
+ dragStartCallback({ x: clientX, y: clientY });
146
+ }, 300);
147
+ },
148
+ [isBlockingActiveInput]
149
+ );
150
+ const handleMove = useCallback(
151
+ ({
152
+ event: _event,
153
+ notDragCallback,
154
+ dragStartCallback,
155
+ moveingCallback
156
+ }) => {
157
+ if (!isMouseDown.current) return;
158
+ const event = _event instanceof Event ? _event : _event.nativeEvent;
159
+ const xy = getClientXy(event);
160
+ if (!xy) return;
161
+ const { clientX, clientY } = xy;
162
+ const deltaX = Math.abs(clientX - touchStartX.current);
163
+ const deltaY = Math.abs(clientY - touchStartY.current);
164
+ if (isPending.current && (deltaX > scrollThreshold || deltaY > scrollThreshold)) {
165
+ isScrolling.current = true;
166
+ isPending.current = false;
167
+ isDragging.current = false;
168
+ if (notDragCallback)
169
+ notDragCallback({ x: clientX, y: clientY });
170
+ if (dragResumeTimer.current) {
171
+ clearTimeout(dragResumeTimer.current);
172
+ dragResumeTimer.current = null;
173
+ }
174
+ dragResumeTimer.current = setTimeout(() => {
175
+ if (!isMouseDown.current) return;
176
+ if (dragStartCallback)
177
+ dragStartCallback({ x: clientX, y: clientY });
178
+ isPending.current = true;
179
+ isScrolling.current = false;
180
+ handleStart({ event: _event, dragStartCallback });
181
+ }, 400);
182
+ return;
183
+ }
184
+ if (!isDragging.current || isPending.current) return;
185
+ moveingCallback({ x: clientX, y: clientY });
186
+ },
187
+ [isBlockingActiveInput]
188
+ );
189
+ const handleEnd = useCallback(
190
+ ({
191
+ event: _event,
192
+ dragEndCallback
193
+ }) => {
194
+ isScrolling.current = false;
195
+ isMouseDown.current = false;
196
+ if (isPending.current) {
197
+ isPending.current = false;
198
+ return;
199
+ }
200
+ const event = _event instanceof Event ? _event : _event.nativeEvent;
201
+ if (!isDragging.current) return;
202
+ isDragging.current = false;
203
+ const xy = getClientXy(event);
204
+ if (!xy) return;
205
+ const { clientX, clientY } = xy;
206
+ dragEndCallback({ x: clientX, y: clientY });
207
+ },
208
+ [isBlockingActiveInput]
209
+ );
210
+ return {
211
+ handleStart,
212
+ handleMove,
213
+ handleEnd
214
+ };
215
+ };
216
+ var folderEventSubject = new Subject();
217
+ var setFolderEvent = (newValue) => {
218
+ folderEventSubject.next(newValue);
219
+ };
220
+ var useFolderEvent = () => {
221
+ const [folderEvent, setFolderEvent2] = useState(
222
+ null
223
+ );
224
+ useEffect(() => {
225
+ const subscription = folderEventSubject.subscribe((e) => {
226
+ if (!e) return;
227
+ setFolderEvent2(e);
228
+ });
229
+ return () => {
230
+ if (subscription) {
231
+ subscription.unsubscribe();
232
+ }
233
+ };
234
+ }, []);
235
+ return { folderEvent };
236
+ };
237
+ var useListPagingForSentinel = ({
238
+ //initPageNumber,
239
+ //initPageSize,
240
+ onReachTerminal
241
+ }) => {
242
+ const [firstChildNode, setFirstChildNode] = useState(null);
243
+ const [lastChildNode, setLastChildNode] = useState(null);
244
+ const observerRef = useRef(null);
245
+ const firstChildRef = useCallback((node) => {
246
+ setFirstChildNode(node);
247
+ }, []);
248
+ const lastChildRef = useCallback((node) => {
249
+ setLastChildNode(node);
250
+ }, []);
251
+ useEffect(() => {
252
+ if (firstChildNode && observerRef.current)
253
+ observerRef.current.unobserve(firstChildNode);
254
+ if (lastChildNode && observerRef.current)
255
+ observerRef.current.unobserve(lastChildNode);
256
+ const handleIntersect = (entries, observer2) => {
257
+ entries.forEach((entry) => {
258
+ if (entry.isIntersecting) {
259
+ if (entry.target === firstChildNode) {
260
+ if (onReachTerminal)
261
+ onReachTerminal({
262
+ isFirst: true,
263
+ isLast: false,
264
+ observer: observer2
265
+ });
266
+ }
267
+ if (entry.target === lastChildNode) {
268
+ if (onReachTerminal)
269
+ onReachTerminal({
270
+ isFirst: false,
271
+ isLast: true,
272
+ observer: observer2
273
+ });
274
+ }
275
+ }
276
+ });
277
+ };
278
+ const observer = new IntersectionObserver(handleIntersect, {
279
+ threshold: 0.1
280
+ });
281
+ observerRef.current = observer;
282
+ if (firstChildNode) observer.observe(firstChildNode);
283
+ if (lastChildNode) observer.observe(lastChildNode);
284
+ return () => {
285
+ if (observerRef.current) {
286
+ observerRef.current.disconnect();
287
+ }
288
+ };
289
+ }, [firstChildNode, lastChildNode]);
290
+ return {
291
+ firstChildRef,
292
+ lastChildRef
293
+ };
294
+ };
295
+ var usePaginationViewNumber = ({
296
+ initPageNumber
297
+ }) => {
298
+ const [showCurrentPageNumber, setShowCurrentPageNumber] = useState(initPageNumber);
299
+ const observerRef = useRef(null);
300
+ const showCurrentPageObserveTarget = useCallback(
301
+ (node) => {
302
+ if (!node) return;
303
+ if (!observerRef.current) {
304
+ observerRef.current = new IntersectionObserver(
305
+ (entries) => {
306
+ entries.forEach((entry) => {
307
+ if (entry.isIntersecting) {
308
+ const pageIndexAttr = entry.target.getAttribute(
309
+ "data-page-index"
310
+ );
311
+ if (!pageIndexAttr) return;
312
+ const pageIndex = parseInt(pageIndexAttr, 10);
313
+ setShowCurrentPageNumber(pageIndex);
314
+ }
315
+ });
316
+ },
317
+ {
318
+ threshold: 0.1
319
+ // 예: 10% 이상 보여야 intersect로 판단
320
+ }
321
+ );
322
+ }
323
+ observerRef.current.observe(node);
324
+ },
325
+ []
326
+ );
327
+ useEffect(() => {
328
+ const currentObserver = observerRef.current;
329
+ return () => {
330
+ if (currentObserver) {
331
+ currentObserver.disconnect();
332
+ }
333
+ };
334
+ }, []);
335
+ return {
336
+ showCurrentPageNumber,
337
+ showCurrentPageObserveTarget
338
+ };
339
+ };
340
+ var usePagingHandler = ({
341
+ lastCallPageNumber,
342
+ dataListRef
343
+ }) => {
344
+ const jumpingPageNumberRef = useRef(lastCallPageNumber);
345
+ useEffect(() => {
346
+ if (jumpingPageNumberRef.current) {
347
+ setTimeout(() => {
348
+ jumpingPageNumberRef.current = null;
349
+ }, 1e3);
350
+ }
351
+ }, [jumpingPageNumberRef]);
352
+ const paginationScrollIntoViewTarget = useRef({});
353
+ const pageNumberRef = useRef(lastCallPageNumber);
354
+ const setPaginationRef = useCallback(
355
+ (i) => (node) => {
356
+ if (!node) return;
357
+ paginationScrollIntoViewTarget.current[i] = node;
358
+ if (jumpingPageNumberRef.current !== null && i === jumpingPageNumberRef.current) {
359
+ node.scrollIntoView({
360
+ behavior: "instant",
361
+ // 필요한 경우 'smooth' 등으로 수정 가능
362
+ block: "start"
363
+ });
364
+ jumpingPageNumberRef.current = null;
365
+ }
366
+ },
367
+ []
368
+ );
369
+ const handleReachTerminal = ({ isFirst, isLast, observer }, dataCallFetch) => {
370
+ if (dataListRef.current.length === 0) return;
371
+ if (jumpingPageNumberRef.current != null) return;
372
+ if (!isFirst && !isLast) return;
373
+ const callPageNumber = isFirst ? Math.max(pageNumberRef.current - 1, 0) : pageNumberRef.current + 1;
374
+ if (dataListRef.current[callPageNumber] != null && (dataListRef.current[callPageNumber]?.length || 0) > 0)
375
+ return;
376
+ jumpingPageNumberRef.current = callPageNumber;
377
+ setTimeout(() => {
378
+ jumpingPageNumberRef.current = null;
379
+ }, 1e3);
380
+ dataCallFetch(callPageNumber);
381
+ };
382
+ const handleClickPageChange = (page, dataCallFetch) => {
383
+ const pageData = dataListRef.current[page];
384
+ if (pageData != null && Array.isArray(pageData) && pageData.length > 0) {
385
+ paginationScrollIntoViewTarget.current[page]?.scrollIntoView({
386
+ behavior: "smooth",
387
+ block: "start"
388
+ });
389
+ return;
390
+ }
391
+ jumpingPageNumberRef.current = page;
392
+ setTimeout(() => {
393
+ jumpingPageNumberRef.current = null;
394
+ }, 1e3);
395
+ dataCallFetch(page);
396
+ };
397
+ return {
398
+ jumpingPageNumberRef,
399
+ paginationScrollIntoViewTarget,
400
+ pageNumberRef,
401
+ setPaginationRef,
402
+ handleReachTerminal,
403
+ handleClickPageChange
404
+ };
405
+ };
406
+
407
+ export { allSplitScreenCount, dragState, dropMovementEventSubject, folderEventSubject, setFolderEvent, useDragCapture, useDragEvents, useFolderEvent, useListPagingForSentinel, usePaginationViewNumber, usePagingHandler };
408
+ //# sourceMappingURL=hooks.js.map
409
+ //# sourceMappingURL=hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/flex-layout/utils/FlexLayoutUtils.ts","../src/flex-layout/hooks/useDrag.ts","../src/flex-layout/hooks/useListPaging.ts"],"names":["setFolderEvent","useState","useRef","useCallback","useEffect","observer"],"mappings":"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","file":"hooks.js","sourcesContent":["export function isDocumentOut({ x, y }: { x: number; y: number }) {\r\n if (typeof window == 'undefined') return;\r\n const { innerWidth, innerHeight, scrollX, scrollY } = window;\r\n\r\n return (\r\n x < 0 || y < 0 || x > innerWidth + scrollX || y > innerHeight + scrollY\r\n );\r\n}\r\n\r\nlet lastTouchEvent: globalThis.TouchEvent;\r\nexport function getClientXy(event: Event) {\r\n let clientX: number;\r\n let clientY: number;\r\n if (window.MouseEvent && event instanceof window.MouseEvent) {\r\n clientX = event.clientX;\r\n clientY = event.clientY;\r\n } else if (window.TouchEvent && event instanceof window.TouchEvent) {\r\n const _event = event.touches.length == 0 ? lastTouchEvent : event;\r\n clientX = _event!.touches[0].clientX;\r\n clientY = _event!.touches[0].clientY;\r\n lastTouchEvent = event;\r\n } else {\r\n return; // 해당 이벤트 타입이 MouseEvent나 TouchEvent가 아니라면 무시\r\n }\r\n return { clientX, clientY };\r\n}\r\n\r\nexport function isOverMove(elementSize: number, elementMinSize: number) {\r\n return (\r\n Math.floor(elementSize) <= 0 ||\r\n (isNaN(elementMinSize)\r\n ? false\r\n : elementMinSize >= Math.floor(elementSize))\r\n );\r\n}\r\n\r\nexport function findNotCloseFlexContent(\r\n target: HTMLElement | Element | null,\r\n direction: 'previousElementSibling' | 'nextElementSibling'\r\n) {\r\n if (!target) return target;\r\n let _target = target as HTMLElement;\r\n const isCloseCheck = () => {\r\n let grow =\r\n parseFloat(window.getComputedStyle(_target).flex.split(' ')[0]) ||\r\n 0;\r\n if (grow == 0) {\r\n return true;\r\n } else {\r\n return false;\r\n }\r\n };\r\n while (isCloseCheck()) {\r\n let nextTarget = _target[direction]?.[direction];\r\n _target = nextTarget as HTMLElement;\r\n if (!_target) {\r\n break;\r\n }\r\n }\r\n return _target as HTMLElement | HTMLDivElement | null;\r\n}\r\n\r\nexport function remain(flexContainerList: Array<HTMLElement>) {\r\n return new Promise(resolve => {\r\n let notGrowList: Array<HTMLElement> = [];\r\n let totalGrow = flexContainerList.reduce((t, e, i) => {\r\n if (e.hasAttribute('data-grow') == false) {\r\n notGrowList.push(e);\r\n return t;\r\n }\r\n let grow = parseFloat(e.dataset.grow || '');\r\n e.style.flex = `${grow} 1 0%`;\r\n t -= grow;\r\n return t;\r\n }, flexContainerList.length);\r\n\r\n if (notGrowList.length != 0) {\r\n resize(notGrowList, totalGrow);\r\n }\r\n\r\n resolve(flexContainerList);\r\n });\r\n}\r\n\r\nexport function resize(list: Array<HTMLElement>, totalGrow: number) {\r\n return new Promise(resolve => {\r\n // totalGrow 값을 리스트의 개수로 나누어 균등 할당\r\n let resizeWeight = totalGrow / list.length;\r\n list.forEach(e => {\r\n e.dataset.grow = resizeWeight.toString();\r\n e.style.flex = `${resizeWeight} 1 0%`;\r\n });\r\n resolve(resizeWeight);\r\n });\r\n}\r\n\r\nexport function mathWeight(totalCount: number, totalGrow: number) {\r\n return 1 + (totalGrow - totalCount) / totalCount;\r\n}\r\nexport function mathGrow(\r\n childSize: number,\r\n parentSize: number,\r\n containerCount: number\r\n) {\r\n return containerCount * (childSize / parentSize);\r\n}\r\n\r\nexport function getGrow(growTarget: HTMLElement | Element) {\r\n const target =\r\n growTarget instanceof Element\r\n ? (growTarget as HTMLElement)\r\n : growTarget;\r\n return (\r\n parseFloat(target.style.flex.split(' ')[0]) ||\r\n parseFloat(target.dataset.grow || '')\r\n );\r\n}\r\n\r\nexport function closeFlex(\r\n resizeTarget: HTMLElement,\r\n containers: HTMLElement[],\r\n {\r\n isResize = false,\r\n isDsiabledResizePanel = false,\r\n sizeName,\r\n }: {\r\n isResize?: boolean;\r\n isDsiabledResizePanel?: boolean;\r\n sizeName: 'width' | 'height';\r\n }\r\n) {\r\n return new Promise(resolve => {\r\n if (!resizeTarget.hasAttribute('data-is_resize_panel')) {\r\n // resolve(resizeTarget);\r\n // return;\r\n } else if (isDsiabledResizePanel) {\r\n resizeTarget.dataset.is_resize_panel = 'false';\r\n }\r\n\r\n resizeTarget.dataset.prev_grow = getGrow(resizeTarget).toString();\r\n\r\n let notCloseList = containers.filter(\r\n e => e.style.flex != '0 1 0%' && e != resizeTarget\r\n );\r\n let notCloseAndOpenTargetList = [...notCloseList, resizeTarget];\r\n //let resizeWeight = this.mathWeight(notCloseList, this.#forResizeList.length);\r\n notCloseAndOpenTargetList.forEach(e => {\r\n e.style.transition = 'flex 0.5s';\r\n e.ontransitionend = event => {\r\n if (event.propertyName != 'flex-grow') {\r\n return;\r\n }\r\n notCloseAndOpenTargetList.forEach(\r\n e => (e.style.transition = '')\r\n );\r\n //e.style.transition = '';\r\n e.ontransitionend = () => {};\r\n };\r\n\r\n if (e == resizeTarget) {\r\n e.dataset.grow = '0';\r\n e.style.flex = `0 1 0%`;\r\n return;\r\n }\r\n\r\n if (isResize) {\r\n return;\r\n }\r\n\r\n let percent = getGrow(e) / containers.length;\r\n //let percentWeight = this.#forResizeList.length * percent;\r\n //let remainWeight = resizeWeight * percent;\r\n if (notCloseList.length == 1) {\r\n e.dataset.grow = containers.length.toString();\r\n e.style.flex = `${containers.length} 1 0%`;\r\n return;\r\n }\r\n e.dataset.grow = (containers.length * percent).toString();\r\n e.style.flex = `${containers.length * percent} 1 0%`;\r\n });\r\n\r\n if (isResize) {\r\n resize(notCloseList, containers.length);\r\n }\r\n\r\n resolve(resizeTarget);\r\n });\r\n}\r\n\r\nexport function openFlex(\r\n resizeTarget: HTMLElement,\r\n containers: HTMLElement[],\r\n {\r\n isPrevSizeOpen = false,\r\n isResize = false,\r\n openGrowImportant = 0,\r\n sizeName,\r\n }: {\r\n isPrevSizeOpen?: boolean;\r\n isResize?: boolean;\r\n openGrowImportant?: number;\r\n sizeName?: 'width' | 'height'; // 유니언 타입으로 수정\r\n }\r\n) {\r\n return new Promise(resolve => {\r\n if (!resizeTarget.hasAttribute('data-is_resize_panel')) {\r\n // resolve(resizeTarget);\r\n // return;\r\n } else if (\r\n resizeTarget.hasAttribute('data-is_resize_panel') &&\r\n resizeTarget.dataset.is_resize_panel == 'false'\r\n ) {\r\n resizeTarget.dataset.is_resize_panel = 'true';\r\n }\r\n\r\n let notCloseList = containers.filter(\r\n e => e.style.flex != '0 1 0%' && e != resizeTarget\r\n );\r\n let notCloseAndOpenTargetList = [...notCloseList, resizeTarget];\r\n //let resizeWeight = this.mathWeight(notCloseAndOpenTargetList, this.#forResizeList.length);\r\n let openTargetGrow = 1;\r\n const sizeStyleName = ('client' +\r\n sizeName!.charAt(0).toUpperCase() +\r\n sizeName!.substring(1)) as 'clientHeight' | 'clientWidth';\r\n const parentSize =\r\n (sizeName &&\r\n resizeTarget.parentElement &&\r\n resizeTarget.parentElement[sizeStyleName]) ||\r\n 0;\r\n if (isPrevSizeOpen && resizeTarget.hasAttribute('data-prev_grow')) {\r\n openTargetGrow =\r\n parseFloat(resizeTarget.dataset.prev_grow || '1') || 1;\r\n //resizeTarget.removeAttribute('data-prev_grow');\r\n } else if (parentSize && parentSize !== 0) {\r\n openTargetGrow =\r\n (parentSize / notCloseList.length / (parentSize - 1)) *\r\n containers.length;\r\n } else {\r\n openTargetGrow = 1;\r\n }\r\n if (openGrowImportant) {\r\n openTargetGrow = openGrowImportant;\r\n }\r\n openTargetGrow = openTargetGrow === Infinity ? 1 : openTargetGrow;\r\n //notCloseList.forEach(e=>{\r\n notCloseAndOpenTargetList.forEach(e => {\r\n e.style.transition = 'flex 0.5s';\r\n e.ontransitionend = event => {\r\n if (event.propertyName != 'flex-grow') {\r\n return;\r\n }\r\n notCloseAndOpenTargetList.forEach(\r\n e => (e.style.transition = '')\r\n );\r\n //e.style.transition = '';\r\n e.ontransitionend = () => {};\r\n };\r\n\r\n if (e == resizeTarget) {\r\n resizeTarget.dataset.grow = openTargetGrow.toString();\r\n resizeTarget.style.flex = `${openTargetGrow} 1 0%`;\r\n return;\r\n }\r\n\r\n if (isResize) {\r\n return;\r\n }\r\n\r\n let grow =\r\n (parentSize / notCloseList.length / (parentSize - 1)) *\r\n (containers.length - openTargetGrow);\r\n grow = grow === Infinity ? 1 : grow;\r\n //let percent = getGrow(e) / totalGrow - openTargetGrow / totalGrow;\r\n e.dataset.grow = grow.toString();\r\n\r\n e.style.flex = `${grow} 1 0%`;\r\n });\r\n\r\n if (isResize) {\r\n resize(notCloseAndOpenTargetList, containers.length);\r\n }\r\n\r\n resolve(openTargetGrow);\r\n });\r\n}\r\n","import equal from \"fast-deep-equal\";\r\nimport {\r\n\tReactElement,\r\n\tRefObject,\r\n\tuseCallback,\r\n\tuseEffect,\r\n\tuseRef,\r\n\tuseState,\r\n} from \"react\";\r\nimport { BehaviorSubject, distinctUntilChanged, map, Subject } from \"rxjs\";\r\nimport { DropDocumentOutsideOption } from \"../components/FlexLayoutSplitScreenDragBox\";\r\nimport { getClientXy } from \"../utils/FlexLayoutUtils\";\r\nexport interface DragStateType {\r\n\tisDragging: boolean;\r\n\tisDrop: boolean;\r\n\tnavigationTitle?: string;\r\n\tchildren?: ReactElement;\r\n\tcontainerName: string;\r\n\tx: number;\r\n\ty: number;\r\n\tdropDocumentOutsideOption?: DropDocumentOutsideOption;\r\n\tdropEndCallback?: ({\r\n\t\tx,\r\n\t\ty,\r\n\t\tcontainerName,\r\n\t}: {\r\n\t\tx: number;\r\n\t\ty: number;\r\n\t\tcontainerName: string;\r\n\t}) => void;\r\n\tscreenKey?: string;\r\n\tcustomData?: Record<string, string | number | boolean | undefined>;\r\n}\r\nexport type PositionName =\r\n\t| \"centerBoundary\"\r\n\t| \"leftBoundary\"\r\n\t| \"rightBoundary\"\r\n\t| \"topBoundary\"\r\n\t| \"bottomBoundary\";\r\n\r\nexport interface DragStateResultType extends DragStateType {\r\n\tpositionName: PositionName;\r\n\tisOver: boolean;\r\n}\r\nexport const dragState = new Subject<DragStateType>();\r\nconst filterChildren = (obj: any) => {\r\n\t// 객체 복사 후 children 속성 제거\r\n\tconst { children, ...rest } = obj || {};\r\n\treturn rest;\r\n};\r\n\r\nexport const useDragCapture = (targetRef: RefObject<HTMLElement | null>) => {\r\n\tconst stateRef = useRef<DragStateResultType | null>(null); // 상태를 저장하는 useRef\r\n\tconst forceUpdate = useRef(0); // 강제로 업데이트를 트리거하기 위한 변수\r\n\r\n\tuseEffect(() => {\r\n\t\tconst subscription = dragState\r\n\t\t\t.pipe(\r\n\t\t\t\tmap((value) => {\r\n\t\t\t\t\tif (!targetRef || !targetRef.current) return null;\r\n\r\n\t\t\t\t\tconst { x, y } = value;\r\n\t\t\t\t\tconst rect = targetRef.current.getBoundingClientRect();\r\n\t\t\t\t\tconst {\r\n\t\t\t\t\t\twidth,\r\n\t\t\t\t\t\theight,\r\n\t\t\t\t\t\tx: rectX,\r\n\t\t\t\t\t\ty: rectY,\r\n\t\t\t\t\t\tright,\r\n\t\t\t\t\t\tbottom,\r\n\t\t\t\t\t} = rect;\r\n\r\n\t\t\t\t\tlet isOver = false;\r\n\t\t\t\t\tif (x < rectX || x > right || y < rectY || y > bottom) {\r\n\t\t\t\t\t\tisOver = true;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tconst leftBoundary = rectX + width * 0.2;\r\n\t\t\t\t\tconst rightBoundary = right - width * 0.2;\r\n\t\t\t\t\tconst topBoundary = rectY + height * 0.2;\r\n\t\t\t\t\tconst bottomBoundary = bottom - height * 0.2;\r\n\r\n\t\t\t\t\tlet position = \"centerBoundary\";\r\n\t\t\t\t\tif (x < leftBoundary) {\r\n\t\t\t\t\t\tposition = \"leftBoundary\";\r\n\t\t\t\t\t} else if (x > rightBoundary) {\r\n\t\t\t\t\t\tposition = \"rightBoundary\";\r\n\t\t\t\t\t} else if (y < topBoundary) {\r\n\t\t\t\t\t\tposition = \"topBoundary\";\r\n\t\t\t\t\t} else if (y > bottomBoundary) {\r\n\t\t\t\t\t\tposition = \"bottomBoundary\";\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\treturn {\r\n\t\t\t\t\t\tpositionName: position as PositionName,\r\n\t\t\t\t\t\tisOver,\r\n\t\t\t\t\t\t...value,\r\n\t\t\t\t\t};\r\n\t\t\t\t}),\r\n\t\t\t\tdistinctUntilChanged((prev, curr) =>\r\n\t\t\t\t\tequal(filterChildren(prev), filterChildren(curr)),\r\n\t\t\t\t),\r\n\t\t\t)\r\n\t\t\t.subscribe({\r\n\t\t\t\tnext: (value) => {\r\n\t\t\t\t\tif (\r\n\t\t\t\t\t\tvalue &&\r\n\t\t\t\t\t\t!equal(\r\n\t\t\t\t\t\t\tfilterChildren(stateRef.current),\r\n\t\t\t\t\t\t\tfilterChildren(value),\r\n\t\t\t\t\t\t)\r\n\t\t\t\t\t) {\r\n\t\t\t\t\t\tstateRef.current = value; // 상태를 업데이트\r\n\t\t\t\t\t\tforceUpdate.current++; // 업데이트 트리거\r\n\t\t\t\t\t}\r\n\t\t\t\t},\r\n\t\t\t\terror: (err) => console.error(err),\r\n\t\t\t});\r\n\r\n\t\treturn () => subscription.unsubscribe();\r\n\t}, [targetRef]);\r\n\r\n\t// 강제 렌더링을 트리거하기 위한 업데이트\r\n\tconst [, rerender] = useState({});\r\n\tuseEffect(() => {\r\n\t\tconst interval = setInterval(() => {\r\n\t\t\trerender({}); // 변경된 ref 상태를 반영\r\n\t\t}, 50); // 50ms 간격으로 렌더링 반영\r\n\t\treturn () => clearInterval(interval);\r\n\t}, []);\r\n\r\n\treturn stateRef.current;\r\n};\r\nexport interface DropTargetComponent {\r\n\tcontainerName: string;\r\n\tcomponent: ReactElement;\r\n\tnavigationTitle?: string;\r\n\tdropDocumentOutsideOption?: DropDocumentOutsideOption;\r\n\tscreenKey: string;\r\n}\r\nexport type DropPositionOrderName = \"before\" | \"center\" | \"after\";\r\n\r\nexport interface DropMovementEventType {\r\n\tstate: \"remove\" | \"append\" | \"change\";\r\n\ttargetParentLayoutName: string;\r\n\ttargetLayoutName: string;\r\n\ttargetContainerName: string;\r\n\ttargetComponent?: ReactElement;\r\n\tnextContainerName?: string;\r\n\tparentOrderName?: DropPositionOrderName;\r\n\torderName?: DropPositionOrderName;\r\n\tx?: number;\r\n\ty?: number;\r\n\tdropEndCallback?: ({\r\n\t\tx,\r\n\t\ty,\r\n\t\tcontainerName,\r\n\t}: {\r\n\t\tx: number;\r\n\t\ty: number;\r\n\t\tcontainerName: string;\r\n\t}) => void;\r\n\tdropTargetComponentEvent?: DropTargetComponentEvent;\r\n}\r\nexport interface DropTargetComponentEvent extends Omit<\r\n\tDropTargetComponent,\r\n\t\"containerName\" | \"component\"\r\n> {\r\n\tdirection: \"row\" | \"column\";\r\n}\r\nexport const dropMovementEventSubject = new Subject<DropMovementEventType>();\r\n\r\nexport const allSplitScreenCount = new BehaviorSubject<number>(0);\r\n\r\nexport const useDragEvents = ({\r\n\tisBlockingActiveInput = false,\r\n}: {\r\n\tisBlockingActiveInput?: boolean;\r\n}) => {\r\n\tconst dragResumeTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\r\n\tconst scrollThreshold = 10; // 이동 거리 임계값\r\n\r\n\tconst isScrolling = useRef<boolean>(false);\r\n\tconst isPending = useRef(false);\r\n\tconst isMouseDown = useRef(false);\r\n\tconst isDragging = useRef(false); // 드래그 상태 플래그\r\n\tconst touchStartX = useRef<number>(0);\r\n\tconst touchStartY = useRef<number>(0);\r\n\r\n\tconst handleStart = useCallback(\r\n\t\t({\r\n\t\t\tevent: _event,\r\n\t\t\tdragStartCallback,\r\n\t\t}: {\r\n\t\t\tevent: React.MouseEvent | React.TouchEvent | Event;\r\n\t\t\tdragStartCallback: ({ x, y }: { x: number; y: number }) => void;\r\n\t\t}) => {\r\n\t\t\tconst event = _event instanceof Event ? _event : _event.nativeEvent;\r\n\r\n\t\t\t// 기존 타이머가 있다면 정리\r\n\t\t\tif (dragResumeTimer.current) {\r\n\t\t\t\tclearTimeout(dragResumeTimer.current);\r\n\t\t\t\tdragResumeTimer.current = null;\r\n\t\t\t}\r\n\r\n\t\t\tif (\r\n\t\t\t\t(event.target as HTMLElement).contentEditable === \"true\" ||\r\n\t\t\t\t(isBlockingActiveInput &&\r\n\t\t\t\t\tdocument.activeElement === event.target)\r\n\t\t\t) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t\tif (event.cancelable) {\r\n\t\t\t\tevent.preventDefault(); // cancelable=false 면 자동 skip\r\n\t\t\t}\r\n\t\t\tisPending.current = true;\r\n\t\t\tisMouseDown.current = true;\r\n\t\t\tif (event instanceof globalThis.TouchEvent) {\r\n\t\t\t\tconst touch = event.touches[0];\r\n\t\t\t\ttouchStartX.current = touch.clientX;\r\n\t\t\t\ttouchStartY.current = touch.clientY;\r\n\t\t\t} else if (event instanceof globalThis.MouseEvent) {\r\n\t\t\t\ttouchStartX.current = event.clientX;\r\n\t\t\t\ttouchStartY.current = event.clientY;\r\n\t\t\t}\r\n\t\t\t//event.preventDefault();\r\n\t\t\tsetTimeout(() => {\r\n\t\t\t\tif (!isPending.current || isScrolling.current) return; // 스크롤 중이면 드래그 취소\r\n\t\t\t\tisPending.current = false;\r\n\t\t\t\tisDragging.current = true;\r\n\r\n\t\t\t\tconst xy = getClientXy(event);\r\n\t\t\t\tif (!xy) return;\r\n\r\n\t\t\t\tconst { clientX, clientY } = xy;\r\n\r\n\t\t\t\tdragStartCallback({ x: clientX, y: clientY });\r\n\t\t\t}, 300);\r\n\t\t},\r\n\t\t[isBlockingActiveInput],\r\n\t);\r\n\r\n\tconst handleMove = useCallback(\r\n\t\t({\r\n\t\t\tevent: _event,\r\n\t\t\tnotDragCallback,\r\n\t\t\tdragStartCallback,\r\n\t\t\tmoveingCallback,\r\n\t\t}: {\r\n\t\t\tevent: React.MouseEvent | React.TouchEvent | Event;\r\n\t\t\tnotDragCallback?: ({ x, y }: { x: number; y: number }) => void;\r\n\t\t\tdragStartCallback: ({ x, y }: { x: number; y: number }) => void;\r\n\t\t\tmoveingCallback: ({ x, y }: { x: number; y: number }) => void;\r\n\t\t}) => {\r\n\t\t\tif (!isMouseDown.current) return;\r\n\t\t\tconst event = _event instanceof Event ? _event : _event.nativeEvent;\r\n\r\n\t\t\tconst xy = getClientXy(event);\r\n\t\t\tif (!xy) return;\r\n\t\t\tconst { clientX, clientY } = xy;\r\n\t\t\tconst deltaX = Math.abs(clientX - touchStartX.current);\r\n\t\t\tconst deltaY = Math.abs(clientY - touchStartY.current);\r\n\r\n\t\t\tif (\r\n\t\t\t\tisPending.current &&\r\n\t\t\t\t(deltaX > scrollThreshold || deltaY > scrollThreshold)\r\n\t\t\t) {\r\n\t\t\t\tisScrolling.current = true; // 스크롤 중으로 설정\r\n\t\t\t\tisPending.current = false; // 드래그 취소\r\n\t\t\t\tisDragging.current = false;\r\n\r\n\t\t\t\tif (notDragCallback)\r\n\t\t\t\t\tnotDragCallback({ x: clientX, y: clientY });\r\n\t\t\t\t//if (clonedNodeRef.current) clonedNodeRef.current.remove();\r\n\r\n\t\t\t\tif (dragResumeTimer.current) {\r\n\t\t\t\t\tclearTimeout(dragResumeTimer.current);\r\n\t\t\t\t\tdragResumeTimer.current = null;\r\n\t\t\t\t}\r\n\t\t\t\tdragResumeTimer.current = setTimeout(() => {\r\n\t\t\t\t\tif (!isMouseDown.current) return;\r\n\t\t\t\t\tif (dragStartCallback)\r\n\t\t\t\t\t\tdragStartCallback({ x: clientX, y: clientY });\r\n\t\t\t\t\tisPending.current = true;\r\n\t\t\t\t\tisScrolling.current = false;\r\n\t\t\t\t\thandleStart({ event: _event, dragStartCallback });\r\n\t\t\t\t}, 400);\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\tif (!isDragging.current || isPending.current) return; // 드래그 중이 아닐 경우 무시\r\n\r\n\t\t\tmoveingCallback({ x: clientX, y: clientY });\r\n\t\t},\r\n\t\t[isBlockingActiveInput],\r\n\t);\r\n\tconst handleEnd = useCallback(\r\n\t\t({\r\n\t\t\tevent: _event,\r\n\t\t\tdragEndCallback,\r\n\t\t}: {\r\n\t\t\tevent: React.MouseEvent | React.TouchEvent | Event;\r\n\t\t\tdragEndCallback: ({ x, y }: { x: number; y: number }) => void;\r\n\t\t}) => {\r\n\t\t\tisScrolling.current = false;\r\n\t\t\tisMouseDown.current = false;\r\n\t\t\tif (isPending.current) {\r\n\t\t\t\tisPending.current = false;\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t\tconst event = _event instanceof Event ? _event : _event.nativeEvent;\r\n\r\n\t\t\tif (!isDragging.current) return; // 드래그 중이 아닐 경우 무시\r\n\r\n\t\t\tisDragging.current = false; // 드래그 종료\r\n\r\n\t\t\tconst xy = getClientXy(event);\r\n\t\t\tif (!xy) return;\r\n\r\n\t\t\tconst { clientX, clientY } = xy;\r\n\r\n\t\t\tdragEndCallback({ x: clientX, y: clientY });\r\n\t\t\t// const href = hrefUrlRef.current;\r\n\r\n\t\t\t// if (clonedNodeRef.current) clonedNodeRef.current.remove();\r\n\t\t\t// //console.log(clientX, clientY);\r\n\t\t\t// if (\r\n\t\t\t// dropDocumentOutsideOption &&\r\n\t\t\t// isDocumentOut({ x: clientX, y: clientY })\r\n\t\t\t// ) {\r\n\t\t\t// if (\r\n\t\t\t// dropDocumentOutsideOption.isNewTap ||\r\n\t\t\t// (!dropDocumentOutsideOption.widthRatio &&\r\n\t\t\t// !dropDocumentOutsideOption.heightRatio)\r\n\t\t\t// ) {\r\n\t\t\t// window.open(href, '_blank');\r\n\t\t\t// } else {\r\n\t\t\t// const width =\r\n\t\t\t// window.innerWidth *\r\n\t\t\t// (dropDocumentOutsideOption.widthRatio || 1);\r\n\t\t\t// const height =\r\n\t\t\t// window.innerHeight *\r\n\t\t\t// (dropDocumentOutsideOption.heightRatio || 1);\r\n\t\t\t// window.open(\r\n\t\t\t// href,\r\n\t\t\t// '_blank',\r\n\t\t\t// `width=${width},height=${height},left=${window.screenLeft - clientX * -1 - width},top=${window.screenTop + clientY}`\r\n\t\t\t// );\r\n\t\t\t// }\r\n\t\t\t// }\r\n\r\n\t\t\t// dragState.next({\r\n\t\t\t// isDragging: false,\r\n\t\t\t// isDrop: true,\r\n\t\t\t// navigationTitle,\r\n\t\t\t// children: targetComponent,\r\n\t\t\t// x: clientX,\r\n\t\t\t// y: clientY,\r\n\t\t\t// containerName,\r\n\t\t\t// dropDocumentOutsideOption,\r\n\t\t\t// dropEndCallback,\r\n\t\t\t// screenKey,\r\n\t\t\t// customData,\r\n\t\t\t// });\r\n\t\t\t//if (dropEndCallback) dropEndCallback({ x: clientX, y: clientY });\r\n\t\t},\r\n\t\t[isBlockingActiveInput],\r\n\t);\r\n\r\n\treturn {\r\n\t\thandleStart,\r\n\t\thandleMove,\r\n\t\thandleEnd,\r\n\t};\r\n};\r\n\r\nexport type FolderEventType = {\r\n\ttype: \"new\" | \"sort\" | \"title\" | \"delete\" | \"insert\" | \"update\" | \"next\";\r\n\tisFolder: boolean;\r\n\ttitle: string;\r\n\tsort?: number;\r\n\tparentId?: string;\r\n\tid?: string;\r\n\tnewData?: any;\r\n};\r\n\r\nexport const folderEventSubject = new Subject<FolderEventType>();\r\n\r\nexport const setFolderEvent = (newValue: FolderEventType) => {\r\n\tfolderEventSubject.next(newValue);\r\n};\r\n\r\nexport const useFolderEvent = () => {\r\n\tconst [folderEvent, setFolderEvent] = useState<FolderEventType | null>(\r\n\t\tnull,\r\n\t);\r\n\tuseEffect(() => {\r\n\t\tconst subscription = folderEventSubject.subscribe((e) => {\r\n\t\t\tif (!e) return;\r\n\t\t\tsetFolderEvent(e);\r\n\t\t});\r\n\r\n\t\treturn () => {\r\n\t\t\tif (subscription) {\r\n\t\t\t\tsubscription.unsubscribe();\r\n\t\t\t}\r\n\t\t};\r\n\t}, []);\r\n\r\n\treturn { folderEvent };\r\n};\r\n","import {\r\n MutableRefObject,\r\n RefObject,\r\n useCallback,\r\n useEffect,\r\n useRef,\r\n useState,\r\n} from 'react';\r\n\r\nexport type OnReachTerminalType = {\r\n isFirst: boolean;\r\n isLast: boolean;\r\n observer: IntersectionObserver;\r\n};\r\ninterface UseListPagingForInfinityProps {\r\n onReachTerminal?: (onReachTerminalData: OnReachTerminalType) => void;\r\n}\r\nexport const useListPagingForSentinel = <E extends HTMLElement>({\r\n //initPageNumber,\r\n //initPageSize,\r\n onReachTerminal,\r\n}: UseListPagingForInfinityProps): {\r\n firstChildRef: (node: E | null) => void;\r\n lastChildRef: (node: E | null) => void;\r\n //pageNumber: number;\r\n //pageSize: number;\r\n //setPageNumber: React.Dispatch<React.SetStateAction<number>>;\r\n //setPageSize: React.Dispatch<React.SetStateAction<number>>;\r\n} => {\r\n const [firstChildNode, setFirstChildNode] = useState<E | null>(null);\r\n const [lastChildNode, setLastChildNode] = useState<E | null>(null);\r\n const observerRef = useRef<IntersectionObserver | null>(null);\r\n\r\n const firstChildRef = useCallback((node: E | null) => {\r\n setFirstChildNode(node);\r\n }, []);\r\n\r\n const lastChildRef = useCallback((node: E | null) => {\r\n setLastChildNode(node);\r\n }, []);\r\n // 페이지 번호가 변경될 때마다 데이터 로드를 위한 콜백 호출\r\n\r\n useEffect(() => {\r\n if (firstChildNode && observerRef.current)\r\n observerRef.current.unobserve(firstChildNode);\r\n if (lastChildNode && observerRef.current)\r\n observerRef.current.unobserve(lastChildNode);\r\n const handleIntersect: IntersectionObserverCallback = (\r\n entries,\r\n observer\r\n ) => {\r\n entries.forEach(entry => {\r\n if (entry.isIntersecting) {\r\n if (entry.target === firstChildNode) {\r\n if (onReachTerminal)\r\n onReachTerminal({\r\n isFirst: true,\r\n isLast: false,\r\n observer,\r\n });\r\n }\r\n\r\n if (entry.target === lastChildNode) {\r\n if (onReachTerminal)\r\n onReachTerminal({\r\n isFirst: false,\r\n isLast: true,\r\n observer,\r\n });\r\n }\r\n }\r\n });\r\n };\r\n\r\n const observer = new IntersectionObserver(handleIntersect, {\r\n threshold: 0.1,\r\n });\r\n\r\n observerRef.current = observer;\r\n\r\n if (firstChildNode) observer.observe(firstChildNode);\r\n if (lastChildNode) observer.observe(lastChildNode);\r\n\r\n return () => {\r\n if (observerRef.current) {\r\n // if (firstChildNode)\r\n // observerRef.current.unobserve(firstChildNode);\r\n // if (lastChildNode) observerRef.current.unobserve(lastChildNode);\r\n observerRef.current.disconnect();\r\n }\r\n };\r\n }, [firstChildNode, lastChildNode]);\r\n\r\n return {\r\n firstChildRef,\r\n lastChildRef,\r\n };\r\n};\r\n\r\nexport const usePaginationViewNumber = ({\r\n initPageNumber,\r\n}: {\r\n initPageNumber: number;\r\n}) => {\r\n const [showCurrentPageNumber, setShowCurrentPageNumber] =\r\n useState<number>(initPageNumber);\r\n\r\n const observerRef = useRef<IntersectionObserver | null>(null);\r\n const showCurrentPageObserveTarget = useCallback(\r\n (node: HTMLElement | null) => {\r\n if (!node) return;\r\n\r\n // 아직 observer가 없으면 새로 생성\r\n if (!observerRef.current) {\r\n observerRef.current = new IntersectionObserver(\r\n entries => {\r\n entries.forEach(entry => {\r\n if (entry.isIntersecting) {\r\n const pageIndexAttr =\r\n entry.target.getAttribute(\r\n 'data-page-index'\r\n );\r\n if (!pageIndexAttr) return;\r\n // if (!entry.target.hasAttribute('data-is-first'))\r\n // return;\r\n const pageIndex = parseInt(pageIndexAttr, 10);\r\n setShowCurrentPageNumber(pageIndex);\r\n }\r\n });\r\n },\r\n {\r\n threshold: 0.1, // 예: 10% 이상 보여야 intersect로 판단\r\n }\r\n );\r\n }\r\n\r\n // 해당 노드를 관찰\r\n observerRef.current.observe(node);\r\n },\r\n []\r\n );\r\n useEffect(() => {\r\n const currentObserver = observerRef.current;\r\n return () => {\r\n if (currentObserver) {\r\n currentObserver.disconnect();\r\n }\r\n };\r\n }, []);\r\n return {\r\n showCurrentPageNumber,\r\n showCurrentPageObserveTarget,\r\n };\r\n};\r\n\r\nexport const usePagingHandler = <T>({\r\n lastCallPageNumber,\r\n dataListRef,\r\n}: {\r\n lastCallPageNumber: number;\r\n dataListRef: MutableRefObject<Array<T[] | null>>;\r\n}) => {\r\n const jumpingPageNumberRef = useRef<number | null>(lastCallPageNumber);\r\n useEffect(() => {\r\n if (jumpingPageNumberRef.current) {\r\n setTimeout(() => {\r\n jumpingPageNumberRef.current = null;\r\n }, 1000);\r\n }\r\n }, [jumpingPageNumberRef]);\r\n const paginationScrollIntoViewTarget = useRef<\r\n Record<number, HTMLDivElement | null>\r\n >({});\r\n const pageNumberRef = useRef<number>(lastCallPageNumber);\r\n\r\n const setPaginationRef = useCallback(\r\n (i: number) => (node: HTMLDivElement | null) => {\r\n if (!node) return;\r\n\r\n paginationScrollIntoViewTarget.current[i] = node;\r\n\r\n // jumpingPageNumberRef에 값이 있고, 그 값이 현재 i와 같으면 스크롤\r\n if (\r\n jumpingPageNumberRef.current !== null &&\r\n i === jumpingPageNumberRef.current\r\n ) {\r\n node.scrollIntoView({\r\n behavior: 'instant', // 필요한 경우 'smooth' 등으로 수정 가능\r\n block: 'start',\r\n });\r\n jumpingPageNumberRef.current = null;\r\n }\r\n },\r\n []\r\n );\r\n\r\n //스크롤이 “첫 아이템” 혹은 “마지막 아이템”에 닿을 때 호출\r\n const handleReachTerminal = (\r\n { isFirst, isLast, observer }: OnReachTerminalType,\r\n dataCallFetch: (callPageNumber: number) => void\r\n ) => {\r\n // 이미 다른 페이지로 점프 중이면, 중복 호출 방지\r\n if (dataListRef.current.length === 0) return;\r\n if (jumpingPageNumberRef.current != null) return;\r\n if (!isFirst && !isLast) return;\r\n\r\n const callPageNumber = isFirst\r\n ? Math.max(pageNumberRef.current - 1, 0)\r\n : pageNumberRef.current + 1;\r\n\r\n if (\r\n dataListRef.current[callPageNumber] != null &&\r\n (dataListRef.current[callPageNumber]?.length || 0) > 0\r\n )\r\n return;\r\n jumpingPageNumberRef.current = callPageNumber;\r\n setTimeout(() => {\r\n jumpingPageNumberRef.current = null;\r\n }, 1000);\r\n dataCallFetch(callPageNumber);\r\n };\r\n\r\n //페이지네이션에서 페이지 번호를 직접 클릭했을 시\r\n const handleClickPageChange = (\r\n page: number,\r\n dataCallFetch: (callPageNumber: number) => void\r\n ) => {\r\n // PaginationLayer는 1-based, 내부 로직은 0-based\r\n // 이미 캐싱된 페이지가 있다면, 스크롤만 이동\r\n const pageData = dataListRef.current[page];\r\n\r\n // 이미 캐싱된 페이지가 있다면, 스크롤만 이동\r\n if (\r\n pageData != null &&\r\n Array.isArray(pageData) &&\r\n pageData.length > 0\r\n ) {\r\n paginationScrollIntoViewTarget.current[page]?.scrollIntoView({\r\n behavior: 'smooth',\r\n block: 'start',\r\n });\r\n return;\r\n }\r\n jumpingPageNumberRef.current = page;\r\n setTimeout(() => {\r\n jumpingPageNumberRef.current = null;\r\n }, 1000);\r\n dataCallFetch(page);\r\n };\r\n return {\r\n jumpingPageNumberRef,\r\n paginationScrollIntoViewTarget,\r\n pageNumberRef,\r\n setPaginationRef,\r\n handleReachTerminal,\r\n handleClickPageChange,\r\n };\r\n};\r\n"]}