Upgrade react-menu and bug fixes

This commit is contained in:
Lim Chee Aun 2023-06-13 17:46:37 +08:00
parent f6ef727cae
commit a1ee5be54b
13 changed files with 120 additions and 75 deletions

49
package-lock.json generated
View file

@ -12,7 +12,8 @@
"@github/text-expander-element": "~2.3.0", "@github/text-expander-element": "~2.3.0",
"@iconify-icons/mingcute": "~1.2.5", "@iconify-icons/mingcute": "~1.2.5",
"@justinribeiro/lite-youtube": "~1.5.0", "@justinribeiro/lite-youtube": "~1.5.0",
"@szhsin/react-menu": "~3.5.3", "@szhsin/react-menu": "~4.0.0",
"@uidotdev/usehooks": "~2.0.1",
"dayjs": "~1.11.8", "dayjs": "~1.11.8",
"dayjs-twitter": "~0.5.0", "dayjs-twitter": "~0.5.0",
"fast-blurhash": "~1.1.2", "fast-blurhash": "~1.1.2",
@ -3126,12 +3127,12 @@
} }
}, },
"node_modules/@szhsin/react-menu": { "node_modules/@szhsin/react-menu": {
"version": "3.5.3", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/@szhsin/react-menu/-/react-menu-3.5.3.tgz", "resolved": "https://registry.npmjs.org/@szhsin/react-menu/-/react-menu-4.0.0.tgz",
"integrity": "sha512-jxo8oaRwxmVjUzkyOi/ZJiXaZiuFPMIxFzyJdUKfnhBLYiEOVTU9M2CiPuEkirILoareR2GJj2K3y8a81CBPlw==", "integrity": "sha512-DOl+IWddgHofcEzSTJfILGvpU67O/y8r07LOVUhfThke9VEZ5LAZNkp2Q3mEFaN7PkmnmJtjPBEdIK3oN1/ZfQ==",
"dependencies": { "dependencies": {
"prop-types": "^15.7.2", "prop-types": "^15.7.2",
"react-transition-state": "^1.1.5" "react-transition-state": "^2.1.0"
}, },
"peerDependencies": { "peerDependencies": {
"react": ">=16.14.0", "react": ">=16.14.0",
@ -3271,6 +3272,18 @@
"integrity": "sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==", "integrity": "sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==",
"dev": true "dev": true
}, },
"node_modules/@uidotdev/usehooks": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@uidotdev/usehooks/-/usehooks-2.0.1.tgz",
"integrity": "sha512-rJXxE3Y8g9utRbOS9Pj9tIvrnOdaakHIhLbMxBlErV8HydnGD0DveD82aLBfVTh1hBp5IXqpeHpMrPE9WIT7vQ==",
"engines": {
"node": ">=16"
},
"peerDependencies": {
"react": ">=18.0.0",
"react-dom": ">=18.0.0"
}
},
"node_modules/@vue/compiler-core": { "node_modules/@vue/compiler-core": {
"version": "3.2.45", "version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.45.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.45.tgz",
@ -6334,9 +6347,9 @@
} }
}, },
"node_modules/react-transition-state": { "node_modules/react-transition-state": {
"version": "1.1.5", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/react-transition-state/-/react-transition-state-1.1.5.tgz", "resolved": "https://registry.npmjs.org/react-transition-state/-/react-transition-state-2.1.0.tgz",
"integrity": "sha512-ITY2mZqc2dWG2eitJkYNdcSFW8aKeOlkL2A/vowRrLL8GH3J6Re/SpD/BLvQzrVOTqjsP0b5S9N10vgNNzwMUQ==", "integrity": "sha512-b8ldw2pbZk++XM43vcD4ETaFWlzTsjpUX33CmT8BBPPFYlQ2R50wxcY4ZeJ1TesJYziYZ9/rNPFnyA9tR0iKDw==",
"peerDependencies": { "peerDependencies": {
"react": ">=16.8.0", "react": ">=16.8.0",
"react-dom": ">=16.8.0" "react-dom": ">=16.8.0"
@ -9619,12 +9632,12 @@
} }
}, },
"@szhsin/react-menu": { "@szhsin/react-menu": {
"version": "3.5.3", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/@szhsin/react-menu/-/react-menu-3.5.3.tgz", "resolved": "https://registry.npmjs.org/@szhsin/react-menu/-/react-menu-4.0.0.tgz",
"integrity": "sha512-jxo8oaRwxmVjUzkyOi/ZJiXaZiuFPMIxFzyJdUKfnhBLYiEOVTU9M2CiPuEkirILoareR2GJj2K3y8a81CBPlw==", "integrity": "sha512-DOl+IWddgHofcEzSTJfILGvpU67O/y8r07LOVUhfThke9VEZ5LAZNkp2Q3mEFaN7PkmnmJtjPBEdIK3oN1/ZfQ==",
"requires": { "requires": {
"prop-types": "^15.7.2", "prop-types": "^15.7.2",
"react-transition-state": "^1.1.5" "react-transition-state": "^2.1.0"
} }
}, },
"@trivago/prettier-plugin-sort-imports": { "@trivago/prettier-plugin-sort-imports": {
@ -9740,6 +9753,12 @@
"integrity": "sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==", "integrity": "sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==",
"dev": true "dev": true
}, },
"@uidotdev/usehooks": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@uidotdev/usehooks/-/usehooks-2.0.1.tgz",
"integrity": "sha512-rJXxE3Y8g9utRbOS9Pj9tIvrnOdaakHIhLbMxBlErV8HydnGD0DveD82aLBfVTh1hBp5IXqpeHpMrPE9WIT7vQ==",
"requires": {}
},
"@vue/compiler-core": { "@vue/compiler-core": {
"version": "3.2.45", "version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.45.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.45.tgz",
@ -11832,9 +11851,9 @@
} }
}, },
"react-transition-state": { "react-transition-state": {
"version": "1.1.5", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/react-transition-state/-/react-transition-state-1.1.5.tgz", "resolved": "https://registry.npmjs.org/react-transition-state/-/react-transition-state-2.1.0.tgz",
"integrity": "sha512-ITY2mZqc2dWG2eitJkYNdcSFW8aKeOlkL2A/vowRrLL8GH3J6Re/SpD/BLvQzrVOTqjsP0b5S9N10vgNNzwMUQ==", "integrity": "sha512-b8ldw2pbZk++XM43vcD4ETaFWlzTsjpUX33CmT8BBPPFYlQ2R50wxcY4ZeJ1TesJYziYZ9/rNPFnyA9tR0iKDw==",
"requires": {} "requires": {}
}, },
"regenerate": { "regenerate": {

View file

@ -14,7 +14,8 @@
"@github/text-expander-element": "~2.3.0", "@github/text-expander-element": "~2.3.0",
"@iconify-icons/mingcute": "~1.2.5", "@iconify-icons/mingcute": "~1.2.5",
"@justinribeiro/lite-youtube": "~1.5.0", "@justinribeiro/lite-youtube": "~1.5.0",
"@szhsin/react-menu": "~3.5.3", "@szhsin/react-menu": "~4.0.0",
"@uidotdev/usehooks": "~2.0.1",
"dayjs": "~1.11.8", "dayjs": "~1.11.8",
"dayjs-twitter": "~0.5.0", "dayjs-twitter": "~0.5.0",
"fast-blurhash": "~1.1.2", "fast-blurhash": "~1.1.2",

View file

@ -673,7 +673,7 @@ function RelatedActions({ info, instance, authenticated }) {
openTrigger="clickOnly" openTrigger="clickOnly"
direction="bottom" direction="bottom"
overflow="auto" overflow="auto"
offsetX={-16} shift={-16}
label={ label={
<> <>
<Icon icon="mute" /> <Icon icon="mute" />

View file

@ -191,7 +191,7 @@ function MediaModal({
align="end" align="end"
position="anchor" position="anchor"
boundingBoxPadding="8 8 8 8" boundingBoxPadding="8 8 8 8"
offsetY={4} gap={4}
menuClassName="glass-menu" menuClassName="glass-menu"
menuButton={ menuButton={
<button type="button" class="carousel-button plain3"> <button type="button" class="carousel-button plain3">

31
src/components/menu2.jsx Normal file
View file

@ -0,0 +1,31 @@
import { Menu } from '@szhsin/react-menu';
import { useWindowSize } from '@uidotdev/usehooks';
import { useRef } from 'preact/hooks';
import safeBoundingBoxPadding from '../utils/safe-bounding-box-padding';
// It's like Menu but with sensible defaults, bug fixes and improvements.
function Menu2(props) {
const { containerProps } = props;
const size = useWindowSize();
const instanceRef = useRef();
return (
<Menu
boundingBoxPadding={safeBoundingBoxPadding()}
repositionFlag={`${size.width}x${size.height}`}
{...props}
instanceRef={instanceRef}
containerProps={{
onClick: (e) => {
if (e.target === e.currentTarget) {
instanceRef.current?.closeMenu?.();
}
containerProps?.onClick?.(e);
},
...containerProps,
}}
/>
);
}
export default Menu2;

View file

@ -132,7 +132,7 @@ function Shortcuts() {
viewScroll="close" viewScroll="close"
boundingBoxPadding="8 8 8 8" boundingBoxPadding="8 8 8 8"
menuClassName="glass-menu shortcuts-menu" menuClassName="glass-menu shortcuts-menu"
offsetY={8} gap={8}
position="anchor" position="anchor"
menuButton={ menuButton={
<button <button

View file

@ -34,6 +34,7 @@ import htmlContentLength from '../utils/html-content-length';
import isMastodonLinkMaybe from '../utils/isMastodonLinkMaybe'; import isMastodonLinkMaybe from '../utils/isMastodonLinkMaybe';
import localeMatch from '../utils/locale-match'; import localeMatch from '../utils/locale-match';
import niceDateTime from '../utils/nice-date-time'; import niceDateTime from '../utils/nice-date-time';
import safeBoundingBoxPadding from '../utils/safe-bounding-box-padding';
import shortenNumber from '../utils/shorten-number'; import shortenNumber from '../utils/shorten-number';
import showToast from '../utils/show-toast'; import showToast from '../utils/show-toast';
import states, { getStatus, saveStatus, statusKey } from '../utils/states'; import states, { getStatus, saveStatus, statusKey } from '../utils/states';
@ -824,7 +825,7 @@ function Status({
}, },
}} }}
align="end" align="end"
offsetY={4} gap={4}
overflow="auto" overflow="auto"
viewScroll="close" viewScroll="close"
boundingBoxPadding="8 8 8 8" boundingBoxPadding="8 8 8 8"
@ -1182,7 +1183,7 @@ function Status({
document.querySelector('.status-deck') || document.body, document.querySelector('.status-deck') || document.body,
}} }}
align="end" align="end"
offsetY={4} gap={4}
overflow="auto" overflow="auto"
viewScroll="close" viewScroll="close"
boundingBoxPadding="8 8 8 8" boundingBoxPadding="8 8 8 8"
@ -1817,30 +1818,6 @@ const unfurlMastodonLink = throttle(
}), }),
); );
const root = document.documentElement;
const defaultBoundingBoxPadding = 8;
function _safeBoundingBoxPadding() {
// Get safe area inset variables from root
const style = getComputedStyle(root);
const safeAreaInsetTop = style.getPropertyValue('--sai-top');
const safeAreaInsetRight = style.getPropertyValue('--sai-right');
const safeAreaInsetBottom = style.getPropertyValue('--sai-bottom');
const safeAreaInsetLeft = style.getPropertyValue('--sai-left');
const str = [
safeAreaInsetTop,
safeAreaInsetRight,
safeAreaInsetBottom,
safeAreaInsetLeft,
]
.map((v) => parseInt(v, 10) || defaultBoundingBoxPadding)
.join(' ');
// console.log(str);
return str;
}
const safeBoundingBoxPadding = mem(_safeBoundingBoxPadding, {
maxAge: 10_000, // 10 seconds
});
function FilteredStatus({ status, filterInfo, instance, containerProps = {} }) { function FilteredStatus({ status, filterInfo, instance, containerProps = {} }) {
const { const {
account: { avatar, avatarStatic, bot }, account: { avatar, avatarStatic, bot },

View file

@ -6,6 +6,7 @@ import { useSnapshot } from 'valtio';
import AccountInfo from '../components/account-info'; import AccountInfo from '../components/account-info';
import Icon from '../components/icon'; import Icon from '../components/icon';
import Link from '../components/link'; import Link from '../components/link';
import Menu2 from '../components/menu2';
import Timeline from '../components/timeline'; import Timeline from '../components/timeline';
import { api } from '../utils/api'; import { api } from '../utils/api';
import emojifyText from '../utils/emojify-text'; import emojifyText from '../utils/emojify-text';
@ -255,15 +256,12 @@ function AccountStatuses() {
timelineStart={TimelineStart} timelineStart={TimelineStart}
refresh={excludeReplies + excludeBoosts + tagged + media} refresh={excludeReplies + excludeBoosts + tagged + media}
headerEnd={ headerEnd={
<Menu <Menu2
portal={{ portal
target: document.body,
}}
// setDownOverflow // setDownOverflow
overflow="auto" overflow="auto"
viewScroll="close" viewScroll="close"
position="anchor" position="anchor"
boundingBoxPadding="8 8 8 8"
menuButton={ menuButton={
<button type="button" class="plain"> <button type="button" class="plain">
<Icon icon="more" size="l" /> <Icon icon="more" size="l" />
@ -295,7 +293,7 @@ function AccountStatuses() {
Switch to account's instance (<b>{accountInstance}</b>) Switch to account's instance (<b>{accountInstance}</b>)
</small> </small>
</MenuItem> </MenuItem>
</Menu> </Menu2>
} }
/> />
); );

View file

@ -9,6 +9,7 @@ import { useEffect, useRef, useState } from 'preact/hooks';
import { useNavigate, useParams } from 'react-router-dom'; import { useNavigate, useParams } from 'react-router-dom';
import Icon from '../components/icon'; import Icon from '../components/icon';
import Menu2 from '../components/menu2';
import Timeline from '../components/timeline'; import Timeline from '../components/timeline';
import { api } from '../utils/api'; import { api } from '../utils/api';
import showToast from '../utils/show-toast'; import showToast from '../utils/show-toast';
@ -122,15 +123,12 @@ function Hashtags(props) {
checkForUpdates={checkForUpdates} checkForUpdates={checkForUpdates}
useItemID useItemID
headerEnd={ headerEnd={
<Menu <Menu2
portal={{ portal
target: document.body,
}}
setDownOverflow setDownOverflow
overflow="auto" overflow="auto"
viewScroll="close" viewScroll="close"
position="anchor" position="anchor"
boundingBoxPadding="8 8 8 8"
menuButton={ menuButton={
<button type="button" class="plain"> <button type="button" class="plain">
<Icon icon="more" size="l" /> <Icon icon="more" size="l" />
@ -306,7 +304,7 @@ function Hashtags(props) {
> >
<Icon icon="bus" /> <span>Go to another instance</span> <Icon icon="bus" /> <span>Go to another instance</span>
</MenuItem> </MenuItem>
</Menu> </Menu2>
} }
/> />
); );

View file

@ -10,6 +10,7 @@ import AccountBlock from '../components/account-block';
import Icon from '../components/icon'; import Icon from '../components/icon';
import Link from '../components/link'; import Link from '../components/link';
import ListAddEdit from '../components/list-add-edit'; import ListAddEdit from '../components/list-add-edit';
import Menu2 from '../components/menu2';
import Modal from '../components/modal'; import Modal from '../components/modal';
import Timeline from '../components/timeline'; import Timeline from '../components/timeline';
import { api } from '../utils/api'; import { api } from '../utils/api';
@ -108,15 +109,12 @@ function List(props) {
</Link> </Link>
} }
headerEnd={ headerEnd={
<Menu <Menu2
portal={{ portal
target: document.body,
}}
setDownOverflow setDownOverflow
overflow="auto" overflow="auto"
viewScroll="close" viewScroll="close"
position="anchor" position="anchor"
boundingBoxPadding="8 8 8 8"
menuButton={ menuButton={
<button type="button" class="plain"> <button type="button" class="plain">
<Icon icon="more" size="l" /> <Icon icon="more" size="l" />
@ -137,7 +135,7 @@ function List(props) {
<Icon icon="group" size="l" /> <Icon icon="group" size="l" />
<span>Manage members</span> <span>Manage members</span>
</MenuItem> </MenuItem>
</Menu> </Menu2>
} }
/> />
{showListAddEditModal && ( {showListAddEditModal && (

View file

@ -4,6 +4,7 @@ import { useNavigate, useParams } from 'react-router-dom';
import { useSnapshot } from 'valtio'; import { useSnapshot } from 'valtio';
import Icon from '../components/icon'; import Icon from '../components/icon';
import Menu2 from '../components/menu2';
import Timeline from '../components/timeline'; import Timeline from '../components/timeline';
import { api } from '../utils/api'; import { api } from '../utils/api';
import { filteredItems } from '../utils/filters'; import { filteredItems } from '../utils/filters';
@ -92,15 +93,12 @@ function Public({ local, ...props }) {
boostsCarousel={snapStates.settings.boostsCarousel} boostsCarousel={snapStates.settings.boostsCarousel}
allowFilters allowFilters
headerEnd={ headerEnd={
<Menu <Menu2
portal={{ portal
target: document.body,
}}
// setDownOverflow // setDownOverflow
overflow="auto" overflow="auto"
viewScroll="close" viewScroll="close"
position="anchor" position="anchor"
boundingBoxPadding="8 8 8 8"
menuButton={ menuButton={
<button type="button" class="plain"> <button type="button" class="plain">
<Icon icon="more" size="l" /> <Icon icon="more" size="l" />
@ -136,7 +134,7 @@ function Public({ local, ...props }) {
> >
<Icon icon="bus" /> <span>Go to another instance</span> <Icon icon="bus" /> <span>Go to another instance</span>
</MenuItem> </MenuItem>
</Menu> </Menu2>
} }
/> />
); );

View file

@ -4,6 +4,7 @@ import { useNavigate, useParams } from 'react-router-dom';
import { useSnapshot } from 'valtio'; import { useSnapshot } from 'valtio';
import Icon from '../components/icon'; import Icon from '../components/icon';
import Menu2 from '../components/menu2';
import Timeline from '../components/timeline'; import Timeline from '../components/timeline';
import { api } from '../utils/api'; import { api } from '../utils/api';
import { filteredItems } from '../utils/filters'; import { filteredItems } from '../utils/filters';
@ -92,15 +93,12 @@ function Trending(props) {
boostsCarousel={snapStates.settings.boostsCarousel} boostsCarousel={snapStates.settings.boostsCarousel}
allowFilters allowFilters
headerEnd={ headerEnd={
<Menu <Menu2
portal={{ portal
target: document.body,
}}
// setDownOverflow // setDownOverflow
overflow="auto" overflow="auto"
viewScroll="close" viewScroll="close"
position="anchor" position="anchor"
boundingBoxPadding="8 8 8 8"
menuButton={ menuButton={
<button type="button" class="plain"> <button type="button" class="plain">
<Icon icon="more" size="l" /> <Icon icon="more" size="l" />
@ -124,7 +122,7 @@ function Trending(props) {
> >
<Icon icon="bus" /> <span>Go to another instance</span> <Icon icon="bus" /> <span>Go to another instance</span>
</MenuItem> </MenuItem>
</Menu> </Menu2>
} }
/> />
); );

View file

@ -0,0 +1,27 @@
import mem from 'mem';
const root = document.documentElement;
const defaultBoundingBoxPadding = 8;
function _safeBoundingBoxPadding() {
// Get safe area inset variables from root
const style = getComputedStyle(root);
const safeAreaInsetTop = style.getPropertyValue('--sai-top');
const safeAreaInsetRight = style.getPropertyValue('--sai-right');
const safeAreaInsetBottom = style.getPropertyValue('--sai-bottom');
const safeAreaInsetLeft = style.getPropertyValue('--sai-left');
const str = [
safeAreaInsetTop,
safeAreaInsetRight,
safeAreaInsetBottom,
safeAreaInsetLeft,
]
.map((v) => parseInt(v, 10) || defaultBoundingBoxPadding)
.join(' ');
// console.log(str);
return str;
}
const safeBoundingBoxPadding = mem(_safeBoundingBoxPadding, {
maxAge: 10000, // 10 seconds
});
export default safeBoundingBoxPadding;