Skip to content

Commit

Permalink
feat: add suggestions rules to extensions
Browse files Browse the repository at this point in the history
And also the extension manager
  • Loading branch information
ifiokjr committed Sep 19, 2019
1 parent 143d878 commit c982cc6
Show file tree
Hide file tree
Showing 15 changed files with 82 additions and 19 deletions.
7 changes: 7 additions & 0 deletions @remirror/core-types/src/core-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,13 @@ export interface ExcludeOptions {
* @default false
*/
ssr?: boolean;

/**
* Whether to include the suggestions plugin configuration for the extension.
*
* @default false
*/
suggesters?: boolean;
}

export interface SSRComponentParams {
Expand Down
1 change: 1 addition & 0 deletions @remirror/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"dependencies": {
"@babel/runtime": "^7.6.0",
"@remirror/core-constants": "0.4.2-canary.2",
"prosemirror-suggest": "0.4.2-canary.2",
"@remirror/core-helpers": "0.4.2-canary.2",
"@remirror/core-types": "0.4.2-canary.2",
"@remirror/core-utils": "0.4.2-canary.2",
Expand Down
1 change: 1 addition & 0 deletions @remirror/core/src/extension-manager.helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ type ExtensionMethodProperties =
| 'styles'
| 'nodeView'
| 'extensionData'
| 'suggesters'
| 'isActive'
| 'isEnabled';

Expand Down
19 changes: 19 additions & 0 deletions @remirror/core/src/extension-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { InputRule, inputRules } from 'prosemirror-inputrules';
import { keymap } from 'prosemirror-keymap';
import { Schema } from 'prosemirror-model';
import { EditorState } from 'prosemirror-state';
import { suggest, Suggester } from 'prosemirror-suggest';
import { ComponentType } from 'react';
import { isMarkExtension, isNodeExtension } from './extension-helpers';
import {
Expand Down Expand Up @@ -71,6 +72,7 @@ export interface ExtensionManagerData<
keymaps: ProsemirrorPlugin[];
inputRules: ProsemirrorPlugin;
pasteRules: ProsemirrorPlugin[];
suggesters: Suggester[];
actions: GActions;
helpers: GHelpers;
view: EditorView<EditorSchema<GNodes, GMarks>>;
Expand Down Expand Up @@ -207,6 +209,7 @@ export class ExtensionManager<GExtension extends AnyExtension = any>
this.initData.keymaps = this.keymaps();
this.initData.inputRules = this.inputRules();
this.initData.pasteRules = this.pasteRules();
this.initData.suggesters = this.suggesters();
this.initData.isActive = this.commandStatusCheck('isActive');
this.initData.isEnabled = this.commandStatusCheck('isEnabled');

Expand Down Expand Up @@ -713,6 +716,22 @@ export class ExtensionManager<GExtension extends AnyExtension = any>
return extensionStyles;
}

private suggesters() {
this.checkInitialized();
const suggesters: Suggester[] = [];

const extensionSuggesters = this.extensions
.filter(hasExtensionProperty('suggesters'))
.filter(extension => !extension.options.exclude.suggesters)
.map(extensionPropertyMapper('suggesters', this.params)) as Suggester[][];

extensionSuggesters.forEach(suggester => {
suggesters.push(...suggester);
});

return suggest(...suggesters);
}

/**
* `Extensions`
*
Expand Down
8 changes: 8 additions & 0 deletions @remirror/core/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
} from '@remirror/core-types';
import { InputRule } from 'prosemirror-inputrules';
import { PluginKey } from 'prosemirror-state';
import { Suggester } from 'prosemirror-suggest';

/**
* These are the default options merged into every extension.
Expand All @@ -37,6 +38,7 @@ const defaultOptions: Required<BaseExtensionOptions> = {
attributes: false,
nodeView: false,
ssr: false,
suggesters: false,
},
};

Expand Down Expand Up @@ -510,4 +512,10 @@ export interface Extension<GOptions extends BaseExtensionOptions = BaseExtension
* @param params - extension manager parameters
*/
styles?(params: ExtensionManagerParams): Interpolation;

/**
* Create suggestions which respond to character key combinations within the
* editor instance.
*/
suggesters?(params: ExtensionManagerTypeParams<GType>): Suggester[];
}
1 change: 1 addition & 0 deletions @remirror/react/src/components/remirror-component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,7 @@ export class Remirror<GExtension extends AnyExtension = any> extends PureCompone
*/
private dispatchTransaction = (tr: Transaction) => {
tr = this.props.onDispatchTransaction(tr, this.getState()) || tr;

const state = this.getState().apply(tr);

this.updateState({
Expand Down
6 changes: 6 additions & 0 deletions packages/multishift/src/multishift-constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ export const Type = {
* filtering potential options.
*/
ComboBox: 'combobox',

/**
* A menu rendered without a toggleButton, combobox or input element. It is up
* to you to provide the input value and manage the focus.
*/
ControlledMenu: 'controlled-menu',
} as const;

export type DropdownType = Value<typeof Type>;
Expand Down
10 changes: 10 additions & 0 deletions packages/multishift/src/multishift-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -806,10 +806,20 @@ export interface MultishiftFocusHelpers {
focusToggleButton(): void;
}

export interface MultishiftStateHelpers<GItem = any> {
addItems: (itemsToAdd: GItem[]) => any[];
addItem: (itemToAdd: GItem) => any[];
removeItems: (itemsToRemove: GItem[]) => GItem[];
removeItem: (itemToRemove: GItem) => GItem[];
toggleItems: (itemsToToggle: GItem[]) => GItem[];
toggleItem: (itemToToggle: GItem) => GItem[];
}

export interface MultishiftReturn<GItem = any>
extends MultishiftState<GItem>,
MultishiftPropGetters<GItem>,
MultishiftDispatchActions<GItem>,
MultishiftStateHelpers<GItem>,
MultishiftHelpers,
MultishiftFocusHelpers {
/**
Expand Down
2 changes: 1 addition & 1 deletion packages/multishift/src/multishift-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1086,7 +1086,7 @@ export const omitUnchangedState = <GItem = any>(
};

/**
* An object of helpers for manipulating the state.
* Helpers for transforming the state object.
*/
export const createStateHelpers = <GItem = any>(
{ getItemId = defaultGetItemId, multiple }: MultishiftProps<GItem>,
Expand Down
13 changes: 11 additions & 2 deletions packages/multishift/src/multishift.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
checkItemHighlighted,
createItemClickPayload,
createKeyDownPayload,
createStateHelpers,
defaultGetItemId,
getItemIndex,
getKeyName,
Expand Down Expand Up @@ -146,7 +147,7 @@ export const useMultishift = <GItem = any>(props: MultishiftProps<GItem>): Multi
if (isOpen) {
if (type === Type.ComboBox && refs.input.current) {
refs.input.current.focus();
} else if (refs.menu.current) {
} else if (refs.menu.current && type !== Type.ControlledMenu) {
refs.menu.current.focus();
}
}
Expand All @@ -158,7 +159,7 @@ export const useMultishift = <GItem = any>(props: MultishiftProps<GItem>): Multi
if (isOpen) {
if (type === Type.ComboBox && refs.input.current) {
refs.input.current.focus();
} else if (refs.menu.current) {
} else if (refs.menu.current && type !== Type.ControlledMenu) {
refs.menu.current.focus();
}
} else if (!isOpen && document.activeElement === refs.menu.current) {
Expand Down Expand Up @@ -363,6 +364,9 @@ export const useMultishift = <GItem = any>(props: MultishiftProps<GItem>): Multi
...rest
}: GetPropsWithRefOptions<GElement, GRefKey> = { refKey: 'ref' as GRefKey },
): GetPropsWithRefReturn<GElement, GRefKey> => {
if (type === Type.ControlledMenu) {
throw new Error('The toggle button props should not be used for the controlled menu');
}
const isInternalEvent = <GEvent extends SyntheticEvent = any>(event: GEvent) =>
[refs.input.current, refs.menu.current, ...refs.items.current].some(
node => node && isOrContainsNode(node, event.target as Node),
Expand Down Expand Up @@ -560,6 +564,8 @@ export const useMultishift = <GItem = any>(props: MultishiftProps<GItem>): Multi
[],
);

const stateHelpers = createStateHelpers(props, state);

return {
// State
highlightedIndexes,
Expand All @@ -571,6 +577,9 @@ export const useMultishift = <GItem = any>(props: MultishiftProps<GItem>): Multi
isOpen,
jumpText,

// StateHelpers
...stateHelpers,

// Data
mostRecentHighlightedIndex,

Expand Down
5 changes: 4 additions & 1 deletion packages/prosemirror-suggest/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@
"types": "lib/index.d.ts",
"dependencies": {
"@babel/runtime": "^7.6.0",
"@remirror/core": "0.4.2-canary.2",
"@remirror/core-helpers": "0.4.2-canary.2",
"@remirror/core-types": "0.4.2-canary.2",
"@remirror/core-constants": "0.4.2-canary.2",
"@remirror/core-utils": "0.4.2-canary.2",
"@types/prosemirror-keymap": "^1.0.1",
"@types/prosemirror-state": "^1.2.3",
"@types/prosemirror-view": "^1.9.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ test('`onChange` and `onExit` handlers are called', () => {
}),
onChange: jest.fn(),
};
const plugin = suggest([{ char: '@', name: 'at', ...handlers, immediateMatch: true }]);
const plugin = suggest({ char: '@', name: 'at', ...handlers, immediateMatch: true });
const editor = createEditor(doc(p('<cursor>')), { plugins: [plugin] }).insertText('@');
expect(handlers.onChange).toHaveBeenCalledTimes(1);

Expand All @@ -23,7 +23,7 @@ test('`onChange` not called for character when immediateMatch is false', () => {
const handlers = {
onChange: jest.fn(),
};
const plugin = suggest([{ char: '@', name: 'at', ...handlers, immediateMatch: false }]);
const plugin = suggest({ char: '@', name: 'at', ...handlers, immediateMatch: false });
createEditor(doc(p('<cursor>')), { plugins: [plugin] }).insertText('@');
expect(handlers.onChange).not.toHaveBeenCalled();
});
10 changes: 6 additions & 4 deletions packages/prosemirror-suggest/src/suggest-plugin.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { noop } from '@remirror/core-helpers';
import {
CompareStateParams,
EditorSchema,
EditorState,
EditorStateParams,
EditorView,
FromToParams,
noop,
ResolvedPosParams,
TextParams,
transactionChanged,
TransactionParams,
} from '@remirror/core';
} from '@remirror/core-types';
import { transactionChanged } from '@remirror/core-utils';
import { Plugin, PluginKey } from 'prosemirror-state';
import { Decoration, DecorationSet } from 'prosemirror-view';
import { ChangeReason, ExitReason } from './suggest-constants';
Expand Down Expand Up @@ -105,6 +105,8 @@ export class SuggestState<GSchema extends EditorSchema = any> {
: 'new';
}

// TODO check for duplicate names and characters and log warnings when these
// are found.
constructor(suggesters: Suggester[]) {
this.suggesters = suggesters.map(suggester => ({ ...DEFAULT_SUGGESTER, ...suggester }));
}
Expand Down Expand Up @@ -309,7 +311,7 @@ export const suggestPluginKey = new PluginKey('suggest');
* This creates the plugin that manages the suggestions to offer when the
* MentionExtension is active.
*/
export const suggest = (suggesters: Suggester[]) => {
export const suggest = (...suggesters: Suggester[]) => {
const pluginState = SuggestState.create(suggesters);

return new Plugin<SuggestState, EditorSchema>({
Expand Down
2 changes: 1 addition & 1 deletion packages/prosemirror-suggest/src/suggest-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
FromToParams,
MakeRequired,
MarkTypeParams,
} from '@remirror/core';
} from '@remirror/core-types';
import { ChangeReason, ExitReason } from './suggest-constants';

export interface Suggester<GSchema extends EditorSchema = any> extends OptionalSuggestMatcher {
Expand Down
12 changes: 4 additions & 8 deletions packages/prosemirror-suggest/src/suggest-utils.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
import { NULL_CHARACTER } from '@remirror/core-constants';
import { bool, findMatches, isRegExp, isString, noop } from '@remirror/core-helpers';
import {
bool,
CommandFunction,
EditorStateParams,
findMatches,
isRegExp,
isString,
MakeOptional,
noop,
NULL_CHARACTER,
ResolvedPosParams,
selectionEmpty,
SelectionParams,
TextParams,
} from '@remirror/core';
} from '@remirror/core-types';
import { selectionEmpty } from '@remirror/core-utils';
import { keydownHandler } from 'prosemirror-keymap';
import { ChangeReason, ExitReason } from './suggest-constants';
import {
Expand Down

0 comments on commit c982cc6

Please sign in to comment.