@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
JavaScript
"use client";
;
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