|
| 1 | +import { useRef, useState, useEffect } from 'react'; |
| 2 | +import { |
| 3 | + Compass, |
| 4 | + CompassContent, |
| 5 | + CompassMainHeader, |
| 6 | + CompassPanel, |
| 7 | + Title, |
| 8 | + NavItem, |
| 9 | + NavList, |
| 10 | + Nav, |
| 11 | + Brand, |
| 12 | + MastheadLogo, |
| 13 | + MastheadBrand, |
| 14 | + MastheadContent, |
| 15 | + MastheadMain, |
| 16 | + Masthead, |
| 17 | + Toolbar, |
| 18 | + ToolbarContent, |
| 19 | + ToolbarItem, |
| 20 | + ToolbarGroup, |
| 21 | + Dropdown, |
| 22 | + DropdownList, |
| 23 | + MenuToggle, |
| 24 | + MenuToggleElement, |
| 25 | + DropdownItem, |
| 26 | + Button, |
| 27 | + ButtonVariant, |
| 28 | + Avatar, |
| 29 | + Tooltip, |
| 30 | + Divider |
| 31 | +} from '@patternfly/react-core'; |
| 32 | +import CubeIcon from '@patternfly/react-icons/dist/esm/icons/cube-icon'; |
| 33 | +import FolderIcon from '@patternfly/react-icons/dist/esm/icons/folder-icon'; |
| 34 | +import QuestionCircleIcon from '@patternfly/react-icons/dist/esm/icons/question-circle-icon'; |
| 35 | +import CloudIcon from '@patternfly/react-icons/dist/esm/icons/cloud-icon'; |
| 36 | +import CodeIcon from '@patternfly/react-icons/dist/esm/icons/code-icon'; |
| 37 | +import pfLogo from '../../assets/PF-IconLogo-color.svg'; |
| 38 | +import imgAvatar from '../../assets/avatarImg.svg'; |
| 39 | + |
| 40 | +interface NavOnSelectProps { |
| 41 | + groupId: number | string; |
| 42 | + itemId: number | string; |
| 43 | + to: string; |
| 44 | +} |
| 45 | + |
| 46 | +export const CompassDockDemo: React.FunctionComponent = () => { |
| 47 | + const [activeItem, setActiveItem] = useState<number>(0); |
| 48 | + const [isDropdownOpen, setIsDropdownOpen] = useState(false); |
| 49 | + const [isOpen, setIsOpen] = useState<boolean>(false); |
| 50 | + const menuRef = useRef<HTMLDivElement>(null); |
| 51 | + const toggleRef = useRef<HTMLButtonElement>(null); |
| 52 | + |
| 53 | + const onNavSelect = (_event: React.FormEvent<HTMLInputElement>, selectedItem: NavOnSelectProps) => { |
| 54 | + typeof selectedItem.itemId === 'number' && setActiveItem(selectedItem.itemId); |
| 55 | + }; |
| 56 | + |
| 57 | + const onDropdownToggle = () => { |
| 58 | + setIsDropdownOpen(!isDropdownOpen); |
| 59 | + }; |
| 60 | + |
| 61 | + const onDropdownSelect = () => { |
| 62 | + setIsDropdownOpen(!isDropdownOpen); |
| 63 | + }; |
| 64 | + |
| 65 | + const handleMenuKeys = (event: KeyboardEvent) => { |
| 66 | + if (!isOpen) { |
| 67 | + return; |
| 68 | + } |
| 69 | + if (menuRef.current?.contains(event.target as Node) || toggleRef.current?.contains(event.target as Node)) { |
| 70 | + if (event.key === 'Escape') { |
| 71 | + setIsOpen(!isOpen); |
| 72 | + toggleRef.current?.focus(); |
| 73 | + } |
| 74 | + } |
| 75 | + }; |
| 76 | + |
| 77 | + const handleClickOutside = (event: MouseEvent) => { |
| 78 | + if (isOpen && !menuRef.current?.contains(event.target as Node)) { |
| 79 | + setIsOpen(false); |
| 80 | + } |
| 81 | + }; |
| 82 | + |
| 83 | + useEffect(() => { |
| 84 | + window.addEventListener('keydown', handleMenuKeys); |
| 85 | + window.addEventListener('click', handleClickOutside); |
| 86 | + |
| 87 | + return () => { |
| 88 | + window.removeEventListener('keydown', handleMenuKeys); |
| 89 | + window.removeEventListener('click', handleClickOutside); |
| 90 | + }; |
| 91 | + }, [isOpen, menuRef]); |
| 92 | + |
| 93 | + const userDropdownItems = [ |
| 94 | + <> |
| 95 | + <DropdownItem key="group 2 profile">My profile</DropdownItem> |
| 96 | + <DropdownItem key="group 2 user">User management</DropdownItem> |
| 97 | + <DropdownItem key="group 2 logout">Logout</DropdownItem> |
| 98 | + </> |
| 99 | + ]; |
| 100 | + |
| 101 | + const navItem1Ref = useRef<HTMLAnchorElement>(null); |
| 102 | + const navItem2Ref = useRef<HTMLAnchorElement>(null); |
| 103 | + const navItem3Ref = useRef<HTMLAnchorElement>(null); |
| 104 | + const navItem4Ref = useRef<HTMLAnchorElement>(null); |
| 105 | + const dockContent = ( |
| 106 | + <Masthead id="icon-router-link" variant="docked"> |
| 107 | + <MastheadMain> |
| 108 | + <MastheadBrand> |
| 109 | + <MastheadLogo component={(props) => <a {...props} href="#" />}> |
| 110 | + <Brand src={pfLogo} alt="PatternFly" heights={{ default: '36px' }} /> |
| 111 | + </MastheadLogo> |
| 112 | + </MastheadBrand> |
| 113 | + </MastheadMain> |
| 114 | + <Divider /> |
| 115 | + <MastheadContent> |
| 116 | + <Toolbar id="toolbar" isVertical> |
| 117 | + <ToolbarContent> |
| 118 | + <ToolbarItem> |
| 119 | + <Nav onSelect={onNavSelect} variant="docked" aria-label="Icon global" ouiaId="IconNav"> |
| 120 | + <NavList> |
| 121 | + <NavItem |
| 122 | + key="nav-icon-link1" |
| 123 | + preventDefault |
| 124 | + id="nav-icon-link1" |
| 125 | + to="#nav-icon-link1" |
| 126 | + itemId={0} |
| 127 | + isActive={activeItem === 0} |
| 128 | + icon={<CubeIcon />} |
| 129 | + ref={navItem1Ref} |
| 130 | + /> |
| 131 | + <NavItem |
| 132 | + key="nav-icon-link2" |
| 133 | + preventDefault |
| 134 | + id="nav-icon-link2" |
| 135 | + to="#nav-icon-link2" |
| 136 | + itemId={1} |
| 137 | + isActive={activeItem === 1} |
| 138 | + icon={<FolderIcon />} |
| 139 | + ref={navItem2Ref} |
| 140 | + /> |
| 141 | + <NavItem |
| 142 | + key="nav-icon-link3" |
| 143 | + preventDefault |
| 144 | + id="nav-icon-link3" |
| 145 | + to="#nav-icon-link3" |
| 146 | + itemId={0} |
| 147 | + isActive={activeItem === 2} |
| 148 | + icon={<CloudIcon />} |
| 149 | + ref={navItem3Ref} |
| 150 | + /> |
| 151 | + <NavItem |
| 152 | + key="nav-icon-link4" |
| 153 | + preventDefault |
| 154 | + id="nav-icon-link4" |
| 155 | + to="#nav-icon-link4" |
| 156 | + itemId={0} |
| 157 | + isActive={activeItem === 3} |
| 158 | + icon={<CodeIcon />} |
| 159 | + ref={navItem4Ref} |
| 160 | + /> |
| 161 | + </NavList> |
| 162 | + </Nav> |
| 163 | + <Tooltip triggerRef={navItem1Ref} content="Link 1"></Tooltip> |
| 164 | + <Tooltip triggerRef={navItem2Ref} content="Link 2"></Tooltip> |
| 165 | + <Tooltip triggerRef={navItem3Ref} content="Link 3"></Tooltip> |
| 166 | + <Tooltip triggerRef={navItem4Ref} content="Link 4"></Tooltip> |
| 167 | + </ToolbarItem> |
| 168 | + <ToolbarGroup |
| 169 | + variant="action-group-plain" |
| 170 | + align={{ default: 'alignEnd' }} |
| 171 | + gap={{ default: 'gapNone', md: 'gapMd' }} |
| 172 | + > |
| 173 | + <ToolbarGroup variant="action-group-plain" visibility={{ default: 'hidden', lg: 'visible' }}> |
| 174 | + <ToolbarItem> |
| 175 | + <Button aria-label="Settings" isSettings variant="plain" /> |
| 176 | + </ToolbarItem> |
| 177 | + <ToolbarItem> |
| 178 | + <Button aria-label="Help" variant={ButtonVariant.plain} icon={<QuestionCircleIcon />} /> |
| 179 | + </ToolbarItem> |
| 180 | + </ToolbarGroup> |
| 181 | + </ToolbarGroup> |
| 182 | + <ToolbarItem> |
| 183 | + <Dropdown |
| 184 | + isOpen={isDropdownOpen} |
| 185 | + onSelect={onDropdownSelect} |
| 186 | + onOpenChange={(isOpen: boolean) => setIsDropdownOpen(isOpen)} |
| 187 | + toggle={(toggleRef: React.Ref<MenuToggleElement>) => ( |
| 188 | + <MenuToggle |
| 189 | + ref={toggleRef} |
| 190 | + onClick={onDropdownToggle} |
| 191 | + isExpanded={isDropdownOpen} |
| 192 | + icon={<Avatar src={imgAvatar} alt="" size="sm" />} |
| 193 | + variant="plain" |
| 194 | + ></MenuToggle> |
| 195 | + )} |
| 196 | + > |
| 197 | + <DropdownList>{userDropdownItems}</DropdownList> |
| 198 | + </Dropdown> |
| 199 | + </ToolbarItem> |
| 200 | + </ToolbarContent> |
| 201 | + </Toolbar> |
| 202 | + </MastheadContent> |
| 203 | + </Masthead> |
| 204 | + ); |
| 205 | + |
| 206 | + const mainContent = ( |
| 207 | + <> |
| 208 | + <CompassMainHeader title={<Title headingLevel="h1">Content title</Title>} /> |
| 209 | + <CompassContent> |
| 210 | + <CompassPanel>Content</CompassPanel> |
| 211 | + </CompassContent> |
| 212 | + </> |
| 213 | + ); |
| 214 | + |
| 215 | + return ( |
| 216 | + <Compass |
| 217 | + dock={dockContent} |
| 218 | + main={mainContent} |
| 219 | + backgroundSrcDark="/assets/images/pf-background.svg" |
| 220 | + backgroundSrcLight="/assets/images/pf-background.svg" |
| 221 | + /> |
| 222 | + ); |
| 223 | +}; |
0 commit comments