Skip to content

Commit

Permalink
Merge branch 'master' into u/jisong/shadoweditselection
Browse files Browse the repository at this point in the history
  • Loading branch information
JiuqingSong committed Apr 4, 2024
2 parents 53e94b0 + f34b360 commit 0213a45
Show file tree
Hide file tree
Showing 18 changed files with 2,618 additions and 1,491 deletions.
9 changes: 2 additions & 7 deletions demo/scripts/controlsV2/mainPane/MainPane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -475,15 +475,10 @@ export class MainPane extends React.Component<{}, MainPaneState> {
imageMenu,
watermarkText,
markdownOptions,
autoFormatOptions,
} = this.state.initState;
return [
pluginList.autoFormat &&
new AutoFormatPlugin({
autoBullet: true,
autoNumbering: true,
autoUnlink: true,
autoLink: true,
}),
pluginList.autoFormat && new AutoFormatPlugin(autoFormatOptions),
pluginList.edit && new EditPlugin(),
pluginList.paste && new PastePlugin(allowExcelNoBorderTable),
pluginList.shortcut && new ShortcutPlugin(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const initialState: OptionState = {
autoLink: true,
autoNumbering: true,
autoUnlink: false,
autoHyphen: true,
},
markdownOptions: {
bold: true,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { createLink } from './link/createLink';
import { createLinkAfterSpace } from './link/createLinkAfterSpace';
import { formatTextSegmentBeforeSelectionMarker } from '../pluginUtils/formatTextSegmentBeforeSelectionMarker';
import { keyboardListTrigger } from './list/keyboardListTrigger';
import { transformHyphen } from './hyphen/transformHyphen';
import { unlink } from './link/unlink';
import type {
ContentChangedEvent,
Expand Down Expand Up @@ -34,6 +36,11 @@ export type AutoFormatOptions = {
* When paste content, create hyperlink for the pasted link
*/
autoLink: boolean;

/**
* Transform -- into hyphen, if typed between two words
*/
autoHyphen: boolean;
};

/**
Expand All @@ -44,6 +51,7 @@ const DefaultOptions: Required<AutoFormatOptions> = {
autoNumbering: false,
autoUnlink: false,
autoLink: false,
autoHyphen: false,
};

/**
Expand All @@ -52,13 +60,13 @@ const DefaultOptions: Required<AutoFormatOptions> = {
*/
export class AutoFormatPlugin implements EditorPlugin {
private editor: IEditor | null = null;

/**
* @param options An optional parameter that takes in an object of type AutoFormatOptions, which includes the following properties:
* - autoBullet: A boolean that enables or disables automatic bullet list formatting. Defaults to false.
* - autoNumbering: A boolean that enables or disables automatic numbering formatting. Defaults to false.
* - autoLink: A boolean that enables or disables automatic hyperlink creation when pasting or typing content. Defaults to false.
* - autoUnlink: A boolean that enables or disables automatic hyperlink removal when pressing backspace. Defaults to false.
* - autoHyphen: A boolean that enables or disables automatic hyphen transformation. Defaults to false.
*/
constructor(private options: AutoFormatOptions = DefaultOptions) {}

Expand Down Expand Up @@ -112,14 +120,52 @@ export class AutoFormatPlugin implements EditorPlugin {

private handleEditorInputEvent(editor: IEditor, event: EditorInputEvent) {
const rawEvent = event.rawEvent;
if (rawEvent.inputType === 'insertText') {
const selection = editor.getDOMSelection();
if (
rawEvent.inputType === 'insertText' &&
selection &&
selection.type === 'range' &&
selection.range.collapsed
) {
switch (rawEvent.data) {
case ' ':
const { autoBullet, autoNumbering, autoLink } = this.options;
keyboardListTrigger(editor, autoBullet, autoNumbering);
if (autoLink) {
createLinkAfterSpace(editor);
}
formatTextSegmentBeforeSelectionMarker(
editor,
(model, previousSegment, paragraph, context) => {
const {
autoBullet,
autoNumbering,
autoLink,
autoHyphen,
} = this.options;
let shouldHyphen = false;
let shouldLink = false;

if (autoLink) {
shouldLink = createLinkAfterSpace(
previousSegment,
paragraph,
context
);
}

if (autoHyphen) {
shouldHyphen = transformHyphen(previousSegment, paragraph, context);
}

return (
keyboardListTrigger(
model,
paragraph,
context,
autoBullet,
autoNumbering
) ||
shouldHyphen ||
shouldLink
);
}
);
break;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { splitTextSegment } from '../../pluginUtils/splitTextSegment';
import type {
ContentModelParagraph,
ContentModelText,
FormatContentModelContext,
} from 'roosterjs-content-model-types';

/**
* @internal
*/
export function transformHyphen(
previousSegment: ContentModelText,
paragraph: ContentModelParagraph,
context: FormatContentModelContext
): boolean {
const segments = previousSegment.text.split(' ');
const dashes = segments[segments.length - 2];
if (dashes === '--') {
const textIndex = previousSegment.text.lastIndexOf('--');
const textSegment = splitTextSegment(previousSegment, paragraph, textIndex, textIndex + 2);

textSegment.text = textSegment.text.replace('--', '—');
context.canUndoByBackspace = true;
return true;
} else {
const text = segments.pop();
const hasDashes = text && text?.indexOf('--') > -1;
if (hasDashes && text.trim() !== '--') {
const textIndex = previousSegment.text.indexOf(text);
const textSegment = splitTextSegment(
previousSegment,
paragraph,
textIndex,
textIndex + text.length - 1
);

const textLength = textSegment.text.length;
if (textSegment.text[0] !== '-' && textSegment.text[textLength - 1] !== '-') {
textSegment.text = textSegment.text.replace('--', '—');
context.canUndoByBackspace = true;
return true;
}
}
}
return false;
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import { addLink } from 'roosterjs-content-model-dom';
import { getLinkSegment } from './getLinkSegment';
import type { IEditor } from 'roosterjs-content-model-types';
import { formatTextSegmentBeforeSelectionMarker } from '../../pluginUtils/formatTextSegmentBeforeSelectionMarker';
import { matchLink } from 'roosterjs-content-model-api';
import type { IEditor, LinkData } from 'roosterjs-content-model-types';

/**
* @internal
*/
export function createLink(editor: IEditor) {
editor.formatContentModel(model => {
const link = getLinkSegment(model);
if (link && !link.link) {
addLink(link, {
formatTextSegmentBeforeSelectionMarker(editor, (_model, linkSegment, _paragraph) => {
let linkData: LinkData | null = null;
if (!linkSegment.link && (linkData = matchLink(linkSegment.text))) {
addLink(linkSegment, {
format: {
href: link.text,
href: linkData.normalizedUrl,
underline: true,
},
dataset: {},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,57 +1,41 @@
import { getSelectedSegmentsAndParagraphs } from 'roosterjs-content-model-dom';
import { matchLink } from 'roosterjs-content-model-api';
import { splitTextSegment } from '../../pluginUtils/splitTextSegment';
import type { IEditor, LinkData } from 'roosterjs-content-model-types';
import type {
ContentModelParagraph,
ContentModelText,
FormatContentModelContext,
LinkData,
} from 'roosterjs-content-model-types';

/**
* @internal
*/
export function createLinkAfterSpace(editor: IEditor) {
editor.formatContentModel((model, context) => {
const selectedSegmentsAndParagraphs = getSelectedSegmentsAndParagraphs(
model,
false /* includingFormatHolder */
export function createLinkAfterSpace(
previousSegment: ContentModelText,
paragraph: ContentModelParagraph,
context: FormatContentModelContext
) {
const link = previousSegment.text.split(' ').pop();
const url = link?.trim();
let linkData: LinkData | null = null;
if (url && link && (linkData = matchLink(url))) {
const linkSegment = splitTextSegment(
previousSegment,
paragraph,
previousSegment.text.length - link.trimLeft().length,
previousSegment.text.trimRight().length
);
if (selectedSegmentsAndParagraphs.length > 0 && selectedSegmentsAndParagraphs[0][1]) {
const markerIndex = selectedSegmentsAndParagraphs[0][1].segments.findIndex(
segment => segment.segmentType == 'SelectionMarker'
);
const paragraph = selectedSegmentsAndParagraphs[0][1];
if (markerIndex > 0) {
const textSegment = paragraph.segments[markerIndex - 1];
const marker = paragraph.segments[markerIndex];
if (
marker.segmentType == 'SelectionMarker' &&
textSegment &&
textSegment.segmentType == 'Text' &&
!textSegment.link
) {
const link = textSegment.text.split(' ').pop();
const url = link?.trim();
let linkData: LinkData | null = null;
if (url && link && (linkData = matchLink(url))) {
const linkSegment = splitTextSegment(
textSegment,
paragraph,
textSegment.text.length - link.trimLeft().length,
textSegment.text.trimRight().length
);
linkSegment.link = {
format: {
href: linkData.normalizedUrl,
underline: true,
},
dataset: {},
};
linkSegment.link = {
format: {
href: linkData.normalizedUrl,
underline: true,
},
dataset: {},
};

context.canUndoByBackspace = true;
context.canUndoByBackspace = true;

return true;
}
}
}
}

return false;
});
return true;
}
return false;
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { getLinkSegment } from './getLinkSegment';
import { formatTextSegmentBeforeSelectionMarker } from '../../pluginUtils/formatTextSegmentBeforeSelectionMarker';

import type { IEditor } from 'roosterjs-content-model-types';

/**
* @internal
*/
export function unlink(editor: IEditor, rawEvent: KeyboardEvent) {
editor.formatContentModel(model => {
const link = getLinkSegment(model);
if (link?.link) {
link.link = undefined;
formatTextSegmentBeforeSelectionMarker(editor, (_model, linkSegment, _paragraph) => {
if (linkSegment?.link) {
linkSegment.link = undefined;
rawEvent.preventDefault();

return true;
}

return false;
});
}
Loading

0 comments on commit 0213a45

Please sign in to comment.