Improve Bridge Flow types (#352)
* Updated local fork of react-window * Updated Fow 97 -> 103 * Lint ignore NPM dist * Improved Bridge Flow types
This commit is contained in:
parent
39ad101ea2
commit
4b34a77d29
|
@ -4,6 +4,7 @@ shells/browser/chrome/build
|
|||
shells/browser/firefox/build
|
||||
shells/browser/shared/build
|
||||
shells/dev/dist
|
||||
packages/react-devtools-core/dist
|
||||
vendor
|
||||
*.js.snap
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@
|
|||
"test:chrome": "node ./shells/browser/chrome/test",
|
||||
"test:firefox": "node ./shells/browser/firefox/test",
|
||||
"test:standalone": "cd packages/react-devtools && yarn start",
|
||||
"typecheck": "flow check"
|
||||
"typecheck": "flow check --show-all-errors"
|
||||
},
|
||||
"devEngines": {
|
||||
"node": "10.x || 11.x"
|
||||
|
@ -119,7 +119,7 @@
|
|||
"fbjs": "0.5.1",
|
||||
"fbjs-scripts": "0.7.0",
|
||||
"firefox-profile": "^1.0.2",
|
||||
"flow-bin": "^0.97.0",
|
||||
"flow-bin": "^0.103.0",
|
||||
"fs-extra": "^3.0.1",
|
||||
"gh-pages": "^1.0.0",
|
||||
"html2canvas": "^1.0.0-alpha.12",
|
||||
|
|
|
@ -8,6 +8,7 @@ import { __DEBUG__ } from 'src/constants';
|
|||
import setupNativeStyleEditor from 'src/backend/NativeStyleEditor/setupNativeStyleEditor';
|
||||
import { getDefaultComponentFilters } from 'src/utils';
|
||||
|
||||
import type { BackendBridge } from 'src/bridge';
|
||||
import type { ComponentFilter } from 'src/types';
|
||||
import type { DevToolsHook } from 'src/backend/types';
|
||||
import type { ResolveNativeStyle } from 'src/backend/NativeStyleEditor/setupNativeStyleEditor';
|
||||
|
@ -64,7 +65,7 @@ export function connectToDevTools(options: ?ConnectOptions) {
|
|||
return;
|
||||
}
|
||||
|
||||
let bridge: Bridge | null = null;
|
||||
let bridge: BackendBridge | null = null;
|
||||
|
||||
const messageListeners = [];
|
||||
const uri = 'ws://' + host + ':' + port;
|
||||
|
|
|
@ -17,6 +17,7 @@ import DevTools from 'src/devtools/views/DevTools';
|
|||
import launchEditor from './launchEditor';
|
||||
import { __DEBUG__ } from 'src/constants';
|
||||
|
||||
import type { FrontendBridge } from 'src/bridge';
|
||||
import type { InspectedElement } from 'src/devtools/views/Components/types';
|
||||
|
||||
installHook(window);
|
||||
|
@ -46,7 +47,7 @@ function setStatusListener(value: StatusListener) {
|
|||
return DevtoolsUI;
|
||||
}
|
||||
|
||||
let bridge: Bridge | null = null;
|
||||
let bridge: FrontendBridge | null = null;
|
||||
let store: Store | null = null;
|
||||
let root = null;
|
||||
|
||||
|
@ -83,7 +84,7 @@ function reload() {
|
|||
root = createRoot(node);
|
||||
root.render(
|
||||
createElement(DevTools, {
|
||||
bridge: ((bridge: any): Bridge),
|
||||
bridge: ((bridge: any): FrontendBridge),
|
||||
showTabBar: true,
|
||||
store: ((store: any): Store),
|
||||
warnIfLegacyBackendDetected: true,
|
||||
|
@ -166,7 +167,7 @@ function initialize(socket: WebSocket) {
|
|||
}
|
||||
},
|
||||
});
|
||||
((bridge: any): Bridge).addListener('shutdown', () => {
|
||||
((bridge: any): FrontendBridge).addListener('shutdown', () => {
|
||||
socket.close();
|
||||
});
|
||||
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
// @flow
|
||||
|
||||
import React, { Suspense, useState } from 'react';
|
||||
import React, { Fragment, Suspense, useState } from 'react';
|
||||
|
||||
function SuspenseTree() {
|
||||
return (
|
||||
<>
|
||||
<Fragment>
|
||||
<h1>Suspense</h1>
|
||||
<h4>Primary to Fallback Cycle</h4>
|
||||
<PrimaryFallbackTest initialSuspend={false} />
|
||||
<h4>Fallback to Primary Cycle</h4>
|
||||
<PrimaryFallbackTest initialSuspend={true} />
|
||||
<NestedSuspenseTest />
|
||||
</>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,7 @@ function PrimaryFallbackTest({ initialSuspend }) {
|
|||
const fallbackStep = useTestSequence('fallback', Fallback1, Fallback2);
|
||||
const primaryStep = useTestSequence('primary', Primary1, Primary2);
|
||||
return (
|
||||
<>
|
||||
<Fragment>
|
||||
<label>
|
||||
<input
|
||||
checked={suspend}
|
||||
|
@ -33,7 +33,7 @@ function PrimaryFallbackTest({ initialSuspend }) {
|
|||
<Suspense fallback={fallbackStep}>
|
||||
{suspend ? <Never /> : primaryStep}
|
||||
</Suspense>
|
||||
</>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -45,32 +45,32 @@ function useTestSequence(label, T1, T2) {
|
|||
</button>
|
||||
);
|
||||
let allSteps = [
|
||||
<>{next}</>,
|
||||
<>
|
||||
<Fragment>{next}</Fragment>,
|
||||
<Fragment>
|
||||
{next} <T1 prop={step}>mount</T1>
|
||||
</>,
|
||||
<>
|
||||
</Fragment>,
|
||||
<Fragment>
|
||||
{next} <T1 prop={step}>update</T1>
|
||||
</>,
|
||||
<>
|
||||
</Fragment>,
|
||||
<Fragment>
|
||||
{next} <T2 prop={step}>several</T2> <T1 prop={step}>different</T1>{' '}
|
||||
<T2 prop={step}>children</T2>
|
||||
</>,
|
||||
<>
|
||||
</Fragment>,
|
||||
<Fragment>
|
||||
{next} <T2 prop={step}>goodbye</T2>
|
||||
</>,
|
||||
</Fragment>,
|
||||
];
|
||||
return allSteps[step];
|
||||
}
|
||||
|
||||
function NestedSuspenseTest() {
|
||||
return (
|
||||
<>
|
||||
<Fragment>
|
||||
<h3>Nested Suspense</h3>
|
||||
<Suspense fallback={<Fallback1>Loading outer</Fallback1>}>
|
||||
<Parent />
|
||||
</Suspense>
|
||||
</>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -118,19 +118,19 @@ function Never() {
|
|||
throw new Promise(resolve => {});
|
||||
}
|
||||
|
||||
function Fallback1({ prop, ...rest }) {
|
||||
function Fallback1({ prop, ...rest }: any) {
|
||||
return <span {...rest} />;
|
||||
}
|
||||
|
||||
function Fallback2({ prop, ...rest }) {
|
||||
function Fallback2({ prop, ...rest }: any) {
|
||||
return <span {...rest} />;
|
||||
}
|
||||
|
||||
function Primary1({ prop, ...rest }) {
|
||||
function Primary1({ prop, ...rest }: any) {
|
||||
return <span {...rest} />;
|
||||
}
|
||||
|
||||
function Primary2({ prop, ...rest }) {
|
||||
function Primary2({ prop, ...rest }: any) {
|
||||
return <span {...rest} />;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,9 +15,9 @@ describe('Bridge', () => {
|
|||
const bridge = new Bridge(wall);
|
||||
|
||||
// Check that we're wired up correctly.
|
||||
bridge.send('init');
|
||||
bridge.send('reloadAppForProfiling');
|
||||
jest.runAllTimers();
|
||||
expect(wall.send).toHaveBeenCalledWith('init', undefined, undefined);
|
||||
expect(wall.send).toHaveBeenCalledWith('reloadAppForProfiling');
|
||||
|
||||
// Should flush pending messages and then shut down.
|
||||
wall.send.mockClear();
|
||||
|
@ -25,9 +25,9 @@ describe('Bridge', () => {
|
|||
bridge.send('update', '2');
|
||||
bridge.shutdown();
|
||||
jest.runAllTimers();
|
||||
expect(wall.send).toHaveBeenCalledWith('update', '1', undefined);
|
||||
expect(wall.send).toHaveBeenCalledWith('update', '2', undefined);
|
||||
expect(wall.send).toHaveBeenCalledWith('shutdown', undefined, undefined);
|
||||
expect(wall.send).toHaveBeenCalledWith('update', '1');
|
||||
expect(wall.send).toHaveBeenCalledWith('update', '2');
|
||||
expect(wall.send).toHaveBeenCalledWith('shutdown');
|
||||
|
||||
// Verify that the Bridge doesn't send messages after shutdown.
|
||||
spyOn(console, 'warn');
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
|
||||
import typeof ReactTestRenderer from 'react-test-renderer';
|
||||
import type { GetInspectedElementPath } from 'src/devtools/views/Components/InspectedElementContext';
|
||||
import type Bridge from 'src/bridge';
|
||||
import type { FrontendBridge } from 'src/bridge';
|
||||
import type Store from 'src/devtools/store';
|
||||
|
||||
describe('InspectedElementContext', () => {
|
||||
let React;
|
||||
let ReactDOM;
|
||||
let TestRenderer: ReactTestRenderer;
|
||||
let bridge: Bridge;
|
||||
let bridge: FrontendBridge;
|
||||
let store: Store;
|
||||
let meta;
|
||||
let utils;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import type { InspectedElementPayload } from 'src/backend/types';
|
||||
import type { DehydratedData } from 'src/devtools/views/Components/types';
|
||||
import type Bridge from 'src/bridge';
|
||||
import type { FrontendBridge } from 'src/bridge';
|
||||
import type Store from 'src/devtools/store';
|
||||
|
||||
describe('InspectedElementContext', () => {
|
||||
|
@ -10,7 +10,7 @@ describe('InspectedElementContext', () => {
|
|||
let ReactDOM;
|
||||
let hydrate;
|
||||
let meta;
|
||||
let bridge: Bridge;
|
||||
let bridge: FrontendBridge;
|
||||
let store: Store;
|
||||
|
||||
const act = (callback: Function) => {
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
|
||||
import typeof ReactTestRenderer from 'react-test-renderer';
|
||||
import type { Element } from 'src/devtools/views/Components/types';
|
||||
import type Bridge from 'src/bridge';
|
||||
import type { FrontendBridge } from 'src/bridge';
|
||||
import type Store from 'src/devtools/store';
|
||||
|
||||
describe('OwnersListContext', () => {
|
||||
let React;
|
||||
let ReactDOM;
|
||||
let TestRenderer: ReactTestRenderer;
|
||||
let bridge: Bridge;
|
||||
let bridge: FrontendBridge;
|
||||
let store: Store;
|
||||
let utils;
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// @flow
|
||||
|
||||
import typeof ReactTestRenderer from 'react-test-renderer';
|
||||
import type Bridge from 'src/bridge';
|
||||
import type { FrontendBridge } from 'src/bridge';
|
||||
import type { Context } from 'src/devtools/views/Profiler/ProfilerContext';
|
||||
import type { DispatcherContext } from 'src/devtools/views/Components/TreeContext';
|
||||
import type Store from 'src/devtools/store';
|
||||
|
@ -10,7 +10,7 @@ describe('ProfilerContext', () => {
|
|||
let React;
|
||||
let ReactDOM;
|
||||
let TestRenderer: ReactTestRenderer;
|
||||
let bridge: Bridge;
|
||||
let bridge: FrontendBridge;
|
||||
let store: Store;
|
||||
let utils;
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// @flow
|
||||
|
||||
import typeof ReactTestRenderer from 'react-test-renderer';
|
||||
import type Bridge from 'src/bridge';
|
||||
import type { FrontendBridge } from 'src/bridge';
|
||||
import type Store from 'src/devtools/store';
|
||||
|
||||
describe('ProfilingCache', () => {
|
||||
|
@ -11,7 +11,7 @@ describe('ProfilingCache', () => {
|
|||
let Scheduler;
|
||||
let SchedulerTracing;
|
||||
let TestRenderer: ReactTestRenderer;
|
||||
let bridge: Bridge;
|
||||
let bridge: FrontendBridge;
|
||||
let store: Store;
|
||||
let utils;
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ describe('profiling charts', () => {
|
|||
|
||||
describe('flamegraph chart', () => {
|
||||
it('should contain valid data', () => {
|
||||
const Parent = ({ count }) => {
|
||||
const Parent = (_: {||}) => {
|
||||
Scheduler.unstable_advanceTime(10);
|
||||
return (
|
||||
<React.Fragment>
|
||||
|
@ -105,7 +105,7 @@ describe('profiling charts', () => {
|
|||
|
||||
describe('ranked chart', () => {
|
||||
it('should contain valid data', () => {
|
||||
const Parent = ({ count }) => {
|
||||
const Parent = (_: {||}) => {
|
||||
Scheduler.unstable_advanceTime(10);
|
||||
return (
|
||||
<React.Fragment>
|
||||
|
@ -177,7 +177,7 @@ describe('profiling charts', () => {
|
|||
|
||||
describe('interactions', () => {
|
||||
it('should contain valid data', () => {
|
||||
const Parent = ({ count }) => {
|
||||
const Parent = (_: {||}) => {
|
||||
Scheduler.unstable_advanceTime(10);
|
||||
return (
|
||||
<React.Fragment>
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
// @flow
|
||||
|
||||
import type { BackendBridge, FrontendBridge } from 'src/bridge';
|
||||
|
||||
const env = jasmine.getEnv();
|
||||
env.beforeEach(() => {
|
||||
// These files should be required (and re-reuired) before each test,
|
||||
|
@ -51,13 +53,13 @@ env.beforeEach(() => {
|
|||
},
|
||||
});
|
||||
|
||||
const agent = new Agent(bridge);
|
||||
const agent = new Agent(((bridge: any): BackendBridge));
|
||||
|
||||
const hook = global.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
||||
|
||||
initBackend(hook, agent, global);
|
||||
|
||||
const store = new Store(bridge);
|
||||
const store = new Store(((bridge: any): FrontendBridge));
|
||||
|
||||
global.agent = agent;
|
||||
global.bridge = bridge;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// @flow
|
||||
|
||||
import type Bridge from 'src/bridge';
|
||||
import type { FrontendBridge } from 'src/bridge';
|
||||
import type Store from 'src/devtools/store';
|
||||
|
||||
describe('Store component filters', () => {
|
||||
|
@ -8,7 +8,7 @@ describe('Store component filters', () => {
|
|||
let ReactDOM;
|
||||
let TestUtils;
|
||||
let Types;
|
||||
let bridge: Bridge;
|
||||
let bridge: FrontendBridge;
|
||||
let store: Store;
|
||||
let utils;
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// @flow
|
||||
|
||||
import typeof ReactTestRenderer from 'react-test-renderer';
|
||||
import type Bridge from 'src/bridge';
|
||||
import type { FrontendBridge } from 'src/bridge';
|
||||
import type Store from 'src/devtools/store';
|
||||
import type {
|
||||
DispatcherContext,
|
||||
|
@ -12,7 +12,7 @@ describe('TreeListContext', () => {
|
|||
let React;
|
||||
let ReactDOM;
|
||||
let TestRenderer: ReactTestRenderer;
|
||||
let bridge: Bridge;
|
||||
let bridge: FrontendBridge;
|
||||
let store: Store;
|
||||
let utils;
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import typeof ReactTestRenderer from 'react-test-renderer';
|
||||
|
||||
import type Bridge from 'src/bridge';
|
||||
import type { FrontendBridge } from 'src/bridge';
|
||||
import type Store from 'src/devtools/store';
|
||||
import type { ProfilingDataFrontend } from 'src/devtools/views/Profiler/types';
|
||||
import type { ElementType } from 'src/types';
|
||||
|
@ -161,7 +161,7 @@ export function requireTestRenderer(): ReactTestRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
export function exportImportHelper(bridge: Bridge, store: Store): void {
|
||||
export function exportImportHelper(bridge: FrontendBridge, store: Store): void {
|
||||
const { act } = require('./utils');
|
||||
const {
|
||||
prepareProfilingDataExport,
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
// @flow
|
||||
|
||||
import Agent from 'src/backend/agent';
|
||||
import Bridge from 'src/bridge';
|
||||
import resolveBoxStyle from './resolveBoxStyle';
|
||||
|
||||
import type { BackendBridge } from 'src/bridge';
|
||||
import type { RendererID } from '../types';
|
||||
import type { StyleAndLayout } from './types';
|
||||
|
||||
export type ResolveNativeStyle = (stylesheetID: number) => ?Object;
|
||||
|
||||
export default function setupNativeStyleEditor(
|
||||
bridge: Bridge,
|
||||
bridge: BackendBridge,
|
||||
agent: Agent,
|
||||
resolveNativeStyle: ResolveNativeStyle,
|
||||
validAttributes?: $ReadOnlyArray<string> | null
|
||||
|
@ -81,7 +81,7 @@ const componentIDToStyleOverrides: Map<number, Object> = new Map();
|
|||
|
||||
function measureStyle(
|
||||
agent: Agent,
|
||||
bridge: Bridge,
|
||||
bridge: BackendBridge,
|
||||
resolveNativeStyle: ResolveNativeStyle,
|
||||
id: number,
|
||||
rendererID: RendererID
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
import EventEmitter from 'events';
|
||||
import throttle from 'lodash.throttle';
|
||||
import Bridge from 'src/bridge';
|
||||
import {
|
||||
SESSION_STORAGE_LAST_SELECTION_KEY,
|
||||
SESSION_STORAGE_RELOAD_AND_PROFILE_KEY,
|
||||
|
@ -17,6 +16,7 @@ import {
|
|||
import setupHighlighter from './views/Highlighter';
|
||||
import { patch as patchConsole, unpatch as unpatchConsole } from './console';
|
||||
|
||||
import type { BackendBridge } from 'src/bridge';
|
||||
import type {
|
||||
InstanceAndStyle,
|
||||
NativeType,
|
||||
|
@ -81,14 +81,14 @@ export default class Agent extends EventEmitter<{|
|
|||
showNativeHighlight: [NativeType],
|
||||
shutdown: [],
|
||||
|}> {
|
||||
_bridge: Bridge;
|
||||
_bridge: BackendBridge;
|
||||
_isProfiling: boolean = false;
|
||||
_recordChangeDescriptions: boolean = false;
|
||||
_rendererInterfaces: { [key: RendererID]: RendererInterface } = {};
|
||||
_persistedSelection: PersistedSelection | null = null;
|
||||
_persistedSelectionMatch: PathMatch | null = null;
|
||||
|
||||
constructor(bridge: Bridge) {
|
||||
constructor(bridge: BackendBridge) {
|
||||
super();
|
||||
|
||||
if (
|
||||
|
@ -259,7 +259,7 @@ export default class Agent extends EventEmitter<{|
|
|||
dataURL: string,
|
||||
rootID: number,
|
||||
|}) => {
|
||||
this._bridge.send('screenshotCaptured', { commitIndex, dataURL });
|
||||
this._bridge.send('screenshotCaptured', { commitIndex, dataURL, rootID });
|
||||
};
|
||||
|
||||
selectElement = ({ id, rendererID }: ElementAndRendererID) => {
|
||||
|
|
|
@ -11,6 +11,8 @@ type Rect = {
|
|||
width: number,
|
||||
};
|
||||
|
||||
type Box = {| top: number, left: number, width: number, height: number |};
|
||||
|
||||
// Note that the Overlay components are not affected by the active Theme,
|
||||
// because they highlight elements in the main Chrome window (outside of devtools).
|
||||
// The colors below were chosen to roughly match those used by Chrome devtools.
|
||||
|
@ -21,7 +23,7 @@ class OverlayRect {
|
|||
padding: HTMLElement;
|
||||
content: HTMLElement;
|
||||
|
||||
constructor(doc, container) {
|
||||
constructor(doc: Document, container: HTMLElement) {
|
||||
this.node = doc.createElement('div');
|
||||
this.border = doc.createElement('div');
|
||||
this.padding = doc.createElement('div');
|
||||
|
@ -51,7 +53,7 @@ class OverlayRect {
|
|||
}
|
||||
}
|
||||
|
||||
update(box, dims) {
|
||||
update(box: Rect, dims: any) {
|
||||
boxWrap(dims, 'margin', this.node);
|
||||
boxWrap(dims, 'border', this.border);
|
||||
boxWrap(dims, 'padding', this.padding);
|
||||
|
@ -85,7 +87,7 @@ class OverlayTip {
|
|||
nameSpan: HTMLElement;
|
||||
dimSpan: HTMLElement;
|
||||
|
||||
constructor(doc, container) {
|
||||
constructor(doc: Document, container: HTMLElement) {
|
||||
this.tip = doc.createElement('div');
|
||||
assign(this.tip.style, {
|
||||
display: 'flex',
|
||||
|
@ -126,13 +128,13 @@ class OverlayTip {
|
|||
}
|
||||
}
|
||||
|
||||
updateText(name, width, height) {
|
||||
updateText(name: string, width: number, height: number) {
|
||||
this.nameSpan.textContent = name;
|
||||
this.dimSpan.textContent =
|
||||
Math.round(width) + 'px × ' + Math.round(height) + 'px';
|
||||
}
|
||||
|
||||
updatePosition(dims, bounds) {
|
||||
updatePosition(dims: Box, bounds: Box) {
|
||||
const tipRect = this.tip.getBoundingClientRect();
|
||||
const tipPos = findTipPos(dims, bounds, {
|
||||
width: tipRect.width,
|
||||
|
@ -247,6 +249,7 @@ export default class Overlay {
|
|||
this.tipBoundsWindow.document.documentElement,
|
||||
this.window
|
||||
);
|
||||
|
||||
this.tip.updatePosition(
|
||||
{
|
||||
top: outerBox.top,
|
||||
|
|
|
@ -2,11 +2,15 @@
|
|||
|
||||
import memoize from 'memoize-one';
|
||||
import throttle from 'lodash.throttle';
|
||||
import Bridge from 'src/bridge';
|
||||
import Agent from 'src/backend/agent';
|
||||
import { hideOverlay, showOverlay } from './Highlighter';
|
||||
|
||||
export default function setup(bridge: Bridge, agent: Agent): void {
|
||||
import type { BackendBridge } from 'src/bridge';
|
||||
|
||||
export default function setupHighlighter(
|
||||
bridge: BackendBridge,
|
||||
agent: Agent
|
||||
): void {
|
||||
bridge.addListener(
|
||||
'clearNativeElementHighlight',
|
||||
clearNativeElementHighlight
|
||||
|
@ -50,7 +54,7 @@ export default function setup(bridge: Bridge, agent: Agent): void {
|
|||
rendererID,
|
||||
scrollIntoView,
|
||||
}: {
|
||||
displayName: string,
|
||||
displayName: string | null,
|
||||
hideAfterTimeout: boolean,
|
||||
id: number,
|
||||
openNativeElementsPanel: boolean,
|
||||
|
|
|
@ -22,7 +22,7 @@ type Message = {|
|
|||
|
||||
type HighlightElementInDOM = {|
|
||||
...ElementAndRendererID,
|
||||
displayName: string,
|
||||
displayName: string | null,
|
||||
hideAfterTimeout: boolean,
|
||||
openNativeElementsPanel: boolean,
|
||||
scrollIntoView: boolean,
|
||||
|
@ -62,33 +62,48 @@ type NativeStyleEditor_SetValueParams = {|
|
|||
value: string,
|
||||
|};
|
||||
|
||||
export default class Bridge extends EventEmitter<{|
|
||||
type BackendEvents = {|
|
||||
captureScreenshot: [{| commitIndex: number, rootID: number |}],
|
||||
inspectedElement: [InspectedElementPayload],
|
||||
isBackendStorageAPISupported: [boolean],
|
||||
operations: [Array<number>],
|
||||
ownersList: [OwnersList],
|
||||
overrideComponentFilters: [Array<ComponentFilter>],
|
||||
profilingData: [ProfilingDataBackend],
|
||||
profilingStatus: [boolean],
|
||||
reloadAppForProfiling: [],
|
||||
screenshotCaptured: [
|
||||
{| commitIndex: number, dataURL: string, rootID: number |},
|
||||
],
|
||||
selectFiber: [number],
|
||||
shutdown: [],
|
||||
stopInspectingNative: [boolean],
|
||||
syncSelectionFromNativeElementsPanel: [],
|
||||
syncSelectionToNativeElementsPanel: [],
|
||||
|
||||
// React Native style editor plug-in.
|
||||
isNativeStyleEditorSupported: [
|
||||
{| isSupported: boolean, validAttributes: ?$ReadOnlyArray<string> |},
|
||||
],
|
||||
NativeStyleEditor_styleAndLayout: [StyleAndLayoutPayload],
|
||||
|};
|
||||
|
||||
type FrontendEvents = {|
|
||||
captureScreenshot: [{| commitIndex: number, rootID: number |}],
|
||||
clearNativeElementHighlight: [],
|
||||
getOwnersList: [ElementAndRendererID],
|
||||
getProfilingData: [{| rendererID: RendererID |}],
|
||||
getProfilingStatus: [],
|
||||
highlightNativeElement: [HighlightElementInDOM],
|
||||
init: [],
|
||||
inspectElement: [InspectElementParams],
|
||||
inspectedElement: [InspectedElementPayload],
|
||||
isBackendStorageAPISupported: [boolean],
|
||||
logElementToConsole: [ElementAndRendererID],
|
||||
operations: [Array<number>],
|
||||
ownersList: [OwnersList],
|
||||
overrideComponentFilters: [Array<ComponentFilter>],
|
||||
overrideContext: [OverrideValue],
|
||||
overrideHookState: [OverrideHookState],
|
||||
overrideProps: [OverrideValue],
|
||||
overrideState: [OverrideValue],
|
||||
overrideSuspense: [OverrideSuspense],
|
||||
profilingData: [ProfilingDataBackend],
|
||||
profilingStatus: [boolean],
|
||||
reloadAndProfile: [boolean],
|
||||
reloadAppForProfiling: [],
|
||||
screenshotCaptured: [
|
||||
{| commitIndex: number, dataURL: string, rootID: number |},
|
||||
],
|
||||
selectElement: [ElementAndRendererID],
|
||||
selectFiber: [number],
|
||||
shutdown: [],
|
||||
|
@ -96,20 +111,22 @@ export default class Bridge extends EventEmitter<{|
|
|||
startProfiling: [boolean],
|
||||
stopInspectingNative: [boolean],
|
||||
stopProfiling: [],
|
||||
syncSelectionFromNativeElementsPanel: [],
|
||||
syncSelectionToNativeElementsPanel: [],
|
||||
updateAppendComponentStack: [boolean],
|
||||
updateComponentFilters: [Array<ComponentFilter>],
|
||||
viewElementSource: [ElementAndRendererID],
|
||||
|
||||
// React Native style editor plug-in.
|
||||
isNativeStyleEditorSupported: [
|
||||
{| isSupported: boolean, validAttributes: $ReadOnlyArray<string> |},
|
||||
],
|
||||
NativeStyleEditor_measure: [ElementAndRendererID],
|
||||
NativeStyleEditor_renameAttribute: [NativeStyleEditor_RenameAttributeParams],
|
||||
NativeStyleEditor_setValue: [NativeStyleEditor_SetValueParams],
|
||||
NativeStyleEditor_styleAndLayout: [StyleAndLayoutPayload],
|
||||
|};
|
||||
|
||||
class Bridge<
|
||||
OutgoingEvents: Object,
|
||||
IncomingEvents: Object
|
||||
> extends EventEmitter<{|
|
||||
...IncomingEvents,
|
||||
...OutgoingEvents,
|
||||
|}> {
|
||||
_isShutdown: boolean = false;
|
||||
_messageQueue: Array<any> = [];
|
||||
|
@ -134,7 +151,10 @@ export default class Bridge extends EventEmitter<{|
|
|||
return this._wall;
|
||||
}
|
||||
|
||||
send(event: string, payload: any, transferable?: Array<any>) {
|
||||
send<EventName: $Keys<OutgoingEvents>>(
|
||||
event: EventName,
|
||||
...payload: $ElementType<OutgoingEvents, EventName>
|
||||
) {
|
||||
if (this._isShutdown) {
|
||||
console.warn(
|
||||
`Cannot send message "${event}" through a Bridge that has been shutdown.`
|
||||
|
@ -150,7 +170,7 @@ export default class Bridge extends EventEmitter<{|
|
|||
// - if there *has* been a message flushed in the last BATCH_DURATION ms
|
||||
// (or we're waiting for our setTimeout-0 to fire), then _timeoutID will
|
||||
// be set, and we'll simply add to the queue and wait for that
|
||||
this._messageQueue.push(event, payload, transferable);
|
||||
this._messageQueue.push(event, payload);
|
||||
if (!this._timeoutID) {
|
||||
this._timeoutID = setTimeout(this._flush, 0);
|
||||
}
|
||||
|
@ -204,12 +224,8 @@ export default class Bridge extends EventEmitter<{|
|
|||
this._timeoutID = null;
|
||||
|
||||
if (this._messageQueue.length) {
|
||||
for (let i = 0; i < this._messageQueue.length; i += 3) {
|
||||
this._wall.send(
|
||||
this._messageQueue[i],
|
||||
this._messageQueue[i + 1],
|
||||
this._messageQueue[i + 2]
|
||||
);
|
||||
for (let i = 0; i < this._messageQueue.length; i += 2) {
|
||||
this._wall.send(this._messageQueue[i], ...this._messageQueue[i + 1]);
|
||||
}
|
||||
this._messageQueue.length = 0;
|
||||
|
||||
|
@ -220,3 +236,8 @@ export default class Bridge extends EventEmitter<{|
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
export type BackendBridge = Bridge<BackendEvents, FrontendEvents>;
|
||||
export type FrontendBridge = Bridge<FrontendEvents, BackendEvents>;
|
||||
|
||||
export default Bridge;
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
import EventEmitter from 'events';
|
||||
import memoize from 'memoize-one';
|
||||
import throttle from 'lodash.throttle';
|
||||
import Bridge from 'src/bridge';
|
||||
import { prepareProfilingDataFrontendFromBackendAndStore } from './views/Profiler/utils';
|
||||
import ProfilingCache from './ProfilingCache';
|
||||
import Store from './store';
|
||||
|
||||
import type { FrontendBridge } from 'src/bridge';
|
||||
import type { ProfilingDataBackend } from 'src/backend/types';
|
||||
import type {
|
||||
CommitDataFrontend,
|
||||
|
@ -23,7 +23,7 @@ export default class ProfilerStore extends EventEmitter<{|
|
|||
isProfiling: [],
|
||||
profilingData: [],
|
||||
|}> {
|
||||
_bridge: Bridge;
|
||||
_bridge: FrontendBridge;
|
||||
|
||||
// Suspense cache for lazily calculating derived profiling data.
|
||||
_cache: ProfilingCache;
|
||||
|
@ -79,7 +79,11 @@ export default class ProfilerStore extends EventEmitter<{|
|
|||
|
||||
_store: Store;
|
||||
|
||||
constructor(bridge: Bridge, store: Store, defaultIsProfiling: boolean) {
|
||||
constructor(
|
||||
bridge: FrontendBridge,
|
||||
store: Store,
|
||||
defaultIsProfiling: boolean
|
||||
) {
|
||||
super();
|
||||
|
||||
this._bridge = bridge;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// @flow
|
||||
|
||||
import Bridge from 'src/bridge';
|
||||
import type { FrontendBridge } from 'src/bridge';
|
||||
|
||||
type Shell = {|
|
||||
connect: (callback: Function) => void,
|
||||
|
@ -8,7 +8,7 @@ type Shell = {|
|
|||
|};
|
||||
|
||||
export function initDevTools(shell: Shell) {
|
||||
shell.connect((bridge: Bridge) => {
|
||||
shell.connect((bridge: FrontendBridge) => {
|
||||
// TODO ...
|
||||
});
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
import EventEmitter from 'events';
|
||||
import { inspect } from 'util';
|
||||
import Bridge from 'src/bridge';
|
||||
import {
|
||||
TREE_OPERATION_ADD,
|
||||
TREE_OPERATION_REMOVE,
|
||||
|
@ -24,6 +23,7 @@ import ProfilerStore from './ProfilerStore';
|
|||
|
||||
import type { Element } from './views/Components/types';
|
||||
import type { ComponentFilter, ElementType } from '../types';
|
||||
import type { FrontendBridge } from 'src/bridge';
|
||||
|
||||
const debug = (methodName, ...args) => {
|
||||
if (__DEBUG__) {
|
||||
|
@ -71,7 +71,7 @@ export default class Store extends EventEmitter<{|
|
|||
supportsProfiling: [],
|
||||
supportsReloadAndProfile: [],
|
||||
|}> {
|
||||
_bridge: Bridge;
|
||||
_bridge: FrontendBridge;
|
||||
|
||||
_captureScreenshots: boolean = false;
|
||||
|
||||
|
@ -129,7 +129,7 @@ export default class Store extends EventEmitter<{|
|
|||
// Used for windowing purposes.
|
||||
_weightAcrossRoots: number = 0;
|
||||
|
||||
constructor(bridge: Bridge, config?: Config) {
|
||||
constructor(bridge: FrontendBridge, config?: Config) {
|
||||
super();
|
||||
|
||||
if (__DEBUG__) {
|
||||
|
@ -701,7 +701,7 @@ export default class Store extends EventEmitter<{|
|
|||
validAttributes,
|
||||
}: {|
|
||||
isSupported: boolean,
|
||||
validAttributes: $ReadOnlyArray<string>,
|
||||
validAttributes: ?$ReadOnlyArray<string>,
|
||||
|}) => {
|
||||
this._isNativeStyleEditorSupported = isSupported;
|
||||
this._nativeStyleEditorValidAttributes = validAttributes || null;
|
||||
|
|
|
@ -221,6 +221,7 @@ function HookView({
|
|||
if (canEditHooks && isStateEditable) {
|
||||
overrideValueFn = (absolutePath: Array<string | number>, value: any) => {
|
||||
const rendererID = store.getRendererIDForElement(id);
|
||||
if (rendererID !== null) {
|
||||
bridge.send('overrideHookState', {
|
||||
id,
|
||||
hookID,
|
||||
|
@ -232,6 +233,7 @@ function HookView({
|
|||
rendererID,
|
||||
value,
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -85,7 +85,9 @@ function InspectedElementContextController({ children }: Props) {
|
|||
const getInspectedElementPath = useCallback<GetInspectedElementPath>(
|
||||
(id: number, path: Array<string | number>) => {
|
||||
const rendererID = store.getRendererIDForElement(id);
|
||||
if (rendererID !== null) {
|
||||
bridge.send('inspectElement', { id, path, rendererID });
|
||||
}
|
||||
},
|
||||
[bridge, store]
|
||||
);
|
||||
|
@ -232,7 +234,9 @@ function InspectedElementContextController({ children }: Props) {
|
|||
const sendRequest = () => {
|
||||
timeoutID = null;
|
||||
|
||||
if (rendererID !== null) {
|
||||
bridge.send('inspectElement', { id: selectedElementID, rendererID });
|
||||
}
|
||||
};
|
||||
|
||||
// Send the initial inspection request.
|
||||
|
@ -240,7 +244,9 @@ function InspectedElementContextController({ children }: Props) {
|
|||
sendRequest();
|
||||
|
||||
// Update the $r variable.
|
||||
if (rendererID !== null) {
|
||||
bridge.send('selectElement', { id: selectedElementID, rendererID });
|
||||
}
|
||||
|
||||
const onInspectedElement = (data: InspectedElementPayload) => {
|
||||
// If this is the element we requested, wait a little bit and then ask for another update.
|
||||
|
|
|
@ -25,22 +25,28 @@ export default function StyleEditor({ id, style }: Props) {
|
|||
const store = useContext(StoreContext);
|
||||
|
||||
const changeAttribute = (oldName: string, newName: string, value: any) => {
|
||||
const rendererID = store.getRendererIDForElement(id);
|
||||
if (rendererID !== null) {
|
||||
bridge.send('NativeStyleEditor_renameAttribute', {
|
||||
id,
|
||||
rendererID: store.getRendererIDForElement(id),
|
||||
rendererID,
|
||||
oldName,
|
||||
newName,
|
||||
value,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const changeValue = (name: string, value: any) => {
|
||||
const rendererID = store.getRendererIDForElement(id);
|
||||
if (rendererID !== null) {
|
||||
bridge.send('NativeStyleEditor_setValue', {
|
||||
id,
|
||||
rendererID: store.getRendererIDForElement(id),
|
||||
rendererID,
|
||||
name,
|
||||
value,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const keys = useMemo(() => Array.from(Object.keys(style)), [style]);
|
||||
|
|
|
@ -136,10 +136,12 @@ function NativeStyleContextController({ children }: Props) {
|
|||
const sendRequest = () => {
|
||||
timeoutID = null;
|
||||
|
||||
if (rendererID !== null) {
|
||||
bridge.send('NativeStyleEditor_measure', {
|
||||
id: selectedElementID,
|
||||
rendererID,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Send the initial measurement request.
|
||||
|
|
|
@ -106,9 +106,10 @@ function OwnersListContextController({ children }: Props) {
|
|||
useEffect(() => {
|
||||
if (ownerID !== null) {
|
||||
const rendererID = store.getRendererIDForElement(ownerID);
|
||||
|
||||
if (rendererID !== null) {
|
||||
bridge.send('getOwnersList', { id: ownerID, rendererID });
|
||||
}
|
||||
}
|
||||
|
||||
return () => {};
|
||||
}, [bridge, ownerID, store]);
|
||||
|
|
|
@ -140,13 +140,19 @@ export default function SelectedElement(_: Props) {
|
|||
});
|
||||
}
|
||||
|
||||
const rendererID = store.getRendererIDForElement(
|
||||
nearestSuspenseElementID
|
||||
);
|
||||
|
||||
// Toggle suspended
|
||||
if (rendererID !== null) {
|
||||
bridge.send('overrideSuspense', {
|
||||
id: nearestSuspenseElementID,
|
||||
rendererID: store.getRendererIDForElement(nearestSuspenseElementID),
|
||||
rendererID,
|
||||
forceFallback: !isSuspended,
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [bridge, dispatch, element, isSuspended, modalDialogDispatch, store]);
|
||||
|
||||
if (element === null) {
|
||||
|
@ -280,15 +286,21 @@ function InspectedElementView({
|
|||
if (type === ElementTypeClass) {
|
||||
overrideContextFn = (path: Array<string | number>, value: any) => {
|
||||
const rendererID = store.getRendererIDForElement(id);
|
||||
if (rendererID !== null) {
|
||||
bridge.send('overrideContext', { id, path, rendererID, value });
|
||||
}
|
||||
};
|
||||
overridePropsFn = (path: Array<string | number>, value: any) => {
|
||||
const rendererID = store.getRendererIDForElement(id);
|
||||
if (rendererID !== null) {
|
||||
bridge.send('overrideProps', { id, path, rendererID, value });
|
||||
}
|
||||
};
|
||||
overrideStateFn = (path: Array<string | number>, value: any) => {
|
||||
const rendererID = store.getRendererIDForElement(id);
|
||||
if (rendererID !== null) {
|
||||
bridge.send('overrideState', { id, path, rendererID, value });
|
||||
}
|
||||
};
|
||||
} else if (
|
||||
(type === ElementTypeFunction ||
|
||||
|
@ -298,7 +310,9 @@ function InspectedElementView({
|
|||
) {
|
||||
overridePropsFn = (path: Array<string | number>, value: any) => {
|
||||
const rendererID = store.getRendererIDForElement(id);
|
||||
if (rendererID !== null) {
|
||||
bridge.send('overrideProps', { id, path, rendererID, value });
|
||||
}
|
||||
};
|
||||
} else if (type === ElementTypeSuspense && canToggleSuspense) {
|
||||
overrideSuspenseFn = (path: Array<string | number>, value: boolean) => {
|
||||
|
@ -306,7 +320,13 @@ function InspectedElementView({
|
|||
throw new Error('Unexpected path.');
|
||||
}
|
||||
const rendererID = store.getRendererIDForElement(id);
|
||||
bridge.send('overrideSuspense', { id, rendererID, forceFallback: value });
|
||||
if (rendererID !== null) {
|
||||
bridge.send('overrideSuspense', {
|
||||
id,
|
||||
rendererID,
|
||||
forceFallback: value,
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -192,7 +192,7 @@ export default function Tree(props: Props) {
|
|||
(id: number) => {
|
||||
const element = store.getElementByID(id);
|
||||
const rendererID = store.getRendererIDForElement(id);
|
||||
if (element !== null) {
|
||||
if (element !== null && rendererID !== null) {
|
||||
bridge.send('highlightNativeElement', {
|
||||
displayName: element.displayName,
|
||||
hideAfterTimeout: false,
|
||||
|
|
|
@ -6,7 +6,6 @@ import '@reach/menu-button/styles.css';
|
|||
import '@reach/tooltip/styles.css';
|
||||
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import Bridge from 'src/bridge';
|
||||
import Store from '../store';
|
||||
import { BridgeContext, StoreContext } from './context';
|
||||
import Components from './Components/Components';
|
||||
|
@ -25,6 +24,7 @@ import styles from './DevTools.css';
|
|||
import './root.css';
|
||||
|
||||
import type { InspectedElement } from 'src/devtools/views/Components/types';
|
||||
import type { FrontendBridge } from 'src/bridge';
|
||||
|
||||
export type BrowserTheme = 'dark' | 'light';
|
||||
export type TabID = 'components' | 'profiler' | 'settings';
|
||||
|
@ -34,7 +34,7 @@ export type ViewElementSource = (
|
|||
) => void;
|
||||
|
||||
export type Props = {|
|
||||
bridge: Bridge,
|
||||
bridge: FrontendBridge,
|
||||
browserTheme?: BrowserTheme,
|
||||
defaultTab?: TabID,
|
||||
showTabBar?: boolean,
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
// @flow
|
||||
|
||||
import { createContext } from 'react';
|
||||
import Bridge from 'src/bridge';
|
||||
|
||||
import Store from '../store';
|
||||
|
||||
export const BridgeContext = createContext<Bridge>(((null: any): Bridge));
|
||||
import type { FrontendBridge } from 'src/bridge';
|
||||
|
||||
export const BridgeContext = createContext<FrontendBridge>(
|
||||
((null: any): FrontendBridge)
|
||||
);
|
||||
BridgeContext.displayName = 'BridgeContext';
|
||||
|
||||
export const StoreContext = createContext<Store>(((null: any): Store));
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -59,6 +59,50 @@ function getScrollbarSize(recalculate) {
|
|||
|
||||
return size;
|
||||
}
|
||||
var cachedRTLResult = null; // TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements.
|
||||
// Chrome does not seem to adhere; its scrollLeft values are positive (measured relative to the left).
|
||||
// Safari's elastic bounce makes detecting this even more complicated wrt potential false positives.
|
||||
// The safest way to check this is to intentionally set a negative offset,
|
||||
// and then verify that the subsequent "scroll" event matches the negative offset.
|
||||
// If it does not match, then we can assume a non-standard RTL scroll implementation.
|
||||
|
||||
function getRTLOffsetType(recalculate) {
|
||||
if (recalculate === void 0) {
|
||||
recalculate = false;
|
||||
}
|
||||
|
||||
if (cachedRTLResult === null || recalculate) {
|
||||
var outerDiv = document.createElement('div');
|
||||
var outerStyle = outerDiv.style;
|
||||
outerStyle.width = '50px';
|
||||
outerStyle.height = '50px';
|
||||
outerStyle.overflow = 'scroll';
|
||||
outerStyle.direction = 'rtl';
|
||||
var innerDiv = document.createElement('div');
|
||||
var innerStyle = innerDiv.style;
|
||||
innerStyle.width = '100px';
|
||||
innerStyle.height = '100px';
|
||||
outerDiv.appendChild(innerDiv);
|
||||
document.body.appendChild(outerDiv);
|
||||
|
||||
if (outerDiv.scrollLeft > 0) {
|
||||
cachedRTLResult = 'positive-descending';
|
||||
} else {
|
||||
outerDiv.scrollLeft = 1;
|
||||
|
||||
if (outerDiv.scrollLeft === 0) {
|
||||
cachedRTLResult = 'negative';
|
||||
} else {
|
||||
cachedRTLResult = 'positive-ascending';
|
||||
}
|
||||
}
|
||||
|
||||
document.body.removeChild(outerDiv);
|
||||
return cachedRTLResult;
|
||||
}
|
||||
|
||||
return cachedRTLResult;
|
||||
}
|
||||
|
||||
var IS_SCROLLING_DEBOUNCE_INTERVAL = 150;
|
||||
|
||||
|
@ -72,6 +116,7 @@ var defaultItemKey = function defaultItemKey(_ref) {
|
|||
|
||||
|
||||
var devWarningsOverscanCount = null;
|
||||
var devWarningsOverscanRowsColumnsCount = null;
|
||||
var devWarningsTagName = null;
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
|
@ -79,6 +124,9 @@ if (process.env.NODE_ENV !== 'production') {
|
|||
devWarningsOverscanCount =
|
||||
/*#__PURE__*/
|
||||
new WeakSet();
|
||||
devWarningsOverscanRowsColumnsCount =
|
||||
/*#__PURE__*/
|
||||
new WeakSet();
|
||||
devWarningsTagName =
|
||||
/*#__PURE__*/
|
||||
new WeakSet();
|
||||
|
@ -183,9 +231,11 @@ function createGridComponent(_ref2) {
|
|||
|
||||
_this._onScroll = function (event) {
|
||||
var _event$currentTarget = event.currentTarget,
|
||||
clientHeight = _event$currentTarget.clientHeight,
|
||||
clientWidth = _event$currentTarget.clientWidth,
|
||||
scrollLeft = _event$currentTarget.scrollLeft,
|
||||
scrollTop = _event$currentTarget.scrollTop,
|
||||
scrollHeight = _event$currentTarget.scrollHeight,
|
||||
scrollWidth = _event$currentTarget.scrollWidth;
|
||||
|
||||
_this.setState(function (prevState) {
|
||||
|
@ -196,25 +246,33 @@ function createGridComponent(_ref2) {
|
|||
return null;
|
||||
}
|
||||
|
||||
var direction = _this.props.direction; // HACK According to the spec, scrollLeft should be negative for RTL aligned elements.
|
||||
// Chrome does not seem to adhere; its scrollLeft values are positive (measured relative to the left).
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollLeft
|
||||
var direction = _this.props.direction; // TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements.
|
||||
// This is not the case for all browsers though (e.g. Chrome reports values as positive, measured relative to the left).
|
||||
// It's also easier for this component if we convert offsets to the same format as they would be in for ltr.
|
||||
// So the simplest solution is to determine which browser behavior we're dealing with, and convert based on it.
|
||||
|
||||
var calculatedScrollLeft = scrollLeft;
|
||||
|
||||
if (direction === 'rtl') {
|
||||
if (scrollLeft <= 0) {
|
||||
switch (getRTLOffsetType()) {
|
||||
case 'negative':
|
||||
calculatedScrollLeft = -scrollLeft;
|
||||
} else {
|
||||
calculatedScrollLeft = scrollWidth - clientWidth - scrollLeft;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'positive-descending':
|
||||
calculatedScrollLeft = scrollWidth - clientWidth - scrollLeft;
|
||||
break;
|
||||
}
|
||||
} // Prevent Safari's elastic scrolling from causing visual shaking when scrolling past bounds.
|
||||
|
||||
|
||||
calculatedScrollLeft = Math.max(0, Math.min(calculatedScrollLeft, scrollWidth - clientWidth));
|
||||
var calculatedScrollTop = Math.max(0, Math.min(scrollTop, scrollHeight - clientHeight));
|
||||
return {
|
||||
isScrolling: true,
|
||||
horizontalScrollDirection: prevState.scrollLeft < scrollLeft ? 'forward' : 'backward',
|
||||
scrollLeft: calculatedScrollLeft,
|
||||
scrollTop: scrollTop,
|
||||
scrollTop: calculatedScrollTop,
|
||||
verticalScrollDirection: prevState.scrollTop < scrollTop ? 'forward' : 'backward',
|
||||
scrollUpdateWasRequested: false
|
||||
};
|
||||
|
@ -339,26 +397,55 @@ function createGridComponent(_ref2) {
|
|||
initialScrollLeft = _this$props3.initialScrollLeft,
|
||||
initialScrollTop = _this$props3.initialScrollTop;
|
||||
|
||||
if (typeof initialScrollLeft === 'number' && this._outerRef != null) {
|
||||
this._outerRef.scrollLeft = initialScrollLeft;
|
||||
if (this._outerRef != null) {
|
||||
var outerRef = this._outerRef;
|
||||
|
||||
if (typeof initialScrollLeft === 'number') {
|
||||
outerRef.scrollLeft = initialScrollLeft;
|
||||
}
|
||||
|
||||
if (typeof initialScrollTop === 'number' && this._outerRef != null) {
|
||||
this._outerRef.scrollTop = initialScrollTop;
|
||||
if (typeof initialScrollTop === 'number') {
|
||||
outerRef.scrollTop = initialScrollTop;
|
||||
}
|
||||
}
|
||||
|
||||
this._callPropsCallbacks();
|
||||
};
|
||||
|
||||
_proto.componentDidUpdate = function componentDidUpdate() {
|
||||
var direction = this.props.direction;
|
||||
var _this$state2 = this.state,
|
||||
scrollLeft = _this$state2.scrollLeft,
|
||||
scrollTop = _this$state2.scrollTop,
|
||||
scrollUpdateWasRequested = _this$state2.scrollUpdateWasRequested;
|
||||
|
||||
if (scrollUpdateWasRequested && this._outerRef !== null) {
|
||||
this._outerRef.scrollLeft = scrollLeft;
|
||||
this._outerRef.scrollTop = scrollTop;
|
||||
if (scrollUpdateWasRequested && this._outerRef != null) {
|
||||
// TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements.
|
||||
// This is not the case for all browsers though (e.g. Chrome reports values as positive, measured relative to the left).
|
||||
// So we need to determine which browser behavior we're dealing with, and mimic it.
|
||||
var outerRef = this._outerRef;
|
||||
|
||||
if (direction === 'rtl') {
|
||||
switch (getRTLOffsetType()) {
|
||||
case 'negative':
|
||||
outerRef.scrollLeft = -scrollLeft;
|
||||
break;
|
||||
|
||||
case 'positive-ascending':
|
||||
outerRef.scrollLeft = scrollLeft;
|
||||
break;
|
||||
|
||||
default:
|
||||
var clientWidth = outerRef.clientWidth,
|
||||
scrollWidth = outerRef.scrollWidth;
|
||||
outerRef.scrollLeft = scrollWidth - clientWidth - scrollLeft;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
outerRef.scrollLeft = Math.max(0, scrollLeft);
|
||||
}
|
||||
|
||||
outerRef.scrollTop = Math.max(0, scrollTop);
|
||||
}
|
||||
|
||||
this._callPropsCallbacks();
|
||||
|
@ -442,7 +529,7 @@ function createGridComponent(_ref2) {
|
|||
ref: innerRef,
|
||||
style: {
|
||||
height: estimatedTotalHeight,
|
||||
pointerEvents: isScrolling ? 'none' : '',
|
||||
pointerEvents: isScrolling ? 'none' : undefined,
|
||||
width: estimatedTotalWidth
|
||||
}
|
||||
}));
|
||||
|
@ -492,6 +579,7 @@ function createGridComponent(_ref2) {
|
|||
_proto._getHorizontalRangeToRender = function _getHorizontalRangeToRender() {
|
||||
var _this$props6 = this.props,
|
||||
columnCount = _this$props6.columnCount,
|
||||
overscanColumnCount = _this$props6.overscanColumnCount,
|
||||
overscanColumnsCount = _this$props6.overscanColumnsCount,
|
||||
overscanCount = _this$props6.overscanCount,
|
||||
rowCount = _this$props6.rowCount;
|
||||
|
@ -499,7 +587,7 @@ function createGridComponent(_ref2) {
|
|||
horizontalScrollDirection = _this$state4.horizontalScrollDirection,
|
||||
isScrolling = _this$state4.isScrolling,
|
||||
scrollLeft = _this$state4.scrollLeft;
|
||||
var overscanCountResolved = overscanColumnsCount || overscanCount || 1;
|
||||
var overscanCountResolved = overscanColumnCount || overscanColumnsCount || overscanCount || 1;
|
||||
|
||||
if (columnCount === 0 || rowCount === 0) {
|
||||
return [0, 0, 0, 0];
|
||||
|
@ -518,13 +606,14 @@ function createGridComponent(_ref2) {
|
|||
var _this$props7 = this.props,
|
||||
columnCount = _this$props7.columnCount,
|
||||
overscanCount = _this$props7.overscanCount,
|
||||
overscanRowCount = _this$props7.overscanRowCount,
|
||||
overscanRowsCount = _this$props7.overscanRowsCount,
|
||||
rowCount = _this$props7.rowCount;
|
||||
var _this$state5 = this.state,
|
||||
isScrolling = _this$state5.isScrolling,
|
||||
verticalScrollDirection = _this$state5.verticalScrollDirection,
|
||||
scrollTop = _this$state5.scrollTop;
|
||||
var overscanCountResolved = overscanRowsCount || overscanCount || 1;
|
||||
var overscanCountResolved = overscanRowCount || overscanRowsCount || overscanCount || 1;
|
||||
|
||||
if (columnCount === 0 || rowCount === 0) {
|
||||
return [0, 0, 0, 0];
|
||||
|
@ -553,7 +642,9 @@ var validateSharedProps = function validateSharedProps(_ref5, _ref6) {
|
|||
height = _ref5.height,
|
||||
innerTagName = _ref5.innerTagName,
|
||||
outerTagName = _ref5.outerTagName,
|
||||
overscanColumnsCount = _ref5.overscanColumnsCount,
|
||||
overscanCount = _ref5.overscanCount,
|
||||
overscanRowsCount = _ref5.overscanRowsCount,
|
||||
width = _ref5.width;
|
||||
var instance = _ref6.instance;
|
||||
|
||||
|
@ -561,7 +652,14 @@ var validateSharedProps = function validateSharedProps(_ref5, _ref6) {
|
|||
if (typeof overscanCount === 'number') {
|
||||
if (devWarningsOverscanCount && !devWarningsOverscanCount.has(instance)) {
|
||||
devWarningsOverscanCount.add(instance);
|
||||
console.warn('The overscanCount prop has been deprecated. ' + 'Please use the overscanColumnsCount and overscanRowsCount props instead.');
|
||||
console.warn('The overscanCount prop has been deprecated. ' + 'Please use the overscanColumnCount and overscanRowCount props instead.');
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof overscanColumnsCount === 'number' || typeof overscanRowsCount === 'number') {
|
||||
if (devWarningsOverscanRowsColumnsCount && !devWarningsOverscanRowsColumnsCount.has(instance)) {
|
||||
devWarningsOverscanRowsColumnsCount.add(instance);
|
||||
console.warn('The overscanColumnsCount and overscanRowsCount props have been deprecated. ' + 'Please use the overscanColumnCount and overscanRowCount props instead.');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -770,7 +868,11 @@ var getOffsetForIndexAndAlignment = function getOffsetForIndexAndAlignment(itemT
|
|||
default:
|
||||
if (scrollOffset >= minOffset && scrollOffset <= maxOffset) {
|
||||
return scrollOffset;
|
||||
} else if (scrollOffset - minOffset < maxOffset - scrollOffset) {
|
||||
} else if (minOffset > maxOffset) {
|
||||
// Because we only take into account the scrollbar size when calculating minOffset
|
||||
// this value can be larger than maxOffset when at the end of the list
|
||||
return minOffset;
|
||||
} else if (scrollOffset < minOffset) {
|
||||
return minOffset;
|
||||
} else {
|
||||
return maxOffset;
|
||||
|
@ -1037,20 +1139,27 @@ function createListComponent(_ref) {
|
|||
return null;
|
||||
}
|
||||
|
||||
var direction = _this.props.direction; // HACK According to the spec, scrollLeft should be negative for RTL aligned elements.
|
||||
// Chrome does not seem to adhere; its scrolLeft values are positive (measured relative to the left).
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollLeft
|
||||
|
||||
var direction = _this.props.direction;
|
||||
var scrollOffset = scrollLeft;
|
||||
|
||||
if (direction === 'rtl') {
|
||||
if (scrollLeft <= 0) {
|
||||
scrollOffset = -scrollOffset;
|
||||
} else {
|
||||
scrollOffset = scrollWidth - clientWidth - scrollLeft;
|
||||
}
|
||||
}
|
||||
// TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements.
|
||||
// This is not the case for all browsers though (e.g. Chrome reports values as positive, measured relative to the left).
|
||||
// It's also easier for this component if we convert offsets to the same format as they would be in for ltr.
|
||||
// So the simplest solution is to determine which browser behavior we're dealing with, and convert based on it.
|
||||
switch (getRTLOffsetType()) {
|
||||
case 'negative':
|
||||
scrollOffset = -scrollLeft;
|
||||
break;
|
||||
|
||||
case 'positive-descending':
|
||||
scrollOffset = scrollWidth - clientWidth - scrollLeft;
|
||||
break;
|
||||
}
|
||||
} // Prevent Safari's elastic scrolling from causing visual shaking when scrolling past bounds.
|
||||
|
||||
|
||||
scrollOffset = Math.max(0, Math.min(scrollOffset, scrollWidth - clientWidth));
|
||||
return {
|
||||
isScrolling: true,
|
||||
scrollDirection: prevState.scrollOffset < scrollLeft ? 'forward' : 'backward',
|
||||
|
@ -1061,7 +1170,10 @@ function createListComponent(_ref) {
|
|||
};
|
||||
|
||||
_this._onScrollVertical = function (event) {
|
||||
var scrollTop = event.currentTarget.scrollTop;
|
||||
var _event$currentTarget2 = event.currentTarget,
|
||||
clientHeight = _event$currentTarget2.clientHeight,
|
||||
scrollHeight = _event$currentTarget2.scrollHeight,
|
||||
scrollTop = _event$currentTarget2.scrollTop;
|
||||
|
||||
_this.setState(function (prevState) {
|
||||
if (prevState.scrollOffset === scrollTop) {
|
||||
|
@ -1069,12 +1181,14 @@ function createListComponent(_ref) {
|
|||
// In which case we don't need to trigger another render,
|
||||
// And we don't want to update state.isScrolling.
|
||||
return null;
|
||||
}
|
||||
} // Prevent Safari's elastic scrolling from causing visual shaking when scrolling past bounds.
|
||||
|
||||
|
||||
var scrollOffset = Math.max(0, Math.min(scrollTop, scrollHeight - clientHeight));
|
||||
return {
|
||||
isScrolling: true,
|
||||
scrollDirection: prevState.scrollOffset < scrollTop ? 'forward' : 'backward',
|
||||
scrollOffset: scrollTop,
|
||||
scrollDirection: prevState.scrollOffset < scrollOffset ? 'forward' : 'backward',
|
||||
scrollOffset: scrollOffset,
|
||||
scrollUpdateWasRequested: false
|
||||
};
|
||||
}, _this._resetIsScrollingDebounced);
|
||||
|
@ -1154,12 +1268,13 @@ function createListComponent(_ref) {
|
|||
initialScrollOffset = _this$props2.initialScrollOffset,
|
||||
layout = _this$props2.layout;
|
||||
|
||||
if (typeof initialScrollOffset === 'number' && this._outerRef !== null) {
|
||||
// TODO Deprecate direction "horizontal"
|
||||
if (typeof initialScrollOffset === 'number' && this._outerRef != null) {
|
||||
var outerRef = this._outerRef; // TODO Deprecate direction "horizontal"
|
||||
|
||||
if (direction === 'horizontal' || layout === 'horizontal') {
|
||||
this._outerRef.scrollLeft = initialScrollOffset;
|
||||
outerRef.scrollLeft = initialScrollOffset;
|
||||
} else {
|
||||
this._outerRef.scrollTop = initialScrollOffset;
|
||||
outerRef.scrollTop = initialScrollOffset;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1174,12 +1289,34 @@ function createListComponent(_ref) {
|
|||
scrollOffset = _this$state.scrollOffset,
|
||||
scrollUpdateWasRequested = _this$state.scrollUpdateWasRequested;
|
||||
|
||||
if (scrollUpdateWasRequested && this._outerRef !== null) {
|
||||
// TODO Deprecate direction "horizontal"
|
||||
if (scrollUpdateWasRequested && this._outerRef != null) {
|
||||
var outerRef = this._outerRef; // TODO Deprecate direction "horizontal"
|
||||
|
||||
if (direction === 'horizontal' || layout === 'horizontal') {
|
||||
this._outerRef.scrollLeft = scrollOffset;
|
||||
if (direction === 'rtl') {
|
||||
// TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements.
|
||||
// This is not the case for all browsers though (e.g. Chrome reports values as positive, measured relative to the left).
|
||||
// So we need to determine which browser behavior we're dealing with, and mimic it.
|
||||
switch (getRTLOffsetType()) {
|
||||
case 'negative':
|
||||
outerRef.scrollLeft = -scrollOffset;
|
||||
break;
|
||||
|
||||
case 'positive-ascending':
|
||||
outerRef.scrollLeft = scrollOffset;
|
||||
break;
|
||||
|
||||
default:
|
||||
var clientWidth = outerRef.clientWidth,
|
||||
scrollWidth = outerRef.scrollWidth;
|
||||
outerRef.scrollLeft = scrollWidth - clientWidth - scrollOffset;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
this._outerRef.scrollTop = scrollOffset;
|
||||
outerRef.scrollLeft = scrollOffset;
|
||||
}
|
||||
} else {
|
||||
outerRef.scrollTop = scrollOffset;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1255,7 +1392,7 @@ function createListComponent(_ref) {
|
|||
ref: innerRef,
|
||||
style: {
|
||||
height: isHorizontal ? '100%' : estimatedTotalSize,
|
||||
pointerEvents: isScrolling ? 'none' : '',
|
||||
pointerEvents: isScrolling ? 'none' : undefined,
|
||||
width: isHorizontal ? estimatedTotalSize : '100%'
|
||||
}
|
||||
}));
|
||||
|
@ -1537,7 +1674,7 @@ createListComponent({
|
|||
default:
|
||||
if (scrollOffset >= minOffset && scrollOffset <= maxOffset) {
|
||||
return scrollOffset;
|
||||
} else if (scrollOffset - minOffset < maxOffset - scrollOffset) {
|
||||
} else if (scrollOffset < minOffset) {
|
||||
return minOffset;
|
||||
} else {
|
||||
return maxOffset;
|
||||
|
@ -1642,7 +1779,8 @@ createGridComponent({
|
|||
var columnCount = _ref7.columnCount,
|
||||
columnWidth = _ref7.columnWidth,
|
||||
width = _ref7.width;
|
||||
var maxOffset = Math.max(0, Math.min(columnCount * columnWidth - width, columnIndex * columnWidth));
|
||||
var lastColumnOffset = Math.max(0, columnCount * columnWidth - width);
|
||||
var maxOffset = Math.min(lastColumnOffset, columnIndex * columnWidth);
|
||||
var minOffset = Math.max(0, columnIndex * columnWidth - width + scrollbarSize + columnWidth);
|
||||
|
||||
if (align === 'smart') {
|
||||
|
@ -1661,13 +1799,27 @@ createGridComponent({
|
|||
return minOffset;
|
||||
|
||||
case 'center':
|
||||
return Math.round(minOffset + (maxOffset - minOffset) / 2);
|
||||
// "Centered" offset is usually the average of the min and max.
|
||||
// But near the edges of the list, this doesn't hold true.
|
||||
var middleOffset = Math.round(minOffset + (maxOffset - minOffset) / 2);
|
||||
|
||||
if (middleOffset < Math.ceil(width / 2)) {
|
||||
return 0; // near the beginning
|
||||
} else if (middleOffset > lastColumnOffset + Math.floor(width / 2)) {
|
||||
return lastColumnOffset; // near the end
|
||||
} else {
|
||||
return middleOffset;
|
||||
}
|
||||
|
||||
case 'auto':
|
||||
default:
|
||||
if (scrollLeft >= minOffset && scrollLeft <= maxOffset) {
|
||||
return scrollLeft;
|
||||
} else if (scrollLeft - minOffset < maxOffset - scrollLeft) {
|
||||
} else if (minOffset > maxOffset) {
|
||||
// Because we only take into account the scrollbar size when calculating minOffset
|
||||
// this value can be larger than maxOffset when at the end of the list
|
||||
return minOffset;
|
||||
} else if (scrollLeft < minOffset) {
|
||||
return minOffset;
|
||||
} else {
|
||||
return maxOffset;
|
||||
|
@ -1679,7 +1831,8 @@ createGridComponent({
|
|||
var rowHeight = _ref8.rowHeight,
|
||||
height = _ref8.height,
|
||||
rowCount = _ref8.rowCount;
|
||||
var maxOffset = Math.max(0, Math.min(rowCount * rowHeight - height, rowIndex * rowHeight));
|
||||
var lastRowOffset = Math.max(0, rowCount * rowHeight - height);
|
||||
var maxOffset = Math.min(lastRowOffset, rowIndex * rowHeight);
|
||||
var minOffset = Math.max(0, rowIndex * rowHeight - height + scrollbarSize + rowHeight);
|
||||
|
||||
if (align === 'smart') {
|
||||
|
@ -1698,13 +1851,27 @@ createGridComponent({
|
|||
return minOffset;
|
||||
|
||||
case 'center':
|
||||
return Math.round(minOffset + (maxOffset - minOffset) / 2);
|
||||
// "Centered" offset is usually the average of the min and max.
|
||||
// But near the edges of the list, this doesn't hold true.
|
||||
var middleOffset = Math.round(minOffset + (maxOffset - minOffset) / 2);
|
||||
|
||||
if (middleOffset < Math.ceil(height / 2)) {
|
||||
return 0; // near the beginning
|
||||
} else if (middleOffset > lastRowOffset + Math.floor(height / 2)) {
|
||||
return lastRowOffset; // near the end
|
||||
} else {
|
||||
return middleOffset;
|
||||
}
|
||||
|
||||
case 'auto':
|
||||
default:
|
||||
if (scrollTop >= minOffset && scrollTop <= maxOffset) {
|
||||
return scrollTop;
|
||||
} else if (scrollTop - minOffset < maxOffset - scrollTop) {
|
||||
} else if (minOffset > maxOffset) {
|
||||
// Because we only take into account the scrollbar size when calculating minOffset
|
||||
// this value can be larger than maxOffset when at the end of the list
|
||||
return minOffset;
|
||||
} else if (scrollTop < minOffset) {
|
||||
return minOffset;
|
||||
} else {
|
||||
return maxOffset;
|
||||
|
@ -1722,7 +1889,9 @@ createGridComponent({
|
|||
columnCount = _ref10.columnCount,
|
||||
width = _ref10.width;
|
||||
var left = startIndex * columnWidth;
|
||||
return Math.max(0, Math.min(columnCount - 1, startIndex + Math.floor((width + (scrollLeft - left)) / columnWidth)));
|
||||
var numVisibleColumns = Math.ceil((width + scrollLeft - left) / columnWidth);
|
||||
return Math.max(0, Math.min(columnCount - 1, startIndex + numVisibleColumns - 1 // -1 is because stop index is inclusive
|
||||
));
|
||||
},
|
||||
getRowStartIndexForOffset: function getRowStartIndexForOffset(_ref11, scrollTop) {
|
||||
var rowHeight = _ref11.rowHeight,
|
||||
|
@ -1733,8 +1902,10 @@ createGridComponent({
|
|||
var rowHeight = _ref12.rowHeight,
|
||||
rowCount = _ref12.rowCount,
|
||||
height = _ref12.height;
|
||||
var left = startIndex * rowHeight;
|
||||
return Math.max(0, Math.min(rowCount - 1, startIndex + Math.floor((height + (scrollTop - left)) / rowHeight)));
|
||||
var top = startIndex * rowHeight;
|
||||
var numVisibleRows = Math.ceil((height + scrollTop - top) / rowHeight);
|
||||
return Math.max(0, Math.min(rowCount - 1, startIndex + numVisibleRows - 1 // -1 is because stop index is inclusive
|
||||
));
|
||||
},
|
||||
initInstanceProps: function initInstanceProps(props) {// Noop
|
||||
},
|
||||
|
@ -1759,13 +1930,11 @@ var FixedSizeList =
|
|||
/*#__PURE__*/
|
||||
createListComponent({
|
||||
getItemOffset: function getItemOffset(_ref, index) {
|
||||
var itemSize = _ref.itemSize,
|
||||
size = _ref.size;
|
||||
var itemSize = _ref.itemSize;
|
||||
return index * itemSize;
|
||||
},
|
||||
getItemSize: function getItemSize(_ref2, index) {
|
||||
var itemSize = _ref2.itemSize,
|
||||
size = _ref2.size;
|
||||
var itemSize = _ref2.itemSize;
|
||||
return itemSize;
|
||||
},
|
||||
getEstimatedTotalSize: function getEstimatedTotalSize(_ref3) {
|
||||
|
@ -1783,7 +1952,8 @@ createListComponent({
|
|||
// TODO Deprecate direction "horizontal"
|
||||
var isHorizontal = direction === 'horizontal' || layout === 'horizontal';
|
||||
var size = isHorizontal ? width : height;
|
||||
var maxOffset = Math.max(0, Math.min(itemCount * itemSize - size, index * itemSize));
|
||||
var lastItemOffset = Math.max(0, itemCount * itemSize - size);
|
||||
var maxOffset = Math.min(lastItemOffset, index * itemSize);
|
||||
var minOffset = Math.max(0, index * itemSize - size + itemSize);
|
||||
|
||||
if (align === 'smart') {
|
||||
|
@ -1802,13 +1972,25 @@ createListComponent({
|
|||
return minOffset;
|
||||
|
||||
case 'center':
|
||||
return Math.round(minOffset + (maxOffset - minOffset) / 2);
|
||||
{
|
||||
// "Centered" offset is usually the average of the min and max.
|
||||
// But near the edges of the list, this doesn't hold true.
|
||||
var middleOffset = Math.round(minOffset + (maxOffset - minOffset) / 2);
|
||||
|
||||
if (middleOffset < Math.ceil(size / 2)) {
|
||||
return 0; // near the beginning
|
||||
} else if (middleOffset > lastItemOffset + Math.floor(size / 2)) {
|
||||
return lastItemOffset; // near the end
|
||||
} else {
|
||||
return middleOffset;
|
||||
}
|
||||
}
|
||||
|
||||
case 'auto':
|
||||
default:
|
||||
if (scrollOffset >= minOffset && scrollOffset <= maxOffset) {
|
||||
return scrollOffset;
|
||||
} else if (scrollOffset - minOffset < maxOffset - scrollOffset) {
|
||||
} else if (scrollOffset < minOffset) {
|
||||
return minOffset;
|
||||
} else {
|
||||
return maxOffset;
|
||||
|
@ -1832,7 +2014,9 @@ createListComponent({
|
|||
var isHorizontal = direction === 'horizontal' || layout === 'horizontal';
|
||||
var offset = startIndex * itemSize;
|
||||
var size = isHorizontal ? width : height;
|
||||
return Math.max(0, Math.min(itemCount - 1, startIndex + Math.floor((size + (scrollOffset - offset)) / itemSize)));
|
||||
var numVisibleItems = Math.ceil((size + scrollOffset - offset) / itemSize);
|
||||
return Math.max(0, Math.min(itemCount - 1, startIndex + numVisibleItems - 1 // -1 is because stop index is inclusive
|
||||
));
|
||||
},
|
||||
initInstanceProps: function initInstanceProps(props) {// Noop
|
||||
},
|
||||
|
@ -1892,3 +2076,4 @@ exports.FixedSizeGrid = FixedSizeGrid;
|
|||
exports.FixedSizeList = FixedSizeList;
|
||||
exports.areEqual = areEqual;
|
||||
exports.shouldComponentUpdate = shouldComponentUpdate;
|
||||
//# sourceMappingURL=index.cjs.js.map
|
||||
|
|
|
@ -54,6 +54,50 @@ function getScrollbarSize(recalculate) {
|
|||
|
||||
return size;
|
||||
}
|
||||
var cachedRTLResult = null; // TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements.
|
||||
// Chrome does not seem to adhere; its scrollLeft values are positive (measured relative to the left).
|
||||
// Safari's elastic bounce makes detecting this even more complicated wrt potential false positives.
|
||||
// The safest way to check this is to intentionally set a negative offset,
|
||||
// and then verify that the subsequent "scroll" event matches the negative offset.
|
||||
// If it does not match, then we can assume a non-standard RTL scroll implementation.
|
||||
|
||||
function getRTLOffsetType(recalculate) {
|
||||
if (recalculate === void 0) {
|
||||
recalculate = false;
|
||||
}
|
||||
|
||||
if (cachedRTLResult === null || recalculate) {
|
||||
var outerDiv = document.createElement('div');
|
||||
var outerStyle = outerDiv.style;
|
||||
outerStyle.width = '50px';
|
||||
outerStyle.height = '50px';
|
||||
outerStyle.overflow = 'scroll';
|
||||
outerStyle.direction = 'rtl';
|
||||
var innerDiv = document.createElement('div');
|
||||
var innerStyle = innerDiv.style;
|
||||
innerStyle.width = '100px';
|
||||
innerStyle.height = '100px';
|
||||
outerDiv.appendChild(innerDiv);
|
||||
document.body.appendChild(outerDiv);
|
||||
|
||||
if (outerDiv.scrollLeft > 0) {
|
||||
cachedRTLResult = 'positive-descending';
|
||||
} else {
|
||||
outerDiv.scrollLeft = 1;
|
||||
|
||||
if (outerDiv.scrollLeft === 0) {
|
||||
cachedRTLResult = 'negative';
|
||||
} else {
|
||||
cachedRTLResult = 'positive-ascending';
|
||||
}
|
||||
}
|
||||
|
||||
document.body.removeChild(outerDiv);
|
||||
return cachedRTLResult;
|
||||
}
|
||||
|
||||
return cachedRTLResult;
|
||||
}
|
||||
|
||||
var IS_SCROLLING_DEBOUNCE_INTERVAL = 150;
|
||||
|
||||
|
@ -67,6 +111,7 @@ var defaultItemKey = function defaultItemKey(_ref) {
|
|||
|
||||
|
||||
var devWarningsOverscanCount = null;
|
||||
var devWarningsOverscanRowsColumnsCount = null;
|
||||
var devWarningsTagName = null;
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
|
@ -74,6 +119,9 @@ if (process.env.NODE_ENV !== 'production') {
|
|||
devWarningsOverscanCount =
|
||||
/*#__PURE__*/
|
||||
new WeakSet();
|
||||
devWarningsOverscanRowsColumnsCount =
|
||||
/*#__PURE__*/
|
||||
new WeakSet();
|
||||
devWarningsTagName =
|
||||
/*#__PURE__*/
|
||||
new WeakSet();
|
||||
|
@ -178,9 +226,11 @@ function createGridComponent(_ref2) {
|
|||
|
||||
_this._onScroll = function (event) {
|
||||
var _event$currentTarget = event.currentTarget,
|
||||
clientHeight = _event$currentTarget.clientHeight,
|
||||
clientWidth = _event$currentTarget.clientWidth,
|
||||
scrollLeft = _event$currentTarget.scrollLeft,
|
||||
scrollTop = _event$currentTarget.scrollTop,
|
||||
scrollHeight = _event$currentTarget.scrollHeight,
|
||||
scrollWidth = _event$currentTarget.scrollWidth;
|
||||
|
||||
// Force flush sync for scroll updates to reduce visual checkerboarding.
|
||||
|
@ -193,25 +243,33 @@ function createGridComponent(_ref2) {
|
|||
return null;
|
||||
}
|
||||
|
||||
var direction = _this.props.direction; // HACK According to the spec, scrollLeft should be negative for RTL aligned elements.
|
||||
// Chrome does not seem to adhere; its scrollLeft values are positive (measured relative to the left).
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollLeft
|
||||
var direction = _this.props.direction; // TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements.
|
||||
// This is not the case for all browsers though (e.g. Chrome reports values as positive, measured relative to the left).
|
||||
// It's also easier for this component if we convert offsets to the same format as they would be in for ltr.
|
||||
// So the simplest solution is to determine which browser behavior we're dealing with, and convert based on it.
|
||||
|
||||
var calculatedScrollLeft = scrollLeft;
|
||||
|
||||
if (direction === 'rtl') {
|
||||
if (scrollLeft <= 0) {
|
||||
switch (getRTLOffsetType()) {
|
||||
case 'negative':
|
||||
calculatedScrollLeft = -scrollLeft;
|
||||
} else {
|
||||
calculatedScrollLeft = scrollWidth - clientWidth - scrollLeft;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'positive-descending':
|
||||
calculatedScrollLeft = scrollWidth - clientWidth - scrollLeft;
|
||||
break;
|
||||
}
|
||||
} // Prevent Safari's elastic scrolling from causing visual shaking when scrolling past bounds.
|
||||
|
||||
|
||||
calculatedScrollLeft = Math.max(0, Math.min(calculatedScrollLeft, scrollWidth - clientWidth));
|
||||
var calculatedScrollTop = Math.max(0, Math.min(scrollTop, scrollHeight - clientHeight));
|
||||
return {
|
||||
isScrolling: true,
|
||||
horizontalScrollDirection: prevState.scrollLeft < scrollLeft ? 'forward' : 'backward',
|
||||
scrollLeft: calculatedScrollLeft,
|
||||
scrollTop: scrollTop,
|
||||
scrollTop: calculatedScrollTop,
|
||||
verticalScrollDirection: prevState.scrollTop < scrollTop ? 'forward' : 'backward',
|
||||
scrollUpdateWasRequested: false
|
||||
};
|
||||
|
@ -337,26 +395,55 @@ function createGridComponent(_ref2) {
|
|||
initialScrollLeft = _this$props3.initialScrollLeft,
|
||||
initialScrollTop = _this$props3.initialScrollTop;
|
||||
|
||||
if (typeof initialScrollLeft === 'number' && this._outerRef != null) {
|
||||
this._outerRef.scrollLeft = initialScrollLeft;
|
||||
if (this._outerRef != null) {
|
||||
var outerRef = this._outerRef;
|
||||
|
||||
if (typeof initialScrollLeft === 'number') {
|
||||
outerRef.scrollLeft = initialScrollLeft;
|
||||
}
|
||||
|
||||
if (typeof initialScrollTop === 'number' && this._outerRef != null) {
|
||||
this._outerRef.scrollTop = initialScrollTop;
|
||||
if (typeof initialScrollTop === 'number') {
|
||||
outerRef.scrollTop = initialScrollTop;
|
||||
}
|
||||
}
|
||||
|
||||
this._callPropsCallbacks();
|
||||
};
|
||||
|
||||
_proto.componentDidUpdate = function componentDidUpdate() {
|
||||
var direction = this.props.direction;
|
||||
var _this$state2 = this.state,
|
||||
scrollLeft = _this$state2.scrollLeft,
|
||||
scrollTop = _this$state2.scrollTop,
|
||||
scrollUpdateWasRequested = _this$state2.scrollUpdateWasRequested;
|
||||
|
||||
if (scrollUpdateWasRequested && this._outerRef !== null) {
|
||||
this._outerRef.scrollLeft = scrollLeft;
|
||||
this._outerRef.scrollTop = scrollTop;
|
||||
if (scrollUpdateWasRequested && this._outerRef != null) {
|
||||
// TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements.
|
||||
// This is not the case for all browsers though (e.g. Chrome reports values as positive, measured relative to the left).
|
||||
// So we need to determine which browser behavior we're dealing with, and mimic it.
|
||||
var outerRef = this._outerRef;
|
||||
|
||||
if (direction === 'rtl') {
|
||||
switch (getRTLOffsetType()) {
|
||||
case 'negative':
|
||||
outerRef.scrollLeft = -scrollLeft;
|
||||
break;
|
||||
|
||||
case 'positive-ascending':
|
||||
outerRef.scrollLeft = scrollLeft;
|
||||
break;
|
||||
|
||||
default:
|
||||
var clientWidth = outerRef.clientWidth,
|
||||
scrollWidth = outerRef.scrollWidth;
|
||||
outerRef.scrollLeft = scrollWidth - clientWidth - scrollLeft;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
outerRef.scrollLeft = Math.max(0, scrollLeft);
|
||||
}
|
||||
|
||||
outerRef.scrollTop = Math.max(0, scrollTop);
|
||||
}
|
||||
|
||||
this._callPropsCallbacks();
|
||||
|
@ -440,7 +527,7 @@ function createGridComponent(_ref2) {
|
|||
ref: innerRef,
|
||||
style: {
|
||||
height: estimatedTotalHeight,
|
||||
pointerEvents: isScrolling ? 'none' : '',
|
||||
pointerEvents: isScrolling ? 'none' : undefined,
|
||||
width: estimatedTotalWidth
|
||||
}
|
||||
}));
|
||||
|
@ -490,6 +577,7 @@ function createGridComponent(_ref2) {
|
|||
_proto._getHorizontalRangeToRender = function _getHorizontalRangeToRender() {
|
||||
var _this$props6 = this.props,
|
||||
columnCount = _this$props6.columnCount,
|
||||
overscanColumnCount = _this$props6.overscanColumnCount,
|
||||
overscanColumnsCount = _this$props6.overscanColumnsCount,
|
||||
overscanCount = _this$props6.overscanCount,
|
||||
rowCount = _this$props6.rowCount;
|
||||
|
@ -497,7 +585,7 @@ function createGridComponent(_ref2) {
|
|||
horizontalScrollDirection = _this$state4.horizontalScrollDirection,
|
||||
isScrolling = _this$state4.isScrolling,
|
||||
scrollLeft = _this$state4.scrollLeft;
|
||||
var overscanCountResolved = overscanColumnsCount || overscanCount || 1;
|
||||
var overscanCountResolved = overscanColumnCount || overscanColumnsCount || overscanCount || 1;
|
||||
|
||||
if (columnCount === 0 || rowCount === 0) {
|
||||
return [0, 0, 0, 0];
|
||||
|
@ -516,13 +604,14 @@ function createGridComponent(_ref2) {
|
|||
var _this$props7 = this.props,
|
||||
columnCount = _this$props7.columnCount,
|
||||
overscanCount = _this$props7.overscanCount,
|
||||
overscanRowCount = _this$props7.overscanRowCount,
|
||||
overscanRowsCount = _this$props7.overscanRowsCount,
|
||||
rowCount = _this$props7.rowCount;
|
||||
var _this$state5 = this.state,
|
||||
isScrolling = _this$state5.isScrolling,
|
||||
verticalScrollDirection = _this$state5.verticalScrollDirection,
|
||||
scrollTop = _this$state5.scrollTop;
|
||||
var overscanCountResolved = overscanRowsCount || overscanCount || 1;
|
||||
var overscanCountResolved = overscanRowCount || overscanRowsCount || overscanCount || 1;
|
||||
|
||||
if (columnCount === 0 || rowCount === 0) {
|
||||
return [0, 0, 0, 0];
|
||||
|
@ -551,7 +640,9 @@ var validateSharedProps = function validateSharedProps(_ref5, _ref6) {
|
|||
height = _ref5.height,
|
||||
innerTagName = _ref5.innerTagName,
|
||||
outerTagName = _ref5.outerTagName,
|
||||
overscanColumnsCount = _ref5.overscanColumnsCount,
|
||||
overscanCount = _ref5.overscanCount,
|
||||
overscanRowsCount = _ref5.overscanRowsCount,
|
||||
width = _ref5.width;
|
||||
var instance = _ref6.instance;
|
||||
|
||||
|
@ -559,7 +650,14 @@ var validateSharedProps = function validateSharedProps(_ref5, _ref6) {
|
|||
if (typeof overscanCount === 'number') {
|
||||
if (devWarningsOverscanCount && !devWarningsOverscanCount.has(instance)) {
|
||||
devWarningsOverscanCount.add(instance);
|
||||
console.warn('The overscanCount prop has been deprecated. ' + 'Please use the overscanColumnsCount and overscanRowsCount props instead.');
|
||||
console.warn('The overscanCount prop has been deprecated. ' + 'Please use the overscanColumnCount and overscanRowCount props instead.');
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof overscanColumnsCount === 'number' || typeof overscanRowsCount === 'number') {
|
||||
if (devWarningsOverscanRowsColumnsCount && !devWarningsOverscanRowsColumnsCount.has(instance)) {
|
||||
devWarningsOverscanRowsColumnsCount.add(instance);
|
||||
console.warn('The overscanColumnsCount and overscanRowsCount props have been deprecated. ' + 'Please use the overscanColumnCount and overscanRowCount props instead.');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -768,7 +866,11 @@ var getOffsetForIndexAndAlignment = function getOffsetForIndexAndAlignment(itemT
|
|||
default:
|
||||
if (scrollOffset >= minOffset && scrollOffset <= maxOffset) {
|
||||
return scrollOffset;
|
||||
} else if (scrollOffset - minOffset < maxOffset - scrollOffset) {
|
||||
} else if (minOffset > maxOffset) {
|
||||
// Because we only take into account the scrollbar size when calculating minOffset
|
||||
// this value can be larger than maxOffset when at the end of the list
|
||||
return minOffset;
|
||||
} else if (scrollOffset < minOffset) {
|
||||
return minOffset;
|
||||
} else {
|
||||
return maxOffset;
|
||||
|
@ -1037,20 +1139,27 @@ function createListComponent(_ref) {
|
|||
return null;
|
||||
}
|
||||
|
||||
var direction = _this.props.direction; // HACK According to the spec, scrollLeft should be negative for RTL aligned elements.
|
||||
// Chrome does not seem to adhere; its scrolLeft values are positive (measured relative to the left).
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollLeft
|
||||
|
||||
var direction = _this.props.direction;
|
||||
var scrollOffset = scrollLeft;
|
||||
|
||||
if (direction === 'rtl') {
|
||||
if (scrollLeft <= 0) {
|
||||
scrollOffset = -scrollOffset;
|
||||
} else {
|
||||
scrollOffset = scrollWidth - clientWidth - scrollLeft;
|
||||
}
|
||||
}
|
||||
// TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements.
|
||||
// This is not the case for all browsers though (e.g. Chrome reports values as positive, measured relative to the left).
|
||||
// It's also easier for this component if we convert offsets to the same format as they would be in for ltr.
|
||||
// So the simplest solution is to determine which browser behavior we're dealing with, and convert based on it.
|
||||
switch (getRTLOffsetType()) {
|
||||
case 'negative':
|
||||
scrollOffset = -scrollLeft;
|
||||
break;
|
||||
|
||||
case 'positive-descending':
|
||||
scrollOffset = scrollWidth - clientWidth - scrollLeft;
|
||||
break;
|
||||
}
|
||||
} // Prevent Safari's elastic scrolling from causing visual shaking when scrolling past bounds.
|
||||
|
||||
|
||||
scrollOffset = Math.max(0, Math.min(scrollOffset, scrollWidth - clientWidth));
|
||||
return {
|
||||
isScrolling: true,
|
||||
scrollDirection: prevState.scrollOffset < scrollLeft ? 'forward' : 'backward',
|
||||
|
@ -1058,11 +1167,14 @@ function createListComponent(_ref) {
|
|||
scrollUpdateWasRequested: false
|
||||
};
|
||||
}, _this._resetIsScrollingDebounced);
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
_this._onScrollVertical = function (event) {
|
||||
var scrollTop = event.currentTarget.scrollTop;
|
||||
var _event$currentTarget2 = event.currentTarget,
|
||||
clientHeight = _event$currentTarget2.clientHeight,
|
||||
scrollHeight = _event$currentTarget2.scrollHeight,
|
||||
scrollTop = _event$currentTarget2.scrollTop;
|
||||
|
||||
// Force flush sync for scroll updates to reduce visual checkerboarding.
|
||||
flushSync(() => {
|
||||
|
@ -1072,17 +1184,19 @@ function createListComponent(_ref) {
|
|||
// In which case we don't need to trigger another render,
|
||||
// And we don't want to update state.isScrolling.
|
||||
return null;
|
||||
}
|
||||
} // Prevent Safari's elastic scrolling from causing visual shaking when scrolling past bounds.
|
||||
|
||||
|
||||
var scrollOffset = Math.max(0, Math.min(scrollTop, scrollHeight - clientHeight));
|
||||
return {
|
||||
isScrolling: true,
|
||||
scrollDirection: prevState.scrollOffset < scrollTop ? 'forward' : 'backward',
|
||||
scrollOffset: scrollTop,
|
||||
scrollDirection: prevState.scrollOffset < scrollOffset ? 'forward' : 'backward',
|
||||
scrollOffset: scrollOffset,
|
||||
scrollUpdateWasRequested: false
|
||||
};
|
||||
}, _this._resetIsScrollingDebounced);
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
_this._outerRefSetter = function (ref) {
|
||||
var outerRef = _this.props.outerRef;
|
||||
|
@ -1158,12 +1272,13 @@ function createListComponent(_ref) {
|
|||
initialScrollOffset = _this$props2.initialScrollOffset,
|
||||
layout = _this$props2.layout;
|
||||
|
||||
if (typeof initialScrollOffset === 'number' && this._outerRef !== null) {
|
||||
// TODO Deprecate direction "horizontal"
|
||||
if (typeof initialScrollOffset === 'number' && this._outerRef != null) {
|
||||
var outerRef = this._outerRef; // TODO Deprecate direction "horizontal"
|
||||
|
||||
if (direction === 'horizontal' || layout === 'horizontal') {
|
||||
this._outerRef.scrollLeft = initialScrollOffset;
|
||||
outerRef.scrollLeft = initialScrollOffset;
|
||||
} else {
|
||||
this._outerRef.scrollTop = initialScrollOffset;
|
||||
outerRef.scrollTop = initialScrollOffset;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1178,12 +1293,34 @@ function createListComponent(_ref) {
|
|||
scrollOffset = _this$state.scrollOffset,
|
||||
scrollUpdateWasRequested = _this$state.scrollUpdateWasRequested;
|
||||
|
||||
if (scrollUpdateWasRequested && this._outerRef !== null) {
|
||||
// TODO Deprecate direction "horizontal"
|
||||
if (scrollUpdateWasRequested && this._outerRef != null) {
|
||||
var outerRef = this._outerRef; // TODO Deprecate direction "horizontal"
|
||||
|
||||
if (direction === 'horizontal' || layout === 'horizontal') {
|
||||
this._outerRef.scrollLeft = scrollOffset;
|
||||
if (direction === 'rtl') {
|
||||
// TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements.
|
||||
// This is not the case for all browsers though (e.g. Chrome reports values as positive, measured relative to the left).
|
||||
// So we need to determine which browser behavior we're dealing with, and mimic it.
|
||||
switch (getRTLOffsetType()) {
|
||||
case 'negative':
|
||||
outerRef.scrollLeft = -scrollOffset;
|
||||
break;
|
||||
|
||||
case 'positive-ascending':
|
||||
outerRef.scrollLeft = scrollOffset;
|
||||
break;
|
||||
|
||||
default:
|
||||
var clientWidth = outerRef.clientWidth,
|
||||
scrollWidth = outerRef.scrollWidth;
|
||||
outerRef.scrollLeft = scrollWidth - clientWidth - scrollOffset;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
this._outerRef.scrollTop = scrollOffset;
|
||||
outerRef.scrollLeft = scrollOffset;
|
||||
}
|
||||
} else {
|
||||
outerRef.scrollTop = scrollOffset;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1259,7 +1396,7 @@ function createListComponent(_ref) {
|
|||
ref: innerRef,
|
||||
style: {
|
||||
height: isHorizontal ? '100%' : estimatedTotalSize,
|
||||
pointerEvents: isScrolling ? 'none' : '',
|
||||
pointerEvents: isScrolling ? 'none' : undefined,
|
||||
width: isHorizontal ? estimatedTotalSize : '100%'
|
||||
}
|
||||
}));
|
||||
|
@ -1541,7 +1678,7 @@ createListComponent({
|
|||
default:
|
||||
if (scrollOffset >= minOffset && scrollOffset <= maxOffset) {
|
||||
return scrollOffset;
|
||||
} else if (scrollOffset - minOffset < maxOffset - scrollOffset) {
|
||||
} else if (scrollOffset < minOffset) {
|
||||
return minOffset;
|
||||
} else {
|
||||
return maxOffset;
|
||||
|
@ -1646,7 +1783,8 @@ createGridComponent({
|
|||
var columnCount = _ref7.columnCount,
|
||||
columnWidth = _ref7.columnWidth,
|
||||
width = _ref7.width;
|
||||
var maxOffset = Math.max(0, Math.min(columnCount * columnWidth - width, columnIndex * columnWidth));
|
||||
var lastColumnOffset = Math.max(0, columnCount * columnWidth - width);
|
||||
var maxOffset = Math.min(lastColumnOffset, columnIndex * columnWidth);
|
||||
var minOffset = Math.max(0, columnIndex * columnWidth - width + scrollbarSize + columnWidth);
|
||||
|
||||
if (align === 'smart') {
|
||||
|
@ -1665,13 +1803,27 @@ createGridComponent({
|
|||
return minOffset;
|
||||
|
||||
case 'center':
|
||||
return Math.round(minOffset + (maxOffset - minOffset) / 2);
|
||||
// "Centered" offset is usually the average of the min and max.
|
||||
// But near the edges of the list, this doesn't hold true.
|
||||
var middleOffset = Math.round(minOffset + (maxOffset - minOffset) / 2);
|
||||
|
||||
if (middleOffset < Math.ceil(width / 2)) {
|
||||
return 0; // near the beginning
|
||||
} else if (middleOffset > lastColumnOffset + Math.floor(width / 2)) {
|
||||
return lastColumnOffset; // near the end
|
||||
} else {
|
||||
return middleOffset;
|
||||
}
|
||||
|
||||
case 'auto':
|
||||
default:
|
||||
if (scrollLeft >= minOffset && scrollLeft <= maxOffset) {
|
||||
return scrollLeft;
|
||||
} else if (scrollLeft - minOffset < maxOffset - scrollLeft) {
|
||||
} else if (minOffset > maxOffset) {
|
||||
// Because we only take into account the scrollbar size when calculating minOffset
|
||||
// this value can be larger than maxOffset when at the end of the list
|
||||
return minOffset;
|
||||
} else if (scrollLeft < minOffset) {
|
||||
return minOffset;
|
||||
} else {
|
||||
return maxOffset;
|
||||
|
@ -1683,7 +1835,8 @@ createGridComponent({
|
|||
var rowHeight = _ref8.rowHeight,
|
||||
height = _ref8.height,
|
||||
rowCount = _ref8.rowCount;
|
||||
var maxOffset = Math.max(0, Math.min(rowCount * rowHeight - height, rowIndex * rowHeight));
|
||||
var lastRowOffset = Math.max(0, rowCount * rowHeight - height);
|
||||
var maxOffset = Math.min(lastRowOffset, rowIndex * rowHeight);
|
||||
var minOffset = Math.max(0, rowIndex * rowHeight - height + scrollbarSize + rowHeight);
|
||||
|
||||
if (align === 'smart') {
|
||||
|
@ -1702,13 +1855,27 @@ createGridComponent({
|
|||
return minOffset;
|
||||
|
||||
case 'center':
|
||||
return Math.round(minOffset + (maxOffset - minOffset) / 2);
|
||||
// "Centered" offset is usually the average of the min and max.
|
||||
// But near the edges of the list, this doesn't hold true.
|
||||
var middleOffset = Math.round(minOffset + (maxOffset - minOffset) / 2);
|
||||
|
||||
if (middleOffset < Math.ceil(height / 2)) {
|
||||
return 0; // near the beginning
|
||||
} else if (middleOffset > lastRowOffset + Math.floor(height / 2)) {
|
||||
return lastRowOffset; // near the end
|
||||
} else {
|
||||
return middleOffset;
|
||||
}
|
||||
|
||||
case 'auto':
|
||||
default:
|
||||
if (scrollTop >= minOffset && scrollTop <= maxOffset) {
|
||||
return scrollTop;
|
||||
} else if (scrollTop - minOffset < maxOffset - scrollTop) {
|
||||
} else if (minOffset > maxOffset) {
|
||||
// Because we only take into account the scrollbar size when calculating minOffset
|
||||
// this value can be larger than maxOffset when at the end of the list
|
||||
return minOffset;
|
||||
} else if (scrollTop < minOffset) {
|
||||
return minOffset;
|
||||
} else {
|
||||
return maxOffset;
|
||||
|
@ -1726,7 +1893,9 @@ createGridComponent({
|
|||
columnCount = _ref10.columnCount,
|
||||
width = _ref10.width;
|
||||
var left = startIndex * columnWidth;
|
||||
return Math.max(0, Math.min(columnCount - 1, startIndex + Math.floor((width + (scrollLeft - left)) / columnWidth)));
|
||||
var numVisibleColumns = Math.ceil((width + scrollLeft - left) / columnWidth);
|
||||
return Math.max(0, Math.min(columnCount - 1, startIndex + numVisibleColumns - 1 // -1 is because stop index is inclusive
|
||||
));
|
||||
},
|
||||
getRowStartIndexForOffset: function getRowStartIndexForOffset(_ref11, scrollTop) {
|
||||
var rowHeight = _ref11.rowHeight,
|
||||
|
@ -1737,8 +1906,10 @@ createGridComponent({
|
|||
var rowHeight = _ref12.rowHeight,
|
||||
rowCount = _ref12.rowCount,
|
||||
height = _ref12.height;
|
||||
var left = startIndex * rowHeight;
|
||||
return Math.max(0, Math.min(rowCount - 1, startIndex + Math.floor((height + (scrollTop - left)) / rowHeight)));
|
||||
var top = startIndex * rowHeight;
|
||||
var numVisibleRows = Math.ceil((height + scrollTop - top) / rowHeight);
|
||||
return Math.max(0, Math.min(rowCount - 1, startIndex + numVisibleRows - 1 // -1 is because stop index is inclusive
|
||||
));
|
||||
},
|
||||
initInstanceProps: function initInstanceProps(props) {// Noop
|
||||
},
|
||||
|
@ -1763,13 +1934,11 @@ var FixedSizeList =
|
|||
/*#__PURE__*/
|
||||
createListComponent({
|
||||
getItemOffset: function getItemOffset(_ref, index) {
|
||||
var itemSize = _ref.itemSize,
|
||||
size = _ref.size;
|
||||
var itemSize = _ref.itemSize;
|
||||
return index * itemSize;
|
||||
},
|
||||
getItemSize: function getItemSize(_ref2, index) {
|
||||
var itemSize = _ref2.itemSize,
|
||||
size = _ref2.size;
|
||||
var itemSize = _ref2.itemSize;
|
||||
return itemSize;
|
||||
},
|
||||
getEstimatedTotalSize: function getEstimatedTotalSize(_ref3) {
|
||||
|
@ -1787,7 +1956,8 @@ createListComponent({
|
|||
// TODO Deprecate direction "horizontal"
|
||||
var isHorizontal = direction === 'horizontal' || layout === 'horizontal';
|
||||
var size = isHorizontal ? width : height;
|
||||
var maxOffset = Math.max(0, Math.min(itemCount * itemSize - size, index * itemSize));
|
||||
var lastItemOffset = Math.max(0, itemCount * itemSize - size);
|
||||
var maxOffset = Math.min(lastItemOffset, index * itemSize);
|
||||
var minOffset = Math.max(0, index * itemSize - size + itemSize);
|
||||
|
||||
if (align === 'smart') {
|
||||
|
@ -1806,13 +1976,25 @@ createListComponent({
|
|||
return minOffset;
|
||||
|
||||
case 'center':
|
||||
return Math.round(minOffset + (maxOffset - minOffset) / 2);
|
||||
{
|
||||
// "Centered" offset is usually the average of the min and max.
|
||||
// But near the edges of the list, this doesn't hold true.
|
||||
var middleOffset = Math.round(minOffset + (maxOffset - minOffset) / 2);
|
||||
|
||||
if (middleOffset < Math.ceil(size / 2)) {
|
||||
return 0; // near the beginning
|
||||
} else if (middleOffset > lastItemOffset + Math.floor(size / 2)) {
|
||||
return lastItemOffset; // near the end
|
||||
} else {
|
||||
return middleOffset;
|
||||
}
|
||||
}
|
||||
|
||||
case 'auto':
|
||||
default:
|
||||
if (scrollOffset >= minOffset && scrollOffset <= maxOffset) {
|
||||
return scrollOffset;
|
||||
} else if (scrollOffset - minOffset < maxOffset - scrollOffset) {
|
||||
} else if (scrollOffset < minOffset) {
|
||||
return minOffset;
|
||||
} else {
|
||||
return maxOffset;
|
||||
|
@ -1836,7 +2018,9 @@ createListComponent({
|
|||
var isHorizontal = direction === 'horizontal' || layout === 'horizontal';
|
||||
var offset = startIndex * itemSize;
|
||||
var size = isHorizontal ? width : height;
|
||||
return Math.max(0, Math.min(itemCount - 1, startIndex + Math.floor((size + (scrollOffset - offset)) / itemSize)));
|
||||
var numVisibleItems = Math.ceil((size + scrollOffset - offset) / itemSize);
|
||||
return Math.max(0, Math.min(itemCount - 1, startIndex + numVisibleItems - 1 // -1 is because stop index is inclusive
|
||||
));
|
||||
},
|
||||
initInstanceProps: function initInstanceProps(props) {// Noop
|
||||
},
|
||||
|
@ -1891,3 +2075,4 @@ function shouldComponentUpdate(nextProps, nextState) {
|
|||
}
|
||||
|
||||
export { VariableSizeGrid, VariableSizeList, FixedSizeGrid, FixedSizeList, areEqual, shouldComponentUpdate };
|
||||
//# sourceMappingURL=index.esm.js.map
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "react-window",
|
||||
"version": "1.8.0",
|
||||
"version": "1.8.5",
|
||||
"description":
|
||||
"React components for efficiently rendering large, scrollable lists and tabular data",
|
||||
"author":
|
||||
|
@ -91,7 +91,7 @@
|
|||
"eslint-plugin-promise": "^3.7.0",
|
||||
"eslint-plugin-react": "^7.7.0",
|
||||
"eslint-plugin-standard": "^3.0.1",
|
||||
"flow-bin": "^0.80.0",
|
||||
"flow-bin": "^0.103.0",
|
||||
"gh-pages": "^1.1.0",
|
||||
"lint-staged": "^7.0.5",
|
||||
"prettier": "^1.12.1",
|
||||
|
@ -103,6 +103,8 @@
|
|||
"rollup": "^1.4.1",
|
||||
"rollup-plugin-babel": "^4.3.2",
|
||||
"rollup-plugin-commonjs": "^9.2.1",
|
||||
"rollup-plugin-node-resolve": "^4.0.1"
|
||||
"rollup-plugin-node-resolve": "^4.0.1",
|
||||
"rollup-plugin-replace": "^2.2.0",
|
||||
"rollup-plugin-terser": "^5.1.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,12 +31,13 @@ const FixedSizeGrid = createGridComponent({
|
|||
instanceProps: typeof undefined,
|
||||
scrollbarSize: number
|
||||
): number => {
|
||||
const maxOffset = Math.max(
|
||||
const lastColumnOffset = Math.max(
|
||||
0,
|
||||
Math.min(
|
||||
columnCount * ((columnWidth: any): number) - width,
|
||||
columnCount * ((columnWidth: any): number) - width
|
||||
);
|
||||
const maxOffset = Math.min(
|
||||
lastColumnOffset,
|
||||
columnIndex * ((columnWidth: any): number)
|
||||
)
|
||||
);
|
||||
const minOffset = Math.max(
|
||||
0,
|
||||
|
@ -60,12 +61,27 @@ const FixedSizeGrid = createGridComponent({
|
|||
case 'end':
|
||||
return minOffset;
|
||||
case 'center':
|
||||
return Math.round(minOffset + (maxOffset - minOffset) / 2);
|
||||
// "Centered" offset is usually the average of the min and max.
|
||||
// But near the edges of the list, this doesn't hold true.
|
||||
const middleOffset = Math.round(
|
||||
minOffset + (maxOffset - minOffset) / 2
|
||||
);
|
||||
if (middleOffset < Math.ceil(width / 2)) {
|
||||
return 0; // near the beginning
|
||||
} else if (middleOffset > lastColumnOffset + Math.floor(width / 2)) {
|
||||
return lastColumnOffset; // near the end
|
||||
} else {
|
||||
return middleOffset;
|
||||
}
|
||||
case 'auto':
|
||||
default:
|
||||
if (scrollLeft >= minOffset && scrollLeft <= maxOffset) {
|
||||
return scrollLeft;
|
||||
} else if (scrollLeft - minOffset < maxOffset - scrollLeft) {
|
||||
} else if (minOffset > maxOffset) {
|
||||
// Because we only take into account the scrollbar size when calculating minOffset
|
||||
// this value can be larger than maxOffset when at the end of the list
|
||||
return minOffset;
|
||||
} else if (scrollLeft < minOffset) {
|
||||
return minOffset;
|
||||
} else {
|
||||
return maxOffset;
|
||||
|
@ -81,12 +97,13 @@ const FixedSizeGrid = createGridComponent({
|
|||
instanceProps: typeof undefined,
|
||||
scrollbarSize: number
|
||||
): number => {
|
||||
const maxOffset = Math.max(
|
||||
const lastRowOffset = Math.max(
|
||||
0,
|
||||
Math.min(
|
||||
rowCount * ((rowHeight: any): number) - height,
|
||||
rowCount * ((rowHeight: any): number) - height
|
||||
);
|
||||
const maxOffset = Math.min(
|
||||
lastRowOffset,
|
||||
rowIndex * ((rowHeight: any): number)
|
||||
)
|
||||
);
|
||||
const minOffset = Math.max(
|
||||
0,
|
||||
|
@ -110,12 +127,27 @@ const FixedSizeGrid = createGridComponent({
|
|||
case 'end':
|
||||
return minOffset;
|
||||
case 'center':
|
||||
return Math.round(minOffset + (maxOffset - minOffset) / 2);
|
||||
// "Centered" offset is usually the average of the min and max.
|
||||
// But near the edges of the list, this doesn't hold true.
|
||||
const middleOffset = Math.round(
|
||||
minOffset + (maxOffset - minOffset) / 2
|
||||
);
|
||||
if (middleOffset < Math.ceil(height / 2)) {
|
||||
return 0; // near the beginning
|
||||
} else if (middleOffset > lastRowOffset + Math.floor(height / 2)) {
|
||||
return lastRowOffset; // near the end
|
||||
} else {
|
||||
return middleOffset;
|
||||
}
|
||||
case 'auto':
|
||||
default:
|
||||
if (scrollTop >= minOffset && scrollTop <= maxOffset) {
|
||||
return scrollTop;
|
||||
} else if (scrollTop - minOffset < maxOffset - scrollTop) {
|
||||
} else if (minOffset > maxOffset) {
|
||||
// Because we only take into account the scrollbar size when calculating minOffset
|
||||
// this value can be larger than maxOffset when at the end of the list
|
||||
return minOffset;
|
||||
} else if (scrollTop < minOffset) {
|
||||
return minOffset;
|
||||
} else {
|
||||
return maxOffset;
|
||||
|
@ -141,14 +173,14 @@ const FixedSizeGrid = createGridComponent({
|
|||
scrollLeft: number
|
||||
): number => {
|
||||
const left = startIndex * ((columnWidth: any): number);
|
||||
const numVisibleColumns = Math.ceil(
|
||||
(width + scrollLeft - left) / ((columnWidth: any): number)
|
||||
);
|
||||
return Math.max(
|
||||
0,
|
||||
Math.min(
|
||||
columnCount - 1,
|
||||
startIndex +
|
||||
Math.floor(
|
||||
(width + (scrollLeft - left)) / ((columnWidth: any): number)
|
||||
)
|
||||
startIndex + numVisibleColumns - 1 // -1 is because stop index is inclusive
|
||||
)
|
||||
);
|
||||
},
|
||||
|
@ -167,13 +199,15 @@ const FixedSizeGrid = createGridComponent({
|
|||
startIndex: number,
|
||||
scrollTop: number
|
||||
): number => {
|
||||
const left = startIndex * ((rowHeight: any): number);
|
||||
const top = startIndex * ((rowHeight: any): number);
|
||||
const numVisibleRows = Math.ceil(
|
||||
(height + scrollTop - top) / ((rowHeight: any): number)
|
||||
);
|
||||
return Math.max(
|
||||
0,
|
||||
Math.min(
|
||||
rowCount - 1,
|
||||
startIndex +
|
||||
Math.floor((height + (scrollTop - left)) / ((rowHeight: any): number))
|
||||
startIndex + numVisibleRows - 1 // -1 is because stop index is inclusive
|
||||
)
|
||||
);
|
||||
},
|
||||
|
|
|
@ -5,10 +5,10 @@ import createListComponent from './createListComponent';
|
|||
import type { Props, ScrollToAlign } from './createListComponent';
|
||||
|
||||
const FixedSizeList = createListComponent({
|
||||
getItemOffset: ({ itemSize, size }: Props<any>, index: number): number =>
|
||||
getItemOffset: ({ itemSize }: Props<any>, index: number): number =>
|
||||
index * ((itemSize: any): number),
|
||||
|
||||
getItemSize: ({ itemSize, size }: Props<any>, index: number): number =>
|
||||
getItemSize: ({ itemSize }: Props<any>, index: number): number =>
|
||||
((itemSize: any): number),
|
||||
|
||||
getEstimatedTotalSize: ({ itemCount, itemSize }: Props<any>) =>
|
||||
|
@ -23,12 +23,13 @@ const FixedSizeList = createListComponent({
|
|||
// TODO Deprecate direction "horizontal"
|
||||
const isHorizontal = direction === 'horizontal' || layout === 'horizontal';
|
||||
const size = (((isHorizontal ? width : height): any): number);
|
||||
const maxOffset = Math.max(
|
||||
const lastItemOffset = Math.max(
|
||||
0,
|
||||
Math.min(
|
||||
itemCount * ((itemSize: any): number) - size,
|
||||
itemCount * ((itemSize: any): number) - size
|
||||
);
|
||||
const maxOffset = Math.min(
|
||||
lastItemOffset,
|
||||
index * ((itemSize: any): number)
|
||||
)
|
||||
);
|
||||
const minOffset = Math.max(
|
||||
0,
|
||||
|
@ -51,13 +52,25 @@ const FixedSizeList = createListComponent({
|
|||
return maxOffset;
|
||||
case 'end':
|
||||
return minOffset;
|
||||
case 'center':
|
||||
return Math.round(minOffset + (maxOffset - minOffset) / 2);
|
||||
case 'center': {
|
||||
// "Centered" offset is usually the average of the min and max.
|
||||
// But near the edges of the list, this doesn't hold true.
|
||||
const middleOffset = Math.round(
|
||||
minOffset + (maxOffset - minOffset) / 2
|
||||
);
|
||||
if (middleOffset < Math.ceil(size / 2)) {
|
||||
return 0; // near the beginning
|
||||
} else if (middleOffset > lastItemOffset + Math.floor(size / 2)) {
|
||||
return lastItemOffset; // near the end
|
||||
} else {
|
||||
return middleOffset;
|
||||
}
|
||||
}
|
||||
case 'auto':
|
||||
default:
|
||||
if (scrollOffset >= minOffset && scrollOffset <= maxOffset) {
|
||||
return scrollOffset;
|
||||
} else if (scrollOffset - minOffset < maxOffset - scrollOffset) {
|
||||
} else if (scrollOffset < minOffset) {
|
||||
return minOffset;
|
||||
} else {
|
||||
return maxOffset;
|
||||
|
@ -83,14 +96,14 @@ const FixedSizeList = createListComponent({
|
|||
const isHorizontal = direction === 'horizontal' || layout === 'horizontal';
|
||||
const offset = startIndex * ((itemSize: any): number);
|
||||
const size = (((isHorizontal ? width : height): any): number);
|
||||
const numVisibleItems = Math.ceil(
|
||||
(size + scrollOffset - offset) / ((itemSize: any): number)
|
||||
);
|
||||
return Math.max(
|
||||
0,
|
||||
Math.min(
|
||||
itemCount - 1,
|
||||
startIndex +
|
||||
Math.floor(
|
||||
(size + (scrollOffset - offset)) / ((itemSize: any): number)
|
||||
)
|
||||
startIndex + numVisibleItems - 1 // -1 is because stop index is inclusive
|
||||
)
|
||||
);
|
||||
},
|
||||
|
|
|
@ -274,7 +274,11 @@ const getOffsetForIndexAndAlignment = (
|
|||
default:
|
||||
if (scrollOffset >= minOffset && scrollOffset <= maxOffset) {
|
||||
return scrollOffset;
|
||||
} else if (scrollOffset - minOffset < maxOffset - scrollOffset) {
|
||||
} else if (minOffset > maxOffset) {
|
||||
// Because we only take into account the scrollbar size when calculating minOffset
|
||||
// this value can be larger than maxOffset when at the end of the list
|
||||
return minOffset;
|
||||
} else if (scrollOffset < minOffset) {
|
||||
return minOffset;
|
||||
} else {
|
||||
return maxOffset;
|
||||
|
|
|
@ -227,7 +227,7 @@ const VariableSizeList = createListComponent({
|
|||
default:
|
||||
if (scrollOffset >= minOffset && scrollOffset <= maxOffset) {
|
||||
return scrollOffset;
|
||||
} else if (scrollOffset - minOffset < maxOffset - scrollOffset) {
|
||||
} else if (scrollOffset < minOffset) {
|
||||
return minOffset;
|
||||
} else {
|
||||
return maxOffset;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
import memoizeOne from 'memoize-one';
|
||||
import { createElement, PureComponent } from 'react';
|
||||
import { cancelTimeout, requestTimeout } from './timer';
|
||||
import { getScrollbarSize } from './domHelpers';
|
||||
import { getScrollbarSize, getRTLOffsetType } from './domHelpers';
|
||||
|
||||
import type { TimeoutID } from './timer';
|
||||
|
||||
|
@ -46,6 +46,22 @@ type OnScrollCallback = ({
|
|||
type ScrollEvent = SyntheticEvent<HTMLDivElement>;
|
||||
type ItemStyleCache = { [key: string]: Object };
|
||||
|
||||
type OuterProps = {|
|
||||
children: React$Node,
|
||||
className: string | void,
|
||||
onScroll: ScrollEvent => void,
|
||||
style: {
|
||||
[string]: mixed,
|
||||
},
|
||||
|};
|
||||
|
||||
type InnerProps = {|
|
||||
children: React$Node,
|
||||
style: {
|
||||
[string]: mixed,
|
||||
},
|
||||
|};
|
||||
|
||||
export type Props<T> = {|
|
||||
children: RenderComponent<T>,
|
||||
className?: string,
|
||||
|
@ -56,7 +72,7 @@ export type Props<T> = {|
|
|||
initialScrollLeft?: number,
|
||||
initialScrollTop?: number,
|
||||
innerRef?: any,
|
||||
innerElementType?: React$ElementType,
|
||||
innerElementType?: string | React$AbstractComponent<InnerProps, any>,
|
||||
innerTagName?: string, // deprecated
|
||||
itemData: T,
|
||||
itemKey?: (params: {|
|
||||
|
@ -67,11 +83,13 @@ export type Props<T> = {|
|
|||
onItemsRendered?: OnItemsRenderedCallback,
|
||||
onScroll?: OnScrollCallback,
|
||||
outerRef?: any,
|
||||
outerElementType?: React$ElementType,
|
||||
outerElementType?: string | React$AbstractComponent<OuterProps, any>,
|
||||
outerTagName?: string, // deprecated
|
||||
overscanColumnsCount?: number,
|
||||
overscanColumnCount?: number,
|
||||
overscanColumnsCount?: number, // deprecated
|
||||
overscanCount?: number, // deprecated
|
||||
overscanRowsCount?: number,
|
||||
overscanRowCount?: number,
|
||||
overscanRowsCount?: number, // deprecated
|
||||
rowCount: number,
|
||||
rowHeight: itemSize,
|
||||
style?: Object,
|
||||
|
@ -130,10 +148,12 @@ const defaultItemKey = ({ columnIndex, data, rowIndex }) =>
|
|||
// In DEV mode, this Set helps us only log a warning once per component instance.
|
||||
// This avoids spamming the console every time a render happens.
|
||||
let devWarningsOverscanCount = null;
|
||||
let devWarningsOverscanRowsColumnsCount = null;
|
||||
let devWarningsTagName = null;
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (typeof window !== 'undefined' && typeof window.WeakSet !== 'undefined') {
|
||||
devWarningsOverscanCount = new WeakSet();
|
||||
devWarningsOverscanRowsColumnsCount = new WeakSet();
|
||||
devWarningsTagName = new WeakSet();
|
||||
}
|
||||
}
|
||||
|
@ -320,21 +340,47 @@ export default function createGridComponent({
|
|||
|
||||
componentDidMount() {
|
||||
const { initialScrollLeft, initialScrollTop } = this.props;
|
||||
if (typeof initialScrollLeft === 'number' && this._outerRef != null) {
|
||||
((this._outerRef: any): HTMLDivElement).scrollLeft = initialScrollLeft;
|
||||
|
||||
if (this._outerRef != null) {
|
||||
const outerRef = ((this._outerRef: any): HTMLElement);
|
||||
if (typeof initialScrollLeft === 'number') {
|
||||
outerRef.scrollLeft = initialScrollLeft;
|
||||
}
|
||||
if (typeof initialScrollTop === 'number') {
|
||||
outerRef.scrollTop = initialScrollTop;
|
||||
}
|
||||
if (typeof initialScrollTop === 'number' && this._outerRef != null) {
|
||||
((this._outerRef: any): HTMLDivElement).scrollTop = initialScrollTop;
|
||||
}
|
||||
|
||||
this._callPropsCallbacks();
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
const { direction } = this.props;
|
||||
const { scrollLeft, scrollTop, scrollUpdateWasRequested } = this.state;
|
||||
if (scrollUpdateWasRequested && this._outerRef !== null) {
|
||||
((this._outerRef: any): HTMLDivElement).scrollLeft = scrollLeft;
|
||||
((this._outerRef: any): HTMLDivElement).scrollTop = scrollTop;
|
||||
|
||||
if (scrollUpdateWasRequested && this._outerRef != null) {
|
||||
// TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements.
|
||||
// This is not the case for all browsers though (e.g. Chrome reports values as positive, measured relative to the left).
|
||||
// So we need to determine which browser behavior we're dealing with, and mimic it.
|
||||
const outerRef = ((this._outerRef: any): HTMLElement);
|
||||
if (direction === 'rtl') {
|
||||
switch (getRTLOffsetType()) {
|
||||
case 'negative':
|
||||
outerRef.scrollLeft = -scrollLeft;
|
||||
break;
|
||||
case 'positive-ascending':
|
||||
outerRef.scrollLeft = scrollLeft;
|
||||
break;
|
||||
default:
|
||||
const { clientWidth, scrollWidth } = outerRef;
|
||||
outerRef.scrollLeft = scrollWidth - clientWidth - scrollLeft;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
outerRef.scrollLeft = Math.max(0, scrollLeft);
|
||||
}
|
||||
|
||||
outerRef.scrollTop = Math.max(0, scrollTop);
|
||||
}
|
||||
|
||||
this._callPropsCallbacks();
|
||||
|
@ -432,7 +478,7 @@ export default function createGridComponent({
|
|||
ref: innerRef,
|
||||
style: {
|
||||
height: estimatedTotalHeight,
|
||||
pointerEvents: isScrolling ? 'none' : '',
|
||||
pointerEvents: isScrolling ? 'none' : undefined,
|
||||
width: estimatedTotalWidth,
|
||||
},
|
||||
})
|
||||
|
@ -586,6 +632,7 @@ export default function createGridComponent({
|
|||
_getHorizontalRangeToRender(): [number, number, number, number] {
|
||||
const {
|
||||
columnCount,
|
||||
overscanColumnCount,
|
||||
overscanColumnsCount,
|
||||
overscanCount,
|
||||
rowCount,
|
||||
|
@ -593,7 +640,7 @@ export default function createGridComponent({
|
|||
const { horizontalScrollDirection, isScrolling, scrollLeft } = this.state;
|
||||
|
||||
const overscanCountResolved: number =
|
||||
overscanColumnsCount || overscanCount || 1;
|
||||
overscanColumnCount || overscanColumnsCount || overscanCount || 1;
|
||||
|
||||
if (columnCount === 0 || rowCount === 0) {
|
||||
return [0, 0, 0, 0];
|
||||
|
@ -634,13 +681,14 @@ export default function createGridComponent({
|
|||
const {
|
||||
columnCount,
|
||||
overscanCount,
|
||||
overscanRowCount,
|
||||
overscanRowsCount,
|
||||
rowCount,
|
||||
} = this.props;
|
||||
const { isScrolling, verticalScrollDirection, scrollTop } = this.state;
|
||||
|
||||
const overscanCountResolved: number =
|
||||
overscanRowsCount || overscanCount || 1;
|
||||
overscanRowCount || overscanRowsCount || overscanCount || 1;
|
||||
|
||||
if (columnCount === 0 || rowCount === 0) {
|
||||
return [0, 0, 0, 0];
|
||||
|
@ -679,9 +727,11 @@ export default function createGridComponent({
|
|||
|
||||
_onScroll = (event: ScrollEvent): void => {
|
||||
const {
|
||||
clientHeight,
|
||||
clientWidth,
|
||||
scrollLeft,
|
||||
scrollTop,
|
||||
scrollHeight,
|
||||
scrollWidth,
|
||||
} = event.currentTarget;
|
||||
this.setState(prevState => {
|
||||
|
@ -697,24 +747,38 @@ export default function createGridComponent({
|
|||
|
||||
const { direction } = this.props;
|
||||
|
||||
// HACK According to the spec, scrollLeft should be negative for RTL aligned elements.
|
||||
// Chrome does not seem to adhere; its scrollLeft values are positive (measured relative to the left).
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollLeft
|
||||
// TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements.
|
||||
// This is not the case for all browsers though (e.g. Chrome reports values as positive, measured relative to the left).
|
||||
// It's also easier for this component if we convert offsets to the same format as they would be in for ltr.
|
||||
// So the simplest solution is to determine which browser behavior we're dealing with, and convert based on it.
|
||||
let calculatedScrollLeft = scrollLeft;
|
||||
if (direction === 'rtl') {
|
||||
if (scrollLeft <= 0) {
|
||||
switch (getRTLOffsetType()) {
|
||||
case 'negative':
|
||||
calculatedScrollLeft = -scrollLeft;
|
||||
} else {
|
||||
break;
|
||||
case 'positive-descending':
|
||||
calculatedScrollLeft = scrollWidth - clientWidth - scrollLeft;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent Safari's elastic scrolling from causing visual shaking when scrolling past bounds.
|
||||
calculatedScrollLeft = Math.max(
|
||||
0,
|
||||
Math.min(calculatedScrollLeft, scrollWidth - clientWidth)
|
||||
);
|
||||
const calculatedScrollTop = Math.max(
|
||||
0,
|
||||
Math.min(scrollTop, scrollHeight - clientHeight)
|
||||
);
|
||||
|
||||
return {
|
||||
isScrolling: true,
|
||||
horizontalScrollDirection:
|
||||
prevState.scrollLeft < scrollLeft ? 'forward' : 'backward',
|
||||
scrollLeft: calculatedScrollLeft,
|
||||
scrollTop,
|
||||
scrollTop: calculatedScrollTop,
|
||||
verticalScrollDirection:
|
||||
prevState.scrollTop < scrollTop ? 'forward' : 'backward',
|
||||
scrollUpdateWasRequested: false,
|
||||
|
@ -768,7 +832,9 @@ const validateSharedProps = (
|
|||
height,
|
||||
innerTagName,
|
||||
outerTagName,
|
||||
overscanColumnsCount,
|
||||
overscanCount,
|
||||
overscanRowsCount,
|
||||
width,
|
||||
}: Props<any>,
|
||||
{ instance }: State
|
||||
|
@ -779,7 +845,23 @@ const validateSharedProps = (
|
|||
devWarningsOverscanCount.add(instance);
|
||||
console.warn(
|
||||
'The overscanCount prop has been deprecated. ' +
|
||||
'Please use the overscanColumnsCount and overscanRowsCount props instead.'
|
||||
'Please use the overscanColumnCount and overscanRowCount props instead.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
typeof overscanColumnsCount === 'number' ||
|
||||
typeof overscanRowsCount === 'number'
|
||||
) {
|
||||
if (
|
||||
devWarningsOverscanRowsColumnsCount &&
|
||||
!devWarningsOverscanRowsColumnsCount.has(instance)
|
||||
) {
|
||||
devWarningsOverscanRowsColumnsCount.add(instance);
|
||||
console.warn(
|
||||
'The overscanColumnsCount and overscanRowsCount props have been deprecated. ' +
|
||||
'Please use the overscanColumnCount and overscanRowCount props instead.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
import memoizeOne from 'memoize-one';
|
||||
import { createElement, PureComponent } from 'react';
|
||||
import { cancelTimeout, requestTimeout } from './timer';
|
||||
import { getRTLOffsetType } from './domHelpers';
|
||||
|
||||
import type { TimeoutID } from './timer';
|
||||
|
||||
|
@ -38,6 +39,22 @@ type onScrollCallback = ({
|
|||
type ScrollEvent = SyntheticEvent<HTMLDivElement>;
|
||||
type ItemStyleCache = { [index: number]: Object };
|
||||
|
||||
type OuterProps = {|
|
||||
children: React$Node,
|
||||
className: string | void,
|
||||
onScroll: ScrollEvent => void,
|
||||
style: {
|
||||
[string]: mixed,
|
||||
},
|
||||
|};
|
||||
|
||||
type InnerProps = {|
|
||||
children: React$Node,
|
||||
style: {
|
||||
[string]: mixed,
|
||||
},
|
||||
|};
|
||||
|
||||
export type Props<T> = {|
|
||||
children: RenderComponent<T>,
|
||||
className?: string,
|
||||
|
@ -45,7 +62,7 @@ export type Props<T> = {|
|
|||
height: number | string,
|
||||
initialScrollOffset?: number,
|
||||
innerRef?: any,
|
||||
innerElementType?: React$ElementType,
|
||||
innerElementType?: string | React$AbstractComponent<InnerProps, any>,
|
||||
innerTagName?: string, // deprecated
|
||||
itemCount: number,
|
||||
itemData: T,
|
||||
|
@ -55,7 +72,7 @@ export type Props<T> = {|
|
|||
onItemsRendered?: onItemsRenderedCallback,
|
||||
onScroll?: onScrollCallback,
|
||||
outerRef?: any,
|
||||
outerElementType?: React$ElementType,
|
||||
outerElementType?: string | React$AbstractComponent<OuterProps, any>,
|
||||
outerTagName?: string, // deprecated
|
||||
overscanCount: number,
|
||||
style?: Object,
|
||||
|
@ -215,14 +232,13 @@ export default function createListComponent({
|
|||
componentDidMount() {
|
||||
const { direction, initialScrollOffset, layout } = this.props;
|
||||
|
||||
if (typeof initialScrollOffset === 'number' && this._outerRef !== null) {
|
||||
if (typeof initialScrollOffset === 'number' && this._outerRef != null) {
|
||||
const outerRef = ((this._outerRef: any): HTMLElement);
|
||||
// TODO Deprecate direction "horizontal"
|
||||
if (direction === 'horizontal' || layout === 'horizontal') {
|
||||
((this
|
||||
._outerRef: any): HTMLDivElement).scrollLeft = initialScrollOffset;
|
||||
outerRef.scrollLeft = initialScrollOffset;
|
||||
} else {
|
||||
((this
|
||||
._outerRef: any): HTMLDivElement).scrollTop = initialScrollOffset;
|
||||
outerRef.scrollTop = initialScrollOffset;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -233,12 +249,32 @@ export default function createListComponent({
|
|||
const { direction, layout } = this.props;
|
||||
const { scrollOffset, scrollUpdateWasRequested } = this.state;
|
||||
|
||||
if (scrollUpdateWasRequested && this._outerRef !== null) {
|
||||
if (scrollUpdateWasRequested && this._outerRef != null) {
|
||||
const outerRef = ((this._outerRef: any): HTMLElement);
|
||||
|
||||
// TODO Deprecate direction "horizontal"
|
||||
if (direction === 'horizontal' || layout === 'horizontal') {
|
||||
((this._outerRef: any): HTMLDivElement).scrollLeft = scrollOffset;
|
||||
if (direction === 'rtl') {
|
||||
// TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements.
|
||||
// This is not the case for all browsers though (e.g. Chrome reports values as positive, measured relative to the left).
|
||||
// So we need to determine which browser behavior we're dealing with, and mimic it.
|
||||
switch (getRTLOffsetType()) {
|
||||
case 'negative':
|
||||
outerRef.scrollLeft = -scrollOffset;
|
||||
break;
|
||||
case 'positive-ascending':
|
||||
outerRef.scrollLeft = scrollOffset;
|
||||
break;
|
||||
default:
|
||||
const { clientWidth, scrollWidth } = outerRef;
|
||||
outerRef.scrollLeft = scrollWidth - clientWidth - scrollOffset;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
((this._outerRef: any): HTMLDivElement).scrollTop = scrollOffset;
|
||||
outerRef.scrollLeft = scrollOffset;
|
||||
}
|
||||
} else {
|
||||
outerRef.scrollTop = scrollOffset;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -326,7 +362,7 @@ export default function createListComponent({
|
|||
ref: innerRef,
|
||||
style: {
|
||||
height: isHorizontal ? '100%' : estimatedTotalSize,
|
||||
pointerEvents: isScrolling ? 'none' : '',
|
||||
pointerEvents: isScrolling ? 'none' : undefined,
|
||||
width: isHorizontal ? estimatedTotalSize : '100%',
|
||||
},
|
||||
})
|
||||
|
@ -496,18 +532,28 @@ export default function createListComponent({
|
|||
|
||||
const { direction } = this.props;
|
||||
|
||||
// HACK According to the spec, scrollLeft should be negative for RTL aligned elements.
|
||||
// Chrome does not seem to adhere; its scrolLeft values are positive (measured relative to the left).
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollLeft
|
||||
let scrollOffset = scrollLeft;
|
||||
if (direction === 'rtl') {
|
||||
if (scrollLeft <= 0) {
|
||||
scrollOffset = -scrollOffset;
|
||||
} else {
|
||||
// TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements.
|
||||
// This is not the case for all browsers though (e.g. Chrome reports values as positive, measured relative to the left).
|
||||
// It's also easier for this component if we convert offsets to the same format as they would be in for ltr.
|
||||
// So the simplest solution is to determine which browser behavior we're dealing with, and convert based on it.
|
||||
switch (getRTLOffsetType()) {
|
||||
case 'negative':
|
||||
scrollOffset = -scrollLeft;
|
||||
break;
|
||||
case 'positive-descending':
|
||||
scrollOffset = scrollWidth - clientWidth - scrollLeft;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent Safari's elastic scrolling from causing visual shaking when scrolling past bounds.
|
||||
scrollOffset = Math.max(
|
||||
0,
|
||||
Math.min(scrollOffset, scrollWidth - clientWidth)
|
||||
);
|
||||
|
||||
return {
|
||||
isScrolling: true,
|
||||
scrollDirection:
|
||||
|
@ -519,7 +565,7 @@ export default function createListComponent({
|
|||
};
|
||||
|
||||
_onScrollVertical = (event: ScrollEvent): void => {
|
||||
const { scrollTop } = event.currentTarget;
|
||||
const { clientHeight, scrollHeight, scrollTop } = event.currentTarget;
|
||||
this.setState(prevState => {
|
||||
if (prevState.scrollOffset === scrollTop) {
|
||||
// Scroll position may have been updated by cDM/cDU,
|
||||
|
@ -528,11 +574,17 @@ export default function createListComponent({
|
|||
return null;
|
||||
}
|
||||
|
||||
// Prevent Safari's elastic scrolling from causing visual shaking when scrolling past bounds.
|
||||
const scrollOffset = Math.max(
|
||||
0,
|
||||
Math.min(scrollTop, scrollHeight - clientHeight)
|
||||
);
|
||||
|
||||
return {
|
||||
isScrolling: true,
|
||||
scrollDirection:
|
||||
prevState.scrollOffset < scrollTop ? 'forward' : 'backward',
|
||||
scrollOffset: scrollTop,
|
||||
prevState.scrollOffset < scrollOffset ? 'forward' : 'backward',
|
||||
scrollOffset,
|
||||
scrollUpdateWasRequested: false,
|
||||
};
|
||||
}, this._resetIsScrollingDebounced);
|
||||
|
|
|
@ -20,3 +20,53 @@ export function getScrollbarSize(recalculate?: boolean = false): number {
|
|||
|
||||
return size;
|
||||
}
|
||||
|
||||
export type RTLOffsetType =
|
||||
| 'negative'
|
||||
| 'positive-descending'
|
||||
| 'positive-ascending';
|
||||
|
||||
let cachedRTLResult: RTLOffsetType | null = null;
|
||||
|
||||
// TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements.
|
||||
// Chrome does not seem to adhere; its scrollLeft values are positive (measured relative to the left).
|
||||
// Safari's elastic bounce makes detecting this even more complicated wrt potential false positives.
|
||||
// The safest way to check this is to intentionally set a negative offset,
|
||||
// and then verify that the subsequent "scroll" event matches the negative offset.
|
||||
// If it does not match, then we can assume a non-standard RTL scroll implementation.
|
||||
export function getRTLOffsetType(recalculate?: boolean = false): RTLOffsetType {
|
||||
if (cachedRTLResult === null || recalculate) {
|
||||
const outerDiv = document.createElement('div');
|
||||
const outerStyle = outerDiv.style;
|
||||
outerStyle.width = '50px';
|
||||
outerStyle.height = '50px';
|
||||
outerStyle.overflow = 'scroll';
|
||||
outerStyle.direction = 'rtl';
|
||||
|
||||
const innerDiv = document.createElement('div');
|
||||
const innerStyle = innerDiv.style;
|
||||
innerStyle.width = '100px';
|
||||
innerStyle.height = '100px';
|
||||
|
||||
outerDiv.appendChild(innerDiv);
|
||||
|
||||
((document.body: any): HTMLBodyElement).appendChild(outerDiv);
|
||||
|
||||
if (outerDiv.scrollLeft > 0) {
|
||||
cachedRTLResult = 'positive-descending';
|
||||
} else {
|
||||
outerDiv.scrollLeft = 1;
|
||||
if (outerDiv.scrollLeft === 0) {
|
||||
cachedRTLResult = 'negative';
|
||||
} else {
|
||||
cachedRTLResult = 'positive-ascending';
|
||||
}
|
||||
}
|
||||
|
||||
((document.body: any): HTMLBodyElement).removeChild(outerDiv);
|
||||
|
||||
return cachedRTLResult;
|
||||
}
|
||||
|
||||
return cachedRTLResult;
|
||||
}
|
||||
|
|
26
yarn.lock
26
yarn.lock
|
@ -766,9 +766,9 @@
|
|||
regenerator-runtime "^0.12.0"
|
||||
|
||||
"@babel/runtime@^7.0.0":
|
||||
version "7.4.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.5.tgz#582bb531f5f9dc67d2fcb682979894f75e253f12"
|
||||
integrity sha512-TuI4qpWZP6lGOGIuGWtp9sPluqYICmbk8T/1vpSysqJxRPkudh/ofFWyqdcMsDf2s7KvDL4/YHgKyvcS3g9CJQ==
|
||||
version "7.5.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.5.5.tgz#74fba56d35efbeca444091c7850ccd494fd2f132"
|
||||
integrity sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.2"
|
||||
|
||||
|
@ -5522,10 +5522,10 @@ flatstr@^1.0.9:
|
|||
resolved "https://registry.yarnpkg.com/flatstr/-/flatstr-1.0.9.tgz#0950d56fec02de1030c1311847ecd58c25690eb9"
|
||||
integrity sha512-qFlJnOBWDfIaunF54/lBqNKmXOI0HqNhu+mHkLmbaBXlS71PUd9OjFOdyevHt/aHoHB1+eW7eKHgRKOG5aHSpw==
|
||||
|
||||
flow-bin@^0.97.0:
|
||||
version "0.97.0"
|
||||
resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.97.0.tgz#036ffcfc27503367a9d906ec9d843a0aa6f6bb83"
|
||||
integrity sha512-jXjD05gkatLuC4+e28frH1hZoRwr1iASP6oJr61Q64+kR4kmzaS+AdFBhYgoYS5kpoe4UzwDebWK8ETQFNh00w==
|
||||
flow-bin@^0.103.0:
|
||||
version "0.103.0"
|
||||
resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.103.0.tgz#7aec510d85e1c1b0f2b912bb988337d70035cb0f"
|
||||
integrity sha512-Y3yrnE5ICN1Kl/y10BwjA3JSuS+gt4jVPNyUNCZb0RqmkdssMrW8QNNysJYvhgAY/JBJH8Qv7NVUf11MiwfSlA==
|
||||
|
||||
fluent-syntax@0.10.0:
|
||||
version "0.10.0"
|
||||
|
@ -8250,9 +8250,9 @@ mem@^4.0.0:
|
|||
p-is-promise "^2.0.0"
|
||||
|
||||
"memoize-one@>=3.1.1 <6":
|
||||
version "5.0.4"
|
||||
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.0.4.tgz#005928aced5c43d890a4dfab18ca908b0ec92cbc"
|
||||
integrity sha512-P0z5IeAH6qHHGkJIXWw0xC2HNEgkx/9uWWBQw64FJj3/ol14VYdfVGWWr0fXfjhhv3TKVIqUq65os6O4GUNksA==
|
||||
version "5.0.5"
|
||||
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.0.5.tgz#8cd3809555723a07684afafcd6f756072ac75d7e"
|
||||
integrity sha512-ey6EpYv0tEaIbM/nTDOpHciXUvd+ackQrJgEzBwemhZZIWZjcyodqEcrmqDy2BKRTM3a65kKBV4WtLXJDt26SQ==
|
||||
|
||||
memoize-one@^3.1.1:
|
||||
version "3.1.1"
|
||||
|
@ -10183,9 +10183,9 @@ regenerator-runtime@^0.12.0:
|
|||
integrity sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==
|
||||
|
||||
regenerator-runtime@^0.13.2:
|
||||
version "0.13.2"
|
||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz#32e59c9a6fb9b1a4aff09b4930ca2d4477343447"
|
||||
integrity sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA==
|
||||
version "0.13.3"
|
||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz#7cf6a77d8f5c6f60eb73c5fc1955b2ceb01e6bf5"
|
||||
integrity sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==
|
||||
|
||||
regenerator-runtime@^0.9.5:
|
||||
version "0.9.6"
|
||||
|
|
Loading…
Reference in New Issue