Counterparts section design update, touch dnd support, marks scrolling #182

Merged
enes merged 9 commits from 174-add-users-updates into staging 2024-09-06 10:22:56 +00:00
8 changed files with 288 additions and 271 deletions

79
package-lock.json generated
View File

@ -34,9 +34,10 @@
"nostr-tools": "2.7.0", "nostr-tools": "2.7.0",
"pdf-lib": "^1.17.1", "pdf-lib": "^1.17.1",
"pdfjs-dist": "^4.4.168", "pdfjs-dist": "^4.4.168",
"rdndmb-html5-to-touch": "^8.0.3",
"react": "^18.2.0", "react": "^18.2.0",
"react-dnd": "16.0.1", "react-dnd": "^16.0.1",
"react-dnd-html5-backend": "16.0.1", "react-dnd-multi-backend": "^8.0.3",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-dropzone": "^14.2.3", "react-dropzone": "^14.2.3",
"react-redux": "9.1.0", "react-redux": "9.1.0",
@ -3267,6 +3268,19 @@
"@babel/runtime": "^7.9.2" "@babel/runtime": "^7.9.2"
} }
}, },
"node_modules/dnd-multi-backend": {
"version": "8.0.3",
"resolved": "https://registry.npmjs.org/dnd-multi-backend/-/dnd-multi-backend-8.0.3.tgz",
"integrity": "sha512-yFFARotr+OEJk787Fsj+V52pi6j7+Pt/CRp3IR2Ai3fnxA/z6J54T7+gxkXzXu4cvxTNE7NiBzzAaJ2f7JjFTw==",
"license": "MIT",
"funding": {
"type": "individual",
"url": "https://github.com/sponsors/LouisBrunner"
},
"peerDependencies": {
"dnd-core": "^16.0.1"
}
},
"node_modules/doctrine": { "node_modules/doctrine": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
@ -5687,6 +5701,21 @@
} }
] ]
}, },
"node_modules/rdndmb-html5-to-touch": {
"version": "8.0.3",
"resolved": "https://registry.npmjs.org/rdndmb-html5-to-touch/-/rdndmb-html5-to-touch-8.0.3.tgz",
"integrity": "sha512-VfIbLjlL9NAnZzc2M5fGPCNkDyK12+ahgILGO5RjS7jkgUlxwB0c/XvxVQNfY/2ocg7isTY/G7tqxJk5fSTZAA==",
"license": "MIT",
"dependencies": {
"dnd-multi-backend": "^8.0.3",
"react-dnd-html5-backend": "^16.0.1",
"react-dnd-touch-backend": "^16.0.1"
},
"funding": {
"type": "individual",
"url": "https://github.com/sponsors/LouisBrunner"
}
},
"node_modules/react": { "node_modules/react": {
"version": "18.2.0", "version": "18.2.0",
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
@ -5702,6 +5731,7 @@
"version": "16.0.1", "version": "16.0.1",
"resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-16.0.1.tgz", "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-16.0.1.tgz",
"integrity": "sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==", "integrity": "sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==",
"license": "MIT",
"dependencies": { "dependencies": {
"@react-dnd/invariant": "^4.0.1", "@react-dnd/invariant": "^4.0.1",
"@react-dnd/shallowequal": "^4.0.1", "@react-dnd/shallowequal": "^4.0.1",
@ -5731,10 +5761,55 @@
"version": "16.0.1", "version": "16.0.1",
"resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-16.0.1.tgz", "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-16.0.1.tgz",
"integrity": "sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw==", "integrity": "sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw==",
"license": "MIT",
"dependencies": { "dependencies": {
"dnd-core": "^16.0.1" "dnd-core": "^16.0.1"
} }
}, },
"node_modules/react-dnd-multi-backend": {
"version": "8.0.3",
"resolved": "https://registry.npmjs.org/react-dnd-multi-backend/-/react-dnd-multi-backend-8.0.3.tgz",
"integrity": "sha512-IwH7Mf6R05KIFohX0hHMTluoAvuUD8SO15KCD+9fY0nJ4nc1FGCMCSyMZw8R1XNStKp+JnNg3ZMtiaf5DebSUg==",
"license": "MIT",
"dependencies": {
"dnd-multi-backend": "^8.0.3",
"react-dnd-preview": "^8.0.3"
},
"funding": {
"type": "individual",
"url": "https://github.com/sponsors/LouisBrunner"
},
"peerDependencies": {
"dnd-core": "^16.0.1",
"react": "^16.14.0 || ^17.0.2 || ^18.0.0",
"react-dnd": "^16.0.1",
"react-dom": "^16.14.0 || ^17.0.2 || ^18.0.0"
}
},
"node_modules/react-dnd-preview": {
"version": "8.0.3",
"resolved": "https://registry.npmjs.org/react-dnd-preview/-/react-dnd-preview-8.0.3.tgz",
"integrity": "sha512-s69Ro47QYDthDhj73iQ0VioMCjtlZ1AytKBDkQaHKm5DTjA8D2bIaFKCBQd330QEW0SIzqLJrZGCSlIY2xraJg==",
"license": "MIT",
"funding": {
"type": "individual",
"url": "https://github.com/sponsors/LouisBrunner"
},
"peerDependencies": {
"react": "^16.14.0 || ^17.0.2 || ^18.0.0",
"react-dnd": "^16.0.1"
}
},
"node_modules/react-dnd-touch-backend": {
"version": "16.0.1",
"resolved": "https://registry.npmjs.org/react-dnd-touch-backend/-/react-dnd-touch-backend-16.0.1.tgz",
"integrity": "sha512-NonoCABzzjyWGZuDxSG77dbgMZ2Wad7eQiCd/ECtsR2/NBLTjGksPUx9UPezZ1nQ/L7iD130Tz3RUshL/ClKLA==",
"license": "MIT",
"dependencies": {
"@react-dnd/invariant": "^4.0.1",
"dnd-core": "^16.0.1"
}
},
"node_modules/react-dom": { "node_modules/react-dom": {
"version": "18.2.0", "version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",

View File

@ -44,9 +44,10 @@
"nostr-tools": "2.7.0", "nostr-tools": "2.7.0",
"pdf-lib": "^1.17.1", "pdf-lib": "^1.17.1",
"pdfjs-dist": "^4.4.168", "pdfjs-dist": "^4.4.168",
"rdndmb-html5-to-touch": "^8.0.3",
"react": "^18.2.0", "react": "^18.2.0",
"react-dnd": "16.0.1", "react-dnd": "^16.0.1",
"react-dnd-html5-backend": "16.0.1", "react-dnd-multi-backend": "^8.0.3",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-dropzone": "^14.2.3", "react-dropzone": "^14.2.3",
"react-redux": "9.1.0", "react-redux": "9.1.0",

View File

@ -141,6 +141,8 @@ li {
color: black; color: black;
letter-spacing: normal; letter-spacing: normal;
border: 1px solid transparent; border: 1px solid transparent;
scroll-margin-top: $header-height + $body-vertical-padding;
} }
[data-dev='true'] { [data-dev='true'] {

View File

@ -2,6 +2,8 @@ import { CurrentUserMark } from '../../types/mark.ts'
import styles from '../DrawPDFFields/style.module.scss' import styles from '../DrawPDFFields/style.module.scss'
import { FONT_SIZE, FONT_TYPE, inPx } from '../../utils/pdf.ts' import { FONT_SIZE, FONT_TYPE, inPx } from '../../utils/pdf.ts'
import { useScale } from '../../hooks/useScale.tsx' import { useScale } from '../../hooks/useScale.tsx'
import { forwardRef } from 'react'
import { npubToHex } from '../../utils/nostr.ts'
interface PdfMarkItemProps { interface PdfMarkItemProps {
userMark: CurrentUserMark userMark: CurrentUserMark
@ -14,13 +16,11 @@ interface PdfMarkItemProps {
/** /**
* Responsible for display an individual Pdf Mark. * Responsible for display an individual Pdf Mark.
*/ */
const PdfMarkItem = ({ const PdfMarkItem = forwardRef<HTMLDivElement, PdfMarkItemProps>(
selectedMark, (
handleMarkClick, { selectedMark, handleMarkClick, selectedMarkValue, userMark, pageWidth },
selectedMarkValue, ref
userMark, ) => {
pageWidth
}: PdfMarkItemProps) => {
const { location } = userMark.mark const { location } = userMark.mark
const handleClick = () => handleMarkClick(userMark.mark.id) const handleClick = () => handleMarkClick(userMark.mark.id)
const isEdited = () => selectedMark?.mark.id === userMark.mark.id const isEdited = () => selectedMark?.mark.id === userMark.mark.id
@ -29,9 +29,16 @@ const PdfMarkItem = ({
const { from } = useScale() const { from } = useScale()
return ( return (
<div <div
ref={ref}
onClick={handleClick} onClick={handleClick}
className={`file-mark ${styles.drawingRectangle} ${isEdited() && styles.edited}`} className={`file-mark ${styles.drawingRectangle} ${isEdited() && styles.edited}`}
style={{ style={{
backgroundColor: selectedMark?.mark.npub
? `#${npubToHex(selectedMark?.mark.npub)?.substring(0, 6)}4b`
: undefined,
borderColor: selectedMark?.mark.npub
? `#${npubToHex(selectedMark?.mark.npub)?.substring(0, 6)}`
: undefined,
left: inPx(from(pageWidth, location.left)), left: inPx(from(pageWidth, location.left)),
top: inPx(from(pageWidth, location.top)), top: inPx(from(pageWidth, location.top)),
width: inPx(from(pageWidth, location.width)), width: inPx(from(pageWidth, location.width)),
@ -44,5 +51,6 @@ const PdfMarkItem = ({
</div> </div>
) )
} }
)
export default PdfMarkItem export default PdfMarkItem

View File

@ -48,16 +48,15 @@ const PdfPageItem = ({
alt={`page ${pageIndex + 1} of ${fileName}`} alt={`page ${pageIndex + 1} of ${fileName}`}
/> />
{currentUserMarks.map((m, i) => ( {currentUserMarks.map((m, i) => (
<div key={i} ref={(el) => (markRefs.current[m.id] = el)}>
<PdfMarkItem <PdfMarkItem
key={i} key={i}
ref={(el) => (markRefs.current[m.id] = el)}
handleMarkClick={handleMarkClick} handleMarkClick={handleMarkClick}
selectedMarkValue={selectedMarkValue} selectedMarkValue={selectedMarkValue}
userMark={m} userMark={m}
selectedMark={selectedMark} selectedMark={selectedMark}
pageWidth={page.width} pageWidth={page.width}
/> />
</div>
))} ))}
{otherUserMarks.map((m, i) => { {otherUserMarks.map((m, i) => {
return ( return (

View File

@ -1,20 +1,13 @@
import { import styles from './style.module.scss'
Button, import { Button, FormHelperText, TextField, Tooltip } from '@mui/material'
FormHelperText,
ListItemIcon,
ListItemText,
MenuItem,
Select,
TextField,
Tooltip
} from '@mui/material'
import type { Identifier, XYCoord } from 'dnd-core' import type { Identifier, XYCoord } from 'dnd-core'
import saveAs from 'file-saver' import saveAs from 'file-saver'
import JSZip from 'jszip' import JSZip from 'jszip'
import { Event, kinds } from 'nostr-tools' import { Event, kinds } from 'nostr-tools'
import { useEffect, useRef, useState } from 'react' import { useEffect, useRef, useState } from 'react'
import { DndProvider, useDrag, useDrop } from 'react-dnd' import { DndProvider, useDrag, useDrop } from 'react-dnd'
import { HTML5Backend } from 'react-dnd-html5-backend' import { MultiBackend } from 'react-dnd-multi-backend'
import { HTML5toTouch } from 'rdndmb-html5-to-touch'
import { useSelector } from 'react-redux' import { useSelector } from 'react-redux'
import { useLocation, useNavigate } from 'react-router-dom' import { useLocation, useNavigate } from 'react-router-dom'
import { toast } from 'react-toastify' import { toast } from 'react-toastify'
@ -49,7 +42,6 @@ import {
uploadToFileStorage uploadToFileStorage
} from '../../utils' } from '../../utils'
import { Container } from '../../components/Container' import { Container } from '../../components/Container'
import styles from './style.module.scss'
import fileListStyles from '../../components/FileList/style.module.scss' import fileListStyles from '../../components/FileList/style.module.scss'
import { DrawTool, MarkType } from '../../types/drawing' import { DrawTool, MarkType } from '../../types/drawing'
import { DrawPDFFields } from '../../components/DrawPDFFields' import { DrawPDFFields } from '../../components/DrawPDFFields'
@ -874,21 +866,12 @@ export const CreatePage = () => {
<div className={styles.flexWrap}> <div className={styles.flexWrap}>
<div className={styles.inputWrapper}> <div className={styles.inputWrapper}>
<TextField <TextField
fullWidth
placeholder="Title" placeholder="Title"
size="small" size="small"
type="text" type="text"
value={title} value={title}
onChange={(e) => setTitle(e.target.value)} onChange={(e) => setTitle(e.target.value)}
sx={{
width: '100%',
fontSize: '16px',
'& .MuiInputBase-input': {
padding: '7px 14px'
},
'& .MuiOutlinedInput-notchedOutline': {
display: 'none'
}
}}
/> />
</div> </div>
<ol className={`${styles.paperGroup} ${styles.orderedFilesList}`}> <ol className={`${styles.paperGroup} ${styles.orderedFilesList}`}>
@ -907,9 +890,6 @@ export const CreatePage = () => {
aria-label={`delete ${file.name}`} aria-label={`delete ${file.name}`}
variant="text" variant="text"
onClick={(event) => handleRemoveFile(event, file)} onClick={(event) => handleRemoveFile(event, file)}
sx={{
minWidth: '44px'
}}
> >
<FontAwesomeIcon icon={faTrash} /> <FontAwesomeIcon icon={faTrash} />
</Button> </Button>
@ -932,76 +912,6 @@ export const CreatePage = () => {
} }
right={ right={
<div className={styles.flexWrap}> <div className={styles.flexWrap}>
<div className={styles.inputWrapper}>
<TextField
placeholder="Add user"
value={userInput}
onChange={(e) => setUserInput(e.target.value)}
onKeyDown={handleInputKeyDown}
error={!!error}
fullWidth
sx={{
fontSize: '16px',
'& .MuiInputBase-input': {
padding: '7px 14px'
},
'& .MuiOutlinedInput-notchedOutline': {
display: 'none'
}
}}
/>
<Select
name="add-user-role"
aria-label="role"
value={userRole}
variant="filled"
// Hide arrow for dropdown
IconComponent={() => null}
renderValue={(value) => (
<FontAwesomeIcon
color="var(--primary-main)"
icon={value === UserRole.signer ? faPen : faEye}
/>
)}
onChange={(e) => setUserRole(e.target.value as UserRole)}
sx={{
fontSize: '16px',
minWidth: '44px',
'& .MuiInputBase-input': {
padding: '7px 14px!important',
textOverflow: 'unset!important'
}
}}
>
<MenuItem value={UserRole.signer}>
<ListItemIcon>
<FontAwesomeIcon icon={faPen} />
</ListItemIcon>
<ListItemText>{UserRole.signer}</ListItemText>
</MenuItem>
<MenuItem value={UserRole.viewer} sx={{}}>
<ListItemIcon>
<FontAwesomeIcon icon={faEye} />
</ListItemIcon>
<ListItemText>{UserRole.viewer}</ListItemText>
</MenuItem>
</Select>
<Button
disabled={!userInput}
onClick={handleAddUser}
variant="contained"
aria-label="Add"
sx={{
minWidth: '44px',
padding: '11.5px 12px',
borderTopLeftRadius: 0,
borderBottomLeftRadius: 0
}}
>
<FontAwesomeIcon icon={faPlus} />
</Button>
</div>
<div className={`${styles.paperGroup} ${styles.users}`}> <div className={`${styles.paperGroup} ${styles.users}`}>
<DisplayUser <DisplayUser
metadata={metadata} metadata={metadata}
@ -1011,7 +921,43 @@ export const CreatePage = () => {
moveSigner={moveSigner} moveSigner={moveSigner}
/> />
</div> </div>
<div className={styles.addCounterpart}>
<div className={styles.inputWrapper}>
<TextField
fullWidth
placeholder="Add counterpart"
value={userInput}
onChange={(e) => setUserInput(e.target.value)}
onKeyDown={handleInputKeyDown}
error={!!error}
/>
</div>
<Button
onClick={() =>
setUserRole(
userRole === UserRole.signer
? UserRole.viewer
: UserRole.signer
)
}
variant="contained"
aria-label="Toggle User Role"
className={styles.counterpartToggleButton}
>
<FontAwesomeIcon
icon={userRole === UserRole.signer ? faPen : faEye}
/>
</Button>
<Button
disabled={!userInput}
onClick={handleAddUser}
variant="contained"
aria-label="Add"
className={styles.counterpartToggleButton}
>
<FontAwesomeIcon icon={faPlus} />
</Button>
</div>
<Button onClick={handleCreate} variant="contained"> <Button onClick={handleCreate} variant="contained">
Publish Publish
</Button> </Button>
@ -1032,11 +978,7 @@ export const CreatePage = () => {
{drawTool.active ? ( {drawTool.active ? (
<FontAwesomeIcon fontSize={'15px'} icon={faEllipsis} /> <FontAwesomeIcon fontSize={'15px'} icon={faEllipsis} />
) : ( ) : (
<span <span className={styles.comingSoonPlaceholder}>
style={{
fontSize: '10px'
}}
>
Coming soon Coming soon
</span> </span>
)} )}
@ -1084,12 +1026,12 @@ const DisplayUser = ({
}: DisplayUsersProps) => { }: DisplayUsersProps) => {
return ( return (
<> <>
<DndProvider backend={HTML5Backend}> <DndProvider backend={MultiBackend} options={HTML5toTouch}>
{users {users
.filter((user) => user.role === UserRole.signer) .filter((user) => user.role === UserRole.signer)
.map((user, index) => ( .map((user, index) => (
<SignerRow <SignerCounterpart
key={`signer-${index}`} key={`signer-${user.pubkey}`}
userMeta={metadata[user.pubkey]} userMeta={metadata[user.pubkey]}
user={user} user={user}
index={index} index={index}
@ -1101,72 +1043,16 @@ const DisplayUser = ({
</DndProvider> </DndProvider>
{users {users
.filter((user) => user.role === UserRole.viewer) .filter((user) => user.role === UserRole.viewer)
.map((user, index) => { .map((user) => {
const userMeta = metadata[user.pubkey]
return ( return (
<div className={styles.user} key={index}> <div className={styles.user} key={`viewer-${user.pubkey}`}>
<div className={styles.avatar}> <Counterpart
<UserAvatar userMeta={metadata[user.pubkey]}
pubkey={user.pubkey} user={user}
name={ handleUserRoleChange={handleUserRoleChange}
userMeta?.display_name || handleRemoveUser={handleRemoveUser}
userMeta?.name ||
shorten(hexToNpub(user.pubkey))
}
image={userMeta?.picture}
/> />
</div> </div>
<Select
name={`change-user-role-${user.pubkey}`}
aria-label="role"
value={user.role}
variant="outlined"
IconComponent={() => null}
renderValue={(value) => (
<FontAwesomeIcon
fontSize={'14px'}
color="var(--primary-main)"
icon={value === UserRole.signer ? faPen : faEye}
/>
)}
onChange={(e) =>
handleUserRoleChange(e.target.value as UserRole, user.pubkey)
}
sx={{
fontSize: '16px',
minWidth: '34px',
maxWidth: '34px',
minHeight: '34px',
maxHeight: '34px',
'& .MuiInputBase-input': {
padding: '10px !important',
textOverflow: 'unset!important'
},
'& .MuiOutlinedInput-notchedOutline': {
display: 'none'
}
}}
>
<MenuItem value={UserRole.signer}>{UserRole.signer}</MenuItem>
<MenuItem value={UserRole.viewer}>{UserRole.viewer}</MenuItem>
</Select>
<Tooltip title="Remove User" arrow>
<Button
onClick={() => handleRemoveUser(user.pubkey)}
sx={{
minWidth: '34px',
height: '34px',
padding: 0,
color: 'rgba(0, 0, 0, 0.35)',
'&:hover': {
color: 'white'
}
}}
>
<FontAwesomeIcon fontSize={'14px'} icon={faTrash} />
</Button>
</Tooltip>
</div>
) )
})} })}
</> </>
@ -1179,23 +1065,26 @@ interface DragItem {
type: string type: string
} }
type SignerRowProps = { type CounterpartProps = {
userMeta: ProfileMetadata userMeta: ProfileMetadata
user: User user: User
index: number
moveSigner: (dragIndex: number, hoverIndex: number) => void
handleUserRoleChange: (role: UserRole, pubkey: string) => void handleUserRoleChange: (role: UserRole, pubkey: string) => void
handleRemoveUser: (pubkey: string) => void handleRemoveUser: (pubkey: string) => void
} }
const SignerRow = ({ type SignerCounterpartProps = CounterpartProps & {
index: number
moveSigner: (dragIndex: number, hoverIndex: number) => void
}
const SignerCounterpart = ({
userMeta, userMeta,
user, user,
index, index,
moveSigner, moveSigner,
handleUserRoleChange, handleUserRoleChange,
handleRemoveUser handleRemoveUser
}: SignerRowProps) => { }: SignerCounterpartProps) => {
const ref = useRef<HTMLTableRowElement>(null) const ref = useRef<HTMLTableRowElement>(null)
const [{ handlerId }, drop] = useDrop< const [{ handlerId }, drop] = useDrop<
@ -1269,7 +1158,7 @@ const SignerRow = ({
}) })
}) })
const opacity = isDragging ? 0 : 1 const opacity = isDragging ? 0.2 : 1
drag(drop(ref)) drag(drop(ref))
return ( return (
@ -1280,6 +1169,24 @@ const SignerRow = ({
ref={ref} ref={ref}
> >
<FontAwesomeIcon width={'14px'} fontSize={'14px'} icon={faGripLines} /> <FontAwesomeIcon width={'14px'} fontSize={'14px'} icon={faGripLines} />
<Counterpart
user={user}
userMeta={userMeta}
handleRemoveUser={handleRemoveUser}
handleUserRoleChange={handleUserRoleChange}
/>
</div>
)
}
const Counterpart = ({
userMeta,
user,
handleUserRoleChange,
handleRemoveUser
}: CounterpartProps) => {
return (
<>
<div className={styles.avatar}> <div className={styles.avatar}>
<UserAvatar <UserAvatar
pubkey={user.pubkey} pubkey={user.pubkey}
@ -1291,56 +1198,31 @@ const SignerRow = ({
image={userMeta?.picture} image={userMeta?.picture}
/> />
</div> </div>
<Select <Tooltip title="Toggle User Role" arrow disableInteractive>
name={`change-user-role-${user.pubkey}`}
aria-label="role"
value={user.role}
variant="outlined"
IconComponent={() => null}
renderValue={(value) => (
<FontAwesomeIcon
fontSize={'14px'}
color="var(--primary-main)"
icon={value === UserRole.signer ? faPen : faEye}
/>
)}
onChange={(e) =>
handleUserRoleChange(e.target.value as UserRole, user.pubkey)
}
sx={{
fontSize: '16px',
minWidth: '34px',
maxWidth: '34px',
minHeight: '34px',
maxHeight: '34px',
'& .MuiInputBase-input': {
padding: '10px !important',
textOverflow: 'unset!important'
},
'& .MuiOutlinedInput-notchedOutline': {
display: 'none'
}
}}
>
<MenuItem value={UserRole.signer}>{UserRole.signer}</MenuItem>
<MenuItem value={UserRole.viewer}>{UserRole.viewer}</MenuItem>
</Select>
<Tooltip title="Remove User" arrow>
<Button <Button
onClick={() => handleRemoveUser(user.pubkey)} onClick={() =>
sx={{ handleUserRoleChange(
minWidth: '34px', user.role === UserRole.signer ? UserRole.viewer : UserRole.signer,
height: '34px', user.pubkey
padding: 0, )
color: 'rgba(0, 0, 0, 0.35)', }
'&:hover': { className={styles.counterpartRowToggleButton}
color: 'white' data-variant="primary"
} >
}} <FontAwesomeIcon
> icon={user.role === UserRole.signer ? faPen : faEye}
<FontAwesomeIcon fontSize={'14px'} icon={faTrash} /> />
</Button> </Button>
</Tooltip> </Tooltip>
</div> <Tooltip title="Remove User" arrow disableInteractive>
<Button
onClick={() => handleRemoveUser(user.pubkey)}
className={styles.counterpartRowToggleButton}
data-variant="secondary"
>
<FontAwesomeIcon icon={faTrash} />
</Button>
</Tooltip>
</>
) )
} }

View File

@ -42,6 +42,7 @@
} }
button { button {
min-width: 44px;
color: $primary-main; color: $primary-main;
} }
@ -78,7 +79,7 @@
align-items: center; align-items: center;
flex-shrink: 0; flex-shrink: 0;
height: 34px; height: 36px;
overflow: hidden; overflow: hidden;
border-radius: 4px; border-radius: 4px;
outline: solid 1px #dddddd; outline: solid 1px #dddddd;
@ -89,11 +90,43 @@
&:focus-within { &:focus-within {
outline-color: $primary-main; outline-color: $primary-main;
} }
// Override default MUI input styles only inside inputWrapepr
:global {
.MuiInputBase-input {
padding: 7px 14px;
}
.MuiOutlinedInput-notchedOutline {
display: none;
}
}
}
.addCounterpart {
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: start;
gap: 10px;
> .inputWrapper {
flex-shrink: 1;
}
button {
min-width: 44px;
padding: 11px 12px;
}
} }
.users { .users {
flex-shrink: 0; flex-shrink: 0;
max-height: 33vh; max-height: 33vh;
.counterpartToggleButton {
min-width: 44px;
padding: 11px 12px;
}
} }
.user { .user {
@ -108,6 +141,22 @@
a:hover { a:hover {
text-decoration: none; text-decoration: none;
} }
// Higher specificify to override default button styles
.counterpartRowToggleButton {
min-width: 34px;
height: 34px;
padding: 0;
}
}
.counterpartRowToggleButton {
&[data-variant='primary'] {
color: $primary-main;
}
&[data-variant='secondary'] {
color: rgba(0, 0, 0, 0.35);
}
} }
.avatar { .avatar {
@ -187,3 +236,7 @@
cursor: not-allowed; cursor: not-allowed;
} }
} }
.comingSoonPlaceholder {
font-size: 10px;
}

View File

@ -1,5 +1,5 @@
/** /**
* Function will replace the middle of the string with 3 dots if length greater then * Function will replace the middle of the string with ellipsis if length greater then
* offset value * offset value
* @param str string to shorten * @param str string to shorten
* @param offset of how many chars to keep in the beginning and the end * @param offset of how many chars to keep in the beginning and the end
@ -9,10 +9,7 @@ export const shorten = (str: string, offset = 9) => {
// return original string if it is not long enough // return original string if it is not long enough
if (str.length < offset * 2 + 4) return str if (str.length < offset * 2 + 4) return str
return `${str.slice(0, offset)}...${str.slice( return `${str.slice(0, offset)}${str.slice(str.length - offset, str.length)}`
str.length - offset,
str.length
)}`
} }
export const stringToHex = (str: string) => { export const stringToHex = (str: string) => {