PageSourceSearch

https://dash.evaboot.com/static/js/components/x-button.c2b38c4f3d94.js

js evaboot.com collected 2026-09-24 10:27:20 UTC 3,925 bytes, 99 lines download raw bytes

1/**
2 * <x-button> — light DOM button component.
3 *
4 * Renders a styled <button> or <a> inside itself (light DOM, no shadow).
5 * Keeps the custom element in the DOM so Alpine/HTMX bindings on parent
6 * elements continue to work.
7 *
8 * Attributes:
9 *   variant: "primary" (default) | "secondary" | "danger" | "ghost"
10 *   size: "sm" (default) | "md" | "lg" | "xl" | "2xl"
11 *   href: if set, renders as <a> instead of <button>
12 *   target, rel: forwarded onto the rendered <a> (href only)
13 *   type: button type (default "button", use "submit" for forms)
14 *   disabled: disables the button
15 *   class: extra classes moved onto the rendered element (e.g. "w-full")
16 *
17 * HTMX/Alpine: put hx-* and @* attributes directly on <x-button>.
18 * They stay on the host element and work normally. Alpine directives on
19 * the children (x-show spans for a button label that changes) work too:
20 * the render below re-inserts the children, and Alpine's MutationObserver
21 * initialises the re-inserted nodes whether it started before or after
22 * the render (verified both orders, welcome.html relies on it).
23 *
24 * Usage:
25 *   <x-button variant="primary" type="submit">Save</x-button>
26 *   <x-button variant="ghost" href="/dash/">Cancel</x-button>
27 *   <x-button variant="danger" size="sm">Delete</x-button>
28 */
29class XButton extends HTMLElement {
30  static BASE =
31    "inline-flex items-center justify-center font-semibold rounded-md " +
32    "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary " +
33    "disabled:opacity-50 disabled:pointer-events-none transition-[background-color,color,box-shadow,transform] " +
34    "active:scale-[0.98] cursor-pointer";
35
36  static VARIANTS = {
37    primary: "bg-primary text-white hover:bg-primary-hover shadow-xs",
38    secondary:
39      "bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 ring-1 ring-gray-300 dark:ring-gray-600 ring-inset hover:bg-gray-50 dark:hover:bg-gray-700 shadow-xs",
40    danger: "bg-red-600 text-white hover:bg-red-500 shadow-xs",
41    ghost: "text-primary hover:underline",
42  };
43
44  static SIZES = {
45    sm: "text-xs px-2 py-1 gap-x-1",
46    md: "text-sm px-3 py-1.5 gap-x-1.5",
47    lg: "text-sm px-3.5 py-2 gap-x-2",
48    xl: "text-[15px] px-4 py-2.5 gap-x-2",
49    "2xl": "text-base px-5 py-3.5 gap-x-2.5",
50  };
51
52  connectedCallback() {
53    if (this.hasAttribute("data-rendered")) return;
54    this.setAttribute("data-rendered", "");
55
56    // Defer to ensure child nodes are parsed before reading innerHTML
57    requestAnimationFrame(() => this._render());
58  }
59
60  _render() {
61    const variant = this.getAttribute("variant") || "primary";
62    const size = this.getAttribute("size") || "sm";
63    const href = this.getAttribute("href");
64    const type = this.getAttribute("type") || "button";
65    const disabled = this.hasAttribute("disabled");
66
67    // Host classes (e.g. w-full) move to the rendered element — the host
68    // stays display:inline and never carries layout itself.
69    const extra = this.getAttribute("class") || "";
70    if (extra) this.removeAttribute("class");
71
72    const classes = [
73      XButton.BASE,
74      XButton.VARIANTS[variant] || XButton.VARIANTS.primary,
75      XButton.SIZES[size] || XButton.SIZES.sm,
76      extra,
77    ]
78      .join(" ")
79      .trim();
80
81    const inner = this.innerHTML;
82
83    // Style the host element as inline so it doesn't affect layout
84    this.style.display = "inline";
85
86    if (href) {
87      const target = this.getAttribute("target");
88      const rel = this.getAttribute("rel");
89      const linkAttrs =
90        (target ? ` target="${target}"` : "") + (rel ? ` rel="${rel}"` : "");
91      this.innerHTML = `<a href="${href}" class="${classes}"${linkAttrs}>${inner}</a>`;
92    } else {
93      const disabledAttr = disabled ? " disabled" : "";
94      this.innerHTML = `<button type="${type}" class="${classes}"${disabledAttr}>${inner}</button>`;
95    }
96  }
97}
98
99customElements.define("x-button", XButton);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.