-
Notifications
You must be signed in to change notification settings - Fork 538
Improve attachments previews #154
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 13 commits
f6fed7a
e3a9fee
9fcb9df
2b71eee
0f46a91
cea17e5
42a0d9f
56576f1
e072af3
a585ac2
fc3665a
956c904
b67f1b5
dc28de1
9935157
6e08ef3
b84885a
90494c9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| import { useFieldValue, useRecordContext, useTranslate } from "ra-core"; | ||
| import type { FileFieldProps } from "@/components/admin"; | ||
| import { cn } from "@/lib/utils"; | ||
|
|
||
| /** | ||
| * Displays a preview for a single note attachment in the note edition form. | ||
| * | ||
| * This component is inspired by react-admin's `ImageField` and is intended for | ||
| * usage inside a `<FileInput>`, where the current attachment is provided through | ||
| * the record context. | ||
| * | ||
| * @param props - Field props provided by react-admin file inputs. | ||
| * @returns An image preview for image attachments, or a regular link for other files. | ||
| */ | ||
| export const AttachmentField = (props: FileFieldProps) => { | ||
| const { | ||
| className, | ||
| empty, | ||
| title, | ||
| target, | ||
| download, | ||
| defaultValue, | ||
| source, | ||
| record: _recordProp, | ||
| ...rest | ||
| } = props; | ||
| const record = useRecordContext(); | ||
| const sourceValue = useFieldValue({ defaultValue, source, record }); | ||
| const titleValue = | ||
| useFieldValue({ | ||
| ...props, | ||
| // @ts-expect-error We ignore here because title might be a custom label or undefined instead of a field name | ||
| source: title, | ||
| })?.toString() ?? title; | ||
| const translate = useTranslate(); | ||
|
|
||
| if (sourceValue == null) { | ||
| if (!empty) { | ||
| return null; | ||
| } | ||
|
|
||
| return ( | ||
| <div className={cn("inline-block", className)} {...rest}> | ||
| {typeof empty === "string" ? translate(empty, { _: empty }) : empty} | ||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| const type = record?.type ?? record?.rawFile?.type; | ||
| const srcValue = sourceValue.toString(); | ||
|
|
||
| return ( | ||
| <div className={cn("inline-block", className)} {...rest}> | ||
| {isImageMimeType(type) ? ( | ||
| <a | ||
| href={srcValue} | ||
| title={titleValue} | ||
| target={target} | ||
| download={download} | ||
| // useful to prevent click bubbling in a DataTable with rowClick | ||
| onClick={(e) => e.stopPropagation()} | ||
| > | ||
WiXSL marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| <img | ||
| alt={titleValue} | ||
| title={titleValue} | ||
| src={srcValue} | ||
| className="w-[200px] h-[100px] object-cover cursor-pointer object-left border border-border" | ||
| /> | ||
| </a> | ||
| ) : ( | ||
| <a | ||
| href={srcValue} | ||
| title={titleValue} | ||
| target={target} | ||
| download={download} | ||
| // useful to prevent click bubbling in a DataTable with rowClick | ||
| onClick={(e) => e.stopPropagation()} | ||
| > | ||
| {titleValue} | ||
| </a> | ||
| )} | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| /** | ||
| * Checks whether a mime type corresponds to an image. | ||
| * | ||
| * @param mimeType - The attachment mime type. | ||
| * @returns `true` when the mime type starts with `image/`. | ||
| */ | ||
| const isImageMimeType = (mimeType?: string): boolean => { | ||
| if (!mimeType) { | ||
| return false; | ||
| } | ||
| return mimeType.startsWith("image/"); | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,15 @@ import { Paperclip } from "lucide-react"; | |
|
|
||
| import type { AttachmentNote, ContactNote, DealNote } from "../types"; | ||
|
|
||
| /** | ||
| * Displays persisted note attachments in note show/list views. | ||
| * | ||
| * This component receives a full note record and renders all attachments. | ||
| * | ||
| * @param props - Component props. | ||
| * @param props.note - Note record containing attachments to render. | ||
| * @returns `null` when there are no attachments, otherwise attachment previews and links. | ||
| */ | ||
| export const NoteAttachments = ({ note }: { note: ContactNote | DealNote }) => { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now this component seems to overlap with AttachmentField. When should we use one or the other? It's not clear.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| if (!note.attachments || note.attachments.length === 0) { | ||
| return null; | ||
|
|
@@ -20,15 +29,19 @@ export const NoteAttachments = ({ note }: { note: ContactNote | DealNote }) => { | |
| <div className="grid grid-cols-4 gap-8"> | ||
| {imageAttachments.map((attachment: AttachmentNote, index: number) => ( | ||
| <div key={index}> | ||
| <img | ||
| src={attachment.src} | ||
| alt={attachment.title} | ||
| className="w-[200px] h-[100px] object-cover cursor-pointer object-left border border-border" | ||
| onClick={(e) => { | ||
| e.stopPropagation(); | ||
| window.open(attachment.src, "_blank"); | ||
| }} | ||
| /> | ||
| <a | ||
| href={attachment.src} | ||
| target="_blank" | ||
| rel="noopener noreferrer" | ||
| className="block" | ||
| onClick={(e) => e.stopPropagation()} | ||
| > | ||
WiXSL marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| <img | ||
| src={attachment.src} | ||
| alt={attachment.title} | ||
| className="w-[200px] h-[100px] object-cover cursor-pointer object-left border border-border" | ||
| /> | ||
WiXSL marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| </a> | ||
| </div> | ||
| ))} | ||
| </div> | ||
|
|
@@ -52,6 +65,12 @@ export const NoteAttachments = ({ note }: { note: ContactNote | DealNote }) => { | |
| ); | ||
| }; | ||
|
|
||
| /** | ||
| * Checks whether a mime type corresponds to an image. | ||
| * | ||
| * @param mimeType - The attachment mime type. | ||
| * @returns `true` when the mime type starts with `image/`. | ||
| */ | ||
| const isImageMimeType = (mimeType?: string): boolean => { | ||
| if (!mimeType) { | ||
| return false; | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.