/
githubmr
/
facebook-react
Обзор
Документация
Войти
/
githubmr
/
facebook-react
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
main
packages/react-dom-bindings/src/events/plugins/SelectEventPlugin.js
209 строк
6 KB
Jan Kassens
[BE] upgrade prettier to 3.3.3 (#30420)
22 июл 2024, 23:09
Не верифицирован
22 июл 2024, 23:09
b7e7f1a
Код
Авторство
О чём код?
/** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @flow */ import type {AnyNativeEvent} from '../PluginModuleType'; import type {DOMEventName} from '../DOMEventNames'; import type {DispatchQueue} from '../DOMPluginEventSystem'; import type {EventSystemFlags} from '../EventSystemFlags'; import type {Fiber} from 'react-reconciler/src/ReactInternalTypes'; import type {ReactSyntheticEvent} from '../ReactSyntheticEventType'; import {canUseDOM} from 'shared/ExecutionEnvironment'; import {SyntheticEvent} from '../../events/SyntheticEvent'; import isTextInputElement from '../isTextInputElement'; import shallowEqual from 'shared/shallowEqual'; import {registerTwoPhaseEvent} from '../EventRegistry'; import getActiveElement from '../../client/getActiveElement'; import {getNodeFromInstance} from '../../client/ReactDOMComponentTree'; import {hasSelectionCapabilities} from '../../client/ReactInputSelection'; import {DOCUMENT_NODE} from '../../client/HTMLNodeType'; import {accumulateTwoPhaseListeners} from '../DOMPluginEventSystem'; const skipSelectionChangeEvent = canUseDOM && 'documentMode' in document && document.documentMode <= 11; function registerEvents() { registerTwoPhaseEvent('onSelect', [ 'focusout', 'contextmenu', 'dragend', 'focusin', 'keydown', 'keyup', 'mousedown', 'mouseup', 'selectionchange', ]); } let activeElement = null; let activeElementInst = null; let lastSelection = null; let mouseDown = false; /** * Get an object which is a unique representation of the current selection. * * The return value will not be consistent across nodes or browsers, but * two identical selections on the same node will return identical objects. */ function getSelection(node: any) { if ('selectionStart' in node && hasSelectionCapabilities(node)) { return { start: node.selectionStart, end: node.selectionEnd, }; } else { const win = (node.ownerDocument && node.ownerDocument.defaultView) || window; const selection = win.getSelection(); return { anchorNode: selection.anchorNode, anchorOffset: selection.anchorOffset, focusNode: selection.focusNode, focusOffset: selection.focusOffset, }; } } /** * Get document associated with the event target. */ function getEventTargetDocument(eventTarget: any) { return eventTarget.window === eventTarget ? eventTarget.document : eventTarget.nodeType === DOCUMENT_NODE ? eventTarget : eventTarget.ownerDocument; } /** * Poll selection to see whether it's changed. * * @param {object} nativeEvent * @param {object} nativeEventTarget * @return {?SyntheticEvent} */ function constructSelectEvent( dispatchQueue: DispatchQueue, nativeEvent: AnyNativeEvent, nativeEventTarget: null | EventTarget, ) { // Ensure we have the right element, and that the user is not dragging a // selection (this matches native `select` event behavior). In HTML5, select // fires only on input and textarea thus if there's no focused element we // won't dispatch. const doc = getEventTargetDocument(nativeEventTarget); if ( mouseDown || activeElement == null || activeElement !== getActiveElement(doc) ) { return; } // Only fire when selection has actually changed. const currentSelection = getSelection(activeElement); if (!lastSelection || !shallowEqual(lastSelection, currentSelection)) { lastSelection = currentSelection; const listeners = accumulateTwoPhaseListeners( activeElementInst, 'onSelect', ); if (listeners.length > 0) { const event: ReactSyntheticEvent = new SyntheticEvent( 'onSelect', 'select', null, nativeEvent, nativeEventTarget, ); dispatchQueue.push({event, listeners}); event.target = activeElement; } } } /** * This plugin creates an `onSelect` event that normalizes select events * across form elements. * * Supported elements are: * - input (see `isTextInputElement`) * - textarea * - contentEditable * * This differs from native browser implementations in the following ways: * - Fires on contentEditable fields as well as inputs. * - Fires for collapsed selection. * - Fires after user input. */ function extractEvents( dispatchQueue: DispatchQueue, domEventName: DOMEventName, targetInst: null | Fiber, nativeEvent: AnyNativeEvent, nativeEventTarget: null | EventTarget, eventSystemFlags: EventSystemFlags, targetContainer: EventTarget, ) { const targetNode = targetInst ? getNodeFromInstance(targetInst) : window; switch (domEventName) { // Track the input node that has focus. case 'focusin': if ( isTextInputElement((targetNode: any)) || targetNode.contentEditable === 'true' ) { activeElement = targetNode; activeElementInst = targetInst; lastSelection = null; } break; case 'focusout': activeElement = null; activeElementInst = null; lastSelection = null; break; // Don't fire the event while the user is dragging. This matches the // semantics of the native select event. case 'mousedown': mouseDown = true; break; case 'contextmenu': case 'mouseup': case 'dragend': mouseDown = false; constructSelectEvent(dispatchQueue, nativeEvent, nativeEventTarget); break; // Chrome and IE fire non-standard event when selection is changed (and // sometimes when it hasn't). IE's event fires out of order with respect // to key and input events on deletion, so we discard it. // // Firefox doesn't support selectionchange, so check selection status // after each key entry. The selection changes after keydown and before // keyup, but we check on keydown as well in the case of holding down a // key, when multiple keydown events are fired but only one keyup is. // This is also our approach for IE handling, for the reason above. case 'selectionchange': if (skipSelectionChangeEvent) { break; } // falls through case 'keydown': case 'keyup': constructSelectEvent(dispatchQueue, nativeEvent, nativeEventTarget); } } export {registerEvents, extractEvents};