phx-react
Version:
PHX REACT
168 lines • 10.1 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const react_1 = tslib_1.__importStar(require("react"));
const Button_1 = require("../Button");
const types_1 = require("../types");
const defaultDaysOfWeek = ['Chủ nhật', 'Thứ 2', 'Thứ 3', 'Thứ 4', 'Thứ 5', 'Thứ 6', 'Thứ 7'];
const PHXTabDate = ({ daysOfWeek = defaultDaysOfWeek, hideTodayButton = false, onChangeTime, timeline: timelineProp, windowSize = 8, showRightArrowButton = false, selectedDate, }) => {
var _a;
const containerRef = (0, react_1.useRef)(null);
const [isScrollX, setIsScrollX] = (0, react_1.useState)(false);
// Dùng ref để tránh onChangeTime gây infinite loop khi cha khai báo inline
const onChangeTimeRef = (0, react_1.useRef)(onChangeTime);
(0, react_1.useEffect)(() => {
onChangeTimeRef.current = onChangeTime;
}, [onChangeTime]);
// Ref để track lần đầu mount, tránh gọi onChangeTime khi init
const isFirstMount = (0, react_1.useRef)(true);
const buildDefaultTimeline = () => {
function getPreviousDays(baseDate, n) {
const result = [];
for (let i = n; i >= 1; i--) {
const d = new Date(baseDate);
d.setDate(baseDate.getDate() - i);
const dayOfWeek = d.getDay();
result.push({
title: daysOfWeek[dayOfWeek],
date: `${d.getDate()}/${d.getMonth() + 1}`,
time: d,
});
}
return result;
}
const today = new Date();
const past = getPreviousDays(today, 30);
const todayDayOfWeek = today.getDay();
past.push({
title: daysOfWeek[todayDayOfWeek],
date: `${today.getDate()}/${today.getMonth() + 1}`,
time: today,
});
return past;
};
const timeline = timelineProp || buildDefaultTimeline();
const isHaveOnlyOneOption = timeline.length === 1;
const [todayIndex, setTodayIndex] = (0, react_1.useState)(-1);
const initialStartIndex = Math.max(todayIndex - (windowSize - 1), 0);
const [startIndex, setStartIndex] = (0, react_1.useState)(initialStartIndex);
const [activeIndex, setActiveIndex] = (0, react_1.useState)(todayIndex);
const visibleDays = timeline.slice(startIndex, startIndex + windowSize);
function handleCheckActive(newActiveIndex) {
setActiveIndex(newActiveIndex);
}
const handlePrevClick = () => {
if (startIndex - 1 >= 0) {
setStartIndex((pre) => pre - 1);
}
handleCheckActive(activeIndex - 1);
};
const handleNextClick = () => {
if (startIndex + windowSize < timeline.length) {
setStartIndex((prev) => prev + 1);
}
handleCheckActive(activeIndex + 1);
};
const handleClickToday = () => {
var _a;
setActiveIndex(todayIndex);
const newStartIndex = Math.max(todayIndex - (windowSize - 1), 0);
setStartIndex(newStartIndex);
(_a = onChangeTimeRef.current) === null || _a === void 0 ? void 0 : _a.call(onChangeTimeRef, new Date());
};
// Sync activeIndex khi selectedDate hoặc timeline thay đổi từ bên ngoài
(0, react_1.useEffect)(() => {
if (selectedDate) {
const selectedIndex = timeline.findIndex((t) => t.date === selectedDate);
if (selectedIndex !== -1) {
setActiveIndex(selectedIndex);
const isVisible = selectedIndex >= startIndex && selectedIndex < startIndex + windowSize;
if (!isVisible) {
const newStart = Math.max(selectedIndex - Math.floor(windowSize / 2), 0);
const safeStart = Math.min(newStart, timeline.length - windowSize);
setStartIndex(Math.max(0, safeStart));
}
}
return;
}
const newTodayIndex = timeline.findIndex((t) => t.time.toDateString() === new Date().toDateString());
setActiveIndex(newTodayIndex);
setTodayIndex(newTodayIndex);
setStartIndex(Math.max(newTodayIndex - (windowSize - 1), 0));
}, [selectedDate, timeline.length]);
// FIX: Bỏ onChangeTime khỏi dependency array, dùng ref thay thế
(0, react_1.useEffect)(() => {
const activeItem = timeline[activeIndex];
if (!activeItem || !onChangeTimeRef.current)
return;
if (isFirstMount.current) {
isFirstMount.current = false;
return;
}
onChangeTimeRef.current(activeItem.time);
}, [(_a = timeline[activeIndex]) === null || _a === void 0 ? void 0 : _a.date]);
(0, react_1.useEffect)(() => {
const el = containerRef.current;
if (!el)
return;
const checkScrollX = () => {
const hasScrollX = el.scrollWidth > el.clientWidth;
setIsScrollX(hasScrollX);
};
checkScrollX();
window.addEventListener('resize', checkScrollX);
return () => {
window.removeEventListener('resize', checkScrollX);
};
}, []);
return (react_1.default.createElement("div", null,
!hideTodayButton && (react_1.default.createElement(Button_1.PHXButton, { className: 'mb-3', iconPosition: 'before', onClick: handleClickToday, secondary: true, size: 'extra-micro', typeIcon: 'calendar' }, "H\u00F4m nay")),
react_1.default.createElement("div", { className: 'flex w-full items-start' },
!isHaveOnlyOneOption && (react_1.default.createElement("button", { style: {
height: isScrollX ? '58px' : '50px',
}, className: '\n mr-1 flex w-[1.875rem] flex-shrink-0 items-center justify-center \n rounded-lg border border-gray-300 bg-white hover:bg-gray-200\n disabled:cursor-not-allowed disabled:bg-gray-100 disabled:text-gray-400\n ', disabled: activeIndex === 0, onClick: handlePrevClick },
react_1.default.createElement("svg", { fill: 'none', height: '24', stroke: 'currentColor', viewBox: '0 0 24 24', width: '24', xmlns: 'http://www.w3.org/2000/svg', className: 'w-4 h-4' },
react_1.default.createElement("path", { d: 'M15 18L9 12L15 6', strokeLinecap: 'round', strokeLinejoin: 'round', strokeWidth: '2' })))),
react_1.default.createElement("style", null, `
.scrollbar-thin::-webkit-scrollbar {
width: 8px;
height: 8px;
}
.scrollbar-thin::-webkit-scrollbar-thumb {
background: rgb(199 199 199);
border-radius: 16px;
border: 2px solid transparent;
background-clip: content-box;
}
.shadow-container {
box-shadow:
0rem 0.3125rem 0.3125rem -0.15625rem rgba(0, 0, 0, 0.03),
0rem 0.1875rem 0.1875rem -0.09375rem rgba(0, 0, 0, 0.02),
0rem 0.125rem 0.125rem -0.0625rem rgba(0, 0, 0, 0.02),
0rem 0.0625rem 0.0625rem -0.03125rem rgba(0, 0, 0, 0.03),
0rem 0.03125rem 0.03125rem 0rem rgba(0, 0, 0, 0.04),
0rem 0rem 0rem 0.0625rem rgba(0, 0, 0, 0.06);
}
`),
react_1.default.createElement("div", { ref: containerRef, className: (0, types_1.classNames)('overflow-x-auto scrollbar-thin rounded-lg shadow-container', isHaveOnlyOneOption ? 'w-[7.5rem]' : ' flex-1') },
react_1.default.createElement("div", { className: (0, types_1.classNames)('flex h-[3.125rem] overflow-y-hidden', isHaveOnlyOneOption ? 'w-[7.5rem]' : 'min-w-max') }, visibleDays.map((item, index) => {
const globalIndex = startIndex + index;
const isActive = activeIndex === globalIndex;
return (react_1.default.createElement("div", { key: globalIndex, className: 'w-full h-[3.125rem] min-w-[7.5rem]' },
react_1.default.createElement("button", { className: `${index === 0 ? '' : 'border-l'}
h-full w-full border-gray-300 bg-white px-1.5 py-1 hover:bg-gray-100`, onClick: () => {
setActiveIndex(globalIndex);
} }, isActive ? (react_1.default.createElement("div", { className: 'flex h-full w-full flex-col items-center justify-center rounded-lg bg-gray-900 text-white' },
react_1.default.createElement("span", { className: 'text-xxs' }, item.title),
react_1.default.createElement("p", { className: 'text-xs font-medium' }, item.date))) : (react_1.default.createElement("div", { className: 'flex h-full w-full flex-col items-center justify-center' },
react_1.default.createElement("span", { className: 'text-xxs text-gray-600' }, item.title),
react_1.default.createElement("p", { className: 'text-xs font-medium text-gray-900' }, item.date))))));
}))),
(startIndex + windowSize < timeline.length || showRightArrowButton) && !isHaveOnlyOneOption && (react_1.default.createElement("button", { style: {
height: isScrollX ? '58px' : '50px',
}, className: 'ml-1 flex w-[1.875rem] flex-shrink-0 items-center justify-center \n rounded-lg border border-gray-300 bg-white hover:bg-gray-200 disabled:cursor-not-allowed disabled:bg-gray-100 disabled:text-gray-400', onClick: handleNextClick, disabled: activeIndex >= timeline.length - 1 },
react_1.default.createElement("svg", { fill: 'none', height: '24', viewBox: '0 0 24 24', width: '24', xmlns: 'http://www.w3.org/2000/svg', className: 'w-4 h-4' },
react_1.default.createElement("path", { d: 'M9 18L15 12L9 6', stroke: 'currentColor', strokeLinecap: 'round', strokeLinejoin: 'round', strokeWidth: '2' })))))));
};
exports.default = PHXTabDate;
//# sourceMappingURL=PHXTabDate.js.map