mirror of
https://github.com/cheeaun/phanpy.git
synced 2024-11-23 09:45:46 +03:00
New component: Menu
It's time to do this menu thing the right way instead of hacky CSS
This commit is contained in:
parent
19ee95d188
commit
28281bb752
7 changed files with 166 additions and 105 deletions
83
package-lock.json
generated
83
package-lock.json
generated
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
51
src/app.css
51
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 */
|
||||
|
|
|
@ -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({
|
|||
/>
|
||||
</div>
|
||||
{isSelf && (
|
||||
<span class="menu-container">
|
||||
<button type="button" title="More" class="plain more-button">
|
||||
<Icon icon="more" size="l" alt="More" />
|
||||
</button>
|
||||
<menu>
|
||||
{isSelf && (
|
||||
<li>
|
||||
<button
|
||||
type="button"
|
||||
class="plain"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
states.showCompose = {
|
||||
editStatus: status,
|
||||
};
|
||||
}}
|
||||
>
|
||||
Edit…
|
||||
</button>
|
||||
</li>
|
||||
)}
|
||||
</menu>
|
||||
</span>
|
||||
<Menu
|
||||
align="end"
|
||||
menuButton={
|
||||
<div class="action">
|
||||
<button
|
||||
type="button"
|
||||
title="More"
|
||||
class="plain more-button"
|
||||
>
|
||||
<Icon icon="more" size="l" alt="More" />
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{isSelf && (
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
states.showCompose = {
|
||||
editStatus: status,
|
||||
};
|
||||
}}
|
||||
>
|
||||
Edit…
|
||||
</MenuItem>
|
||||
)}
|
||||
</Menu>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import './index.css';
|
||||
|
||||
import '@szhsin/react-menu/dist/core.css';
|
||||
|
||||
import { render } from 'preact';
|
||||
import { HashRouter } from 'react-router-dom';
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 }) {
|
|||
<Icon icon="transfer" /> Switch
|
||||
</button>
|
||||
)}
|
||||
<span>
|
||||
{!isDefault && moreThanOneAccount && (
|
||||
<Menu
|
||||
align="end"
|
||||
menuButton={
|
||||
<button
|
||||
type="button"
|
||||
class="plain small"
|
||||
onClick={() => {
|
||||
// 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
|
||||
<Icon icon="more" size="l" alt="More" />
|
||||
</button>
|
||||
)}
|
||||
{isCurrent && (
|
||||
<>
|
||||
{' '}
|
||||
<button
|
||||
type="button"
|
||||
class="plain small"
|
||||
onClick={() => {
|
||||
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
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<MenuItem
|
||||
disabled={isDefault || !moreThanOneAccount}
|
||||
onClick={() => {
|
||||
// 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
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
disabled={!isCurrent}
|
||||
onClick={() => {
|
||||
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
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
|
|
Loading…
Reference in a new issue