@udixio/ui-react 2.10.12 → 2.10.14
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/index.cjs +3 -3
- package/dist/index.js +2696 -2710
- package/dist/lib/effects/ThemeProvider.d.ts.map +1 -1
- package/dist/theme.worker.js +6633 -0
- package/package.json +4 -1
- package/.eslintrc.mjs +0 -22
- package/.storybook/main.ts +0 -20
- package/.storybook/preview.ts +0 -1
- package/CHANGELOG.md +0 -1130
- package/dist/scrollDriven-AP2yWhzi.js +0 -121
- package/postcss.config.mjs +0 -5
- package/src/index.css +0 -4
- package/src/index.ts +0 -1
- package/src/lib/components/AnchorPositioner.tsx +0 -185
- package/src/lib/components/Button.tsx +0 -208
- package/src/lib/components/Card.tsx +0 -47
- package/src/lib/components/Carousel.tsx +0 -437
- package/src/lib/components/CarouselItem.tsx +0 -61
- package/src/lib/components/Checkbox.tsx +0 -120
- package/src/lib/components/Chip.tsx +0 -341
- package/src/lib/components/Chips.tsx +0 -331
- package/src/lib/components/ContextMenu.tsx +0 -109
- package/src/lib/components/DatePicker.tsx +0 -432
- package/src/lib/components/Divider.tsx +0 -20
- package/src/lib/components/Fab.tsx +0 -127
- package/src/lib/components/FabMenu.tsx +0 -239
- package/src/lib/components/IconButton.tsx +0 -146
- package/src/lib/components/Menu.tsx +0 -88
- package/src/lib/components/MenuGroup.tsx +0 -34
- package/src/lib/components/MenuHeadline.tsx +0 -9
- package/src/lib/components/MenuItem.tsx +0 -215
- package/src/lib/components/NavigationRail.tsx +0 -186
- package/src/lib/components/NavigationRailItem.tsx +0 -227
- package/src/lib/components/ProgressIndicator.tsx +0 -214
- package/src/lib/components/SideSheet.tsx +0 -135
- package/src/lib/components/Slider.tsx +0 -374
- package/src/lib/components/Snackbar.tsx +0 -77
- package/src/lib/components/Switch.tsx +0 -107
- package/src/lib/components/Tab.tsx +0 -123
- package/src/lib/components/TabGroup.tsx +0 -66
- package/src/lib/components/TabGroupContext.tsx +0 -16
- package/src/lib/components/TabPanel.tsx +0 -27
- package/src/lib/components/TabPanels.tsx +0 -76
- package/src/lib/components/Tabs.tsx +0 -105
- package/src/lib/components/TextField.tsx +0 -586
- package/src/lib/components/Tooltip.tsx +0 -217
- package/src/lib/components/index.ts +0 -34
- package/src/lib/config/config.interface.ts +0 -9
- package/src/lib/config/define-config.ts +0 -16
- package/src/lib/config/index.ts +0 -2
- package/src/lib/effects/AnimateOnScroll.ts +0 -391
- package/src/lib/effects/State.tsx +0 -90
- package/src/lib/effects/SyncedFixedWrapper.tsx +0 -62
- package/src/lib/effects/ThemeProvider.tsx +0 -172
- package/src/lib/effects/block-scroll.effect.tsx +0 -313
- package/src/lib/effects/custom-scroll/custom-scroll.effect.tsx +0 -407
- package/src/lib/effects/custom-scroll/custom-scroll.interface.ts +0 -29
- package/src/lib/effects/custom-scroll/custom-scroll.style.ts +0 -32
- package/src/lib/effects/custom-scroll/index.ts +0 -3
- package/src/lib/effects/index.ts +0 -7
- package/src/lib/effects/ripple/RippleEffect.tsx +0 -116
- package/src/lib/effects/ripple/index.tsx +0 -1
- package/src/lib/effects/scrollDriven.ts +0 -239
- package/src/lib/effects/smooth-scroll.effect.tsx +0 -112
- package/src/lib/effects/theme.worker.ts +0 -97
- package/src/lib/hooks/index.ts +0 -10
- package/src/lib/hooks/useTooltipTrigger.ts +0 -270
- package/src/lib/icon/icon.tsx +0 -125
- package/src/lib/icon/index.ts +0 -1
- package/src/lib/index.ts +0 -8
- package/src/lib/interfaces/button.interface.ts +0 -65
- package/src/lib/interfaces/card.interface.ts +0 -11
- package/src/lib/interfaces/carousel-item.interface.ts +0 -12
- package/src/lib/interfaces/carousel.interface.ts +0 -41
- package/src/lib/interfaces/checkbox.interface.ts +0 -39
- package/src/lib/interfaces/chip.interface.ts +0 -97
- package/src/lib/interfaces/chips.interface.ts +0 -37
- package/src/lib/interfaces/date-picker.interface.ts +0 -79
- package/src/lib/interfaces/divider.interface.ts +0 -7
- package/src/lib/interfaces/fab-menu.interface.ts +0 -12
- package/src/lib/interfaces/fab.interface.ts +0 -27
- package/src/lib/interfaces/icon-button.interface.ts +0 -38
- package/src/lib/interfaces/index.ts +0 -26
- package/src/lib/interfaces/menu-group.interface.ts +0 -13
- package/src/lib/interfaces/menu-item.interface.ts +0 -29
- package/src/lib/interfaces/menu.interface.ts +0 -19
- package/src/lib/interfaces/navigation-rail-item.interface.ts +0 -39
- package/src/lib/interfaces/navigation-rail.interface.ts +0 -39
- package/src/lib/interfaces/progress-indicator.interface.ts +0 -41
- package/src/lib/interfaces/side-sheet.interface.tsx +0 -28
- package/src/lib/interfaces/slider.interface.ts +0 -27
- package/src/lib/interfaces/snackbar.interface.ts +0 -13
- package/src/lib/interfaces/switch.interface.ts +0 -14
- package/src/lib/interfaces/tab-group.interface.ts +0 -13
- package/src/lib/interfaces/tab-panels.interface.ts +0 -21
- package/src/lib/interfaces/tab.interface.ts +0 -31
- package/src/lib/interfaces/tabs.interface.ts +0 -22
- package/src/lib/interfaces/text-field.interface.ts +0 -61
- package/src/lib/interfaces/tooltip.interface.ts +0 -61
- package/src/lib/styles/button.style.ts +0 -136
- package/src/lib/styles/card.style.ts +0 -29
- package/src/lib/styles/carousel-item.style.ts +0 -24
- package/src/lib/styles/carousel.style.ts +0 -22
- package/src/lib/styles/checkbox.style.ts +0 -64
- package/src/lib/styles/chip.style.ts +0 -62
- package/src/lib/styles/chips.style.ts +0 -20
- package/src/lib/styles/date-picker.style.ts +0 -43
- package/src/lib/styles/divider.style.ts +0 -31
- package/src/lib/styles/fab-menu.style.ts +0 -29
- package/src/lib/styles/fab.style.ts +0 -49
- package/src/lib/styles/icon-button.style.ts +0 -168
- package/src/lib/styles/index.ts +0 -25
- package/src/lib/styles/menu-group.style.ts +0 -34
- package/src/lib/styles/menu-headline.style.ts +0 -20
- package/src/lib/styles/menu-item.style.ts +0 -45
- package/src/lib/styles/menu.style.ts +0 -32
- package/src/lib/styles/navigation-rail-item.style.ts +0 -56
- package/src/lib/styles/navigation-rail.style.ts +0 -36
- package/src/lib/styles/progress-indicator.style.ts +0 -72
- package/src/lib/styles/side-sheet.style.ts +0 -45
- package/src/lib/styles/slider.style.ts +0 -41
- package/src/lib/styles/snackbar.style.ts +0 -26
- package/src/lib/styles/switch.style.ts +0 -67
- package/src/lib/styles/tab-panels.style.ts +0 -35
- package/src/lib/styles/tab.style.ts +0 -78
- package/src/lib/styles/tabs.style.ts +0 -22
- package/src/lib/styles/text-field.style.ts +0 -115
- package/src/lib/styles/tooltip.style.ts +0 -48
- package/src/lib/utils/component-helper.ts +0 -134
- package/src/lib/utils/component.ts +0 -34
- package/src/lib/utils/index.ts +0 -7
- package/src/lib/utils/string.ts +0 -9
- package/src/lib/utils/styles/classnames.ts +0 -49
- package/src/lib/utils/styles/get-classname.ts +0 -96
- package/src/lib/utils/styles/index.ts +0 -4
- package/src/lib/utils/styles/use-classnames.ts +0 -25
- package/src/stories/action/button.stories.tsx +0 -86
- package/src/stories/action/fab.stories.tsx +0 -54
- package/src/stories/action/icon-button.stories.tsx +0 -134
- package/src/stories/assets/accessibility.png +0 -0
- package/src/stories/assets/accessibility.svg +0 -5
- package/src/stories/assets/addon-library.png +0 -0
- package/src/stories/assets/assets.png +0 -0
- package/src/stories/assets/context.png +0 -0
- package/src/stories/assets/discord.svg +0 -15
- package/src/stories/assets/docs.png +0 -0
- package/src/stories/assets/figma-plugin.png +0 -0
- package/src/stories/assets/github.svg +0 -3
- package/src/stories/assets/share.png +0 -0
- package/src/stories/assets/styling.png +0 -0
- package/src/stories/assets/testing.png +0 -0
- package/src/stories/assets/theming.png +0 -0
- package/src/stories/assets/tutorials.svg +0 -12
- package/src/stories/assets/youtube.svg +0 -4
- package/src/stories/communication/ProgressIndicator.stories.tsx +0 -57
- package/src/stories/communication/SnackBar.stories.tsx +0 -32
- package/src/stories/communication/tool-tip.stories.tsx +0 -133
- package/src/stories/containment/card.stories.tsx +0 -42
- package/src/stories/containment/carousel.stories.tsx +0 -65
- package/src/stories/containment/divider.stories.tsx +0 -35
- package/src/stories/containment/slide-sheet.stories.tsx +0 -45
- package/src/stories/effect/smooth-scroll.stories.tsx +0 -54
- package/src/stories/navigation/navigation-rail/navigation-rail-item.stories.tsx +0 -65
- package/src/stories/navigation/navigation-rail/navigation-rail.stories.tsx +0 -122
- package/src/stories/navigation/tabs/tab.stories.tsx +0 -57
- package/src/stories/navigation/tabs/tabs.stories.tsx +0 -102
- package/src/stories/selection/slider.stories.tsx +0 -85
- package/src/stories/selection/switch.stories.tsx +0 -46
- package/src/stories/text-inputs/text-field.stories.tsx +0 -135
- package/src/tests/Button.spec.tsx +0 -67
- package/src/tests/useClassNames.spec.tsx +0 -82
- package/src/udixio.css +0 -120
- package/theme.config.ts +0 -7
- package/tsconfig.json +0 -16
- package/tsconfig.lib.json +0 -51
- package/tsconfig.spec.json +0 -37
- package/tsconfig.storybook.json +0 -38
- package/vite.config.ts +0 -82
- /package/dist/{scrollDriven-DWAu7CR0.cjs → scrollDriven.js} +0 -0
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
import React, { useEffect, useRef, useState } from 'react';
|
|
2
|
-
import { AnchorPositioner } from './AnchorPositioner';
|
|
3
|
-
import { Menu } from './Menu';
|
|
4
|
-
// import { MenuProps } from '../interfaces/menu.interface'; // MenuProps is not exported from interface file usually, check file content
|
|
5
|
-
import { MenuInterface } from '../interfaces';
|
|
6
|
-
import { ReactProps } from '../utils';
|
|
7
|
-
|
|
8
|
-
// MenuInterface has props: MenuProps.
|
|
9
|
-
// But MenuProps might not be exported directly from the package index, so accessing it via MenuInterface['props'] is safer if we can't import it.
|
|
10
|
-
// Actually checking Step 1271, MenuProps IS exported.
|
|
11
|
-
|
|
12
|
-
export type ContextMenuProps = {
|
|
13
|
-
props: { trigger: React.ReactNode } & MenuInterface['props'];
|
|
14
|
-
type: 'div';
|
|
15
|
-
states: {
|
|
16
|
-
hasGroups: boolean;
|
|
17
|
-
};
|
|
18
|
-
elements: [''];
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export const ContextMenu = ({
|
|
22
|
-
trigger,
|
|
23
|
-
children,
|
|
24
|
-
...menuProps
|
|
25
|
-
}: ReactProps<ContextMenuProps>) => {
|
|
26
|
-
const [contextMenu, setContextMenu] = useState<{
|
|
27
|
-
mouseX: number;
|
|
28
|
-
mouseY: number;
|
|
29
|
-
} | null>(null);
|
|
30
|
-
const anchorRef = useRef<HTMLDivElement>(null);
|
|
31
|
-
|
|
32
|
-
const handleContextMenu = (event: React.MouseEvent) => {
|
|
33
|
-
event.preventDefault();
|
|
34
|
-
setContextMenu({
|
|
35
|
-
mouseX: event.clientX,
|
|
36
|
-
mouseY: event.clientY,
|
|
37
|
-
});
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
const handleClose = () => {
|
|
41
|
-
setContextMenu(null);
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
const handleSelect = () => {
|
|
45
|
-
handleClose();
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
useEffect(() => {
|
|
49
|
-
if (!contextMenu) return;
|
|
50
|
-
const handleOutsideInteraction = () => setContextMenu(null);
|
|
51
|
-
window.addEventListener('click', handleOutsideInteraction);
|
|
52
|
-
window.addEventListener('scroll', handleOutsideInteraction, true);
|
|
53
|
-
|
|
54
|
-
return () => {
|
|
55
|
-
window.removeEventListener('click', handleOutsideInteraction);
|
|
56
|
-
window.removeEventListener('scroll', handleOutsideInteraction, true);
|
|
57
|
-
};
|
|
58
|
-
}, [contextMenu]);
|
|
59
|
-
|
|
60
|
-
// Clone trigger if valid element to attach onContextMenu, otherwise wrap
|
|
61
|
-
const triggerElement = React.isValidElement(trigger) ? (
|
|
62
|
-
React.cloneElement(
|
|
63
|
-
trigger as React.ReactElement,
|
|
64
|
-
{
|
|
65
|
-
onContextMenu: (e: React.MouseEvent) => {
|
|
66
|
-
handleContextMenu(e);
|
|
67
|
-
// Call original handler if exists
|
|
68
|
-
(trigger as React.ReactElement).props.onContextMenu?.(e);
|
|
69
|
-
},
|
|
70
|
-
} as any,
|
|
71
|
-
)
|
|
72
|
-
) : (
|
|
73
|
-
<div onContextMenu={handleContextMenu} className="inline-block">
|
|
74
|
-
{trigger}
|
|
75
|
-
</div>
|
|
76
|
-
);
|
|
77
|
-
|
|
78
|
-
return (
|
|
79
|
-
<>
|
|
80
|
-
{triggerElement}
|
|
81
|
-
|
|
82
|
-
{/* Invisible anchor element positioned at cursor */}
|
|
83
|
-
<div
|
|
84
|
-
ref={anchorRef}
|
|
85
|
-
style={{
|
|
86
|
-
position: 'fixed',
|
|
87
|
-
top: contextMenu?.mouseY ?? 0,
|
|
88
|
-
left: contextMenu?.mouseX ?? 0,
|
|
89
|
-
width: 1,
|
|
90
|
-
height: 1,
|
|
91
|
-
pointerEvents: 'none',
|
|
92
|
-
visibility: 'hidden',
|
|
93
|
-
}}
|
|
94
|
-
/>
|
|
95
|
-
|
|
96
|
-
{contextMenu && (
|
|
97
|
-
<AnchorPositioner
|
|
98
|
-
anchorRef={anchorRef}
|
|
99
|
-
position="bottom right"
|
|
100
|
-
onClick={(e) => e.stopPropagation()}
|
|
101
|
-
>
|
|
102
|
-
<Menu onClick={handleSelect} {...menuProps}>
|
|
103
|
-
{children}
|
|
104
|
-
</Menu>
|
|
105
|
-
</AnchorPositioner>
|
|
106
|
-
)}
|
|
107
|
-
</>
|
|
108
|
-
);
|
|
109
|
-
};
|
|
@@ -1,432 +0,0 @@
|
|
|
1
|
-
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
-
import { useDatePickerStyle } from '../styles/date-picker.style';
|
|
3
|
-
import { classNames, ReactProps } from '../utils';
|
|
4
|
-
import {
|
|
5
|
-
DatePickerInterface,
|
|
6
|
-
DateRange,
|
|
7
|
-
} from '../interfaces/date-picker.interface';
|
|
8
|
-
import {
|
|
9
|
-
faChevronDown,
|
|
10
|
-
faChevronLeft,
|
|
11
|
-
faChevronRight,
|
|
12
|
-
} from '@fortawesome/free-solid-svg-icons';
|
|
13
|
-
import { Button } from './Button';
|
|
14
|
-
import { IconButton } from './IconButton';
|
|
15
|
-
import { AnimatePresence, motion } from 'motion/react';
|
|
16
|
-
import { Icon } from '../icon';
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* DatePickers let users select a date, or a range of dates.
|
|
20
|
-
* @status beta
|
|
21
|
-
* @category Selection
|
|
22
|
-
*/
|
|
23
|
-
export const DatePicker = ({
|
|
24
|
-
value: valueProp,
|
|
25
|
-
defaultValue,
|
|
26
|
-
onChange,
|
|
27
|
-
minDate,
|
|
28
|
-
maxDate,
|
|
29
|
-
shouldDisableDate,
|
|
30
|
-
locale = 'default',
|
|
31
|
-
weekStartDay = 0,
|
|
32
|
-
className,
|
|
33
|
-
style,
|
|
34
|
-
mode = 'single',
|
|
35
|
-
...restProps
|
|
36
|
-
}: ReactProps<DatePickerInterface>) => {
|
|
37
|
-
// State for the currently displayed month (always set to the 1st of the month)
|
|
38
|
-
const [viewDate, setViewDate] = useState(() => {
|
|
39
|
-
// Try to find a valid start date from value to focus
|
|
40
|
-
const extractDate = (v: any): Date | null => {
|
|
41
|
-
if (v instanceof Date) return v;
|
|
42
|
-
if (Array.isArray(v) && v[0]) return v[0];
|
|
43
|
-
return null;
|
|
44
|
-
};
|
|
45
|
-
const start =
|
|
46
|
-
extractDate(valueProp) || extractDate(defaultValue) || new Date();
|
|
47
|
-
return new Date(start.getFullYear(), start.getMonth(), 1);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
const [direction, setDirection] = useState(0);
|
|
51
|
-
const [viewMode, setViewMode] = useState<'day' | 'year'>('day');
|
|
52
|
-
|
|
53
|
-
// State for selected date
|
|
54
|
-
const isControlled = valueProp !== undefined;
|
|
55
|
-
const [internalValue, setInternalValue] = useState<Date | DateRange | null>(
|
|
56
|
-
defaultValue || null,
|
|
57
|
-
);
|
|
58
|
-
const selectedValue = isControlled ? valueProp || null : internalValue;
|
|
59
|
-
|
|
60
|
-
// Calendar generation logic
|
|
61
|
-
const daysInMonth = (year: number, month: number) =>
|
|
62
|
-
new Date(year, month + 1, 0).getDate();
|
|
63
|
-
|
|
64
|
-
const calendarDays = useMemo(() => {
|
|
65
|
-
const year = viewDate.getFullYear();
|
|
66
|
-
const month = viewDate.getMonth();
|
|
67
|
-
const daysCount = daysInMonth(year, month);
|
|
68
|
-
const startDay = new Date(year, month, 1).getDay(); // 0=Sun (Fixed JS getDay)
|
|
69
|
-
|
|
70
|
-
// Adjust start index based on weekStartDay
|
|
71
|
-
// shift: logic to map standard JS Day (0=Sun) to our week start
|
|
72
|
-
// If weekStart=1 (Mon): Sun(0) -> 6, Mon(1) -> 0, Tue(2) -> 1
|
|
73
|
-
const startIndex = (startDay - weekStartDay + 7) % 7;
|
|
74
|
-
|
|
75
|
-
const days: Array<{ date: Date; isCurrentMonth: boolean }> = [];
|
|
76
|
-
|
|
77
|
-
// Prev month
|
|
78
|
-
const prevMonthDaysCount = daysInMonth(year, month - 1);
|
|
79
|
-
for (let i = startIndex - 1; i >= 0; i--) {
|
|
80
|
-
days.push({
|
|
81
|
-
date: new Date(year, month - 1, prevMonthDaysCount - i),
|
|
82
|
-
isCurrentMonth: false,
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Current month
|
|
87
|
-
for (let i = 1; i <= daysCount; i++) {
|
|
88
|
-
days.push({ date: new Date(year, month, i), isCurrentMonth: true });
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Next month padding - Ensure always 42 days (6 rows) for fixed height animation
|
|
92
|
-
const currentLen = days.length;
|
|
93
|
-
const remaining = 42 - currentLen;
|
|
94
|
-
for (let i = 1; i <= remaining; i++) {
|
|
95
|
-
days.push({
|
|
96
|
-
date: new Date(year, month + 1, i),
|
|
97
|
-
isCurrentMonth: false,
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
return days;
|
|
102
|
-
}, [viewDate, weekStartDay]);
|
|
103
|
-
|
|
104
|
-
const years = useMemo(() => {
|
|
105
|
-
const currentYear = new Date().getFullYear();
|
|
106
|
-
const start = currentYear - 100;
|
|
107
|
-
const end = currentYear + 100;
|
|
108
|
-
const list = [];
|
|
109
|
-
for (let i = start; i <= end; i++) {
|
|
110
|
-
list.push(i);
|
|
111
|
-
}
|
|
112
|
-
return list;
|
|
113
|
-
}, []);
|
|
114
|
-
|
|
115
|
-
const yearsContainerRef = useRef<HTMLDivElement>(null);
|
|
116
|
-
|
|
117
|
-
// Scroll to selected year when opening year view
|
|
118
|
-
useEffect(() => {
|
|
119
|
-
if (viewMode === 'year' && yearsContainerRef.current) {
|
|
120
|
-
const selectedYearBtn = yearsContainerRef.current.querySelector(
|
|
121
|
-
'[data-selected="true"]',
|
|
122
|
-
);
|
|
123
|
-
if (selectedYearBtn) {
|
|
124
|
-
selectedYearBtn.scrollIntoView({ block: 'center' });
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}, [viewMode]);
|
|
128
|
-
|
|
129
|
-
// Formatters
|
|
130
|
-
const monthFormatter = useMemo(
|
|
131
|
-
() => new Intl.DateTimeFormat(locale, { month: 'long', year: 'numeric' }),
|
|
132
|
-
[locale],
|
|
133
|
-
);
|
|
134
|
-
const weekDayFormatter = useMemo(
|
|
135
|
-
() => new Intl.DateTimeFormat(locale, { weekday: 'narrow' }),
|
|
136
|
-
[locale],
|
|
137
|
-
);
|
|
138
|
-
|
|
139
|
-
const weekDays = useMemo(() => {
|
|
140
|
-
const baseDate = new Date(2023, 0, 1 + weekStartDay); // Jan 1 2023 was Sun. Jan (1+1)=2 is Mon.
|
|
141
|
-
return Array.from({ length: 7 }).map((_, i) => {
|
|
142
|
-
const d = new Date(baseDate);
|
|
143
|
-
d.setDate(baseDate.getDate() + i);
|
|
144
|
-
return weekDayFormatter.format(d).charAt(0).toUpperCase();
|
|
145
|
-
});
|
|
146
|
-
}, [weekDayFormatter, weekStartDay]);
|
|
147
|
-
|
|
148
|
-
// Handlers
|
|
149
|
-
const handlePrevMonth = () => {
|
|
150
|
-
setDirection(-1);
|
|
151
|
-
setViewDate((d) => new Date(d.getFullYear(), d.getMonth() - 1, 1));
|
|
152
|
-
};
|
|
153
|
-
const handleNextMonth = () => {
|
|
154
|
-
setDirection(1);
|
|
155
|
-
setViewDate((d) => new Date(d.getFullYear(), d.getMonth() + 1, 1));
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
const handleYearSelect = (year: number) => {
|
|
159
|
-
setViewDate((d) => new Date(year, d.getMonth(), 1));
|
|
160
|
-
setViewMode('day');
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
const isSameDay = (d1: Date | null | undefined, d2: Date) => {
|
|
164
|
-
if (!d1) return false;
|
|
165
|
-
return (
|
|
166
|
-
d1.getDate() === d2.getDate() &&
|
|
167
|
-
d1.getMonth() === d2.getMonth() &&
|
|
168
|
-
d1.getFullYear() === d2.getFullYear()
|
|
169
|
-
);
|
|
170
|
-
};
|
|
171
|
-
|
|
172
|
-
const isToday = (date: Date) => isSameDay(new Date(), date);
|
|
173
|
-
|
|
174
|
-
const handleDateClick = (date: Date) => {
|
|
175
|
-
let newValue: Date | DateRange | null = date;
|
|
176
|
-
|
|
177
|
-
if (mode === 'single') {
|
|
178
|
-
newValue = date;
|
|
179
|
-
// Single mode always sets date
|
|
180
|
-
} else {
|
|
181
|
-
// Range mode
|
|
182
|
-
const current = selectedValue as DateRange | null;
|
|
183
|
-
const [start, end] = Array.isArray(current) ? current : [null, null];
|
|
184
|
-
|
|
185
|
-
if (!start || (start && end)) {
|
|
186
|
-
// Start new range (if was simple date or full range)
|
|
187
|
-
newValue = [date, null];
|
|
188
|
-
} else {
|
|
189
|
-
// Complete range
|
|
190
|
-
if (date < start) {
|
|
191
|
-
newValue = [date, start];
|
|
192
|
-
} else {
|
|
193
|
-
newValue = [start, date];
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
if (!isControlled) {
|
|
199
|
-
setInternalValue(newValue);
|
|
200
|
-
}
|
|
201
|
-
if (onChange) {
|
|
202
|
-
onChange(newValue);
|
|
203
|
-
}
|
|
204
|
-
};
|
|
205
|
-
|
|
206
|
-
const checkSelection = (date: Date) => {
|
|
207
|
-
if (mode === 'single') {
|
|
208
|
-
return {
|
|
209
|
-
isSelected: isSameDay(selectedValue as Date, date),
|
|
210
|
-
isStart: false,
|
|
211
|
-
isEnd: false,
|
|
212
|
-
isInRange: false,
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
const safeRange = Array.isArray(selectedValue)
|
|
216
|
-
? selectedValue
|
|
217
|
-
: [selectedValue, null];
|
|
218
|
-
const [start, end] = safeRange as DateRange;
|
|
219
|
-
const isStart = isSameDay(start, date);
|
|
220
|
-
const isEnd = isSameDay(end, date);
|
|
221
|
-
|
|
222
|
-
// Check range
|
|
223
|
-
let isInRange = false;
|
|
224
|
-
if (start && end) {
|
|
225
|
-
// Simple range check (ignore time components for safety)
|
|
226
|
-
// Normalize to midnight for strict day comparison
|
|
227
|
-
const s = new Date(
|
|
228
|
-
start.getFullYear(),
|
|
229
|
-
start.getMonth(),
|
|
230
|
-
start.getDate(),
|
|
231
|
-
).getTime();
|
|
232
|
-
const e = new Date(
|
|
233
|
-
end.getFullYear(),
|
|
234
|
-
end.getMonth(),
|
|
235
|
-
end.getDate(),
|
|
236
|
-
).getTime();
|
|
237
|
-
const d = new Date(
|
|
238
|
-
date.getFullYear(),
|
|
239
|
-
date.getMonth(),
|
|
240
|
-
date.getDate(),
|
|
241
|
-
).getTime();
|
|
242
|
-
isInRange = d > s && d < e;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
return { isSelected: isStart || isEnd, isStart, isEnd, isInRange };
|
|
246
|
-
};
|
|
247
|
-
|
|
248
|
-
const styles = useDatePickerStyle({
|
|
249
|
-
hasSelected: !!selectedValue,
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
const variants = {
|
|
253
|
-
enter: (direction: number) => ({
|
|
254
|
-
x: direction > 0 ? '100%' : '-100%',
|
|
255
|
-
opacity: 0,
|
|
256
|
-
}),
|
|
257
|
-
center: {
|
|
258
|
-
x: 0,
|
|
259
|
-
opacity: 1,
|
|
260
|
-
},
|
|
261
|
-
exit: (direction: number) => ({
|
|
262
|
-
x: direction < 0 ? '100%' : '-100%',
|
|
263
|
-
opacity: 0,
|
|
264
|
-
}),
|
|
265
|
-
};
|
|
266
|
-
|
|
267
|
-
return (
|
|
268
|
-
<div
|
|
269
|
-
className={classNames(styles.datePicker, className)}
|
|
270
|
-
style={style}
|
|
271
|
-
{...(restProps as any)}
|
|
272
|
-
>
|
|
273
|
-
{/* Header */}
|
|
274
|
-
<div className={styles.header}>
|
|
275
|
-
<Button
|
|
276
|
-
variant="text"
|
|
277
|
-
disableTextMargins
|
|
278
|
-
size="small"
|
|
279
|
-
onClick={() => setViewMode((m) => (m === 'day' ? 'year' : 'day'))}
|
|
280
|
-
className="text-label-large font-bold capitalize text-on-surface hover:bg-surface-container-highest"
|
|
281
|
-
>
|
|
282
|
-
<span className="mr-2">
|
|
283
|
-
{viewMode === 'day'
|
|
284
|
-
? monthFormatter.format(viewDate)
|
|
285
|
-
: viewDate.getFullYear()}
|
|
286
|
-
</span>
|
|
287
|
-
<Icon
|
|
288
|
-
icon={faChevronDown}
|
|
289
|
-
className={classNames(
|
|
290
|
-
'w-3 h-3 transition-transform duration-200 inline',
|
|
291
|
-
viewMode === 'year' && 'rotate-180',
|
|
292
|
-
)}
|
|
293
|
-
/>
|
|
294
|
-
</Button>
|
|
295
|
-
|
|
296
|
-
{viewMode === 'day' && (
|
|
297
|
-
<div className="flex items-center">
|
|
298
|
-
<IconButton
|
|
299
|
-
size={'xSmall'}
|
|
300
|
-
allowShapeTransformation={false}
|
|
301
|
-
onClick={handlePrevMonth}
|
|
302
|
-
icon={faChevronLeft}
|
|
303
|
-
label="Previous month"
|
|
304
|
-
title={null}
|
|
305
|
-
/>
|
|
306
|
-
<IconButton
|
|
307
|
-
size={'xSmall'}
|
|
308
|
-
allowShapeTransformation={false}
|
|
309
|
-
onClick={handleNextMonth}
|
|
310
|
-
icon={faChevronRight}
|
|
311
|
-
label="Next month"
|
|
312
|
-
title={null}
|
|
313
|
-
/>
|
|
314
|
-
</div>
|
|
315
|
-
)}
|
|
316
|
-
</div>
|
|
317
|
-
|
|
318
|
-
{viewMode === 'year' ? (
|
|
319
|
-
<div
|
|
320
|
-
className="h-[280px] overflow-y-auto grid grid-cols-3 gap-2 p-2 scrollbar-hide"
|
|
321
|
-
ref={yearsContainerRef}
|
|
322
|
-
>
|
|
323
|
-
{years.map((year) => (
|
|
324
|
-
<Button
|
|
325
|
-
size={'small'}
|
|
326
|
-
key={year}
|
|
327
|
-
variant={year === viewDate.getFullYear() ? 'filled' : 'text'}
|
|
328
|
-
onClick={() => handleYearSelect(year)}
|
|
329
|
-
data-selected={year === viewDate.getFullYear()}
|
|
330
|
-
className={classNames('w-full', {
|
|
331
|
-
'text-on-surface': year !== viewDate.getFullYear(),
|
|
332
|
-
})}
|
|
333
|
-
label={year.toString()}
|
|
334
|
-
>
|
|
335
|
-
{year}
|
|
336
|
-
</Button>
|
|
337
|
-
))}
|
|
338
|
-
</div>
|
|
339
|
-
) : (
|
|
340
|
-
<>
|
|
341
|
-
{/* Week Days */}
|
|
342
|
-
<div className={styles.weekDays}>
|
|
343
|
-
{weekDays.map((day, i) => (
|
|
344
|
-
<div key={i} className={styles.weekDay}>
|
|
345
|
-
{day}
|
|
346
|
-
</div>
|
|
347
|
-
))}
|
|
348
|
-
</div>
|
|
349
|
-
|
|
350
|
-
{/* Days Grid */}
|
|
351
|
-
<div className="overflow-hidden relative min-h-[240px]">
|
|
352
|
-
<AnimatePresence
|
|
353
|
-
mode="popLayout"
|
|
354
|
-
initial={false}
|
|
355
|
-
custom={direction}
|
|
356
|
-
>
|
|
357
|
-
<motion.div
|
|
358
|
-
key={viewDate.toISOString()}
|
|
359
|
-
custom={direction}
|
|
360
|
-
variants={variants}
|
|
361
|
-
initial="enter"
|
|
362
|
-
animate="center"
|
|
363
|
-
exit="exit"
|
|
364
|
-
transition={{ type: 'spring', bounce: 0, duration: 0.3 }}
|
|
365
|
-
className={styles.daysGrid}
|
|
366
|
-
>
|
|
367
|
-
{calendarDays.map((item, index) => {
|
|
368
|
-
if (!item.isCurrentMonth) {
|
|
369
|
-
return <div key={index} className={styles.dayCell} />;
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
const { isSelected, isStart, isEnd, isInRange } =
|
|
373
|
-
checkSelection(item.date);
|
|
374
|
-
const isTodayDate = isToday(item.date);
|
|
375
|
-
const isDisabled =
|
|
376
|
-
(minDate && item.date < minDate) ||
|
|
377
|
-
(maxDate && item.date > maxDate) ||
|
|
378
|
-
shouldDisableDate?.(item.date);
|
|
379
|
-
|
|
380
|
-
return (
|
|
381
|
-
<div
|
|
382
|
-
key={index}
|
|
383
|
-
className={classNames(
|
|
384
|
-
styles.dayCell,
|
|
385
|
-
// Range background styles applied to the cell wrapper
|
|
386
|
-
isInRange && 'bg-primary/20',
|
|
387
|
-
isStart &&
|
|
388
|
-
(selectedValue as DateRange)?.[1] &&
|
|
389
|
-
'bg-gradient-to-r from-transparent to-primary/20',
|
|
390
|
-
isEnd &&
|
|
391
|
-
(selectedValue as DateRange)?.[0] &&
|
|
392
|
-
'bg-gradient-to-l from-transparent to-primary/20',
|
|
393
|
-
)}
|
|
394
|
-
>
|
|
395
|
-
<Button
|
|
396
|
-
className={() => ({
|
|
397
|
-
button: classNames('aspect-square h-[40px] p-0', {
|
|
398
|
-
'text-on-surface': !isSelected && !isTodayDate,
|
|
399
|
-
'opacity-50': isDisabled,
|
|
400
|
-
}),
|
|
401
|
-
stateLayer: classNames({
|
|
402
|
-
'!bg-transparent': isDisabled,
|
|
403
|
-
}),
|
|
404
|
-
})}
|
|
405
|
-
size="small"
|
|
406
|
-
allowShapeTransformation={false}
|
|
407
|
-
variant={
|
|
408
|
-
classNames({
|
|
409
|
-
filled: isSelected,
|
|
410
|
-
outlined: isTodayDate,
|
|
411
|
-
text: !isSelected && !isTodayDate,
|
|
412
|
-
}) as any
|
|
413
|
-
}
|
|
414
|
-
label={item.date.getDate().toString()}
|
|
415
|
-
onClick={() => handleDateClick(item.date)}
|
|
416
|
-
disabled={isDisabled}
|
|
417
|
-
>
|
|
418
|
-
{item.date.getDate().toString()}
|
|
419
|
-
</Button>
|
|
420
|
-
</div>
|
|
421
|
-
);
|
|
422
|
-
})}
|
|
423
|
-
</motion.div>
|
|
424
|
-
</AnimatePresence>
|
|
425
|
-
</div>
|
|
426
|
-
</>
|
|
427
|
-
)}
|
|
428
|
-
</div>
|
|
429
|
-
);
|
|
430
|
-
};
|
|
431
|
-
|
|
432
|
-
// Helper for generic check - removed unused
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { useDividerStyle } from '../styles';
|
|
2
|
-
import { DividerInterface } from '../interfaces';
|
|
3
|
-
import { ReactProps } from '../utils';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Dividers are thin lines that group content in lists or other containers
|
|
7
|
-
* @status beta
|
|
8
|
-
* @category Layout
|
|
9
|
-
* @devx
|
|
10
|
-
* - Renders a semantic `<hr>`; use `orientation` for vertical dividers.
|
|
11
|
-
*/
|
|
12
|
-
export const Divider = ({
|
|
13
|
-
orientation = 'horizontal',
|
|
14
|
-
className,
|
|
15
|
-
...restProps
|
|
16
|
-
}: ReactProps<DividerInterface>) => {
|
|
17
|
-
const styles = useDividerStyle({ orientation, className });
|
|
18
|
-
|
|
19
|
-
return <hr className={styles.divider} {...restProps} />;
|
|
20
|
-
};
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import React, { useRef } from 'react';
|
|
2
|
-
import { Icon } from '../icon';
|
|
3
|
-
import { AnimatePresence, motion } from 'motion/react';
|
|
4
|
-
import { FabInterface } from '../interfaces/fab.interface';
|
|
5
|
-
import { useFabStyle } from '../styles/fab.style';
|
|
6
|
-
import { classNames } from '../utils';
|
|
7
|
-
import { ReactProps } from '../utils/component';
|
|
8
|
-
import { Tooltip } from './Tooltip';
|
|
9
|
-
import { State } from '../effects';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Floating action buttons (FABs) help people take primary actions
|
|
13
|
-
* @status beta
|
|
14
|
-
* @category Action
|
|
15
|
-
* @devx
|
|
16
|
-
* - Requires `label` or children; icon-only still needs a label for a11y.
|
|
17
|
-
* @limitations
|
|
18
|
-
* - No built-in positioning; placement is handled by the layout.
|
|
19
|
-
*/
|
|
20
|
-
export const Fab = ({
|
|
21
|
-
className,
|
|
22
|
-
label,
|
|
23
|
-
variant = 'primary',
|
|
24
|
-
size = 'medium',
|
|
25
|
-
href,
|
|
26
|
-
type,
|
|
27
|
-
icon,
|
|
28
|
-
extended = false,
|
|
29
|
-
ref,
|
|
30
|
-
transition,
|
|
31
|
-
children,
|
|
32
|
-
...restProps
|
|
33
|
-
}: ReactProps<FabInterface>) => {
|
|
34
|
-
if (children) label = children;
|
|
35
|
-
if (!label) {
|
|
36
|
-
throw new Error(
|
|
37
|
-
'FAB component requires either a label prop or children content',
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
const ElementType = href ? 'a' : 'button';
|
|
41
|
-
|
|
42
|
-
const styles = useFabStyle({
|
|
43
|
-
href,
|
|
44
|
-
icon,
|
|
45
|
-
extended,
|
|
46
|
-
label,
|
|
47
|
-
size,
|
|
48
|
-
variant,
|
|
49
|
-
className,
|
|
50
|
-
transition,
|
|
51
|
-
children: label,
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
transition = { duration: 0.3, ...transition };
|
|
55
|
-
|
|
56
|
-
const defaultRef = useRef(null);
|
|
57
|
-
const resolvedRef = ref || defaultRef;
|
|
58
|
-
|
|
59
|
-
const labelVariants = {
|
|
60
|
-
visible: {
|
|
61
|
-
width: 'auto',
|
|
62
|
-
marginLeft: 12,
|
|
63
|
-
opacity: 1,
|
|
64
|
-
transition: {
|
|
65
|
-
...transition,
|
|
66
|
-
opacity: {
|
|
67
|
-
duration: transition.duration! / 2,
|
|
68
|
-
delay: transition.duration! - transition.duration! / 2,
|
|
69
|
-
},
|
|
70
|
-
},
|
|
71
|
-
},
|
|
72
|
-
hidden: {
|
|
73
|
-
width: 0,
|
|
74
|
-
marginLeft: 0,
|
|
75
|
-
opacity: 0,
|
|
76
|
-
transition: {
|
|
77
|
-
...transition,
|
|
78
|
-
marginLeft: {
|
|
79
|
-
duration: transition.duration! / 2,
|
|
80
|
-
delay: transition.duration! - transition.duration! / 2,
|
|
81
|
-
},
|
|
82
|
-
},
|
|
83
|
-
},
|
|
84
|
-
};
|
|
85
|
-
return (
|
|
86
|
-
<ElementType
|
|
87
|
-
{...(restProps as any)}
|
|
88
|
-
ref={resolvedRef}
|
|
89
|
-
href={href}
|
|
90
|
-
aria-label={extended ? undefined : label}
|
|
91
|
-
className={styles.fab}
|
|
92
|
-
>
|
|
93
|
-
<Tooltip
|
|
94
|
-
trigger={extended ? null : undefined}
|
|
95
|
-
text={label}
|
|
96
|
-
targetRef={resolvedRef}
|
|
97
|
-
/>
|
|
98
|
-
<State
|
|
99
|
-
style={{ transition: transition.duration + 's' }}
|
|
100
|
-
className={styles.stateLayer}
|
|
101
|
-
colorName={classNames({
|
|
102
|
-
'on-primary': variant == 'primary',
|
|
103
|
-
'on-secondary': variant == 'secondary',
|
|
104
|
-
'on-tertiary': variant == 'tertiary',
|
|
105
|
-
'on-primary-container': variant == 'primaryContainer',
|
|
106
|
-
'on-secondary-container': variant == 'secondaryContainer',
|
|
107
|
-
'on-tertiary-container': variant == 'tertiaryContainer',
|
|
108
|
-
})}
|
|
109
|
-
stateClassName={'state-ripple-group-[fab]'}
|
|
110
|
-
/>
|
|
111
|
-
<Icon icon={icon} className={styles.icon} />
|
|
112
|
-
<AnimatePresence>
|
|
113
|
-
{extended && (
|
|
114
|
-
<motion.span
|
|
115
|
-
variants={labelVariants}
|
|
116
|
-
initial="hidden"
|
|
117
|
-
animate="visible"
|
|
118
|
-
exit="hidden"
|
|
119
|
-
className={styles.label}
|
|
120
|
-
>
|
|
121
|
-
{label}
|
|
122
|
-
</motion.span>
|
|
123
|
-
)}
|
|
124
|
-
</AnimatePresence>
|
|
125
|
-
</ElementType>
|
|
126
|
-
);
|
|
127
|
-
};
|