Compare commits
10 Commits
7d0d4fcb48
...
bf506705e6
Author | SHA1 | Date | |
---|---|---|---|
bf506705e6 | |||
b8811d730a | |||
479cca2180 | |||
82b7b9f7ce | |||
3e075754e5 | |||
c6010a5bef | |||
81f40c345a | |||
734026b2ee | |||
5caa7f2282 | |||
fa7f0e2fc0 |
79
package-lock.json
generated
79
package-lock.json
generated
@ -34,9 +34,10 @@
|
||||
"nostr-tools": "2.7.0",
|
||||
"pdf-lib": "^1.17.1",
|
||||
"pdfjs-dist": "^4.4.168",
|
||||
"rdndmb-html5-to-touch": "^8.0.3",
|
||||
"react": "^18.2.0",
|
||||
"react-dnd": "16.0.1",
|
||||
"react-dnd-html5-backend": "16.0.1",
|
||||
"react-dnd": "^16.0.1",
|
||||
"react-dnd-multi-backend": "^8.0.3",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-dropzone": "^14.2.3",
|
||||
"react-redux": "9.1.0",
|
||||
@ -3267,6 +3268,19 @@
|
||||
"@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": {
|
||||
"version": "3.0.0",
|
||||
"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": {
|
||||
"version": "18.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
|
||||
@ -5702,6 +5731,7 @@
|
||||
"version": "16.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-16.0.1.tgz",
|
||||
"integrity": "sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@react-dnd/invariant": "^4.0.1",
|
||||
"@react-dnd/shallowequal": "^4.0.1",
|
||||
@ -5731,10 +5761,55 @@
|
||||
"version": "16.0.1",
|
||||
"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==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"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": {
|
||||
"version": "18.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
|
||||
|
@ -44,9 +44,10 @@
|
||||
"nostr-tools": "2.7.0",
|
||||
"pdf-lib": "^1.17.1",
|
||||
"pdfjs-dist": "^4.4.168",
|
||||
"rdndmb-html5-to-touch": "^8.0.3",
|
||||
"react": "^18.2.0",
|
||||
"react-dnd": "16.0.1",
|
||||
"react-dnd-html5-backend": "16.0.1",
|
||||
"react-dnd": "^16.0.1",
|
||||
"react-dnd-multi-backend": "^8.0.3",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-dropzone": "^14.2.3",
|
||||
"react-redux": "9.1.0",
|
||||
|
@ -141,6 +141,8 @@ li {
|
||||
color: black;
|
||||
letter-spacing: normal;
|
||||
border: 1px solid transparent;
|
||||
|
||||
scroll-margin-top: $header-height + $body-vertical-padding;
|
||||
}
|
||||
|
||||
[data-dev='true'] {
|
||||
|
@ -2,6 +2,8 @@ import { CurrentUserMark } from '../../types/mark.ts'
|
||||
import styles from '../DrawPDFFields/style.module.scss'
|
||||
import { FONT_SIZE, FONT_TYPE, inPx } from '../../utils/pdf.ts'
|
||||
import { useScale } from '../../hooks/useScale.tsx'
|
||||
import { forwardRef } from 'react'
|
||||
import { npubToHex } from '../../utils/nostr.ts'
|
||||
|
||||
interface PdfMarkItemProps {
|
||||
userMark: CurrentUserMark
|
||||
@ -14,13 +16,11 @@ interface PdfMarkItemProps {
|
||||
/**
|
||||
* Responsible for display an individual Pdf Mark.
|
||||
*/
|
||||
const PdfMarkItem = ({
|
||||
selectedMark,
|
||||
handleMarkClick,
|
||||
selectedMarkValue,
|
||||
userMark,
|
||||
pageWidth
|
||||
}: PdfMarkItemProps) => {
|
||||
const PdfMarkItem = forwardRef<HTMLDivElement, PdfMarkItemProps>(
|
||||
(
|
||||
{ selectedMark, handleMarkClick, selectedMarkValue, userMark, pageWidth },
|
||||
ref
|
||||
) => {
|
||||
const { location } = userMark.mark
|
||||
const handleClick = () => handleMarkClick(userMark.mark.id)
|
||||
const isEdited = () => selectedMark?.mark.id === userMark.mark.id
|
||||
@ -29,9 +29,16 @@ const PdfMarkItem = ({
|
||||
const { from } = useScale()
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
onClick={handleClick}
|
||||
className={`file-mark ${styles.drawingRectangle} ${isEdited() && styles.edited}`}
|
||||
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)),
|
||||
top: inPx(from(pageWidth, location.top)),
|
||||
width: inPx(from(pageWidth, location.width)),
|
||||
@ -44,5 +51,6 @@ const PdfMarkItem = ({
|
||||
</div>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
export default PdfMarkItem
|
||||
|
@ -48,16 +48,15 @@ const PdfPageItem = ({
|
||||
alt={`page ${pageIndex + 1} of ${fileName}`}
|
||||
/>
|
||||
{currentUserMarks.map((m, i) => (
|
||||
<div key={i} ref={(el) => (markRefs.current[m.id] = el)}>
|
||||
<PdfMarkItem
|
||||
key={i}
|
||||
ref={(el) => (markRefs.current[m.id] = el)}
|
||||
handleMarkClick={handleMarkClick}
|
||||
selectedMarkValue={selectedMarkValue}
|
||||
userMark={m}
|
||||
selectedMark={selectedMark}
|
||||
pageWidth={page.width}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
{otherUserMarks.map((m, i) => {
|
||||
return (
|
||||
|
@ -1,20 +1,13 @@
|
||||
import {
|
||||
Button,
|
||||
FormHelperText,
|
||||
ListItemIcon,
|
||||
ListItemText,
|
||||
MenuItem,
|
||||
Select,
|
||||
TextField,
|
||||
Tooltip
|
||||
} from '@mui/material'
|
||||
import styles from './style.module.scss'
|
||||
import { Button, FormHelperText, TextField, Tooltip } from '@mui/material'
|
||||
import type { Identifier, XYCoord } from 'dnd-core'
|
||||
import saveAs from 'file-saver'
|
||||
import JSZip from 'jszip'
|
||||
import { Event, kinds } from 'nostr-tools'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
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 { useLocation, useNavigate } from 'react-router-dom'
|
||||
import { toast } from 'react-toastify'
|
||||
@ -49,7 +42,6 @@ import {
|
||||
uploadToFileStorage
|
||||
} from '../../utils'
|
||||
import { Container } from '../../components/Container'
|
||||
import styles from './style.module.scss'
|
||||
import fileListStyles from '../../components/FileList/style.module.scss'
|
||||
import { DrawTool, MarkType } from '../../types/drawing'
|
||||
import { DrawPDFFields } from '../../components/DrawPDFFields'
|
||||
@ -874,21 +866,12 @@ export const CreatePage = () => {
|
||||
<div className={styles.flexWrap}>
|
||||
<div className={styles.inputWrapper}>
|
||||
<TextField
|
||||
fullWidth
|
||||
placeholder="Title"
|
||||
size="small"
|
||||
type="text"
|
||||
value={title}
|
||||
onChange={(e) => setTitle(e.target.value)}
|
||||
sx={{
|
||||
width: '100%',
|
||||
fontSize: '16px',
|
||||
'& .MuiInputBase-input': {
|
||||
padding: '7px 14px'
|
||||
},
|
||||
'& .MuiOutlinedInput-notchedOutline': {
|
||||
display: 'none'
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<ol className={`${styles.paperGroup} ${styles.orderedFilesList}`}>
|
||||
@ -907,9 +890,6 @@ export const CreatePage = () => {
|
||||
aria-label={`delete ${file.name}`}
|
||||
variant="text"
|
||||
onClick={(event) => handleRemoveFile(event, file)}
|
||||
sx={{
|
||||
minWidth: '44px'
|
||||
}}
|
||||
>
|
||||
<FontAwesomeIcon icon={faTrash} />
|
||||
</Button>
|
||||
@ -932,76 +912,6 @@ export const CreatePage = () => {
|
||||
}
|
||||
right={
|
||||
<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}`}>
|
||||
<DisplayUser
|
||||
metadata={metadata}
|
||||
@ -1011,7 +921,43 @@ export const CreatePage = () => {
|
||||
moveSigner={moveSigner}
|
||||
/>
|
||||
</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">
|
||||
Publish
|
||||
</Button>
|
||||
@ -1032,11 +978,7 @@ export const CreatePage = () => {
|
||||
{drawTool.active ? (
|
||||
<FontAwesomeIcon fontSize={'15px'} icon={faEllipsis} />
|
||||
) : (
|
||||
<span
|
||||
style={{
|
||||
fontSize: '10px'
|
||||
}}
|
||||
>
|
||||
<span className={styles.comingSoonPlaceholder}>
|
||||
Coming soon
|
||||
</span>
|
||||
)}
|
||||
@ -1084,12 +1026,12 @@ const DisplayUser = ({
|
||||
}: DisplayUsersProps) => {
|
||||
return (
|
||||
<>
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
<DndProvider backend={MultiBackend} options={HTML5toTouch}>
|
||||
{users
|
||||
.filter((user) => user.role === UserRole.signer)
|
||||
.map((user, index) => (
|
||||
<SignerRow
|
||||
key={`signer-${index}`}
|
||||
<SignerCounterpart
|
||||
key={`signer-${user.pubkey}`}
|
||||
userMeta={metadata[user.pubkey]}
|
||||
user={user}
|
||||
index={index}
|
||||
@ -1101,72 +1043,16 @@ const DisplayUser = ({
|
||||
</DndProvider>
|
||||
{users
|
||||
.filter((user) => user.role === UserRole.viewer)
|
||||
.map((user, index) => {
|
||||
const userMeta = metadata[user.pubkey]
|
||||
.map((user) => {
|
||||
return (
|
||||
<div className={styles.user} key={index}>
|
||||
<div className={styles.avatar}>
|
||||
<UserAvatar
|
||||
pubkey={user.pubkey}
|
||||
name={
|
||||
userMeta?.display_name ||
|
||||
userMeta?.name ||
|
||||
shorten(hexToNpub(user.pubkey))
|
||||
}
|
||||
image={userMeta?.picture}
|
||||
<div className={styles.user} key={`viewer-${user.pubkey}`}>
|
||||
<Counterpart
|
||||
userMeta={metadata[user.pubkey]}
|
||||
user={user}
|
||||
handleUserRoleChange={handleUserRoleChange}
|
||||
handleRemoveUser={handleRemoveUser}
|
||||
/>
|
||||
</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 SignerRowProps = {
|
||||
type CounterpartProps = {
|
||||
userMeta: ProfileMetadata
|
||||
user: User
|
||||
index: number
|
||||
moveSigner: (dragIndex: number, hoverIndex: number) => void
|
||||
handleUserRoleChange: (role: UserRole, pubkey: string) => void
|
||||
handleRemoveUser: (pubkey: string) => void
|
||||
}
|
||||
|
||||
const SignerRow = ({
|
||||
type SignerCounterpartProps = CounterpartProps & {
|
||||
index: number
|
||||
moveSigner: (dragIndex: number, hoverIndex: number) => void
|
||||
}
|
||||
|
||||
const SignerCounterpart = ({
|
||||
userMeta,
|
||||
user,
|
||||
index,
|
||||
moveSigner,
|
||||
handleUserRoleChange,
|
||||
handleRemoveUser
|
||||
}: SignerRowProps) => {
|
||||
}: SignerCounterpartProps) => {
|
||||
const ref = useRef<HTMLTableRowElement>(null)
|
||||
|
||||
const [{ handlerId }, drop] = useDrop<
|
||||
@ -1269,7 +1158,7 @@ const SignerRow = ({
|
||||
})
|
||||
})
|
||||
|
||||
const opacity = isDragging ? 0 : 1
|
||||
const opacity = isDragging ? 0.2 : 1
|
||||
drag(drop(ref))
|
||||
|
||||
return (
|
||||
@ -1280,6 +1169,24 @@ const SignerRow = ({
|
||||
ref={ref}
|
||||
>
|
||||
<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}>
|
||||
<UserAvatar
|
||||
pubkey={user.pubkey}
|
||||
@ -1291,56 +1198,31 @@ const SignerRow = ({
|
||||
image={userMeta?.picture}
|
||||
/>
|
||||
</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>
|
||||
<Tooltip title="Toggle User Role" arrow disableInteractive>
|
||||
<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>
|
||||
onClick={() =>
|
||||
handleUserRoleChange(
|
||||
user.role === UserRole.signer ? UserRole.viewer : UserRole.signer,
|
||||
user.pubkey
|
||||
)
|
||||
}
|
||||
className={styles.counterpartRowToggleButton}
|
||||
data-variant="primary"
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={user.role === UserRole.signer ? faPen : faEye}
|
||||
/>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip title="Remove User" arrow disableInteractive>
|
||||
<Button
|
||||
onClick={() => handleRemoveUser(user.pubkey)}
|
||||
className={styles.counterpartRowToggleButton}
|
||||
data-variant="secondary"
|
||||
>
|
||||
<FontAwesomeIcon icon={faTrash} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -42,6 +42,7 @@
|
||||
}
|
||||
|
||||
button {
|
||||
min-width: 44px;
|
||||
color: $primary-main;
|
||||
}
|
||||
|
||||
@ -78,7 +79,7 @@
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
|
||||
height: 34px;
|
||||
height: 36px;
|
||||
overflow: hidden;
|
||||
border-radius: 4px;
|
||||
outline: solid 1px #dddddd;
|
||||
@ -89,11 +90,43 @@
|
||||
&:focus-within {
|
||||
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 {
|
||||
flex-shrink: 0;
|
||||
max-height: 33vh;
|
||||
|
||||
.counterpartToggleButton {
|
||||
min-width: 44px;
|
||||
padding: 11px 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.user {
|
||||
@ -108,6 +141,22 @@
|
||||
a:hover {
|
||||
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 {
|
||||
@ -187,3 +236,7 @@
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
.comingSoonPlaceholder {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
@ -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
|
||||
* @param str string to shorten
|
||||
* @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
|
||||
if (str.length < offset * 2 + 4) return str
|
||||
|
||||
return `${str.slice(0, offset)}...${str.slice(
|
||||
str.length - offset,
|
||||
str.length
|
||||
)}`
|
||||
return `${str.slice(0, offset)}…${str.slice(str.length - offset, str.length)}`
|
||||
}
|
||||
|
||||
export const stringToHex = (str: string) => {
|
||||
|
Loading…
Reference in New Issue
Block a user