11import Build from "../../types/build" ;
22import Change from "../../types/change" ;
33import ChangeComponent from "./changeComponent" ;
4- import React , { useState } from "react" ;
4+ import React , { useEffect , useRef , useState } from "react" ;
55import Panel from "../common/uiLibrary/panel" ;
6+ import Button from "../common/uiLibrary/Button" ;
7+ import { BiSolidChevronDown } from "react-icons/bi" ;
8+ import classNames from "classnames" ;
69
710function populateChangeList ( changes : Change [ ] ) {
811 let changesList ;
@@ -35,10 +38,12 @@ interface BuildProps {
3538 build : Build
3639}
3740
38- const BuildComponent = ( props : BuildProps ) => {
39- const { version_number, date_created, changes } = props . build ;
40- const changesList = populateChangeList ( changes ) ;
41- const [ isDropdownOpen , setIsDropdownOpen ] = useState ( false ) ;
41+ //TODO: make this component a general dropdown instead and move it to commons
42+ const DownloadBuildDropdown = ( props : { version : string } ) => {
43+ 'use client'
44+
45+ const [ isOpen , setIsOpen ] = useState ( false ) ;
46+ const dropdownRef = useRef < HTMLDivElement > ( null ) ;
4247
4348 const platforms = [
4449 "linuxserver" ,
@@ -47,39 +52,60 @@ const BuildComponent = (props: BuildProps) => {
4752 "StandaloneWindows64"
4853 ] ;
4954
50- const renderDownloadSection = ( ) => {
51- return (
52- < div className = "relative mt-2 flex justify-end" >
53- < button
54- onClick = { toggleDropdown }
55- className = "bg-blue-500 hover:bg-blue-700 text-white font-bold py-1 px-4 rounded"
56- >
57- Download
58- </ button >
59- { isDropdownOpen && (
60- < div className = "absolute right-0 mt-12 w-56 rounded-md shadow-lg bg-slate-600 ring-1 ring-black ring-opacity-5" >
61- < div className = "py-1" role = "menu" aria-orientation = "vertical" aria-labelledby = "options-menu" >
62- { platforms . map ( ( platform ) => (
63- < a
64- key = { platform }
65- href = { `https://unitystationfile.b-cdn.net/UnityStationDevelop/${ platform } /${ version_number } .zip` }
66- className = "block px-4 py-2 text-sm text-blue-50 hover:bg-gray-100 hover:text-gray-900"
67- role = "menuitem"
68- >
69- { platform }
70- </ a >
71- ) ) }
72- </ div >
73- </ div >
74- ) }
75- </ div >
76- ) ;
77- } ;
55+ const listClasses = classNames (
56+ 'absolute top-full mt-2 w-56 rounded-md shadow-lg bg-slate-600 ring-1 ring-black ring-opacity-5 transition-opacity transition-transform duration-500 ease-out' ,
57+ {
58+ 'opacity-100 translate-y-0' : isOpen ,
59+ 'opacity-0 -translate-y-4 pointer-events-none' : ! isOpen
60+ }
61+ ) ;
7862
79- const toggleDropdown = ( ) => {
80- setIsDropdownOpen ( ! isDropdownOpen ) ;
63+ const handleClick = ( ) => {
64+ setIsOpen ( ! isOpen ) ;
65+ }
66+
67+ const handleClickOutside = ( event : MouseEvent ) => {
68+ if ( dropdownRef . current && ! dropdownRef . current . contains ( event . target as Node ) ) {
69+ setIsOpen ( false ) ;
70+ }
8171 } ;
8272
73+ useEffect ( ( ) => {
74+ if ( isOpen ) {
75+ document . addEventListener ( 'mousedown' , handleClickOutside ) ;
76+ } else {
77+ document . removeEventListener ( 'mousedown' , handleClickOutside ) ;
78+ }
79+ return ( ) => document . removeEventListener ( 'mousedown' , handleClickOutside ) ;
80+ } , [ isOpen ] ) ;
81+
82+ return (
83+ < div className = "relative" ref = { dropdownRef } >
84+ < Button upperCase = { false } iconRight = { BiSolidChevronDown } onClick = { handleClick } >
85+ Download
86+ </ Button >
87+ < div className = { listClasses } >
88+ < div className = "py-1" role = "menu" aria-orientation = "vertical" aria-labelledby = "options-menu" >
89+ { platforms . map ( ( platform ) => (
90+ < a
91+ key = { platform }
92+ href = { `https://unitystationfile.b-cdn.net/UnityStationDevelop/${ platform } /${ props . version } .zip` }
93+ className = "block px-4 py-2 text-sm text-blue-50 hover:bg-gray-100 hover:text-gray-900"
94+ role = "menuitem"
95+ >
96+ { platform }
97+ </ a >
98+ ) ) }
99+ </ div >
100+ </ div >
101+ </ div >
102+ ) ;
103+ }
104+
105+ const BuildComponent = ( props : BuildProps ) => {
106+ const { version_number, date_created, changes } = props . build ;
107+ const changesList = populateChangeList ( changes ) ;
108+
83109 return (
84110 < Panel >
85111 < div className = 'flex justify-between' >
@@ -95,7 +121,9 @@ const BuildComponent = (props: BuildProps) => {
95121 { changesList }
96122 </ ul >
97123 </ div >
98- { renderDownloadSection ( ) }
124+ < div className = 'relative mt-2 flex justify-end' >
125+ < DownloadBuildDropdown version = { version_number } />
126+ </ div >
99127 </ Panel >
100128 )
101129}
0 commit comments