summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: b2cd31c)
raw | patch | inline | side by side (parent: b2cd31c)
author | Eric Wertz <ericwertz@Erics-MacBook-Pro.local> | |
Wed, 4 Jun 2025 23:26:18 +0000 (19:26 -0400) | ||
committer | Eric Wertz <ericwertz@Erics-MacBook-Pro.local> | |
Wed, 4 Jun 2025 23:26:18 +0000 (19:26 -0400) |
frontend/components/ToolbarButton.tsx | [new file with mode: 0644] | patch | blob |
diff --git a/frontend/components/ToolbarButton.tsx b/frontend/components/ToolbarButton.tsx
--- /dev/null
@@ -0,0 +1,105 @@
+import { Fragment, h, JSX } from 'preact';
+import { useState, useRef, useEffect } from 'preact/hooks';
+
+export interface MenuOption {
+ label: string;
+ onClick: () => void;
+ disabled?: boolean;
+ icon?: JSX.Element;
+}
+
+export interface ToolbarButtonProps {
+ icon: JSX.Element;
+ onClick?: () => void;
+ title: string;
+ disabled?: boolean;
+ active?: boolean;
+ separatorAfter?: boolean;
+ menuOptions?: MenuOption[];
+}
+
+export const ToolbarButton = ({
+ icon,
+ onClick,
+ title,
+ disabled,
+ active,
+ separatorAfter,
+ menuOptions
+}: ToolbarButtonProps) => {
+ const [showMenu, setShowMenu] = useState(false);
+ const buttonRef = useRef<HTMLButtonElement>(null);
+ const menuRef = useRef<HTMLDivElement>(null);
+
+ const hasMenu = menuOptions && menuOptions.length > 0;
+
+ // Handle outside clicks to close menu
+ useEffect(() => {
+ const handleClickOutside = (event: MouseEvent) => {
+ if (
+ showMenu &&
+ buttonRef.current &&
+ menuRef.current &&
+ !buttonRef.current.contains(event.target as Node) &&
+ !menuRef.current.contains(event.target as Node)
+ ) {
+ setShowMenu(false);
+ }
+ };
+
+ document.addEventListener('mousedown', handleClickOutside);
+ return () => {
+ document.removeEventListener('mousedown', handleClickOutside);
+ };
+ }, [showMenu]);
+
+ const handleButtonClick = () => {
+ if (disabled) return;
+
+ if (hasMenu) {
+ setShowMenu(!showMenu);
+ } else if (onClick) {
+ onClick();
+ }
+ };
+
+ const handleMenuItemClick = (menuItemOnClick: () => void) => {
+ menuItemOnClick();
+ setShowMenu(false);
+ };
+
+ return (
+ <div class="toolbar-button-wrapper">
+ <button
+ ref={buttonRef}
+ onClick={handleButtonClick}
+ title={title}
+ disabled={disabled}
+ class={`toolbar-button ${active ? 'active' : ''} ${disabled ? 'disabled' : ''}`}
+ >
+ {icon}
+ {/*hasMenu && (
+ <span class="menu-indicator">▼</span>
+ )*/}
+ </button>
+
+ {hasMenu && showMenu && (
+ <div ref={menuRef} class="toolbar-menu">
+ {menuOptions.map((option, index) => (
+ <button
+ key={index}
+ class={`toolbar-menu-item ${option.disabled ? 'disabled' : ''}`}
+ onClick={() => !option.disabled && handleMenuItemClick(option.onClick)}
+ disabled={option.disabled}
+ >
+ {option.icon && <span class="menu-item-icon">{option.icon}</span>}
+ <span class="menu-item-label">{option.label}</span>
+ </button>
+ ))}
+ </div>
+ )}
+ </div>
+ );
+};
+
+export default ToolbarButton;
\ No newline at end of file