-
Notifications
You must be signed in to change notification settings - Fork 1
Inline media block #968
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
Merged
Merged
Inline media block #968
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
5835212
Add InlineMedia inline block with position, size, and alignment options
busbyk 170000e
Rework InlineMedia sizing to percentage widths, fixed height, and vis…
busbyk dd16847
removing as const
busbyk 47ad611
Merge branch 'main' into inline-media-block
busbyk 1903e72
fixing types
busbyk 1c36201
Add InlineMediaBlock to all lexical editors via DEFAULT_INLINE_BLOCKS
busbyk ac35d98
Add thumbnail to InlineMediaBlock config
busbyk 1827a03
Merge branch 'main' into inline-media-block
busbyk dfe299f
Fix circular import by moving DEFAULT_INLINE_BLOCKS to its own file
busbyk 61ba101
Update position labels to 'Left'/'Right' and simplify description
busbyk 4b352f3
Add pictureClassName="my-0" to fixed-height Media path for consistency
busbyk dd99700
updated types
busbyk c2f967c
Merge branch 'main' into inline-media-block
busbyk File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| import type { InlineMediaBlock } from '@/payload-types' | ||
|
|
||
| import { Media } from '@/components/Media' | ||
| import { cn } from '@/utilities/ui' | ||
|
|
||
| type Props = Omit<InlineMediaBlock, 'blockType' | 'id'> | ||
|
|
||
| const widthClasses = { | ||
| '25': 'w-1/4', | ||
| '50': 'w-1/2', | ||
| '75': 'w-3/4', | ||
| '100': 'w-full', | ||
| } | ||
|
|
||
| const verticalAlignClasses = { | ||
| top: 'align-top', | ||
| middle: 'align-middle', | ||
| bottom: 'align-bottom', | ||
| baseline: 'align-baseline', | ||
| } | ||
|
|
||
| type WidthSize = keyof typeof widthClasses | ||
|
|
||
| function isWidthSize(size: string): size is WidthSize { | ||
| return size in widthClasses | ||
| } | ||
|
|
||
| export const InlineMediaComponent = ({ | ||
| media, | ||
| position = 'inline', | ||
| verticalAlign = 'middle', | ||
| size = 'original', | ||
| fixedHeight, | ||
| caption, | ||
| }: Props) => { | ||
| if (!media || typeof media === 'number' || typeof media === 'string') { | ||
| return null | ||
| } | ||
|
|
||
| const isFloat = position === 'float-left' || position === 'float-right' | ||
| const resolvedSize = size ?? 'original' | ||
|
|
||
| let sizeClass = '' | ||
| let imgSizeClass = 'w-auto h-auto' | ||
| let sizes = '100vw' | ||
| const isFixedHeight = resolvedSize === 'fixed-height' && fixedHeight | ||
|
|
||
| if (resolvedSize === 'original') { | ||
| sizeClass = 'max-w-fit' | ||
| } else if (isWidthSize(resolvedSize)) { | ||
| sizeClass = widthClasses[resolvedSize] | ||
| imgSizeClass = 'w-full h-auto' | ||
| // Approximate sizes hint for responsive images | ||
| sizes = `${resolvedSize}vw` | ||
| } else if (isFixedHeight) { | ||
| imgSizeClass = 'h-full w-auto' | ||
| sizes = '96px' | ||
| } | ||
|
|
||
| const positionClasses = isFloat | ||
| ? cn(position === 'float-left' ? 'float-left mr-2' : 'float-right ml-2', 'mb-1') | ||
| : cn('inline-block', verticalAlignClasses[verticalAlign ?? 'middle']) | ||
|
|
||
| // For fixed height, wrap Media in a span with explicit height. | ||
| // Descendant selectors propagate height through Media's intermediate span and picture elements. | ||
| const mediaElement = isFixedHeight ? ( | ||
| <span | ||
| className="block [&>span]:h-full [&_picture]:h-full" | ||
| style={{ height: `${fixedHeight}px` }} | ||
|
rchlfryn marked this conversation as resolved.
|
||
| > | ||
| <Media | ||
| htmlElement="span" | ||
| resource={media} | ||
| imgClassName={imgSizeClass} | ||
| pictureClassName="my-0" | ||
| sizes={sizes} | ||
| /> | ||
| </span> | ||
| ) : ( | ||
| <Media | ||
| htmlElement="span" | ||
| resource={media} | ||
| imgClassName={imgSizeClass} | ||
| pictureClassName="my-0" | ||
| sizes={sizes} | ||
| /> | ||
| ) | ||
|
|
||
| return ( | ||
| <span className={cn(positionClasses, sizeClass)}> | ||
| {mediaElement} | ||
| {caption && <span className="block text-xs text-gray-500 mt-0.5">{caption}</span>} | ||
| </span> | ||
| ) | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| import type { Block } from 'payload' | ||
|
|
||
| export const InlineMediaBlock: Block = { | ||
| slug: 'inlineMedia', | ||
| interfaceName: 'InlineMediaBlock', | ||
| imageURL: '/thumbnail/MediaThumbnail.jpg', | ||
| fields: [ | ||
| { | ||
| name: 'media', | ||
| type: 'upload', | ||
| relationTo: 'media', | ||
| required: true, | ||
| }, | ||
| { | ||
| name: 'position', | ||
| type: 'select', | ||
| defaultValue: 'inline', | ||
| options: [ | ||
| { label: 'Inline', value: 'inline' }, | ||
| { label: 'Left', value: 'float-left' }, | ||
| { label: 'Right', value: 'float-right' }, | ||
| ], | ||
| admin: { | ||
| description: | ||
| 'Inline renders the image within the text flow. Left or Right positions the image to that side with text wrapping around it.', | ||
| }, | ||
| }, | ||
| { | ||
| name: 'verticalAlign', | ||
| type: 'select', | ||
| defaultValue: 'middle', | ||
| options: [ | ||
| { label: 'Middle', value: 'middle' }, | ||
| { label: 'Top', value: 'top' }, | ||
| { label: 'Bottom', value: 'bottom' }, | ||
| { label: 'Baseline', value: 'baseline' }, | ||
| ], | ||
| admin: { | ||
| description: 'Vertical alignment relative to the surrounding text.', | ||
| condition: (_, siblingData) => siblingData?.position === 'inline', | ||
| }, | ||
| }, | ||
| { | ||
| name: 'size', | ||
| type: 'select', | ||
| defaultValue: 'original', | ||
| options: [ | ||
| { label: 'Original (natural size)', value: 'original' }, | ||
| { label: '25% width', value: '25' }, | ||
| { label: '50% width', value: '50' }, | ||
| { label: '75% width', value: '75' }, | ||
| { label: '100% width', value: '100' }, | ||
|
rchlfryn marked this conversation as resolved.
|
||
| { label: 'Fixed height', value: 'fixed-height' }, | ||
| ], | ||
| admin: { | ||
| description: | ||
| 'Original uses the natural image size. Percentage widths are relative to the containing block. Fixed height lets you specify an exact pixel height.', | ||
| }, | ||
| }, | ||
| { | ||
| name: 'fixedHeight', | ||
| type: 'number', | ||
| min: 1, | ||
| admin: { | ||
| description: 'Height in pixels.', | ||
| condition: (_, siblingData) => siblingData?.size === 'fixed-height', | ||
| }, | ||
| }, | ||
| { | ||
| name: 'caption', | ||
| type: 'text', | ||
| admin: { | ||
| description: 'Optional text shown as a tooltip on hover.', | ||
| }, | ||
| }, | ||
| ], | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| import { InlineMediaBlock } from '@/blocks/InlineMedia/config' | ||
|
|
||
| export const DEFAULT_INLINE_BLOCKS = [InlineMediaBlock] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How is this being calculated/ is it needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the
attribute — a hint that tells the browser which source to pick from a responsive srcset before layout is known. For fixed-height images the rendered width depends on the image's aspect ratio, which we don't have at render time without reading the media dimensions. 96px is a conservative fallback so the browser doesn't load the full-resolution source. It's imperfect but errs on the side of a smaller download; the browser will still render at the correct size.