Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: remove circular dependencies #1027

Merged
merged 2 commits into from
Aug 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions src/clipboard/copy.ts
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Loading