]> Eric's Git Repo - listv4.git/commitdiff
new toolbar button component
authorEric Wertz <ericwertz@Erics-MacBook-Pro.local>
Wed, 4 Jun 2025 23:26:18 +0000 (19:26 -0400)
committerEric 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]

diff --git a/frontend/components/ToolbarButton.tsx b/frontend/components/ToolbarButton.tsx
new file mode 100644 (file)
index 0000000..6df8e0f
--- /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