Skip to content

Commit e68613e

Browse files
j7-devclaude
andcommitted
feat(editor): 新增 MediaResizer 元件支援媒體寬度拖曳編輯
- 新增 MediaResizer 元件,在編輯時於媒體左右邊緣疊加拖曳 handle - 支援 px 與 % 單位的寬度調整,對齐模式影響拖曳行為(置中時兩側同步) - 整合至 BunnyVideo 與 MediaLibrary 按鈕,提升編輯體驗 Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012JvQZZEoPHk54By61DZ8by
1 parent b418ce7 commit e68613e

3 files changed

Lines changed: 299 additions & 17 deletions

File tree

lib/main/components/editor/BlockNote/CustomBlocks/BunnyVideo/Button/index.tsx

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { memo, useEffect, useState } from 'react'
1+
import { memo, useEffect, useRef, useState } from 'react'
22
import { Button, Space, InputNumber, Tooltip, Radio } from 'antd'
33
import {
44
AlignLeftOutlined,
@@ -12,6 +12,7 @@ import type { CustomBlockConfig } from '@blocknote/core'
1212
import { TbSwitchHorizontal } from 'react-icons/tb'
1313
import { debounce } from 'lodash-es'
1414
import Render from '../Render'
15+
import { MediaResizer } from '../../MediaResizer'
1516
import { cn } from '@/main/utils'
1617
import { MediaLibraryModal, useMediaLibraryModal } from '@/refine'
1718

@@ -33,6 +34,8 @@ const MediaLibraryButton = (
3334
) => {
3435
// 為了讓 input 的 defaultValue 的整個組件可以重新 render 重新設置 defaultValue,透過 key 來強制重新 render
3536
const [key, setKey] = useState(0)
37+
// 定位祖先,供 MediaResizer 量測 media 位置
38+
const containerRef = useRef<HTMLDivElement>(null)
3639
const currentBlock = props ? props?.block : null
3740
const currentBlockProps = currentBlock?.props || null
3841

@@ -79,7 +82,8 @@ const MediaLibraryButton = (
7982
return (
8083
<>
8184
<div
82-
className="at-w-full"
85+
ref={containerRef}
86+
className="at-w-full at-relative"
8387
onMouseEnter={() => setShowTool(true)}
8488
onMouseLeave={() => setShowTool(false)}
8589
style={{
@@ -98,6 +102,23 @@ const MediaLibraryButton = (
98102
editor={props.editor}
99103
/>
100104
</div>
105+
<MediaResizer
106+
containerRef={containerRef}
107+
widthValue={currentBlockProps.widthValue}
108+
widthUnit={currentBlockProps.widthUnit}
109+
align={currentBlockProps.align || 'start'}
110+
visible={showTool}
111+
onResizeEnd={(widthValue) => {
112+
props.editor.updateBlock(props.block, {
113+
type: 'bunnyVideo',
114+
props: {
115+
...currentBlockProps,
116+
widthValue,
117+
} as any,
118+
})
119+
setKey((prev: number) => prev + 1)
120+
}}
121+
/>
101122
<div
102123
className={cn(
103124
'at-py-1 at-px-2 at-bg-gray-100 at-rounded-md at-transition-opacity at-duration-300',
@@ -116,8 +137,8 @@ const MediaLibraryButton = (
116137
/>
117138

118139
<Space.Compact>
140+
<Space.Addon></Space.Addon>
119141
<InputNumber
120-
prefix="寬"
121142
size="small"
122143
className="at-w-32"
123144
defaultValue={currentBlockProps.widthValue}
@@ -140,17 +161,17 @@ const MediaLibraryButton = (
140161
</Space.Compact>
141162

142163
{'video' === currentBlockProps.player && (
143-
<InputNumber
144-
prefix={
145-
<Tooltip title="可自由填入影片長寬比,例如 16/9 為 1.7778">
146-
比例
147-
</Tooltip>
148-
}
149-
className="at-w-32"
150-
size="small"
151-
defaultValue={currentBlockProps.aspectRatio}
152-
onChange={(value) => update('aspectRatio', value)}
153-
/>
164+
<Space.Compact>
165+
<Space.Addon><Tooltip title="可自由填入影片長寬比,例如 16/9 為 1.7778">
166+
比例
167+
</Tooltip></Space.Addon>
168+
<InputNumber
169+
className="at-w-32"
170+
size="small"
171+
defaultValue={currentBlockProps.aspectRatio}
172+
onChange={(value) => update('aspectRatio', value)}
173+
/>
174+
</Space.Compact>
154175
)}
155176

156177
<Space.Compact>

lib/main/components/editor/BlockNote/CustomBlocks/MediaLibrary/Button/index.tsx

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { memo, useEffect, useState } from 'react'
1+
import { memo, useEffect, useRef, useState } from 'react'
22
import { Button, Space, InputNumber, Input, Checkbox, Tooltip } from 'antd'
33
import {
44
AlignLeftOutlined,
@@ -12,6 +12,7 @@ import type { CustomBlockConfig } from '@blocknote/core'
1212
import { debounce } from 'lodash-es'
1313
import { TbSwitchHorizontal } from 'react-icons/tb'
1414
import Render from '../Render'
15+
import { MediaResizer } from '../../MediaResizer'
1516
import { cn, isImageFile, isAudioFile, isVideoFile, AltIcon } from '@/main'
1617
import { MediaLibraryModal, useMediaLibraryModal } from '@/wp'
1718

@@ -35,6 +36,8 @@ const MediaLibraryButton = (
3536
) => {
3637
// 為了讓 input 的 defaultValue 的整個組件可以重新 render 重新設置 defaultValue,透過 key 來強制重新 render
3738
const [key, setKey] = useState(0)
39+
// 定位祖先,供 MediaResizer 量測 media 位置
40+
const containerRef = useRef<HTMLDivElement>(null)
3841
const currentBlock = props ? props?.block : null
3942
const currentBlockProps = currentBlock?.props || null
4043

@@ -95,7 +98,8 @@ const MediaLibraryButton = (
9598
return (
9699
<>
97100
<div
98-
className="at-w-full"
101+
ref={containerRef}
102+
className="at-w-full at-relative"
99103
onMouseEnter={() => setShowTool(true)}
100104
onMouseLeave={() => setShowTool(false)}
101105
style={{
@@ -110,6 +114,25 @@ const MediaLibraryButton = (
110114
>
111115
<Render block={props.block} editor={props.editor} />
112116
</div>
117+
{'other' !== fileType && (
118+
<MediaResizer
119+
containerRef={containerRef}
120+
widthValue={currentBlockProps.widthValue}
121+
widthUnit={currentBlockProps.widthUnit}
122+
align={currentBlockProps.align || 'start'}
123+
visible={showTool}
124+
onResizeEnd={(widthValue) => {
125+
props.editor.updateBlock(props.block, {
126+
type: 'mediaLibrary',
127+
props: {
128+
...currentBlockProps,
129+
widthValue,
130+
} as any,
131+
})
132+
setKey((prev: number) => prev + 1)
133+
}}
134+
/>
135+
)}
113136
<div
114137
className={cn(
115138
'at-py-1 at-px-2 at-bg-gray-100 at-rounded-md at-transition-opacity at-duration-300',
@@ -120,8 +143,8 @@ const MediaLibraryButton = (
120143
<div className="at-flex at-items-center at-justify-center at-gap-x-2">
121144
{'other' !== fileType && (
122145
<Space.Compact>
146+
<Space.Addon></Space.Addon>
123147
<InputNumber
124-
prefix="寬"
125148
className="at-w-32"
126149
size="small"
127150
defaultValue={currentBlockProps.widthValue}

0 commit comments

Comments
 (0)