Skip to content

Commit a7323a4

Browse files
feat(video): add alignment options for video elements
1 parent 723c098 commit a7323a4

File tree

3 files changed

+49
-7
lines changed

3 files changed

+49
-7
lines changed

src/components/menus/bubble.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type { Editor } from '@tiptap/react';
44
import { ActionButton } from '@/components';
55
import { BUBBLE_TEXT_LIST, IMAGE_SIZE, VIDEO_SIZE } from '@/constants';
66
import { localeActions } from '@/locales';
7-
import type { ButtonViewParams, ButtonViewReturn, ExtensionNameKeys } from '@/types';
7+
import type { ButtonViewParams, ButtonViewReturn, ExtensionNameKeys,VideoAlignment } from '@/types';
88

99
/** Represents the size types for bubble images or videos */
1010
type BubbleImageOrVideoSizeType = 'size-small' | 'size-medium' | 'size-large';
@@ -189,6 +189,28 @@ function imageDrawerAlignMenus(editor: Editor): BubbleMenuItem[] {
189189
},
190190
}));
191191
}
192+
function videoAlignMenus(editor: Editor): BubbleMenuItem[] {
193+
const alignments: {
194+
type: VideoAlignment;
195+
icon: string;
196+
tooltip: string;
197+
}[] = [
198+
{ type: 'flex-start', icon: 'AlignLeft', tooltip: 'Align left' },
199+
{ type: 'center', icon: 'AlignCenter', tooltip: 'Align center' },
200+
{ type: 'flex-end', icon: 'AlignRight', tooltip: 'Align right' },
201+
];
202+
203+
return alignments.map((align) => ({
204+
type: `video-align-${align.type}`,
205+
component: ActionButton,
206+
componentProps: {
207+
tooltip: align.tooltip,
208+
icon: align.icon,
209+
action: () => editor.commands.updateVideo({ align: align.type }),
210+
isActive: () => editor.getAttributes('video').align === align.type,
211+
},
212+
}));
213+
}
192214

193215
function videoSizeMenus(editor: Editor): BubbleMenuItem[] {
194216
const types: BubbleImageOrVideoSizeType[] = ['size-small', 'size-medium', 'size-large'];
@@ -356,6 +378,7 @@ export function getBubbleDrawer(editor: Editor): BubbleMenuItem[] {
356378
export function getBubbleVideo(editor: Editor): BubbleMenuItem[] {
357379
return [
358380
...videoSizeMenus(editor),
381+
...videoAlignMenus(editor),
359382
{
360383
type: 'remove',
361384
component: ActionButton,

src/extensions/Video/Video.ts

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import { Node } from '@tiptap/core';
22

33
import { VIDEO_SIZE } from '@/constants';
44
import ActionVideoButton from '@/extensions/Video/components/ActiveVideoButton';
5-
import type { GeneralOptions } from '@/types';
5+
import type { GeneralOptions,VideoAlignment } from '@/types';
6+
67
import { getCssUnitWithDefault } from '@/utils/utils';
78

89
/**
@@ -54,6 +55,8 @@ interface SetVideoOptions {
5455
src: string
5556
/** The width of the video */
5657
width: string | number
58+
59+
align: VideoAlignment;
5760
}
5861

5962
declare module '@tiptap/core' {
@@ -113,7 +116,7 @@ export const Video = /* @__PURE__ */ Node.create<VideoOptions>({
113116
width: VIDEO_SIZE['size-medium'],
114117
HTMLAttributes: {
115118
class: 'iframe-wrapper',
116-
style: 'display: flex;justify-content: center;',
119+
// style: 'display: flex;justify-content: center;',
117120
},
118121
button: ({ editor, t }: any) => {
119122
return {
@@ -157,6 +160,12 @@ export const Video = /* @__PURE__ */ Node.create<VideoOptions>({
157160
default: this.options.allowFullscreen,
158161
parseHTML: () => this.options.allowFullscreen,
159162
},
163+
align: {
164+
default: 'center', // Default alignment
165+
renderHTML: ({ align }) => ({
166+
align: align,
167+
}),
168+
},
160169
};
161170
},
162171

@@ -169,27 +178,35 @@ export const Video = /* @__PURE__ */ Node.create<VideoOptions>({
169178
},
170179

171180
renderHTML({ HTMLAttributes }) {
172-
const { width = '100%' } = HTMLAttributes ?? {};
181+
const { width = '100%' ,align = 'center' } = HTMLAttributes ?? {};
173182

174-
const iframeHTMLAttributes = {
183+
const iframeHTMLAttributes = {
175184
...HTMLAttributes,
176185
width: '100%',
177186
height: '100%',
178187
};
179188

180189
const responsiveStyle = `position: relative;overflow: hidden;display: flex;flex: 1;max-width: ${width};`;
181190
const responsiveSizesStyle = `flex: 1;padding-bottom: ${(9 / 16) * 100}%;`;
191+
const positionStyle = `display: flex; justify-content: ${align};`;
182192

183193
const iframeDOM = ['iframe', iframeHTMLAttributes];
184194
const sizesDOM = ['div', { style: responsiveSizesStyle }];
185-
const responsiveDOM = ['div', { style: responsiveStyle }, sizesDOM, iframeDOM];
195+
const responsiveDOM = [
196+
'div',
197+
{ style: responsiveStyle },
198+
sizesDOM,
199+
iframeDOM,
200+
];
201+
const positionDiv = ['div', { style: positionStyle }, responsiveDOM];
186202

187203
const divAttrs = {
188204
...this.options.HTMLAttributes,
205+
class: 'iframe-wrapper',
189206
'data-video': '',
190207
};
191208

192-
return ['div', divAttrs, responsiveDOM];
209+
return ['div', divAttrs, positionDiv];
193210
},
194211

195212
addCommands() {

src/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,3 +262,5 @@ export interface NameValueOption<T = string> {
262262
name: string
263263
value: T
264264
}
265+
266+
export type VideoAlignment = 'flex-start' | 'center' | 'flex-end';

0 commit comments

Comments
 (0)