mirror of
https://github.com/actualbudget/actual.git
synced 2026-04-30 10:14:53 -05:00
♻️ (tooltip) refactoring to react-aria (vol.6) (#2771)
* ♻️ (tooltip) refactoring to react-aria (vol.6)
* Release notes
This commit is contained in:
committed by
GitHub
parent
c92266fd7f
commit
0e86dea544
@@ -28,9 +28,9 @@ import { styles, theme } from '../../style';
|
|||||||
import { tokens } from '../../tokens';
|
import { tokens } from '../../tokens';
|
||||||
import { Button } from '../common/Button';
|
import { Button } from '../common/Button';
|
||||||
import { Menu } from '../common/Menu';
|
import { Menu } from '../common/Menu';
|
||||||
|
import { Popover } from '../common/Popover';
|
||||||
import { Text } from '../common/Text';
|
import { Text } from '../common/Text';
|
||||||
import { View } from '../common/View';
|
import { View } from '../common/View';
|
||||||
import { Tooltip } from '../tooltips';
|
|
||||||
|
|
||||||
function getFileDescription(file) {
|
function getFileDescription(file) {
|
||||||
if (file.state === 'unknown') {
|
if (file.state === 'unknown') {
|
||||||
@@ -84,11 +84,13 @@ function FileMenu({ onDelete, onClose }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function FileMenuButton({ state, onDelete }) {
|
function FileMenuButton({ state, onDelete }) {
|
||||||
|
const triggerRef = useRef(null);
|
||||||
const [menuOpen, setMenuOpen] = useState(false);
|
const [menuOpen, setMenuOpen] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
<Button
|
<Button
|
||||||
|
ref={triggerRef}
|
||||||
type="bare"
|
type="bare"
|
||||||
aria-label="Menu"
|
aria-label="Menu"
|
||||||
onClick={e => {
|
onClick={e => {
|
||||||
@@ -98,19 +100,18 @@ function FileMenuButton({ state, onDelete }) {
|
|||||||
>
|
>
|
||||||
<SvgDotsHorizontalTriple style={{ width: 16, height: 16 }} />
|
<SvgDotsHorizontalTriple style={{ width: 16, height: 16 }} />
|
||||||
</Button>
|
</Button>
|
||||||
{menuOpen && (
|
|
||||||
<Tooltip
|
<Popover
|
||||||
position="bottom-right"
|
triggerRef={triggerRef}
|
||||||
style={{ padding: 0 }}
|
isOpen={menuOpen}
|
||||||
|
onOpenChange={() => setMenuOpen(false)}
|
||||||
|
>
|
||||||
|
<FileMenu
|
||||||
|
state={state}
|
||||||
|
onDelete={onDelete}
|
||||||
onClose={() => setMenuOpen(false)}
|
onClose={() => setMenuOpen(false)}
|
||||||
>
|
/>
|
||||||
<FileMenu
|
</Popover>
|
||||||
state={state}
|
|
||||||
onDelete={onDelete}
|
|
||||||
onClose={() => setMenuOpen(false)}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { type ComponentProps, useState } from 'react';
|
import React, { type ComponentProps, useRef, useState } from 'react';
|
||||||
|
|
||||||
import { type AccountEntity } from 'loot-core/types/models';
|
import { type AccountEntity } from 'loot-core/types/models';
|
||||||
|
|
||||||
@@ -10,10 +10,10 @@ import { type CSSProperties, styles, theme } from '../../style';
|
|||||||
import { Button } from '../common/Button';
|
import { Button } from '../common/Button';
|
||||||
import { Menu } from '../common/Menu';
|
import { Menu } from '../common/Menu';
|
||||||
import { Modal, ModalTitle } from '../common/Modal';
|
import { Modal, ModalTitle } from '../common/Modal';
|
||||||
|
import { Popover } from '../common/Popover';
|
||||||
import { View } from '../common/View';
|
import { View } from '../common/View';
|
||||||
import { type CommonModalProps } from '../Modals';
|
import { type CommonModalProps } from '../Modals';
|
||||||
import { Notes } from '../Notes';
|
import { Notes } from '../Notes';
|
||||||
import { Tooltip } from '../tooltips';
|
|
||||||
|
|
||||||
type AccountMenuModalProps = {
|
type AccountMenuModalProps = {
|
||||||
modalProps: CommonModalProps;
|
modalProps: CommonModalProps;
|
||||||
@@ -155,6 +155,7 @@ function AdditionalAccountMenu({
|
|||||||
onClose,
|
onClose,
|
||||||
onReopen,
|
onReopen,
|
||||||
}: AdditionalAccountMenuProps) {
|
}: AdditionalAccountMenuProps) {
|
||||||
|
const triggerRef = useRef(null);
|
||||||
const [menuOpen, setMenuOpen] = useState(false);
|
const [menuOpen, setMenuOpen] = useState(false);
|
||||||
const itemStyle: CSSProperties = {
|
const itemStyle: CSSProperties = {
|
||||||
...styles.mediumText,
|
...styles.mediumText,
|
||||||
@@ -169,6 +170,7 @@ function AdditionalAccountMenu({
|
|||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
<Button
|
<Button
|
||||||
|
ref={triggerRef}
|
||||||
type="bare"
|
type="bare"
|
||||||
aria-label="Menu"
|
aria-label="Menu"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -180,47 +182,44 @@ function AdditionalAccountMenu({
|
|||||||
height={17}
|
height={17}
|
||||||
style={{ color: 'currentColor' }}
|
style={{ color: 'currentColor' }}
|
||||||
/>
|
/>
|
||||||
{menuOpen && (
|
<Popover
|
||||||
<Tooltip
|
triggerRef={triggerRef}
|
||||||
position="bottom-left"
|
isOpen={menuOpen}
|
||||||
style={{ padding: 0 }}
|
placement="bottom start"
|
||||||
onClose={() => {
|
onOpenChange={() => setMenuOpen(false)}
|
||||||
|
>
|
||||||
|
<Menu
|
||||||
|
getItemStyle={getItemStyle}
|
||||||
|
items={[
|
||||||
|
account.closed
|
||||||
|
? {
|
||||||
|
name: 'reopen',
|
||||||
|
text: 'Reopen account',
|
||||||
|
icon: SvgLockOpen,
|
||||||
|
iconSize: 15,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
name: 'close',
|
||||||
|
text: 'Close account',
|
||||||
|
icon: SvgClose,
|
||||||
|
iconSize: 15,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
onMenuSelect={name => {
|
||||||
setMenuOpen(false);
|
setMenuOpen(false);
|
||||||
|
switch (name) {
|
||||||
|
case 'close':
|
||||||
|
onClose?.(account.id);
|
||||||
|
break;
|
||||||
|
case 'reopen':
|
||||||
|
onReopen?.(account.id);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error(`Unrecognized menu option: ${name}`);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
>
|
/>
|
||||||
<Menu
|
</Popover>
|
||||||
getItemStyle={getItemStyle}
|
|
||||||
items={[
|
|
||||||
account.closed
|
|
||||||
? {
|
|
||||||
name: 'reopen',
|
|
||||||
text: 'Reopen account',
|
|
||||||
icon: SvgLockOpen,
|
|
||||||
iconSize: 15,
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
name: 'close',
|
|
||||||
text: 'Close account',
|
|
||||||
icon: SvgClose,
|
|
||||||
iconSize: 15,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
onMenuSelect={name => {
|
|
||||||
setMenuOpen(false);
|
|
||||||
switch (name) {
|
|
||||||
case 'close':
|
|
||||||
onClose?.(account.id);
|
|
||||||
break;
|
|
||||||
case 'reopen':
|
|
||||||
onReopen?.(account.id);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error(`Unrecognized menu option: ${name}`);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
)}
|
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// @ts-strict-ignore
|
// @ts-strict-ignore
|
||||||
import React, { type ComponentProps, useState } from 'react';
|
import React, { type ComponentProps, useRef, useState } from 'react';
|
||||||
|
|
||||||
import { type CategoryGroupEntity } from 'loot-core/src/types/models';
|
import { type CategoryGroupEntity } from 'loot-core/src/types/models';
|
||||||
|
|
||||||
@@ -11,10 +11,10 @@ import { type CSSProperties, styles, theme } from '../../style';
|
|||||||
import { Button } from '../common/Button';
|
import { Button } from '../common/Button';
|
||||||
import { Menu } from '../common/Menu';
|
import { Menu } from '../common/Menu';
|
||||||
import { Modal, ModalTitle } from '../common/Modal';
|
import { Modal, ModalTitle } from '../common/Modal';
|
||||||
|
import { Popover } from '../common/Popover';
|
||||||
import { View } from '../common/View';
|
import { View } from '../common/View';
|
||||||
import { type CommonModalProps } from '../Modals';
|
import { type CommonModalProps } from '../Modals';
|
||||||
import { Notes } from '../Notes';
|
import { Notes } from '../Notes';
|
||||||
import { Tooltip } from '../tooltips';
|
|
||||||
|
|
||||||
type CategoryGroupMenuModalProps = {
|
type CategoryGroupMenuModalProps = {
|
||||||
modalProps: CommonModalProps;
|
modalProps: CommonModalProps;
|
||||||
@@ -155,6 +155,7 @@ export function CategoryGroupMenuModal({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function AdditionalCategoryGroupMenu({ group, onDelete, onToggleVisibility }) {
|
function AdditionalCategoryGroupMenu({ group, onDelete, onToggleVisibility }) {
|
||||||
|
const triggerRef = useRef(null);
|
||||||
const [menuOpen, setMenuOpen] = useState(false);
|
const [menuOpen, setMenuOpen] = useState(false);
|
||||||
const itemStyle: CSSProperties = {
|
const itemStyle: CSSProperties = {
|
||||||
...styles.mediumText,
|
...styles.mediumText,
|
||||||
@@ -170,6 +171,7 @@ function AdditionalCategoryGroupMenu({ group, onDelete, onToggleVisibility }) {
|
|||||||
<View>
|
<View>
|
||||||
{!group.is_income && (
|
{!group.is_income && (
|
||||||
<Button
|
<Button
|
||||||
|
ref={triggerRef}
|
||||||
type="bare"
|
type="bare"
|
||||||
aria-label="Menu"
|
aria-label="Menu"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -181,52 +183,47 @@ function AdditionalCategoryGroupMenu({ group, onDelete, onToggleVisibility }) {
|
|||||||
height={17}
|
height={17}
|
||||||
style={{ color: 'currentColor' }}
|
style={{ color: 'currentColor' }}
|
||||||
/>
|
/>
|
||||||
{menuOpen && (
|
<Popover
|
||||||
<Tooltip
|
triggerRef={triggerRef}
|
||||||
position="bottom-left"
|
isOpen={menuOpen}
|
||||||
style={{ padding: 0 }}
|
placement="bottom start"
|
||||||
onClose={() => {
|
onOpenChange={() => setMenuOpen(false)}
|
||||||
setMenuOpen(false);
|
>
|
||||||
|
<Menu
|
||||||
|
style={{
|
||||||
|
...styles.mediumText,
|
||||||
|
color: theme.formLabelText,
|
||||||
}}
|
}}
|
||||||
>
|
getItemStyle={getItemStyle}
|
||||||
<Menu
|
items={
|
||||||
style={{
|
[
|
||||||
...styles.mediumText,
|
{
|
||||||
color: theme.formLabelText,
|
name: 'toggleVisibility',
|
||||||
}}
|
text: group.hidden ? 'Show' : 'Hide',
|
||||||
getItemStyle={getItemStyle}
|
icon: group.hidden ? SvgViewShow : SvgViewHide,
|
||||||
items={
|
iconSize: 16,
|
||||||
[
|
},
|
||||||
|
...(!group.is_income && [
|
||||||
|
Menu.line,
|
||||||
{
|
{
|
||||||
name: 'toggleVisibility',
|
name: 'delete',
|
||||||
text: group.hidden ? 'Show' : 'Hide',
|
text: 'Delete',
|
||||||
icon: group.hidden ? SvgViewShow : SvgViewHide,
|
icon: SvgTrash,
|
||||||
iconSize: 16,
|
iconSize: 15,
|
||||||
},
|
},
|
||||||
...(!group.is_income && [
|
]),
|
||||||
Menu.line,
|
].filter(i => i != null) as ComponentProps<typeof Menu>['items']
|
||||||
{
|
}
|
||||||
name: 'delete',
|
onMenuSelect={itemName => {
|
||||||
text: 'Delete',
|
setMenuOpen(false);
|
||||||
icon: SvgTrash,
|
if (itemName === 'delete') {
|
||||||
iconSize: 15,
|
onDelete();
|
||||||
},
|
} else if (itemName === 'toggleVisibility') {
|
||||||
]),
|
onToggleVisibility();
|
||||||
].filter(i => i != null) as ComponentProps<
|
|
||||||
typeof Menu
|
|
||||||
>['items']
|
|
||||||
}
|
}
|
||||||
onMenuSelect={itemName => {
|
}}
|
||||||
setMenuOpen(false);
|
/>
|
||||||
if (itemName === 'delete') {
|
</Popover>
|
||||||
onDelete();
|
|
||||||
} else if (itemName === 'toggleVisibility') {
|
|
||||||
onToggleVisibility();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
)}
|
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// @ts-strict-ignore
|
// @ts-strict-ignore
|
||||||
import React, { useState } from 'react';
|
import React, { useRef, useState } from 'react';
|
||||||
|
|
||||||
import { type CategoryEntity } from 'loot-core/src/types/models';
|
import { type CategoryEntity } from 'loot-core/src/types/models';
|
||||||
|
|
||||||
@@ -12,10 +12,10 @@ import { type CSSProperties, styles, theme } from '../../style';
|
|||||||
import { Button } from '../common/Button';
|
import { Button } from '../common/Button';
|
||||||
import { Menu } from '../common/Menu';
|
import { Menu } from '../common/Menu';
|
||||||
import { Modal, ModalTitle } from '../common/Modal';
|
import { Modal, ModalTitle } from '../common/Modal';
|
||||||
|
import { Popover } from '../common/Popover';
|
||||||
import { View } from '../common/View';
|
import { View } from '../common/View';
|
||||||
import { type CommonModalProps } from '../Modals';
|
import { type CommonModalProps } from '../Modals';
|
||||||
import { Notes } from '../Notes';
|
import { Notes } from '../Notes';
|
||||||
import { Tooltip } from '../tooltips';
|
|
||||||
|
|
||||||
type CategoryMenuModalProps = {
|
type CategoryMenuModalProps = {
|
||||||
modalProps: CommonModalProps;
|
modalProps: CommonModalProps;
|
||||||
@@ -147,6 +147,7 @@ function AdditionalCategoryMenu({
|
|||||||
onDelete,
|
onDelete,
|
||||||
onToggleVisibility,
|
onToggleVisibility,
|
||||||
}) {
|
}) {
|
||||||
|
const triggerRef = useRef(null);
|
||||||
const [menuOpen, setMenuOpen] = useState(false);
|
const [menuOpen, setMenuOpen] = useState(false);
|
||||||
const itemStyle: CSSProperties = {
|
const itemStyle: CSSProperties = {
|
||||||
...styles.mediumText,
|
...styles.mediumText,
|
||||||
@@ -161,6 +162,7 @@ function AdditionalCategoryMenu({
|
|||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
<Button
|
<Button
|
||||||
|
ref={triggerRef}
|
||||||
type="bare"
|
type="bare"
|
||||||
aria-label="Menu"
|
aria-label="Menu"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -172,42 +174,39 @@ function AdditionalCategoryMenu({
|
|||||||
height={17}
|
height={17}
|
||||||
style={{ color: 'currentColor' }}
|
style={{ color: 'currentColor' }}
|
||||||
/>
|
/>
|
||||||
{menuOpen && (
|
<Popover
|
||||||
<Tooltip
|
triggerRef={triggerRef}
|
||||||
position="bottom-left"
|
isOpen={menuOpen}
|
||||||
style={{ padding: 0 }}
|
placement="bottom start"
|
||||||
onClose={() => {
|
onOpenChange={() => setMenuOpen(false)}
|
||||||
|
>
|
||||||
|
<Menu
|
||||||
|
getItemStyle={getItemStyle}
|
||||||
|
items={[
|
||||||
|
!categoryGroup?.hidden && {
|
||||||
|
name: 'toggleVisibility',
|
||||||
|
text: category.hidden ? 'Show' : 'Hide',
|
||||||
|
icon: category.hidden ? SvgViewShow : SvgViewHide,
|
||||||
|
iconSize: 16,
|
||||||
|
},
|
||||||
|
!categoryGroup?.hidden && Menu.line,
|
||||||
|
{
|
||||||
|
name: 'delete',
|
||||||
|
text: 'Delete',
|
||||||
|
icon: SvgTrash,
|
||||||
|
iconSize: 15,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
onMenuSelect={itemName => {
|
||||||
setMenuOpen(false);
|
setMenuOpen(false);
|
||||||
|
if (itemName === 'delete') {
|
||||||
|
onDelete();
|
||||||
|
} else if (itemName === 'toggleVisibility') {
|
||||||
|
onToggleVisibility();
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
>
|
/>
|
||||||
<Menu
|
</Popover>
|
||||||
getItemStyle={getItemStyle}
|
|
||||||
items={[
|
|
||||||
!categoryGroup?.hidden && {
|
|
||||||
name: 'toggleVisibility',
|
|
||||||
text: category.hidden ? 'Show' : 'Hide',
|
|
||||||
icon: category.hidden ? SvgViewShow : SvgViewHide,
|
|
||||||
iconSize: 16,
|
|
||||||
},
|
|
||||||
!categoryGroup?.hidden && Menu.line,
|
|
||||||
{
|
|
||||||
name: 'delete',
|
|
||||||
text: 'Delete',
|
|
||||||
icon: SvgTrash,
|
|
||||||
iconSize: 15,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
onMenuSelect={itemName => {
|
|
||||||
setMenuOpen(false);
|
|
||||||
if (itemName === 'delete') {
|
|
||||||
onDelete();
|
|
||||||
} else if (itemName === 'toggleVisibility') {
|
|
||||||
onToggleVisibility();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
)}
|
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -40,9 +40,9 @@ import { Modal } from '../common/Modal';
|
|||||||
import { Select } from '../common/Select';
|
import { Select } from '../common/Select';
|
||||||
import { Stack } from '../common/Stack';
|
import { Stack } from '../common/Stack';
|
||||||
import { Text } from '../common/Text';
|
import { Text } from '../common/Text';
|
||||||
|
import { Tooltip } from '../common/Tooltip';
|
||||||
import { View } from '../common/View';
|
import { View } from '../common/View';
|
||||||
import { StatusBadge } from '../schedules/StatusBadge';
|
import { StatusBadge } from '../schedules/StatusBadge';
|
||||||
import { Tooltip } from '../tooltips';
|
|
||||||
import { SimpleTransactionsTable } from '../transactions/SimpleTransactionsTable';
|
import { SimpleTransactionsTable } from '../transactions/SimpleTransactionsTable';
|
||||||
import { BetweenAmountInput } from '../util/AmountInput';
|
import { BetweenAmountInput } from '../util/AmountInput';
|
||||||
import { DisplayId } from '../util/DisplayId';
|
import { DisplayId } from '../util/DisplayId';
|
||||||
@@ -415,33 +415,29 @@ function ActionEditor({ action, editorStyle, onChange, onDelete, onAdd }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function StageInfo() {
|
function StageInfo() {
|
||||||
const [open, setOpen] = useState();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={{ position: 'relative', marginLeft: 5 }}>
|
<View style={{ position: 'relative', marginLeft: 5 }}>
|
||||||
<View
|
<Tooltip
|
||||||
onMouseEnter={() => setOpen(true)}
|
content={
|
||||||
onMouseLeave={() => setOpen(false)}
|
<>
|
||||||
|
The stage of a rule allows you to force a specific order. Pre rules
|
||||||
|
always run first, and post rules always run last. Within each stage
|
||||||
|
rules are automatically ordered from least to most specific.
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
placement="bottom start"
|
||||||
|
style={{
|
||||||
|
...styles.tooltip,
|
||||||
|
padding: 10,
|
||||||
|
color: theme.pageTextLight,
|
||||||
|
maxWidth: 450,
|
||||||
|
lineHeight: 1.5,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<SvgInformationOutline
|
<SvgInformationOutline
|
||||||
style={{ width: 11, height: 11, color: theme.pageTextLight }}
|
style={{ width: 11, height: 11, color: theme.pageTextLight }}
|
||||||
/>
|
/>
|
||||||
</View>
|
</Tooltip>
|
||||||
{open && (
|
|
||||||
<Tooltip
|
|
||||||
position="bottom-left"
|
|
||||||
style={{
|
|
||||||
padding: 10,
|
|
||||||
color: theme.pageTextLight,
|
|
||||||
maxWidth: 450,
|
|
||||||
lineHeight: 1.5,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
The stage of a rule allows you to force a specific order. Pre rules
|
|
||||||
always run first, and post rules always run last. Within each stage
|
|
||||||
rules are automatically ordered from least to most specific.
|
|
||||||
</Tooltip>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,10 +20,10 @@ import { Link } from '../common/Link';
|
|||||||
import { Menu } from '../common/Menu';
|
import { Menu } from '../common/Menu';
|
||||||
import { Modal } from '../common/Modal';
|
import { Modal } from '../common/Modal';
|
||||||
import { Paragraph } from '../common/Paragraph';
|
import { Paragraph } from '../common/Paragraph';
|
||||||
|
import { Popover } from '../common/Popover';
|
||||||
import { View } from '../common/View';
|
import { View } from '../common/View';
|
||||||
import { FormField, FormLabel } from '../forms';
|
import { FormField, FormLabel } from '../forms';
|
||||||
import { type CommonModalProps } from '../Modals';
|
import { type CommonModalProps } from '../Modals';
|
||||||
import { Tooltip } from '../tooltips';
|
|
||||||
|
|
||||||
import { COUNTRY_OPTIONS } from './countries';
|
import { COUNTRY_OPTIONS } from './countries';
|
||||||
|
|
||||||
@@ -103,6 +103,7 @@ export function GoCardlessExternalMsg({
|
|||||||
>(null);
|
>(null);
|
||||||
const [menuOpen, setMenuOpen] = useState<boolean>(false);
|
const [menuOpen, setMenuOpen] = useState<boolean>(false);
|
||||||
const data = useRef<GoCardlessToken | null>(null);
|
const data = useRef<GoCardlessToken | null>(null);
|
||||||
|
const triggerRef = useRef(null);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: bankOptions,
|
data: bankOptions,
|
||||||
@@ -230,6 +231,7 @@ export function GoCardlessExternalMsg({
|
|||||||
Link bank in browser →
|
Link bank in browser →
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
|
ref={triggerRef}
|
||||||
type="bare"
|
type="bare"
|
||||||
onClick={() => setMenuOpen(true)}
|
onClick={() => setMenuOpen(true)}
|
||||||
aria-label="Menu"
|
aria-label="Menu"
|
||||||
@@ -239,28 +241,27 @@ export function GoCardlessExternalMsg({
|
|||||||
height={15}
|
height={15}
|
||||||
style={{ transform: 'rotateZ(90deg)' }}
|
style={{ transform: 'rotateZ(90deg)' }}
|
||||||
/>
|
/>
|
||||||
{menuOpen && (
|
|
||||||
<Tooltip
|
<Popover
|
||||||
position="bottom-right"
|
triggerRef={triggerRef}
|
||||||
width={200}
|
isOpen={menuOpen}
|
||||||
style={{ padding: 0 }}
|
style={{ width: 200 }}
|
||||||
onClose={() => setMenuOpen(false)}
|
onOpenChange={() => setMenuOpen(false)}
|
||||||
>
|
>
|
||||||
<Menu
|
<Menu
|
||||||
onMenuSelect={item => {
|
onMenuSelect={item => {
|
||||||
if (item === 'reconfigure') {
|
if (item === 'reconfigure') {
|
||||||
onGoCardlessInit();
|
onGoCardlessInit();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
items={[
|
items={[
|
||||||
{
|
{
|
||||||
name: 'reconfigure',
|
name: 'reconfigure',
|
||||||
text: 'Set new API secrets',
|
text: 'Set new API secrets',
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Popover>
|
||||||
)}
|
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import { useStableCallback } from '../../hooks/useStableCallback';
|
|||||||
import { SvgExpandArrow } from '../../icons/v0';
|
import { SvgExpandArrow } from '../../icons/v0';
|
||||||
import { theme } from '../../style';
|
import { theme } from '../../style';
|
||||||
import { Button } from '../common/Button';
|
import { Button } from '../common/Button';
|
||||||
|
import { Popover } from '../common/Popover';
|
||||||
import { Search } from '../common/Search';
|
import { Search } from '../common/Search';
|
||||||
import { View } from '../common/View';
|
import { View } from '../common/View';
|
||||||
import { TableHeader, Cell, SelectCell, useTableNavigator } from '../table';
|
import { TableHeader, Cell, SelectCell, useTableNavigator } from '../table';
|
||||||
@@ -102,6 +103,7 @@ export const ManagePayees = forwardRef(
|
|||||||
const [filter, setFilter] = useState('');
|
const [filter, setFilter] = useState('');
|
||||||
const table = useRef(null);
|
const table = useRef(null);
|
||||||
const scrollTo = useRef(null);
|
const scrollTo = useRef(null);
|
||||||
|
const triggerRef = useRef(null);
|
||||||
const resetAnimation = useRef(false);
|
const resetAnimation = useRef(false);
|
||||||
const [orphanedOnly, setOrphanedOnly] = useState(false);
|
const [orphanedOnly, setOrphanedOnly] = useState(false);
|
||||||
|
|
||||||
@@ -233,6 +235,7 @@ export const ManagePayees = forwardRef(
|
|||||||
>
|
>
|
||||||
<View style={{ flexShrink: 0 }}>
|
<View style={{ flexShrink: 0 }}>
|
||||||
<Button
|
<Button
|
||||||
|
ref={triggerRef}
|
||||||
type="bare"
|
type="bare"
|
||||||
style={{ marginRight: 10 }}
|
style={{ marginRight: 10 }}
|
||||||
disabled={buttonsDisabled}
|
disabled={buttonsDisabled}
|
||||||
@@ -245,7 +248,14 @@ export const ManagePayees = forwardRef(
|
|||||||
plural(selected.items.size, 'payee', 'payees')}
|
plural(selected.items.size, 'payee', 'payees')}
|
||||||
<SvgExpandArrow width={8} height={8} style={{ marginLeft: 5 }} />
|
<SvgExpandArrow width={8} height={8} style={{ marginLeft: 5 }} />
|
||||||
</Button>
|
</Button>
|
||||||
{menuOpen && (
|
|
||||||
|
<Popover
|
||||||
|
triggerRef={triggerRef}
|
||||||
|
isOpen={menuOpen}
|
||||||
|
placement="bottom start"
|
||||||
|
style={{ width: 250 }}
|
||||||
|
onOpenChange={() => setMenuOpen(false)}
|
||||||
|
>
|
||||||
<PayeeMenu
|
<PayeeMenu
|
||||||
payeesById={payeesById}
|
payeesById={payeesById}
|
||||||
selectedPayees={selected.items}
|
selectedPayees={selected.items}
|
||||||
@@ -253,7 +263,7 @@ export const ManagePayees = forwardRef(
|
|||||||
onDelete={onDelete}
|
onDelete={onDelete}
|
||||||
onMerge={onMerge}
|
onMerge={onMerge}
|
||||||
/>
|
/>
|
||||||
)}
|
</Popover>
|
||||||
</View>
|
</View>
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { SvgDelete, SvgMerge } from '../../icons/v0';
|
|||||||
import { theme } from '../../style';
|
import { theme } from '../../style';
|
||||||
import { Menu } from '../common/Menu';
|
import { Menu } from '../common/Menu';
|
||||||
import { View } from '../common/View';
|
import { View } from '../common/View';
|
||||||
import { Tooltip } from '../tooltips';
|
|
||||||
|
|
||||||
type PayeeMenuProps = {
|
type PayeeMenuProps = {
|
||||||
payeesById: Record<PayeeEntity['id'], PayeeEntity>;
|
payeesById: Record<PayeeEntity['id'], PayeeEntity>;
|
||||||
@@ -27,57 +26,50 @@ export function PayeeMenu({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip
|
<Menu
|
||||||
position="bottom"
|
onMenuSelect={type => {
|
||||||
width={250}
|
onClose();
|
||||||
style={{ padding: 0 }}
|
switch (type) {
|
||||||
onClose={onClose}
|
case 'delete':
|
||||||
>
|
onDelete();
|
||||||
<Menu
|
break;
|
||||||
onMenuSelect={type => {
|
case 'merge':
|
||||||
onClose();
|
onMerge();
|
||||||
switch (type) {
|
break;
|
||||||
case 'delete':
|
default:
|
||||||
onDelete();
|
|
||||||
break;
|
|
||||||
case 'merge':
|
|
||||||
onMerge();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
footer={
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
padding: 3,
|
|
||||||
fontSize: 11,
|
|
||||||
fontStyle: 'italic',
|
|
||||||
color: theme.pageTextSubdued,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{[...selectedPayees]
|
|
||||||
.slice(0, 4)
|
|
||||||
.map(id => payeesById[id].name)
|
|
||||||
.join(', ') + (selectedPayees.size > 4 ? ', and more' : '')}
|
|
||||||
</View>
|
|
||||||
}
|
}
|
||||||
items={[
|
}}
|
||||||
{
|
footer={
|
||||||
icon: SvgDelete,
|
<View
|
||||||
name: 'delete',
|
style={{
|
||||||
text: 'Delete',
|
padding: 3,
|
||||||
disabled: isDisabled,
|
fontSize: 11,
|
||||||
},
|
fontStyle: 'italic',
|
||||||
{
|
color: theme.pageTextSubdued,
|
||||||
icon: SvgMerge,
|
}}
|
||||||
iconSize: 9,
|
>
|
||||||
name: 'merge',
|
{[...selectedPayees]
|
||||||
text: 'Merge',
|
.slice(0, 4)
|
||||||
disabled: isDisabled || selectedPayees.size < 2,
|
.map(id => payeesById[id].name)
|
||||||
},
|
.join(', ') + (selectedPayees.size > 4 ? ', and more' : '')}
|
||||||
Menu.line,
|
</View>
|
||||||
]}
|
}
|
||||||
/>
|
items={[
|
||||||
</Tooltip>
|
{
|
||||||
|
icon: SvgDelete,
|
||||||
|
name: 'delete',
|
||||||
|
text: 'Delete',
|
||||||
|
disabled: isDisabled,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: SvgMerge,
|
||||||
|
iconSize: 9,
|
||||||
|
name: 'merge',
|
||||||
|
text: 'Merge',
|
||||||
|
disabled: isDisabled || selectedPayees.size < 2,
|
||||||
|
},
|
||||||
|
Menu.line,
|
||||||
|
]}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// @ts-strict-ignore
|
// @ts-strict-ignore
|
||||||
import React, { useState, useMemo, type CSSProperties } from 'react';
|
import React, { useRef, useState, useMemo, type CSSProperties } from 'react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
type ScheduleStatusType,
|
type ScheduleStatusType,
|
||||||
@@ -18,11 +18,11 @@ import { SvgCheck } from '../../icons/v2';
|
|||||||
import { theme } from '../../style';
|
import { theme } from '../../style';
|
||||||
import { Button } from '../common/Button';
|
import { Button } from '../common/Button';
|
||||||
import { Menu } from '../common/Menu';
|
import { Menu } from '../common/Menu';
|
||||||
|
import { Popover } from '../common/Popover';
|
||||||
import { Text } from '../common/Text';
|
import { Text } from '../common/Text';
|
||||||
import { View } from '../common/View';
|
import { View } from '../common/View';
|
||||||
import { PrivacyFilter } from '../PrivacyFilter';
|
import { PrivacyFilter } from '../PrivacyFilter';
|
||||||
import { Table, TableHeader, Row, Field, Cell } from '../table';
|
import { Table, TableHeader, Row, Field, Cell } from '../table';
|
||||||
import { Tooltip } from '../tooltips';
|
|
||||||
import { DisplayId } from '../util/DisplayId';
|
import { DisplayId } from '../util/DisplayId';
|
||||||
|
|
||||||
import { StatusBadge } from './StatusBadge';
|
import { StatusBadge } from './StatusBadge';
|
||||||
@@ -60,6 +60,7 @@ function OverflowMenu({
|
|||||||
status: ScheduleStatusType;
|
status: ScheduleStatusType;
|
||||||
onAction: SchedulesTableProps['onAction'];
|
onAction: SchedulesTableProps['onAction'];
|
||||||
}) {
|
}) {
|
||||||
|
const triggerRef = useRef(null);
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
const getMenuItems = () => {
|
const getMenuItems = () => {
|
||||||
@@ -96,6 +97,7 @@ function OverflowMenu({
|
|||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
<Button
|
<Button
|
||||||
|
ref={triggerRef}
|
||||||
type="bare"
|
type="bare"
|
||||||
aria-label="Menu"
|
aria-label="Menu"
|
||||||
onClick={e => {
|
onClick={e => {
|
||||||
@@ -109,22 +111,20 @@ function OverflowMenu({
|
|||||||
style={{ transform: 'rotateZ(90deg)' }}
|
style={{ transform: 'rotateZ(90deg)' }}
|
||||||
/>
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
{open && (
|
|
||||||
<Tooltip
|
<Popover
|
||||||
position="bottom-right"
|
triggerRef={triggerRef}
|
||||||
width={150}
|
isOpen={open}
|
||||||
style={{ padding: 0 }}
|
onOpenChange={() => setOpen(false)}
|
||||||
onClose={() => setOpen(false)}
|
>
|
||||||
>
|
<Menu
|
||||||
<Menu
|
onMenuSelect={name => {
|
||||||
onMenuSelect={name => {
|
onAction(name, schedule.id);
|
||||||
onAction(name, schedule.id);
|
setOpen(false);
|
||||||
setOpen(false);
|
}}
|
||||||
}}
|
items={getMenuItems()}
|
||||||
items={getMenuItems()}
|
/>
|
||||||
/>
|
</Popover>
|
||||||
</Tooltip>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
6
upcoming-release-notes/2771.md
Normal file
6
upcoming-release-notes/2771.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
category: Maintenance
|
||||||
|
authors: [MatissJanis]
|
||||||
|
---
|
||||||
|
|
||||||
|
Migrating native `Tooltip` component to react-aria Tooltip/Popover (vol.6)
|
||||||
Reference in New Issue
Block a user