diff --git a/CHANGELOG.md b/CHANGELOG.md index cb067fe8..51bb4c40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,12 +23,17 @@ See also the [v0.107.40 GitHub milestone][ms-v0.107.40]. NOTE: Add new changes BELOW THIS COMMENT. --> +### Changed + +- "Block" and "Unblock" buttons of the query log moved to the tooltip menu ([#684]). + ### Fixed - The time shown in the statistics is one hour less than the current time ([#6296]). - Issues with QUIC and HTTP/3 upstreams on FreeBSD ([#6301]). - Panic on clearing query log ([#6304]). +[#684]: https://github.com/AdguardTeam/AdGuardHome/issues/684 [#6296]: https://github.com/AdguardTeam/AdGuardHome/issues/6296 [#6301]: https://github.com/AdguardTeam/AdGuardHome/issues/6301 [#6304]: https://github.com/AdguardTeam/AdGuardHome/issues/6304 diff --git a/client/src/components/App/index.css b/client/src/components/App/index.css index 78bd0096..91cd1749 100644 --- a/client/src/components/App/index.css +++ b/client/src/components/App/index.css @@ -134,15 +134,6 @@ body { cursor: not-allowed; } -.button-action { - visibility: hidden; -} - -.logs__row:hover .button-action, -.button-action--active { - visibility: visible; -} - .ReactModal__Body--open { overflow: hidden; } diff --git a/client/src/components/Dashboard/Clients.js b/client/src/components/Dashboard/Clients.js index 76cf998a..ac243491 100644 --- a/client/src/components/Dashboard/Clients.js +++ b/client/src/components/Dashboard/Clients.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState } from 'react'; import ReactTable from 'react-table'; import PropTypes from 'prop-types'; import { Trans, useTranslation } from 'react-i18next'; @@ -13,6 +13,7 @@ import { BLOCK_ACTIONS, STATUS_COLORS } from '../../helpers/constants'; import { toggleClientBlock } from '../../actions/access'; import { renderFormattedClientCell } from '../../helpers/renderFormattedClientCell'; import { getStats } from '../../actions/stats'; +import IconTooltip from '../Logs/Cells/IconTooltip'; const getClientsPercentColor = (percent) => { if (percent > 50) { @@ -40,9 +41,7 @@ const renderBlockingButton = (ip, disallowed, disallowed_rule) => { const processingSet = useSelector((state) => state.access.processingSet); const allowedСlients = useSelector((state) => state.access.allowed_clients, shallowEqual); - const buttonClass = classNames('button-action button-action--main', { - 'button-action--unblock': disallowed, - }); + const [isOptionsOpened, setOptionsOpened] = useState(false); const toggleClientStatus = async (ip, disallowed, disallowed_rule) => { let confirmMessage; @@ -62,23 +61,49 @@ const renderBlockingButton = (ip, disallowed, disallowed_rule) => { } }; - const onClick = () => toggleClientStatus(ip, disallowed, disallowed_rule); + const onClick = () => { + toggleClientStatus(ip, disallowed, disallowed_rule); + setOptionsOpened(false); + }; const text = disallowed ? BLOCK_ACTIONS.UNBLOCK : BLOCK_ACTIONS.BLOCK; const lastRuleInAllowlist = !disallowed && allowedСlients === disallowed_rule; const disabled = processingSet || lastRuleInAllowlist; return ( -
+
+ {isOptionsOpened && ( + + {text} + + )} + placement="bottom-end" + trigger="click" + onVisibilityChange={setOptionsOpened} + defaultTooltipShown={true} + delayHide={0} + /> + )}
); }; diff --git a/client/src/components/Dashboard/Dashboard.css b/client/src/components/Dashboard/Dashboard.css index 765c9ed1..67a69aec 100644 --- a/client/src/components/Dashboard/Dashboard.css +++ b/client/src/components/Dashboard/Dashboard.css @@ -28,11 +28,13 @@ border-bottom: 6px solid #585965; } -@media (max-width: 1279.98px) { - .table__action { - position: absolute; - right: 0; - } +.table__action { + position: relative; + margin-left: auto; +} + +.table__action .btn-icon { + margin: 2px; } .page-title--dashboard { diff --git a/client/src/components/Logs/Cells/ClientCell.js b/client/src/components/Logs/Cells/ClientCell.js index 3590df48..d0dd3168 100644 --- a/client/src/components/Logs/Cells/ClientCell.js +++ b/client/src/components/Logs/Cells/ClientCell.js @@ -26,9 +26,7 @@ const ClientCell = ({ const { t } = useTranslation(); const dispatch = useDispatch(); const autoClients = useSelector((state) => state.dashboard.autoClients, shallowEqual); - const processingRules = useSelector((state) => state.filtering.processingRules); const isDetailed = useSelector((state) => state.queryLogs.isDetailed); - const processingSet = useSelector((state) => state.access.processingSet); const allowedСlients = useSelector((state) => state.access.allowed_clients, shallowEqual); const [isOptionsOpened, setOptionsOpened] = useState(false); @@ -84,11 +82,23 @@ const ClientCell = ({ const blockingForClientKey = isFiltered ? 'unblock_for_this_client_only' : 'block_for_this_client_only'; const clientNameBlockingFor = getBlockingClientName(clients, client); + const onClick = async () => { + await dispatch(toggleBlocking(buttonType, domain)); + await dispatch(getStats()); + setOptionsOpened(false); + }; + const BUTTON_OPTIONS = [ + { + name: buttonType, + onClick, + className: isFiltered ? 'bg--green' : 'bg--danger', + }, { name: blockingForClientKey, onClick: () => { dispatch(toggleBlockingForClient(buttonType, domain, clientNameBlockingFor)); + setOptionsOpened(false); }, }, { @@ -101,27 +111,25 @@ const ClientCell = ({ client_info?.disallowed_rule || '', )); await dispatch(updateLogs()); + setOptionsOpened(false); } }, - disabled: processingSet || lastRuleInAllowlist, + disabled: lastRuleInAllowlist, }, ]; - const onClick = async () => { - await dispatch(toggleBlocking(buttonType, domain)); - await dispatch(getStats()); - }; - const getOptions = (options) => { if (options.length === 0) { return null; } return ( <> - {options.map(({ name, onClick, disabled }) => ( + {options.map(({ + name, onClick, disabled, className, + }) => ( - {content && ( - + {isOptionsOpened && ( + )}
); @@ -198,7 +196,7 @@ const ClientCell = ({ {isDetailed && clientName && !whoisAvailable && ( diff --git a/client/src/components/Logs/Cells/IconTooltip.css b/client/src/components/Logs/Cells/IconTooltip.css index 0b55ee6a..bbaf1604 100644 --- a/client/src/components/Logs/Cells/IconTooltip.css +++ b/client/src/components/Logs/Cells/IconTooltip.css @@ -1,4 +1,5 @@ .tooltip-custom__container { + min-width: 150px; padding: 1rem 1.5rem 1.25rem 1.5rem; font-size: 16px !important; box-shadow: 2px 4px 8px rgba(0, 0, 0, 0.2); diff --git a/client/src/components/Logs/Cells/IconTooltip.js b/client/src/components/Logs/Cells/IconTooltip.js index 8bb3d624..304aded8 100644 --- a/client/src/components/Logs/Cells/IconTooltip.js +++ b/client/src/components/Logs/Cells/IconTooltip.js @@ -21,6 +21,8 @@ const IconTooltip = ({ content, trigger, onVisibilityChange, + defaultTooltipShown, + delayHide, renderContent = content ? React.Children.map( processContent(content), (item, idx) =>
@@ -44,6 +46,8 @@ const IconTooltip = ({ trigger={trigger} onVisibilityChange={onVisibilityChange} delayShow={trigger === 'click' ? 0 : SHOW_TOOLTIP_DELAY} + delayHide={delayHide} + defaultTooltipShown={defaultTooltipShown} > {xlinkHref && @@ -65,6 +69,8 @@ IconTooltip.propTypes = { content: PropTypes.node, renderContent: PropTypes.arrayOf(PropTypes.element), onVisibilityChange: PropTypes.func, + defaultTooltipShown: PropTypes.bool, + delayHide: PropTypes.number, }; export default IconTooltip; diff --git a/client/src/components/Logs/Logs.css b/client/src/components/Logs/Logs.css index 762dfa50..4935192a 100644 --- a/client/src/components/Logs/Logs.css +++ b/client/src/components/Logs/Logs.css @@ -80,6 +80,10 @@ color: var(--gray-f3); } +.logs__text--client { + padding-right: 32px; +} + .icon--selected { background-color: var(--gray-f3); border: solid 1px var(--gray-d8); @@ -261,9 +265,8 @@ .button-action__container { display: flex; position: absolute; - right: 0; + right: 2px; bottom: 0.5rem; - height: 1.6rem; } @media screen and (max-width: 1024px) { @@ -307,45 +310,10 @@ border-bottom-right-radius: 0; } -.button-action--arrow { - border-top-left-radius: 0; - border-bottom-left-radius: 0; - border-left: 1px solid var(--white); - width: 1.5625rem; - padding: 0; - display: flex; - align-items: center; - justify-content: center; -} - .button-action:hover { cursor: pointer; } -.button-action--arrow .button-action--icon { - width: 100%; - height: 100%; - display: flex; - justify-content: center; -} - -.button-action:active { - background: var(--btn-block-active); -} - -.button-action--unblock:active { - background: var(--btn-unblock-active); -} - -.button-action:disabled { - background: var(--btn-block-disabled); - cursor: default; -} - -.button-action--unblock:disabled { - background: var(--btn-unblock-disabled); -} - .button-action--arrow-option { background: transparent; border: 0; @@ -551,3 +519,20 @@ padding: 1rem 1.5rem; background-color: var(--card-bgcolor); } + +.button-action__hidden-trigger { + position: absolute; + top: 0; + right: 0; + width: 1px; + height: 33px; + margin: -1px; + padding: 0; + overflow: hidden; + border: 0; + clip: rect(0 0 0 0); +} + +[data-theme="dark"] .button-action__icon { + color: var(--gray-f3); +} diff --git a/client/src/components/ui/Icons.js b/client/src/components/ui/Icons.js index 288eadfc..af5ca52c 100644 --- a/client/src/components/ui/Icons.js +++ b/client/src/components/ui/Icons.js @@ -239,6 +239,12 @@ const Icons = () => ( + + + + + + ); diff --git a/client/src/components/ui/Tooltip.js b/client/src/components/ui/Tooltip.js index 87b353de..b72a3667 100644 --- a/client/src/components/ui/Tooltip.js +++ b/client/src/components/ui/Tooltip.js @@ -21,6 +21,7 @@ const Tooltip = ({ delayShow = SHOW_TOOLTIP_DELAY, delayHide = HIDE_TOOLTIP_DELAY, onVisibilityChange, + defaultTooltipShown, }) => { const { t } = useTranslation(); const touchEventsAvailable = 'ontouchstart' in window; @@ -75,6 +76,7 @@ const Tooltip = ({ delayShow={delayShowValue} tooltip={renderTooltip} onVisibilityChange={onVisibilityChange} + defaultTooltipShown={defaultTooltipShown} > {renderTrigger} @@ -97,6 +99,7 @@ Tooltip.propTypes = { className: propTypes.string, triggerClass: propTypes.string, onVisibilityChange: propTypes.func, + defaultTooltipShown: propTypes.bool, }; export default Tooltip; diff --git a/client/src/helpers/renderFormattedClientCell.js b/client/src/helpers/renderFormattedClientCell.js index 3e610430..e70b6548 100644 --- a/client/src/helpers/renderFormattedClientCell.js +++ b/client/src/helpers/renderFormattedClientCell.js @@ -43,7 +43,7 @@ export const renderFormattedClientCell = (value, info, isDetailed = false, isLog const whoisAvailable = whois_info && Object.keys(whois_info).length > 0; if (name) { - const nameValue =
+ const nameValue =
{name} {`(${value})`}
;