Merge branch 'develop' into feature/refactoring-media-viewer
# Conflicts: # packages/shared/package.json
This commit is contained in:
commit
72be9e6da7
@ -4,19 +4,23 @@ import { TableVersions } from "SRC_DIR/helpers/constants";
|
||||
const TABLE_COLUMNS = `filesTableColumns_ver-${TableVersions.Files}`;
|
||||
const TABLE_ROOMS_COLUMNS = `roomsTableColumns_ver-${TableVersions.Rooms}`;
|
||||
const TABLE_TRASH_COLUMNS = `trashTableColumns_ver-${TableVersions.Trash}`;
|
||||
const TABLE_SDK_COLUMNS = `filesSDKTableColumns_ver-${TableVersions.Files}`;
|
||||
|
||||
const COLUMNS_SIZE = `filesColumnsSize_ver-${TableVersions.Files}`;
|
||||
const COLUMNS_ROOMS_SIZE = `roomsColumnsSize_ver-${TableVersions.Rooms}`;
|
||||
const COLUMNS_TRASH_SIZE = `trashColumnsSize_ver-${TableVersions.Trash}`;
|
||||
const COLUMNS_SDK_SIZE = `filesSDKColumnsSize_ver-${TableVersions.Files}`;
|
||||
|
||||
const COLUMNS_SIZE_INFO_PANEL = `filesColumnsSizeInfoPanel_ver-${TableVersions.Files}`;
|
||||
const COLUMNS_ROOMS_SIZE_INFO_PANEL = `roomsColumnsSizeInfoPanel_ver-${TableVersions.Rooms}`;
|
||||
const COLUMNS_TRASH_SIZE_INFO_PANEL = `trashColumnsSizeInfoPanel_ver-${TableVersions.Trash}`;
|
||||
const COLUMNS_SDK_SIZE_INFO_PANEL = `filesSDKColumnsSizeInfoPanel_ver-${TableVersions.Files}`;
|
||||
|
||||
class TableStore {
|
||||
authStore;
|
||||
treeFoldersStore;
|
||||
userStore;
|
||||
settingsStore;
|
||||
|
||||
roomColumnNameIsEnabled = true; // always true
|
||||
roomColumnTypeIsEnabled = false;
|
||||
@ -40,12 +44,13 @@ class TableStore {
|
||||
sizeTrashColumnIsEnabled = false;
|
||||
typeTrashColumnIsEnabled = false;
|
||||
|
||||
constructor(authStore, treeFoldersStore, userStore) {
|
||||
constructor(authStore, treeFoldersStore, userStore, settingsStore) {
|
||||
makeAutoObservable(this);
|
||||
|
||||
this.authStore = authStore;
|
||||
this.treeFoldersStore = treeFoldersStore;
|
||||
this.userStore = userStore;
|
||||
this.settingsStore = settingsStore;
|
||||
}
|
||||
|
||||
setRoomColumnType = (enable) => {
|
||||
@ -234,6 +239,9 @@ class TableStore {
|
||||
this.treeFoldersStore;
|
||||
const isRooms = isRoomsFolder || isArchiveFolder;
|
||||
const userId = this.userStore.user?.id;
|
||||
const isFrame = this.settingsStore.isFrame;
|
||||
|
||||
if (isFrame) return `${TABLE_SDK_COLUMNS}=${userId}`;
|
||||
|
||||
return isRooms
|
||||
? `${TABLE_ROOMS_COLUMNS}=${userId}`
|
||||
@ -247,6 +255,9 @@ class TableStore {
|
||||
this.treeFoldersStore;
|
||||
const isRooms = isRoomsFolder || isArchiveFolder;
|
||||
const userId = this.userStore.user?.id;
|
||||
const isFrame = this.settingsStore.isFrame;
|
||||
|
||||
if (isFrame) return `${COLUMNS_SDK_SIZE}=${userId}`;
|
||||
|
||||
return isRooms
|
||||
? `${COLUMNS_ROOMS_SIZE}=${userId}`
|
||||
@ -260,6 +271,9 @@ class TableStore {
|
||||
this.treeFoldersStore;
|
||||
const isRooms = isRoomsFolder || isArchiveFolder;
|
||||
const userId = this.userStore.user?.id;
|
||||
const isFrame = this.settingsStore.isFrame;
|
||||
|
||||
if (isFrame) return `${COLUMNS_SDK_SIZE_INFO_PANEL}=${userId}`;
|
||||
|
||||
return isRooms
|
||||
? `${COLUMNS_ROOMS_SIZE_INFO_PANEL}=${userId}`
|
||||
|
@ -93,7 +93,7 @@ class UploadDataStore {
|
||||
removeFiles = (fileIds) => {
|
||||
fileIds.forEach((id) => {
|
||||
this.files = this.files?.filter(
|
||||
(file) => !(file.action === "converted" && file.fileInfo.id === id)
|
||||
(file) => !(file.action === "converted" && file.fileInfo?.id === id)
|
||||
);
|
||||
});
|
||||
};
|
||||
@ -464,7 +464,7 @@ class UploadDataStore {
|
||||
while (progress < 100) {
|
||||
const res = await this.getConversationProgress(fileId);
|
||||
progress = res && res[0] && res[0].progress;
|
||||
fileInfo = res && res[0].result;
|
||||
fileInfo = res && res[0] && res[0].result;
|
||||
|
||||
runInAction(() => {
|
||||
const file = this.files.find((file) => file.fileId === fileId);
|
||||
@ -905,19 +905,22 @@ class UploadDataStore {
|
||||
}
|
||||
};
|
||||
|
||||
checkChunkUpload = (
|
||||
t,
|
||||
res,
|
||||
fileSize,
|
||||
index,
|
||||
indexOfFile,
|
||||
path,
|
||||
length,
|
||||
resolve,
|
||||
reject,
|
||||
isAsyncUpload = false,
|
||||
isFinalize = false
|
||||
) => {
|
||||
checkChunkUpload = (chunkUploadObj) => {
|
||||
const {
|
||||
t,
|
||||
res, // file response data
|
||||
fileSize, // file size
|
||||
index, // chunk index
|
||||
indexOfFile, // file index in the list
|
||||
path, // file path
|
||||
chunksLength, // length of file chunks
|
||||
resolve, // resolve cb
|
||||
reject, // reject cb
|
||||
isAsyncUpload = false, // async upload checker
|
||||
isFinalize = false, // is finalize chunk
|
||||
allChunkUploaded, // needed for progress, files is uploaded, awaiting finalized chunk
|
||||
} = chunkUploadObj;
|
||||
|
||||
if (!res.data.data && res.data.message) {
|
||||
return reject(res.data.message);
|
||||
}
|
||||
@ -933,7 +936,7 @@ class UploadDataStore {
|
||||
|
||||
newPercent = this.getNewPercent(uploadedSize, indexOfFile);
|
||||
} else {
|
||||
if (!uploaded) {
|
||||
if (!uploaded && !allChunkUploaded) {
|
||||
uploadedSize =
|
||||
fileSize <= this.filesSettingsStore.chunkUploadSize
|
||||
? fileSize
|
||||
@ -948,11 +951,7 @@ class UploadDataStore {
|
||||
newPercent = this.getFilesPercent(uploadedSize);
|
||||
}
|
||||
|
||||
if (isAsyncUpload && !uploaded && newPercent >= 100) {
|
||||
newPercent = 99;
|
||||
}
|
||||
|
||||
const percentCurrentFile = ((index + 1) / length) * 100;
|
||||
const percentCurrentFile = ((index + 1) / chunksLength) * 100;
|
||||
|
||||
const fileIndex = this.uploadedFilesHistory.findIndex(
|
||||
(f) => f.uniqueId === this.files[indexOfFile].uniqueId
|
||||
@ -1055,8 +1054,11 @@ class UploadDataStore {
|
||||
chunkObjIndex
|
||||
].onUpload();
|
||||
|
||||
this.asyncUploadObj[operationId].chunksArray[chunkObjIndex].isFinished =
|
||||
true;
|
||||
if (this.asyncUploadObj[operationId]) {
|
||||
this.asyncUploadObj[operationId].chunksArray[
|
||||
chunkObjIndex
|
||||
].isFinished = true;
|
||||
}
|
||||
|
||||
if (!res.data.data && res.data.message) {
|
||||
delete this.asyncUploadObj[operationId];
|
||||
@ -1069,22 +1071,39 @@ class UploadDataStore {
|
||||
).length - 1
|
||||
: 0;
|
||||
|
||||
this.checkChunkUpload(
|
||||
let allIsUploaded;
|
||||
if (this.asyncUploadObj[operationId]) {
|
||||
const finished = this.asyncUploadObj[operationId].chunksArray.filter(
|
||||
(x) => x.isFinished
|
||||
);
|
||||
|
||||
allIsUploaded =
|
||||
this.asyncUploadObj[operationId].chunksArray.length -
|
||||
finished.length -
|
||||
1; // 1 last
|
||||
}
|
||||
|
||||
this.checkChunkUpload({
|
||||
t,
|
||||
res,
|
||||
fileSize,
|
||||
activeLength,
|
||||
index: activeLength,
|
||||
indexOfFile,
|
||||
path,
|
||||
length,
|
||||
chunksLength: length,
|
||||
resolve,
|
||||
reject,
|
||||
true
|
||||
);
|
||||
isAsyncUpload: true,
|
||||
isFinalize: false,
|
||||
allChunkUploaded: allIsUploaded === 0,
|
||||
});
|
||||
|
||||
const finalizeChunk = this.asyncUploadObj[
|
||||
operationId
|
||||
].chunksArray.findIndex((x) => !x.isFinished && !x.isFinalize);
|
||||
let finalizeChunk = -1;
|
||||
if (this.asyncUploadObj[operationId]) {
|
||||
finalizeChunk = this.asyncUploadObj[
|
||||
operationId
|
||||
].chunksArray.findIndex((x) => !x.isFinished && !x.isFinalize);
|
||||
}
|
||||
|
||||
if (finalizeChunk === -1) {
|
||||
const finalizeChunkIndex = this.asyncUploadObj[
|
||||
@ -1100,19 +1119,19 @@ class UploadDataStore {
|
||||
finalizeChunkIndex
|
||||
].onUpload();
|
||||
|
||||
this.checkChunkUpload(
|
||||
this.checkChunkUpload({
|
||||
t,
|
||||
finalizeRes,
|
||||
res: finalizeRes,
|
||||
fileSize,
|
||||
finalizeIndex,
|
||||
index: finalizeIndex,
|
||||
indexOfFile,
|
||||
path,
|
||||
length,
|
||||
chunksLength: length,
|
||||
resolve,
|
||||
reject,
|
||||
true, // isAsyncUpload
|
||||
true //isFinalize
|
||||
);
|
||||
isAsyncUpload: true,
|
||||
isFinalize: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
@ -1191,17 +1210,17 @@ class UploadDataStore {
|
||||
const resolve = (res) => Promise.resolve(res);
|
||||
const reject = (err) => Promise.reject(err);
|
||||
|
||||
this.checkChunkUpload(
|
||||
this.checkChunkUpload({
|
||||
t,
|
||||
res,
|
||||
fileSize,
|
||||
index,
|
||||
indexOfFile,
|
||||
path,
|
||||
length,
|
||||
chunksLength: length,
|
||||
resolve,
|
||||
reject
|
||||
);
|
||||
reject,
|
||||
});
|
||||
|
||||
//console.log(`Uploaded chunk ${index}/${length}`, res);
|
||||
}
|
||||
|
@ -220,7 +220,12 @@ const profileActionsStore = new ProfileActionsStore(
|
||||
|
||||
peopleStore.profileActionsStore = profileActionsStore;
|
||||
|
||||
const tableStore = new TableStore(authStore, treeFoldersStore, userStore);
|
||||
const tableStore = new TableStore(
|
||||
authStore,
|
||||
treeFoldersStore,
|
||||
userStore,
|
||||
settingsStore
|
||||
);
|
||||
|
||||
infoPanelStore.filesSettingsStore = filesSettingsStore;
|
||||
infoPanelStore.filesStore = filesStore;
|
||||
|
@ -58,5 +58,10 @@ module.exports = {
|
||||
},
|
||||
],
|
||||
},
|
||||
ignorePatterns: ["./tsconfig.json", "coverage/**", "storybook-static/**"],
|
||||
ignorePatterns: [
|
||||
"./tsconfig.json",
|
||||
"coverage/**",
|
||||
"storybook-static/**",
|
||||
"./utils/custom-scrollbar/**",
|
||||
],
|
||||
};
|
||||
|
@ -1,11 +1,12 @@
|
||||
import Scrollbar from "react-scrollbars-custom";
|
||||
import styled from "styled-components";
|
||||
import { isIOS, isIOS13, isIPad13 } from "react-device-detect";
|
||||
|
||||
import { Scrollbar } from "./custom-scrollbar";
|
||||
|
||||
import { Base } from "../../themes";
|
||||
import { mobile, desktop, tablet } from "../../utils";
|
||||
|
||||
const StyledScrollbar = styled(Scrollbar)`
|
||||
const StyledScrollbar = styled(Scrollbar)<{ $fixedSize?: boolean }>`
|
||||
.scroller::-webkit-scrollbar {
|
||||
${(isIOS || isIOS13 || isIPad13) && `display: none;`}
|
||||
}
|
||||
|
220
packages/shared/components/scrollbar/custom-scrollbar/Emittr.ts
Normal file
220
packages/shared/components/scrollbar/custom-scrollbar/Emittr.ts
Normal file
@ -0,0 +1,220 @@
|
||||
import { isFun, isNum, isUndef } from "./util";
|
||||
|
||||
type EventHandler = (...args: unknown[]) => void;
|
||||
type OnceHandler = OnceHandlerState & { (...args: unknown[]): void };
|
||||
type EventHandlersList = (OnceHandler | EventHandler)[];
|
||||
type EmitterEventHandlers = { [key: string]: EventHandlersList };
|
||||
type OnceHandlerState = {
|
||||
fired: boolean;
|
||||
handler: EventHandler;
|
||||
wrappedHandler?: OnceHandler;
|
||||
emitter: Emittr;
|
||||
event: string;
|
||||
};
|
||||
|
||||
export default class Emittr {
|
||||
private _handlers: EmitterEventHandlers;
|
||||
|
||||
private _maxHandlers: number;
|
||||
|
||||
constructor(maxHandlers = 10) {
|
||||
this.setMaxHandlers(maxHandlers);
|
||||
this._handlers = Object.create(null);
|
||||
}
|
||||
|
||||
private static _callEventHandlers(
|
||||
emitter: Emittr,
|
||||
handlers: EventHandlersList,
|
||||
args: unknown[],
|
||||
) {
|
||||
if (!handlers.length) {
|
||||
return;
|
||||
}
|
||||
if (handlers.length === 1) {
|
||||
Reflect.apply(handlers[0], emitter, args);
|
||||
return;
|
||||
}
|
||||
handlers = [...handlers];
|
||||
let idx;
|
||||
for (idx = 0; idx < handlers.length; idx++) {
|
||||
Reflect.apply(handlers[idx], emitter, args);
|
||||
}
|
||||
}
|
||||
|
||||
private static _addHandler = (
|
||||
emitter: Emittr,
|
||||
name: string,
|
||||
handler: EventHandler,
|
||||
prepend = false,
|
||||
): Emittr => {
|
||||
if (!isFun(handler)) {
|
||||
throw new TypeError(
|
||||
`Expected event handler to be a function, got ${typeof handler}`,
|
||||
);
|
||||
}
|
||||
emitter._handlers[name] = emitter._handlers[name] || [];
|
||||
emitter.emit("addHandler", name, handler);
|
||||
|
||||
if (prepend) {
|
||||
emitter._handlers[name].unshift(handler);
|
||||
} else {
|
||||
emitter._handlers[name].push(handler);
|
||||
}
|
||||
|
||||
return emitter;
|
||||
};
|
||||
|
||||
private static _onceWrapper = function _onceWrapper(...args: unknown[]) {
|
||||
if (!this.fired) {
|
||||
this.fired = true;
|
||||
this.emitter.off(this.event, this.wrappedHandler);
|
||||
Reflect.apply(this.handler, this.emitter, args);
|
||||
}
|
||||
};
|
||||
|
||||
private static _removeHandler = (
|
||||
emitter: Emittr,
|
||||
name: string,
|
||||
handler: EventHandler,
|
||||
): Emittr => {
|
||||
if (!isFun(handler)) {
|
||||
throw new TypeError(
|
||||
`Expected event handler to be a function, got ${typeof handler}`,
|
||||
);
|
||||
}
|
||||
if (isUndef(emitter._handlers[name]) || !emitter._handlers[name].length) {
|
||||
return emitter;
|
||||
}
|
||||
let idx = -1;
|
||||
if (emitter._handlers[name].length === 1) {
|
||||
if (
|
||||
emitter._handlers[name][0] === handler ||
|
||||
(emitter._handlers[name][0] as OnceHandler).handler === handler
|
||||
) {
|
||||
idx = 0;
|
||||
handler =
|
||||
(emitter._handlers[name][0] as OnceHandler).handler ||
|
||||
emitter._handlers[name][0];
|
||||
}
|
||||
} else {
|
||||
for (idx = emitter._handlers[name].length - 1; idx >= 0; idx--) {
|
||||
if (
|
||||
emitter._handlers[name][idx] === handler ||
|
||||
(emitter._handlers[name][idx] as OnceHandler).handler === handler
|
||||
) {
|
||||
handler =
|
||||
(emitter._handlers[name][idx] as OnceHandler).handler ||
|
||||
emitter._handlers[name][idx];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (idx === -1) {
|
||||
return emitter;
|
||||
}
|
||||
|
||||
if (idx === 0) {
|
||||
emitter._handlers[name].shift();
|
||||
} else {
|
||||
emitter._handlers[name].splice(idx, 1);
|
||||
}
|
||||
|
||||
emitter.emit("removeHandler", name, handler);
|
||||
return emitter;
|
||||
};
|
||||
|
||||
setMaxHandlers(count: number): this {
|
||||
if (!isNum(count) || count <= 0) {
|
||||
throw new TypeError(
|
||||
`Expected maxHandlers to be a positive number, got '${count}' of type ${typeof count}`,
|
||||
);
|
||||
}
|
||||
this._maxHandlers = count;
|
||||
return this;
|
||||
}
|
||||
|
||||
getMaxHandlers(): number {
|
||||
return this._maxHandlers;
|
||||
}
|
||||
|
||||
public emit(name: string, ...args: unknown[]): boolean {
|
||||
if (
|
||||
typeof this._handlers[name] !== "object" ||
|
||||
!Array.isArray(this._handlers[name])
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
Emittr._callEventHandlers(this, this._handlers[name], args);
|
||||
return true;
|
||||
}
|
||||
|
||||
public on(name: string, handler: EventHandler): this {
|
||||
Emittr._addHandler(this, name, handler);
|
||||
return this;
|
||||
}
|
||||
|
||||
public prependOn(name: string, handler: EventHandler): this {
|
||||
Emittr._addHandler(this, name, handler, true);
|
||||
return this;
|
||||
}
|
||||
|
||||
public once(name: string, handler: EventHandler): this {
|
||||
if (!isFun(handler)) {
|
||||
throw new TypeError(
|
||||
`Expected event handler to be a function, got ${typeof handler}`,
|
||||
);
|
||||
}
|
||||
Emittr._addHandler(this, name, this._wrapOnceHandler(name, handler));
|
||||
return this;
|
||||
}
|
||||
|
||||
public prependOnce(name: string, handler: EventHandler): this {
|
||||
if (!isFun(handler)) {
|
||||
throw new TypeError(
|
||||
`Expected event handler to be a function, got ${typeof handler}`,
|
||||
);
|
||||
}
|
||||
Emittr._addHandler(this, name, this._wrapOnceHandler(name, handler), true);
|
||||
return this;
|
||||
}
|
||||
|
||||
public off(name: string, handler: EventHandler): this {
|
||||
Emittr._removeHandler(this, name, handler);
|
||||
return this;
|
||||
}
|
||||
|
||||
public removeAllHandlers(): this {
|
||||
const handlers = this._handlers;
|
||||
this._handlers = Object.create(null);
|
||||
const removeHandlers = handlers.removeHandler;
|
||||
delete handlers.removeHandler;
|
||||
let idx;
|
||||
let eventName;
|
||||
// eslint-disable-next-line guard-for-in,no-restricted-syntax
|
||||
for (eventName in handlers) {
|
||||
for (idx = handlers[eventName].length - 1; idx >= 0; idx--) {
|
||||
Emittr._callEventHandlers(this, removeHandlers, [
|
||||
eventName,
|
||||
(handlers[eventName][idx] as OnceHandler).handler ||
|
||||
handlers[eventName][idx],
|
||||
]);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private _wrapOnceHandler(name: string, handler: EventHandler): OnceHandler {
|
||||
const onceState: OnceHandlerState = {
|
||||
fired: false,
|
||||
handler,
|
||||
wrappedHandler: undefined,
|
||||
emitter: this,
|
||||
event: name,
|
||||
};
|
||||
const wrappedHandler: OnceHandler = Emittr._onceWrapper.bind(onceState);
|
||||
onceState.wrappedHandler = wrappedHandler;
|
||||
wrappedHandler.handler = handler;
|
||||
wrappedHandler.event = name;
|
||||
return wrappedHandler;
|
||||
}
|
||||
}
|
102
packages/shared/components/scrollbar/custom-scrollbar/Loop.ts
Normal file
102
packages/shared/components/scrollbar/custom-scrollbar/Loop.ts
Normal file
@ -0,0 +1,102 @@
|
||||
interface UpdatableItem {
|
||||
_unmounted?: boolean;
|
||||
update: () => unknown;
|
||||
}
|
||||
|
||||
export class RAFLoop {
|
||||
/**
|
||||
* @description List of targets to update
|
||||
*/
|
||||
private readonly targets: UpdatableItem[] = [];
|
||||
|
||||
/**
|
||||
* @description ID of requested animation frame. Valuable only if loop is active and has items to iterate.
|
||||
*/
|
||||
private animationFrameID = 0;
|
||||
|
||||
/**
|
||||
* @description Loop's state.
|
||||
*/
|
||||
private _isActive = false;
|
||||
|
||||
/**
|
||||
* @description Loop's state.
|
||||
*/
|
||||
public get isActive(): boolean {
|
||||
return this._isActive;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Start the loop if it wasn't yet.
|
||||
*/
|
||||
public start = (): this => {
|
||||
if (!this._isActive && this.targets.length) {
|
||||
this._isActive = true;
|
||||
|
||||
if (this.animationFrameID) cancelAnimationFrame(this.animationFrameID);
|
||||
this.animationFrameID = requestAnimationFrame(this.rafCallback);
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Stop the loop if is was active.
|
||||
*/
|
||||
public stop = (): this => {
|
||||
if (this._isActive) {
|
||||
this._isActive = false;
|
||||
|
||||
if (this.animationFrameID) cancelAnimationFrame(this.animationFrameID);
|
||||
this.animationFrameID = 0;
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Add target to the iteration list if it's not there.
|
||||
*/
|
||||
public addTarget = (target: UpdatableItem, silent = false): this => {
|
||||
if (!this.targets.includes(target)) {
|
||||
this.targets.push(target);
|
||||
|
||||
if (this.targets.length === 1 && !silent) this.start();
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Remove target from iteration list if it was there.
|
||||
*/
|
||||
public removeTarget = (target: UpdatableItem): this => {
|
||||
const idx = this.targets.indexOf(target);
|
||||
|
||||
if (idx !== -1) {
|
||||
this.targets.splice(idx, 1);
|
||||
|
||||
if (this.targets.length === 0) this.stop();
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Callback that called each animation frame.
|
||||
*/
|
||||
private rafCallback = (): number => {
|
||||
if (!this._isActive) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.targets.length; i++) {
|
||||
if (!this.targets[i]._unmounted) this.targets[i].update();
|
||||
}
|
||||
|
||||
this.animationFrameID = requestAnimationFrame(this.rafCallback);
|
||||
return this.animationFrameID;
|
||||
};
|
||||
}
|
||||
|
||||
export default new RAFLoop();
|
1522
packages/shared/components/scrollbar/custom-scrollbar/Scrollbar.tsx
Normal file
1522
packages/shared/components/scrollbar/custom-scrollbar/Scrollbar.tsx
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,234 @@
|
||||
/* eslint-disable @typescript-eslint/no-useless-constructor */
|
||||
import { cnb } from "cnbuilder";
|
||||
import React from "react";
|
||||
import { DraggableCore, DraggableData, DraggableEvent } from "react-draggable";
|
||||
import { AxisDirection, ElementPropsWithElementRefAndRenderer } from "./types";
|
||||
import { isBrowser, isFun, isUndef, renderDivWithRenderer } from "./util";
|
||||
|
||||
export type DragCallbackData = Pick<
|
||||
DraggableData,
|
||||
Exclude<keyof DraggableData, "node">
|
||||
>;
|
||||
|
||||
export type ScrollbarThumbProps = ElementPropsWithElementRefAndRenderer & {
|
||||
axis: AxisDirection;
|
||||
|
||||
onDrag?: (data: DragCallbackData) => void;
|
||||
onDragStart?: (data: DragCallbackData) => void;
|
||||
onDragEnd?: (data: DragCallbackData) => void;
|
||||
|
||||
ref?: (ref: ScrollbarThumb | null) => void;
|
||||
};
|
||||
|
||||
class ScrollbarThumb extends React.Component<ScrollbarThumbProps, unknown> {
|
||||
private static selectStartReplacer = () => false;
|
||||
|
||||
public element: HTMLDivElement | null = null;
|
||||
|
||||
public initialOffsetX = 0;
|
||||
|
||||
public initialOffsetY = 0;
|
||||
|
||||
private prevUserSelect: string;
|
||||
|
||||
private prevOnSelectStart: ((ev: Event) => boolean) | null;
|
||||
|
||||
private elementRefHack = React.createRef<HTMLElement>();
|
||||
|
||||
public lastDragData: DragCallbackData = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
deltaX: 0,
|
||||
deltaY: 0,
|
||||
lastX: 0,
|
||||
lastY: 0,
|
||||
};
|
||||
|
||||
public componentDidMount(): void {
|
||||
if (!this.element) {
|
||||
this.setState(() => {
|
||||
throw new Error(
|
||||
"<ScrollbarThumb> Element was not created. Possibly you haven't provided HTMLDivElement to renderer's `elementRef` function.",
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public componentWillUnmount(): void {
|
||||
this.handleOnDragStop();
|
||||
|
||||
this.elementRef(null);
|
||||
}
|
||||
|
||||
public handleOnDragStart = (ev: DraggableEvent, data: DraggableData) => {
|
||||
if (!this.element) {
|
||||
this.handleOnDragStop(ev, data);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isBrowser) {
|
||||
this.prevUserSelect = document.body.style.userSelect;
|
||||
document.body.style.userSelect = "none";
|
||||
|
||||
this.prevOnSelectStart = document.onselectstart;
|
||||
document.addEventListener(
|
||||
"selectstart",
|
||||
ScrollbarThumb.selectStartReplacer,
|
||||
);
|
||||
}
|
||||
|
||||
if (this.props.onDragStart) {
|
||||
this.props.onDragStart(
|
||||
(this.lastDragData = {
|
||||
x: data.x - this.initialOffsetX,
|
||||
y: data.y - this.initialOffsetY,
|
||||
lastX: data.lastX - this.initialOffsetX,
|
||||
lastY: data.lastY - this.initialOffsetY,
|
||||
deltaX: data.deltaX,
|
||||
deltaY: data.deltaY,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
this.element.classList.add("dragging");
|
||||
};
|
||||
|
||||
public handleOnDrag = (ev: DraggableEvent, data: DraggableData) => {
|
||||
if (!this.element) {
|
||||
this.handleOnDragStop(ev, data);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.props.onDrag) {
|
||||
this.props.onDrag(
|
||||
(this.lastDragData = {
|
||||
x: data.x - this.initialOffsetX,
|
||||
y: data.y - this.initialOffsetY,
|
||||
lastX: data.lastX - this.initialOffsetX,
|
||||
lastY: data.lastY - this.initialOffsetY,
|
||||
deltaX: data.deltaX,
|
||||
deltaY: data.deltaY,
|
||||
}),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
public handleOnDragStop = (ev?: DraggableEvent, data?: DraggableData) => {
|
||||
const resultData = data
|
||||
? {
|
||||
x: data.x - this.initialOffsetX,
|
||||
y: data.y - this.initialOffsetY,
|
||||
lastX: data.lastX - this.initialOffsetX,
|
||||
lastY: data.lastY - this.initialOffsetY,
|
||||
deltaX: data.deltaX,
|
||||
deltaY: data.deltaY,
|
||||
}
|
||||
: this.lastDragData;
|
||||
|
||||
if (this.props.onDragEnd) this.props.onDragEnd(resultData);
|
||||
|
||||
if (this.element) this.element.classList.remove("dragging");
|
||||
|
||||
if (isBrowser) {
|
||||
document.body.style.userSelect = this.prevUserSelect;
|
||||
|
||||
if (this.prevOnSelectStart) {
|
||||
document.addEventListener("selectstart", this.prevOnSelectStart);
|
||||
}
|
||||
|
||||
this.prevOnSelectStart = null;
|
||||
}
|
||||
|
||||
this.initialOffsetX = 0;
|
||||
this.initialOffsetY = 0;
|
||||
this.lastDragData = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
deltaX: 0,
|
||||
deltaY: 0,
|
||||
lastX: 0,
|
||||
lastY: 0,
|
||||
};
|
||||
};
|
||||
|
||||
public handleOnMouseDown = (ev: MouseEvent) => {
|
||||
if (!this.element) {
|
||||
return;
|
||||
}
|
||||
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
if (!isUndef(ev.offsetX)) {
|
||||
/* istanbul ignore next */
|
||||
this.initialOffsetX = ev.offsetX;
|
||||
/* istanbul ignore next */
|
||||
this.initialOffsetY = ev.offsetY;
|
||||
} else {
|
||||
const rect: DOMRect = this.element.getBoundingClientRect();
|
||||
this.initialOffsetX =
|
||||
(ev.clientX || (ev as unknown as TouchEvent).touches[0].clientX) -
|
||||
rect.left;
|
||||
this.initialOffsetY =
|
||||
(ev.clientY || (ev as unknown as TouchEvent).touches[0].clientY) -
|
||||
rect.top;
|
||||
}
|
||||
};
|
||||
|
||||
private elementRef = (ref: HTMLDivElement | null): void => {
|
||||
if (!ref && this.element) return;
|
||||
|
||||
if (isFun(this.props.elementRef)) this.props.elementRef(ref);
|
||||
this.element = ref;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
this.elementRefHack.current = ref;
|
||||
};
|
||||
|
||||
public render(): React.ReactElement<unknown> | null {
|
||||
const {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
elementRef,
|
||||
|
||||
axis,
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
onDrag,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
onDragEnd,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
onDragStart,
|
||||
|
||||
...props
|
||||
} = this.props as ScrollbarThumbProps;
|
||||
|
||||
props.className = cnb(
|
||||
"ScrollbarsCustom-Thumb",
|
||||
axis === AxisDirection.X
|
||||
? "ScrollbarsCustom-ThumbX"
|
||||
: "ScrollbarsCustom-ThumbY",
|
||||
props.className,
|
||||
);
|
||||
|
||||
if (props.renderer) {
|
||||
(props as ScrollbarThumbProps).axis = axis;
|
||||
}
|
||||
|
||||
return (
|
||||
<DraggableCore
|
||||
allowAnyClick={false}
|
||||
enableUserSelectHack={false}
|
||||
onMouseDown={this.handleOnMouseDown}
|
||||
onDrag={this.handleOnDrag}
|
||||
onStart={this.handleOnDragStart}
|
||||
onStop={this.handleOnDragStop}
|
||||
nodeRef={this.elementRefHack}
|
||||
>
|
||||
{renderDivWithRenderer(props, this.elementRef)}
|
||||
</DraggableCore>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ScrollbarThumb;
|
@ -0,0 +1,109 @@
|
||||
import { cnb } from "cnbuilder";
|
||||
import React from "react";
|
||||
import { AxisDirection, ElementPropsWithElementRefAndRenderer } from "./types";
|
||||
import { isFun, isUndef, renderDivWithRenderer } from "./util";
|
||||
|
||||
export interface ScrollbarTrackClickParameters {
|
||||
axis: AxisDirection;
|
||||
offset: number;
|
||||
}
|
||||
|
||||
export type ScrollbarTrackProps = ElementPropsWithElementRefAndRenderer & {
|
||||
axis: AxisDirection;
|
||||
|
||||
onClick?: (ev: MouseEvent, values: ScrollbarTrackClickParameters) => void;
|
||||
|
||||
ref?: (ref: ScrollbarTrack | null) => void;
|
||||
};
|
||||
|
||||
class ScrollbarTrack extends React.Component<ScrollbarTrackProps, unknown> {
|
||||
public element: HTMLDivElement | null = null;
|
||||
|
||||
public componentDidMount(): void {
|
||||
if (!this.element) {
|
||||
this.setState(() => {
|
||||
throw new Error(
|
||||
"Element was not created. Possibly you haven't provided HTMLDivElement to renderer's `elementRef` function.",
|
||||
);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.element?.addEventListener("click", this.handleClick);
|
||||
}
|
||||
|
||||
public componentWillUnmount(): void {
|
||||
if (this.element) {
|
||||
this.element.removeEventListener("click", this.handleClick);
|
||||
this.element = null;
|
||||
|
||||
this.elementRef(null);
|
||||
}
|
||||
}
|
||||
|
||||
private elementRef = (ref: HTMLDivElement | null): void => {
|
||||
if (!ref && this.element) return;
|
||||
if (isFun(this.props.elementRef)) this.props.elementRef(ref);
|
||||
this.element = ref;
|
||||
};
|
||||
|
||||
private handleClick = (ev: MouseEvent) => {
|
||||
if (!ev || !this.element || ev.button !== 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isFun(this.props.onClick) && ev.target === this.element) {
|
||||
if (!isUndef(ev.offsetX)) {
|
||||
this.props.onClick(ev, {
|
||||
axis: this.props.axis,
|
||||
offset: this.props.axis === AxisDirection.X ? ev.offsetX : ev.offsetY,
|
||||
});
|
||||
} else {
|
||||
// support for old browsers
|
||||
/* istanbul ignore next */
|
||||
const rect: ClientRect = this.element.getBoundingClientRect();
|
||||
/* istanbul ignore next */
|
||||
this.props.onClick(ev, {
|
||||
axis: this.props.axis,
|
||||
offset:
|
||||
this.props.axis === AxisDirection.X
|
||||
? (ev.clientX ||
|
||||
(ev as unknown as TouchEvent).touches[0].clientX) - rect.left
|
||||
: (ev.clientY ||
|
||||
(ev as unknown as TouchEvent).touches[0].clientY) - rect.top,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
public render(): React.ReactElement<unknown> | null {
|
||||
const {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
elementRef,
|
||||
|
||||
axis,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
onClick,
|
||||
|
||||
...props
|
||||
} = this.props as ScrollbarTrackProps;
|
||||
|
||||
props.className = cnb(
|
||||
"ScrollbarsCustom-Track",
|
||||
axis === AxisDirection.X
|
||||
? "ScrollbarsCustom-TrackX"
|
||||
: "ScrollbarsCustom-TrackY",
|
||||
props.className,
|
||||
);
|
||||
|
||||
if (props.renderer) {
|
||||
(props as ScrollbarTrackProps).axis = axis;
|
||||
}
|
||||
|
||||
return renderDivWithRenderer(props, this.elementRef);
|
||||
}
|
||||
}
|
||||
|
||||
export default ScrollbarTrack;
|
@ -0,0 +1,7 @@
|
||||
import Scrollbar, { ScrollbarProps, ScrollbarState } from "./Scrollbar";
|
||||
|
||||
export { ScrollbarContext } from "./Scrollbar";
|
||||
|
||||
export { Scrollbar };
|
||||
|
||||
export type { ScrollbarProps, ScrollbarState };
|
@ -0,0 +1,60 @@
|
||||
import * as React from "react";
|
||||
|
||||
export const style = {
|
||||
holder: {
|
||||
position: "relative",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
} as React.CSSProperties,
|
||||
|
||||
wrapper: {
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
} as React.CSSProperties,
|
||||
|
||||
content: {
|
||||
boxSizing: "border-box",
|
||||
} as React.CSSProperties,
|
||||
|
||||
track: {
|
||||
common: {
|
||||
position: "absolute",
|
||||
overflow: "hidden",
|
||||
borderRadius: 4,
|
||||
background: "rgba(0,0,0,.1)",
|
||||
userSelect: "none",
|
||||
} as React.CSSProperties,
|
||||
x: {
|
||||
height: 10,
|
||||
width: "calc(100% - 20px)",
|
||||
bottom: 0,
|
||||
left: 10,
|
||||
} as React.CSSProperties,
|
||||
y: {
|
||||
width: 10,
|
||||
height: "calc(100% - 20px)",
|
||||
top: 10,
|
||||
} as React.CSSProperties,
|
||||
},
|
||||
|
||||
thumb: {
|
||||
common: {
|
||||
cursor: "pointer",
|
||||
borderRadius: 4,
|
||||
background: "rgba(0,0,0,.4)",
|
||||
} as React.CSSProperties,
|
||||
x: {
|
||||
height: "100%",
|
||||
width: 0,
|
||||
} as React.CSSProperties,
|
||||
y: {
|
||||
width: "100%",
|
||||
height: 0,
|
||||
} as React.CSSProperties,
|
||||
},
|
||||
};
|
||||
|
||||
export default style;
|
106
packages/shared/components/scrollbar/custom-scrollbar/types.ts
Normal file
106
packages/shared/components/scrollbar/custom-scrollbar/types.ts
Normal file
@ -0,0 +1,106 @@
|
||||
import * as React from "react";
|
||||
|
||||
export enum AxisDirection {
|
||||
X = "x",
|
||||
Y = "y",
|
||||
}
|
||||
|
||||
export enum TrackClickBehavior {
|
||||
JUMP = "jump",
|
||||
STEP = "step",
|
||||
}
|
||||
|
||||
export type ElementRef<T = HTMLDivElement> = (element: T | null) => void;
|
||||
|
||||
export type ElementPropsWithElementRef<T = HTMLDivElement> =
|
||||
React.HTMLProps<T> & {
|
||||
elementRef?: ElementRef<T>;
|
||||
};
|
||||
|
||||
export type ElementRenderer<T = HTMLDivElement> = React.FC<
|
||||
React.PropsWithChildren<ElementPropsWithElementRef<T>>
|
||||
>;
|
||||
|
||||
export type ElementPropsWithElementRefAndRenderer<T = HTMLDivElement> =
|
||||
React.HTMLProps<T> & {
|
||||
elementRef?: ElementRef<T>;
|
||||
renderer?: ElementRenderer<T>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Contains all scroll-related values
|
||||
*/
|
||||
export type ScrollState = {
|
||||
/**
|
||||
* @description Scroller's native clientHeight parameter
|
||||
*/
|
||||
clientHeight: number;
|
||||
/**
|
||||
* @description Scroller's native clientWidth parameter
|
||||
*/
|
||||
clientWidth: number;
|
||||
|
||||
/**
|
||||
* @description Content's scroll height
|
||||
*/
|
||||
contentScrollHeight: number;
|
||||
/**
|
||||
* @description Content's scroll width
|
||||
*/
|
||||
contentScrollWidth: number;
|
||||
|
||||
/**
|
||||
* @description Scroller's native scrollHeight parameter
|
||||
*/
|
||||
scrollHeight: number;
|
||||
/**
|
||||
* @description Scroller's native scrollWidth parameter
|
||||
*/
|
||||
scrollWidth: number;
|
||||
|
||||
/**
|
||||
* @description Scroller's native scrollTop parameter
|
||||
*/
|
||||
scrollTop: number;
|
||||
/**
|
||||
* @description Scroller's native scrollLeft parameter
|
||||
*/
|
||||
scrollLeft: number;
|
||||
|
||||
/**
|
||||
* @description Indicates whether vertical scroll blocked via properties
|
||||
*/
|
||||
scrollYBlocked: boolean;
|
||||
/**
|
||||
* @description Indicates whether horizontal scroll blocked via properties
|
||||
*/
|
||||
scrollXBlocked: boolean;
|
||||
|
||||
/**
|
||||
* @description Indicates whether the content overflows vertically and scrolling not blocked
|
||||
*/
|
||||
scrollYPossible: boolean;
|
||||
/**
|
||||
* @description Indicates whether the content overflows horizontally and scrolling not blocked
|
||||
*/
|
||||
scrollXPossible: boolean;
|
||||
|
||||
/**
|
||||
* @description Indicates whether vertical track is visible
|
||||
*/
|
||||
trackYVisible: boolean;
|
||||
/**
|
||||
* @description Indicates whether horizontal track is visible
|
||||
*/
|
||||
trackXVisible: boolean;
|
||||
|
||||
/**
|
||||
* @description Indicates whether display direction is right-to-left
|
||||
*/
|
||||
isRTL?: boolean;
|
||||
|
||||
/**
|
||||
* @description Pages zoom level - it affects scrollbars
|
||||
*/
|
||||
zoomLevel: number;
|
||||
};
|
299
packages/shared/components/scrollbar/custom-scrollbar/util.tsx
Normal file
299
packages/shared/components/scrollbar/custom-scrollbar/util.tsx
Normal file
@ -0,0 +1,299 @@
|
||||
import * as React from "react";
|
||||
import { ElementPropsWithElementRefAndRenderer, ElementRef } from "./types";
|
||||
|
||||
let doc: Document | null = typeof document === "object" ? document : null;
|
||||
|
||||
export const isBrowser =
|
||||
typeof window !== "undefined" &&
|
||||
typeof navigator !== "undefined" &&
|
||||
typeof document !== "undefined";
|
||||
|
||||
export const isUndef = (v: unknown): v is Exclude<typeof v, undefined> => {
|
||||
return typeof v === "undefined";
|
||||
};
|
||||
|
||||
export const isFun = (v: unknown): v is CallableFunction => {
|
||||
return typeof v === "function";
|
||||
};
|
||||
|
||||
export const isNum = (v: unknown): v is number => {
|
||||
return typeof v === "number";
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Will return renderer result if presented, div element otherwise.
|
||||
* If renderer is presented it'll receive `elementRef` function which should be used as HTMLElement's ref.
|
||||
*
|
||||
* @param props {ElementPropsWithElementRefAndRenderer}
|
||||
* @param elementRef {ElementRef}
|
||||
*/
|
||||
export const renderDivWithRenderer = (
|
||||
props: ElementPropsWithElementRefAndRenderer,
|
||||
elementRef: ElementRef,
|
||||
): React.ReactElement | null => {
|
||||
if (isFun(props.renderer)) {
|
||||
props.elementRef = elementRef;
|
||||
|
||||
const { renderer } = props;
|
||||
|
||||
delete props.renderer;
|
||||
|
||||
return renderer(props);
|
||||
}
|
||||
|
||||
delete props.elementRef;
|
||||
|
||||
return <div {...props} key={props.key} ref={elementRef} />;
|
||||
};
|
||||
|
||||
const getInnerSize = (
|
||||
el: HTMLElement,
|
||||
dimension: string,
|
||||
padding1: string,
|
||||
padding2: string,
|
||||
): number => {
|
||||
const styles = getComputedStyle(el);
|
||||
|
||||
if (styles.boxSizing === "border-box") {
|
||||
return Math.max(
|
||||
0,
|
||||
(Number.parseFloat(styles[dimension] as string) || 0) -
|
||||
(Number.parseFloat(styles[padding1] as string) || 0) -
|
||||
(Number.parseFloat(styles[padding2] as string) || 0),
|
||||
);
|
||||
}
|
||||
|
||||
return Number.parseFloat(styles[dimension] as string) || 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Return element's height without padding
|
||||
*/
|
||||
export const getInnerHeight = (el: HTMLElement): number => {
|
||||
return getInnerSize(el, "height", "paddingTop", "paddingBottom");
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Return element's width without padding
|
||||
*/
|
||||
export const getInnerWidth = (el: HTMLElement): number => {
|
||||
return getInnerSize(el, "width", "paddingLeft", "paddingRight");
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Return unique UUID v4
|
||||
*/
|
||||
export const uuid = () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||
let uuid = "";
|
||||
|
||||
for (let i = 0; i < 32; i++) {
|
||||
switch (i) {
|
||||
case 8:
|
||||
case 20: {
|
||||
uuid += `-${Math.trunc(Math.random() * 16).toString(16)}`;
|
||||
|
||||
break;
|
||||
}
|
||||
case 12: {
|
||||
uuid += "-4";
|
||||
|
||||
break;
|
||||
}
|
||||
case 16: {
|
||||
uuid += `-${((Math.random() * 16) | (0 & 3) | 8).toString(16)}`;
|
||||
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
uuid += Math.trunc(Math.random() * 16).toString(16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return uuid;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Calculate thumb size for given viewport and track parameters
|
||||
*
|
||||
* @param {number} contentSize - Scrollable content size
|
||||
* @param {number} viewportSize - Viewport size
|
||||
* @param {number} trackSize - Track size thumb can move
|
||||
* @param {number} minimalSize - Minimal thumb's size
|
||||
* @param {number} maximalSize - Maximal thumb's size
|
||||
*/
|
||||
export const calcThumbSize = (
|
||||
contentSize: number,
|
||||
viewportSize: number,
|
||||
trackSize: number,
|
||||
minimalSize?: number,
|
||||
maximalSize?: number,
|
||||
): number => {
|
||||
if (viewportSize >= contentSize) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let thumbSize = (viewportSize / contentSize) * trackSize;
|
||||
|
||||
if (isNum(maximalSize)) {
|
||||
thumbSize = Math.min(maximalSize, thumbSize);
|
||||
}
|
||||
if (isNum(minimalSize)) {
|
||||
thumbSize = Math.max(minimalSize, thumbSize);
|
||||
}
|
||||
|
||||
return thumbSize;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Calculate thumb offset for given viewport, track and thumb parameters
|
||||
*
|
||||
* @param {number} contentSize - Scrollable content size
|
||||
* @param {number} viewportSize - Viewport size
|
||||
* @param {number} trackSize - Track size thumb can move
|
||||
* @param {number} thumbSize - Thumb size
|
||||
* @param {number} scroll - Scroll value to represent
|
||||
*/
|
||||
export const calcThumbOffset = (
|
||||
contentSize: number,
|
||||
viewportSize: number,
|
||||
trackSize: number,
|
||||
thumbSize: number,
|
||||
scroll: number,
|
||||
): number => {
|
||||
if (!scroll || !thumbSize || viewportSize >= contentSize) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ((trackSize - thumbSize) * scroll) / (contentSize - viewportSize);
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Calculate scroll for given viewport, track and thumb parameters
|
||||
*
|
||||
* @param {number} contentSize - Scrollable content size
|
||||
* @param {number} viewportSize - Viewport size
|
||||
* @param {number} trackSize - Track size thumb can move
|
||||
* @param {number} thumbSize - Thumb size
|
||||
* @param {number} thumbOffset - Thumb's offset representing the scroll
|
||||
*/
|
||||
export const calcScrollForThumbOffset = (
|
||||
contentSize: number,
|
||||
viewportSize: number,
|
||||
trackSize: number,
|
||||
thumbSize: number,
|
||||
thumbOffset: number,
|
||||
): number => {
|
||||
if (!thumbOffset || !thumbSize || viewportSize >= contentSize) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (thumbOffset * (contentSize - viewportSize)) / (trackSize - thumbSize);
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Set the document node to calculate the scrollbar width.<br/>
|
||||
* <i>null</i> will force getter to return 0 (it'll imitate SSR).
|
||||
*/
|
||||
export const _dbgSetDocument = (v: Document | null): Document | null => {
|
||||
if (v === null || v instanceof HTMLDocument) {
|
||||
doc = v;
|
||||
return doc;
|
||||
}
|
||||
|
||||
throw new TypeError(
|
||||
`override value expected to be an instance of HTMLDocument or null, got ${typeof v}`,
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Return current document node
|
||||
*/
|
||||
export const _dbgGetDocument = (): Document | null => doc;
|
||||
|
||||
interface GetScrollbarWidthFN {
|
||||
_cache?: number;
|
||||
|
||||
(force?: boolean): number | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Returns scrollbar width specific for current environment. Can return undefined if DOM is not ready yet.
|
||||
*/
|
||||
export const getScrollbarWidth: GetScrollbarWidthFN = (
|
||||
force = false,
|
||||
): number | undefined => {
|
||||
if (!doc) {
|
||||
getScrollbarWidth._cache = 0;
|
||||
|
||||
return getScrollbarWidth._cache;
|
||||
}
|
||||
|
||||
if (!force && !isUndef(getScrollbarWidth._cache)) {
|
||||
return getScrollbarWidth._cache as number;
|
||||
}
|
||||
|
||||
const el = doc.createElement("div");
|
||||
el.setAttribute(
|
||||
"style",
|
||||
"position:absolute;width:100px;height:100px;top:-999px;left:-999px;overflow:scroll;",
|
||||
);
|
||||
|
||||
doc.body.append(el);
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (el.clientWidth === 0) {
|
||||
// Do not even cache this value because there is no calculations. Issue https://github.com/xobotyi/react-scrollbars-custom/issues/123
|
||||
el.remove();
|
||||
return;
|
||||
}
|
||||
getScrollbarWidth._cache = 100 - el.clientWidth;
|
||||
el.remove();
|
||||
|
||||
return getScrollbarWidth._cache;
|
||||
};
|
||||
|
||||
interface ShouldReverseRtlScroll {
|
||||
_cache?: boolean;
|
||||
|
||||
(force?: boolean): boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Detect need of horizontal scroll reverse while RTL.
|
||||
*/
|
||||
export const shouldReverseRtlScroll: ShouldReverseRtlScroll = (
|
||||
force = false,
|
||||
): boolean => {
|
||||
if (!force && !isUndef(shouldReverseRtlScroll._cache)) {
|
||||
return shouldReverseRtlScroll._cache as boolean;
|
||||
}
|
||||
|
||||
if (!doc) {
|
||||
shouldReverseRtlScroll._cache = false;
|
||||
|
||||
return shouldReverseRtlScroll._cache;
|
||||
}
|
||||
|
||||
const el = doc.createElement("div");
|
||||
const child = doc.createElement("div");
|
||||
|
||||
el.append(child);
|
||||
|
||||
el.setAttribute(
|
||||
"style",
|
||||
"position:absolute;width:100px;height:100px;top:-999px;left:-999px;overflow:scroll;direction:rtl",
|
||||
);
|
||||
child.setAttribute("style", "width:1000px;height:1000px");
|
||||
|
||||
doc.body.append(el);
|
||||
|
||||
el.scrollLeft = -50;
|
||||
shouldReverseRtlScroll._cache = el.scrollLeft === -50;
|
||||
|
||||
el.remove();
|
||||
|
||||
return shouldReverseRtlScroll._cache;
|
||||
};
|
||||
|
@ -100,10 +100,6 @@ module.exports = {
|
||||
singleton: true,
|
||||
requiredVersion: deps["prop-types"],
|
||||
},
|
||||
"react-custom-scrollbars": {
|
||||
singleton: true,
|
||||
requiredVersion: compDeps["react-custom-scrollbars"],
|
||||
},
|
||||
"react-device-detect": {
|
||||
singleton: true,
|
||||
requiredVersion: compDeps["react-device-detect"],
|
||||
|
@ -24,6 +24,7 @@
|
||||
"@use-gesture/react": "^10.2.24",
|
||||
"attr-accept": "^2.2.2",
|
||||
"axios": "^0.22.0",
|
||||
"cnbuilder": "^3.1.0",
|
||||
"cross-fetch": "3.1.5",
|
||||
"csvjson-json_beautifier": "^5.0.4",
|
||||
"email-addresses": "^3.1.0",
|
||||
@ -47,9 +48,9 @@
|
||||
"react-autosize-textarea": "^7.1.0",
|
||||
"react-content-loader": "^5.1.4",
|
||||
"react-countdown": "2.3.5",
|
||||
"react-custom-scrollbars": "^4.2.1",
|
||||
"react-device-detect": "^1.17.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-draggable": "^4.4.6",
|
||||
"react-dropzone": "^11.7.1",
|
||||
"react-hammerjs": "^1.0.1",
|
||||
"react-i18next": "^13.2.1",
|
||||
@ -75,7 +76,8 @@
|
||||
"socket.io-client": "^4.6.1",
|
||||
"styled-components": "^5.3.9",
|
||||
"utif": "^3.1.0",
|
||||
"workbox-window": "^6.5.4"
|
||||
"workbox-window": "^6.5.4",
|
||||
"zoom-level": "^2.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.21.0",
|
||||
@ -171,3 +173,4 @@
|
||||
"react-dom": "^18.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -348,10 +348,8 @@ export function convertLanguage(key: string) {
|
||||
case "fr-FR":
|
||||
return "fr";
|
||||
default:
|
||||
return "en-GB";
|
||||
return key;
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
export function convertToCulture(key: string) {
|
||||
@ -377,9 +375,8 @@ export function convertToCulture(key: string) {
|
||||
case "zh":
|
||||
return "zh-CN";
|
||||
default:
|
||||
return "en-US";
|
||||
return key;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
export function convertToLanguage(key: string) {
|
||||
|
72
yarn.lock
72
yarn.lock
@ -3304,6 +3304,7 @@ __metadata:
|
||||
babel-plugin-transform-react-remove-prop-types: "npm:^0.4.24"
|
||||
babel-plugin-transform-rename-import: "npm:^2.3.0"
|
||||
babel-preset-react-app: "npm:^9.1.2"
|
||||
cnbuilder: "npm:^3.1.0"
|
||||
copy-webpack-plugin: "npm:6.4.1"
|
||||
cross-env: "npm:^6.0.3"
|
||||
cross-fetch: "npm:3.1.5"
|
||||
@ -3347,10 +3348,10 @@ __metadata:
|
||||
react-autosize-textarea: "npm:^7.1.0"
|
||||
react-content-loader: "npm:^5.1.4"
|
||||
react-countdown: "npm:2.3.5"
|
||||
react-custom-scrollbars: "npm:^4.2.1"
|
||||
react-device-detect: "npm:^1.17.0"
|
||||
react-docgen-typescript-plugin: "npm:^1.0.5"
|
||||
react-dom: "npm:^18.2.0"
|
||||
react-draggable: "npm:^4.4.6"
|
||||
react-dropzone: "npm:^11.7.1"
|
||||
react-hammerjs: "npm:^1.0.1"
|
||||
react-i18next: "npm:^13.2.1"
|
||||
@ -3388,6 +3389,7 @@ __metadata:
|
||||
ts-node: "npm:^10.9.1"
|
||||
utif: "npm:^3.1.0"
|
||||
workbox-window: "npm:^6.5.4"
|
||||
zoom-level: "npm:^2.5.0"
|
||||
peerDependencies:
|
||||
react: ^18.2.0
|
||||
react-dom: ^18.2.0
|
||||
@ -9855,13 +9857,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"add-px-to-style@npm:1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "add-px-to-style@npm:1.0.0"
|
||||
checksum: 2e8c3c2adce84253c4240b83e69574b1799c2bc4fb97998985b6f04520a0c2be3e1ea7fd498c29f38286ca9fbd24f7974faa6ad3a91ef4a2b7abd0404957ded2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"address@npm:^1.0.1":
|
||||
version: 1.2.2
|
||||
resolution: "address@npm:1.2.2"
|
||||
@ -13676,17 +13671,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"dom-css@npm:^2.0.0":
|
||||
version: 2.1.0
|
||||
resolution: "dom-css@npm:2.1.0"
|
||||
dependencies:
|
||||
add-px-to-style: "npm:1.0.0"
|
||||
prefix-style: "npm:2.0.1"
|
||||
to-camel-case: "npm:1.0.0"
|
||||
checksum: 6180c872f17a4c026a690e399dd55e41a90f4e98c5af33ca0f16934309b67e762c929d07e38accc4e05ac4d22e8527af0252bf478a2d780b9e593a415b18905a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"dom-helpers@npm:^5.0.1, dom-helpers@npm:^5.1.3":
|
||||
version: 5.2.1
|
||||
resolution: "dom-helpers@npm:5.2.1"
|
||||
@ -23209,13 +23193,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"prefix-style@npm:2.0.1":
|
||||
version: 2.0.1
|
||||
resolution: "prefix-style@npm:2.0.1"
|
||||
checksum: 26503ee6629e8a5d6b1de75e4a6529bdcd17767d35afb4763c4985034d15a625def95831c94cb063bf4bfa7f6be430c9b41f7af8e0625017365a52510a6e25fc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"prelude-ls@npm:^1.2.1":
|
||||
version: 1.2.1
|
||||
resolution: "prelude-ls@npm:1.2.1"
|
||||
@ -23669,7 +23646,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"raf@npm:^3.1.0, raf@npm:^3.4.1":
|
||||
"raf@npm:^3.4.1":
|
||||
version: 3.4.1
|
||||
resolution: "raf@npm:3.4.1"
|
||||
dependencies:
|
||||
@ -23957,20 +23934,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-custom-scrollbars@npm:^4.2.1":
|
||||
version: 4.2.1
|
||||
resolution: "react-custom-scrollbars@npm:4.2.1"
|
||||
dependencies:
|
||||
dom-css: "npm:^2.0.0"
|
||||
prop-types: "npm:^15.5.10"
|
||||
raf: "npm:^3.1.0"
|
||||
peerDependencies:
|
||||
react: ^0.14.0 || ^15.0.0 || ^16.0.0
|
||||
react-dom: ^0.14.0 || ^15.0.0 || ^16.0.0
|
||||
checksum: 58c62e04f6a23558f9f4c3df1e96f30efa344d0ce63eb7894f58ca497f8c383e25b86d8055496c7e7910d61eb77496dbe4b451097c98f0347a31aaa7880879f6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-device-detect@npm:^1.17.0":
|
||||
version: 1.17.0
|
||||
resolution: "react-device-detect@npm:1.17.0"
|
||||
@ -24054,7 +24017,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-draggable@npm:^4.4.5":
|
||||
"react-draggable@npm:^4.4.5, react-draggable@npm:^4.4.6":
|
||||
version: 4.4.6
|
||||
resolution: "react-draggable@npm:4.4.6"
|
||||
dependencies:
|
||||
@ -27468,15 +27431,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"to-camel-case@npm:1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "to-camel-case@npm:1.0.0"
|
||||
dependencies:
|
||||
to-space-case: "npm:^1.0.0"
|
||||
checksum: 2f74cfcffa58e8ddede7e01a03eda2cc3f0ab50efdad1d0f1092d55b4e499be43846d1f9087c458fa9efde4958e407738197d65858272c56c915b649b9ca1e62
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"to-fast-properties@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "to-fast-properties@npm:2.0.0"
|
||||
@ -27484,13 +27438,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"to-no-case@npm:^1.0.0":
|
||||
version: 1.0.2
|
||||
resolution: "to-no-case@npm:1.0.2"
|
||||
checksum: 80671e9cb9695ceea2fff23f4678caa6d7a579a2077ecb8f9a0745d90a6435b505bd299688041328f702836289b6ac6a32abba76c5c3922639c0d0224df3dffa
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"to-object-path@npm:^0.3.0":
|
||||
version: 0.3.0
|
||||
resolution: "to-object-path@npm:0.3.0"
|
||||
@ -27531,15 +27478,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"to-space-case@npm:^1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "to-space-case@npm:1.0.0"
|
||||
dependencies:
|
||||
to-no-case: "npm:^1.0.0"
|
||||
checksum: 157cebe3e98e7cb465fe1978cf26450cc8ea8e637a01039854fac7ed60ad074e5e18b32333cc5f30df81b81ca374d63df768cd4c1fa0fe672605f965376227f4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tocbot@npm:^4.20.1":
|
||||
version: 4.25.0
|
||||
resolution: "tocbot@npm:4.25.0"
|
||||
|
Loading…
Reference in New Issue
Block a user