beacon-ui 3.1.8 → 3.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,23 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [3.1.9] - 2025-12-29
9
+
10
+ ### Added
11
+ - Menu component now supports render props for full customization:
12
+ - `renderSwitch` - Customize or replace the Switch component with your own implementation
13
+ - `renderToggleButton` - Customize or replace the toggle button (menu/close icon) with your own component
14
+ - `renderButton` - Customize or replace the menu action button with your own component
15
+ - Menu component callback props for handling interactions:
16
+ - `onSwitchChange` - Callback when Switch value changes
17
+ - `onToggleButtonClick` - Callback when toggle button is clicked
18
+ - `onButtonClick` - Callback when menu action button is clicked
19
+ - Exported render prop types: `SwitchRenderProps`, `ToggleButtonRenderProps`, `MenuButtonRenderProps`
20
+
21
+ ### Changed
22
+ - Menu component now uses internal state management for Switch, allowing better control and customization
23
+ - Render props are optional - if not provided, Menu uses default components (backward compatible)
24
+
8
25
  ## [3.1.8] - 2025-12-29
9
26
 
10
27
  ### Fixed
@@ -7,6 +7,17 @@ export interface MenuItem {
7
7
  selected?: boolean;
8
8
  onClick?: (item: MenuItem) => void;
9
9
  }
