UNPKG

@helpwave/hightide

Version:

helpwave's component and theming library

641 lines (634 loc) 23.5 kB
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); // src/components/layout-and-navigation/Carousel.tsx var Carousel_exports = {}; __export(Carousel_exports, { Carousel: () => Carousel }); module.exports = __toCommonJS(Carousel_exports); var import_react2 = require("react"); var import_clsx2 = __toESM(require("clsx")); var import_lucide_react = require("lucide-react"); // src/util/array.ts var defaultRangeOptions = { allowEmptyRange: false, stepSize: 1, exclusiveStart: false, exclusiveEnd: true }; var range = (endOrRange, options) => { const { allowEmptyRange, stepSize, exclusiveStart, exclusiveEnd } = { ...defaultRangeOptions, ...options }; let start = 0; let end; if (typeof endOrRange === "number") { end = endOrRange; } else { start = endOrRange[0]; end = endOrRange[1]; } if (!exclusiveEnd) { end -= 1; } if (exclusiveStart) { start += 1; } if (end - 1 < start) { if (!allowEmptyRange) { console.warn(`range: end (${end}) < start (${start}) should be allowed explicitly, set options.allowEmptyRange to true`); } return []; } return Array.from({ length: end - start }, (_, index) => index * stepSize + start); }; var createLoopingListWithIndex = (list, startIndex = 0, length = 0, forwards = true) => { if (length < 0) { console.warn(`createLoopingList: length must be >= 0, given ${length}`); } else if (length === 0) { length = list.length; } const returnList = []; if (forwards) { for (let i = startIndex; returnList.length < length; i = (i + 1) % list.length) { returnList.push([i, list[i]]); } } else { for (let i = startIndex; returnList.length < length; i = i === 0 ? i = list.length - 1 : i - 1) { returnList.push([i, list[i]]); } } return returnList; }; // src/util/math.ts var clamp = (value, min = 0, max = 1) => { return Math.min(Math.max(value, min), max); }; // src/util/easeFunctions.ts var EaseFunctions = class _EaseFunctions { static cubicBezierGeneric(x1, y1, x2, y2) { const cx = 3 * x1; const bx = 3 * (x2 - x1) - cx; const ax = 1 - cx - bx; const cy = 3 * y1; const by = 3 * (y2 - y1) - cy; const ay = 1 - cy - by; const x = (t) => ((ax * t + bx) * t + cx) * t; const y = (t) => ((ay * t + by) * t + cy) * t; return { x, y }; } static cubicBezier(x1, y1, x2, y2) { const { y } = _EaseFunctions.cubicBezierGeneric(x1, y1, x2, y2); return (t) => { t = clamp(t); return y(t); }; } static easeInEaseOut(t) { return _EaseFunctions.cubicBezier(0.65, 0, 0.35, 1)(t); } }; // src/util/loopingArray.ts var LoopingArrayCalculator = class _LoopingArrayCalculator { constructor(length, isLooping = true, allowedOverScroll = 0.1) { if (allowedOverScroll < 0 || length < 1) { throw new Error("Invalid parameters: allowedOverScroll >= 0 and length >= 1 must be true"); } this.length = length; this.isLooping = isLooping; this.allowedOverScroll = allowedOverScroll; } getCorrectedPosition(position) { if (!this.isLooping) { return Math.max(-this.allowedOverScroll, Math.min(this.allowedOverScroll + this.length - 1, position)); } if (position >= this.length) { return position % this.length; } if (position < 0) { return this.length - Math.abs(position) % this.length; } return position; } static withoutOffset(position) { return position + _LoopingArrayCalculator.getOffset(position); } static getOffset(position) { return Math.round(position) - position; } /** * @return absolute distance forwards or Infinity when the target cannot be reached (only possible when not isLooping) */ getDistanceDirectional(position, target, direction) { if (!this.isLooping && (position < -this.allowedOverScroll || position > this.allowedOverScroll + this.length - 1)) { throw new Error("Invalid parameters: position is out of bounds."); } const isForwardInvalid = direction === 1 && position > target; const isBackwardInvalid = direction === -1 && target < position; if (!this.isLooping && (isForwardInvalid || isBackwardInvalid)) { return Infinity; } if (direction === -1) { return this.getDistanceDirectional(target, position, 1); } position = this.getCorrectedPosition(position); target = this.getCorrectedPosition(target); let distance = (target - position) * direction; if (distance < 0) { distance = this.length - Math.abs(position) % this.length + target; } return distance; } getDistanceForward(position, target) { return this.getDistanceDirectional(position, target, 1); } getDistanceBackward(position, target) { return this.getDistanceDirectional(position, target, -1); } getDistance(position, target) { const forwardDistance = this.getDistanceForward(position, target); const backwardDistance = this.getDistanceBackward(position, target); return Math.min(forwardDistance, backwardDistance); } getBestDirection(position, target) { const forwardDistance = this.getDistanceForward(position, target); const backwardDistance = this.getDistanceBackward(position, target); return forwardDistance < backwardDistance ? 1 : -1; } }; // src/components/user-action/Button.tsx var import_react = require("react"); var import_clsx = __toESM(require("clsx")); var import_jsx_runtime = require("react/jsx-runtime"); var ButtonColorUtil = { solid: ["primary", "secondary", "tertiary", "positive", "warning", "negative", "neutral"], text: ["primary", "negative", "neutral"], outline: ["primary"] }; var IconButtonUtil = { icon: [...ButtonColorUtil.solid, "transparent"] }; var paddingMapping = { small: "btn-sm", medium: "btn-md", large: "btn-lg" }; var iconPaddingMapping = { tiny: "icon-btn-xs", small: "icon-btn-sm", medium: "icon-btn-md", large: "icon-btn-lg" }; var ButtonUtil = { paddingMapping, iconPaddingMapping }; var SolidButton = (0, import_react.forwardRef)(function SolidButton2({ children, color = "primary", size = "medium", startIcon, endIcon, onClick, className, ...restProps }, ref) { const colorClasses = { primary: "not-disabled:bg-button-solid-primary-background not-disabled:text-button-solid-primary-text", secondary: "not-disabled:bg-button-solid-secondary-background not-disabled:text-button-solid-secondary-text", tertiary: "not-disabled:bg-button-solid-tertiary-background not-disabled:text-button-solid-tertiary-text", positive: "not-disabled:bg-button-solid-positive-background not-disabled:text-button-solid-positive-text", warning: "not-disabled:bg-button-solid-warning-background not-disabled:text-button-solid-warning-text", negative: "not-disabled:bg-button-solid-negative-background not-disabled:text-button-solid-negative-text", neutral: "not-disabled:bg-button-solid-neutral-background not-disabled:text-button-solid-neutral-text" }[color]; const iconColorClasses = { primary: "not-group-disabled:text-button-solid-primary-icon", secondary: "not-group-disabled:text-button-solid-secondary-icon", tertiary: "not-group-disabled:text-button-solid-tertiary-icon", positive: "not-group-disabled:text-button-solid-positive-icon", warning: "not-group-disabled:text-button-solid-warning-icon", negative: "not-group-disabled:text-button-solid-negative-icon", neutral: "not-group-disabled:text-button-solid-neutral-icon" }[color]; return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)( "button", { ref, onClick, className: (0, import_clsx.default)( "group font-semibold", colorClasses, "not-disabled:hover:brightness-90", "disabled:text-disabled-text disabled:bg-disabled-background", ButtonUtil.paddingMapping[size], className ), ...restProps, children: [ startIcon && /* @__PURE__ */ (0, import_jsx_runtime.jsx)( "span", { className: (0, import_clsx.default)( iconColorClasses, "group-disabled:text-disabled-icon" ), children: startIcon } ), children, endIcon && /* @__PURE__ */ (0, import_jsx_runtime.jsx)( "span", { className: (0, import_clsx.default)( iconColorClasses, "group-disabled:text-disabled-icon" ), children: endIcon } ) ] } ); }); var IconButton = ({ children, color = "primary", size = "medium", className, ...restProps }) => { const colorClasses = { primary: "not-disabled:bg-button-solid-primary-background not-disabled:text-button-solid-primary-text", secondary: "not-disabled:bg-button-solid-secondary-background not-disabled:text-button-solid-secondary-text", tertiary: "not-disabled:bg-button-solid-tertiary-background not-disabled:text-button-solid-tertiary-text", positive: "not-disabled:bg-button-solid-positive-background not-disabled:text-button-solid-positive-text", warning: "not-disabled:bg-button-solid-warning-background not-disabled:text-button-solid-warning-text", negative: "not-disabled:bg-button-solid-negative-background not-disabled:text-button-solid-negative-text", neutral: "not-disabled:bg-button-solid-neutral-background not-disabled:text-button-solid-neutral-text", transparent: "not-disabled:bg-transparent" }[color]; return /* @__PURE__ */ (0, import_jsx_runtime.jsx)( "button", { className: (0, import_clsx.default)( colorClasses, "not-disabled:hover:brightness-90", "disabled:text-disabled-text", { "disabled:bg-disabled-background": color !== "transparent", "disabled:opacity-70": color === "transparent", "not-disabled:hover:bg-button-text-hover-background": color === "transparent" }, ButtonUtil.iconPaddingMapping[size], className ), ...restProps, children } ); }; // src/components/layout-and-navigation/Carousel.tsx var import_jsx_runtime2 = require("react/jsx-runtime"); var Carousel = ({ children, animationTime = 200, isLooping = false, isAutoLooping = false, autoLoopingTimeOut = 5e3, autoLoopAnimationTime = 500, hintNext = false, arrows = false, dots = true, overScrollThreshold = 0.1, blurColor = "from-background", className = "", heightClassName = "h-96", widthClassName = "w-[70%] desktop:w-1/2" }) => { if (isAutoLooping && !isLooping) { console.error("When isAutoLooping is true, isLooping should also be true"); isLooping = true; } const [{ currentPosition, dragState, animationState }, setCarouselInformation] = (0, import_react2.useState)({ currentPosition: 0 }); const animationId = (0, import_react2.useRef)(void 0); const timeOut = (0, import_react2.useRef)(void 0); autoLoopingTimeOut = Math.max(0, autoLoopingTimeOut); const length = children.length; const paddingItemCount = 3; const util = (0, import_react2.useMemo)(() => new LoopingArrayCalculator(length, isLooping, overScrollThreshold), [length, isLooping, overScrollThreshold]); const currentIndex = util.getCorrectedPosition(LoopingArrayCalculator.withoutOffset(currentPosition)); animationTime = Math.max(200, animationTime); autoLoopAnimationTime = Math.max(200, autoLoopAnimationTime); const getStyleOffset = (index) => { const baseOffset = -50 + (index - currentPosition) * 100; return `${baseOffset}%`; }; const animation = (0, import_react2.useCallback)((time) => { let keepAnimating = true; setCarouselInformation((state) => { const { animationState: animationState2, dragState: dragState2 } = state; if (animationState2 === void 0 || dragState2 !== void 0) { keepAnimating = false; return state; } if (!animationState2.startTime || !animationState2.lastUpdateTime) { return { ...state, animationState: { ...animationState2, startTime: time, lastUpdateTime: time } }; } const useAnimationTime = animationState2.isAutoLooping ? autoLoopAnimationTime : animationTime; const progress = clamp((time - animationState2.startTime) / useAnimationTime); const easedProgress = EaseFunctions.easeInEaseOut(progress); const distance = util.getDistanceDirectional(animationState2.startPosition, animationState2.targetPosition, animationState2.direction); const newPosition = util.getCorrectedPosition(easedProgress * distance * animationState2.direction + animationState2.startPosition); if (animationState2.targetPosition === newPosition || progress === 1) { keepAnimating = false; return { currentPosition: LoopingArrayCalculator.withoutOffset(newPosition), animationState: void 0 }; } return { currentPosition: newPosition, animationState: { ...animationState2, lastUpdateTime: time } }; }); if (keepAnimating) { animationId.current = requestAnimationFrame((time1) => animation(time1)); } }, [animationTime, autoLoopAnimationTime, util]); (0, import_react2.useEffect)(() => { if (animationState) { animationId.current = requestAnimationFrame(animation); } return () => { if (animationId.current) { cancelAnimationFrame(animationId.current); animationId.current = 0; } }; }, [animationState]); const startAutoLoop = () => setCarouselInformation((prevState) => ({ ...prevState, dragState: prevState.dragState, animationState: prevState.animationState || prevState.dragState ? prevState.animationState : { startPosition: currentPosition, targetPosition: (currentPosition + 1) % length, direction: 1, // always move forward isAutoLooping: true } })); (0, import_react2.useEffect)(() => { if (!animationId.current && !animationState && !dragState && !timeOut.current) { if (autoLoopingTimeOut > 0) { timeOut.current = setTimeout(() => { startAutoLoop(); timeOut.current = void 0; }, autoLoopingTimeOut); } else { startAutoLoop(); } } }, [animationState, dragState, animationId.current, timeOut.current]); const startAnimation = (targetPosition) => { if (targetPosition === void 0) { targetPosition = LoopingArrayCalculator.withoutOffset(currentPosition); } if (targetPosition === currentPosition) { return; } const direction = util.getBestDirection(currentPosition, targetPosition); clearTimeout(timeOut.current); timeOut.current = void 0; if (animationId.current) { cancelAnimationFrame(animationId.current); animationId.current = void 0; } setCarouselInformation((prevState) => ({ ...prevState, dragState: void 0, animationState: { targetPosition, direction, startPosition: currentPosition, isAutoLooping: false }, timeOut: void 0 })); }; const canGoLeft = () => { return isLooping || currentPosition !== 0; }; const canGoRight = () => { return isLooping || currentPosition !== length - 1; }; const left = () => { if (canGoLeft()) { startAnimation(currentPosition === 0 ? length - 1 : LoopingArrayCalculator.withoutOffset(currentPosition - 1)); } }; const right = () => { if (canGoRight()) { startAnimation(LoopingArrayCalculator.withoutOffset((currentPosition + 1) % length)); } }; let items = children.map((item, index) => ({ index, item })); if (isLooping) { const before = createLoopingListWithIndex(children, length - 1, paddingItemCount, false).reverse().map(([index, item]) => ({ index, item })); const after = createLoopingListWithIndex(children, 0, paddingItemCount).map(([index, item]) => ({ index, item })); items = [ ...before, ...items, ...after ]; } const onDragStart = (x) => setCarouselInformation((prevState) => ({ ...prevState, dragState: { lastX: x, startX: x, startTime: Date.now(), startIndex: currentPosition }, animationState: void 0 // cancel animation })); const onDrag = (x, width) => { if (!dragState || x === 0) { return; } const offsetUpdate = (dragState.lastX - x) / width; const newPosition = util.getCorrectedPosition(currentPosition + offsetUpdate); setCarouselInformation((prevState) => ({ ...prevState, currentPosition: newPosition, dragState: { ...dragState, lastX: x } })); }; const onDragEnd = (x, width) => { if (!dragState) { return; } const distance = dragState.startX - x; const relativeDistance = distance / width; const duration = Date.now() - dragState.startTime; const velocity = distance / (Date.now() - dragState.startTime); const isSlide = Math.abs(velocity) > 2 || duration < 200 && (Math.abs(relativeDistance) > 0.2 || Math.abs(distance) > 50); if (isSlide) { if (distance > 0 && canGoRight()) { right(); return; } else if (distance < 0 && canGoLeft()) { left(); return; } } startAnimation(); }; const dragHandlers = { draggable: true, onDragStart: (event) => { onDragStart(event.clientX); event.dataTransfer.setDragImage(document.createElement("div"), 0, 0); }, onDrag: (event) => onDrag(event.clientX, event.target.getBoundingClientRect().width), onDragEnd: (event) => onDragEnd(event.clientX, event.target.getBoundingClientRect().width), onTouchStart: (event) => onDragStart(event.touches[0].clientX), onTouchMove: (event) => onDrag(event.touches[0].clientX, event.target.getBoundingClientRect().width), onTouchEnd: (event) => onDragEnd(event.changedTouches[0].clientX, event.target.getBoundingClientRect().width), onTouchCancel: (event) => onDragEnd(event.changedTouches[0].clientX, event.target.getBoundingClientRect().width) }; return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex-col-2 items-center w-full", children: [ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: (0, import_clsx2.default)(`relative w-full overflow-hidden`, heightClassName, className), children: [ arrows && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( IconButton, { color: "neutral", className: (0, import_clsx2.default)("absolute z-10 left-0 top-1/2 -translate-y-1/2 shadow-md", { hidden: !canGoLeft() }), disabled: !canGoLeft(), onClick: () => left(), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react.ChevronLeft, { size: 24 }) } ), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( IconButton, { color: "neutral", className: (0, import_clsx2.default)("absolute z-10 right-0 top-1/2 -translate-y-1/2 shadow-md", { hidden: !canGoRight() }), disabled: !canGoRight(), onClick: () => right(), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react.ChevronRight, { size: 24 }) } ) ] }), hintNext ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: (0, import_clsx2.default)(`flex-row-2 relative h-full`, heightClassName), children: [ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex-row-2 relative h-full w-full px-2 overflow-hidden", children: items.map(({ item, index }, listIndex) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( "div", { className: (0, import_clsx2.default)(`absolute left-[50%] h-full overflow-hidden`, widthClassName, { "!cursor-grabbing": !!dragState }), style: { translate: getStyleOffset(listIndex - (isLooping ? paddingItemCount : 0)) }, ...dragHandlers, onClick: () => startAnimation(index), children: item }, listIndex )) }), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( "div", { className: (0, import_clsx2.default)(`hidden pointer-events-none desktop:block absolute left-0 h-full w-[20%] bg-gradient-to-r to-transparent`, blurColor) } ), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( "div", { className: (0, import_clsx2.default)(`hidden pointer-events-none desktop:block absolute right-0 h-full w-[20%] bg-gradient-to-l to-transparent`, blurColor) } ) ] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: (0, import_clsx2.default)("px-16 h-full", { "!cursor-grabbing": !!dragState }), ...dragHandlers, children: children[currentIndex] }) ] }), dots && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( "div", { className: "flex-row-2 items-center justify-center w-full my-2", children: range(length).map((index) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( "button", { className: (0, import_clsx2.default)("w-8 min-w-8 h-3 min-h-3 first:rounded-l-md last:rounded-r-md", { "bg-carousel-dot-disabled hover:bg-carousel-dot-active": currentIndex !== index, "bg-carousel-dot-active hover:brightness-90": currentIndex === index }), onClick: () => startAnimation(index) }, index )) } ) ] }); }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { Carousel }); //# sourceMappingURL=Carousel.js.map