aboutsummaryrefslogtreecommitdiff
path: root/users/ryan/modules/simple-bar/simple-bar-source/lib/components/data/data-widget.jsx
blob: f5555a1575ad80ba274aa12dce5d3ea6dd1c1972 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import * as Uebersicht from "uebersicht";
import * as Specter from "./specter.jsx";
import * as Utils from "../../utils";
import { SuspenseIcon } from "../icons/icon.jsx";
export { dataWidgetStyles as styles } from "../../styles/components/data/data-widget";

const { React } = Uebersicht;

/**
 * Widget component that renders a clickable data widget with optional icon and specter.
 * @param {Object} props - The properties object.
 * @param {React.Component} props.Icon - The icon component to display.
 * @param {string} props.classes - Additional classes for the widget.
 * @param {string} props.href - The URL to link to.
 * @param {function} props.onClick - The click event handler.
 * @param {function} props.onRightClick - The right-click event handler.
 * @param {function} props.onMiddleClick - The middle-click event handler.
 * @param {Object} props.style - The style object.
 * @param {boolean} props.disableSlider - Flag to disable the slider effect.
 * @param {boolean} props.showSpecter - Flag to show the specter widget.
 * @param {boolean} props.useDivForClick - Render a div for click handling instead of a button.
 * @param {React.ReactNode} props.children - The child elements to render inside the widget.
 * @returns {React.ReactElement} The rendered widget component.
 */
export function Widget({
  Icon,
  classes,
  href,
  onClick,
  onRightClick,
  onMiddleClick,
  style,
  disableSlider,
  showSpecter,
  useDivForClick,
  children,
}) {
  const ref = React.useRef();

  let Tag = "div";
  if (href) Tag = "a";
  if (onClick && !href && !useDivForClick) Tag = "button";

  const renderDivButton = Tag === "div" && Boolean(onClick);

  const dataWidgetClasses = Utils.classNames("data-widget", classes, {
    "data-widget--clickable": href || onClick,
  });

  /**
   * Handles the click event, determining the appropriate action based on the event.
   * @param {MouseEvent} e - The mouse event.
   */
  const onClickProp = (e) => {
    const { metaKey } = e;
    const action = metaKey || isMiddleClick(e) ? onMiddleClick : onClick;
    if (action) action(e);
  };

  /**
   * Handles the mouse enter event to start the sliding effect.
   */
  const onMouseEnter = () => {
    Utils.startSliding(
      ref.current,
      ".data-widget__inner",
      ".data-widget__slider",
    );
  };

  /**
   * Handles the mouse leave event to stop the sliding effect.
   */
  const onMouseLeave = () => {
    Utils.stopSliding(ref.current, ".data-widget__slider");
  };

  /**
   * Handles keyboard interaction for div-based clickable widgets.
   * @param {KeyboardEvent} e - The keyboard event.
   */
  const onKeyDown = (e) => {
    if (e.key !== "Enter" && e.key !== " ") return;
    e.preventDefault();
    onClickProp(e);
  };

  return (
    <Tag
      ref={ref}
      className={dataWidgetClasses}
      href={href}
      onClick={onClickProp}
      role={renderDivButton ? "button" : undefined}
      tabIndex={renderDivButton ? 0 : undefined}
      onKeyDown={renderDivButton ? onKeyDown : undefined}
      onContextMenu={onRightClick || undefined}
      onMouseEnter={!disableSlider ? onMouseEnter : undefined}
      onMouseLeave={!disableSlider ? onMouseLeave : undefined}
      style={style}
    >
      {Icon && (
        <SuspenseIcon>
          <Icon />
        </SuspenseIcon>
      )}
      {showSpecter && <Specter.Widget />}
      <Inner disableSlider={disableSlider}>{children}</Inner>
    </Tag>
  );
}

/**
 * Inner component that optionally wraps children with sliding effect elements.
 * @param {Object} props - The properties object.
 * @param {boolean} props.disableSlider - Flag to disable the slider effect.
 * @param {React.ReactNode} props.children - The child elements to render inside the inner component.
 * @returns {React.ReactElement} The rendered inner component.
 */
function Inner({ disableSlider, children }) {
  if (disableSlider) return children;
  return (
    <div className="data-widget__inner">
      <div className="data-widget__slider">{children}</div>
    </div>
  );
}

/**
 * Checks if the event is a middle-click.
 * @param {MouseEvent} e - The mouse event.
 * @returns {boolean} True if the event is a middle-click, false otherwise.
 */
function isMiddleClick(e) {
  return e.button === 1 || e["button&2"] === 1;
}