UNPKG

@aristobyte-ui/dropdown

Version:

react dropdown component with trigger button, dropdownoptions, placement variants, fully typed typescript support, and composable integration with aristobyte ui button

239 lines (235 loc) 8.75 kB
"use client"; "use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // index.ts var index_exports = {}; __export(index_exports, { Dropdown: () => Dropdown, DropdownOption: () => DropdownOption }); module.exports = __toCommonJS(index_exports); // components/Dropdown/index.tsx var React2 = __toESM(require("react")); var import_framer_motion = require("framer-motion"); // components/DropdownOption/index.tsx var React = __toESM(require("react")); var import_utils = require("@aristobyte-ui/utils"); var import_jsx_runtime = require("react/jsx-runtime"); var DropdownOption = ({ variant, children, value, selectedValues, onChange, // icon, description, disabled, style = {} }) => { const uniqueId = React.useId(); return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)( "button", { style, disabled, className: `dropdown-option dropdown-option-variant--${variant} ${disabled ? "dropdown-option--disabled" : ""}`, onClick: onChange, children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dropdown-option__content", children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { className: "dropdown-option__title", children }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dropdown-option__description", children: description }) ] }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)( "div", { className: `dropdown-option__tick ${(selectedValues == null ? void 0 : selectedValues.includes(value)) ? "dropdown-option__tick--active" : ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_utils.Icons.Success, { size: 18 }) } ) ] }, uniqueId ); }; // components/Dropdown/index.tsx var import_button = require("@aristobyte-ui/button"); var import_utils2 = require("@aristobyte-ui/utils"); var import_jsx_runtime2 = require("react/jsx-runtime"); var import_react = require("react"); var Dropdown = ({ children, value, onChange, appearance = "outline", variant = "default", placeholder = "Select", choice = "single", className = "", initiallyOpened = false, disabled = false, button = {}, style = {} }) => { const [isOpened, setIsOpened] = React2.useState(initiallyOpened); const [selected, setSelected] = React2.useState( value ? [value] : [] ); const [position, setPosition] = React2.useState({ top: 0, left: 0, width: 0 }); const [dropdownHeight, setDropdownHeight] = React2.useState(0); const [buttonHeight, setButtonHeight] = React2.useState(0); const buttonRef = React2.useRef(null); const boxRef = React2.useRef(null); const uniqueId = React2.useId(); React2.useLayoutEffect(() => { if (!isOpened) { return; } if (boxRef.current) { setDropdownHeight(boxRef.current.getBoundingClientRect().height); } if (buttonRef.current) { setButtonHeight(buttonRef.current.getBoundingClientRect().height); } }, [isOpened]); const options = React2.Children.toArray(children).filter( (child) => React2.isValidElement(child) && child.type === DropdownOption ); const isValidValue = () => { return !!options.find(({ props }) => props.value === value); }; const handleChange = (currentRadioValue) => { onChange == null ? void 0 : onChange(currentRadioValue); if (!choice) { setSelected([currentRadioValue]); setIsOpened(false); return; } if (choice === "single") { setSelected([currentRadioValue]); } if (choice === "multiple") { setSelected( (prev) => prev.includes(currentRadioValue) ? prev.filter((v) => v !== currentRadioValue) : [...prev, currentRadioValue] ); } }; const handleToggle = (e) => { var _a; if (disabled) return; const rect = (_a = buttonRef.current) == null ? void 0 : _a.getBoundingClientRect(); if (!rect) return; const spaceBelow = window.innerHeight - rect.bottom; const spaceAbove = rect.top; const shouldOpenUpwards = dropdownHeight > 0 && spaceBelow < dropdownHeight && spaceAbove > dropdownHeight; const finalPosition = { top: shouldOpenUpwards ? rect.top + window.scrollY - dropdownHeight - buttonHeight / 2 : rect.top + window.scrollY + buttonHeight + 6, left: rect.left + window.scrollX, width: rect.width }; setPosition(finalPosition); if (button == null ? void 0 : button.onClick) button.onClick(e); setIsOpened((prev) => !prev); }; if (!isValidValue()) { throw new Error( 'The "value" prop did not match with any of the DropdownOption "value" prop' ); } return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: `dropdown ${className}`, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( import_button.Button, { onClick: handleToggle, className: `${"dropdown__button"} ${(button == null ? void 0 : button.className) || ""}`, appearance: (button == null ? void 0 : button.appearance) || appearance, variant: (button == null ? void 0 : button.variant) || variant, disabled: (button == null ? void 0 : button.disabled) || disabled, ...{ ref: buttonRef }, children: placeholder } ) }), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_utils2.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_framer_motion.AnimatePresence, { children: isOpened && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)( "div", { className: `dropdown__box dropdown__box-variant--${variant}`, style, children: [ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( import_framer_motion.motion.div, { className: "dropdown__box-overlay", initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.3, ease: "easeIn" }, onClick: () => setIsOpened(false) } ), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( import_framer_motion.motion.div, { ref: boxRef, className: "dropdown__box-options", initial: { opacity: 0, y: 20, scale: 0.95 }, animate: { opacity: 1, y: 0, scale: 1 }, exit: { opacity: 0, y: 20, scale: 0.95 }, transition: { duration: 0.2, ease: "easeIn" }, style: { top: position.top, left: position.left, width: position.width }, children: options.map(({ props }) => /* @__PURE__ */ (0, import_react.createElement)( DropdownOption, { ...props, variant, appearance, key: `${props.value}-${uniqueId}`, selectedValues: selected, onChange: () => handleChange(props.value) } )) } ) ] } ) }) }) ] }); }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { Dropdown, DropdownOption }); //# sourceMappingURL=index.js.map