Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
lassespilling committed Jun 18, 2024
1 parent 91aa93f commit 721d2ae
Showing 1 changed file with 90 additions and 87 deletions.
177 changes: 90 additions & 87 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ React-Quill
https://github.com/zenoamaro/react-quill
*/

import React from 'react';
import ReactDOM from 'react-dom';
import React, { createRef } from 'react';
import isEqual from 'lodash/isEqual';

import Quill, {
Expand All @@ -23,49 +22,49 @@ namespace ReactQuill {
export type Range = RangeStatic | null;

export interface QuillOptions extends QuillOptionsStatic {
tabIndex?: number,
tabIndex?: number;
}

export interface ReactQuillProps {
bounds?: string | HTMLElement,
children?: React.ReactElement<any>,
className?: string,
defaultValue?: Value,
formats?: string[],
id?: string,
modules?: StringMap,
bounds?: string | HTMLElement;
children?: React.ReactElement<any>;
className?: string;
defaultValue?: Value;
formats?: string[];
id?: string;
modules?: StringMap;
onChange?(
value: string,
delta: DeltaStatic,
source: Sources,
editor: UnprivilegedEditor,
): void,
editor: UnprivilegedEditor
): void;
onChangeSelection?(
selection: Range,
source: Sources,
editor: UnprivilegedEditor,
): void,
editor: UnprivilegedEditor
): void;
onFocus?(
selection: Range,
source: Sources,
editor: UnprivilegedEditor,
): void,
editor: UnprivilegedEditor
): void;
onBlur?(
previousSelection: Range,
source: Sources,
editor: UnprivilegedEditor,
): void,
onKeyDown?: React.EventHandler<any>,
onKeyPress?: React.EventHandler<any>,
onKeyUp?: React.EventHandler<any>,
placeholder?: string,
preserveWhitespace?: boolean,
readOnly?: boolean,
scrollingContainer?: string | HTMLElement,
style?: React.CSSProperties,
tabIndex?: number,
theme?: string,
value?: Value,
editor: UnprivilegedEditor
): void;
onKeyDown?: React.EventHandler<any>;
onKeyPress?: React.EventHandler<any>;
onKeyUp?: React.EventHandler<any>;
placeholder?: string;
preserveWhitespace?: boolean;
readOnly?: boolean;
scrollingContainer?: string | HTMLElement;
style?: React.CSSProperties;
tabIndex?: number;
theme?: string;
value?: Value;
}

