diff --git a/packages/client/src/assets/img/BLOCK_SIDEBARMENU.png b/packages/client/src/assets/img/BLOCK_SIDEBARMENU.png new file mode 100644 index 0000000000..44864ea47c Binary files /dev/null and b/packages/client/src/assets/img/BLOCK_SIDEBARMENU.png differ diff --git a/packages/client/src/components/block-defaults/sidebar-menu-block/SidebarMenuBlock.tsx b/packages/client/src/components/block-defaults/sidebar-menu-block/SidebarMenuBlock.tsx new file mode 100644 index 0000000000..6cff218431 --- /dev/null +++ b/packages/client/src/components/block-defaults/sidebar-menu-block/SidebarMenuBlock.tsx @@ -0,0 +1,80 @@ +import { CSSProperties, useMemo } from 'react'; +import { observer } from 'mobx-react-lite'; +import { useBlock, useBlocks } from '@/hooks'; +import { BlockDef, BlockComponent } from '@/stores'; +import { Slot } from '@/components/blocks'; +import { Drawer, Stack, styled, Box } from '@semoss/ui'; + +export interface SidebarMenuBlockDef extends BlockDef<'sidebar-menu'> { + widget: 'sidebar-menu'; + data: { + style: CSSProperties; + anchor: 'left' | 'top'; + sidebarWidth: number; + sidebarHeight: number | string; + designMode: boolean; + open: string | boolean | number; // Changed to string to store query + }; + slots: { + content: true; + }; +} + +const SideBarWrapper = styled(Box)<{ $visible: boolean }>(({ $visible }) => ({ + visibility: $visible ? 'visible' : 'hidden', +})); + +export const SidebarMenuBlock: BlockComponent = observer(({ id }) => { + const { attrs, data, slots } = useBlock(id); + const { state } = useBlocks(); + const isStatic = state.mode === 'static'; + + const open = useMemo(() => { + let o = false; + // Interpret Python + if ( + data.open === true || + data.open === 'true' || + data.open === 1 || + data.open === '1' + ) { + o = true; + } + + return o; + }, [data.open]); + + // Helper to determine if modal should be shown + const shouldShowSidebar = isStatic + ? data.designMode // In static mode, show when design mode is on + : Boolean(open); // In interactive mode, show when query returns true + + // In static mode with design mode on, show as modal but without portal + if (!shouldShowSidebar && !isStatic) { + return <>; + } + + return ( + + + + + + + + ); +}); diff --git a/packages/client/src/components/block-defaults/sidebar-menu-block/config.tsx b/packages/client/src/components/block-defaults/sidebar-menu-block/config.tsx new file mode 100644 index 0000000000..2633920f63 --- /dev/null +++ b/packages/client/src/components/block-defaults/sidebar-menu-block/config.tsx @@ -0,0 +1,130 @@ +import { BlockConfig } from '@/stores'; +import { + ArrowBack, + ArrowDownward, + ArrowForward, + ArrowUpward, + Schema, +} from '@mui/icons-material'; +import { BLOCK_TYPE_LAYOUT } from '../block-defaults.constants'; +import { SidebarMenuBlock, SidebarMenuBlockDef } from './SidebarMenuBlock'; +import { + ButtonGroupSettings, + SizeSettings, + SwitchSettings, + QueryInputSettings, +} from '@/components/block-settings'; +import { + buildColorSection, + buildBorderSection, +} from '../block-defaults.shared'; + +export const config: BlockConfig = { + widget: 'sidebar-menu', + type: BLOCK_TYPE_LAYOUT, + data: { + style: {}, + anchor: 'left', + sidebarWidth: 240, + sidebarHeight: '100%', + designMode: true, // Default to design mode when first dropped + open: '', // Default to closed + }, + listeners: {}, + slots: { + content: [], + }, + render: SidebarMenuBlock, + icon: Schema, + contentMenu: [ + { + name: 'General', + children: [ + { + description: 'Design Mode', + render: ({ id }) => ( + + ), + }, + { + description: 'Open', + render: ({ id }) => ( + + ), + }, + { + description: 'Sidebar Width', + render: ({ id }) => ( + + ), + }, + { + description: 'Sidebar Height', + render: ({ id }) => ( + + ), + }, + { + description: 'Direction', + render: ({ id }) => ( + + ), + }, + ], + }, + ], + styleMenu: [ + buildColorSection(), + { + name: 'Spacing', + children: [ + { + description: 'Padding', + render: ({ id }) => ( + + ), + }, + ], + }, + buildBorderSection(), + ], +}; diff --git a/packages/client/src/components/block-defaults/sidebar-menu-block/index.ts b/packages/client/src/components/block-defaults/sidebar-menu-block/index.ts new file mode 100644 index 0000000000..73ec55c947 --- /dev/null +++ b/packages/client/src/components/block-defaults/sidebar-menu-block/index.ts @@ -0,0 +1,2 @@ +export * from './config'; +export * from './SidebarMenuBlock'; diff --git a/packages/client/src/components/designer/menu/default-menu.ts b/packages/client/src/components/designer/menu/default-menu.ts index 2cbacdfc0b..b3ecb7f798 100644 --- a/packages/client/src/components/designer/menu/default-menu.ts +++ b/packages/client/src/components/designer/menu/default-menu.ts @@ -33,6 +33,7 @@ import HTML_BLOCK from '@/assets/img/HTML_BLOCK_SM.png'; import BLOCK_MODAL from '@/assets/img/BLOCK_MODAL.png'; import BLOCK_THEME from '@/assets/img/BLOCK_THEME.png'; import BLOCK_ACCORDION from '@/assets/img/BLOCK_ACCORDION.png'; +import BLOCK_SIDEBARMENU from '@/assets/img/BLOCK_SIDEBARMENU.png'; const SECTION_ELEMENT = 'Element'; const SECTION_INPUT = 'Input'; @@ -187,6 +188,26 @@ export const DEFAULT_MENU: DesignerMenuItem[] = [ }, }, }, + { + section: SECTION_LAYOUT, + image: BLOCK_SIDEBARMENU, + name: 'Sidebar-Menu', + json: { + widget: 'sidebar-menu', + data: { + style: {}, + open: false, + anchor: 'left', + sidebarWidth: 240, + sidebarHeight: '100%', + designMode: true, + }, + listeners: {}, + slots: { + content: [], + }, + }, + }, { section: SECTION_INPUT, image: BLOCK_INPUT,