From 691377cd4c94f8c30ecce15ec1484c8726c17a41 Mon Sep 17 00:00:00 2001 From: Zihua Li Date: Sun, 13 Aug 2023 12:12:59 +0800 Subject: [PATCH] Improve types for quill --- core/quill.ts | 75 +++++++++++++++++------------------------------ core/selection.ts | 11 ++++++- themes/bubble.ts | 5 ++-- ui/tooltip.ts | 4 +-- 4 files changed, 41 insertions(+), 54 deletions(-) diff --git a/core/quill.ts b/core/quill.ts index e1e75a3a2d..eb353868d7 100644 --- a/core/quill.ts +++ b/core/quill.ts @@ -13,9 +13,9 @@ import Emitter, { EmitterSource } from './emitter'; import instances from './instances'; import logger, { DebugLevel } from './logger'; import Module from './module'; -import Selection, { Range } from './selection'; +import Selection, { Bounds, Range } from './selection'; import Composition from './composition'; -import Theme from './theme'; +import Theme, { ThemeConstructor } from './theme'; import scrollRectIntoView, { Rect } from './utils/scrollRectIntoView'; const debug = logger('quill'); @@ -23,8 +23,6 @@ const debug = logger('quill'); const globalRegistry = new Parchment.Registry(); Parchment.ParentBlot.uiClass = 'ql-ui'; -export type Sources = 'api' | 'silent' | 'user'; - interface Options { theme?: string; debug?: DebugLevel | boolean; @@ -37,23 +35,13 @@ interface Options { } interface ExpandedOptions extends Omit { - theme?: typeof Theme; - registry?: Parchment.Registry; - container?: HTMLElement | string; + theme: ThemeConstructor; + registry: Parchment.Registry; + container: HTMLElement; modules: Record; - bounds?: HTMLElement | string | null; - [key: string]: unknown; + bounds?: HTMLElement | null; } -export type BoundsStatic = { - bottom: number; - height: number; - left: number; - right: number; - top: number; - width: number; -}; - class Quill { static DEFAULTS: Partial = { bounds: null, @@ -159,19 +147,11 @@ class Quill { constructor(container: HTMLElement | string, options: Options = {}) { this.options = expandConfig(container, options); - if (this.options.container == null) { + this.container = this.options.container; + if (this.container == null) { debug.error('Invalid Quill container', container); return; } - if (this.options.container instanceof HTMLElement) { - this.container = this.options.container; - } - if (typeof this.options.container === 'string') { - const el = document.querySelector(this.options.container); - if (el != null) { - this.container = el; - } - } if (this.options.debug) { Quill.debug(this.options.debug); } @@ -182,21 +162,17 @@ class Quill { this.root = this.addContainer('ql-editor'); this.root.classList.add('ql-blank'); this.emitter = new Emitter(); - if (this.options.registry) { - // @ts-expect-error TODO: fix BlotConstructor - const ScrollBlot = this.options.registry.query( - Parchment.ScrollBlot.blotName, - ) as ScrollConstructor; - this.scroll = new ScrollBlot(this.options.registry, this.root, { - emitter: this.emitter, - }); - } + // @ts-expect-error TODO: fix BlotConstructor + const ScrollBlot = this.options.registry.query( + Parchment.ScrollBlot.blotName, + ) as ScrollConstructor; + this.scroll = new ScrollBlot(this.options.registry, this.root, { + emitter: this.emitter, + }); this.editor = new Editor(this.scroll); this.selection = new Selection(this.scroll, this.emitter); this.composition = new Composition(this.scroll, this.emitter); - if (this.options.theme) { - this.theme = new this.options.theme(this, this.options); // eslint-disable-line new-cap - } + this.theme = new this.options.theme(this, this.options); // eslint-disable-line new-cap this.keyboard = this.theme.addModule('keyboard'); this.clipboard = this.theme.addModule('clipboard'); this.history = this.theme.addModule('history'); @@ -428,8 +404,8 @@ class Quill { ); } - getBounds(index: number | Range, length = 0): BoundsStatic | null { - let bounds; + getBounds(index: number | Range, length = 0): Bounds | null { + let bounds: Bounds | null = null; if (typeof index === 'number') { bounds = this.selection.getBounds(index, length); } else { @@ -758,13 +734,14 @@ function expandConfig( if (!expandedConfig.theme || expandedConfig.theme === Quill.DEFAULTS.theme) { expandedConfig.theme = Theme; } else { - expandedConfig.theme = Quill.import(`themes/${userConfig.theme}`); + expandedConfig.theme = Quill.import(`themes/${expandedConfig.theme}`); if (expandedConfig.theme == null) { throw new Error( `Invalid theme ${expandedConfig.theme}. Did you register it?`, ); } } + // @ts-expect-error -- TODO fix this later const themeConfig = cloneDeep(expandedConfig.theme.DEFAULTS); [themeConfig, expandedConfig].forEach(config => { config.modules = config.modules || {}; @@ -806,10 +783,11 @@ function expandConfig( themeConfig, expandedConfig, ); - ['bounds', 'container'].forEach(key => { + (['bounds', 'container'] as const).forEach(key => { const selector = expandedConfig[key]; if (typeof selector === 'string') { - expandedConfig[key] = document.querySelector(selector); + // @ts-expect-error Handle null case + expandedConfig[key] = document.querySelector(selector) as HTMLElement; } }); expandedConfig.modules = Object.keys(expandedConfig.modules).reduce( @@ -848,7 +826,8 @@ function modify( } if (shift == null) { range = shiftRange(range, change, source); - } else if (shift !== 0 && typeof index === 'number') { + } else if (shift !== 0) { + // @ts-expect-error index should always be number range = shiftRange(range, index, shift, source); } this.setSelection(range, Emitter.sources.SILENT); @@ -963,10 +942,10 @@ function shiftRange( function shiftRange( range: Range, index: number | Delta, - _length?: number | EmitterSource, + lengthOrSource?: number | EmitterSource, source?: EmitterSource, ) { - const length = typeof _length === 'number' ? _length : 0; + const length = typeof lengthOrSource === 'number' ? lengthOrSource : 0; if (range == null) return null; let start; let end; diff --git a/core/selection.ts b/core/selection.ts index b9aae8b1a3..e05924a5b9 100644 --- a/core/selection.ts +++ b/core/selection.ts @@ -19,6 +19,15 @@ interface NormalizedRange { native: NativeRange; } +export interface Bounds { + bottom: number; + height: number; + left: number; + right: number; + top: number; + width: number; +} + class Range { constructor(public index: number, public length = 0) {} } @@ -164,7 +173,7 @@ class Selection { this.update(); } - getBounds(index: number, length = 0) { + getBounds(index: number, length = 0): Bounds | null { const scrollLength = this.scroll.length(); index = Math.min(index, scrollLength - 1); length = Math.min(index + length, scrollLength - 1) - index; diff --git a/themes/bubble.ts b/themes/bubble.ts index ec6d800b51..0cb1695c07 100644 --- a/themes/bubble.ts +++ b/themes/bubble.ts @@ -1,10 +1,9 @@ import merge from 'lodash.merge'; import Emitter from '../core/emitter'; import BaseTheme, { BaseTooltip } from './base'; -import { Range } from '../core/selection'; +import { Bounds, Range } from '../core/selection'; import icons from '../ui/icons'; import Quill from '../core'; -import { BoundsStatic } from '../core/quill'; import { ThemeOptions } from '../core/theme'; import Toolbar, { ToolbarConfig } from '../modules/toolbar'; @@ -91,7 +90,7 @@ class BubbleTooltip extends BaseTooltip { this.show(); } - position(reference: BoundsStatic) { + position(reference: Bounds) { const shift = super.position(reference); const arrow = this.root.querySelector('.ql-tooltip-arrow'); // @ts-expect-error diff --git a/ui/tooltip.ts b/ui/tooltip.ts index 6dd113834c..5412a6ddd8 100644 --- a/ui/tooltip.ts +++ b/ui/tooltip.ts @@ -1,5 +1,5 @@ import Quill from '../core'; -import { BoundsStatic } from '../core/quill'; +import { Bounds } from '../core/selection'; const isScrollable = (el: Element) => { const { overflowY } = getComputedStyle(el, null); @@ -29,7 +29,7 @@ class Tooltip { this.root.classList.add('ql-hidden'); } - position(reference: BoundsStatic) { + position(reference: Bounds) { const left = reference.left + reference.width / 2 - this.root.offsetWidth / 2; // root.scrollTop should be 0 if scrollContainer !== root