export interface UnprivilegedEditor {
Expand All @@ -86,12 +85,13 @@ import ReactQuillProps = ReactQuill.ReactQuillProps;
import UnprivilegedEditor = ReactQuill.UnprivilegedEditor;

interface ReactQuillState {
generation: number,
generation: number;
}

class ReactQuill extends React.Component<ReactQuillProps, ReactQuillState> {
editingAreaRef = createRef<any>();

static displayName = 'React Quill'
static displayName = 'React Quill';

/*
Export Quill to be able to call `register`
Expand All @@ -108,7 +108,7 @@ class ReactQuill extends React.Component<ReactQuillProps, ReactQuillState> {
'bounds',
'theme',
'children',
]
];

/*
Changing one of these props should cause a regular update. These are mostly
Expand All @@ -127,86 +127,89 @@ class ReactQuill extends React.Component<ReactQuillProps, ReactQuillState> {
'onKeyPress',
'onKeyDown',
'onKeyUp',
]
];

static defaultProps = {
theme: 'snow',
modules: {},
readOnly: false,
}
};

state: ReactQuillState = {
generation: 0,
}
};

/*
The Quill Editor instance.
*/
editor?: Quill
editor?: Quill;

/*
Reference to the element holding the Quill editing area.
*/
editingArea?: React.ReactInstance | null
editingArea?: React.ReactInstance | null;

/*
Tracks the internal value of the Quill editor
*/
value: Value
value: Value;

/*
Tracks the internal selection of the Quill editor
*/
selection: Range = null
selection: Range = null;

/*
Used to compare whether deltas from `onChange` are being used as `value`.
*/
lastDeltaChangeSet?: DeltaStatic
lastDeltaChangeSet?: DeltaStatic;

/*
Stores the contents of the editor to be restored after regeneration.
*/
regenerationSnapshot?: {
delta: DeltaStatic,
selection: Range,
}
delta: DeltaStatic;
selection: Range;
};

/*
A weaker, unprivileged proxy for the editor that does not allow accidentally
modifying editor state.
*/
unprivilegedEditor?: UnprivilegedEditor
unprivilegedEditor?: UnprivilegedEditor;

constructor(props: ReactQuillProps) {
super(props);
const value = this.isControlled()? props.value : props.defaultValue;
const value = this.isControlled() ? props.value : props.defaultValue;
this.value = value ?? '';
}

validateProps(props: ReactQuillProps): void {
if (React.Children.count(props.children) > 1) throw new Error(
'The Quill editing area can only be composed of a single React element.'
);
if (React.Children.count(props.children) > 1)
throw new Error(
'The Quill editing area can only be composed of a single React element.'
);

if (React.Children.count(props.children)) {
const child = React.Children.only(props.children);
if (child?.type === 'textarea') throw new Error(
'Quill does not support editing on a <textarea>. Use a <div> instead.'
);
if (child?.type === 'textarea')
throw new Error(
'Quill does not support editing on a <textarea>. Use a <div> instead.'
);
}

if (
this.lastDeltaChangeSet &&
props.value === this.lastDeltaChangeSet
) throw new Error(
'You are passing the `delta` object from the `onChange` event back ' +
'as `value`. You most probably want `editor.getContents()` instead. ' +
'See: https://github.com/zenoamaro/react-quill#using-deltas'
);
if (this.lastDeltaChangeSet && props.value === this.lastDeltaChangeSet)
throw new Error(
'You are passing the `delta` object from the `onChange` event back ' +
'as `value`. You most probably want `editor.getContents()` instead. ' +
'See: https://github.com/zenoamaro/react-quill#using-deltas'
);
}

shouldComponentUpdate(nextProps: ReactQuillProps, nextState: ReactQuillState) {
shouldComponentUpdate(
nextProps: ReactQuillProps,
nextState: ReactQuillState
) {
this.validateProps(nextProps);

// If the editor hasn't been instantiated yet, or the component has been
Expand Down Expand Up @@ -266,15 +269,15 @@ class ReactQuill extends React.Component<ReactQuillProps, ReactQuillState> {
if (this.editor && this.shouldComponentRegenerate(prevProps)) {
const delta = this.editor.getContents();
const selection = this.editor.getSelection();
this.regenerationSnapshot = {delta, selection};
this.setState({generation: this.state.generation + 1});
this.regenerationSnapshot = { delta, selection };
this.setState({ generation: this.state.generation + 1 });
this.destroyEditor();
}

// The component has been regenerated, so it must be re-instantiated, and
// its content must be restored to the previous values from the snapshot.
if (this.state.generation !== prevState.generation) {
const {delta, selection} = this.regenerationSnapshot!;
const { delta, selection } = this.regenerationSnapshot!;
delete this.regenerationSnapshot;
this.instantiateEditor();
const editor = this.editor!;
Expand Down Expand Up @@ -396,8 +399,11 @@ class ReactQuill extends React.Component<ReactQuillProps, ReactQuillState> {
if (range) {
// Validate bounds before applying.
const length = editor.getLength();
range.index = Math.max(0, Math.min(range.index, length-1));
range.length = Math.max(0, Math.min(range.length, (length-1) - range.index));
range.index = Math.max(0, Math.min(range.index, length - 1));
range.length = Math.max(
0,
Math.min(range.length, length - 1 - range.index)
);
editor.setSelection(range);
}
}
Expand All @@ -423,20 +429,20 @@ class ReactQuill extends React.Component<ReactQuillProps, ReactQuillState> {
makeUnprivilegedEditor(editor: Quill) {
const e = editor;
return {
getHTML: () => e.root.innerHTML,
getLength: e.getLength.bind(e),
getText: e.getText.bind(e),
getContents: e.getContents.bind(e),
getHTML: () => e.root.innerHTML,
getLength: e.getLength.bind(e),
getText: e.getText.bind(e),
getContents: e.getContents.bind(e),
getSelection: e.getSelection.bind(e),
getBounds: e.getBounds.bind(e),
getBounds: e.getBounds.bind(e),
};
}

getEditingArea(): Element {
if (!this.editingArea) {
if (!this.editingAreaRef.current) {
throw new Error('Instantiating on missing editing area');
}
const element = ReactDOM.findDOMNode(this.editingArea);
const element = this.editingAreaRef.current;
if (!element) {
throw new Error('Cannot find element for editing area');
}
Expand All @@ -450,26 +456,23 @@ class ReactQuill extends React.Component<ReactQuillProps, ReactQuillState> {
Renders an editor area, unless it has been provided one to clone.
*/
renderEditingArea(): JSX.Element {
const {children, preserveWhitespace} = this.props;
const {generation} = this.state;
const { children, preserveWhitespace } = this.props;
const { generation } = this.state;

const properties = {
key: generation,
ref: (instance: React.ReactInstance | null) => {
this.editingArea = instance
},
ref: this.editingAreaRef,
};

if (React.Children.count(children)) {
return React.cloneElement(
React.Children.only(children)!,
properties
);
return React.cloneElement(React.Children.only(children)!, properties);
}

return preserveWhitespace ?
<pre {...properties}/> :
<div {...properties}/>;
return preserveWhitespace ? (
<pre {...properties} />
) : (
<div {...properties} />
);
}

render() {
Expand All @@ -492,7 +495,7 @@ class ReactQuill extends React.Component<ReactQuillProps, ReactQuillState> {
eventName: 'text-change' | 'selection-change',
rangeOrDelta: Range | DeltaStatic,
oldRangeOrDelta: Range | DeltaStatic,
source: Sources,
source: Sources
) => {
if (eventName === 'text-change') {
this.onEditorChangeText?.(
Expand All @@ -514,7 +517,7 @@ class ReactQuill extends React.Component<ReactQuillProps, ReactQuillState> {
value: string,
delta: DeltaStatic,
source: Sources,
editor: UnprivilegedEditor,
editor: UnprivilegedEditor
): void {
if (!this.editor) return;

Expand All @@ -537,7 +540,7 @@ class ReactQuill extends React.Component<ReactQuillProps, ReactQuillState> {
onEditorChangeSelection(
nextSelection: RangeStatic,
source: Sources,
editor: UnprivilegedEditor,
editor: UnprivilegedEditor
): void {
if (!this.editor) return;
const currentSelection = this.getEditorSelection();
Expand Down

0 comments on commit 721d2ae

Please sign in to comment.