prosemirror-slash-menu 0.1.2 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -1
- package/dist/actions.d.ts +2 -9
- package/dist/cases.d.ts +1 -1
- package/dist/index.es.js +1 -1
- package/dist/index.js +1 -1
- package/dist/plugin.d.ts +1 -1
- package/dist/plugin.js +151 -0
- package/dist/types.d.ts +3 -0
- package/package.json +19 -18
package/README.md
CHANGED
|
@@ -51,6 +51,8 @@ Extension.create({
|
|
|
51
51
|
|
|
52
52
|
A menu elements can either be a simple `CommandItem` that executes an action, or it can be a `SubMenu` that can be opened to show its elements.
|
|
53
53
|
You can nest submenus into other submenus as needed.
|
|
54
|
+
The `locked` property can be used to hide a menu element from the user. The main idea behind it is to have a `SubMenu` that can only be opened by sending a transaction with `openSubMenu` meta.
|
|
55
|
+
Once opened it behaves like a second, hidden slash menu. For eg. you can have a command that needs approval or rejection after execution, you could open the slash menu with just these two options that are otherwise hidden.
|
|
54
56
|
|
|
55
57
|
NOTE: It is necessary to add unique ids to every menu element.
|
|
56
58
|
```typescript
|
|
@@ -62,12 +64,13 @@ type MenuItem = {
|
|
|
62
64
|
id: ItemId;
|
|
63
65
|
label: string;
|
|
64
66
|
type: ItemType;
|
|
67
|
+
available: () => boolean;
|
|
68
|
+
locked?: boolean;
|
|
65
69
|
};
|
|
66
70
|
|
|
67
71
|
interface CommandItem extends MenuItem {
|
|
68
72
|
type: "command";
|
|
69
73
|
command: (view: EditorView) => void;
|
|
70
|
-
available: () => boolean;
|
|
71
74
|
}
|
|
72
75
|
|
|
73
76
|
interface SubMenu extends MenuItem {
|
|
@@ -102,6 +105,11 @@ interface OpeningConditions {
|
|
|
102
105
|
) => boolean;
|
|
103
106
|
}
|
|
104
107
|
```
|
|
108
|
+
### Open in selection
|
|
109
|
+
|
|
110
|
+
You have the option to open the menu even if you have something selected.
|
|
111
|
+
|
|
112
|
+
`openInSelection: boolean`
|
|
105
113
|
|
|
106
114
|
# Behaviour
|
|
107
115
|
|
package/dist/actions.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { SlashMenuMeta, SlashMenuState } from "./types";
|
|
2
|
+
export declare const closeMenu: (initialState: SlashMenuState) => SlashMenuState;
|
|
2
3
|
export declare const openSubMenu: (state: SlashMenuState, meta: SlashMenuMeta) => SlashMenuState;
|
|
3
4
|
export declare const closeSubMenu: (state: SlashMenuState, meta: SlashMenuMeta, initialState: SlashMenuState) => SlashMenuState;
|
|
4
5
|
export declare const nextItem: (state: SlashMenuState) => SlashMenuState;
|
|
@@ -11,13 +12,5 @@ export declare const filterItems: (state: SlashMenuState, filter: string) => {
|
|
|
11
12
|
subMenuId?: string | undefined;
|
|
12
13
|
elements: import("./types").MenuElement[];
|
|
13
14
|
ignoredKeys: string[];
|
|
14
|
-
|
|
15
|
-
export declare const updateInput: (state: SlashMenuState, meta: SlashMenuMeta, initialState: SlashMenuState) => {
|
|
16
|
-
filteredElements: import("./types").MenuElement[];
|
|
17
|
-
filter: string;
|
|
18
|
-
selected: string;
|
|
19
|
-
open: boolean;
|
|
20
|
-
subMenuId?: string | undefined;
|
|
21
|
-
elements: import("./types").MenuElement[];
|
|
22
|
-
ignoredKeys: string[];
|
|
15
|
+
callbackOnClose?: (() => void) | undefined;
|
|
23
16
|
};
|
package/dist/cases.d.ts
CHANGED
|
@@ -12,4 +12,4 @@ export declare enum SlashCases {
|
|
|
12
12
|
Ignore = "Ignore",
|
|
13
13
|
Catch = "Catch"
|
|
14
14
|
}
|
|
15
|
-
export declare const getCase: (state: SlashMenuState, event: KeyboardEvent, view: EditorView, ignoredKeys: string[], customConditions?: OpeningConditions) => SlashCases;
|
|
15
|
+
export declare const getCase: (state: SlashMenuState, event: KeyboardEvent, view: EditorView, ignoredKeys: string[], customConditions?: OpeningConditions, shouldOpenInSelection?: boolean) => SlashCases;
|
package/dist/index.es.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{PluginKey as e,Plugin as t}from"prosemirror-state";const n=e=>"submenu"===e.type?[e.id,...e.elements.map((e=>n(e)))].flat():[e.id],r=e=>{const t=(e=>e.filteredElements.map((e=>n(e))).flat())(e);return t.length!==new Set(t).size},s=e=>"submenu"===e.type?[e,...e.elements.map((e=>s(e)))].flat():[e],o=e=>e.elements.map((e=>s(e))).flat()
|
|
1
|
+
import{PluginKey as e,Plugin as t}from"prosemirror-state";const n=e=>"submenu"===e.type?[e.id,...e.elements.map((e=>n(e)))].flat():[e.id],r=e=>{const t=(e=>e.filteredElements.map((e=>n(e))).flat())(e);return t.length!==new Set(t).size},s=e=>"submenu"===e.type?[e,...e.elements.map((e=>s(e)))].flat():[e],o=(e,t)=>(e=>e.elements.map((e=>s(e))).flat())(t).find((t=>t.id===e)),l=(e,t,n="root")=>{let r="root";return t.forEach((t=>{if("submenu"===t.type){t.id===e&&(r=n);const s=t.elements.map((e=>e.id));r=s.includes(e)?t.id:l(e,t.elements,t.id)}t.id===e&&(r=n)})),r},a=(e,t,n)=>e.dispatch(e.state.tr.setMeta(t,n)),i=["Unidentified","Alt","AltGraph","CapsLock","Control","Fn","FnLock","F1","F2","F3","F4","F5","F6","F7","F8","F9","F10","F11","F12","F13","F14","F15","F16","F17","F18","F19","F20","F21","F22","F23","F24","Hyper","Meta","NumLock","PageDown","PageUp","Pause","PrintScreen","Redo","ScrollLock","Shift","Super","Symbol","SymbolLock","Enter","Tab","ArrowDown","ArrowLeft","ArrowRight","ArrowUp","End","Home","PageDown","PageUp","Backspace","Clear","Copy","CrSel","Cut","Delete","EraseEof","ExSel","Insert","Paste","Redo","Undo","Accept","Again","Attn","Cancel","ContextMenu","Escape","Execute","Find","Finish","Help","Pause","Play","Props","Select","ZoomIn","ZoomOut","BrightnessDown","BrightnessUp","Eject","LogOff","Power","PowerOff","PrintScreen","Hibernate","Standby","WakeUp","AllCandidates","Alphanumeric","CodeInput","Compose","Convert","Dead","FinalMode","GroupFirst","GroupLast","GroupNext","GroupPrevious","ModeChange","NextCandidate","NonConvert","PreviousCandidate","Process","SingleCandidate","HangulMode","HanjaMode","JunjaMode","Eisu","Hankaku","Hiragana","HiraganaKatakana","KanaMode","KanjiMode","Katakana","Romaji","Zenkaku","ZenkakuHanaku"];var u;!function(e){e.OpenMenu="openMenu",e.CloseMenu="closeMenu",e.Execute="Execute",e.NextItem="NextItem",e.PrevItem="PrevItem",e.inputChange="InputChange",e.addChar="addChar",e.removeChar="removeChar",e.Ignore="Ignore",e.Catch="Catch"}(u||(u={}));const c=e=>{const t=(e=>{const t=l(e.selected,e.filteredElements),n=o(t,e);if("root"===t){const t=e.filteredElements.findIndex((t=>t.id===e.selected))+1;if(t<e.filteredElements.length)return e.filteredElements[t].id}if(n&&"submenu"===n.type){const t=n.elements.findIndex((t=>t.id===e.selected))+1;if(t<n.elements.length)return n.elements[t].id}})(e);return t?Object.assign(Object.assign({},e),{selected:t}):e},d=e=>{const t=(e=>{const t=l(e.selected,e.filteredElements),n=o(t,e);if("root"===t){const t=e.filteredElements.findIndex((t=>t.id===e.selected))-1;if(t>=0)return e.filteredElements[t].id}if(n&&"submenu"===n.type){const t=n.elements.findIndex((t=>t.id===e.selected))-1;if(t>=0)return n.elements[t].id}})(e);return t?Object.assign(Object.assign({},e),{selected:t}):e};var p;!function(e){e.open="open",e.close="close",e.execute="execute",e.nextItem="nextItem",e.prevItem="prevItem",e.openSubMenu="openSubMenu",e.closeSubMenu="closeSubMenu",e.inputChange="inputChange"}(p||(p={}));const m=new e("slash-menu-plugin"),f=(e,n,s,f)=>{const g={selected:e[0].id,open:!1,filter:"",ignoredKeys:n?[...i,...n]:i,filteredElements:e.filter((e=>!e.locked)),elements:e};if(r(g))throw new Error("Menu elements must have unique id's!");return new t({key:m,props:{handleKeyDown(e,t){const n=e.state,r=m.getState(n);if(!r)return!1;const l=((e,t,n,r,s,l)=>{const a=s||((e=!1)=>({shouldOpen:(t,n,r)=>{const s=r.state,o=s.selection.from<0||s.selection.from>s.doc.content.size?null:s.doc.resolve(s.selection.from),l=null==o?void 0:o.parent,a="paragraph"===(null==l?void 0:l.type.name),i=a&&2===(null==l?void 0:l.nodeSize),u=s.selection.$head.parentOffset,c=s.selection.$head.parent.textContent.slice(u-1,u),d=" "===c||""===c||" "===c;return!t.open&&"/"===n.key&&a&&(i||d||s.selection.from!==s.selection.to&&e)},shouldClose:(e,t)=>e.open&&("/"===t.key||"Escape"===t.key||"Backspace"===t.key)&&0===e.filter.length}))(l),i=o(e.selected,e);if(a.shouldOpen(e,t,n))return u.OpenMenu;if(a.shouldClose(e,t,n))return u.CloseMenu;if(e.open){if("ArrowDown"===t.key)return u.NextItem;if("ArrowUp"===t.key)return u.PrevItem;if("Enter"===t.key||"Tab"===t.key||"ArrowRight"===t.key&&"submenu"===(null==i?void 0:i.type))return u.Execute;if("Escape"===t.key||"Backspace"===t.key&&0===e.filter.length||"ArrowLeft"===t.key&&e.subMenuId)return u.CloseMenu;if(e.filter.length>0&&"Backspace"===t.key)return u.removeChar;if(!r.includes(t.key))return u.addChar;if("ArrowLeft"===t.key||"ArrowRight"===t.key)return u.Catch}return u.Ignore})(r,t,e,g.ignoredKeys,s,f);switch(l){case u.OpenMenu:return a(e,m,{type:p.open}),!0;case u.CloseMenu:{const{subMenuId:s}=r;if(s){const t=o(s,g),n=null==t?void 0:t.callbackOnClose;(null==t?void 0:t.locked)?a(e,m,{type:p.close}):(n&&n(),a(e,m,{type:p.closeSubMenu,element:o(s,g)}))}else"/"===t.key?e.dispatch(n.tr.insertText("/").setMeta(m,{type:p.close})):a(e,m,{type:p.close});return!0}case u.Execute:{const t=o(r.selected,r);return!!t&&("command"===t.type&&(a(e,m,{type:p.execute}),t.command(e)),"submenu"===t.type&&a(e,m,{type:p.openSubMenu,element:t}),!0)}case u.NextItem:return a(e,m,{type:p.nextItem}),!0;case u.PrevItem:return a(e,m,{type:p.prevItem}),!0;case u.addChar:return a(e,m,{type:p.inputChange,filter:r.filter+t.key}),!0;case u.removeChar:{const t=1===r.filter.length?"":r.filter.slice(0,-1);return a(e,m,{type:p.inputChange,filter:t}),!0}case u.Catch:return!0;default:return!1}}},state:{init:()=>g,apply(e,t){var n;const r=e.getMeta(m);switch(null==r?void 0:r.type){case p.open:return Object.assign(Object.assign({},g),{open:!0});case p.close:return(e=>{const t=e.callbackOnClose;return t&&t(),e})(g);case p.execute:return g;case p.openSubMenu:return((e,t)=>{const n=t.element;return"submenu"===(null==n?void 0:n.type)?Object.assign(Object.assign({},e),{open:!0,filteredElements:n.elements,selected:n.elements[0].id,subMenuId:n.id}):e})(t,r);case p.closeSubMenu:return((e,t,n)=>{const r=t.element,s=r.callbackOnClose;if(s&&s(),"submenu"===(null==r?void 0:r.type)){const t=l(r.id,n.filteredElements);if("root"===t)return Object.assign(Object.assign({},n),{open:!0});const s=o(t,n);return"submenu"!==(null==s?void 0:s.type)?e:Object.assign(Object.assign({},e),{filteredElements:s.elements,selected:s.elements[0].id,subMenuId:t})}return e})(t,r,g);case p.nextItem:return c(t);case p.prevItem:return d(t);case p.inputChange:{const e=r.filter?((e,t)=>{const n=new RegExp(`${t.toLowerCase().replace(/\s/g,"\\s")}`);if(e.subMenuId&&"root"!==e.subMenuId)return o(e.subMenuId,e).elements.filter((e=>null!==e.label.toLowerCase().match(n)));return e.elements.filter((e=>null!==e.label.toLowerCase().match(n)&&!e.locked))})(t,r.filter):g.elements,s=null===(n=null==e?void 0:e[0])||void 0===n?void 0:n.id;return Object.assign(Object.assign({},t),{selected:s||t.selected,filteredElements:e,filter:r.filter||""})}default:return t}}},initialState:g})};export{m as SlashMenuKey,f as SlashMenuPlugin,p as SlashMetaTypes,i as defaultIgnoredKeys,a as dispatchWithMeta,o as getElementById};
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var e=require("prosemirror-state");const t=e=>"submenu"===e.type?[e.id,...e.elements.map((e=>t(e)))].flat():[e.id],n=e=>{const n=(e=>e.filteredElements.map((e=>t(e))).flat())(e);return n.length!==new Set(n).size},s=e=>"submenu"===e.type?[e,...e.elements.map((e=>s(e)))].flat():[e],r=e=>e.elements.map((e=>s(e))).flat()
|
|
1
|
+
"use strict";var e=require("prosemirror-state");const t=e=>"submenu"===e.type?[e.id,...e.elements.map((e=>t(e)))].flat():[e.id],n=e=>{const n=(e=>e.filteredElements.map((e=>t(e))).flat())(e);return n.length!==new Set(n).size},s=e=>"submenu"===e.type?[e,...e.elements.map((e=>s(e)))].flat():[e],r=(e,t)=>(e=>e.elements.map((e=>s(e))).flat())(t).find((t=>t.id===e)),a=(e,t,n="root")=>{let s="root";return t.forEach((t=>{if("submenu"===t.type){t.id===e&&(s=n);const r=t.elements.map((e=>e.id));s=r.includes(e)?t.id:a(e,t.elements,t.id)}t.id===e&&(s=n)})),s},o=(e,t,n)=>e.dispatch(e.state.tr.setMeta(t,n)),l=["Unidentified","Alt","AltGraph","CapsLock","Control","Fn","FnLock","F1","F2","F3","F4","F5","F6","F7","F8","F9","F10","F11","F12","F13","F14","F15","F16","F17","F18","F19","F20","F21","F22","F23","F24","Hyper","Meta","NumLock","PageDown","PageUp","Pause","PrintScreen","Redo","ScrollLock","Shift","Super","Symbol","SymbolLock","Enter","Tab","ArrowDown","ArrowLeft","ArrowRight","ArrowUp","End","Home","PageDown","PageUp","Backspace","Clear","Copy","CrSel","Cut","Delete","EraseEof","ExSel","Insert","Paste","Redo","Undo","Accept","Again","Attn","Cancel","ContextMenu","Escape","Execute","Find","Finish","Help","Pause","Play","Props","Select","ZoomIn","ZoomOut","BrightnessDown","BrightnessUp","Eject","LogOff","Power","PowerOff","PrintScreen","Hibernate","Standby","WakeUp","AllCandidates","Alphanumeric","CodeInput","Compose","Convert","Dead","FinalMode","GroupFirst","GroupLast","GroupNext","GroupPrevious","ModeChange","NextCandidate","NonConvert","PreviousCandidate","Process","SingleCandidate","HangulMode","HanjaMode","JunjaMode","Eisu","Hankaku","Hiragana","HiraganaKatakana","KanaMode","KanjiMode","Katakana","Romaji","Zenkaku","ZenkakuHanaku"];var i;!function(e){e.OpenMenu="openMenu",e.CloseMenu="closeMenu",e.Execute="Execute",e.NextItem="NextItem",e.PrevItem="PrevItem",e.inputChange="InputChange",e.addChar="addChar",e.removeChar="removeChar",e.Ignore="Ignore",e.Catch="Catch"}(i||(i={}));const u=e=>{const t=(e=>{const t=a(e.selected,e.filteredElements),n=r(t,e);if("root"===t){const t=e.filteredElements.findIndex((t=>t.id===e.selected))+1;if(t<e.filteredElements.length)return e.filteredElements[t].id}if(n&&"submenu"===n.type){const t=n.elements.findIndex((t=>t.id===e.selected))+1;if(t<n.elements.length)return n.elements[t].id}})(e);return t?Object.assign(Object.assign({},e),{selected:t}):e},c=e=>{const t=(e=>{const t=a(e.selected,e.filteredElements),n=r(t,e);if("root"===t){const t=e.filteredElements.findIndex((t=>t.id===e.selected))-1;if(t>=0)return e.filteredElements[t].id}if(n&&"submenu"===n.type){const t=n.elements.findIndex((t=>t.id===e.selected))-1;if(t>=0)return n.elements[t].id}})(e);return t?Object.assign(Object.assign({},e),{selected:t}):e};var p;exports.SlashMetaTypes=void 0,(p=exports.SlashMetaTypes||(exports.SlashMetaTypes={})).open="open",p.close="close",p.execute="execute",p.nextItem="nextItem",p.prevItem="prevItem",p.openSubMenu="openSubMenu",p.closeSubMenu="closeSubMenu",p.inputChange="inputChange";const d=new e.PluginKey("slash-menu-plugin");exports.SlashMenuKey=d,exports.SlashMenuPlugin=(t,s,p,m)=>{const f={selected:t[0].id,open:!1,filter:"",ignoredKeys:s?[...l,...s]:l,filteredElements:t.filter((e=>!e.locked)),elements:t};if(n(f))throw new Error("Menu elements must have unique id's!");return new e.Plugin({key:d,props:{handleKeyDown(e,t){const n=e.state,s=d.getState(n);if(!s)return!1;const a=((e,t,n,s,a,o)=>{const l=a||((e=!1)=>({shouldOpen:(t,n,s)=>{const r=s.state,a=r.selection.from<0||r.selection.from>r.doc.content.size?null:r.doc.resolve(r.selection.from),o=null==a?void 0:a.parent,l="paragraph"===(null==o?void 0:o.type.name),i=l&&2===(null==o?void 0:o.nodeSize),u=r.selection.$head.parentOffset,c=r.selection.$head.parent.textContent.slice(u-1,u),p=" "===c||""===c||" "===c;return!t.open&&"/"===n.key&&l&&(i||p||r.selection.from!==r.selection.to&&e)},shouldClose:(e,t)=>e.open&&("/"===t.key||"Escape"===t.key||"Backspace"===t.key)&&0===e.filter.length}))(o),u=r(e.selected,e);if(l.shouldOpen(e,t,n))return i.OpenMenu;if(l.shouldClose(e,t,n))return i.CloseMenu;if(e.open){if("ArrowDown"===t.key)return i.NextItem;if("ArrowUp"===t.key)return i.PrevItem;if("Enter"===t.key||"Tab"===t.key||"ArrowRight"===t.key&&"submenu"===(null==u?void 0:u.type))return i.Execute;if("Escape"===t.key||"Backspace"===t.key&&0===e.filter.length||"ArrowLeft"===t.key&&e.subMenuId)return i.CloseMenu;if(e.filter.length>0&&"Backspace"===t.key)return i.removeChar;if(!s.includes(t.key))return i.addChar;if("ArrowLeft"===t.key||"ArrowRight"===t.key)return i.Catch}return i.Ignore})(s,t,e,f.ignoredKeys,p,m);switch(a){case i.OpenMenu:return o(e,d,{type:exports.SlashMetaTypes.open}),!0;case i.CloseMenu:{const{subMenuId:a}=s;if(a){const t=r(a,f),n=null==t?void 0:t.callbackOnClose;(null==t?void 0:t.locked)?o(e,d,{type:exports.SlashMetaTypes.close}):(n&&n(),o(e,d,{type:exports.SlashMetaTypes.closeSubMenu,element:r(a,f)}))}else"/"===t.key?e.dispatch(n.tr.insertText("/").setMeta(d,{type:exports.SlashMetaTypes.close})):o(e,d,{type:exports.SlashMetaTypes.close});return!0}case i.Execute:{const t=r(s.selected,s);return!!t&&("command"===t.type&&(o(e,d,{type:exports.SlashMetaTypes.execute}),t.command(e)),"submenu"===t.type&&o(e,d,{type:exports.SlashMetaTypes.openSubMenu,element:t}),!0)}case i.NextItem:return o(e,d,{type:exports.SlashMetaTypes.nextItem}),!0;case i.PrevItem:return o(e,d,{type:exports.SlashMetaTypes.prevItem}),!0;case i.addChar:return o(e,d,{type:exports.SlashMetaTypes.inputChange,filter:s.filter+t.key}),!0;case i.removeChar:{const t=1===s.filter.length?"":s.filter.slice(0,-1);return o(e,d,{type:exports.SlashMetaTypes.inputChange,filter:t}),!0}case i.Catch:return!0;default:return!1}}},state:{init:()=>f,apply(e,t){var n;const s=e.getMeta(d);switch(null==s?void 0:s.type){case exports.SlashMetaTypes.open:return Object.assign(Object.assign({},f),{open:!0});case exports.SlashMetaTypes.close:return(e=>{const t=e.callbackOnClose;return t&&t(),e})(f);case exports.SlashMetaTypes.execute:return f;case exports.SlashMetaTypes.openSubMenu:return((e,t)=>{const n=t.element;return"submenu"===(null==n?void 0:n.type)?Object.assign(Object.assign({},e),{open:!0,filteredElements:n.elements,selected:n.elements[0].id,subMenuId:n.id}):e})(t,s);case exports.SlashMetaTypes.closeSubMenu:return((e,t,n)=>{const s=t.element,o=s.callbackOnClose;if(o&&o(),"submenu"===(null==s?void 0:s.type)){const t=a(s.id,n.filteredElements);if("root"===t)return Object.assign(Object.assign({},n),{open:!0});const o=r(t,n);return"submenu"!==(null==o?void 0:o.type)?e:Object.assign(Object.assign({},e),{filteredElements:o.elements,selected:o.elements[0].id,subMenuId:t})}return e})(t,s,f);case exports.SlashMetaTypes.nextItem:return u(t);case exports.SlashMetaTypes.prevItem:return c(t);case exports.SlashMetaTypes.inputChange:{const e=s.filter?((e,t)=>{const n=new RegExp(`${t.toLowerCase().replace(/\s/g,"\\s")}`);if(e.subMenuId&&"root"!==e.subMenuId)return r(e.subMenuId,e).elements.filter((e=>null!==e.label.toLowerCase().match(n)));return e.elements.filter((e=>null!==e.label.toLowerCase().match(n)&&!e.locked))})(t,s.filter):f.elements,a=null===(n=null==e?void 0:e[0])||void 0===n?void 0:n.id;return Object.assign(Object.assign({},t),{selected:a||t.selected,filteredElements:e,filter:s.filter||""})}default:return t}}},initialState:f})},exports.defaultIgnoredKeys=l,exports.dispatchWithMeta=o,exports.getElementById=r;
|
package/dist/plugin.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { Plugin, PluginKey } from "prosemirror-state";
|
|
2
2
|
import { MenuElement, OpeningConditions, SlashMenuState } from "./types";
|
|
3
3
|
export declare const SlashMenuKey: PluginKey<SlashMenuState>;
|
|
4
|
-
export declare const SlashMenuPlugin: (menuElements: MenuElement[], ignoredKeys?: string[], customConditions?: OpeningConditions) => Plugin<SlashMenuState>;
|
|
4
|
+
export declare const SlashMenuPlugin: (menuElements: MenuElement[], ignoredKeys?: string[], customConditions?: OpeningConditions, openInSelection?: boolean) => Plugin<SlashMenuState>;
|
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { Plugin, PluginKey } from "prosemirror-state";
|
|
2
|
+
import { dispatchWithMeta, getElementById, getFilteredItems, hasDuplicateIds, defaultIgnoredKeys, } from "./utils";
|
|
3
|
+
import { getCase, SlashCases } from "./cases";
|
|
4
|
+
import { closeMenu, closeSubMenu, nextItem, openSubMenu, prevItem, } from "./actions";
|
|
5
|
+
import { SlashMetaTypes } from "./enums";
|
|
6
|
+
export const SlashMenuKey = new PluginKey("slash-menu-plugin");
|
|
7
|
+
export const SlashMenuPlugin = (menuElements, ignoredKeys, customConditions, openInSelection) => {
|
|
8
|
+
const initialState = {
|
|
9
|
+
selected: menuElements[0].id,
|
|
10
|
+
open: false,
|
|
11
|
+
filter: "",
|
|
12
|
+
ignoredKeys: ignoredKeys
|
|
13
|
+
? [...defaultIgnoredKeys, ...ignoredKeys]
|
|
14
|
+
: defaultIgnoredKeys,
|
|
15
|
+
filteredElements: menuElements.filter((element) => !element.locked),
|
|
16
|
+
elements: menuElements,
|
|
17
|
+
};
|
|
18
|
+
if (hasDuplicateIds(initialState)) {
|
|
19
|
+
throw new Error("Menu elements must have unique id's!");
|
|
20
|
+
}
|
|
21
|
+
return new Plugin({
|
|
22
|
+
key: SlashMenuKey,
|
|
23
|
+
props: {
|
|
24
|
+
handleKeyDown(view, event) {
|
|
25
|
+
const editorState = view.state;
|
|
26
|
+
const state = SlashMenuKey.getState(editorState);
|
|
27
|
+
if (!state)
|
|
28
|
+
return false;
|
|
29
|
+
const slashCase = getCase(state, event, view, initialState.ignoredKeys, customConditions, openInSelection);
|
|
30
|
+
switch (slashCase) {
|
|
31
|
+
case SlashCases.OpenMenu:
|
|
32
|
+
dispatchWithMeta(view, SlashMenuKey, { type: SlashMetaTypes.open });
|
|
33
|
+
return true;
|
|
34
|
+
case SlashCases.CloseMenu: {
|
|
35
|
+
const { subMenuId } = state;
|
|
36
|
+
if (subMenuId) {
|
|
37
|
+
const submenu = getElementById(subMenuId, initialState);
|
|
38
|
+
const callback = submenu === null || submenu === void 0 ? void 0 : submenu.callbackOnClose;
|
|
39
|
+
if (!(submenu === null || submenu === void 0 ? void 0 : submenu.locked)) {
|
|
40
|
+
if (callback) {
|
|
41
|
+
callback();
|
|
42
|
+
}
|
|
43
|
+
dispatchWithMeta(view, SlashMenuKey, {
|
|
44
|
+
type: SlashMetaTypes.closeSubMenu,
|
|
45
|
+
element: getElementById(subMenuId, initialState),
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
else
|
|
49
|
+
dispatchWithMeta(view, SlashMenuKey, {
|
|
50
|
+
type: SlashMetaTypes.close,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
else if (event.key === "/") {
|
|
54
|
+
view.dispatch(editorState.tr.insertText("/").setMeta(SlashMenuKey, {
|
|
55
|
+
type: SlashMetaTypes.close,
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
else
|
|
59
|
+
dispatchWithMeta(view, SlashMenuKey, {
|
|
60
|
+
type: SlashMetaTypes.close,
|
|
61
|
+
});
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
case SlashCases.Execute: {
|
|
65
|
+
const menuElement = getElementById(state.selected, state);
|
|
66
|
+
if (!menuElement)
|
|
67
|
+
return false;
|
|
68
|
+
if (menuElement.type === "command") {
|
|
69
|
+
dispatchWithMeta(view, SlashMenuKey, {
|
|
70
|
+
type: SlashMetaTypes.execute,
|
|
71
|
+
});
|
|
72
|
+
menuElement.command(view);
|
|
73
|
+
}
|
|
74
|
+
if (menuElement.type === "submenu") {
|
|
75
|
+
dispatchWithMeta(view, SlashMenuKey, {
|
|
76
|
+
type: SlashMetaTypes.openSubMenu,
|
|
77
|
+
element: menuElement,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
case SlashCases.NextItem:
|
|
83
|
+
dispatchWithMeta(view, SlashMenuKey, {
|
|
84
|
+
type: SlashMetaTypes.nextItem,
|
|
85
|
+
});
|
|
86
|
+
return true;
|
|
87
|
+
case SlashCases.PrevItem:
|
|
88
|
+
dispatchWithMeta(view, SlashMenuKey, {
|
|
89
|
+
type: SlashMetaTypes.prevItem,
|
|
90
|
+
});
|
|
91
|
+
return true;
|
|
92
|
+
case SlashCases.addChar: {
|
|
93
|
+
dispatchWithMeta(view, SlashMenuKey, {
|
|
94
|
+
type: SlashMetaTypes.inputChange,
|
|
95
|
+
filter: state.filter + event.key,
|
|
96
|
+
});
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
case SlashCases.removeChar: {
|
|
100
|
+
const newFilter = state.filter.length === 1 ? "" : state.filter.slice(0, -1);
|
|
101
|
+
dispatchWithMeta(view, SlashMenuKey, {
|
|
102
|
+
type: SlashMetaTypes.inputChange,
|
|
103
|
+
filter: newFilter,
|
|
104
|
+
});
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
case SlashCases.Catch: {
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
default:
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
state: {
|
|
116
|
+
init() {
|
|
117
|
+
return initialState;
|
|
118
|
+
},
|
|
119
|
+
apply(tr, state) {
|
|
120
|
+
var _a;
|
|
121
|
+
const meta = tr.getMeta(SlashMenuKey);
|
|
122
|
+
switch (meta === null || meta === void 0 ? void 0 : meta.type) {
|
|
123
|
+
case SlashMetaTypes.open:
|
|
124
|
+
return Object.assign(Object.assign({}, initialState), { open: true });
|
|
125
|
+
case SlashMetaTypes.close:
|
|
126
|
+
return closeMenu(initialState);
|
|
127
|
+
case SlashMetaTypes.execute:
|
|
128
|
+
return initialState;
|
|
129
|
+
case SlashMetaTypes.openSubMenu:
|
|
130
|
+
return openSubMenu(state, meta);
|
|
131
|
+
case SlashMetaTypes.closeSubMenu:
|
|
132
|
+
return closeSubMenu(state, meta, initialState);
|
|
133
|
+
case SlashMetaTypes.nextItem:
|
|
134
|
+
return nextItem(state);
|
|
135
|
+
case SlashMetaTypes.prevItem:
|
|
136
|
+
return prevItem(state);
|
|
137
|
+
case SlashMetaTypes.inputChange: {
|
|
138
|
+
const newElements = meta.filter
|
|
139
|
+
? getFilteredItems(state, meta.filter)
|
|
140
|
+
: initialState.elements;
|
|
141
|
+
const selectedId = (_a = newElements === null || newElements === void 0 ? void 0 : newElements[0]) === null || _a === void 0 ? void 0 : _a.id;
|
|
142
|
+
return Object.assign(Object.assign({}, state), { selected: selectedId || state.selected, filteredElements: newElements, filter: meta.filter || "" });
|
|
143
|
+
}
|
|
144
|
+
default:
|
|
145
|
+
return state;
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
initialState,
|
|
150
|
+
});
|
|
151
|
+
};
|
package/dist/types.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export type MenuItem = {
|
|
|
7
7
|
label: string;
|
|
8
8
|
type: ItemType;
|
|
9
9
|
available: (view: EditorView) => boolean;
|
|
10
|
+
locked?: boolean;
|
|
10
11
|
};
|
|
11
12
|
export interface CommandItem extends MenuItem {
|
|
12
13
|
type: "command";
|
|
@@ -16,6 +17,7 @@ export type MenuElement = CommandItem | SubMenu;
|
|
|
16
17
|
export interface SubMenu extends MenuItem {
|
|
17
18
|
type: "submenu";
|
|
18
19
|
elements: MenuElement[];
|
|
20
|
+
callbackOnClose?: () => void;
|
|
19
21
|
}
|
|
20
22
|
export type SlashMenuState = {
|
|
21
23
|
selected: ItemId;
|
|
@@ -25,6 +27,7 @@ export type SlashMenuState = {
|
|
|
25
27
|
filter: string;
|
|
26
28
|
elements: MenuElement[];
|
|
27
29
|
ignoredKeys: string[];
|
|
30
|
+
callbackOnClose?: () => void;
|
|
28
31
|
};
|
|
29
32
|
export interface SlashMenuMeta {
|
|
30
33
|
type: SlashMetaTypes;
|
package/package.json
CHANGED
|
@@ -1,25 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prosemirror-slash-menu",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Slash menu for ProseMirror",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.es.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
|
-
"scripts": {
|
|
9
|
-
"prebuild": "rimraf dist",
|
|
10
|
-
"build": "rollup -c --bundleConfigAsCjs",
|
|
11
|
-
"dev": "rollup -c -w --bundleConfigAsCjs",
|
|
12
|
-
"yalc:watch": "nodemon --watch dist --exec 'yalc push'",
|
|
13
|
-
"dev:watch": "npm-run-all --parallel dev yalc:watch",
|
|
14
|
-
"format": "eslint src --ext .ts --fix",
|
|
15
|
-
"prepublishOnly": "npm run build && npm test && npm run lint",
|
|
16
|
-
"version": "npm run format && git add -A src",
|
|
17
|
-
"postversion": "git push && git push --tags",
|
|
18
|
-
"lint": "tsc --noEmit && eslint src --ext .ts",
|
|
19
|
-
"test": "echo \"no test specified\" && exit 0",
|
|
20
|
-
"upgrade-interactive": "npm-check --update",
|
|
21
|
-
"publish:np": "np"
|
|
22
|
-
},
|
|
23
8
|
"repository": {
|
|
24
9
|
"type": "git",
|
|
25
10
|
"url": "git+https://github.com/emergence-engineering/prosemirror-slash-menu.git"
|
|
@@ -62,10 +47,26 @@
|
|
|
62
47
|
"rollup-plugin-minification": "^0.2.0",
|
|
63
48
|
"rollup-plugin-peer-deps-external": "^2.2.4",
|
|
64
49
|
"rollup-plugin-typescript2": "^0.34.1",
|
|
65
|
-
"typescript": "^5.1.
|
|
50
|
+
"typescript": "^5.1.6"
|
|
66
51
|
},
|
|
67
52
|
"engines": {
|
|
68
53
|
"node": ">=12",
|
|
69
54
|
"npm": ">=7"
|
|
55
|
+
},
|
|
56
|
+
"scripts": {
|
|
57
|
+
"preinstall": "npx only-allow pnpm",
|
|
58
|
+
"prebuild": "rimraf dist",
|
|
59
|
+
"build": "rollup -c --bundleConfigAsCjs",
|
|
60
|
+
"rebuild": "rollup -c --bundleConfigAsCjs",
|
|
61
|
+
"dev": "rollup -c -w --bundleConfigAsCjs",
|
|
62
|
+
"yalc:watch": "nodemon --watch dist --exec 'yalc push'",
|
|
63
|
+
"dev:watch": "pnpm-run-all --parallel dev yalc:watch",
|
|
64
|
+
"format": "eslint src --ext .ts --fix",
|
|
65
|
+
"version": "pnpm run format && git add -A src",
|
|
66
|
+
"postversion": "git push && git push --tags",
|
|
67
|
+
"lint": "tsc --noEmit && eslint src --ext .ts",
|
|
68
|
+
"test": "echo \"no test specified\" && exit 0",
|
|
69
|
+
"upgrade-interactive": "npm-check --update",
|
|
70
|
+
"publish:np": "np"
|
|
70
71
|
}
|
|
71
|
-
}
|
|
72
|
+
}
|