funda-ui 4.7.701 → 4.7.723
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/DynamicFields/index.d.ts +1 -0
- package/DynamicFields/index.js +108 -8
- package/EventCalendarTimeline/index.css +12 -1
- package/EventCalendarTimeline/index.d.ts +2 -1
- package/EventCalendarTimeline/index.js +114 -13
- package/Utils/usePageVisibility.d.ts +5 -0
- package/Utils/usePageVisibility.js +166 -0
- package/Utils/useSSE.d.ts +41 -0
- package/Utils/useSSE.js +36 -12
- package/lib/cjs/DynamicFields/index.d.ts +1 -0
- package/lib/cjs/DynamicFields/index.js +108 -8
- package/lib/cjs/EventCalendarTimeline/index.d.ts +2 -1
- package/lib/cjs/EventCalendarTimeline/index.js +114 -13
- package/lib/cjs/Utils/usePageVisibility.d.ts +5 -0
- package/lib/cjs/Utils/usePageVisibility.js +166 -0
- package/lib/cjs/Utils/useSSE.d.ts +41 -0
- package/lib/cjs/Utils/useSSE.js +36 -12
- package/lib/css/EventCalendarTimeline/index.css +12 -1
- package/lib/esm/DynamicFields/index.tsx +107 -8
- package/lib/esm/EventCalendarTimeline/index.scss +15 -1
- package/lib/esm/EventCalendarTimeline/index.tsx +150 -23
- package/lib/esm/ModalDialog/index.tsx +0 -2
- package/lib/esm/Utils/hooks/usePageVisibility.tsx +84 -0
- package/lib/esm/Utils/hooks/useSSE.tsx +37 -12
- package/package.json +1 -1
|
@@ -1,3 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSE
|
|
3
|
+
*
|
|
4
|
+
* @usage:
|
|
5
|
+
*
|
|
6
|
+
const App = () => {
|
|
7
|
+
const { connected, messages, disconnect, reconnect } = useSSE('http://localhost:3000/sse');
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
<div>
|
|
11
|
+
<p>Status: {connected ? '✅ Connected' : '❌ Disconnected'}</p>
|
|
12
|
+
<button onClick={disconnect}>Disconnect</button>
|
|
13
|
+
<button onClick={reconnect}>Reconnect</button>
|
|
14
|
+
{messages.map((m, i) => <div key={i}>{m}</div>)}
|
|
15
|
+
</div>
|
|
16
|
+
);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
* It is recommended to use it in conjunction with usePageVisibility, because in HTTP mode,
|
|
20
|
+
* browsers allow a maximum of 6 connections; otherwise, other normal interfaces will be suspended and inaccessible.
|
|
21
|
+
|
|
22
|
+
import usePageVisibility from 'funda-ui/Utils/usePageVisibility';
|
|
23
|
+
|
|
24
|
+
const App = () => {
|
|
25
|
+
const { connected, messages, disconnect, reconnect } = useSSE('http://localhost:3000/sse');
|
|
26
|
+
|
|
27
|
+
// add new
|
|
28
|
+
usePageVisibility(
|
|
29
|
+
() => {
|
|
30
|
+
reconnect();
|
|
31
|
+
},
|
|
32
|
+
() => {
|
|
33
|
+
disconnect();
|
|
34
|
+
},
|
|
35
|
+
() => console.log("🎬 Page initialized while visible.")
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
return '';
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
*/
|
|
1
42
|
declare const useSSE: (url: string | null | undefined, retryDelay?: number) => {
|
|
2
43
|
readonly connected: boolean;
|
|
3
44
|
readonly messages: string[];
|
package/lib/cjs/Utils/useSSE.js
CHANGED
|
@@ -108,20 +108,44 @@ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
|
108
108
|
*
|
|
109
109
|
* @usage:
|
|
110
110
|
*
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
111
|
+
const App = () => {
|
|
112
|
+
const { connected, messages, disconnect, reconnect } = useSSE('http://localhost:3000/sse');
|
|
113
|
+
|
|
114
|
+
return (
|
|
115
|
+
<div>
|
|
116
|
+
<p>Status: {connected ? '✅ Connected' : '❌ Disconnected'}</p>
|
|
117
|
+
<button onClick={disconnect}>Disconnect</button>
|
|
118
|
+
<button onClick={reconnect}>Reconnect</button>
|
|
119
|
+
{messages.map((m, i) => <div key={i}>{m}</div>)}
|
|
120
|
+
</div>
|
|
121
|
+
);
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
* It is recommended to use it in conjunction with usePageVisibility, because in HTTP mode,
|
|
125
|
+
* browsers allow a maximum of 6 connections; otherwise, other normal interfaces will be suspended and inaccessible.
|
|
126
|
+
|
|
127
|
+
import usePageVisibility from 'funda-ui/Utils/usePageVisibility';
|
|
128
|
+
|
|
129
|
+
const App = () => {
|
|
130
|
+
const { connected, messages, disconnect, reconnect } = useSSE('http://localhost:3000/sse');
|
|
131
|
+
|
|
132
|
+
// add new
|
|
133
|
+
usePageVisibility(
|
|
134
|
+
() => {
|
|
135
|
+
reconnect();
|
|
136
|
+
},
|
|
137
|
+
() => {
|
|
138
|
+
disconnect();
|
|
139
|
+
},
|
|
140
|
+
() => console.log("🎬 Page initialized while visible.")
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
return '';
|
|
144
|
+
};
|
|
145
|
+
|
|
123
146
|
*/
|
|
124
147
|
|
|
148
|
+
|
|
125
149
|
var useSSE = function useSSE(url) {
|
|
126
150
|
var retryDelay = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 3000;
|
|
127
151
|
var _useState = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false),
|
|
@@ -235,7 +235,7 @@
|
|
|
235
235
|
}
|
|
236
236
|
*/
|
|
237
237
|
/* cell */
|
|
238
|
-
/* header */
|
|
238
|
+
/* The width of the left column header */
|
|
239
239
|
/* event container */
|
|
240
240
|
/* days container */
|
|
241
241
|
/* remove empty cells */
|
|
@@ -466,10 +466,21 @@
|
|
|
466
466
|
background-color: var(--custom-event-tl-table-divider-bg);
|
|
467
467
|
border-left: var(--custom-event-tl-table-divider-border);
|
|
468
468
|
border-right: var(--custom-event-tl-table-divider-border);
|
|
469
|
+
position: relative;
|
|
469
470
|
}
|
|
470
471
|
.custom-event-tl-table__timeline-table__wrapper .custom-event-tl-table__timeline-divider > div {
|
|
471
472
|
width: var(--custom-event-tl-table-divider-w);
|
|
472
473
|
}
|
|
474
|
+
.custom-event-tl-table__timeline-table__wrapper .custom-event-tl-table__timeline-divider--resizable {
|
|
475
|
+
cursor: col-resize;
|
|
476
|
+
user-select: none;
|
|
477
|
+
}
|
|
478
|
+
.custom-event-tl-table__timeline-table__wrapper .custom-event-tl-table__timeline-divider--resizable:hover {
|
|
479
|
+
background-color: rgba(0, 0, 0, 0.05);
|
|
480
|
+
}
|
|
481
|
+
.custom-event-tl-table__timeline-table__wrapper .custom-event-tl-table__timeline-divider--resizable:active {
|
|
482
|
+
background-color: rgba(0, 0, 0, 0.1);
|
|
483
|
+
}
|
|
473
484
|
.custom-event-tl-table__timeline-table__wrapper .custom-event-tl-table__cell-cushion {
|
|
474
485
|
padding: var(--custom-event-tl-table-cell-padding);
|
|
475
486
|
}
|
|
@@ -20,6 +20,7 @@ export type DynamicFieldsProps = {
|
|
|
20
20
|
label?: React.ReactNode | string;
|
|
21
21
|
data: DynamicFieldsValueProps | null;
|
|
22
22
|
maxFields?: any;
|
|
23
|
+
defaultRows?: number;
|
|
23
24
|
confirmText?: string;
|
|
24
25
|
doNotRemoveDom?: boolean;
|
|
25
26
|
iconAddBefore?: React.ReactNode | string;
|
|
@@ -55,6 +56,7 @@ const DynamicFields = (props: DynamicFieldsProps) => {
|
|
|
55
56
|
label,
|
|
56
57
|
data,
|
|
57
58
|
maxFields,
|
|
59
|
+
defaultRows,
|
|
58
60
|
iconAddBefore,
|
|
59
61
|
iconAddAfter,
|
|
60
62
|
iconAdd,
|
|
@@ -96,6 +98,10 @@ const DynamicFields = (props: DynamicFieldsProps) => {
|
|
|
96
98
|
const [tmpl, setTmpl] = useState<React.ReactNode>([]);
|
|
97
99
|
const addBtnIdRef = useRef<string>('');
|
|
98
100
|
addBtnIdRef.current = `dynamic-fields-add-${idRes}`;
|
|
101
|
+
const defaultRowsInitializedRef = useRef<boolean>(false);
|
|
102
|
+
const isInitializingDefaultRowsRef = useRef<boolean>(false);
|
|
103
|
+
const rafIdRef = useRef<number | null>(null);
|
|
104
|
+
const timeoutIdsRef = useRef<ReturnType<typeof setTimeout>[]>([]);
|
|
99
105
|
|
|
100
106
|
// exposes the following methods
|
|
101
107
|
useImperativeHandle(
|
|
@@ -169,17 +175,12 @@ const DynamicFields = (props: DynamicFieldsProps) => {
|
|
|
169
175
|
}
|
|
170
176
|
}
|
|
171
177
|
|
|
172
|
-
|
|
173
|
-
function handleClickAdd(event: any = null) {
|
|
174
|
-
if (event !== null) {
|
|
175
|
-
if (typeof event !== 'undefined') event.preventDefault();
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
+
function addRowWithTemplate(template: React.ReactNode) {
|
|
178
179
|
//button status
|
|
179
180
|
checkMaxStatus();
|
|
180
181
|
|
|
181
182
|
//
|
|
182
|
-
setVal((prevState: any[]) => [...prevState, ...generateGroup(
|
|
183
|
+
setVal((prevState: any[]) => [...prevState, ...generateGroup(template)]);
|
|
183
184
|
|
|
184
185
|
|
|
185
186
|
//
|
|
@@ -209,6 +210,14 @@ const DynamicFields = (props: DynamicFieldsProps) => {
|
|
|
209
210
|
}, 0);
|
|
210
211
|
}
|
|
211
212
|
|
|
213
|
+
function handleClickAdd(event: any = null) {
|
|
214
|
+
if (event !== null) {
|
|
215
|
+
if (typeof event !== 'undefined') event.preventDefault();
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
addRowWithTemplate(tmpl);
|
|
219
|
+
}
|
|
220
|
+
|
|
212
221
|
|
|
213
222
|
function handleClickRemove(e: React.MouseEvent) {
|
|
214
223
|
if (e !== null && typeof e !== 'undefined') e.preventDefault();
|
|
@@ -331,6 +340,96 @@ const DynamicFields = (props: DynamicFieldsProps) => {
|
|
|
331
340
|
|
|
332
341
|
//
|
|
333
342
|
onLoad?.(addBtnIdRef.current, rootRef.current, PER_ROW_DOM_STRING);
|
|
343
|
+
|
|
344
|
+
// Reset initialization flag when data becomes null
|
|
345
|
+
if (!data) {
|
|
346
|
+
defaultRowsInitializedRef.current = false;
|
|
347
|
+
isInitializingDefaultRowsRef.current = false;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Add default rows if specified (only initialize once per data setup)
|
|
351
|
+
if (!defaultRowsInitializedRef.current && typeof defaultRows === 'number' && defaultRows > 0 && data && data.tmpl) {
|
|
352
|
+
// Get the initial data length
|
|
353
|
+
const initDataLength = Array.isArray(data.init) ? data.init.length : 0;
|
|
354
|
+
|
|
355
|
+
// Calculate how many rows need to be added
|
|
356
|
+
const maxRows = typeof maxFields !== 'undefined' ? parseFloat(maxFields) : Infinity;
|
|
357
|
+
const targetRows = Math.min(defaultRows, maxRows);
|
|
358
|
+
const rowsToAdd = Math.max(0, targetRows - initDataLength);
|
|
359
|
+
|
|
360
|
+
// Mark as initialized immediately to prevent re-adding
|
|
361
|
+
defaultRowsInitializedRef.current = true;
|
|
362
|
+
|
|
363
|
+
// Only add rows if needed
|
|
364
|
+
if (rowsToAdd > 0) {
|
|
365
|
+
// Mark that we're initializing default rows
|
|
366
|
+
isInitializingDefaultRowsRef.current = true;
|
|
367
|
+
|
|
368
|
+
// Clear any existing timeouts
|
|
369
|
+
timeoutIdsRef.current.forEach(id => clearTimeout(id));
|
|
370
|
+
timeoutIdsRef.current = [];
|
|
371
|
+
|
|
372
|
+
// Use requestAnimationFrame to ensure DOM is ready and state is updated
|
|
373
|
+
rafIdRef.current = requestAnimationFrame(() => {
|
|
374
|
+
// Check if component is still mounted
|
|
375
|
+
if (!rootRef.current) {
|
|
376
|
+
isInitializingDefaultRowsRef.current = false;
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const timeoutId1 = setTimeout(() => {
|
|
381
|
+
// Check if component is still mounted
|
|
382
|
+
if (!rootRef.current) {
|
|
383
|
+
isInitializingDefaultRowsRef.current = false;
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Add rows one by one with a small delay to ensure state updates
|
|
388
|
+
let addedCount = 0;
|
|
389
|
+
const addNextRow = () => {
|
|
390
|
+
// Check if component is still mounted before each operation
|
|
391
|
+
if (addedCount < rowsToAdd && rootRef.current) {
|
|
392
|
+
// Use data.tmpl directly to avoid dependency on state
|
|
393
|
+
addRowWithTemplate(data.tmpl);
|
|
394
|
+
addedCount++;
|
|
395
|
+
// Wait a bit before adding the next row to ensure state updates
|
|
396
|
+
if (addedCount < rowsToAdd) {
|
|
397
|
+
const timeoutId2 = setTimeout(addNextRow, 10);
|
|
398
|
+
timeoutIdsRef.current.push(timeoutId2);
|
|
399
|
+
} else {
|
|
400
|
+
// All rows added, mark initialization as complete
|
|
401
|
+
isInitializingDefaultRowsRef.current = false;
|
|
402
|
+
}
|
|
403
|
+
} else {
|
|
404
|
+
// No more rows to add or component unmounted
|
|
405
|
+
isInitializingDefaultRowsRef.current = false;
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
addNextRow();
|
|
409
|
+
}, 50);
|
|
410
|
+
timeoutIdsRef.current.push(timeoutId1);
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Cleanup function to cancel requestAnimationFrame and timeouts
|
|
416
|
+
return () => {
|
|
417
|
+
// Don't cleanup if we're in the middle of initializing defaultRows
|
|
418
|
+
// This prevents the cleanup from canceling the async operations before they complete
|
|
419
|
+
if (isInitializingDefaultRowsRef.current) {
|
|
420
|
+
// Allow defaultRows initialization to complete
|
|
421
|
+
// The cleanup will happen naturally when initialization finishes
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// Normal cleanup for other cases
|
|
426
|
+
if (rafIdRef.current !== null) {
|
|
427
|
+
cancelAnimationFrame(rafIdRef.current);
|
|
428
|
+
rafIdRef.current = null;
|
|
429
|
+
}
|
|
430
|
+
timeoutIdsRef.current.forEach(id => clearTimeout(id));
|
|
431
|
+
timeoutIdsRef.current = [];
|
|
432
|
+
};
|
|
334
433
|
}, [data]);
|
|
335
434
|
|
|
336
435
|
|
|
@@ -406,4 +505,4 @@ const DynamicFields = (props: DynamicFieldsProps) => {
|
|
|
406
505
|
)
|
|
407
506
|
};
|
|
408
507
|
|
|
409
|
-
export default DynamicFields;
|
|
508
|
+
export default DynamicFields;
|
|
@@ -470,7 +470,7 @@
|
|
|
470
470
|
word-break: break-word;
|
|
471
471
|
}
|
|
472
472
|
|
|
473
|
-
/* header */
|
|
473
|
+
/* The width of the left column header */
|
|
474
474
|
.custom-event-tl-table__cell-cushion-headertitle {
|
|
475
475
|
width: var(--custom-event-tl-table-header-w);
|
|
476
476
|
}
|
|
@@ -614,10 +614,24 @@
|
|
|
614
614
|
background-color: var(--custom-event-tl-table-divider-bg);
|
|
615
615
|
border-left: var(--custom-event-tl-table-divider-border);
|
|
616
616
|
border-right: var(--custom-event-tl-table-divider-border);
|
|
617
|
+
position: relative;
|
|
617
618
|
|
|
618
619
|
> div {
|
|
619
620
|
width: var(--custom-event-tl-table-divider-w);
|
|
620
621
|
}
|
|
622
|
+
|
|
623
|
+
&--resizable {
|
|
624
|
+
cursor: col-resize;
|
|
625
|
+
user-select: none;
|
|
626
|
+
|
|
627
|
+
&:hover {
|
|
628
|
+
background-color: rgba(0, 0, 0, 0.05);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
&:active {
|
|
632
|
+
background-color: rgba(0, 0, 0, 0.1);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
621
635
|
}
|
|
622
636
|
|
|
623
637
|
/* cell */
|
|
@@ -71,7 +71,7 @@ export type EventCalendarTimelineProps = {
|
|
|
71
71
|
cellAddBtnLabel?: string | React.ReactNode;
|
|
72
72
|
forwardAndBackFillDisabled?: boolean;
|
|
73
73
|
draggable?: boolean;
|
|
74
|
-
|
|
74
|
+
headerShowWeek?: boolean;
|
|
75
75
|
autoScroll?: boolean;
|
|
76
76
|
onChangeDate?: (e: any, currentData: any) => void;
|
|
77
77
|
onChangeMonth?: (currentData: any) => void;
|
|
@@ -80,7 +80,7 @@ export type EventCalendarTimelineProps = {
|
|
|
80
80
|
onChangeWeek?: (startDate: string, endDate: string) => void;
|
|
81
81
|
onListRenderComplete?: () => void;
|
|
82
82
|
onChangeAppearanceMode?: (mode: string) => void;
|
|
83
|
-
|
|
83
|
+
enableHeaderResize?: boolean;
|
|
84
84
|
|
|
85
85
|
|
|
86
86
|
// modal dialog
|
|
@@ -161,7 +161,7 @@ const EventCalendarTimeline = (props: EventCalendarTimelineProps) => {
|
|
|
161
161
|
cellAddBtnLabel,
|
|
162
162
|
forwardAndBackFillDisabled,
|
|
163
163
|
draggable,
|
|
164
|
-
|
|
164
|
+
headerShowWeek,
|
|
165
165
|
autoScroll,
|
|
166
166
|
onChangeDate,
|
|
167
167
|
onChangeMonth,
|
|
@@ -170,6 +170,7 @@ const EventCalendarTimeline = (props: EventCalendarTimelineProps) => {
|
|
|
170
170
|
onChangeWeek,
|
|
171
171
|
onListRenderComplete,
|
|
172
172
|
onChangeAppearanceMode,
|
|
173
|
+
enableHeaderResize,
|
|
173
174
|
|
|
174
175
|
//
|
|
175
176
|
modalMaskOpacity,
|
|
@@ -272,13 +273,19 @@ const EventCalendarTimeline = (props: EventCalendarTimelineProps) => {
|
|
|
272
273
|
|
|
273
274
|
// table grid
|
|
274
275
|
const AUTO_SCROLL = autoScroll || false;
|
|
275
|
-
const
|
|
276
|
+
const HEADER_SHOW_WEEK = headerShowWeek || false;
|
|
276
277
|
const BODY_DRAG = draggable || false;
|
|
278
|
+
const ENABLE_HEADER_RESIZE = enableHeaderResize || false;
|
|
277
279
|
const tableGridRef = useRef<any>(null);
|
|
278
280
|
const tableGridHeaderRef = useRef<any>(null);
|
|
279
281
|
const scrollHeaderRef = useRef(null);
|
|
280
282
|
const scrollBodyRef = useRef(null);
|
|
281
283
|
const scrollListRef = useRef(null);
|
|
284
|
+
|
|
285
|
+
// header resize drag
|
|
286
|
+
const isResizingRef = useRef<boolean>(false);
|
|
287
|
+
const resizeStartXRef = useRef<number>(0);
|
|
288
|
+
const resizeStartWidthRef = useRef<number>(0);
|
|
282
289
|
|
|
283
290
|
// Temporary date
|
|
284
291
|
const [tempDate, setTempDate] = useState<string>('');
|
|
@@ -303,13 +310,13 @@ const EventCalendarTimeline = (props: EventCalendarTimelineProps) => {
|
|
|
303
310
|
// Calculate CELL_MIN_W based on appearanceMode and tableCellMinWidth
|
|
304
311
|
const CELL_MIN_W = useMemo(() => {
|
|
305
312
|
if (typeof tableCellMinWidth === 'undefined') {
|
|
306
|
-
return
|
|
313
|
+
return (headerShowWeek || false) ? 100 : 50;
|
|
307
314
|
}
|
|
308
315
|
if (typeof tableCellMinWidth === 'function') {
|
|
309
316
|
return tableCellMinWidth(appearanceMode as 'week' | 'month');
|
|
310
317
|
}
|
|
311
318
|
return tableCellMinWidth;
|
|
312
|
-
}, [tableCellMinWidth, appearanceMode]);
|
|
319
|
+
}, [tableCellMinWidth, appearanceMode, headerShowWeek]);
|
|
313
320
|
|
|
314
321
|
const findMondayAndTruncate = (dates: string[]) => {
|
|
315
322
|
const _res = dates.map((s: string) => new Date(s));
|
|
@@ -1255,7 +1262,7 @@ const EventCalendarTimeline = (props: EventCalendarTimelineProps) => {
|
|
|
1255
1262
|
|
|
1256
1263
|
// week day
|
|
1257
1264
|
const weekDay = item.week[i];
|
|
1258
|
-
const _weekDayStr =
|
|
1265
|
+
const _weekDayStr = HEADER_SHOW_WEEK ? <span dangerouslySetInnerHTML={{
|
|
1259
1266
|
__html: item.weekDisplay[i]
|
|
1260
1267
|
}} /> : null;
|
|
1261
1268
|
|
|
@@ -1355,7 +1362,7 @@ const EventCalendarTimeline = (props: EventCalendarTimelineProps) => {
|
|
|
1355
1362
|
'disabled': !isInteractive
|
|
1356
1363
|
}
|
|
1357
1364
|
)}
|
|
1358
|
-
style={{ width: (CELL_MIN_W - 1) + 'px' }}
|
|
1365
|
+
style={{ width: (CELL_MIN_W as number - 1) + 'px' }}
|
|
1359
1366
|
>
|
|
1360
1367
|
|
|
1361
1368
|
{d}
|
|
@@ -1633,7 +1640,7 @@ const EventCalendarTimeline = (props: EventCalendarTimelineProps) => {
|
|
|
1633
1640
|
'disabled': !isInteractive
|
|
1634
1641
|
}
|
|
1635
1642
|
)}
|
|
1636
|
-
style={{ width: (CELL_MIN_W - 1) + 'px' }}
|
|
1643
|
+
style={{ width: (CELL_MIN_W as number - 1) + 'px' }}
|
|
1637
1644
|
>
|
|
1638
1645
|
|
|
1639
1646
|
{/*++++++++++++++++ EVENT ++++++++++++++++*/}
|
|
@@ -1865,18 +1872,26 @@ const EventCalendarTimeline = (props: EventCalendarTimelineProps) => {
|
|
|
1865
1872
|
const tableGridEl: any = tableGridRef.current;
|
|
1866
1873
|
|
|
1867
1874
|
// initialize cell height
|
|
1868
|
-
const
|
|
1869
|
-
const
|
|
1875
|
+
const headerTitleTbody = tableGridEl.querySelector('.custom-event-tl-table__datagrid-body__title tbody');
|
|
1876
|
+
const contentTbody = tableGridEl.querySelector('.custom-event-tl-table__datagrid-body__content tbody');
|
|
1877
|
+
if (!headerTitleTbody || !contentTbody) return;
|
|
1878
|
+
|
|
1879
|
+
const headerTitleTrElements = headerTitleTbody.getElementsByTagName('tr');
|
|
1880
|
+
const trElements = contentTbody.getElementsByTagName('tr');
|
|
1870
1881
|
|
|
1882
|
+
// Reset any previously set inline heights so we measure natural heights.
|
|
1871
1883
|
for (let i = 0; i < headerTitleTrElements.length; i++) {
|
|
1884
|
+
// set to 'auto' (or remove inline style) to allow shrink
|
|
1885
|
+
headerTitleTrElements[i].style.height = 'auto';
|
|
1886
|
+
if (trElements[i]) trElements[i].style.height = 'auto';
|
|
1872
1887
|
|
|
1873
1888
|
const targetElement = headerTitleTrElements[i].offsetHeight > trElements[i].offsetHeight ? headerTitleTrElements[i] : trElements[i];
|
|
1874
1889
|
const tdOHeight = window.getComputedStyle(targetElement).height;
|
|
1875
1890
|
headerTitleTrElements[i].style.height = tdOHeight;
|
|
1876
1891
|
trElements[i].style.height = tdOHeight;
|
|
1877
|
-
|
|
1878
1892
|
}
|
|
1879
1893
|
|
|
1894
|
+
|
|
1880
1895
|
}
|
|
1881
1896
|
|
|
1882
1897
|
function tableGridInit() {
|
|
@@ -1886,7 +1901,7 @@ const EventCalendarTimeline = (props: EventCalendarTimelineProps) => {
|
|
|
1886
1901
|
|
|
1887
1902
|
const { forwardFillTotal, list: cellsList } = getCells();
|
|
1888
1903
|
const tableGridEl: any = tableGridRef.current;
|
|
1889
|
-
let _curCellMinWidth: number = CELL_MIN_W;
|
|
1904
|
+
let _curCellMinWidth: number = CELL_MIN_W as number;
|
|
1890
1905
|
let _curColCount: number = FILL_BLANK_DATE_DISABLD ? days[month] : 7 * cellsList.length;
|
|
1891
1906
|
|
|
1892
1907
|
|
|
@@ -2114,7 +2129,6 @@ const EventCalendarTimeline = (props: EventCalendarTimelineProps) => {
|
|
|
2114
2129
|
|
|
2115
2130
|
}
|
|
2116
2131
|
|
|
2117
|
-
|
|
2118
2132
|
function tableGridReset() {
|
|
2119
2133
|
if (tableGridRef.current === null) return;
|
|
2120
2134
|
|
|
@@ -2146,13 +2160,7 @@ const EventCalendarTimeline = (props: EventCalendarTimelineProps) => {
|
|
|
2146
2160
|
|
|
2147
2161
|
}
|
|
2148
2162
|
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
// ================================================================
|
|
2153
|
-
//
|
|
2154
|
-
// ================================================================
|
|
2155
|
-
//if user change the selectedYear, then udate the years array that is displayed on year tab view
|
|
2163
|
+
// if user change the selectedYear, then udate the years array that is displayed on year tab view
|
|
2156
2164
|
useEffect(() => {
|
|
2157
2165
|
const years: number[] = [];
|
|
2158
2166
|
for (let y = selectedYear - 10; y < selectedYear + 10; y++) {
|
|
@@ -2203,6 +2211,7 @@ const EventCalendarTimeline = (props: EventCalendarTimelineProps) => {
|
|
|
2203
2211
|
// Call a function when the list has been rendered completely
|
|
2204
2212
|
onListRenderComplete?.();
|
|
2205
2213
|
|
|
2214
|
+
|
|
2206
2215
|
}, [eventsValue, customTodayDate, appearanceMode]);
|
|
2207
2216
|
|
|
2208
2217
|
|
|
@@ -2222,6 +2231,102 @@ const EventCalendarTimeline = (props: EventCalendarTimelineProps) => {
|
|
|
2222
2231
|
}
|
|
2223
2232
|
|
|
2224
2233
|
}, []);
|
|
2234
|
+
|
|
2235
|
+
|
|
2236
|
+
// ================================================================
|
|
2237
|
+
// Handle header resize drag
|
|
2238
|
+
// ================================================================
|
|
2239
|
+
const handleHeaderResizeMove = useCallback((e: MouseEvent | TouchEvent) => {
|
|
2240
|
+
if (!isResizingRef.current || !tableGridRef.current) return;
|
|
2241
|
+
|
|
2242
|
+
const tableGridEl: any = tableGridRef.current;
|
|
2243
|
+
// Find the header title element that should have the CSS variable
|
|
2244
|
+
const headerTitleEl = tableGridEl.querySelector('.custom-event-tl-table__cell-cushion-headertitle') as HTMLElement;
|
|
2245
|
+
if (!headerTitleEl) return;
|
|
2246
|
+
|
|
2247
|
+
// Get clientX from either MouseEvent or TouchEvent
|
|
2248
|
+
const clientX = 'touches' in e ? e.touches[0]?.clientX : e.clientX;
|
|
2249
|
+
if (clientX === undefined) return;
|
|
2250
|
+
|
|
2251
|
+
const deltaX = clientX - resizeStartXRef.current;
|
|
2252
|
+
const newWidth = Math.max(100, resizeStartWidthRef.current + deltaX); // Minimum width 100px
|
|
2253
|
+
|
|
2254
|
+
// Update CSS variable on the header title element
|
|
2255
|
+
headerTitleEl.style.setProperty('--custom-event-tl-table-header-w', `${newWidth}px`);
|
|
2256
|
+
}, []);
|
|
2257
|
+
|
|
2258
|
+
const handleHeaderResizeEnd = useCallback(() => {
|
|
2259
|
+
if (!isResizingRef.current) return;
|
|
2260
|
+
|
|
2261
|
+
isResizingRef.current = false;
|
|
2262
|
+
document.removeEventListener('mousemove', handleHeaderResizeMove);
|
|
2263
|
+
document.removeEventListener('mouseup', handleHeaderResizeEnd);
|
|
2264
|
+
document.removeEventListener('touchmove', handleHeaderResizeMove);
|
|
2265
|
+
document.removeEventListener('touchend', handleHeaderResizeEnd);
|
|
2266
|
+
|
|
2267
|
+
// Recalculate table grid after resize
|
|
2268
|
+
if (tableGridRef.current) {
|
|
2269
|
+
tableGridInit();
|
|
2270
|
+
}
|
|
2271
|
+
}, [handleHeaderResizeMove]);
|
|
2272
|
+
|
|
2273
|
+
const handleHeaderResizeStart = useCallback((e: React.MouseEvent) => {
|
|
2274
|
+
if (!ENABLE_HEADER_RESIZE || !tableGridRef.current) return;
|
|
2275
|
+
|
|
2276
|
+
e.preventDefault();
|
|
2277
|
+
e.stopPropagation();
|
|
2278
|
+
|
|
2279
|
+
isResizingRef.current = true;
|
|
2280
|
+
resizeStartXRef.current = e.clientX;
|
|
2281
|
+
|
|
2282
|
+
const tableGridEl: any = tableGridRef.current;
|
|
2283
|
+
const headerTitleEl = tableGridEl.querySelector('.custom-event-tl-table__cell-cushion-headertitle') as HTMLElement;
|
|
2284
|
+
if (headerTitleEl) {
|
|
2285
|
+
resizeStartWidthRef.current = headerTitleEl.clientWidth;
|
|
2286
|
+
}
|
|
2287
|
+
|
|
2288
|
+
document.addEventListener('mousemove', handleHeaderResizeMove);
|
|
2289
|
+
document.addEventListener('mouseup', handleHeaderResizeEnd);
|
|
2290
|
+
}, [ENABLE_HEADER_RESIZE, handleHeaderResizeMove, handleHeaderResizeEnd]);
|
|
2291
|
+
|
|
2292
|
+
const handleHeaderResizeTouchStart = useCallback((e: React.TouchEvent) => {
|
|
2293
|
+
if (!ENABLE_HEADER_RESIZE || !tableGridRef.current) return;
|
|
2294
|
+
|
|
2295
|
+
e.preventDefault();
|
|
2296
|
+
e.stopPropagation();
|
|
2297
|
+
|
|
2298
|
+
isResizingRef.current = true;
|
|
2299
|
+
const touch = e.touches[0];
|
|
2300
|
+
if (touch) {
|
|
2301
|
+
resizeStartXRef.current = touch.clientX;
|
|
2302
|
+
}
|
|
2303
|
+
|
|
2304
|
+
const tableGridEl: any = tableGridRef.current;
|
|
2305
|
+
const headerTitleEl = tableGridEl.querySelector('.custom-event-tl-table__cell-cushion-headertitle') as HTMLElement;
|
|
2306
|
+
if (headerTitleEl) {
|
|
2307
|
+
resizeStartWidthRef.current = headerTitleEl.clientWidth;
|
|
2308
|
+
}
|
|
2309
|
+
|
|
2310
|
+
document.addEventListener('touchmove', handleHeaderResizeMove, { passive: false });
|
|
2311
|
+
document.addEventListener('touchend', handleHeaderResizeEnd);
|
|
2312
|
+
}, [ENABLE_HEADER_RESIZE, handleHeaderResizeMove, handleHeaderResizeEnd]);
|
|
2313
|
+
|
|
2314
|
+
|
|
2315
|
+
|
|
2316
|
+
// Cleanup on unmount
|
|
2317
|
+
useEffect(() => {
|
|
2318
|
+
return () => {
|
|
2319
|
+
document.removeEventListener('mousemove', handleHeaderResizeMove);
|
|
2320
|
+
document.removeEventListener('mouseup', handleHeaderResizeEnd);
|
|
2321
|
+
document.removeEventListener('touchmove', handleHeaderResizeMove);
|
|
2322
|
+
document.removeEventListener('touchend', handleHeaderResizeEnd);
|
|
2323
|
+
};
|
|
2324
|
+
}, [handleHeaderResizeMove, handleHeaderResizeEnd]);
|
|
2325
|
+
|
|
2326
|
+
|
|
2327
|
+
|
|
2328
|
+
|
|
2329
|
+
|
|
2225
2330
|
return (
|
|
2226
2331
|
<>
|
|
2227
2332
|
|
|
@@ -2409,6 +2514,9 @@ const EventCalendarTimeline = (props: EventCalendarTimelineProps) => {
|
|
|
2409
2514
|
<thead role="presentation">
|
|
2410
2515
|
<tr role="row">
|
|
2411
2516
|
<th role="columnheader">
|
|
2517
|
+
{/**
|
|
2518
|
+
* The width of the left column header is set using ".custom-event-tl-table__cell-cushion-headertitle".
|
|
2519
|
+
*/}
|
|
2412
2520
|
<div className="custom-event-tl-table__cell-cushion custom-event-tl-table__cell-cushion-headertitle">
|
|
2413
2521
|
{tableListSectionTitle || ''}
|
|
2414
2522
|
</div>
|
|
@@ -2423,7 +2531,18 @@ const EventCalendarTimeline = (props: EventCalendarTimelineProps) => {
|
|
|
2423
2531
|
|
|
2424
2532
|
|
|
2425
2533
|
</th>
|
|
2426
|
-
<th
|
|
2534
|
+
<th
|
|
2535
|
+
role="presentation"
|
|
2536
|
+
className={combinedCls(
|
|
2537
|
+
'custom-event-tl-table__timeline-divider',
|
|
2538
|
+
ENABLE_HEADER_RESIZE ? 'custom-event-tl-table__timeline-divider--resizable' : ''
|
|
2539
|
+
)}
|
|
2540
|
+
onMouseDown={ENABLE_HEADER_RESIZE ? handleHeaderResizeStart : undefined}
|
|
2541
|
+
onTouchStart={ENABLE_HEADER_RESIZE ? handleHeaderResizeTouchStart : undefined}
|
|
2542
|
+
style={ENABLE_HEADER_RESIZE ? { cursor: 'col-resize', userSelect: 'none' } : undefined}
|
|
2543
|
+
>
|
|
2544
|
+
<div></div>
|
|
2545
|
+
</th>
|
|
2427
2546
|
<th role="presentation">
|
|
2428
2547
|
<div
|
|
2429
2548
|
ref={scrollHeaderRef}
|
|
@@ -2499,12 +2618,20 @@ const EventCalendarTimeline = (props: EventCalendarTimelineProps) => {
|
|
|
2499
2618
|
|
|
2500
2619
|
|
|
2501
2620
|
</td>
|
|
2621
|
+
|
|
2622
|
+
{/**
|
|
2623
|
+
* The dividing line between the left title column and the right content
|
|
2624
|
+
*/}
|
|
2502
2625
|
<td
|
|
2503
2626
|
role="presentation"
|
|
2504
2627
|
className={combinedCls(
|
|
2505
2628
|
'custom-event-tl-table__timeline-divider',
|
|
2506
|
-
tableListDividerClassName
|
|
2629
|
+
tableListDividerClassName,
|
|
2630
|
+
ENABLE_HEADER_RESIZE ? 'custom-event-tl-table__timeline-divider--resizable' : ''
|
|
2507
2631
|
)}
|
|
2632
|
+
onMouseDown={ENABLE_HEADER_RESIZE ? handleHeaderResizeStart : undefined}
|
|
2633
|
+
onTouchStart={ENABLE_HEADER_RESIZE ? handleHeaderResizeTouchStart : undefined}
|
|
2634
|
+
style={ENABLE_HEADER_RESIZE ? { cursor: 'col-resize', userSelect: 'none' } : undefined}
|
|
2508
2635
|
>
|
|
2509
2636
|
<div></div>
|
|
2510
2637
|
</td>
|