users/ryan/modules/simple-bar/simple-bar-source/lib/components/data/data-widget.jsx
22e588d2f7dc164806f01511b46a52d6e6852505
· 4.2 KB · 136 lines
raw
| 1 | import * as Uebersicht from "uebersicht"; |
| 2 | import * as Specter from "./specter.jsx"; |
| 3 | import * as Utils from "../../utils"; |
| 4 | import { SuspenseIcon } from "../icons/icon.jsx"; |
| 5 | export { dataWidgetStyles as styles } from "../../styles/components/data/data-widget"; |
| 6 | |
| 7 | const { React } = Uebersicht; |
| 8 | |
| 9 | /** |
| 10 | * Widget component that renders a clickable data widget with optional icon and specter. |
| 11 | * @param {Object} props - The properties object. |
| 12 | * @param {React.Component} props.Icon - The icon component to display. |
| 13 | * @param {string} props.classes - Additional classes for the widget. |
| 14 | * @param {string} props.href - The URL to link to. |
| 15 | * @param {function} props.onClick - The click event handler. |
| 16 | * @param {function} props.onRightClick - The right-click event handler. |
| 17 | * @param {function} props.onMiddleClick - The middle-click event handler. |
| 18 | * @param {Object} props.style - The style object. |
| 19 | * @param {boolean} props.disableSlider - Flag to disable the slider effect. |
| 20 | * @param {boolean} props.showSpecter - Flag to show the specter widget. |
| 21 | * @param {boolean} props.useDivForClick - Render a div for click handling instead of a button. |
| 22 | * @param {React.ReactNode} props.children - The child elements to render inside the widget. |
| 23 | * @returns {React.ReactElement} The rendered widget component. |
| 24 | */ |
| 25 | export function Widget({ |
| 26 | Icon, |
| 27 | classes, |
| 28 | href, |
| 29 | onClick, |
| 30 | onRightClick, |
| 31 | onMiddleClick, |
| 32 | style, |
| 33 | disableSlider, |
| 34 | showSpecter, |
| 35 | useDivForClick, |
| 36 | children, |
| 37 | }) { |
| 38 | const ref = React.useRef(); |
| 39 | |
| 40 | let Tag = "div"; |
| 41 | if (href) Tag = "a"; |
| 42 | if (onClick && !href && !useDivForClick) Tag = "button"; |
| 43 | |
| 44 | const renderDivButton = Tag === "div" && Boolean(onClick); |
| 45 | |
| 46 | const dataWidgetClasses = Utils.classNames("data-widget", classes, { |
| 47 | "data-widget--clickable": href || onClick, |
| 48 | }); |
| 49 | |
| 50 | /** |
| 51 | * Handles the click event, determining the appropriate action based on the event. |
| 52 | * @param {MouseEvent} e - The mouse event. |
| 53 | */ |
| 54 | const onClickProp = (e) => { |
| 55 | const { metaKey } = e; |
| 56 | const action = metaKey || isMiddleClick(e) ? onMiddleClick : onClick; |
| 57 | if (action) action(e); |
| 58 | }; |
| 59 | |
| 60 | /** |
| 61 | * Handles the mouse enter event to start the sliding effect. |
| 62 | */ |
| 63 | const onMouseEnter = () => { |
| 64 | Utils.startSliding( |
| 65 | ref.current, |
| 66 | ".data-widget__inner", |
| 67 | ".data-widget__slider", |
| 68 | ); |
| 69 | }; |
| 70 | |
| 71 | /** |
| 72 | * Handles the mouse leave event to stop the sliding effect. |
| 73 | */ |
| 74 | const onMouseLeave = () => { |
| 75 | Utils.stopSliding(ref.current, ".data-widget__slider"); |
| 76 | }; |
| 77 | |
| 78 | /** |
| 79 | * Handles keyboard interaction for div-based clickable widgets. |
| 80 | * @param {KeyboardEvent} e - The keyboard event. |
| 81 | */ |
| 82 | const onKeyDown = (e) => { |
| 83 | if (e.key !== "Enter" && e.key !== " ") return; |
| 84 | e.preventDefault(); |
| 85 | onClickProp(e); |
| 86 | }; |
| 87 | |
| 88 | return ( |
| 89 | <Tag |
| 90 | ref={ref} |
| 91 | className={dataWidgetClasses} |
| 92 | href={href} |
| 93 | onClick={onClickProp} |
| 94 | role={renderDivButton ? "button" : undefined} |
| 95 | tabIndex={renderDivButton ? 0 : undefined} |
| 96 | onKeyDown={renderDivButton ? onKeyDown : undefined} |
| 97 | onContextMenu={onRightClick || undefined} |
| 98 | onMouseEnter={!disableSlider ? onMouseEnter : undefined} |
| 99 | onMouseLeave={!disableSlider ? onMouseLeave : undefined} |
| 100 | style={style} |
| 101 | > |
| 102 | {Icon && ( |
| 103 | <SuspenseIcon> |
| 104 | <Icon /> |
| 105 | </SuspenseIcon> |
| 106 | )} |
| 107 | {showSpecter && <Specter.Widget />} |
| 108 | <Inner disableSlider={disableSlider}>{children}</Inner> |
| 109 | </Tag> |
| 110 | ); |
| 111 | } |
| 112 | |
| 113 | /** |
| 114 | * Inner component that optionally wraps children with sliding effect elements. |
| 115 | * @param {Object} props - The properties object. |
| 116 | * @param {boolean} props.disableSlider - Flag to disable the slider effect. |
| 117 | * @param {React.ReactNode} props.children - The child elements to render inside the inner component. |
| 118 | * @returns {React.ReactElement} The rendered inner component. |
| 119 | */ |
| 120 | function Inner({ disableSlider, children }) { |
| 121 | if (disableSlider) return children; |
| 122 | return ( |
| 123 | <div className="data-widget__inner"> |
| 124 | <div className="data-widget__slider">{children}</div> |
| 125 | </div> |
| 126 | ); |
| 127 | } |
| 128 | |
| 129 | /** |
| 130 | * Checks if the event is a middle-click. |
| 131 | * @param {MouseEvent} e - The mouse event. |
| 132 | * @returns {boolean} True if the event is a middle-click, false otherwise. |
| 133 | */ |
| 134 | function isMiddleClick(e) { |
| 135 | return e.button === 1 || e["button&2"] === 1; |
| 136 | } |