@windrun-huaiin/third-ui 29.2.0 → 30.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/fuma/mdx/cheet-table.d.ts +13 -0
- package/dist/fuma/mdx/cheet-table.js +295 -0
- package/dist/fuma/mdx/cheet-table.mjs +293 -0
- package/dist/fuma/mdx/index.d.ts +1 -0
- package/dist/fuma/mdx/index.js +2 -0
- package/dist/fuma/mdx/index.mjs +1 -0
- package/dist/fuma/server/features/widgets.js +2 -0
- package/dist/fuma/server/features/widgets.mjs +2 -0
- package/dist/lib/fuma-schema-check-util.d.ts +1 -1
- package/dist/main/alert-dialog/confirm-dialog.d.ts +2 -1
- package/dist/main/alert-dialog/confirm-dialog.js +3 -3
- package/dist/main/alert-dialog/confirm-dialog.mjs +4 -4
- package/dist/main/alert-dialog/dialog-loading-action.d.ts +2 -1
- package/dist/main/alert-dialog/dialog-loading-action.js +6 -3
- package/dist/main/alert-dialog/dialog-loading-action.mjs +6 -3
- package/dist/main/alert-dialog/dialog-styles.d.ts +4 -2
- package/dist/main/alert-dialog/dialog-styles.js +8 -4
- package/dist/main/alert-dialog/dialog-styles.mjs +7 -5
- package/dist/main/alert-dialog/high-priority-confirm-dialog.d.ts +2 -1
- package/dist/main/alert-dialog/high-priority-confirm-dialog.js +7 -7
- package/dist/main/alert-dialog/high-priority-confirm-dialog.mjs +8 -8
- package/dist/main/alert-dialog/info-dialog.d.ts +2 -1
- package/dist/main/alert-dialog/info-dialog.js +3 -3
- package/dist/main/alert-dialog/info-dialog.mjs +4 -4
- package/dist/main/alert-dialog/undoable-confirm-dialog.d.ts +2 -1
- package/dist/main/alert-dialog/undoable-confirm-dialog.js +4 -4
- package/dist/main/alert-dialog/undoable-confirm-dialog.mjs +5 -5
- package/dist/main/anime/anime-beam-frame.d.ts +3 -0
- package/dist/main/anime/anime-beam-frame.js +63 -0
- package/dist/main/anime/anime-beam-frame.mjs +61 -0
- package/dist/main/anime/anime-spiral-loading.d.ts +10 -0
- package/dist/main/anime/anime-spiral-loading.js +77 -0
- package/dist/main/anime/anime-spiral-loading.mjs +75 -0
- package/dist/main/anime/index.d.ts +2 -0
- package/dist/main/anime/index.js +10 -0
- package/dist/main/anime/index.mjs +3 -0
- package/dist/main/beam-frame/animate.d.ts +3 -0
- package/dist/main/beam-frame/animate.js +63 -0
- package/dist/main/beam-frame/animate.mjs +61 -0
- package/dist/main/beam-frame/beam-frame.d.ts +4 -0
- package/dist/main/beam-frame/beam-frame.js +262 -0
- package/dist/main/beam-frame/beam-frame.mjs +258 -0
- package/dist/main/beam-frame/index.d.ts +4 -0
- package/dist/main/beam-frame/index.js +11 -0
- package/dist/main/beam-frame/index.mjs +3 -0
- package/dist/main/beam-frame/motion.d.ts +3 -0
- package/dist/main/beam-frame/motion.js +61 -0
- package/dist/main/beam-frame/motion.mjs +59 -0
- package/dist/main/beam-frame/share-config.d.ts +54 -0
- package/dist/main/beam-frame/share-config.js +161 -0
- package/dist/main/beam-frame/share-config.mjs +152 -0
- package/dist/main/beam-frame-config.d.ts +54 -0
- package/dist/main/beam-frame-config.js +161 -0
- package/dist/main/beam-frame-config.mjs +152 -0
- package/dist/main/calendar/random-date-range-dialog.d.ts +5 -2
- package/dist/main/calendar/random-date-range-dialog.js +239 -109
- package/dist/main/calendar/random-date-range-dialog.mjs +242 -112
- package/dist/main/cta.js +17 -1
- package/dist/main/cta.mjs +18 -2
- package/dist/main/delayed-img.d.ts +1 -1
- package/dist/main/delayed-img.js +8 -5
- package/dist/main/delayed-img.mjs +8 -5
- package/dist/main/info-tooltip.js +70 -9
- package/dist/main/info-tooltip.mjs +70 -9
- package/dist/main/loading-frame/index.d.ts +1 -0
- package/dist/main/loading.d.ts +2 -1
- package/dist/main/loading.js +64 -26
- package/dist/main/loading.mjs +64 -26
- package/dist/main/motion/index.d.ts +1 -0
- package/dist/main/motion/index.js +9 -0
- package/dist/main/motion/index.mjs +2 -0
- package/dist/main/motion/motion-beam-frame.d.ts +3 -0
- package/dist/main/motion/motion-beam-frame.js +61 -0
- package/dist/main/motion/motion-beam-frame.mjs +59 -0
- package/dist/main/snake-loading-frame.d.ts +7 -3
- package/dist/main/snake-loading-frame.js +44 -252
- package/dist/main/snake-loading-frame.mjs +46 -254
- package/package.json +16 -5
- package/src/fuma/mdx/cheet-table.tsx +650 -0
- package/src/fuma/mdx/index.ts +1 -0
- package/src/fuma/server/features/widgets.tsx +2 -0
- package/src/main/alert-dialog/confirm-dialog.tsx +5 -2
- package/src/main/alert-dialog/dialog-loading-action.tsx +22 -5
- package/src/main/alert-dialog/dialog-styles.ts +13 -3
- package/src/main/alert-dialog/high-priority-confirm-dialog.tsx +29 -24
- package/src/main/alert-dialog/info-dialog.tsx +5 -2
- package/src/main/alert-dialog/undoable-confirm-dialog.tsx +21 -18
- package/src/main/anime/anime-beam-frame.tsx +128 -0
- package/src/main/anime/anime-spiral-loading.tsx +123 -0
- package/src/main/anime/index.ts +9 -0
- package/src/main/beam-frame-config.tsx +341 -0
- package/src/main/calendar/random-date-range-dialog.tsx +242 -74
- package/src/main/cta.tsx +50 -21
- package/src/main/delayed-img.tsx +9 -4
- package/src/main/info-tooltip.tsx +116 -20
- package/src/main/loading-frame/index.ts +4 -0
- package/src/main/loading.tsx +75 -24
- package/src/main/motion/index.ts +8 -0
- package/src/main/motion/motion-beam-frame.tsx +137 -0
- package/src/main/snake-loading-frame.tsx +95 -496
- package/src/styles/cta.css +21 -4
- package/src/styles/third-ui.css +0 -20
|
@@ -10,7 +10,10 @@ import {
|
|
|
10
10
|
ChevronsRightIcon,
|
|
11
11
|
XIcon,
|
|
12
12
|
} from '@windrun-huaiin/base-ui/icons';
|
|
13
|
+
import { themeSvgIconColor } from '@windrun-huaiin/base-ui/lib';
|
|
13
14
|
import { cn } from '@windrun-huaiin/lib/utils';
|
|
15
|
+
import { DialogLoadingAction, DialogActionHandler, useDialogLoadingAction } from '../alert-dialog/dialog-loading-action';
|
|
16
|
+
import { XButton } from '../buttons/x-button';
|
|
14
17
|
import { usePressFeedback } from '../buttons/use-press-feedback';
|
|
15
18
|
|
|
16
19
|
export type RandomCalendarRange = {
|
|
@@ -24,7 +27,9 @@ type RandomDateRangeDialogProps = {
|
|
|
24
27
|
anchorDate: string;
|
|
25
28
|
defaultRangeDays?: number;
|
|
26
29
|
onOpenChange: (open: boolean) => void;
|
|
27
|
-
|
|
30
|
+
loadingActions?: readonly DialogLoadingAction[];
|
|
31
|
+
loadingFullPage?: boolean;
|
|
32
|
+
onApply: (range: RandomCalendarRange) => void | Promise<void>;
|
|
28
33
|
onClear?: (range: RandomCalendarRange) => void;
|
|
29
34
|
};
|
|
30
35
|
|
|
@@ -32,11 +37,11 @@ type QuickRangeDays = 7 | 10 | 15 | 30;
|
|
|
32
37
|
type DialogNavButtonKey = 'prevYear' | 'prevMonth' | 'nextMonth' | 'nextYear';
|
|
33
38
|
|
|
34
39
|
const DEFAULT_RANGE_DAYS = 7;
|
|
35
|
-
const MAX_RANGE_DAYS =
|
|
36
|
-
const
|
|
37
|
-
const
|
|
40
|
+
const MAX_RANGE_DAYS = 31;
|
|
41
|
+
const VISIBLE_TRACK_DAYS = 36;
|
|
42
|
+
const EDGE_OVERFLOW_PIXELS_PER_DAY = 24;
|
|
38
43
|
const DIALOG_ICON_BUTTON_CLASS_NAME =
|
|
39
|
-
'inline-flex h-8 w-8 items-center justify-center rounded-full
|
|
44
|
+
'inline-flex h-8 w-8 items-center justify-center rounded-full bg-white text-slate-500 transition duration-150 hover:bg-black/5 hover:text-slate-900 dark:bg-slate-950 dark:text-slate-300 dark:hover:bg-white/5 dark:hover:text-white';
|
|
40
45
|
const DIALOG_NAV_BUTTON_CLASS_NAME =
|
|
41
46
|
'inline-flex h-8 w-8 items-center justify-center rounded-full transition-[transform,background-color,color,box-shadow,border-color] duration-150 ease-out';
|
|
42
47
|
const DIALOG_NAV_BUTTON_REST_CLASS_NAME =
|
|
@@ -95,13 +100,38 @@ function clampWindowDays(days: number): number {
|
|
|
95
100
|
|
|
96
101
|
function buildTrackRange(referenceDate: string, windowDays = DEFAULT_RANGE_DAYS): RandomCalendarRange {
|
|
97
102
|
const resolvedWindowDays = clampWindowDays(windowDays);
|
|
98
|
-
const
|
|
99
|
-
const daysBefore = Math.floor((resolvedTotalDays - resolvedWindowDays) / 3);
|
|
103
|
+
const daysBefore = Math.floor((VISIBLE_TRACK_DAYS - resolvedWindowDays) / 3);
|
|
100
104
|
const startDate = addDays(referenceDate, -daysBefore);
|
|
101
|
-
const endDate = addDays(startDate,
|
|
105
|
+
const endDate = addDays(startDate, VISIBLE_TRACK_DAYS - 1);
|
|
102
106
|
return { startDate, endDate };
|
|
103
107
|
}
|
|
104
108
|
|
|
109
|
+
function ensureRangeVisibleOnTrack(range: RandomCalendarRange, bounds: RandomCalendarRange): RandomCalendarRange {
|
|
110
|
+
if (!range.startDate || !range.endDate || !bounds.startDate || !bounds.endDate) {
|
|
111
|
+
return bounds;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
let nextStartDate = bounds.startDate;
|
|
115
|
+
|
|
116
|
+
if (compareDateStrings(range.startDate, nextStartDate) < 0) {
|
|
117
|
+
nextStartDate = range.startDate;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const nextEndDate = addDays(nextStartDate, VISIBLE_TRACK_DAYS - 1);
|
|
121
|
+
if (compareDateStrings(range.endDate, nextEndDate) > 0) {
|
|
122
|
+
nextStartDate = addDays(range.endDate, -(VISIBLE_TRACK_DAYS - 1));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (nextStartDate === bounds.startDate && addDays(nextStartDate, VISIBLE_TRACK_DAYS - 1) === bounds.endDate) {
|
|
126
|
+
return bounds;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
startDate: nextStartDate,
|
|
131
|
+
endDate: addDays(nextStartDate, VISIBLE_TRACK_DAYS - 1),
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
105
135
|
function clampDateToRange(date: string, bounds: RandomCalendarRange): string {
|
|
106
136
|
if (!bounds.startDate || !bounds.endDate) {
|
|
107
137
|
return date;
|
|
@@ -130,6 +160,23 @@ function getDateByRatio(bounds: RandomCalendarRange, ratio: number): string {
|
|
|
130
160
|
}
|
|
131
161
|
|
|
132
162
|
const totalDays = Math.max(1, getDaysBetween(bounds.startDate, bounds.endDate));
|
|
163
|
+
return addDays(bounds.startDate, Math.round(totalDays * Math.max(0, Math.min(1, ratio))));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function getDateByOverflowRatio(bounds: RandomCalendarRange, ratio: number, trackWidth: number): string {
|
|
167
|
+
if (!bounds.startDate || !bounds.endDate) {
|
|
168
|
+
return getTodayString();
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const totalDays = Math.max(1, getDaysBetween(bounds.startDate, bounds.endDate));
|
|
172
|
+
if (ratio < 0) {
|
|
173
|
+
return addDays(bounds.startDate, Math.floor((ratio * trackWidth) / EDGE_OVERFLOW_PIXELS_PER_DAY));
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (ratio > 1) {
|
|
177
|
+
return addDays(bounds.endDate, Math.ceil(((ratio - 1) * trackWidth) / EDGE_OVERFLOW_PIXELS_PER_DAY));
|
|
178
|
+
}
|
|
179
|
+
|
|
133
180
|
return addDays(bounds.startDate, Math.round(totalDays * ratio));
|
|
134
181
|
}
|
|
135
182
|
|
|
@@ -174,12 +221,47 @@ function getMonthEnd(value: string): string {
|
|
|
174
221
|
return formatDateString(new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + 1, 0)));
|
|
175
222
|
}
|
|
176
223
|
|
|
224
|
+
function RollingMonthLabel({ value }: { value: string }) {
|
|
225
|
+
const [displayValue, setDisplayValue] = useState(value);
|
|
226
|
+
const [previousValue, setPreviousValue] = useState<string | null>(null);
|
|
227
|
+
|
|
228
|
+
useEffect(() => {
|
|
229
|
+
if (value === displayValue) {
|
|
230
|
+
return undefined;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
setPreviousValue(displayValue);
|
|
234
|
+
setDisplayValue(value);
|
|
235
|
+
|
|
236
|
+
const timeout = window.setTimeout(() => {
|
|
237
|
+
setPreviousValue(null);
|
|
238
|
+
}, 180);
|
|
239
|
+
|
|
240
|
+
return () => window.clearTimeout(timeout);
|
|
241
|
+
}, [displayValue, value]);
|
|
242
|
+
|
|
243
|
+
return (
|
|
244
|
+
<span className="relative inline-block h-5 min-w-10 overflow-hidden align-bottom">
|
|
245
|
+
{previousValue ? (
|
|
246
|
+
<span className="rd-date-range-month-out absolute inset-x-0 top-0 text-center">
|
|
247
|
+
{previousValue}
|
|
248
|
+
</span>
|
|
249
|
+
) : null}
|
|
250
|
+
<span className={cn('absolute inset-x-0 top-0 text-center', previousValue && 'rd-date-range-month-in')}>
|
|
251
|
+
{displayValue}
|
|
252
|
+
</span>
|
|
253
|
+
</span>
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
|
|
177
257
|
export function RandomDateRangeDialog({
|
|
178
258
|
open,
|
|
179
259
|
value,
|
|
180
260
|
anchorDate,
|
|
181
261
|
defaultRangeDays = DEFAULT_RANGE_DAYS,
|
|
182
262
|
onOpenChange,
|
|
263
|
+
loadingActions,
|
|
264
|
+
loadingFullPage,
|
|
183
265
|
onApply,
|
|
184
266
|
onClear,
|
|
185
267
|
}: RandomDateRangeDialogProps) {
|
|
@@ -204,12 +286,15 @@ export function RandomDateRangeDialog({
|
|
|
204
286
|
const resultLabelRef = useRef<HTMLDivElement | null>(null);
|
|
205
287
|
const selectionDaysRef = useRef<HTMLDivElement | null>(null);
|
|
206
288
|
const dragPreviewRef = useRef<RandomCalendarRange | null>(null);
|
|
289
|
+
const trackBoundsRef = useRef<RandomCalendarRange>(trackBounds);
|
|
290
|
+
const dragStartTrackBoundsRef = useRef<RandomCalendarRange | null>(null);
|
|
207
291
|
const frameRef = useRef<number | null>(null);
|
|
208
292
|
const pendingClientXRef = useRef<number | null>(null);
|
|
209
293
|
const syncPreviewDomRef = useRef<(range: RandomCalendarRange) => void>(() => {});
|
|
210
294
|
const buildDraggedRangeRef = useRef<(clientX: number) => RandomCalendarRange | null>(() => null);
|
|
211
295
|
const previousBodyOverflowRef = useRef<string | null>(null);
|
|
212
296
|
const today = useMemo(() => getTodayString(), []);
|
|
297
|
+
const { dialogLoading, runDialogAction } = useDialogLoadingAction({ loadingActions, loadingFullPage, onOpenChange });
|
|
213
298
|
const baseReferenceDate = anchorDate || today;
|
|
214
299
|
const previousOpenRef = useRef(false);
|
|
215
300
|
const startRatio = getRatioByDate(draftRange.startDate ?? baseReferenceDate, trackBounds);
|
|
@@ -220,24 +305,30 @@ export function RandomDateRangeDialog({
|
|
|
220
305
|
const isSingleDay = (draftRange.startDate ?? null) === (draftRange.endDate ?? null);
|
|
221
306
|
const startHandlePercent = isSingleDay ? Math.max(leftPercent - 0.8, 0) : leftPercent;
|
|
222
307
|
const endHandlePercent = isSingleDay ? Math.min(rightPercent + 0.8, 100) : rightPercent;
|
|
223
|
-
const trackTickCount =
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
.filter((item): item is string => Boolean(item))
|
|
227
|
-
.map((item) => formatMonthShort(item));
|
|
308
|
+
const trackTickCount = VISIBLE_TRACK_DAYS;
|
|
309
|
+
const leftMonthLabel = formatMonthShort(trackBounds.startDate ?? baseReferenceDate);
|
|
310
|
+
const rightMonthLabel = formatMonthShort(trackBounds.endDate ?? baseReferenceDate);
|
|
228
311
|
|
|
229
|
-
|
|
230
|
-
|
|
312
|
+
const handleApply = useCallback<DialogActionHandler>(() => {
|
|
313
|
+
return onApply(draftRange);
|
|
314
|
+
}, [draftRange, onApply]);
|
|
315
|
+
|
|
316
|
+
function commitTrackBounds(nextTrackBounds: RandomCalendarRange) {
|
|
317
|
+
trackBoundsRef.current = nextTrackBounds;
|
|
318
|
+
setTrackBounds(nextTrackBounds);
|
|
319
|
+
}
|
|
231
320
|
|
|
232
321
|
useEffect(() => {
|
|
233
322
|
if (open && !previousOpenRef.current) {
|
|
323
|
+
const nextTrackBounds = buildTrackRange(baseReferenceDate, resolvedDefaultRangeDays);
|
|
234
324
|
const nextRange = {
|
|
235
325
|
startDate: baseReferenceDate,
|
|
236
326
|
endDate: addDays(baseReferenceDate, resolvedDefaultRangeDays - 1),
|
|
237
327
|
};
|
|
238
328
|
setDraftRange(nextRange);
|
|
239
329
|
setReferenceDate(baseReferenceDate);
|
|
240
|
-
|
|
330
|
+
trackBoundsRef.current = nextTrackBounds;
|
|
331
|
+
setTrackBounds(nextTrackBounds);
|
|
241
332
|
setWindowDays(resolvedDefaultRangeDays);
|
|
242
333
|
dragStartRangeRef.current = null;
|
|
243
334
|
dragModeRef.current = null;
|
|
@@ -256,16 +347,19 @@ export function RandomDateRangeDialog({
|
|
|
256
347
|
setReferenceDate(nextReferenceDate);
|
|
257
348
|
setWindowDays(clampedWindowDays);
|
|
258
349
|
setDraftRange(nextRange);
|
|
259
|
-
if (
|
|
260
|
-
|
|
350
|
+
if (options?.preserveTrack) {
|
|
351
|
+
commitTrackBounds(ensureRangeVisibleOnTrack(nextRange, trackBoundsRef.current));
|
|
352
|
+
} else {
|
|
353
|
+
commitTrackBounds(buildTrackRange(nextReferenceDate, clampedWindowDays));
|
|
261
354
|
}
|
|
262
355
|
}
|
|
263
356
|
|
|
264
357
|
const getPreviewPercents = useCallback((range: RandomCalendarRange) => {
|
|
265
358
|
const start = range.startDate ?? baseReferenceDate;
|
|
266
359
|
const end = range.endDate ?? start;
|
|
267
|
-
const
|
|
268
|
-
const
|
|
360
|
+
const currentTrackBounds = trackBoundsRef.current;
|
|
361
|
+
const startR = getRatioByDate(start, currentTrackBounds);
|
|
362
|
+
const endR = getRatioByDate(end, currentTrackBounds);
|
|
269
363
|
const left = Math.min(startR, endR) * 100;
|
|
270
364
|
const right = Math.max(startR, endR) * 100;
|
|
271
365
|
const width = Math.max(right - left, 0.5);
|
|
@@ -278,7 +372,7 @@ export function RandomDateRangeDialog({
|
|
|
278
372
|
startHandle: single ? Math.max(left - 0.8, 0) : left,
|
|
279
373
|
endHandle: single ? Math.min(right + 0.8, 100) : right,
|
|
280
374
|
};
|
|
281
|
-
}, [baseReferenceDate
|
|
375
|
+
}, [baseReferenceDate]);
|
|
282
376
|
|
|
283
377
|
const syncPreviewDom = useCallback((range: RandomCalendarRange) => {
|
|
284
378
|
const percents = getPreviewPercents(range);
|
|
@@ -305,19 +399,23 @@ export function RandomDateRangeDialog({
|
|
|
305
399
|
syncPreviewDom(draftRange);
|
|
306
400
|
}, [draftRange, syncPreviewDom]);
|
|
307
401
|
|
|
402
|
+
useEffect(() => {
|
|
403
|
+
trackBoundsRef.current = trackBounds;
|
|
404
|
+
}, [trackBounds]);
|
|
405
|
+
|
|
308
406
|
function resetReferenceFromClientX(clientX: number) {
|
|
309
407
|
if (!trackRef.current) {
|
|
310
408
|
return;
|
|
311
409
|
}
|
|
312
410
|
|
|
313
411
|
const rect = trackRef.current.getBoundingClientRect();
|
|
314
|
-
const ratio =
|
|
412
|
+
const ratio = (clientX - rect.left) / rect.width;
|
|
315
413
|
const nextReferenceDate = getDateByRatio(trackBounds, ratio);
|
|
316
414
|
updateRangeByReference(nextReferenceDate, resolvedDefaultRangeDays, { preserveTrack: true });
|
|
317
415
|
}
|
|
318
416
|
|
|
319
417
|
function applyQuickRange(dayCount: QuickRangeDays) {
|
|
320
|
-
updateRangeByReference(referenceDate, dayCount);
|
|
418
|
+
updateRangeByReference(referenceDate, dayCount, { preserveTrack: true });
|
|
321
419
|
}
|
|
322
420
|
|
|
323
421
|
function shiftReferenceDateByMonths(monthOffset: number) {
|
|
@@ -334,6 +432,7 @@ export function RandomDateRangeDialog({
|
|
|
334
432
|
pointerIdRef.current = pointerId;
|
|
335
433
|
dragStartRangeRef.current = { ...draftRange };
|
|
336
434
|
dragPreviewRef.current = { ...draftRange };
|
|
435
|
+
dragStartTrackBoundsRef.current = { ...trackBoundsRef.current };
|
|
337
436
|
|
|
338
437
|
if (
|
|
339
438
|
mode === 'window' &&
|
|
@@ -341,12 +440,12 @@ export function RandomDateRangeDialog({
|
|
|
341
440
|
trackRef.current &&
|
|
342
441
|
draftRange.startDate &&
|
|
343
442
|
draftRange.endDate &&
|
|
344
|
-
|
|
345
|
-
|
|
443
|
+
dragStartTrackBoundsRef.current.startDate &&
|
|
444
|
+
dragStartTrackBoundsRef.current.endDate
|
|
346
445
|
) {
|
|
347
446
|
const rect = trackRef.current.getBoundingClientRect();
|
|
348
|
-
const ratio =
|
|
349
|
-
const pointerDate = getDateByRatio(
|
|
447
|
+
const ratio = (clientX - rect.left) / rect.width;
|
|
448
|
+
const pointerDate = getDateByRatio(dragStartTrackBoundsRef.current, ratio);
|
|
350
449
|
windowDragOffsetDaysRef.current = getDaysBetween(draftRange.startDate, pointerDate);
|
|
351
450
|
} else {
|
|
352
451
|
windowDragOffsetDaysRef.current = 0;
|
|
@@ -354,13 +453,23 @@ export function RandomDateRangeDialog({
|
|
|
354
453
|
}
|
|
355
454
|
|
|
356
455
|
const buildDraggedRange = useCallback((clientX: number): RandomCalendarRange | null => {
|
|
357
|
-
|
|
456
|
+
const currentTrackBounds = trackBoundsRef.current;
|
|
457
|
+
const dragStartTrackBounds = dragStartTrackBoundsRef.current;
|
|
458
|
+
if (
|
|
459
|
+
!dragModeRef.current ||
|
|
460
|
+
!dragStartRangeRef.current ||
|
|
461
|
+
!dragStartTrackBounds?.startDate ||
|
|
462
|
+
!dragStartTrackBounds.endDate ||
|
|
463
|
+
!currentTrackBounds.startDate ||
|
|
464
|
+
!currentTrackBounds.endDate ||
|
|
465
|
+
!trackRef.current
|
|
466
|
+
) {
|
|
358
467
|
return null;
|
|
359
468
|
}
|
|
360
469
|
|
|
361
470
|
const rect = trackRef.current.getBoundingClientRect();
|
|
362
|
-
const ratio =
|
|
363
|
-
const pointerDate =
|
|
471
|
+
const ratio = (clientX - rect.left) / rect.width;
|
|
472
|
+
const pointerDate = getDateByOverflowRatio(dragStartTrackBounds, ratio, rect.width);
|
|
364
473
|
const currentRange = dragStartRangeRef.current;
|
|
365
474
|
|
|
366
475
|
if (!currentRange.startDate || !currentRange.endDate) {
|
|
@@ -371,24 +480,42 @@ export function RandomDateRangeDialog({
|
|
|
371
480
|
const earliestStart = addDays(currentRange.endDate, -(MAX_RANGE_DAYS - 1));
|
|
372
481
|
const boundedPointerDate = compareDateStrings(pointerDate, earliestStart) < 0 ? earliestStart : pointerDate;
|
|
373
482
|
const nextStart = compareDateStrings(boundedPointerDate, currentRange.endDate) > 0 ? currentRange.endDate : boundedPointerDate;
|
|
374
|
-
|
|
483
|
+
const nextRange = { startDate: nextStart, endDate: currentRange.endDate };
|
|
484
|
+
const nextTrackBounds = ensureRangeVisibleOnTrack(nextRange, currentTrackBounds);
|
|
485
|
+
if (nextTrackBounds !== currentTrackBounds) {
|
|
486
|
+
trackBoundsRef.current = nextTrackBounds;
|
|
487
|
+
setDraftRange(nextRange);
|
|
488
|
+
setTrackBounds(nextTrackBounds);
|
|
489
|
+
}
|
|
490
|
+
return nextRange;
|
|
375
491
|
}
|
|
376
492
|
|
|
377
493
|
if (dragModeRef.current === 'end') {
|
|
378
494
|
const latestEnd = addDays(currentRange.startDate, MAX_RANGE_DAYS - 1);
|
|
379
495
|
const boundedPointerDate = compareDateStrings(pointerDate, latestEnd) > 0 ? latestEnd : pointerDate;
|
|
380
496
|
const nextEnd = compareDateStrings(boundedPointerDate, currentRange.startDate) < 0 ? currentRange.startDate : boundedPointerDate;
|
|
381
|
-
|
|
497
|
+
const nextRange = { startDate: currentRange.startDate, endDate: nextEnd };
|
|
498
|
+
const nextTrackBounds = ensureRangeVisibleOnTrack(nextRange, currentTrackBounds);
|
|
499
|
+
if (nextTrackBounds !== currentTrackBounds) {
|
|
500
|
+
trackBoundsRef.current = nextTrackBounds;
|
|
501
|
+
setDraftRange(nextRange);
|
|
502
|
+
setTrackBounds(nextTrackBounds);
|
|
503
|
+
}
|
|
504
|
+
return nextRange;
|
|
382
505
|
}
|
|
383
506
|
|
|
384
507
|
const spanDays = getDaysBetween(currentRange.startDate, currentRange.endDate);
|
|
385
|
-
const nextStart =
|
|
386
|
-
startDate: trackBounds.startDate,
|
|
387
|
-
endDate: addDays(trackBounds.endDate, -spanDays),
|
|
388
|
-
});
|
|
508
|
+
const nextStart = addDays(pointerDate, -windowDragOffsetDaysRef.current);
|
|
389
509
|
const nextEnd = addDays(nextStart, spanDays);
|
|
390
|
-
|
|
391
|
-
|
|
510
|
+
const nextRange = { startDate: nextStart, endDate: nextEnd };
|
|
511
|
+
const nextTrackBounds = ensureRangeVisibleOnTrack(nextRange, currentTrackBounds);
|
|
512
|
+
if (nextTrackBounds !== currentTrackBounds) {
|
|
513
|
+
trackBoundsRef.current = nextTrackBounds;
|
|
514
|
+
setDraftRange(nextRange);
|
|
515
|
+
setTrackBounds(nextTrackBounds);
|
|
516
|
+
}
|
|
517
|
+
return nextRange;
|
|
518
|
+
}, []);
|
|
392
519
|
|
|
393
520
|
useEffect(() => {
|
|
394
521
|
syncPreviewDomRef.current = syncPreviewDom;
|
|
@@ -405,6 +532,7 @@ export function RandomDateRangeDialog({
|
|
|
405
532
|
|
|
406
533
|
const nextRange = dragPreviewRef.current;
|
|
407
534
|
dragStartRangeRef.current = null;
|
|
535
|
+
dragStartTrackBoundsRef.current = null;
|
|
408
536
|
dragModeRef.current = null;
|
|
409
537
|
pointerIdRef.current = null;
|
|
410
538
|
windowDragOffsetDaysRef.current = 0;
|
|
@@ -412,6 +540,7 @@ export function RandomDateRangeDialog({
|
|
|
412
540
|
setDraftRange(nextRange);
|
|
413
541
|
setReferenceDate(nextRange.startDate);
|
|
414
542
|
setWindowDays(getInclusiveDayCount(nextRange));
|
|
543
|
+
commitTrackBounds(ensureRangeVisibleOnTrack(nextRange, trackBoundsRef.current));
|
|
415
544
|
}
|
|
416
545
|
}
|
|
417
546
|
|
|
@@ -472,16 +601,17 @@ export function RandomDateRangeDialog({
|
|
|
472
601
|
}, [open]);
|
|
473
602
|
|
|
474
603
|
if (!open) {
|
|
475
|
-
return
|
|
604
|
+
return <>{dialogLoading}</>;
|
|
476
605
|
}
|
|
477
606
|
|
|
478
607
|
return createPortal(
|
|
479
|
-
|
|
480
|
-
<div className="
|
|
608
|
+
<>
|
|
609
|
+
<div className="fixed inset-0 z-120 flex select-none items-center justify-center bg-slate-950/60 px-3 py-6 backdrop-blur-sm">
|
|
610
|
+
<div className="w-full max-w-2xl overflow-hidden rounded-3xl border border-black/10 bg-white shadow-2xl dark:border-white/10 dark:bg-slate-950">
|
|
481
611
|
<div className="space-y-5 p-4">
|
|
482
|
-
<div className="relative flex items-center justify-center px-
|
|
483
|
-
<div ref={resultLabelRef} className="select-none text-base font-semibold text-slate-900 dark:text-white">{getRangeLabel(draftRange)}</div>
|
|
484
|
-
<div className="absolute right-0 top-1/2 flex -translate-y-1/2 items-center
|
|
612
|
+
<div className="relative flex items-center justify-center px-9 text-center sm:px-16">
|
|
613
|
+
<div ref={resultLabelRef} className="min-w-0 select-none truncate text-base font-semibold text-slate-900 dark:text-white">{getRangeLabel(draftRange)}</div>
|
|
614
|
+
<div className="absolute right-0 top-1/2 flex -translate-y-1/2 translate-x-1 items-center sm:translate-x-0">
|
|
485
615
|
<button
|
|
486
616
|
type="button"
|
|
487
617
|
onClick={() => onOpenChange(false)}
|
|
@@ -490,26 +620,10 @@ export function RandomDateRangeDialog({
|
|
|
490
620
|
>
|
|
491
621
|
<XIcon className="h-4 w-4" />
|
|
492
622
|
</button>
|
|
493
|
-
<button
|
|
494
|
-
type="button"
|
|
495
|
-
onClick={() => {
|
|
496
|
-
onApply(draftRange);
|
|
497
|
-
onOpenChange(false);
|
|
498
|
-
}}
|
|
499
|
-
disabled={!draftRange.startDate || !draftRange.endDate}
|
|
500
|
-
className={cn(
|
|
501
|
-
DIALOG_ICON_BUTTON_CLASS_NAME,
|
|
502
|
-
'text-slate-700 dark:text-slate-100',
|
|
503
|
-
'disabled:cursor-not-allowed disabled:opacity-40 disabled:hover:border-black/10 disabled:hover:bg-white disabled:hover:text-slate-700 dark:disabled:hover:border-white/10 dark:disabled:hover:bg-slate-950 dark:disabled:hover:text-slate-100'
|
|
504
|
-
)}
|
|
505
|
-
aria-label="Apply"
|
|
506
|
-
>
|
|
507
|
-
<CheckCheckIcon className="h-4 w-4" />
|
|
508
|
-
</button>
|
|
509
623
|
</div>
|
|
510
624
|
</div>
|
|
511
625
|
|
|
512
|
-
<div className="space-y-
|
|
626
|
+
<div className="space-y-3">
|
|
513
627
|
<div className="flex items-center justify-between gap-2">
|
|
514
628
|
<div className="flex items-center gap-1">
|
|
515
629
|
<button
|
|
@@ -559,14 +673,14 @@ export function RandomDateRangeDialog({
|
|
|
559
673
|
endDate: addDays(baseReferenceDate, resolvedDefaultRangeDays - 1),
|
|
560
674
|
};
|
|
561
675
|
setReferenceDate(baseReferenceDate);
|
|
562
|
-
|
|
676
|
+
commitTrackBounds(buildTrackRange(baseReferenceDate, resolvedDefaultRangeDays));
|
|
563
677
|
setWindowDays(resolvedDefaultRangeDays);
|
|
564
678
|
setDraftRange(nextRange);
|
|
565
679
|
onClear?.(nextRange);
|
|
566
680
|
}}
|
|
567
681
|
className={DIALOG_PILL_BUTTON_CLASS_NAME}
|
|
568
682
|
>
|
|
569
|
-
|
|
683
|
+
Today
|
|
570
684
|
</button>
|
|
571
685
|
<button
|
|
572
686
|
type="button"
|
|
@@ -584,7 +698,8 @@ export function RandomDateRangeDialog({
|
|
|
584
698
|
};
|
|
585
699
|
setDraftRange(normalizedRange);
|
|
586
700
|
setWindowDays(getInclusiveDayCount(normalizedRange));
|
|
587
|
-
|
|
701
|
+
setReferenceDate(normalizedRange.startDate);
|
|
702
|
+
commitTrackBounds(buildTrackRange(normalizedRange.startDate, getInclusiveDayCount(normalizedRange)));
|
|
588
703
|
}}
|
|
589
704
|
className={DIALOG_PILL_BUTTON_CLASS_NAME}
|
|
590
705
|
>
|
|
@@ -632,10 +747,10 @@ export function RandomDateRangeDialog({
|
|
|
632
747
|
</div>
|
|
633
748
|
</div>
|
|
634
749
|
|
|
635
|
-
<div className="relative h-
|
|
750
|
+
<div className="relative h-21">
|
|
636
751
|
<div className="absolute inset-x-0 top-0 grid grid-cols-[3.5rem_minmax(0,1fr)_3.5rem] items-center gap-2 text-sm font-semibold text-slate-500 dark:text-slate-400">
|
|
637
752
|
<span className="relative block select-none text-center">
|
|
638
|
-
{
|
|
753
|
+
<RollingMonthLabel value={leftMonthLabel} />
|
|
639
754
|
<span className="pointer-events-none absolute left-1/2 top-7 h-2.5 w-2.5 -translate-x-1/2 rounded-full bg-slate-400 dark:bg-slate-500" />
|
|
640
755
|
<span className="pointer-events-none absolute left-1/2 top-[1.95rem] h-9 w-0.5 -translate-x-1/2 bg-slate-400 dark:bg-slate-500" />
|
|
641
756
|
</span>
|
|
@@ -657,7 +772,7 @@ export function RandomDateRangeDialog({
|
|
|
657
772
|
))}
|
|
658
773
|
</div>
|
|
659
774
|
<span className="relative block select-none text-center">
|
|
660
|
-
{
|
|
775
|
+
<RollingMonthLabel value={rightMonthLabel} />
|
|
661
776
|
<span className="pointer-events-none absolute right-1/2 top-7 h-2.5 w-2.5 translate-x-1/2 rounded-full bg-slate-400 dark:bg-slate-500" />
|
|
662
777
|
<span className="pointer-events-none absolute right-1/2 top-[1.95rem] h-9 w-0.5 translate-x-1/2 bg-slate-400 dark:bg-slate-500" />
|
|
663
778
|
</span>
|
|
@@ -694,8 +809,8 @@ export function RandomDateRangeDialog({
|
|
|
694
809
|
</div>
|
|
695
810
|
<div
|
|
696
811
|
ref={selectionRef}
|
|
697
|
-
className="absolute top-1/2 z-10 h-4 touch-none -translate-y-1/2 overflow-visible rounded-md border
|
|
698
|
-
style={{ left: `${leftPercent}%`, width: `${widthPercent}
|
|
812
|
+
className="absolute top-1/2 z-10 h-4 touch-none -translate-y-1/2 overflow-visible rounded-md border bg-white dark:bg-slate-950"
|
|
813
|
+
style={{ left: `${leftPercent}%`, width: `${widthPercent}%`, borderColor: themeSvgIconColor }}
|
|
699
814
|
onPointerDown={(event) => {
|
|
700
815
|
event.stopPropagation();
|
|
701
816
|
beginDrag('window', event.pointerId, event.clientX);
|
|
@@ -709,8 +824,8 @@ export function RandomDateRangeDialog({
|
|
|
709
824
|
<button
|
|
710
825
|
ref={startHandleRef}
|
|
711
826
|
type="button"
|
|
712
|
-
className="absolute top-1/2 z-20 h-6 w-6 touch-none -translate-x-1/2 -translate-y-1/2 rounded-full border
|
|
713
|
-
style={{ left: `${startHandlePercent}
|
|
827
|
+
className="absolute top-1/2 z-20 h-6 w-6 touch-none -translate-x-1/2 -translate-y-1/2 rounded-full border bg-white shadow-sm dark:bg-slate-950"
|
|
828
|
+
style={{ left: `${startHandlePercent}%`, borderColor: themeSvgIconColor }}
|
|
714
829
|
onPointerDown={(event) => {
|
|
715
830
|
event.stopPropagation();
|
|
716
831
|
beginDrag('start', event.pointerId);
|
|
@@ -720,8 +835,8 @@ export function RandomDateRangeDialog({
|
|
|
720
835
|
<button
|
|
721
836
|
ref={endHandleRef}
|
|
722
837
|
type="button"
|
|
723
|
-
className="absolute top-1/2 z-20 h-6 w-6 touch-none -translate-x-1/2 -translate-y-1/2 rounded-full border
|
|
724
|
-
style={{ left: `${endHandlePercent}
|
|
838
|
+
className="absolute top-1/2 z-20 h-6 w-6 touch-none -translate-x-1/2 -translate-y-1/2 rounded-full border bg-white shadow-sm dark:bg-slate-950"
|
|
839
|
+
style={{ left: `${endHandlePercent}%`, borderColor: themeSvgIconColor }}
|
|
725
840
|
onPointerDown={(event) => {
|
|
726
841
|
event.stopPropagation();
|
|
727
842
|
beginDrag('end', event.pointerId);
|
|
@@ -732,10 +847,63 @@ export function RandomDateRangeDialog({
|
|
|
732
847
|
</div>
|
|
733
848
|
</div>
|
|
734
849
|
|
|
850
|
+
<div className="flex justify-end">
|
|
851
|
+
<XButton
|
|
852
|
+
type="single"
|
|
853
|
+
variant="soft"
|
|
854
|
+
minWidth="min-w-[110px]"
|
|
855
|
+
className="w-auto"
|
|
856
|
+
iconClassName="h-4 w-4"
|
|
857
|
+
button={{
|
|
858
|
+
icon: <CheckCheckIcon />,
|
|
859
|
+
text: 'Apply',
|
|
860
|
+
disabled: !draftRange.startDate || !draftRange.endDate,
|
|
861
|
+
onClick: () => {
|
|
862
|
+
void runDialogAction('confirm', handleApply);
|
|
863
|
+
},
|
|
864
|
+
}}
|
|
865
|
+
/>
|
|
866
|
+
</div>
|
|
867
|
+
|
|
735
868
|
</div>
|
|
736
869
|
</div>
|
|
870
|
+
</div>
|
|
737
871
|
</div>
|
|
738
|
-
|
|
872
|
+
<style>
|
|
873
|
+
{`
|
|
874
|
+
@keyframes rd-date-range-month-in {
|
|
875
|
+
from {
|
|
876
|
+
opacity: 0;
|
|
877
|
+
transform: translateY(-0.45rem);
|
|
878
|
+
}
|
|
879
|
+
to {
|
|
880
|
+
opacity: 1;
|
|
881
|
+
transform: translateY(0);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
@keyframes rd-date-range-month-out {
|
|
886
|
+
from {
|
|
887
|
+
opacity: 1;
|
|
888
|
+
transform: translateY(0);
|
|
889
|
+
}
|
|
890
|
+
to {
|
|
891
|
+
opacity: 0;
|
|
892
|
+
transform: translateY(0.45rem);
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
.rd-date-range-month-in {
|
|
897
|
+
animation: rd-date-range-month-in 180ms ease-out both;
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
.rd-date-range-month-out {
|
|
901
|
+
animation: rd-date-range-month-out 180ms ease-out both;
|
|
902
|
+
}
|
|
903
|
+
`}
|
|
904
|
+
</style>
|
|
905
|
+
{dialogLoading}
|
|
906
|
+
</>,
|
|
739
907
|
document.body
|
|
740
908
|
);
|
|
741
909
|
}
|
package/src/main/cta.tsx
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { getTranslations } from 'next-intl/server';
|
|
2
2
|
import { GradientButton } from "./buttons";
|
|
3
3
|
import { cn } from '@windrun-huaiin/lib/utils';
|
|
4
|
-
import { themeIconColor } from '@windrun-huaiin/base-ui/lib';
|
|
4
|
+
import { themeIconColor, themeName, themeSvgIconColor } from '@windrun-huaiin/base-ui/lib';
|
|
5
5
|
import { richText } from './rich-text-expert';
|
|
6
6
|
import { responsiveSection } from './section-layout';
|
|
7
|
+
import type { CSSProperties } from 'react';
|
|
7
8
|
|
|
8
9
|
interface CTAData {
|
|
9
10
|
title: string;
|
|
@@ -14,6 +15,29 @@ interface CTAData {
|
|
|
14
15
|
url: string;
|
|
15
16
|
}
|
|
16
17
|
|
|
18
|
+
type CTAThemePalette = {
|
|
19
|
+
b: string;
|
|
20
|
+
c: string;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const CTA_THEME_PALETTES: Record<string, CTAThemePalette> = {
|
|
24
|
+
purple: { b: '#EC4899', c: '#6366F1' },
|
|
25
|
+
orange: { b: '#F59E0B', c: '#EF4444' },
|
|
26
|
+
indigo: { b: '#3B82F6', c: '#06B6D4' },
|
|
27
|
+
emerald: { b: '#14B8A6', c: '#22C55E' },
|
|
28
|
+
rose: { b: '#EC4899', c: '#FB7185' },
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
function createCTAStyle(): CSSProperties {
|
|
32
|
+
const palette = CTA_THEME_PALETTES[themeName] ?? CTA_THEME_PALETTES.purple;
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
'--cta-color-a': themeSvgIconColor,
|
|
36
|
+
'--cta-color-b': palette.b,
|
|
37
|
+
'--cta-color-c': palette.c,
|
|
38
|
+
} as CSSProperties;
|
|
39
|
+
}
|
|
40
|
+
|
|
17
41
|
export async function CTA({
|
|
18
42
|
locale,
|
|
19
43
|
sectionClassName
|
|
@@ -34,26 +58,31 @@ export async function CTA({
|
|
|
34
58
|
|
|
35
59
|
return (
|
|
36
60
|
<section id="cta" className={cn(responsiveSection, sectionClassName)}>
|
|
37
|
-
<div
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
<
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
61
|
+
<div
|
|
62
|
+
className="
|
|
63
|
+
third-ui-cta-surface
|
|
64
|
+
relative overflow-hidden rounded-2xl border border-black/5 py-3 text-center shadow-sm
|
|
65
|
+
animate-cta-gradient-wave
|
|
66
|
+
sm:py-6 md:py-8
|
|
67
|
+
dark:border-white/10 dark:shadow-none
|
|
68
|
+
"
|
|
69
|
+
style={createCTAStyle()}
|
|
70
|
+
>
|
|
71
|
+
<div className="relative z-10 px-4 sm:px-6">
|
|
72
|
+
<h2 className="mb-6 text-3xl font-bold text-neutral-950 md:text-4xl dark:text-neutral-50">
|
|
73
|
+
{data.title} <span className={themeIconColor}>{data.eyesOn}</span>?
|
|
74
|
+
</h2>
|
|
75
|
+
<p className="mx-auto mb-8 max-w-3xl text-base text-neutral-700 sm:text-xl dark:text-neutral-300">
|
|
76
|
+
{data.description1}
|
|
77
|
+
<br />
|
|
78
|
+
<span className={cn(themeIconColor, "text-xl sm:text-2xl")}>{data.description2}</span>
|
|
79
|
+
</p>
|
|
80
|
+
<GradientButton
|
|
81
|
+
title={data.button}
|
|
82
|
+
href={data.url}
|
|
83
|
+
align="center"
|
|
84
|
+
/>
|
|
85
|
+
</div>
|
|
57
86
|
</div>
|
|
58
87
|
</section>
|
|
59
88
|
)
|