diff --git a/examples/Components/Routes/Focus/index.vue b/examples/Components/Routes/Focus/index.vue
new file mode 100644
index 00000000..2cacb82d
--- /dev/null
+++ b/examples/Components/Routes/Focus/index.vue
@@ -0,0 +1,97 @@
+
+
+
+
+
+
+
+
+
diff --git a/examples/Components/Subnavigation/index.vue b/examples/Components/Subnavigation/index.vue
index 35a023e9..f1a05f92 100644
--- a/examples/Components/Subnavigation/index.vue
+++ b/examples/Components/Subnavigation/index.vue
@@ -48,6 +48,9 @@
Placeholder
+
+ Focus
+
Collaboration
diff --git a/examples/main.js b/examples/main.js
index 3702cad6..8ee8e3e7 100644
--- a/examples/main.js
+++ b/examples/main.js
@@ -123,6 +123,13 @@ const routes = [
githubUrl: 'https://github.com/scrumpy/tiptap/tree/master/examples/Components/Routes/Placeholder',
},
},
+ {
+ path: '/focus',
+ component: () => import('Components/Routes/Focus'),
+ meta: {
+ githubUrl: 'https://github.com/scrumpy/tiptap/tree/master/examples/Components/Routes/Focus',
+ },
+ },
{
path: '/collaboration',
component: () => import('Components/Routes/Collaboration'),
diff --git a/packages/tiptap-extensions/src/extensions/Focus.js b/packages/tiptap-extensions/src/extensions/Focus.js
new file mode 100644
index 00000000..5f78a011
--- /dev/null
+++ b/packages/tiptap-extensions/src/extensions/Focus.js
@@ -0,0 +1,53 @@
+import { Extension, Plugin } from 'tiptap'
+import { DecorationSet, Decoration } from 'prosemirror-view'
+
+export default class Focus extends Extension {
+
+ get name() {
+ return 'focus'
+ }
+
+ get defaultOptions() {
+ return {
+ className: 'has-focus',
+ nested: false,
+ }
+ }
+
+ get plugins() {
+ return [
+ new Plugin({
+ props: {
+ decorations: ({ doc, plugins, selection }) => {
+ const editablePlugin = plugins.find(plugin => plugin.key.startsWith('editable$'))
+ const editable = editablePlugin.props.editable()
+ const active = editable && this.options.className
+ const { focused } = this.editor
+ const { anchor } = selection
+ const decorations = []
+
+ if (!active || !focused) {
+ return false
+ }
+
+ doc.descendants((node, pos) => {
+ const hasAnchor = anchor >= pos && anchor <= (pos + node.nodeSize)
+
+ if (hasAnchor && !node.isText) {
+ const decoration = Decoration.node(pos, pos + node.nodeSize, {
+ class: this.options.className,
+ })
+ decorations.push(decoration)
+ }
+
+ return this.options.nested
+ })
+
+ return DecorationSet.create(doc, decorations)
+ },
+ },
+ }),
+ ]
+ }
+
+}
diff --git a/packages/tiptap-extensions/src/index.js b/packages/tiptap-extensions/src/index.js
index 3165bccb..557f008a 100644
--- a/packages/tiptap-extensions/src/index.js
+++ b/packages/tiptap-extensions/src/index.js
@@ -24,6 +24,7 @@ export { default as Strike } from './marks/Strike'
export { default as Underline } from './marks/Underline'
export { default as Collaboration } from './extensions/Collaboration'
+export { default as Focus } from './extensions/Focus'
export { default as History } from './extensions/History'
export { default as Placeholder } from './extensions/Placeholder'
export { default as Search } from './extensions/Search'