Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
canifa_note
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Vũ Hoàng Anh
canifa_note
Commits
a6e8ba7f
Unverified
Commit
a6e8ba7f
authored
Jan 03, 2026
by
Johnny
Committed by
GitHub
Jan 03, 2026
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
refactor: consolidate MemoEditor components (#5409)
parent
a630b70b
Changes
7
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
211 additions
and
185 deletions
+211
-185
AttachmentList.tsx
web/src/components/MemoEditor/components/AttachmentList.tsx
+82
-23
AttachmentListV2.tsx
...src/components/MemoEditor/components/AttachmentListV2.tsx
+0
-81
EditorMetadata.tsx
web/src/components/MemoEditor/components/EditorMetadata.tsx
+6
-9
LocationDisplay.tsx
web/src/components/MemoEditor/components/LocationDisplay.tsx
+3
-3
RelationItemCard.tsx
...src/components/MemoEditor/components/RelationItemCard.tsx
+0
-64
RelationList.tsx
web/src/components/MemoEditor/components/RelationList.tsx
+117
-0
index.ts
web/src/components/MemoEditor/components/index.ts
+3
-5
No files found.
web/src/components/MemoEditor/components/Attachment
ItemCard
.tsx
→
web/src/components/MemoEditor/components/Attachment
List
.tsx
View file @
a6e8ba7f
import
{
ChevronDownIcon
,
ChevronUpIcon
,
FileIcon
,
Loader2Icon
,
XIcon
}
from
"lucide-react"
;
import
{
ChevronDownIcon
,
ChevronUpIcon
,
FileIcon
,
Loader2Icon
,
PaperclipIcon
,
XIcon
}
from
"lucide-react"
;
import
type
{
FC
}
from
"react"
;
import
type
{
FC
}
from
"react"
;
import
type
{
AttachmentItem
}
from
"@/components/memo-metadata/types"
;
import
type
{
LocalFile
}
from
"@/components/memo-metadata/types"
;
import
{
toAttachmentItems
}
from
"@/components/memo-metadata/types"
;
import
{
cn
}
from
"@/lib/utils"
;
import
{
cn
}
from
"@/lib/utils"
;
import
type
{
Attachment
}
from
"@/types/proto/api/v1/attachment_service_pb"
;
import
{
formatFileSize
,
getFileTypeLabel
}
from
"@/utils/format"
;
import
{
formatFileSize
,
getFileTypeLabel
}
from
"@/utils/format"
;
interface
AttachmentItemCardProps
{
interface
AttachmentListProps
{
item
:
AttachmentItem
;
attachments
:
Attachment
[];
localFiles
?:
LocalFile
[];
onAttachmentsChange
?:
(
attachments
:
Attachment
[])
=>
void
;
onRemoveLocalFile
?:
(
previewUrl
:
string
)
=>
void
;
}
const
AttachmentItemCard
:
FC
<
{
item
:
ReturnType
<
typeof
toAttachmentItems
>
[
0
];
onRemove
?:
()
=>
void
;
onRemove
?:
()
=>
void
;
onMoveUp
?:
()
=>
void
;
onMoveUp
?:
()
=>
void
;
onMoveDown
?:
()
=>
void
;
onMoveDown
?:
()
=>
void
;
canMoveUp
?:
boolean
;
canMoveUp
?:
boolean
;
canMoveDown
?:
boolean
;
canMoveDown
?:
boolean
;
className
?:
string
;
}
>
=
({
item
,
onRemove
,
onMoveUp
,
onMoveDown
,
canMoveUp
=
true
,
canMoveDown
=
true
})
=>
{
}
const
AttachmentItemCard
:
FC
<
AttachmentItemCardProps
>
=
({
item
,
onRemove
,
onMoveUp
,
onMoveDown
,
canMoveUp
=
true
,
canMoveDown
=
true
,
className
,
})
=>
{
const
{
category
,
filename
,
thumbnailUrl
,
mimeType
,
size
,
isLocal
}
=
item
;
const
{
category
,
filename
,
thumbnailUrl
,
mimeType
,
size
,
isLocal
}
=
item
;
const
fileTypeLabel
=
getFileTypeLabel
(
mimeType
);
const
fileTypeLabel
=
getFileTypeLabel
(
mimeType
);
const
fileSizeLabel
=
size
?
formatFileSize
(
size
)
:
undefined
;
const
fileSizeLabel
=
size
?
formatFileSize
(
size
)
:
undefined
;
return
(
return
(
<
div
<
div
className=
"relative flex items-center gap-1.5 px-1.5 py-1 rounded border border-transparent hover:border-border hover:bg-accent/20 transition-all"
>
className=
{
cn
(
"relative flex items-center gap-1.5 px-1.5 py-1 rounded border border-transparent hover:border-border hover:bg-accent/20 transition-all"
,
className
,
)
}
>
<
div
className=
"flex-shrink-0 w-6 h-6 rounded overflow-hidden bg-muted/40 flex items-center justify-center"
>
<
div
className=
"flex-shrink-0 w-6 h-6 rounded overflow-hidden bg-muted/40 flex items-center justify-center"
>
{
category
===
"image"
&&
thumbnailUrl
?
(
{
category
===
"image"
&&
thumbnailUrl
?
(
<
img
src=
{
thumbnailUrl
}
alt=
""
className=
"w-full h-full object-cover"
/>
<
img
src=
{
thumbnailUrl
}
alt=
""
className=
"w-full h-full object-cover"
/>
...
@@ -113,4 +106,70 @@ const AttachmentItemCard: FC<AttachmentItemCardProps> = ({
...
@@ -113,4 +106,70 @@ const AttachmentItemCard: FC<AttachmentItemCardProps> = ({
);
);
};
};
export
default
AttachmentItemCard
;
const
AttachmentList
:
FC
<
AttachmentListProps
>
=
({
attachments
,
localFiles
=
[],
onAttachmentsChange
,
onRemoveLocalFile
})
=>
{
if
(
attachments
.
length
===
0
&&
localFiles
.
length
===
0
)
{
return
null
;
}
const
items
=
toAttachmentItems
(
attachments
,
localFiles
);
const
handleMoveUp
=
(
index
:
number
)
=>
{
if
(
index
===
0
||
!
onAttachmentsChange
)
return
;
const
newAttachments
=
[...
attachments
];
[
newAttachments
[
index
-
1
],
newAttachments
[
index
]]
=
[
newAttachments
[
index
],
newAttachments
[
index
-
1
]];
onAttachmentsChange
(
newAttachments
);
};
const
handleMoveDown
=
(
index
:
number
)
=>
{
if
(
index
===
attachments
.
length
-
1
||
!
onAttachmentsChange
)
return
;
const
newAttachments
=
[...
attachments
];
[
newAttachments
[
index
],
newAttachments
[
index
+
1
]]
=
[
newAttachments
[
index
+
1
],
newAttachments
[
index
]];
onAttachmentsChange
(
newAttachments
);
};
const
handleRemoveAttachment
=
(
name
:
string
)
=>
{
if
(
onAttachmentsChange
)
{
onAttachmentsChange
(
attachments
.
filter
((
attachment
)
=>
attachment
.
name
!==
name
));
}
};
const
handleRemoveItem
=
(
item
:
(
typeof
items
)[
0
])
=>
{
if
(
item
.
isLocal
)
{
onRemoveLocalFile
?.(
item
.
id
);
}
else
{
handleRemoveAttachment
(
item
.
id
);
}
};
return
(
<
div
className=
"w-full rounded-lg border border-border bg-muted/20 overflow-hidden"
>
<
div
className=
"flex items-center gap-1.5 px-2 py-1.5 border-b border-border bg-muted/30"
>
<
PaperclipIcon
className=
"w-3.5 h-3.5 text-muted-foreground"
/>
<
span
className=
"text-xs font-medium text-muted-foreground"
>
Attachments (
{
items
.
length
}
)
</
span
>
</
div
>
<
div
className=
"p-1 sm:p-1.5 flex flex-col gap-0.5"
>
{
items
.
map
((
item
)
=>
{
const
isLocalFile
=
item
.
isLocal
;
const
attachmentIndex
=
isLocalFile
?
-
1
:
attachments
.
findIndex
((
a
)
=>
a
.
name
===
item
.
id
);
return
(
<
AttachmentItemCard
key=
{
item
.
id
}
item=
{
item
}
onRemove=
{
()
=>
handleRemoveItem
(
item
)
}
onMoveUp=
{
!
isLocalFile
?
()
=>
handleMoveUp
(
attachmentIndex
)
:
undefined
}
onMoveDown=
{
!
isLocalFile
?
()
=>
handleMoveDown
(
attachmentIndex
)
:
undefined
}
canMoveUp=
{
!
isLocalFile
&&
attachmentIndex
>
0
}
canMoveDown=
{
!
isLocalFile
&&
attachmentIndex
<
attachments
.
length
-
1
}
/>
);
})
}
</
div
>
</
div
>
);
};
export
default
AttachmentList
;
web/src/components/MemoEditor/components/AttachmentListV2.tsx
deleted
100644 → 0
View file @
a630b70b
import
{
PaperclipIcon
}
from
"lucide-react"
;
import
type
{
FC
}
from
"react"
;
import
type
{
LocalFile
}
from
"@/components/memo-metadata/types"
;
import
{
toAttachmentItems
}
from
"@/components/memo-metadata/types"
;
import
type
{
Attachment
}
from
"@/types/proto/api/v1/attachment_service_pb"
;
import
AttachmentItemCard
from
"./AttachmentItemCard"
;
interface
AttachmentListV2Props
{
attachments
:
Attachment
[];
localFiles
?:
LocalFile
[];
onAttachmentsChange
?:
(
attachments
:
Attachment
[])
=>
void
;
onRemoveLocalFile
?:
(
previewUrl
:
string
)
=>
void
;
}
const
AttachmentListV2
:
FC
<
AttachmentListV2Props
>
=
({
attachments
,
localFiles
=
[],
onAttachmentsChange
,
onRemoveLocalFile
})
=>
{
if
(
attachments
.
length
===
0
&&
localFiles
.
length
===
0
)
{
return
null
;
}
const
items
=
toAttachmentItems
(
attachments
,
localFiles
);
const
handleMoveUp
=
(
index
:
number
)
=>
{
if
(
index
===
0
||
!
onAttachmentsChange
)
return
;
const
newAttachments
=
[...
attachments
];
[
newAttachments
[
index
-
1
],
newAttachments
[
index
]]
=
[
newAttachments
[
index
],
newAttachments
[
index
-
1
]];
onAttachmentsChange
(
newAttachments
);
};
const
handleMoveDown
=
(
index
:
number
)
=>
{
if
(
index
===
attachments
.
length
-
1
||
!
onAttachmentsChange
)
return
;
const
newAttachments
=
[...
attachments
];
[
newAttachments
[
index
],
newAttachments
[
index
+
1
]]
=
[
newAttachments
[
index
+
1
],
newAttachments
[
index
]];
onAttachmentsChange
(
newAttachments
);
};
const
handleRemoveAttachment
=
(
name
:
string
)
=>
{
if
(
onAttachmentsChange
)
{
onAttachmentsChange
(
attachments
.
filter
((
attachment
)
=>
attachment
.
name
!==
name
));
}
};
const
handleRemoveItem
=
(
item
:
(
typeof
items
)[
0
])
=>
{
if
(
item
.
isLocal
)
{
onRemoveLocalFile
?.(
item
.
id
);
}
else
{
handleRemoveAttachment
(
item
.
id
);
}
};
return
(
<
div
className=
"w-full rounded-lg border border-border bg-muted/20 overflow-hidden"
>
<
div
className=
"flex items-center gap-1.5 px-2 py-1.5 border-b border-border bg-muted/30"
>
<
PaperclipIcon
className=
"w-3.5 h-3.5 text-muted-foreground"
/>
<
span
className=
"text-xs font-medium text-muted-foreground"
>
Attachments (
{
items
.
length
}
)
</
span
>
</
div
>
<
div
className=
"p-1 sm:p-1.5 flex flex-col gap-0.5"
>
{
items
.
map
((
item
)
=>
{
const
isLocalFile
=
item
.
isLocal
;
const
attachmentIndex
=
isLocalFile
?
-
1
:
attachments
.
findIndex
((
a
)
=>
a
.
name
===
item
.
id
);
return
(
<
AttachmentItemCard
key=
{
item
.
id
}
item=
{
item
}
onRemove=
{
()
=>
handleRemoveItem
(
item
)
}
onMoveUp=
{
!
isLocalFile
?
()
=>
handleMoveUp
(
attachmentIndex
)
:
undefined
}
onMoveDown=
{
!
isLocalFile
?
()
=>
handleMoveDown
(
attachmentIndex
)
:
undefined
}
canMoveUp=
{
!
isLocalFile
&&
attachmentIndex
>
0
}
canMoveDown=
{
!
isLocalFile
&&
attachmentIndex
<
attachments
.
length
-
1
}
/>
);
})
}
</
div
>
</
div
>
);
};
export
default
AttachmentListV2
;
web/src/components/MemoEditor/components/EditorMetadata.tsx
View file @
a6e8ba7f
import
type
{
FC
}
from
"react"
;
import
type
{
FC
}
from
"react"
;
import
{
useEditorContext
}
from
"../state"
;
import
{
useEditorContext
}
from
"../state"
;
import
type
{
EditorMetadataProps
}
from
"../types"
;
import
type
{
EditorMetadataProps
}
from
"../types"
;
import
AttachmentList
V2
from
"./AttachmentListV2
"
;
import
AttachmentList
from
"./AttachmentList
"
;
import
LocationDisplay
V2
from
"./LocationDisplayV2
"
;
import
LocationDisplay
from
"./LocationDisplay
"
;
import
RelationList
V2
from
"./RelationListV2
"
;
import
RelationList
from
"./RelationList
"
;
export
const
EditorMetadata
:
FC
<
EditorMetadataProps
>
=
()
=>
{
export
const
EditorMetadata
:
FC
<
EditorMetadataProps
>
=
()
=>
{
const
{
state
,
actions
,
dispatch
}
=
useEditorContext
();
const
{
state
,
actions
,
dispatch
}
=
useEditorContext
();
return
(
return
(
<
div
className=
"w-full flex flex-col gap-2"
>
<
div
className=
"w-full flex flex-col gap-2"
>
<
AttachmentList
V2
<
AttachmentList
attachments=
{
state
.
metadata
.
attachments
}
attachments=
{
state
.
metadata
.
attachments
}
localFiles=
{
state
.
localFiles
}
localFiles=
{
state
.
localFiles
}
onAttachmentsChange=
{
(
attachments
)
=>
dispatch
(
actions
.
setMetadata
({
attachments
}))
}
onAttachmentsChange=
{
(
attachments
)
=>
dispatch
(
actions
.
setMetadata
({
attachments
}))
}
onRemoveLocalFile=
{
(
previewUrl
)
=>
dispatch
(
actions
.
removeLocalFile
(
previewUrl
))
}
onRemoveLocalFile=
{
(
previewUrl
)
=>
dispatch
(
actions
.
removeLocalFile
(
previewUrl
))
}
/>
/>
<
RelationListV2
<
RelationList
relations=
{
state
.
metadata
.
relations
}
onRelationsChange=
{
(
relations
)
=>
dispatch
(
actions
.
setMetadata
({
relations
}))
}
/>
relations=
{
state
.
metadata
.
relations
}
onRelationsChange=
{
(
relations
)
=>
dispatch
(
actions
.
setMetadata
({
relations
}))
}
/>
{
state
.
metadata
.
location
&&
(
{
state
.
metadata
.
location
&&
(
<
LocationDisplay
V2
location=
{
state
.
metadata
.
location
}
onRemove=
{
()
=>
dispatch
(
actions
.
setMetadata
({
location
:
undefined
}))
}
/>
<
LocationDisplay
location=
{
state
.
metadata
.
location
}
onRemove=
{
()
=>
dispatch
(
actions
.
setMetadata
({
location
:
undefined
}))
}
/>
)
}
)
}
</
div
>
</
div
>
);
);
...
...
web/src/components/MemoEditor/components/LocationDisplay
V2
.tsx
→
web/src/components/MemoEditor/components/LocationDisplay.tsx
View file @
a6e8ba7f
...
@@ -3,13 +3,13 @@ import type { FC } from "react";
...
@@ -3,13 +3,13 @@ import type { FC } from "react";
import
{
cn
}
from
"@/lib/utils"
;
import
{
cn
}
from
"@/lib/utils"
;
import
type
{
Location
}
from
"@/types/proto/api/v1/memo_service_pb"
;
import
type
{
Location
}
from
"@/types/proto/api/v1/memo_service_pb"
;
interface
LocationDisplay
V2
Props
{
interface
LocationDisplayProps
{
location
:
Location
;
location
:
Location
;
onRemove
?:
()
=>
void
;
onRemove
?:
()
=>
void
;
className
?:
string
;
className
?:
string
;
}
}
const
LocationDisplay
V2
:
FC
<
LocationDisplayV2
Props
>
=
({
location
,
onRemove
,
className
})
=>
{
const
LocationDisplay
:
FC
<
LocationDisplay
Props
>
=
({
location
,
onRemove
,
className
})
=>
{
const
displayText
=
location
.
placeholder
||
`
${
location
.
latitude
.
toFixed
(
6
)}
,
${
location
.
longitude
.
toFixed
(
6
)}
`
;
const
displayText
=
location
.
placeholder
||
`
${
location
.
latitude
.
toFixed
(
6
)}
,
${
location
.
longitude
.
toFixed
(
6
)}
`
;
return
(
return
(
...
@@ -45,4 +45,4 @@ const LocationDisplayV2: FC<LocationDisplayV2Props> = ({ location, onRemove, cla
...
@@ -45,4 +45,4 @@ const LocationDisplayV2: FC<LocationDisplayV2Props> = ({ location, onRemove, cla
);
);
};
};
export
default
LocationDisplay
V2
;
export
default
LocationDisplay
;
web/src/components/MemoEditor/components/RelationItemCard.tsx
deleted
100644 → 0
View file @
a630b70b
import
{
LinkIcon
,
XIcon
}
from
"lucide-react"
;
import
type
{
FC
}
from
"react"
;
import
{
Link
}
from
"react-router-dom"
;
import
{
extractMemoIdFromName
}
from
"@/helpers/resource-names"
;
import
{
cn
}
from
"@/lib/utils"
;
import
type
{
MemoRelation_Memo
}
from
"@/types/proto/api/v1/memo_service_pb"
;
interface
RelationItemCardProps
{
memo
:
MemoRelation_Memo
;
onRemove
?:
()
=>
void
;
parentPage
?:
string
;
className
?:
string
;
}
const
RelationItemCard
:
FC
<
RelationItemCardProps
>
=
({
memo
,
onRemove
,
parentPage
,
className
})
=>
{
const
memoId
=
extractMemoIdFromName
(
memo
.
name
);
if
(
onRemove
)
{
return
(
<
div
className=
{
cn
(
"relative flex items-center gap-1.5 px-1.5 py-1 rounded border border-transparent hover:border-border hover:bg-accent/20 transition-all"
,
className
,
)
}
>
<
LinkIcon
className=
"w-3.5 h-3.5 shrink-0 text-muted-foreground"
/>
<
span
className=
"text-xs font-medium truncate flex-1"
title=
{
memo
.
snippet
}
>
{
memo
.
snippet
}
</
span
>
<
div
className=
"flex-shrink-0 flex items-center gap-0.5"
>
<
button
type=
"button"
onClick=
{
onRemove
}
className=
"p-0.5 rounded hover:bg-destructive/10 active:bg-destructive/10 transition-colors touch-manipulation"
title=
"Remove"
aria
-
label=
"Remove relation"
>
<
XIcon
className=
"w-3 h-3 text-muted-foreground hover:text-destructive"
/>
</
button
>
</
div
>
</
div
>
);
}
return
(
<
Link
className=
{
cn
(
"relative flex items-center gap-1.5 px-1.5 py-1 rounded border border-transparent hover:border-border hover:bg-accent/20 transition-all"
,
className
,
)
}
to=
{
`/${memo.name}`
}
viewTransition
state=
{
{
from
:
parentPage
}
}
>
<
span
className=
"text-[10px] font-mono px-1 py-0.5 rounded bg-muted/50 text-muted-foreground shrink-0"
>
{
memoId
.
slice
(
0
,
6
)
}
</
span
>
<
span
className=
"text-xs truncate flex-1"
title=
{
memo
.
snippet
}
>
{
memo
.
snippet
}
</
span
>
</
Link
>
);
};
export
default
RelationItemCard
;
web/src/components/MemoEditor/components/RelationList
V2
.tsx
→
web/src/components/MemoEditor/components/RelationList.tsx
View file @
a6e8ba7f
import
{
create
}
from
"@bufbuild/protobuf"
;
import
{
create
}
from
"@bufbuild/protobuf"
;
import
{
LinkIcon
}
from
"lucide-react"
;
import
{
LinkIcon
,
XIcon
}
from
"lucide-react"
;
import
type
{
FC
}
from
"react"
;
import
type
{
FC
}
from
"react"
;
import
{
useEffect
,
useState
}
from
"react"
;
import
{
useEffect
,
useState
}
from
"react"
;
import
{
Link
}
from
"react-router-dom"
;
import
{
memoServiceClient
}
from
"@/connect"
;
import
{
memoServiceClient
}
from
"@/connect"
;
import
{
extractMemoIdFromName
}
from
"@/helpers/resource-names"
;
import
{
cn
}
from
"@/lib/utils"
;
import
type
{
Memo
,
MemoRelation
}
from
"@/types/proto/api/v1/memo_service_pb"
;
import
type
{
Memo
,
MemoRelation
}
from
"@/types/proto/api/v1/memo_service_pb"
;
import
{
MemoRelation_MemoSchema
}
from
"@/types/proto/api/v1/memo_service_pb"
;
import
{
MemoRelation_MemoSchema
}
from
"@/types/proto/api/v1/memo_service_pb"
;
import
RelationItemCard
from
"./RelationItemCard"
;
interface
RelationList
V2
Props
{
interface
RelationListProps
{
relations
:
MemoRelation
[];
relations
:
MemoRelation
[];
onRelationsChange
?:
(
relations
:
MemoRelation
[])
=>
void
;
onRelationsChange
?:
(
relations
:
MemoRelation
[])
=>
void
;
parentPage
?:
string
;
}
}
const
RelationListV2
:
FC
<
RelationListV2Props
>
=
({
relations
,
onRelationsChange
})
=>
{
const
RelationItemCard
:
FC
<
{
memo
:
MemoRelation
[
"relatedMemo"
];
onRemove
?:
()
=>
void
;
parentPage
?:
string
;
}
>
=
({
memo
,
onRemove
,
parentPage
})
=>
{
const
memoId
=
extractMemoIdFromName
(
memo
!
.
name
);
if
(
onRemove
)
{
return
(
<
div
className=
{
cn
(
"relative flex items-center gap-1.5 px-1.5 py-1 rounded border border-transparent hover:border-border hover:bg-accent/20 transition-all"
,
)
}
>
<
LinkIcon
className=
"w-3.5 h-3.5 shrink-0 text-muted-foreground"
/>
<
span
className=
"text-xs font-medium truncate flex-1"
title=
{
memo
!
.
snippet
}
>
{
memo
!
.
snippet
}
</
span
>
<
div
className=
"flex-shrink-0 flex items-center gap-0.5"
>
<
button
type=
"button"
onClick=
{
onRemove
}
className=
"p-0.5 rounded hover:bg-destructive/10 active:bg-destructive/10 transition-colors touch-manipulation"
title=
"Remove"
aria
-
label=
"Remove relation"
>
<
XIcon
className=
"w-3 h-3 text-muted-foreground hover:text-destructive"
/>
</
button
>
</
div
>
</
div
>
);
}
return
(
<
Link
className=
{
cn
(
"relative flex items-center gap-1.5 px-1.5 py-1 rounded border border-transparent hover:border-border hover:bg-accent/20 transition-all"
,
)
}
to=
{
`/${memo!.name}`
}
viewTransition
state=
{
{
from
:
parentPage
}
}
>
<
span
className=
"text-[10px] font-mono px-1 py-0.5 rounded bg-muted/50 text-muted-foreground shrink-0"
>
{
memoId
.
slice
(
0
,
6
)
}
</
span
>
<
span
className=
"text-xs truncate flex-1"
title=
{
memo
!
.
snippet
}
>
{
memo
!
.
snippet
}
</
span
>
</
Link
>
);
};
const
RelationList
:
FC
<
RelationListProps
>
=
({
relations
,
onRelationsChange
,
parentPage
})
=>
{
const
[
referencingMemos
,
setReferencingMemos
]
=
useState
<
Memo
[]
>
([]);
const
[
referencingMemos
,
setReferencingMemos
]
=
useState
<
Memo
[]
>
([]);
useEffect
(()
=>
{
useEffect
(()
=>
{
...
@@ -52,6 +106,7 @@ const RelationListV2: FC<RelationListV2Props> = ({ relations, onRelationsChange
...
@@ -52,6 +106,7 @@ const RelationListV2: FC<RelationListV2Props> = ({ relations, onRelationsChange
key=
{
memo
.
name
}
key=
{
memo
.
name
}
memo=
{
create
(
MemoRelation_MemoSchema
,
{
name
:
memo
.
name
,
snippet
:
memo
.
snippet
})
}
memo=
{
create
(
MemoRelation_MemoSchema
,
{
name
:
memo
.
name
,
snippet
:
memo
.
snippet
})
}
onRemove=
{
()
=>
handleDeleteRelation
(
memo
.
name
)
}
onRemove=
{
()
=>
handleDeleteRelation
(
memo
.
name
)
}
parentPage=
{
parentPage
}
/>
/>
))
}
))
}
</
div
>
</
div
>
...
@@ -59,4 +114,4 @@ const RelationListV2: FC<RelationListV2Props> = ({ relations, onRelationsChange
...
@@ -59,4 +114,4 @@ const RelationListV2: FC<RelationListV2Props> = ({ relations, onRelationsChange
);
);
};
};
export
default
RelationList
V2
;
export
default
RelationList
;
web/src/components/MemoEditor/components/index.ts
View file @
a6e8ba7f
// UI components for MemoEditor
// UI components for MemoEditor
export
{
default
as
AttachmentItemCard
}
from
"./AttachmentItemCard"
;
export
{
default
as
AttachmentList
}
from
"./AttachmentList"
;
export
{
default
as
AttachmentListV2
}
from
"./AttachmentListV2"
;
export
*
from
"./EditorContent"
;
export
*
from
"./EditorContent"
;
export
*
from
"./EditorMetadata"
;
export
*
from
"./EditorMetadata"
;
export
*
from
"./EditorToolbar"
;
export
*
from
"./EditorToolbar"
;
export
{
FocusModeExitButton
,
FocusModeOverlay
}
from
"./FocusModeOverlay"
;
export
{
FocusModeExitButton
,
FocusModeOverlay
}
from
"./FocusModeOverlay"
;
export
{
LinkMemoDialog
}
from
"./LinkMemoDialog"
;
export
{
LinkMemoDialog
}
from
"./LinkMemoDialog"
;
export
{
LocationDialog
}
from
"./LocationDialog"
;
export
{
LocationDialog
}
from
"./LocationDialog"
;
export
{
default
as
LocationDisplayV2
}
from
"./LocationDisplayV2"
;
export
{
default
as
LocationDisplay
}
from
"./LocationDisplay"
;
export
{
default
as
RelationItemCard
}
from
"./RelationItemCard"
;
export
{
default
as
RelationList
}
from
"./RelationList"
;
export
{
default
as
RelationListV2
}
from
"./RelationListV2"
;
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment