1
1
import Build from "../../types/build" ;
2
2
import Change from "../../types/change" ;
3
3
import ChangeComponent from "./changeComponent" ;
4
- import React , { useState } from "react" ;
4
+ import React , { useEffect , useRef , useState } from "react" ;
5
5
import Panel from "../common/uiLibrary/panel" ;
6
+ import Button from "../common/uiLibrary/Button" ;
7
+ import { BiSolidChevronDown } from "react-icons/bi" ;
8
+ import classNames from "classnames" ;
6
9
7
10
function populateChangeList ( changes : Change [ ] ) {
8
11
let changesList ;
@@ -35,10 +38,12 @@ interface BuildProps {
35
38
build : Build
36
39
}
37
40
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 ) ;
42
47
43
48
const platforms = [
44
49
"linuxserver" ,
@@ -47,39 +52,60 @@ const BuildComponent = (props: BuildProps) => {
47
52
"StandaloneWindows64"
48
53
] ;
49
54
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
+ ) ;
78
62
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
+ }
81
71
} ;
82
72
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
+
83
109
return (
84
110
< Panel >
85
111
< div className = 'flex justify-between' >
@@ -95,7 +121,9 @@ const BuildComponent = (props: BuildProps) => {
95
121
{ changesList }
96
122
</ ul >
97
123
</ div >
98
- { renderDownloadSection ( ) }
124
+ < div className = 'relative mt-2 flex justify-end' >
125
+ < DownloadBuildDropdown version = { version_number } />
126
+ </ div >
99
127
</ Panel >
100
128
)
101
129
}
0 commit comments