10
+ export interface SwitchRenderProps {
11
+ checked: boolean;
12
+ onChange: (checked: boolean) => void;
13
+ }
14
+ export interface ToggleButtonRenderProps {
15
+ isOpen: boolean;
16
+ onClick: () => void;
17
+ }
18
+ export interface MenuButtonRenderProps {
19
+ onClick: () => void;
20
+ }
10
21
  export interface MenuProps extends ComponentPropsWithRef<"div"> {
11
22
  variant?: MenuVariant;
12
23
  showMenu?: boolean;
@@ -18,7 +29,13 @@ export interface MenuProps extends ComponentPropsWithRef<"div"> {
18
29
  avatarImageUrl?: string;
19
30
  selectedItemId?: string;
20
31
  onItemClick?: (item: MenuItem) => void;
32
+ renderSwitch?: (props: SwitchRenderProps) => ReactNode;
33
+ renderToggleButton?: (props: ToggleButtonRenderProps) => ReactNode;
34
+ renderButton?: (props: MenuButtonRenderProps) => ReactNode;
35
+ onSwitchChange?: (checked: boolean) => void;
36
+ onToggleButtonClick?: () => void;
37
+ onButtonClick?: () => void;
21
38
  }
22
- export declare function Menu({ variant, showMenu, showButton, menuItems, headerTitle, headerSubtitle, showChevrons, avatarImageUrl, selectedItemId, onItemClick, className, style, ref, ...rest }: MenuProps): import("react/jsx-runtime").JSX.Element;
39
+ export declare function Menu({ variant, showMenu, showButton, menuItems, headerTitle, headerSubtitle, showChevrons, avatarImageUrl, selectedItemId, onItemClick, renderSwitch, renderToggleButton, renderButton, onSwitchChange, onToggleButtonClick, onButtonClick, className, style, ref, ...rest }: MenuProps): import("react/jsx-runtime").JSX.Element;
23
40
  export {};
24
41
  //# sourceMappingURL=Menu.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Menu.d.ts","sourceRoot":"","sources":["../../src/components/Menu.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAW,qBAAqB,EAAY,SAAS,EAAE,MAAM,OAAO,CAAC;AAM5E,KAAK,WAAW,GAAG,SAAS,GAAG,aAAa,GAAG,eAAe,GAAG,aAAa,GAAG,eAAe,GAAG,YAAY,CAAC;AAEhH,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,CAAC;CACpC;AAED,MAAM,WAAW,SAAU,SAAQ,qBAAqB,CAAC,KAAK,CAAC;IAC7D,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,CAAC;CACxC;AAUD,wBAAgB,IAAI,CAAC,EACnB,OAAmB,EACnB,QAAe,EACf,UAAiB,EACjB,SAA8B,EAC9B,WAAqB,EACrB,cAA2B,EAC3B,YAAmB,EACnB,cAAc,EACd,cAAc,EACd,WAAW,EACX,SAAS,EACT,KAAK,EACL,GAAG,EACH,GAAG,IAAI,EACR,EAAE,SAAS,2CA2aX"}
1
+ {"version":3,"file":"Menu.d.ts","sourceRoot":"","sources":["../../src/components/Menu.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAW,qBAAqB,EAAoC,SAAS,EAAE,MAAM,OAAO,CAAC;AAMpG,KAAK,WAAW,GAAG,SAAS,GAAG,aAAa,GAAG,eAAe,GAAG,aAAa,GAAG,eAAe,GAAG,YAAY,CAAC;AAEhH,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,CAAC;CACpC;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;CACtC;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,MAAM,WAAW,SAAU,SAAQ,qBAAqB,CAAC,KAAK,CAAC;IAC7D,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,CAAC;IACvC,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,SAAS,CAAC;IACvD,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE,uBAAuB,KAAK,SAAS,CAAC;IACnE,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,SAAS,CAAC;IAC3D,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5C,mBAAmB,CAAC,EAAE,MAAM,IAAI,CAAC;IACjC,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;CAC5B;AAUD,wBAAgB,IAAI,CAAC,EACnB,OAAmB,EACnB,QAAe,EACf,UAAiB,EACjB,SAA8B,EAC9B,WAAqB,EACrB,cAA2B,EAC3B,YAAmB,EACnB,cAAc,EACd,cAAc,EACd,WAAW,EACX,YAAY,EACZ,kBAAkB,EAClB,YAAY,EACZ,cAAc,EACd,mBAAmB,EACnB,aAAa,EACb,SAAS,EACT,KAAK,EACL,GAAG,EACH,GAAG,IAAI,EACR,EAAE,SAAS,2CAieX"}
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { useMemo, useState } from "react";
3
+ import { useMemo, useState, useCallback, useEffect } from "react";
4
4
  import { CloseIcon, MenuIcon, DownloadIcon } from "../icons";
5
5
  import { Switch } from "./Switch";
6
6
  import { useThemeSafe } from "../providers/ThemeProvider";
@@ -12,10 +12,31 @@ const DEFAULT_MENU_ITEMS = [
12
12
  { id: "4", label: "Menu Item #4" },
13
13
  { id: "5", label: "Menu Item #5" },
14
14
  ];
15
- export function Menu({ variant = "desktop", showMenu = true, showButton = true, menuItems = DEFAULT_MENU_ITEMS, headerTitle = "Title", headerSubtitle = "Subtitle", showChevrons = true, avatarImageUrl, selectedItemId, onItemClick, className, style, ref, ...rest }) {
15
+ export function Menu({ variant = "desktop", showMenu = true, showButton = true, menuItems = DEFAULT_MENU_ITEMS, headerTitle = "Title", headerSubtitle = "Subtitle", showChevrons = true, avatarImageUrl, selectedItemId, onItemClick, renderSwitch, renderToggleButton, renderButton, onSwitchChange, onToggleButtonClick, onButtonClick, className, style, ref, ...rest }) {
16
16
  const themeContext = useThemeSafe();
17
17
  const theme = themeContext?.theme ?? "dark";
18
18
  const [hoveredItemId, setHoveredItemId] = useState(null);
19
+ const [switchChecked, setSwitchChecked] = useState(theme === "dark");
20
+ // Update switch checked state when theme changes
21
+ useEffect(() => {
22
+ setSwitchChecked(theme === "dark");
23
+ }, [theme]);
24
+ const handleSwitchChange = useCallback((checked) => {
25
+ setSwitchChecked(checked);
26
+ if (onSwitchChange) {
27
+ onSwitchChange(checked);
28
+ }
29
+ }, [onSwitchChange]);
30
+ const handleToggleButtonClick = useCallback(() => {
31
+ if (onToggleButtonClick) {
32
+ onToggleButtonClick();
33
+ }
34
+ }, [onToggleButtonClick]);
35
+ const handleButtonClick = useCallback(() => {
36
+ if (onButtonClick) {
37
+ onButtonClick();
38
+ }
39
+ }, [onButtonClick]);
19
40
  const containerStyles = useMemo(() => {
20
41
  const baseStyles = {
21
42
  display: "flex",
@@ -178,7 +199,7 @@ export function Menu({ variant = "desktop", showMenu = true, showButton = true,
178
199
  };
179
200
  }, []);
180
201
  if (variant === "close-menu") {
181
- return (_jsx("div", { ref: ref, className: className, style: { ...containerStyles, ...style }, ...rest, children: _jsx("button", { style: iconButtonStyles, children: _jsx("div", { style: { display: "flex", alignItems: "center", justifyContent: "center", width: "32px", height: "32px" }, children: _jsx(CloseIcon, { size: 32 }) }) }) }));
202
+ return (_jsx("div", { ref: ref, className: className, style: { ...containerStyles, ...style }, ...rest, children: renderToggleButton ? (renderToggleButton({ isOpen: true, onClick: handleToggleButtonClick })) : (_jsx("button", { style: iconButtonStyles, onClick: handleToggleButtonClick, children: _jsx("div", { style: { display: "flex", alignItems: "center", justifyContent: "center", width: "32px", height: "32px" }, children: _jsx(CloseIcon, { size: 32 }) }) })) }));
182
203
  }
183
204
  const isDesktop = variant === "desktop";
184
205
  const isTabletOpen = variant === "tablet-open";
@@ -257,7 +278,10 @@ export function Menu({ variant = "desktop", showMenu = true, showButton = true,
257
278
  fontWeight: "var(--font-weight-secondary-medium)",
258
279
  color: "var(--fg-neutral)",
259
280
  margin: 0,
260
- }, children: headerSubtitle })] }))] }), _jsxs("div", { style: footerStyles, children: [showButton && (_jsxs("div", { style: buttonStyles, children: [_jsx("div", { style: { display: "flex", alignItems: "center", justifyContent: "center", width: "16px", height: "16px" }, children: _jsx(DownloadIcon, { size: 16 }) }), _jsx("span", { children: "Button" })] })), _jsx(Switch, { checked: theme === "dark", showIcons: true }), _jsx("button", { style: iconButtonStyles, children: _jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "center", width: "32px", height: "32px" }, children: [(isTabletOpen || isMobileOpen) && _jsx(CloseIcon, { size: 32 }), (isTabletClosed || isMobileClosed) && _jsx(MenuIcon, { size: 32 })] }) })] })] })), showMenuItems && (_jsx("div", { style: {
281
+ }, children: headerSubtitle })] }))] }), _jsxs("div", { style: footerStyles, children: [showButton && (renderButton ? (renderButton({ onClick: handleButtonClick })) : (_jsxs("div", { style: buttonStyles, onClick: handleButtonClick, children: [_jsx("div", { style: { display: "flex", alignItems: "center", justifyContent: "center", width: "16px", height: "16px" }, children: _jsx(DownloadIcon, { size: 16 }) }), _jsx("span", { children: "Button" })] }))), renderSwitch ? (renderSwitch({ checked: switchChecked, onChange: handleSwitchChange })) : (_jsx(Switch, { checked: switchChecked, onChange: handleSwitchChange, showIcons: true })), renderToggleButton ? (renderToggleButton({
282
+ isOpen: isTabletOpen || isMobileOpen,
283
+ onClick: handleToggleButtonClick
284
+ })) : (_jsx("button", { style: iconButtonStyles, onClick: handleToggleButtonClick, children: _jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "center", width: "32px", height: "32px" }, children: [(isTabletOpen || isMobileOpen) && _jsx(CloseIcon, { size: 32 }), (isTabletClosed || isMobileClosed) && _jsx(MenuIcon, { size: 32 })] }) }))] })] })), showMenuItems && (_jsx("div", { style: {
261
285
  display: "flex",
262
286
  flexDirection: "column",
263
287
  gap: "var(--spacing-200)",
@@ -284,5 +308,5 @@ export function Menu({ variant = "desktop", showMenu = true, showButton = true,
284
308
  // Determine state for MenuItem component
285
309
  const itemState = isSelected ? "Active" : isHovered ? "Hovered" : "Default";
286
310
  return (_jsx(MenuItemComponent, { menuTitle: item.label, iconStart: true, iconStart1: item.icon || null, iconEnd: showChevrons, iconEnd1: null, state: itemState, onClick: handleClick, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave }, item.id));
287
- }) })), isDesktop && (_jsxs(_Fragment, { children: [showButton && (_jsx("div", { style: { display: "flex", flexDirection: "column", gap: "var(--spacing-400)", padding: "var(--spacing-400)", width: "100%" }, children: _jsxs("div", { style: buttonStyles, children: [_jsx("div", { style: { display: "flex", alignItems: "center", justifyContent: "center", width: "16px", height: "16px" }, children: _jsx(DownloadIcon, { size: 16 }) }), _jsx("span", { children: "Button" })] }) })), _jsx("div", { style: footerStyles, children: _jsx(Switch, { checked: theme === "dark", showIcons: true }) })] }))] }));
311
+ }) })), isDesktop && (_jsxs(_Fragment, { children: [showButton && (_jsx("div", { style: { display: "flex", flexDirection: "column", gap: "var(--spacing-400)", padding: "var(--spacing-400)", width: "100%" }, children: renderButton ? (renderButton({ onClick: handleButtonClick })) : (_jsxs("div", { style: buttonStyles, onClick: handleButtonClick, children: [_jsx("div", { style: { display: "flex", alignItems: "center", justifyContent: "center", width: "16px", height: "16px" }, children: _jsx(DownloadIcon, { size: 16 }) }), _jsx("span", { children: "Button" })] })) })), _jsx("div", { style: footerStyles, children: renderSwitch ? (renderSwitch({ checked: switchChecked, onChange: handleSwitchChange })) : (_jsx(Switch, { checked: switchChecked, onChange: handleSwitchChange, showIcons: true })) })] }))] }));
288
312
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "beacon-ui",
3
- "version": "3.1.8",
3
+ "version": "3.1.9",
4
4
  "description": "Beacon Design System - Components and tokens",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",