From 1d42e84cb7552be1e72f9578612807f4937cfb0a Mon Sep 17 00:00:00 2001 From: mrskycriper <32013287+mrskycriper@users.noreply.github.com> Date: Thu, 1 Feb 2024 17:57:41 +0300 Subject: [PATCH 01/14] feat(i18n): crop transparency from flags --- src/i18n/flags/it.png | Bin 982 -> 2438 bytes src/i18n/flags/no.png | Bin 952 -> 2573 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/src/i18n/flags/it.png b/src/i18n/flags/it.png index 1f50482aa2dfac6eed4922f29bf4cbc76445b7c9..2ab69d5266ddebd8f6e4ab3d0b15a1a2d0c6d164 100644 GIT binary patch literal 2438 zcmeHIeNYr-7=HmVMMX*tiWs()37x##-TUCUjScaR;1~`LybP>H_IBTcjl11tcY%Yk zU?{)R(X??g1|@Q;A=3ZSlrc2&W3<7b7{(@srkTEM;>JvKbj;p8jvICC%fI@=o4wn2 z-~GMM^Zb6#$8GYI70pX}I0*nS&sFTK1RxGA!O@g>^lQnyte_2>;Ba^fc*zH`%Ayt6 z63TART?)XG?K`&(lni!XXJ5st`W!pWo*9cOw8$L~&2+wLCbN5eLPF!mGOe+-kH>l z&)({OA!UAhZ`oJYl=9`hZ(K|FBq=vQ$>~dLQ`WSfQ<=;y_M>x^HF2dG&)2uF-jV!e zb=Z66eONs*`B*{Lw)@`PI%GbSxnWbug3;lM)KlSv8Y(kj%Q&>^!w-&JYWst^f9xU@ zyNhNmU&f?X7le9@ZBM5?@%qoclk!8!VYb!F_uJ%yadQq99$3BZ*9Y^G+rZHi<<~E+ z%s+*coGlkus{qVdpnu~)XIB~kGcE|;Dy_<0N^?@s$m;qULqS=WALQkSWR|Ok8s>ww zg2>?E{s9~lcm{vQ>L%T?1J((}tqNS(TIS_i>p44*=Rb+%g=l0T2sIW91p}f=hZsC! zmqu%So4~OMM5|};sf1R!J(xpMAeLh!4ID|DF`L~;ySq(Bc%A3VN zQU){WrE+M}Mw_gUlV+MEr=V_$7yK=^K`9+-ve0JREl^Z6Jgc$)2I7RO>j(heygDabL3&K!xt?WSF#s<9#mT}}o^tc`-e({|2cHv8<7^(QVWaQmVeLD?D!$4ogrVx6F%`qsV;iaG{wQu;nP5|PsxSR#vP}eV`J;4qZ zfZ@!A2lgbsmbK_qQcd!*J%vBUmwqxcE3KpVeDcmkfn(W|M`jO=2Rc7Kd+oY&H&@H*=Iy$K*~5P-Mm0_^m?+dT>XS5dz>;o|jGUTiOzN@u7|?f7=S@u)E;cgYmJ3oMN*RwT2joOEBfF@BEmp-^)spqZ3_ zzPU%6qHHl_zB!=+B9zo$Rtl!KbLh*2~7ZGTX#|b diff --git a/src/i18n/flags/no.png b/src/i18n/flags/no.png index 0514ea0815a012757f472891ba98daaa32ffe716..17054c0f7c4f5cb6c1641642df161b13c53cb4ac 100644 GIT binary patch literal 2573 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 From 0fa9b158f964ffe7ed6181c95b36b7f86ffb2b7d Mon Sep 17 00:00:00 2001 From: mrskycriper <32013287+mrskycriper@users.noreply.github.com> Date: Thu, 1 Feb 2024 18:00:02 +0300 Subject: [PATCH 02/14] feat(ui): rework theme switching --- src/components/theme-switcher.tsx | 86 ++++++++++--- src/index.tsx | 202 +++++++++++++++--------------- src/styles/styles.global.scss | 3 + 3 files changed, 169 insertions(+), 122 deletions(-) diff --git a/src/components/theme-switcher.tsx b/src/components/theme-switcher.tsx index bf7194030..c45081d9d 100644 --- a/src/components/theme-switcher.tsx +++ b/src/components/theme-switcher.tsx @@ -1,28 +1,74 @@ import * as React from 'react'; -import { useThemeSwitcher } from 'react-css-theme-switcher'; -import Button from './button'; -export type Theme = 'light' | 'dark'; +import Dropdown from 'react-bootstrap/Dropdown'; -type ThemeSwitcherProps = { - saveCurrentTheme(theme: Theme): void; -}; -export const ThemeSwitcher = (props: ThemeSwitcherProps): JSX.Element => { - const { saveCurrentTheme } = props; - const { switcher, themes, status, currentTheme } = useThemeSwitcher(); - const isDarkMode = currentTheme === 'dark'; +export const ThemeSwitcher = (): JSX.Element => { + const getStoredTheme = () => localStorage.getItem('theme') + const setStoredTheme = theme => localStorage.setItem('theme', theme) + + const getPreferredTheme = () => { + const storedTheme = getStoredTheme() + if (storedTheme) { + return storedTheme + } + + return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light' + } - if (status === 'loading') { - return
Loading styles...
; + const setTheme = theme => { + if (theme === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches) { + document.documentElement.setAttribute('data-bs-theme', 'dark') + } else { + document.documentElement.setAttribute('data-bs-theme', theme) + } } - const toggleDarkMode = (light: boolean) => { - const theme = light ? themes.light : themes.dark; - saveCurrentTheme(theme as Theme); - switcher({ theme }); - }; + + setTheme(getPreferredTheme()) + + window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => { + const storedTheme = getStoredTheme() + if (storedTheme !== 'light' && storedTheme !== 'dark') { + setTheme(getPreferredTheme()) + } + }) + + const updateTheme = (theme: string) => { + setStoredTheme(theme) + setTheme(getPreferredTheme()) + const icon = document.getElementById("theme-dropdown-icon") + icon!.className = getCurrentThemeClass() + } + + const getCurrentThemeClass = () => { + const theme = getStoredTheme() + let icon = "fa-solid me-1" + switch (theme) { + case 'light': { icon += " fa-sun"; break } + case 'dark': { icon += " fa-moon"; break } + default: { icon += " fa-circle-half-stroke"; break } + } + return icon + } + return ( - item={isDarkMode} className="btn btn-info" onClick={toggleDarkMode}> - {isDarkMode ? '🌑' : `🌞`} - + + + + + + { updateTheme('light') }}> + + Light {/* probably something like t('theme:light')*/} + + { updateTheme('dark') }}> + + Dark {/* probably something like t('theme:dark')*/} + + { updateTheme('auto') }}> + + Auto {/* probably something like t('theme:auto')*/} + + + ); }; diff --git a/src/index.tsx b/src/index.tsx index a73a4ec5b..1dd26e94b 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -54,109 +54,107 @@ const Main = () => { - - - -
- -
-
- - ( - - - - )} - /> - ( - - - - )} - /> - ( - - - - )} - /> - ( - - - - )} - /> - ( - - - - )} - /> - ( - - - - )} - /> + + +
+ +
+
+ + ( + + + + )} + /> + ( + + + + )} + /> + ( + + + + )} + /> + ( + + + + )} + /> + ( + + + + )} + /> + ( + + + + )} + /> - ( - - - - )} - /> - ( - - - - )} - /> - ( - - - - )} - /> - ( - - - - )} - /> - ( - - - - )} - /> - -
-
-
-
- + ( + + + + )} + /> + ( + + + + )} + /> + ( + + + + )} + /> + ( + + + + )} + /> + ( + + + + )} + /> +
+
+
+
+
diff --git a/src/styles/styles.global.scss b/src/styles/styles.global.scss index 76e3b5c40..5d2ba16d7 100644 --- a/src/styles/styles.global.scss +++ b/src/styles/styles.global.scss @@ -68,3 +68,6 @@ body { display: none; } } + +// Import all of Bootstrap's CSS +@import "bootstrap/scss/bootstrap"; From 4f7d10683e30986926de0712535f9c706b1fcbdc Mon Sep 17 00:00:00 2001 From: mrskycriper <32013287+mrskycriper@users.noreply.github.com> Date: Thu, 1 Feb 2024 18:01:01 +0300 Subject: [PATCH 03/14] feat(navbar): make responsive and update design --- src/components/navbar/StartStopJoinButton.tsx | 59 +++++++++--------- src/components/navbar/index.tsx | 53 +++++++++++----- src/i18n/LocalePicker.tsx | 50 +++++++-------- src/images/logo.png | Bin 0 -> 83057 bytes 4 files changed, 86 insertions(+), 76 deletions(-) create mode 100644 src/images/logo.png diff --git a/src/components/navbar/StartStopJoinButton.tsx b/src/components/navbar/StartStopJoinButton.tsx index aa1306e5a..353346d38 100644 --- a/src/components/navbar/StartStopJoinButton.tsx +++ b/src/components/navbar/StartStopJoinButton.tsx @@ -7,6 +7,9 @@ import { useTranslation } from 'react-i18next'; import { toHHMMSS } from '../../utils'; import { BridgeApi } from '../../actions/BridgeApi'; import { GlobalState } from '../../store'; +import Dropdown from 'react-bootstrap/Dropdown'; +import BootstrapButton from 'react-bootstrap/Button'; +import ButtonGroup from 'react-bootstrap/ButtonGroup'; export type StartStopJoinButtonProps = Pick & Pick; @@ -16,9 +19,8 @@ export function StartStopJoinButton({ devices, setPermitJoin, bridgeInfo }: Star const [selectedRouter, setSelectedRouter] = useState({} as Device); const { permit_join: permitJoin, permit_join_timeout: permitJoinTimeout } = bridgeInfo; - const selectAndHide = (device: Device) => { - setSelectedRouter(device); - setIsComponentVisible(false); + const select = (device?: Device) => { + setSelectedRouter(device? device : {} as Device ); }; const sortByName = (a: Device, b: Device) => a.friendly_name.localeCompare(b.friendly_name); const prioritizeCoordinator = (a: Device, b: Device) => @@ -28,11 +30,9 @@ export function StartStopJoinButton({ devices, setPermitJoin, bridgeInfo }: Star .sort(sortByName) .sort(prioritizeCoordinator) .map((device) => ( -
  • - item={device} className="dropdown-item" onClick={selectAndHide}> - {device.friendly_name} - -
  • + {select(device)}}> + {device.friendly_name} + )); const onBtnClick = () => { @@ -40,8 +40,8 @@ export function StartStopJoinButton({ devices, setPermitJoin, bridgeInfo }: Star }; const permitJoinTimer = ( <> - {permitJoinTimeout ? ( -
    + {permitJoin ? ( +
    {toHHMMSS(permitJoinTimeout)}
    ) : null} @@ -49,38 +49,37 @@ export function StartStopJoinButton({ devices, setPermitJoin, bridgeInfo }: Star ); const buttonLabel = ( <> - {permitJoin ? t('disable_join') : t('permit_join')} ({selectedRouter?.friendly_name ?? t('all')}) + {permitJoin ? t('disable_join') : t('permit_join')} {permitJoinTimer} ); return ( -
    - + {routers.length ? ( <> - - type="button" - className="btn btn-outline-secondary dropdown-toggle dropdown-toggle-split" - onClick={setIsComponentVisible} - item={!isComponentVisible} + {t('toggle_dropdown')} - -
      } - className={cx('dropdown-menu', { show: isComponentVisible })} - > -
    • - -
    • + {routers} -
    + ) : null} -
    + ); } diff --git a/src/components/navbar/index.tsx b/src/components/navbar/index.tsx index bb44017aa..6139213a3 100644 --- a/src/components/navbar/index.tsx +++ b/src/components/navbar/index.tsx @@ -14,6 +14,13 @@ import LocalePicker from '../../i18n/LocalePicker'; import { isIframe } from '../../utils'; import { StartStopJoinButton } from './StartStopJoinButton'; +import BootstrapButton from 'react-bootstrap/Button'; +import Navbar from 'react-bootstrap/Navbar'; +import Container from 'react-bootstrap/Container'; +import Nav from 'react-bootstrap/Nav'; +import Collapse from 'react-bootstrap/esm/Collapse'; +import NavbarBrand from 'react-bootstrap/esm/NavbarBrand'; + const urls = [ { href: '/', @@ -57,18 +64,29 @@ const urls = [ type PropsFromStore = Pick; const NavBar: FunctionComponent & BridgeApi> = (props) => { - const { devices, setPermitJoin, bridgeInfo, restartBridge, setTheme, t } = props; + const { devices, setPermitJoin, bridgeInfo, restartBridge, t } = props; const ref = useRef(); const [navbarIsVisible, setNavbarIsVisible] = useState(false); useOnClickOutside(ref, () => { setNavbarIsVisible(false); }); return ( -