-
Notifications
You must be signed in to change notification settings - Fork 471
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(addon-editor): support
audio/video
tag in editor (#3585)
Co-authored-by: tinkoff-bot <[email protected]>
- Loading branch information
1 parent
3018ed2
commit 5f65b72
Showing
18 changed files
with
316 additions
and
2 deletions.
There are no files selected for viewing
This file contains 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 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 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 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 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,33 @@ | ||
import {tuiGetNestedNodes, tuiParseNodeAttributes} from '@taiga-ui/addon-editor/utils'; | ||
import {Node} from '@tiptap/core'; | ||
import {MarkSpec} from 'prosemirror-model'; | ||
|
||
export const TuiAudio = Node.create({ | ||
name: `audio`, | ||
group: `block`, | ||
content: `source+`, | ||
|
||
addAttributes() { | ||
return tuiParseNodeAttributes([ | ||
`id`, | ||
`class`, | ||
`src`, | ||
`style`, | ||
`controls`, | ||
`loop`, | ||
`muted`, | ||
`preload`, | ||
`autoplay`, | ||
`width`, | ||
`height`, | ||
]); | ||
}, | ||
|
||
parseHTML(): MarkSpec['parseDOM'] { | ||
return [{tag: `audio`}]; | ||
}, | ||
|
||
renderHTML({node, HTMLAttributes}) { | ||
return [`audio`, HTMLAttributes, ...tuiGetNestedNodes(node)]; | ||
}, | ||
}); |
This file contains 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 @@ | ||
export * from './audio.extension'; | ||
export * from './source.extension'; | ||
export * from './video.extension'; |
This file contains 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,8 @@ | ||
{ | ||
"lib": { | ||
"entryFile": "index.ts", | ||
"styleIncludePaths": [ | ||
"../../../core/styles" | ||
] | ||
} | ||
} |
27 changes: 27 additions & 0 deletions
27
projects/addon-editor/extensions/media/source.extension.ts
This file contains 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,27 @@ | ||
import {tuiParseNodeAttributes} from '@taiga-ui/addon-editor/utils'; | ||
import {mergeAttributes, Node} from '@tiptap/core'; | ||
import {MarkSpec} from 'prosemirror-model'; | ||
|
||
export const TuiSource = Node.create({ | ||
name: `source`, | ||
|
||
addAttributes() { | ||
return tuiParseNodeAttributes([ | ||
`src`, | ||
`type`, | ||
`width`, | ||
`height`, | ||
`media`, | ||
`sizes`, | ||
`srcset`, | ||
]); | ||
}, | ||
|
||
parseHTML(): MarkSpec['parseDOM'] { | ||
return [{tag: `source`}]; | ||
}, | ||
|
||
renderHTML({HTMLAttributes}: Record<string, any>) { | ||
return [`source`, mergeAttributes(HTMLAttributes)]; | ||
}, | ||
}); |
This file contains 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,33 @@ | ||
import {tuiGetNestedNodes, tuiParseNodeAttributes} from '@taiga-ui/addon-editor/utils'; | ||
import {Node} from '@tiptap/core'; | ||
import {MarkSpec} from 'prosemirror-model'; | ||
|
||
export const TuiVideo = Node.create({ | ||
name: `video`, | ||
group: `block`, | ||
content: `source+`, | ||
|
||
addAttributes() { | ||
return tuiParseNodeAttributes([ | ||
`id`, | ||
`class`, | ||
`src`, | ||
`style`, | ||
`controls`, | ||
`loop`, | ||
`muted`, | ||
`preload`, | ||
`autoplay`, | ||
`width`, | ||
`height`, | ||
]); | ||
}, | ||
|
||
parseHTML(): MarkSpec['parseDOM'] { | ||
return [{tag: `video`}]; | ||
}, | ||
|
||
renderHTML({node, HTMLAttributes}) { | ||
return [`video`, HTMLAttributes, ...tuiGetNestedNodes(node)]; | ||
}, | ||
}); |
This file contains 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 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,14 @@ | ||
import {Attrs, Node as NodeElement} from 'prosemirror-model'; | ||
|
||
export function tuiGetNestedNodes(node: NodeElement): Array<Array<Attrs | string>> { | ||
const nodes: Array<Array<Attrs | string>> = []; | ||
|
||
// @note: the content field is not array type | ||
node.content.forEach(child => { | ||
if (child instanceof NodeElement) { | ||
nodes.push([child.type.name, child.attrs]); | ||
} | ||
}); | ||
|
||
return nodes; | ||
} |
This file contains 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 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,13 @@ | ||
import {Attribute} from '@tiptap/core'; | ||
|
||
export function tuiParseNodeAttributes( | ||
attrs: string[], | ||
): Record<string, Partial<Attribute>> { | ||
return attrs.reduce((result, attribute) => { | ||
result[attribute] = { | ||
parseHTML: element => element?.getAttribute(`${attribute}`), | ||
}; | ||
|
||
return result; | ||
}, {} as Record<string, Partial<Attribute>>); | ||
} |
This file contains 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 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 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
12 changes: 12 additions & 0 deletions
12
projects/demo/src/modules/components/editor/embed/examples/3/index.html
This file contains 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,12 @@ | ||
<tui-editor | ||
class="editor" | ||
[formControl]="control" | ||
[tools]="builtInTools" | ||
(fileAttached)="attach($event)" | ||
></tui-editor> | ||
|
||
<h4>HTML:</h4> | ||
<div [innerHTML]="safe(control.value)"></div> | ||
|
||
<h4>Text:</h4> | ||
<p>{{ control.value }}</p> |
135 changes: 135 additions & 0 deletions
135
projects/demo/src/modules/components/editor/embed/examples/3/index.ts
This file contains 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,135 @@ | ||
import {Component, Inject, Injector, ViewChild} from '@angular/core'; | ||
import {FormControl, Validators} from '@angular/forms'; | ||
import {DomSanitizer, SafeHtml} from '@angular/platform-browser'; | ||
import {changeDetection} from '@demo/emulate/change-detection'; | ||
import {encapsulation} from '@demo/emulate/encapsulation'; | ||
import { | ||
TUI_ATTACH_FILES_LOADER, | ||
TUI_ATTACH_FILES_OPTIONS, | ||
TUI_EDITOR_EXTENSIONS, | ||
TuiEditorAttachedFile, | ||
TuiEditorComponent, | ||
TuiEditorTool, | ||
} from '@taiga-ui/addon-editor'; | ||
import {tuiPure, tuiTypedFromEvent} from '@taiga-ui/cdk'; | ||
import {Observable} from 'rxjs'; | ||
import {map} from 'rxjs/operators'; | ||
|
||
@Component({ | ||
selector: 'tui-editor-embed-example-3', | ||
templateUrl: './index.html', | ||
providers: [ | ||
{ | ||
provide: TUI_EDITOR_EXTENSIONS, | ||
deps: [Injector], | ||
useFactory: (_injector: Injector) => [ | ||
import('@taiga-ui/addon-editor/extensions/starter-kit').then( | ||
({StarterKit}) => StarterKit, | ||
), | ||
import('@tiptap/extension-text-style').then(({TextStyle}) => TextStyle), | ||
import('@taiga-ui/addon-editor/extensions/link').then( | ||
({TuiLink}) => TuiLink, | ||
), | ||
import('@taiga-ui/addon-editor/extensions/jump-anchor').then( | ||
({TuiJumpAnchor}) => TuiJumpAnchor, | ||
), | ||
import('@taiga-ui/addon-editor/extensions/file-link').then( | ||
({TuiFileLink}) => TuiFileLink, | ||
), | ||
import('@taiga-ui/addon-editor/extensions/media').then( | ||
({TuiVideo}) => TuiVideo, | ||
), | ||
import('@taiga-ui/addon-editor/extensions/media').then( | ||
({TuiAudio}) => TuiAudio, | ||
), | ||
import('@taiga-ui/addon-editor/extensions/media').then( | ||
({TuiSource}) => TuiSource, | ||
), | ||
], | ||
}, | ||
{ | ||
provide: TUI_ATTACH_FILES_LOADER, | ||
deps: [], | ||
useFactory: | ||
() => | ||
([file]: File[]): Observable< | ||
Array<TuiEditorAttachedFile<{type: string}>> | ||
> => { | ||
const fileReader = new FileReader(); | ||
|
||
// For example, instead of uploading to a file server, | ||
// we convert the result immediately into content to base64 | ||
fileReader.readAsDataURL(file); | ||
|
||
return tuiTypedFromEvent(fileReader, 'load').pipe( | ||
map(() => [ | ||
{ | ||
name: file.name, | ||
|
||
/* base64 or link to the file on your server */ | ||
link: String(fileReader.result), | ||
|
||
attrs: { | ||
type: file.type, | ||
}, | ||
}, | ||
]), | ||
); | ||
}, | ||
}, | ||
{ | ||
provide: TUI_ATTACH_FILES_OPTIONS, | ||
useValue: { | ||
accept: 'video/mp4,video/x-m4v,video/*,audio/x-m4a,audio/*', | ||
multiple: false, | ||
}, | ||
}, | ||
], | ||
changeDetection, | ||
encapsulation, | ||
}) | ||
export class TuiEditorEmbedExample3 { | ||
@ViewChild(TuiEditorComponent) | ||
private readonly wysiwyg?: TuiEditorComponent; | ||
|
||
readonly builtInTools = [ | ||
TuiEditorTool.Undo, | ||
TuiEditorTool.Link, | ||
TuiEditorTool.Attach, | ||
]; | ||
|
||
readonly control = new FormControl( | ||
` | ||
<p>Here is video: </p> | ||
<video controls="controls" width="100%"> | ||
<source src="https://www.w3schools.com/html/mov_bbb.mp4" type="video/mp4"> | ||
</video> | ||
<p>Here is audio: </p> | ||
<audio controls style="width: 100%"> | ||
<source src="https://www.w3docs.com/build/audios/audio.mp3" type="audio/mp3"> | ||
</audio> | ||
<p></p> | ||
`, | ||
Validators.required, | ||
); | ||
|
||
constructor(@Inject(DomSanitizer) private readonly sanitizer: DomSanitizer) {} | ||
|
||
@tuiPure | ||
safe(content: string): SafeHtml { | ||
return this.sanitizer.bypassSecurityTrustHtml(content); | ||
} | ||
|
||
attach([file]: Array<TuiEditorAttachedFile<{type: string}>>): void { | ||
const tag = `${file.attrs?.type ?? ''}`.split('/')[0]; | ||
|
||
this.wysiwyg?.editor | ||
?.getOriginTiptapEditor() | ||
.commands.insertContent( | ||
`<${tag} controls width="100%"><source src="${file.link}" type="${file.attrs?.type}"></${tag}><p><a href="${file.link}" download="${file.name}">Download ${file.name}</a></p>`, | ||
); | ||
} | ||
} |