From 28281bb75289198aae1e2c1f930a3a80abc6d7a9 Mon Sep 17 00:00:00 2001 From: Lim Chee Aun Date: Tue, 24 Jan 2023 20:56:43 +0800 Subject: [PATCH] New component: Menu It's time to do this menu thing the right way instead of hacky CSS --- package-lock.json | 83 ++++++++++++++++++++++++++++++++++++++- package.json | 1 + src/app.css | 51 ++++++------------------ src/components/status.jsx | 51 +++++++++++++----------- src/main.jsx | 2 + src/pages/settings.css | 14 +++---- src/pages/settings.jsx | 69 ++++++++++++++++---------------- 7 files changed, 166 insertions(+), 105 deletions(-) diff --git a/package-lock.json b/package-lock.json index 86775d23..afe92294 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@github/text-expander-element": "~2.3.0", "@iconify-icons/mingcute": "~1.2.3", + "@szhsin/react-menu": "~3.3.1", "dayjs": "~1.11.7", "dayjs-twitter": "~0.5.0", "fast-blurhash": "~1.1.2", @@ -2364,6 +2365,19 @@ "string.prototype.matchall": "^4.0.6" } }, + "node_modules/@szhsin/react-menu": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@szhsin/react-menu/-/react-menu-3.3.1.tgz", + "integrity": "sha512-e8vK+N1YWwTdYXElvRRf5GIImtcDecqTCzpAa0DkGAknKwfQwtQtUnBn+DECodwsWi5H5ONKTU+kn0qJ70hEYQ==", + "dependencies": { + "prop-types": "^15.7.2", + "react-transition-state": "^1.1.5" + }, + "peerDependencies": { + "react": ">=16.14.0", + "react-dom": ">=16.14.0" + } + }, "node_modules/@trivago/prettier-plugin-sort-imports": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-4.0.0.tgz", @@ -4130,7 +4144,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "peer": true, "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, @@ -4370,6 +4383,14 @@ "node": ">=0.10.0" } }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-inspect": { "version": "1.12.2", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", @@ -4565,6 +4586,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, "node_modules/proxy-compare": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/proxy-compare/-/proxy-compare-2.4.0.tgz", @@ -4664,6 +4695,11 @@ "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, "node_modules/react-router": { "version": "6.6.2", "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.6.2.tgz", @@ -4694,6 +4730,15 @@ "react-dom": ">=16.8" } }, + "node_modules/react-transition-state": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/react-transition-state/-/react-transition-state-1.1.5.tgz", + "integrity": "sha512-ITY2mZqc2dWG2eitJkYNdcSFW8aKeOlkL2A/vowRrLL8GH3J6Re/SpD/BLvQzrVOTqjsP0b5S9N10vgNNzwMUQ==", + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -7477,6 +7522,15 @@ "string.prototype.matchall": "^4.0.6" } }, + "@szhsin/react-menu": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@szhsin/react-menu/-/react-menu-3.3.1.tgz", + "integrity": "sha512-e8vK+N1YWwTdYXElvRRf5GIImtcDecqTCzpAa0DkGAknKwfQwtQtUnBn+DECodwsWi5H5ONKTU+kn0qJ70hEYQ==", + "requires": { + "prop-types": "^15.7.2", + "react-transition-state": "^1.1.5" + } + }, "@trivago/prettier-plugin-sort-imports": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-4.0.0.tgz", @@ -8819,7 +8873,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "peer": true, "requires": { "js-tokens": "^3.0.0 || ^4.0.0" } @@ -9001,6 +9054,11 @@ "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", "dev": true }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" + }, "object-inspect": { "version": "1.12.2", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", @@ -9131,6 +9189,16 @@ "integrity": "sha512-6UqkYefdogmzqAZWzJ7laYeJnaXDy2/J+ZqiiMtS7t7OfpXWTlaeGMwX8U6EFvPV/YWWEKRkS8hKS4k60WHTOg==", "dev": true }, + "prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, "proxy-compare": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/proxy-compare/-/proxy-compare-2.4.0.tgz", @@ -9196,6 +9264,11 @@ "integrity": "sha512-IXpIsPe6BleFOEHKzKh5UjwRUaz/JYS0lT/HPsupWEQou2hDqjhLMStc5zyE3eQVT4Fk3FufM8Fw33qW1uyeiw==", "requires": {} }, + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, "react-router": { "version": "6.6.2", "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.6.2.tgz", @@ -9213,6 +9286,12 @@ "react-router": "6.6.2" } }, + "react-transition-state": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/react-transition-state/-/react-transition-state-1.1.5.tgz", + "integrity": "sha512-ITY2mZqc2dWG2eitJkYNdcSFW8aKeOlkL2A/vowRrLL8GH3J6Re/SpD/BLvQzrVOTqjsP0b5S9N10vgNNzwMUQ==", + "requires": {} + }, "regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", diff --git a/package.json b/package.json index 4020465e..a8f291f1 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "dependencies": { "@github/text-expander-element": "~2.3.0", "@iconify-icons/mingcute": "~1.2.3", + "@szhsin/react-menu": "~3.3.1", "dayjs": "~1.11.7", "dayjs-twitter": "~0.5.0", "fast-blurhash": "~1.1.2", diff --git a/src/app.css b/src/app.css index de025319..bd76a0c4 100644 --- a/src/app.css +++ b/src/app.css @@ -805,54 +805,27 @@ button.carousel-dot:is(.active, [disabled].active) { /* MENU POPUP */ -.menu-container { - position: relative; -} -.menu-container button { - color: inherit !important; -} -.menu-container button:is(:hover, :active, :focus) { - background-color: var(--button-plain-bg-hover-color); -} -.menu-container menu { - position: absolute; - right: 0; - top: 0; - transform: translateY(-100%); - opacity: 0; - pointer-events: none; - padding: 8px 0; +.szh-menu { + padding: 8px 0 !important; margin: 0; font-size: 16px; background-color: var(--bg-color); - width: 10em; - list-style: none; - z-index: 100; border: 1px solid var(--outline-color); border-radius: 8px; - transition: all 0.2s ease-in-out; box-shadow: 0 0 8px var(--bg-faded-color), 0 4px 8px var(--bg-faded-color), 0 2px 4px var(--bg-faded-color); -} -.menu-container menu li { - margin: 0; - padding: 0; - list-style: none; -} -.menu-container > button:is(:hover, :active, :focus) + menu, -.menu-container menu:is(:hover, :active) { - opacity: 1; - pointer-events: auto; -} -.menu-container menu button { - width: 100%; text-align: left; - color: var(--text-color) !important; - border-radius: 0; } -.menu-container menu button:is(:hover, :focus) { - color: var(--bg-color) !important; - background-color: var(--link-color); +.szh-menu .szh-menu__item { + padding: 8px 16px !important; +} +.szh-menu + .szh-menu__item:not(.szh-menu__item--disabled, .szh-menu__item--hover) { + color: var(--text-color); +} +.szh-menu .szh-menu__item--hover { + color: var(--button-text-color); + background-color: var(--button-bg-color); } /* DONUT METER */ diff --git a/src/components/status.jsx b/src/components/status.jsx index d9044d85..d4a20aba 100644 --- a/src/components/status.jsx +++ b/src/components/status.jsx @@ -1,5 +1,6 @@ import './status.css'; +import { Menu, MenuItem } from '@szhsin/react-menu'; import { getBlurHashAverageColor } from 'fast-blurhash'; import mem from 'mem'; import { memo } from 'preact/compat'; @@ -587,30 +588,32 @@ function Status({ /> {isSelf && ( - - - - {isSelf && ( -
  • - -
  • - )} -
    -
    + + + + } + > + {isSelf && ( + { + states.showCompose = { + editStatus: status, + }; + }} + > + Edit… + + )} + )} diff --git a/src/main.jsx b/src/main.jsx index f1fd4def..bee7d956 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -1,5 +1,7 @@ import './index.css'; +import '@szhsin/react-menu/dist/core.css'; + import { render } from 'preact'; import { HashRouter } from 'react-router-dom'; diff --git a/src/pages/settings.css b/src/pages/settings.css index 2b940da9..54e14f34 100644 --- a/src/pages/settings.css +++ b/src/pages/settings.css @@ -29,7 +29,7 @@ padding: 0; list-style: none; } -#settings-container ul li { +#settings-container ul:not([role='menu']) > li { padding: 8px 0 16px; display: flex; justify-content: space-between; @@ -37,26 +37,26 @@ flex-wrap: wrap; border-bottom: var(--hairline-width) solid var(--outline-color); } -#settings-container ul li .current { +#settings-container ul:not([role='menu']) > li .current { margin-right: 8px; color: var(--green-color); opacity: 0.1; } -#settings-container ul li .current.is-current { +#settings-container ul:not([role='menu']) > li .current.is-current { opacity: 1; } -#settings-container ul li .current.is-current + .avatar { +#settings-container ul:not([role='menu']) > li .current.is-current + .avatar { box-shadow: 0 0 0 1.5px var(--green-color), 0 0 8px var(--green-color); } -#settings-container ul li > div { +#settings-container ul:not([role='menu']) > li > div { flex-grow: 1; max-width: 100%; } -#settings-container ul li > div.actions { +#settings-container ul:not([role='menu']) > li > div.actions { flex-basis: fit-content; margin-top: 8px; } -#settings-container ul li > div:last-child { +#settings-container ul:not([role='menu']) > li > div:last-child { text-align: right; } #settings-container div, diff --git a/src/pages/settings.jsx b/src/pages/settings.jsx index 8b086384..d0b5468e 100644 --- a/src/pages/settings.jsx +++ b/src/pages/settings.jsx @@ -1,5 +1,6 @@ import './settings.css'; +import { Menu, MenuItem } from '@szhsin/react-menu'; import { useReducer, useRef, useState } from 'preact/hooks'; import { useSnapshot } from 'valtio'; @@ -92,43 +93,45 @@ function Settings({ onClose }) { Switch )} - - {!isDefault && moreThanOneAccount && ( + { - // Move account to the top of the list - accounts.splice(i, 1); - accounts.unshift(account); - store.local.setJSON('accounts', accounts); - setCurrentDefault(i); - }} + title="More" + class="plain more-button" > - Set as default + - )} - {isCurrent && ( - <> - {' '} - - - )} - + } + > + { + // Move account to the top of the list + accounts.splice(i, 1); + accounts.unshift(account); + store.local.setJSON('accounts', accounts); + setCurrentDefault(i); + }} + > + Set as default + + { + const yes = confirm( + 'Are you sure you want to log out?', + ); + if (!yes) return; + accounts.splice(i, 1); + store.local.setJSON('accounts', accounts); + location.reload(); + }} + > + Log out + + );