phx-react
Version:
PHX REACT
137 lines • 11.8 kB
JavaScript
import { Menu, Transition } from '@headlessui/react';
import { ChevronDownIcon, ChevronUpIcon, EllipsisHorizontalIcon } from '@heroicons/react/20/solid';
import * as OutlineIcons from '@heroicons/react/24/outline';
import * as SolidIcons from '@heroicons/react/24/solid';
import React, { Fragment, useEffect, useRef, useState } from 'react';
import ReactDOM from 'react-dom';
import { classNames } from '../types';
import Calendar from '../DatePicker/components/Calendar';
import { PHXSpinner } from '../Loading';
export function PHXDropdown(_a) {
var buttonContent = _a.buttonContent, className = _a.className, defaultDate = _a.defaultDate, disabledDate = _a.disabledDate, endDisabled = _a.endDisabled, loading = _a.loading, max = _a.max, min = _a.min, onChangeDate = _a.onChangeDate, optionTitle = _a.optionTitle, options = _a.options, _b = _a.origin, origin = _b === void 0 ? 'left' : _b, _c = _a.soft, soft = _c === void 0 ? false : _c, startDisabled = _a.startDisabled, _d = _a.type, type = _d === void 0 ? 'default' : _d, widthContent = _a.widthContent, _e = _a.isFullContentOption, isFullContentOption = _e === void 0 ? false : _e, _f = _a.disabled, disabled = _f === void 0 ? false : _f;
var _g = useState(null), portalElement = _g[0], setPortalElement = _g[1];
var menuButtonRef = useRef(null);
var scrollContainerRef = useRef(null);
var handleCloseRef = useRef();
useEffect(function () {
var element = document.createElement('div');
document.body.appendChild(element);
setPortalElement(element);
return function () {
document.body.removeChild(element);
};
}, []);
useEffect(function () {
// Tìm phần tử cha có scroll
var findScrollContainer = function (element) {
while (element) {
var overflowY = window.getComputedStyle(element).overflowY;
if (overflowY === 'scroll' || overflowY === 'auto') {
return element;
}
element = element.parentElement;
}
return null;
};
if (menuButtonRef.current) {
scrollContainerRef.current = findScrollContainer(menuButtonRef.current);
}
}, []);
var calculatePosition = function () {
if (menuButtonRef.current && portalElement) {
var buttonRect = menuButtonRef.current.getBoundingClientRect();
portalElement.style.position = 'absolute';
switch (origin) {
case 'left':
portalElement.style.top = "".concat(buttonRect.bottom + window.scrollY, "px");
portalElement.style.left = "".concat(buttonRect.left + window.scrollX, "px");
break;
case 'right':
portalElement.style.top = "".concat(buttonRect.bottom + window.scrollY, "px");
portalElement.style.right = "".concat(window.innerWidth - buttonRect.right, "px");
break;
case 'top-left':
portalElement.style.top = "".concat(buttonRect.bottom + window.scrollY, "px");
portalElement.style.bottom = "".concat(window.innerHeight - buttonRect.top, "px");
portalElement.style.left = "".concat(buttonRect.left + window.scrollX, "px");
break;
default:
portalElement.style.top = "".concat(buttonRect.bottom + window.scrollY, "px");
portalElement.style.left = "".concat(buttonRect.left + window.scrollX, "px");
}
}
};
useEffect(function () {
if (portalElement) {
calculatePosition();
}
}, [portalElement]);
useEffect(function () {
var handleScrollAndResize = function (event) {
calculatePosition();
var isScrollInsideDropdown = event.target instanceof Node && (portalElement === null || portalElement === void 0 ? void 0 : portalElement.contains(event.target));
if (!isScrollInsideDropdown && (handleCloseRef === null || handleCloseRef === void 0 ? void 0 : handleCloseRef.current)) {
handleCloseRef.current();
}
};
window.addEventListener('scroll', handleScrollAndResize, true);
window.addEventListener('resize', handleScrollAndResize);
if (scrollContainerRef.current) {
scrollContainerRef.current.addEventListener('scroll', handleScrollAndResize);
}
return function () {
window.removeEventListener('scroll', handleScrollAndResize, true);
window.removeEventListener('resize', handleScrollAndResize);
if (scrollContainerRef.current) {
scrollContainerRef.current.removeEventListener('scroll', handleScrollAndResize);
}
};
}, [portalElement]);
var Icon = function (_a) {
var active = _a.active, icon = _a.icon, iconOutline = _a.iconOutline;
// @ts-ignore
var Icon = iconOutline ? OutlineIcons[icon] : SolidIcons[icon];
return React.createElement(Icon, { className: classNames('h-4 w-4', active && 'font-semibold') });
};
var renderMenuItems = function () { return (React.createElement(Transition, { as: Fragment, enter: 'transition ease-out duration-100', enterFrom: 'transform opacity-0 scale-95', enterTo: 'transform opacity-100 scale-100', leave: 'transition ease-in duration-75', leaveFrom: 'transform opacity-100 scale-100', leaveTo: 'transform opacity-0 scale-95' },
React.createElement(Menu.Items, { className: classNames(origin === 'left' && 'left-0 origin-top-left', origin === 'right' && 'right-0 origin-top-right', origin === 'top-left' && 'bottom-8 left-0', type === 'date-picker' ? '' : 'border px-1', 'absolute z-10 mt-1.5 max-h-96 min-w-[7.2rem] overflow-y-auto overscroll-contain whitespace-nowrap rounded-lg bg-white shadow-[0rem_0.25rem_0.375rem_-0.125rem_rgba(26,26,26,.2)] focus:outline-none') }, type === 'date-picker' ? (React.createElement(Calendar, { defaultDate: defaultDate, disabledDate: disabledDate, endDisabled: endDisabled, max: max, min: min, onChangeDate: onChangeDate, startDisabled: startDisabled })) : (React.createElement(React.Fragment, null,
optionTitle && React.createElement("p", { className: 'px-2.5 pb-1 pt-2 text-xs font-semibold' }, optionTitle),
React.createElement("div", { className: 'py-1' }, options === null || options === void 0 ? void 0 : options.map(function (item) { return (React.createElement(Menu.Item, { key: item.content }, function (_a) {
var active = _a.active;
return (React.createElement("div", { className: classNames(active ? 'cursor-pointer rounded-lg bg-gray-100 text-gray-900' : 'text-gray-700', 'block px-2.5 py-2 text-xs', item.active && !item.destructive && 'rounded-lg bg-gray-200 font-semibold text-gray-900', item.destructive && !item.active && 'text-red-800 hover:bg-red-100', item.destructive && item.active && 'rounded-lg bg-red-200 font-semibold text-red-800'), onClick: item.onClick },
React.createElement("div", { className: classNames(item.icon && 'flex items-center gap-x-2') },
item.icon && React.createElement(Icon, { active: item.active, icon: item.icon, iconOutline: item.iconOutline }),
React.createElement("p", { className: "".concat(widthContent, " overflow-ellipsis break-words ").concat(isFullContentOption ? 'whitespace-normal' : '') }, item.content))));
})); }))))))); };
return (React.createElement("div", { className: classNames('relative max-w-full md:max-w-fit', type === 'ellipsis-icon' && 'flex items-center') },
React.createElement(Menu, { as: 'div', className: classNames('relative inline-block w-full text-left', className && "".concat(className), type === 'ellipsis-icon' && 'flex items-center') }, function (_a) {
var close = _a.close, open = _a.open;
open && calculatePosition();
handleCloseRef.current = close;
return (React.createElement(React.Fragment, null,
React.createElement("div", null,
React.createElement(Menu.Button, { disabled: disabled, ref: menuButtonRef, className: classNames('w-full', disabled
? 'relative rounded-lg bg-indigo-50 px-2.5 pb-[0.42rem] pt-[0.45rem] text-xs font-normal shadow-sm'
: (type === 'default' || type === 'date-picker') &&
!soft &&
"rounded-lg border border-gray-300 border-b-gray-400 bg-white px-2.5 py-1.5 text-xs font-normal text-gray-900 shadow-sm hover:bg-gray-50 active:border-b-gray-100 active:bg-gray-100 active:pb-[0.3rem] active:pt-[0.45rem] active:shadow-[0rem_0.125rem_0.1rem_0rem_#0004_inset] ".concat(open
? '!border-b-gray-100 !bg-gray-100 !shadow-[0rem_0.125rem_0.1rem_0rem_#0004_inset]'
: ''), type === 'icon-only' &&
!soft &&
'flex items-center rounded-full bg-gray-100 text-gray-600 hover:text-gray-900 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 focus:ring-offset-gray-100', soft &&
"rounded-lg bg-indigo-50 px-2.5 py-1.5 text-xs font-normal text-gray-700 shadow-sm hover:bg-indigo-100 active:pb-[0.3rem] active:pt-[0.45rem] active:shadow-[0rem_0.125rem_0.1rem_0rem_#0004_inset] ".concat(open ? '!bg-indigo-100 shadow-[0rem_0.125rem_0.1rem_0rem_#0004_inset]' : ''), type === 'soft-small' &&
"rounded-lg bg-gray-200 px-2 py-1 text-xs hover:bg-gray-300 active:pb-[0.2rem] active:pt-[0.35rem] active:shadow-[0rem_0.125rem_0.1rem_0rem_#0004_inset]\n ".concat(open ? '!bg-gray-300 shadow-[0rem_0.125rem_0.1rem_0rem_#0004_inset]' : '', "\n "), type === 'ellipsis-icon' && 'flex items-center') },
React.createElement("div", { className: classNames((type === 'default' || type === 'date-picker' || type === 'soft-small') &&
"flex items-center justify-between gap-x-1 ".concat(disabled ? 'text-gray-400' : 'text-gray-900', " md:justify-center"), type === 'ellipsis-icon' && 'flex items-center') },
React.createElement("p", { className: classNames('max-w-[200px] truncate', type === 'icon-only' && 'sr-only') }, buttonContent),
(type === 'default' || type === 'date-picker' || type === 'soft-small') &&
(open && origin === 'top-left' ? (React.createElement(ChevronUpIcon, { "aria-hidden": 'true', className: 'mt-0.5 h-4 w-4' })) : (React.createElement(ChevronDownIcon, { "aria-hidden": 'true', className: 'mt-0.5 h-4 w-4' }))),
type === 'icon-only' && React.createElement(EllipsisHorizontalIcon, { "aria-hidden": 'true', className: 'h-4 w-4' }),
type === 'ellipsis-icon' && (React.createElement("div", { className: 'rounded-lg p-1 hover:bg-gray-300' },
React.createElement(EllipsisHorizontalIcon, { "aria-hidden": 'true', className: 'h-4 w-4' })))))),
portalElement ? ReactDOM.createPortal(renderMenuItems(), portalElement) : null));
}),
loading && (React.createElement("div", { className: 'absolute left-[1px] top-[1px] flex h-[calc(100%-2px)] w-[calc(100%-2px)] items-center justify-center rounded-lg bg-white' },
React.createElement(PHXSpinner, { className: 'h-[16px] w-[16px] text-gray-400' })))));
}
//# sourceMappingURL=Dropdown.js.map