Skip to content

Commit

Permalink
fix: remove circular dependencies (#1027)
Browse files Browse the repository at this point in the history
  • Loading branch information
ph-fritsche committed Aug 9, 2022
1 parent 5bed8c6 commit 1aa2027
Show file tree
Hide file tree
Showing 86 changed files with 1,537 additions and 1,542 deletions.
9 changes: 5 additions & 4 deletions src/clipboard/copy.ts
@@ -1,8 +1,9 @@
import {Config, Instance} from '../setup'
import {copySelection, writeDataTransferToClipboard} from '../utils'
import {copySelection} from '../document'
import type {Instance} from '../setup'
import {writeDataTransferToClipboard} from '../utils'

export async function copy(this: Instance) {
const doc = this[Config].document
const doc = this.config.document
const target = doc.activeElement ?? /* istanbul ignore next */ doc.body

const clipboardData = copySelection(target)
Expand All @@ -15,7 +16,7 @@ export async function copy(this: Instance) {
this.dispatchUIEvent(target, 'copy', {
clipboardData,
}) &&
this[Config].writeToClipboard
this.config.writeToClipboard
) {
await writeDataTransferToClipboard(doc, clipboardData)
}
Expand Down
9 changes: 5 additions & 4 deletions src/clipboard/cut.ts
@@ -1,8 +1,9 @@
import {Config, Instance} from '../setup'
import {copySelection, writeDataTransferToClipboard} from '../utils'
import {copySelection} from '../document'
import type {Instance} from '../setup'
import {writeDataTransferToClipboard} from '../utils'

export async function cut(this: Instance) {
const doc = this[Config].document
const doc = this.config.document
const target = doc.activeElement ?? /* istanbul ignore next */ doc.body

const clipboardData = copySelection(target)
Expand All @@ -15,7 +16,7 @@ export async function cut(this: Instance) {
this.dispatchUIEvent(target, 'cut', {
clipboardData,
}) &&
this[Config].writeToClipboard
this.config.writeToClipboard
) {
await writeDataTransferToClipboard(target.ownerDocument, clipboardData)
}
Expand Down
4 changes: 2 additions & 2 deletions src/clipboard/paste.ts
@@ -1,4 +1,4 @@
import {Config, Instance} from '../setup'
import type {Instance} from '../setup'
import {
createDataTransfer,
getWindow,
Expand All @@ -9,7 +9,7 @@ export async function paste(
this: Instance,
clipboardData?: DataTransfer | string,
) {
const doc = this[Config].document
const doc = this.config.document
const target = doc.activeElement ?? /* istanbul ignore next */ doc.body

const dataTransfer: DataTransfer =
Expand Down
4 changes: 2 additions & 2 deletions src/convenience/click.ts
@@ -1,9 +1,9 @@
import type {PointerInput} from '../pointer'
import {Config, Instance} from '../setup'
import type {Instance} from '../setup'

export async function click(this: Instance, element: Element): Promise<void> {
const pointerIn: PointerInput = []
if (!this[Config].skipHover) {
if (!this.config.skipHover) {
pointerIn.push({target: element})
}
pointerIn.push({keys: '[MouseLeft]', target: element})
Expand Down
7 changes: 2 additions & 5 deletions src/convenience/hover.ts
@@ -1,14 +1,11 @@
import {Config, Instance} from '../setup'
import type {Instance} from '../setup'
import {assertPointerEvents} from '../utils'

export async function hover(this: Instance, element: Element) {
return this.pointer({target: element})
}

export async function unhover(this: Instance, element: Element) {
assertPointerEvents(
this[Config],
this[Config].system.pointer.getMouseTarget(this[Config]),
)
assertPointerEvents(this, this.system.pointer.getMouseTarget(this))
return this.pointer({target: element.ownerDocument.body})
}
150 changes: 75 additions & 75 deletions src/document/selection.ts → src/document/UI.ts
@@ -1,87 +1,81 @@
import {getUIValue} from '.'
import {prepareInterceptor} from './interceptor'

const UIValue = Symbol('Displayed value in UI')
const UISelection = Symbol('Displayed selection in UI')
const InitialValue = Symbol('Initial value to compare on blur')

interface Value extends Number {
[UISelection]?: typeof UISelection
}

export interface UISelectionRange {
startOffset: number
endOffset: number
declare global {
interface Element {
[UIValue]?: string
[InitialValue]?: string
[UISelection]?: UISelection
}
}

export interface UISelection {
interface UISelection {
anchorOffset: number
focusOffset: number
}

declare global {
interface Element {
[UISelection]?: UISelection
export type UIValueString = String & {[UIValue]: true}
export type UISelectionStart = Number & {[UISelection]: true}

export function isUIValue(
value: string | UIValueString,
): value is UIValueString {
return typeof value === 'object' && UIValue in value
}

export function isUISelectionStart(
start: number | UISelectionStart | null,
): start is UISelectionStart {
return !!start && typeof start === 'object' && UISelection in start
}

export function setUIValue(
element: HTMLInputElement | HTMLTextAreaElement,
value: string,
) {
if (element[InitialValue] === undefined) {
element[InitialValue] = element.value
}

element[UIValue] = value

// eslint-disable-next-line no-new-wrappers
element.value = Object.assign(new String(value), {
[UIValue]: true,
}) as unknown as string
}

export function getUIValue(element: HTMLInputElement | HTMLTextAreaElement) {
return element[UIValue] === undefined
? element.value
: String(element[UIValue])
}

/** Flag the IDL value as clean. This does not change the value.*/
export function setUIValueClean(
element: HTMLInputElement | HTMLTextAreaElement,
) {
element[UIValue] = undefined
}

export function clearInitialValue(
element: HTMLInputElement | HTMLTextAreaElement,
) {
element[InitialValue] = undefined
}

export function getInitialValue(
element: HTMLInputElement | HTMLTextAreaElement,
) {
return element[InitialValue]
}

export function prepareSelectionInterceptor(
export function setUISelectionRaw(
element: HTMLInputElement | HTMLTextAreaElement,
selection: UISelection,
) {
prepareInterceptor(
element,
'setSelectionRange',
function interceptorImpl(
this: HTMLInputElement | HTMLTextAreaElement,
start: number | Value | null,
...others
) {
const isUI = start && typeof start === 'object' && start[UISelection]

if (!isUI) {
this[UISelection] = undefined
}

return {
applyNative: !!isUI,
realArgs: [Number(start), ...others] as [
number,
number,
'forward' | 'backward' | 'none' | undefined,
],
}
},
)

prepareInterceptor(
element,
'selectionStart',
function interceptorImpl(this, v) {
this[UISelection] = undefined

return {realArgs: v}
},
)
prepareInterceptor(
element,
'selectionEnd',
function interceptorImpl(this, v) {
this[UISelection] = undefined

return {realArgs: v}
},
)

prepareInterceptor(
element,
'select',
function interceptorImpl(this: HTMLInputElement | HTMLTextAreaElement) {
this[UISelection] = {
anchorOffset: 0,
focusOffset: getUIValue(element).length,
}

return {realArgs: [] as []}
},
)
element[UISelection] = selection
}

export function setUISelection(
Expand Down Expand Up @@ -120,20 +114,26 @@ export function setUISelection(
}

// eslint-disable-next-line no-new-wrappers
const startObj = new Number(startOffset)
;(startObj as Value)[UISelection] = UISelection
const startObj = Object.assign(new Number(startOffset), {
[UISelection]: true,
}) as unknown as number

try {
element.setSelectionRange(startObj as number, endOffset)
element.setSelectionRange(startObj, endOffset)
} catch {
// DOMException for invalid state is expected when calling this
// on an element without support for setSelectionRange
}
}

export type UISelectionRange = UISelection & {
startOffset: number
endOffset: number
}

export function getUISelection(
element: HTMLInputElement | HTMLTextAreaElement,
) {
): UISelectionRange {
const sel = element[UISelection] ?? {
anchorOffset: element.selectionStart ?? 0,
focusOffset: element.selectionEnd ?? 0,
Expand Down
@@ -1,8 +1,10 @@
import {getUISelection, getUIValue} from '../../document'
import {createDataTransfer} from '../dataTransfer/DataTransfer'
import {EditableInputType} from '../edit/isEditable'
import {getWindow} from '../misc/getWindow'
import {hasOwnSelection} from './selection'
import {
createDataTransfer,
EditableInputOrTextarea,
getWindow,
hasOwnSelection,
} from '../utils'
import {getUISelection, getUIValue} from './UI'

export function copySelection(target: Element) {
const data: Record<string, string> = hasOwnSelection(target)
Expand All @@ -20,9 +22,7 @@ export function copySelection(target: Element) {
return dt
}

function readSelectedValueFromInput(
target: (HTMLInputElement & {type: EditableInputType}) | HTMLTextAreaElement,
) {
function readSelectedValueFromInput(target: EditableInputOrTextarea) {
const sel = getUISelection(target)
const val = getUIValue(target)

Expand Down
@@ -1,10 +1,12 @@
import {getUIValue} from '../../document'
import {isContentEditable} from './isContentEditable'
import {isContentEditable} from '../utils'
import {getUIValue} from './UI'

export function getValue<T extends Element | null>(
export function getValueOrTextContent<T extends Element | null>(
element: T,
): T extends HTMLInputElement | HTMLTextAreaElement ? string : string | null
export function getValue(element: Element | null): string | null | undefined {
export function getValueOrTextContent(
element: Element | null,
): string | null | undefined {
// istanbul ignore if
if (!element) {
return null
Expand Down

0 comments on commit 1aa2027

Please sign in to comment.