@@ -5,26 +5,34 @@ import { Section, sectionNames } from "lowcoder-design";
55import styled from "styled-components" ;
66import { clickEvent , eventHandlerControl } from "comps/controls/eventHandlerControl" ;
77import { BoolCodeControl , StringControl } from "comps/controls/codeControl" ;
8+ import { dropdownControl } from "comps/controls/dropdownControl" ;
89import { alignWithJustifyControl } from "comps/controls/alignControl" ;
910import { navListComp } from "./navItemComp" ;
1011import { menuPropertyView } from "./components/MenuItemList" ;
1112import { default as DownOutlined } from "@ant-design/icons/DownOutlined" ;
13+ import { default as MenuOutlined } from "@ant-design/icons/MenuOutlined" ;
1214import { default as Dropdown } from "antd/es/dropdown" ;
1315import { default as Menu , MenuProps } from "antd/es/menu" ;
16+ import { default as Drawer } from "antd/es/drawer" ;
1417import { migrateOldData } from "comps/generators/simpleGenerators" ;
1518import { styleControl } from "comps/controls/styleControl" ;
1619import {
1720 AnimationStyle ,
1821 AnimationStyleType ,
1922 NavigationStyle ,
23+ HamburgerButtonStyle ,
24+ DrawerContainerStyle ,
25+ NavLayoutItemStyle ,
26+ NavLayoutItemHoverStyle ,
27+ NavLayoutItemActiveStyle ,
2028} from "comps/controls/styleControlConstants" ;
2129import { hiddenPropertyView , showDataLoadingIndicatorsPropertyView } from "comps/utils/propertyUtils" ;
2230import { trans } from "i18n" ;
2331
24- import { useContext } from "react" ;
32+ import { useContext , useState } from "react" ;
2533import { EditorContext } from "comps/editorState" ;
26- import { controlItem } from "lowcoder-design" ;
2734import { createNavItemsControl } from "./components/NavItemsControl" ;
35+ import { Layers } from "constants/Layers" ;
2836
2937type IProps = {
3038 $justify : boolean ;
@@ -34,6 +42,7 @@ type IProps = {
3442 $borderRadius : string ;
3543 $borderStyle : string ;
3644 $animationStyle : AnimationStyleType ;
45+ $orientation : "horizontal" | "vertical" ;
3746} ;
3847
3948const Wrapper = styled ( "div" ) <
@@ -45,18 +54,21 @@ ${props=>props.$animationStyle}
4554 box-sizing: border-box;
4655 border: ${ ( props ) => props . $borderWidth ? `${ props . $borderWidth } ` : '1px' } ${ props => props . $borderStyle } ${ ( props ) => props . $borderColor } ;
4756 background: ${ ( props ) => props . $bgColor } ;
57+ position: relative;
4858` ;
4959
50- const NavInner = styled ( "div" ) < Pick < IProps , "$justify" > > `
60+ const NavInner = styled ( "div" ) < Pick < IProps , "$justify" | "$orientation" > > `
5161 // margin: 0 -16px;
5262 height: 100%;
5363 display: flex;
54- justify-content: ${ ( props ) => ( props . $justify ? "space-between" : "left" ) } ;
64+ flex-direction: ${ ( props ) => ( props . $orientation === "vertical" ? "column" : "row" ) } ;
65+ justify-content: ${ ( props ) => ( props . $orientation === "vertical" ? "flex-start" : ( props . $justify ? "space-between" : "left" ) ) } ;
5566` ;
5667
5768const Item = styled . div < {
5869 $active : boolean ;
5970 $activeColor : string ;
71+ $hoverColor : string ;
6072 $color : string ;
6173 $fontFamily : string ;
6274 $fontStyle : string ;
@@ -66,12 +78,22 @@ const Item = styled.div<{
6678 $padding : string ;
6779 $textTransform :string ;
6880 $textDecoration :string ;
81+ $bg ?: string ;
82+ $hoverBg ?: string ;
83+ $activeBg ?: string ;
84+ $border ?: string ;
85+ $hoverBorder ?: string ;
86+ $activeBorder ?: string ;
87+ $radius ?: string ;
6988 $disabled ?: boolean ;
7089} > `
7190 height: 30px;
7291 line-height: 30px;
7392 padding: ${ ( props ) => props . $padding ? props . $padding : '0 16px' } ;
7493 color: ${ ( props ) => props . $disabled ? `${ props . $color } 80` : ( props . $active ? props . $activeColor : props . $color ) } ;
94+ background-color: ${ ( props ) => ( props . $active ? ( props . $activeBg || 'transparent' ) : ( props . $bg || 'transparent' ) ) } ;
95+ border: ${ ( props ) => props . $border ? `1px solid ${ props . $border } ` : '1px solid transparent' } ;
96+ border-radius: ${ ( props ) => props . $radius ? props . $radius : '0px' } ;
7597 font-weight: ${ ( props ) => ( props . $textWeight ? props . $textWeight : 500 ) } ;
7698 font-family:${ ( props ) => ( props . $fontFamily ? props . $fontFamily : 'sans-serif' ) } ;
7799 font-style:${ ( props ) => ( props . $fontStyle ? props . $fontStyle : 'normal' ) } ;
@@ -81,7 +103,9 @@ const Item = styled.div<{
81103 margin:${ ( props ) => props . $margin ? props . $margin : '0px' } ;
82104
83105 &:hover {
84- color: ${ ( props ) => props . $disabled ? ( props . $active ? props . $activeColor : props . $color ) : props . $activeColor } ;
106+ color: ${ ( props ) => props . $disabled ? ( props . $active ? props . $activeColor : props . $color ) : ( props . $hoverColor || props . $activeColor ) } ;
107+ background-color: ${ ( props ) => props . $disabled ? ( props . $active ? ( props . $activeBg || 'transparent' ) : ( props . $bg || 'transparent' ) ) : ( props . $hoverBg || props . $activeBg || props . $bg || 'transparent' ) } ;
108+ border: ${ ( props ) => props . $hoverBorder ? `1px solid ${ props . $hoverBorder } ` : ( props . $activeBorder ? `1px solid ${ props . $activeBorder } ` : ( props . $border ? `1px solid ${ props . $border } ` : '1px solid transparent' ) ) } ;
85109 cursor: ${ ( props ) => props . $disabled ? 'not-allowed' : 'pointer' } ;
86110 }
87111
@@ -101,10 +125,10 @@ const LogoWrapper = styled.div`
101125 }
102126` ;
103127
104- const ItemList = styled . div < { $align : string } > `
128+ const ItemList = styled . div < { $align : string , $orientation ?: string } > `
105129 flex: 1;
106130 display: flex;
107- flex-direction: row;
131+ flex-direction: ${ ( props ) => ( props . $orientation === "vertical" ? "column" : " row" ) } ;
108132 justify-content: ${ ( props ) => props . $align } ;
109133` ;
110134
@@ -114,6 +138,37 @@ const StyledMenu = styled(Menu) <MenuProps>`
114138 }
115139` ;
116140
141+ const FloatingHamburgerButton = styled . button < {
142+ $size : string ;
143+ $position : string ; // top-right | top-left | bottom-right | bottom-left
144+ $zIndex : number ;
145+ $background ?: string ;
146+ $borderColor ?: string ;
147+ $radius ?: string ;
148+ $margin ?: string ;
149+ $padding ?: string ;
150+ $borderWidth ?: string ;
151+ $iconColor ?: string ;
152+ } > `
153+ position: fixed;
154+ ${ ( props ) => ( props . $position . includes ( 'bottom' ) ? 'bottom: 16px;' : 'top: 16px;' ) }
155+ ${ ( props ) => ( props . $position . includes ( 'right' ) ? 'right: 16px;' : 'left: 16px;' ) }
156+ width: ${ ( props ) => props . $size } ;
157+ height: ${ ( props ) => props . $size } ;
158+ border-radius: ${ ( props ) => props . $radius || '50%' } ;
159+ border: ${ ( props ) => props . $borderWidth || '1px' } solid ${ ( props ) => props . $borderColor || 'rgba(0,0,0,0.1)' } ;
160+ background: ${ ( props ) => props . $background || 'white' } ;
161+ margin: ${ ( props ) => props . $margin || '0px' } ;
162+ padding: ${ ( props ) => props . $padding || '0px' } ;
163+ display: flex;
164+ align-items: center;
165+ justify-content: center;
166+ z-index: ${ ( props ) => props . $zIndex } ;
167+ cursor: pointer;
168+ box-shadow: 0 6px 16px rgba(0,0,0,0.15);
169+ color: ${ ( props ) => props . $iconColor || 'inherit' } ;
170+ ` ;
171+
117172const logoEventHandlers = [ clickEvent ] ;
118173
119174// Compatible with historical style data 2022-8-26
@@ -154,8 +209,33 @@ function fixOldItemsData(oldData: any) {
154209const childrenMap = {
155210 logoUrl : StringControl ,
156211 logoEvent : withDefault ( eventHandlerControl ( logoEventHandlers ) , [ { name : "click" } ] ) ,
212+ orientation : dropdownControl ( [
213+ { label : "Horizontal" , value : "horizontal" } ,
214+ { label : "Vertical" , value : "vertical" } ,
215+ ] , "horizontal" ) ,
216+ displayMode : dropdownControl ( [
217+ { label : "Bar" , value : "bar" } ,
218+ { label : "Hamburger" , value : "hamburger" } ,
219+ ] , "bar" ) ,
220+ hamburgerPosition : dropdownControl ( [
221+ { label : "Top Right" , value : "top-right" } ,
222+ { label : "Top Left" , value : "top-left" } ,
223+ { label : "Bottom Right" , value : "bottom-right" } ,
224+ { label : "Bottom Left" , value : "bottom-left" } ,
225+ ] , "top-right" ) ,
226+ hamburgerSize : withDefault ( StringControl , "56px" ) ,
227+ drawerPlacement : dropdownControl ( [
228+ { label : "Left" , value : "left" } ,
229+ { label : "Right" , value : "right" } ,
230+ ] , "right" ) ,
231+ shadowOverlay : withDefault ( BoolCodeControl , true ) ,
157232 horizontalAlignment : alignWithJustifyControl ( ) ,
158233 style : migrateOldData ( styleControl ( NavigationStyle , 'style' ) , fixOldStyleData ) ,
234+ navItemStyle : styleControl ( NavLayoutItemStyle , 'navItemStyle' ) ,
235+ navItemHoverStyle : styleControl ( NavLayoutItemHoverStyle , 'navItemHoverStyle' ) ,
236+ navItemActiveStyle : styleControl ( NavLayoutItemActiveStyle , 'navItemActiveStyle' ) ,
237+ hamburgerButtonStyle : styleControl ( HamburgerButtonStyle , 'hamburgerButtonStyle' ) ,
238+ drawerContainerStyle : styleControl ( DrawerContainerStyle , 'drawerContainerStyle' ) ,
159239 animationStyle : styleControl ( AnimationStyle , 'animationStyle' ) ,
160240 items : withDefault ( migrateOldData ( createNavItemsControl ( ) , fixOldItemsData ) , {
161241 optionType : "manual" ,
@@ -168,6 +248,7 @@ const childrenMap = {
168248} ;
169249
170250const NavCompBase = new UICompBuilder ( childrenMap , ( props ) => {
251+ const [ drawerVisible , setDrawerVisible ] = useState ( false ) ;
171252 const data = props . items ;
172253 const items = (
173254 < >
@@ -207,16 +288,24 @@ const NavCompBase = new UICompBuilder(childrenMap, (props) => {
207288 < Item
208289 key = { idx }
209290 $active = { active || subMenuSelectedKeys . length > 0 }
210- $color = { props . style . text }
211- $activeColor = { props . style . accent }
291+ $color = { ( props . navItemStyle && props . navItemStyle . text ) || props . style . text }
292+ $hoverColor = { ( props . navItemHoverStyle && props . navItemHoverStyle . text ) || props . style . accent }
293+ $activeColor = { ( props . navItemActiveStyle && props . navItemActiveStyle . text ) || props . style . accent }
212294 $fontFamily = { props . style . fontFamily }
213295 $fontStyle = { props . style . fontStyle }
214296 $textWeight = { props . style . textWeight }
215297 $textSize = { props . style . textSize }
216- $padding = { props . style . padding }
298+ $padding = { ( props . navItemStyle && props . navItemStyle . padding ) || props . style . padding }
217299 $textTransform = { props . style . textTransform }
218300 $textDecoration = { props . style . textDecoration }
219- $margin = { props . style . margin }
301+ $margin = { ( props . navItemStyle && props . navItemStyle . margin ) || props . style . margin }
302+ $bg = { ( props . navItemStyle && props . navItemStyle . background ) || undefined }
303+ $hoverBg = { ( props . navItemHoverStyle && props . navItemHoverStyle . background ) || undefined }
304+ $activeBg = { ( props . navItemActiveStyle && props . navItemActiveStyle . background ) || undefined }
305+ $border = { ( props . navItemStyle && props . navItemStyle . border ) || undefined }
306+ $hoverBorder = { ( props . navItemHoverStyle && props . navItemHoverStyle . border ) || undefined }
307+ $activeBorder = { ( props . navItemActiveStyle && props . navItemActiveStyle . border ) || undefined }
308+ $radius = { ( props . navItemStyle && props . navItemStyle . radius ) || undefined }
220309 $disabled = { disabled }
221310 onClick = { ( ) => { if ( ! disabled && onEvent ) onEvent ( "click" ) ; } }
222311 >
@@ -255,6 +344,8 @@ const NavCompBase = new UICompBuilder(childrenMap, (props) => {
255344 ) ;
256345
257346 const justify = props . horizontalAlignment === "justify" ;
347+ const isVertical = props . orientation === "vertical" ;
348+ const isHamburger = props . displayMode === "hamburger" ;
258349
259350 return (
260351 < Wrapper
@@ -265,14 +356,46 @@ const NavCompBase = new UICompBuilder(childrenMap, (props) => {
265356 $borderWidth = { props . style . borderWidth }
266357 $borderRadius = { props . style . radius }
267358 >
268- < NavInner $justify = { justify } >
269- { props . logoUrl && (
270- < LogoWrapper onClick = { ( ) => props . logoEvent ( "click" ) } >
271- < img src = { props . logoUrl } alt = "LOGO" />
272- </ LogoWrapper >
273- ) }
274- { ! justify ? < ItemList $align = { props . horizontalAlignment } > { items } </ ItemList > : items }
275- </ NavInner >
359+ { ! isHamburger && (
360+ < NavInner $justify = { justify } $orientation = { isVertical ? "vertical" : "horizontal" } >
361+ { props . logoUrl && (
362+ < LogoWrapper onClick = { ( ) => props . logoEvent ( "click" ) } >
363+ < img src = { props . logoUrl } alt = "LOGO" />
364+ </ LogoWrapper >
365+ ) }
366+ { ! justify ? < ItemList $align = { props . horizontalAlignment } $orientation = { isVertical ? "vertical" : "horizontal" } > { items } </ ItemList > : items }
367+ </ NavInner >
368+ ) }
369+ { isHamburger && (
370+ < >
371+ < FloatingHamburgerButton
372+ $size = { props . hamburgerSize || "56px" }
373+ $position = { props . hamburgerPosition || "top-right" }
374+ $zIndex = { Layers . tabBar + 1 }
375+ $background = { props . hamburgerButtonStyle ?. background }
376+ $borderColor = { props . hamburgerButtonStyle ?. border }
377+ $radius = { props . hamburgerButtonStyle ?. radius }
378+ $margin = { props . hamburgerButtonStyle ?. margin }
379+ $padding = { props . hamburgerButtonStyle ?. padding }
380+ $borderWidth = { props . hamburgerButtonStyle ?. borderWidth }
381+ $iconColor = { props . hamburgerButtonStyle ?. iconFill }
382+ onClick = { ( ) => setDrawerVisible ( true ) }
383+ >
384+ < MenuOutlined />
385+ </ FloatingHamburgerButton >
386+ < Drawer
387+ placement = { ( props . drawerPlacement as any ) || "right" }
388+ closable = { true }
389+ onClose = { ( ) => setDrawerVisible ( false ) }
390+ open = { drawerVisible }
391+ mask = { props . shadowOverlay }
392+ styles = { { body : { padding : "8px" , background : props . drawerContainerStyle ?. background } } }
393+ destroyOnClose
394+ >
395+ < ItemList $align = { "flex-start" } $orientation = { "vertical" } > { items } </ ItemList >
396+ </ Drawer >
397+ </ >
398+ ) }
276399 </ Wrapper >
277400 ) ;
278401} )
@@ -292,10 +415,21 @@ const NavCompBase = new UICompBuilder(childrenMap, (props) => {
292415
293416 { ( useContext ( EditorContext ) . editorModeStatus === "layout" || useContext ( EditorContext ) . editorModeStatus === "both" ) && (
294417 < Section name = { sectionNames . layout } >
295- { children . horizontalAlignment . propertyView ( {
296- label : trans ( "navigation.horizontalAlignment" ) ,
297- radioButton : true ,
298- } ) }
418+ { children . orientation . propertyView ( { label : "Orientation" , radioButton : true } ) }
419+ { children . displayMode . propertyView ( { label : "Display Mode" , radioButton : true } ) }
420+ { children . displayMode . getView ( ) === 'hamburger' ? (
421+ [
422+ children . hamburgerPosition . propertyView ( { label : "Hamburger Position" } ) ,
423+ children . hamburgerSize . propertyView ( { label : "Hamburger Size" } ) ,
424+ children . drawerPlacement . propertyView ( { label : "Drawer Placement" , radioButton : true } ) ,
425+ children . shadowOverlay . propertyView ( { label : "Shadow Overlay" } ) ,
426+ ]
427+ ) : (
428+ children . horizontalAlignment . propertyView ( {
429+ label : trans ( "navigation.horizontalAlignment" ) ,
430+ radioButton : true ,
431+ } )
432+ ) }
299433 { hiddenPropertyView ( children ) }
300434 </ Section >
301435 ) }
@@ -313,6 +447,25 @@ const NavCompBase = new UICompBuilder(childrenMap, (props) => {
313447 < Section name = { sectionNames . style } >
314448 { children . style . getPropertyView ( ) }
315449 </ Section >
450+ < Section name = { "Item Style" } >
451+ { children . navItemStyle . getPropertyView ( ) }
452+ </ Section >
453+ < Section name = { "Item Hover Style" } >
454+ { children . navItemHoverStyle . getPropertyView ( ) }
455+ </ Section >
456+ < Section name = { "Item Active Style" } >
457+ { children . navItemActiveStyle . getPropertyView ( ) }
458+ </ Section >
459+ { children . displayMode . getView ( ) === 'hamburger' && (
460+ < >
461+ < Section name = { "Hamburger Button Style" } >
462+ { children . hamburgerButtonStyle . getPropertyView ( ) }
463+ </ Section >
464+ < Section name = { "Drawer Container Style" } >
465+ { children . drawerContainerStyle . getPropertyView ( ) }
466+ </ Section >
467+ </ >
468+ ) }
316469 < Section name = { sectionNames . animationStyle } hasTooltip = { true } >
317470 { children . animationStyle . getPropertyView ( ) }
318471 </ Section >
0 commit comments