Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/ousc/ng-prosemirror-adapter
prosemirror adapter for angular 17+
https://github.com/ousc/ng-prosemirror-adapter
angular javascript prosemirror typescript
Last synced: about 2 months ago
JSON representation
prosemirror adapter for angular 17+
- Host: GitHub
- URL: https://github.com/ousc/ng-prosemirror-adapter
- Owner: ousc
- License: mit
- Created: 2023-12-02T03:16:50.000Z (about 1 year ago)
- Default Branch: main
- Last Pushed: 2024-08-15T08:56:35.000Z (5 months ago)
- Last Synced: 2024-08-15T10:29:24.542Z (5 months ago)
- Topics: angular, javascript, prosemirror, typescript
- Language: TypeScript
- Homepage:
- Size: 3.17 MB
- Stars: 2
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
NG-PROSEMIRROR-ADAPTER[Angular](https://angular.dev/) adapter for [ProseMirror](https://prosemirror.net/), only supports Angular **17**+.
More detail please move to [**prosemirror-adapter**](https://github.com/Saul-Mirone/prosemirror-adapter) by [**@Saul-Mirone**](https://github.com/Saul-Mirone).**This project is just adding support for Angular.**
## Example
You can run this example by:
```bash
git clone https://github.com/ousc/ng-prosemirror-adapter.git
cd ng-prosemirror-adapter
npm install
npm run start
```## Online Demo
[https://ousc.github.io/ng-prosemirror-adapter](https://ousc.github.io/ng-prosemirror-adapter/)## Getting Started
### Install the package
```bash
npm install ng-prosemirror-adapter
```### Wrap your component with provider
```html
```
### Play with node view
In this section we will implement a node view for paragraph node.
#### Build component for [node view](https://prosemirror.net/docs/ref/#view.NodeView)
```ts
import {Component} from '@angular/core';
import {NgProsemirrorNode} from 'ng-prosemirror-adapter';@Component({
selector: 'paragraph',
template: `
`,
styles: [`
:host .selected {
outline: blue solid 1px;
}
`],
standalone: true
})
export class Paragraph extends NgProsemirrorNode {}
```#### Bind node view components with prosemirror
```ts
import {AfterViewInit, Component, ElementRef, forwardRef, ViewChild} from '@angular/core';
import {Paragraph} from "../paragraph.component";
import {NgProsemirrorEditor} from 'ng-prosemirror-adapter';@Component({
`,
selector: 'editor',
standalone: true,
template: `
providers: [{provide: NgProsemirrorEditor, useExisting: forwardRef(() => EditorComponent)}],
})
export class EditorComponent extends NgProsemirrorEditor implements AfterViewInit {
@ViewChild('editorRef') editorRef: ElementRef;async ngAfterViewInit(): Promise {
const element = this.editorRef.nativeElement;
if (!element || element.firstChild)
returnconst editorView = new EditorView(element, {
state: YourProsemirrorEditorState,
nodeViews: {
paragraph: this.provider.createNodeView({
component: Paragraph,
as: 'div',
contentAs: 'p',
}),
}
})
}
}```
🚀 Congratulations! You have built your first angular node view with prosemirror-adapter.
### Play with plugin view
In this section we will implement a plugin view that will display the size of the document.
#### Build component for [plugin view](https://prosemirror.net/docs/ref/#state.PluginView)
```ts
import {Component} from '@angular/core';
import {NgProsemirrorPlugin} from 'ng-prosemirror-adapter';@Component({
selector: 'size',
template: `Size for document: {{ size }}`,
styles: [],
standalone: true
})
export class Size extends NgProsemirrorPlugin {get size() {
return this.state?.doc?.nodeSize
}
}```
#### Bind plugin view components with prosemirror
```tsx
import {AfterViewInit, Component, ElementRef, forwardRef, ViewChild} from '@angular/core';
import {Size} from "../size.component";
import {NgProsemirrorEditor} from 'ng-prosemirror-adapter';@Component({
`,
selector: 'editor',
standalone: true,
template: `
providers: [{provide: NgProsemirrorEditor, useExisting: forwardRef(() => EditorComponent)}],
})
export class EditorComponent extends NgProsemirrorEditor implements AfterViewInit {
@ViewChild('editorRef') editorRef: ElementRef;async ngAfterViewInit(): Promise {
const element = this.editorRef.nativeElement;
if (!element || element.firstChild)
returnconst editorView = new EditorView(element, {
state: YourProsemirrorEditorState,
plugins: [
new Plugin({
view: await this.provider.createPluginView({ component: Size }),
}),
]
})
}
}
```🚀 Congratulations! You have built your first angular plugin view with prosemirror-adapter.
### Play with widget view
In this section we will implement a widget view that will add hashes for heading when selected.
#### Build component for [widget decoration view](https://prosemirror.net/docs/ref/#view.Decoration%5Ewidget)
```ts
import {Component} from '@angular/core';
import {NgProsemirrorWidget} from 'ng-prosemirror-adapter';@Component({
selector: 'hashes',
template: `
{{ hashes }}`,
styles: [`
.hash {
color: blue;
margin-right: 6px;
}`],
standalone: true
})
export class Hashes extends NgProsemirrorWidget {
get level() {
return this.spec?.['level'];
}get hashes() {
return Array(this.level || 0).fill('#').join('');
}
}```
#### Bind widget view components with prosemirror
```ts
import {AfterViewInit, Component, ElementRef, forwardRef, ViewChild} from '@angular/core';
import {Hashes} from "../hashes.component";
import {NgProsemirrorEditor} from 'ng-prosemirror-adapter';
import {Plugin} from "prosemirror-state";@Component({
`,
selector: 'editor',
standalone: true,
template: `
providers: [{provide: NgProsemirrorEditor, useExisting: forwardRef(() => EditorComponent)}],
})
export class EditorComponent extends NgProsemirrorEditor implements AfterViewInit {
@ViewChild('editorRef') editorRef: ElementRef;async ngAfterViewInit(): Promise {
const element = this.editorRef.nativeElement;
if (!element || element.firstChild)
returnconst editorView = new EditorView(element, {
state: YourProsemirrorEditorState,
plugins: [
new Plugin({
props: {
decorations: (state) => {
const getHashWidget = this.provider.createWidgetView({
as: 'i',
component: Hashes,
})
const {$from} = state.selection
const node = $from.node()
if (node.type.name !== 'heading')
return DecorationSet.emptyconst widget = getHashWidget($from.before() + 1, {
side: -1,
level: node.attrs['level'],
})
return DecorationSet.create(state.doc, [widget])
},
},
}),
]
})
}
}
```🚀 Congratulations! You have built your first angular widget view with prosemirror-adapter.
## API
if you properly wrap your component with `NgProsemirrorAdapterProvider`, and extends `NgProsemirrorEditor` or `NgProsemirrorNode` or `NgProsemirrorPlugin` or `NgProsemirrorWidget`, you can use `this.provider` to access the following APIs.
else you can use @ViewChild to access the NgProsemirrorAdapterProvider instance and use the following APIs.### NgProsemirrorNode API
#### NgProsemirrorEditor.provider.createNodeView: NodeViewFactory => (options: NgNodeViewUserOptions) => NodeViewConstructor
```ts
type NgNodeViewUserOptions = {
component: Type
as?: string | HTMLElement
contentAs?: string | HTMLElement
update?: (node: Node, decorations: readonly Decoration[], innerDecorations: DecorationSource) => boolean | void
ignoreMutation?: (mutation: MutationRecord) => boolean | void
selectNode?: () => void
deselectNode?: () => void
setSelection?: (anchor: number, head: number, root: Document | ShadowRoot) => void
stopEvent?: (event: Event) => boolean
destroy?: () => void// Additional
onUpdate?: () => void
inputs?: {
[key: string]: any
},
key?: string
}type NodeViewFactory = (options: NgNodeViewUserOptions) => NodeViewConstructor
```#### NgProsemirrorNode.context: NodeViewContext
```ts
interface NodeViewContext {
// won't change
contentRef: NodeViewContentRef
view: EditorView
getPos: () => number | undefined
setAttrs: (attrs: Attrs) => void// changes between updates
node: Node
selected: boolean
decorations: readonly Decoration[]
innerDecorations: DecorationSource
}type NodeViewContentRef = (node: HTMLElement | null) => void
```### NgProsemirrorNode.view: EditorView
### NgProsemirrorNode.contentRef: NodeViewContentRef
### NgProsemirrorNode.getPos: () => number | undefined
### NgProsemirrorNode.setAttrs: (attrs: Attrs) => void### NgProsemirrorNode.node: Node
### NgProsemirrorNode.selected: boolean
### NgProsemirrorNode.decorations: readonly Decoration[]
### NgProsemirrorNode.innerDecorations: DecorationSource
### NgProsemirrorPlugin API
#### NgProsemirrorEditor.provider.createPluginView: NodeViewFactory => (options: NgNodeViewUserOptions) => NodeViewConstructor
```ts
export type NgPluginViewUserOptions = {
component: Type
root?: (viewDOM: HTMLElement) => HTMLElement
update?: (view: EditorView, prevState: EditorState) => void
destroy?: () => void,
inputs?: {
[key: string]: any
},
key?: string
}export type PluginViewFactory = (options: NgPluginViewUserOptions) => Promise
```
#### NgProsemirrorPlugin.context: PluginViewContext
```ts
export interface PluginViewContext {
view: EditorView
prevState: EditorState
}
```#### NgProsemirrorPlugin.view: EditorView
#### NgProsemirrorPlugin.state: EditorState
#### NgProsemirrorPlugin.prevState: EditorState
### NgProsemirrorWidget API
#### NgProsemirrorEditor.provider.createWidgetView: WidgetViewFactory => (options: NgWidgetViewUserOptions) => WidgetViewConstructor
```ts
export type NgWidgetUserOptions = {
as: string | HTMLElement
component: Type,
inputs?: {
[key: string]: any
},
key?: string
}export type WidgetViewFactory = (options: NgWidgetUserOptions) => WidgetDecorationFactory
```#### NgProsemirrorWidget.context: WidgetViewContext
```ts
export interface WidgetViewContext {
view: EditorView
getPos: () => number | undefined
spec?: WidgetDecorationSpec
}
```#### NgProsemirrorWidget.view: EditorView
#### NgProsemirrorWidget.getPos: () => number | undefined
#### NgProsemirrorWidget.spec: WidgetDecorationSpec
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
## License
[MIT License](LICENSE)