Skip to content

Commit 1aa2027

Browse files
authored
fix: remove circular dependencies (#1027)
1 parent 5bed8c6 commit 1aa2027

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

86 files changed

+1537
-1542
lines changed

src/clipboard/copy.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import {Config, Instance} from '../setup'
2-
import {copySelection, writeDataTransferToClipboard} from '../utils'
1+
import {copySelection} from '../document'
2+
import type {Instance} from '../setup'
3+
import {writeDataTransferToClipboard} from '../utils'
34

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

89
const clipboardData = copySelection(target)
@@ -15,7 +16,7 @@ export async function copy(this: Instance) {
1516
this.dispatchUIEvent(target, 'copy', {
1617
clipboardData,
1718
}) &&
18-
this[Config].writeToClipboard
19+
this.config.writeToClipboard
1920
) {
2021
await writeDataTransferToClipboard(doc, clipboardData)
2122
}

src/clipboard/cut.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import {Config, Instance} from '../setup'
2-
import {copySelection, writeDataTransferToClipboard} from '../utils'
1+
import {copySelection} from '../document'
2+
import type {Instance} from '../setup'
3+
import {writeDataTransferToClipboard} from '../utils'
34

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

89
const clipboardData = copySelection(target)
@@ -15,7 +16,7 @@ export async function cut(this: Instance) {
1516
this.dispatchUIEvent(target, 'cut', {
1617
clipboardData,
1718
}) &&
18-
this[Config].writeToClipboard
19+
this.config.writeToClipboard
1920
) {
2021
await writeDataTransferToClipboard(target.ownerDocument, clipboardData)
2122
}

src/clipboard/paste.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {Config, Instance} from '../setup'
1+
import type {Instance} from '../setup'
22
import {
33
createDataTransfer,
44
getWindow,
@@ -9,7 +9,7 @@ export async function paste(
99
this: Instance,
1010
clipboardData?: DataTransfer | string,
1111
) {
12-
const doc = this[Config].document
12+
const doc = this.config.document
1313
const target = doc.activeElement ?? /* istanbul ignore next */ doc.body
1414

1515
const dataTransfer: DataTransfer =

src/convenience/click.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import type {PointerInput} from '../pointer'
2-
import {Config, Instance} from '../setup'
2+
import type {Instance} from '../setup'
33

44
export async function click(this: Instance, element: Element): Promise<void> {
55
const pointerIn: PointerInput = []
6-
if (!this[Config].skipHover) {
6+
if (!this.config.skipHover) {
77
pointerIn.push({target: element})
88
}
99
pointerIn.push({keys: '[MouseLeft]', target: element})

src/convenience/hover.ts

+2-5
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
1-
import {Config, Instance} from '../setup'
1+
import type {Instance} from '../setup'
22
import {assertPointerEvents} from '../utils'
33

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

88
export async function unhover(this: Instance, element: Element) {
9-
assertPointerEvents(
10-
this[Config],
11-
this[Config].system.pointer.getMouseTarget(this[Config]),
12-
)
9+
assertPointerEvents(this, this.system.pointer.getMouseTarget(this))
1310
return this.pointer({target: element.ownerDocument.body})
1411
}

src/document/selection.ts src/document/UI.ts

+75-75
Original file line numberDiff line numberDiff line change
@@ -1,87 +1,81 @@
1-
import {getUIValue} from '.'
2-
import {prepareInterceptor} from './interceptor'
3-
1+
const UIValue = Symbol('Displayed value in UI')
42
const UISelection = Symbol('Displayed selection in UI')
3+
const InitialValue = Symbol('Initial value to compare on blur')
54

6-
interface Value extends Number {
7-
[UISelection]?: typeof UISelection
8-
}
9-
10-
export interface UISelectionRange {
11-
startOffset: number
12-
endOffset: number
5+
declare global {
6+
interface Element {
7+
[UIValue]?: string
8+
[InitialValue]?: string
9+
[UISelection]?: UISelection
10+
}
1311
}
1412

15-
export interface UISelection {
13+
interface UISelection {
1614
anchorOffset: number
1715
focusOffset: number
1816
}
1917

20-
declare global {
21-
interface Element {
22-
[UISelection]?: UISelection
18+
export type UIValueString = String & {[UIValue]: true}
19+
export type UISelectionStart = Number & {[UISelection]: true}
20+
21+
export function isUIValue(
22+
value: string | UIValueString,
23+
): value is UIValueString {
24+
return typeof value === 'object' && UIValue in value
25+
}
26+
27+
export function isUISelectionStart(
28+
start: number | UISelectionStart | null,
29+
): start is UISelectionStart {
30+
return !!start && typeof start === 'object' && UISelection in start
31+
}
32+
33+
export function setUIValue(
34+
element: HTMLInputElement | HTMLTextAreaElement,
35+
value: string,
36+
) {
37+
if (element[InitialValue] === undefined) {
38+
element[InitialValue] = element.value
2339
}
40+
41+
element[UIValue] = value
42+
43+
// eslint-disable-next-line no-new-wrappers
44+
element.value = Object.assign(new String(value), {
45+
[UIValue]: true,
46+
}) as unknown as string
47+
}
48+
49+
export function getUIValue(element: HTMLInputElement | HTMLTextAreaElement) {
50+
return element[UIValue] === undefined
51+
? element.value
52+
: String(element[UIValue])
53+
}
54+
55+
/** Flag the IDL value as clean. This does not change the value.*/
56+
export function setUIValueClean(
57+
element: HTMLInputElement | HTMLTextAreaElement,
58+
) {
59+
element[UIValue] = undefined
60+
}
61+
62+
export function clearInitialValue(
63+
element: HTMLInputElement | HTMLTextAreaElement,
64+
) {
65+
element[InitialValue] = undefined
66+
}
67+
68+
export function getInitialValue(
69+
element: HTMLInputElement | HTMLTextAreaElement,
70+
) {
71+
return element[InitialValue]
2472
}
2573

26-
export function prepareSelectionInterceptor(
74+
export function setUISelectionRaw(
2775
element: HTMLInputElement | HTMLTextAreaElement,
76+
selection: UISelection,
2877
) {
29-
prepareInterceptor(
30-
element,
31-
'setSelectionRange',
32-
function interceptorImpl(
33-
this: HTMLInputElement | HTMLTextAreaElement,
34-
start: number | Value | null,
35-
...others
36-
) {
37-
const isUI = start && typeof start === 'object' && start[UISelection]
38-
39-
if (!isUI) {
40-
this[UISelection] = undefined
41-
}
42-
43-
return {
44-
applyNative: !!isUI,
45-
realArgs: [Number(start), ...others] as [
46-
number,
47-
number,
48-
'forward' | 'backward' | 'none' | undefined,
49-
],
50-
}
51-
},
52-
)
53-
54-
prepareInterceptor(
55-
element,
56-
'selectionStart',
57-
function interceptorImpl(this, v) {
58-
this[UISelection] = undefined
59-
60-
return {realArgs: v}
61-
},
62-
)
63-
prepareInterceptor(
64-
element,
65-
'selectionEnd',
66-
function interceptorImpl(this, v) {
67-
this[UISelection] = undefined
68-
69-
return {realArgs: v}
70-
},
71-
)
72-
73-
prepareInterceptor(
74-
element,
75-
'select',
76-
function interceptorImpl(this: HTMLInputElement | HTMLTextAreaElement) {
77-
this[UISelection] = {
78-
anchorOffset: 0,
79-
focusOffset: getUIValue(element).length,
80-
}
81-
82-
return {realArgs: [] as []}
83-
},
84-
)
78+
element[UISelection] = selection
8579
}
8680

8781
export function setUISelection(
@@ -120,20 +114,26 @@ export function setUISelection(
120114
}
121115

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

126121
try {
127-
element.setSelectionRange(startObj as number, endOffset)
122+
element.setSelectionRange(startObj, endOffset)
128123
} catch {
129124
// DOMException for invalid state is expected when calling this
130125
// on an element without support for setSelectionRange
131126
}
132127
}
133128

129+
export type UISelectionRange = UISelection & {
130+
startOffset: number
131+
endOffset: number
132+
}
133+
134134
export function getUISelection(
135135
element: HTMLInputElement | HTMLTextAreaElement,
136-
) {
136+
): UISelectionRange {
137137
const sel = element[UISelection] ?? {
138138
anchorOffset: element.selectionStart ?? 0,
139139
focusOffset: element.selectionEnd ?? 0,

src/utils/focus/copySelection.ts src/document/copySelection.ts

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
import {getUISelection, getUIValue} from '../../document'
2-
import {createDataTransfer} from '../dataTransfer/DataTransfer'
3-
import {EditableInputType} from '../edit/isEditable'
4-
import {getWindow} from '../misc/getWindow'
5-
import {hasOwnSelection} from './selection'
1+
import {
2+
createDataTransfer,
3+
EditableInputOrTextarea,
4+
getWindow,
5+
hasOwnSelection,
6+
} from '../utils'
7+
import {getUISelection, getUIValue} from './UI'
68

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

23-
function readSelectedValueFromInput(
24-
target: (HTMLInputElement & {type: EditableInputType}) | HTMLTextAreaElement,
25-
) {
25+
function readSelectedValueFromInput(target: EditableInputOrTextarea) {
2626
const sel = getUISelection(target)
2727
const val = getUIValue(target)
2828

src/utils/edit/getValue.ts src/document/getValueOrTextContent.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
import {getUIValue} from '../../document'
2-
import {isContentEditable} from './isContentEditable'
1+
import {isContentEditable} from '../utils'
2+
import {getUIValue} from './UI'
33

4-
export function getValue<T extends Element | null>(
4+
export function getValueOrTextContent<T extends Element | null>(
55
element: T,
66
): T extends HTMLInputElement | HTMLTextAreaElement ? string : string | null
7-
export function getValue(element: Element | null): string | null | undefined {
7+
export function getValueOrTextContent(
8+
element: Element | null,
9+
): string | null | undefined {
810
// istanbul ignore if
911
if (!element) {
1012
return null

0 commit comments

Comments
 (0)