chore: minor vaults ui improvements
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import Button from '@/Components/Button/Button'
|
||||
import Icon from '@/Components/Icon/Icon'
|
||||
import ModalOverlay from '@/Components/Modal/ModalOverlay'
|
||||
import { TrustedContactInterface } from '@standardnotes/snjs'
|
||||
import { TrustedContactInterface, classNames } from '@standardnotes/snjs'
|
||||
import EditContactModal from './EditContactModal'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { useApplication } from '@/Components/ApplicationProvider'
|
||||
@@ -28,15 +28,19 @@ const ContactItem = ({ contact }: Props) => {
|
||||
<EditContactModal editContactUuid={contact.contactUuid} onCloseDialog={closeContactModal} />
|
||||
</ModalOverlay>
|
||||
|
||||
<div className="bg-gray-100 flex flex-row gap-3.5 rounded-lg px-3.5 py-2.5 shadow-md">
|
||||
<Icon type={'user'} size="custom" className="mt-2.5 h-5.5 w-5.5 flex-shrink-0" />
|
||||
<div className="flex flex-col gap-2 py-1.5">
|
||||
<div className="bg-gray-100 flex flex-row gap-3.5 rounded-lg px-3.5 py-2.5 border border-border shadow">
|
||||
<Icon type="user" size="custom" className="mt-2 h-5 w-5 flex-shrink-0" />
|
||||
<div className="flex flex-col gap-1 py-1.5 overflow-hidden">
|
||||
<span
|
||||
className={`mr-auto overflow-hidden text-ellipsis text-base font-bold ${contact.isMe ? 'text-info' : ''}`}
|
||||
className={classNames(
|
||||
'mr-auto overflow-hidden text-ellipsis text-base font-bold w-full',
|
||||
contact.isMe ? 'text-info' : '',
|
||||
)}
|
||||
>
|
||||
{contact.name}
|
||||
</span>
|
||||
<span className="mr-auto overflow-hidden text-ellipsis text-sm">{collaborationID}</span>
|
||||
|
||||
<span className="mr-auto overflow-hidden text-ellipsis text-sm w-full">{collaborationID}</span>
|
||||
|
||||
<div className="mt-2.5 flex flex-row">
|
||||
<Button label="Edit" className={'mr-3 text-xs'} onClick={() => setIsContactModalOpen(true)} />
|
||||
|
||||
@@ -19,6 +19,8 @@ import VaultItem from './Vaults/VaultItem'
|
||||
import Button from '@/Components/Button/Button'
|
||||
import InviteItem from './Invites/InviteItem'
|
||||
import EditVaultModal from './Vaults/VaultModal/EditVaultModal'
|
||||
import PreferencesPane from '../../PreferencesComponents/PreferencesPane'
|
||||
import { ToastType, addToast } from '@standardnotes/toast'
|
||||
|
||||
const Vaults = () => {
|
||||
const application = useApplication()
|
||||
@@ -94,7 +96,7 @@ const Vaults = () => {
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
<PreferencesPane>
|
||||
<ModalOverlay isOpen={isAddContactModalOpen} close={closeAddContactModal}>
|
||||
<EditContactModal onCloseDialog={closeAddContactModal} />
|
||||
</ModalOverlay>
|
||||
@@ -147,23 +149,42 @@ const Vaults = () => {
|
||||
<Title>CollaborationID</Title>
|
||||
<Subtitle>Share your CollaborationID with collaborators to join their vaults.</Subtitle>
|
||||
{contactService.isCollaborationEnabled() ? (
|
||||
<div className="mt-2.5 flex flex-row">
|
||||
<code>
|
||||
<pre>{contactService.getCollaborationID()}</pre>
|
||||
<>
|
||||
<code className="mt-2.5 overflow-hidden whitespace-pre-wrap break-words p-3 border border-border rounded bg-contrast">
|
||||
{contactService.getCollaborationID()}
|
||||
</code>
|
||||
</div>
|
||||
<Button
|
||||
label="Copy ID"
|
||||
className="mt-2"
|
||||
onClick={async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(contactService.getCollaborationID())
|
||||
addToast({
|
||||
type: ToastType.Success,
|
||||
message: 'Copied to clipboard',
|
||||
})
|
||||
} catch (error) {
|
||||
addToast({
|
||||
type: ToastType.Error,
|
||||
message: 'Failed to copy to clipboard',
|
||||
})
|
||||
console.error(error)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<div className="mt-2.5 flex flex-row">
|
||||
<Button
|
||||
label="Enable Vault Sharing"
|
||||
className={'mr-3 text-xs'}
|
||||
className="mr-3 text-xs"
|
||||
onClick={() => contactService.enableCollaboration()}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</PreferencesSegment>
|
||||
</PreferencesGroup>
|
||||
</>
|
||||
</PreferencesPane>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,12 @@ const RadioButtonGroup = ({ value, items, onChange, className }: Props) => {
|
||||
})
|
||||
|
||||
return (
|
||||
<RadioGroup store={radio} className={`flex divide-x divide-border rounded border border-border ${className ?? ''}`}>
|
||||
<RadioGroup
|
||||
store={radio}
|
||||
className={`flex divide-x divide-border rounded border border-border md:translucent-ui:border-[--popover-border-color] ${
|
||||
className ?? ''
|
||||
}`}
|
||||
>
|
||||
{items.map(({ label, value: itemValue }) => (
|
||||
<label
|
||||
className={classNames(
|
||||
|
||||
@@ -30,6 +30,7 @@ const ManyVaultSelectionMenu: FunctionComponent = () => {
|
||||
|
||||
return (
|
||||
<Menu a11yLabel="Vault selection menu" isOpen>
|
||||
{!vaults.length && <div className="text-center py-1">No vaults found</div>}
|
||||
{vaults.map((vault) => (
|
||||
<MenuSwitchButtonItem
|
||||
onChange={() => {
|
||||
|
||||
@@ -26,6 +26,7 @@ const SingleVaultSelectionMenu: FunctionComponent = () => {
|
||||
|
||||
return (
|
||||
<Menu a11yLabel="Vault selection menu" isOpen>
|
||||
{!vaults.length && <div className="text-center py-1">No vaults found</div>}
|
||||
{vaults.map((vault) => (
|
||||
<MenuRadioButtonItem key={vault.uuid} checked={isVaultVisible(vault)} onClick={() => selectVault(vault)}>
|
||||
<div className="flex w-full items-center gap-1">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { FunctionComponent, useCallback, useRef, useState } from 'react'
|
||||
import { useCallback, useRef, useState } from 'react'
|
||||
import Icon from '@/Components/Icon/Icon'
|
||||
import { KeyboardKey } from '@standardnotes/ui-services'
|
||||
import Popover from '../Popover/Popover'
|
||||
@@ -9,24 +9,10 @@ import MenuItem from '../Menu/MenuItem'
|
||||
import Menu from '../Menu/Menu'
|
||||
import { featureTrunkVaultsEnabled } from '@/FeatureTrunk'
|
||||
|
||||
type Props = {
|
||||
iconClassName: string
|
||||
items: DecryptedItemInterface[]
|
||||
}
|
||||
|
||||
const AddToVaultMenuOption: FunctionComponent<Props> = ({ iconClassName, items }) => {
|
||||
const VaultMenu = ({ items }: { items: DecryptedItemInterface[] }) => {
|
||||
const application = useApplication()
|
||||
const menuContainerRef = useRef<HTMLDivElement>(null)
|
||||
const buttonRef = useRef<HTMLButtonElement>(null)
|
||||
|
||||
const vaults = application.vaults.getVaults()
|
||||
|
||||
const [isSubMenuOpen, setIsSubMenuOpen] = useState(false)
|
||||
|
||||
const toggleSubMenu = useCallback(() => {
|
||||
setIsSubMenuOpen((isOpen) => !isOpen)
|
||||
}, [])
|
||||
|
||||
const addItemsToVault = useCallback(
|
||||
async (vault: VaultListingInterface) => {
|
||||
if (application.vaultLocks.isVaultLocked(vault)) {
|
||||
@@ -68,6 +54,68 @@ const AddToVaultMenuOption: FunctionComponent<Props> = ({ iconClassName, items }
|
||||
|
||||
const singleItemVault = items.length === 1 ? application.vaults.getItemVault(items[0]) : undefined
|
||||
|
||||
return (
|
||||
<Menu a11yLabel="Vault selection menu" isOpen={true}>
|
||||
{doSomeItemsBelongToVault && (
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
void removeItemsFromVault()
|
||||
}}
|
||||
>
|
||||
<span className="flex overflow-hidden overflow-ellipsis whitespace-nowrap">
|
||||
<Icon type="close" className="mr-2 text-neutral" />
|
||||
<div className="flex w-full items-center gap-1">
|
||||
Move out of {singleItemVault ? singleItemVault.name : 'vaults'}
|
||||
</div>
|
||||
</span>
|
||||
</MenuItem>
|
||||
)}
|
||||
{!vaults.length && <div className="flex flex-col items-center justify-center py-1">No vaults found</div>}
|
||||
{vaults.map((vault) => {
|
||||
if (singleItemVault) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<MenuItem
|
||||
key={vault.uuid}
|
||||
onClick={() => {
|
||||
doesVaultContainItems(vault) ? void removeItemsFromVault() : void addItemsToVault(vault)
|
||||
}}
|
||||
>
|
||||
<span
|
||||
className={classNames(
|
||||
'flex overflow-ellipsis whitespace-nowrap',
|
||||
doesVaultContainItems(vault) ? 'font-bold' : '',
|
||||
)}
|
||||
>
|
||||
<Icon
|
||||
type="safe-square"
|
||||
size="large"
|
||||
className={classNames('mr-2 text-neutral', doesVaultContainItems(vault) ? 'text-info' : '')}
|
||||
/>
|
||||
<div className="flex w-full items-center">
|
||||
{vault.name}
|
||||
{application.vaultLocks.isVaultLocked(vault) && <Icon className="ml-1" type="lock" size={'small'} />}
|
||||
</div>
|
||||
</span>
|
||||
</MenuItem>
|
||||
)
|
||||
})}
|
||||
</Menu>
|
||||
)
|
||||
}
|
||||
|
||||
const AddToVaultMenuOption = ({ iconClassName, items }: { iconClassName: string; items: DecryptedItemInterface[] }) => {
|
||||
const menuContainerRef = useRef<HTMLDivElement>(null)
|
||||
const buttonRef = useRef<HTMLButtonElement>(null)
|
||||
|
||||
const [isSubMenuOpen, setIsSubMenuOpen] = useState(false)
|
||||
|
||||
const toggleSubMenu = useCallback(() => {
|
||||
setIsSubMenuOpen((isOpen) => !isOpen)
|
||||
}, [])
|
||||
|
||||
if (!featureTrunkVaultsEnabled()) {
|
||||
return null
|
||||
}
|
||||
@@ -100,55 +148,7 @@ const AddToVaultMenuOption: FunctionComponent<Props> = ({ iconClassName, items }
|
||||
className="py-2"
|
||||
overrideZIndex="z-modal"
|
||||
>
|
||||
<Menu a11yLabel="Vault selection menu" isOpen={isSubMenuOpen}>
|
||||
{doSomeItemsBelongToVault && (
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
void removeItemsFromVault()
|
||||
}}
|
||||
>
|
||||
<span className="flex overflow-hidden overflow-ellipsis whitespace-nowrap">
|
||||
<Icon type="close" className="mr-2 text-neutral" />
|
||||
<div className="flex w-full items-center gap-1">
|
||||
Move out of {singleItemVault ? singleItemVault.name : 'vaults'}
|
||||
</div>
|
||||
</span>
|
||||
</MenuItem>
|
||||
)}
|
||||
{vaults.map((vault) => {
|
||||
if (singleItemVault) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<MenuItem
|
||||
key={vault.uuid}
|
||||
onClick={() => {
|
||||
doesVaultContainItems(vault) ? void removeItemsFromVault() : void addItemsToVault(vault)
|
||||
}}
|
||||
>
|
||||
<span
|
||||
className={classNames(
|
||||
'flex overflow-ellipsis whitespace-nowrap',
|
||||
doesVaultContainItems(vault) ? 'font-bold' : '',
|
||||
)}
|
||||
>
|
||||
<Icon
|
||||
type="safe-square"
|
||||
size="large"
|
||||
className={`mr-2 text-neutral ${doesVaultContainItems(vault) ? 'text-info' : ''}`}
|
||||
/>
|
||||
<div className="flex w-full items-center">
|
||||
{vault.name}
|
||||
{application.vaultLocks.isVaultLocked(vault) && (
|
||||
<Icon className="ml-1" type="lock" size={'small'} />
|
||||
)}
|
||||
</div>
|
||||
</span>
|
||||
</MenuItem>
|
||||
)
|
||||
})}
|
||||
</Menu>
|
||||
<VaultMenu items={items} />
|
||||
</Popover>
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user