From 1e749d931a050febe3b794e3049a578233a86b02 Mon Sep 17 00:00:00 2001 From: Bogdanov Anton Date: Thu, 8 Feb 2024 22:26:51 +0300 Subject: [PATCH] modified closing dropdowns after outside click --- CHANGELOG.md | 1 + .../stylesheets/application.tailwind.css | 14 ++++- app/javascript/application.jsx | 11 ++++ app/javascript/assets/Chevron.jsx | 4 +- .../components/Transfers/Transfers.jsx | 22 ++------ app/javascript/components/atoms/Dropdown.jsx | 56 +++++++++++++------ app/javascript/components/atoms/Toggle.jsx | 2 +- 7 files changed, 70 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 70443803..51c5203b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## Unreleased ### Modified - navigation styles for better mobile +- closing dropdowns after outside click ## [1.1.6] - 2024-02-06 ### Added diff --git a/app/assets/stylesheets/application.tailwind.css b/app/assets/stylesheets/application.tailwind.css index ea67a414..b4989c74 100644 --- a/app/assets/stylesheets/application.tailwind.css +++ b/app/assets/stylesheets/application.tailwind.css @@ -103,7 +103,19 @@ min-height: 42px; } - .form-dropdown { + .dropdown .dropdown-content { + @apply hidden; + } + + .dropdown-toggle-icon { + @apply transition-transform rotate-0; + } + + .dropdown.opened .dropdown-toggle-icon { + @apply rotate-180; + } + + .dropdown.opened .dropdown-content { @apply block absolute z-10 left-0 w-full border border-stone-200 rounded-sm max-h-72 overflow-x-scroll; top: 42px; diff --git a/app/javascript/application.jsx b/app/javascript/application.jsx index 1a5addbb..cfd0ca61 100644 --- a/app/javascript/application.jsx +++ b/app/javascript/application.jsx @@ -58,4 +58,15 @@ document.addEventListener('DOMContentLoaded', () => { document.querySelector('.navigation-menu').classList.toggle('hidden'); }); }; + + // Clicking outside of an open dropdown menu closes it + window.addEventListener('click', function (e) { + if (!e.target.matches('.dropdown-toggle')) { + document.querySelectorAll('.dropdown.opened').forEach((form) => { + if (!form.contains(e.target)) { + form.classList.remove('opened') + } + }) + } + }) }); diff --git a/app/javascript/assets/Chevron.jsx b/app/javascript/assets/Chevron.jsx index cfb05f9c..15530ea1 100644 --- a/app/javascript/assets/Chevron.jsx +++ b/app/javascript/assets/Chevron.jsx @@ -1,13 +1,13 @@ import React from 'react'; -export const Chevron = ({ rotated }) => ( +export const Chevron = ({ className }) => ( setTeamState({ ...teamState, openDropdown: true })} - onClose={() => setTeamState({ ...teamState, openDropdown: false })} - onSelect={(value) => setTeamState({ ...teamState, uuid: value, openDropdown: false })} + onSelect={(value) => setTeamState({ ...teamState, uuid: value })} selectedValue={teamState.uuid} placeholder={strings.transfers.selectFavouriteTeam} /> @@ -641,10 +636,7 @@ export const Transfers = ({ }, { all: strings.transfers.allPlayers }, )} - isOpen={filterState.openDropdown === 'position'} - onOpen={() => setFilterState({ ...filterState, openDropdown: 'position' })} - onClose={() => setFilterState({ ...filterState, openDropdown: null })} - onSelect={(value) => setFilterState({ ...filterState, position: value, page: 0, openDropdown: null })} + onSelect={(value) => setFilterState({ ...filterState, position: value, page: 0 })} selectedValue={filterState.position} /> setFilterState({ ...filterState, openDropdown: 'team' })} - onClose={() => setFilterState({ ...filterState, openDropdown: null })} - onSelect={(value) => setFilterState({ ...filterState, team: value, page: 0, openDropdown: null })} + onSelect={(value) => setFilterState({ ...filterState, team: value, page: 0 })} selectedValue={filterState.team} /> setFilterState({ ...filterState, openDropdown: 'sortBy' })} - onClose={() => setFilterState({ ...filterState, openDropdown: null })} - onSelect={(value) => setFilterState({ ...filterState, sortBy: value, page: 0, openDropdown: null })} + onSelect={(value) => setFilterState({ ...filterState, sortBy: value, page: 0 })} selectedValue={filterState.sortBy} />
diff --git a/app/javascript/components/atoms/Dropdown.jsx b/app/javascript/components/atoms/Dropdown.jsx index 7f7264da..68b7810f 100644 --- a/app/javascript/components/atoms/Dropdown.jsx +++ b/app/javascript/components/atoms/Dropdown.jsx @@ -1,37 +1,57 @@ -import React from 'react'; +import React, { useRef } from 'react'; import { Chevron } from '../../assets'; export const Dropdown = ({ title, items, - isOpen, - onOpen, - onClose, onSelect, selectedValue, placeholder = '' -}) => ( -
- -
-
isOpen ? onClose() : onOpen()}> - {selectedValue ? items[selectedValue] : placeholder} - -
- {isOpen && ( -
    +}) => { + const dropdownRef = useRef(); + + const onDropdownToggle = () => { + const currentState = Array.from(dropdownRef.current.classList).includes('opened'); + + // close opened dropdowns + document.querySelectorAll('.dropdown.opened').forEach((form) => form.classList.remove('opened')); + + // open current dropdown if it was closed + if (!currentState) dropdownRef.current.classList.toggle('opened') + }; + + const onDropdownSelect = (value) => { + dropdownRef.current.classList.toggle('opened'); + onSelect(value); + }; + + return ( +
    + +
    +
    onDropdownToggle()} + > + {selectedValue ? items[selectedValue] : placeholder} + +
    +
      {Object.entries(items).map(([key, value]) => (
    • onSelect(key)} + onClick={() => onDropdownSelect(key)} key={key} > {value}
    • ))}
    - )} +
    -
-); + ); +}; diff --git a/app/javascript/components/atoms/Toggle.jsx b/app/javascript/components/atoms/Toggle.jsx index b39f75bd..7e265b87 100644 --- a/app/javascript/components/atoms/Toggle.jsx +++ b/app/javascript/components/atoms/Toggle.jsx @@ -9,7 +9,7 @@ export const Toggle = ({ header, children }) => {
setIsOpen(!isOpen)}>

{header}

- +
{children && isOpen ? (