mirror of
https://github.com/prymitive/karma
synced 2026-08-23 11:56:20 +00:00
chore(ui): migrate more code to typescript
This commit is contained in:
committed by
Łukasz Mierzwa
parent
55170f8812
commit
4d4dd111c1
Generated
+86
@@ -4936,6 +4936,16 @@
|
|||||||
"@babel/types": "^7.3.0"
|
"@babel/types": "^7.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/body-scroll-lock": {
|
||||||
|
"version": "2.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/body-scroll-lock/-/body-scroll-lock-2.6.1.tgz",
|
||||||
|
"integrity": "sha512-PPFm/2A6LfKmSpvMg58gHtSqwwMChbcKKGhSCRIhY4MyFzhY8moAN6HrTCpOeZQUqkFdTFfMqr7njeqGLKt72Q=="
|
||||||
|
},
|
||||||
|
"@types/bricks.js": {
|
||||||
|
"version": "1.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/bricks.js/-/bricks.js-1.8.1.tgz",
|
||||||
|
"integrity": "sha512-cIg3aZFILk8FsR8q1j76ThSo7JaLRHfk6vwiTDeqfUaAikqgu7j+bkAdiJknIbw8CNSSKo6Lrp7wiMuShVCWxg=="
|
||||||
|
},
|
||||||
"@types/color-name": {
|
"@types/color-name": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
|
||||||
@@ -4994,6 +5004,43 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.5.tgz",
|
||||||
"integrity": "sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ=="
|
"integrity": "sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ=="
|
||||||
},
|
},
|
||||||
|
"@types/lodash": {
|
||||||
|
"version": "4.14.157",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.157.tgz",
|
||||||
|
"integrity": "sha512-Ft5BNFmv2pHDgxV5JDsndOWTRJ+56zte0ZpYLowp03tW+K+t8u8YMOzAnpuqPgzX6WO1XpDIUm7u04M8vdDiVQ=="
|
||||||
|
},
|
||||||
|
"@types/lodash.debounce": {
|
||||||
|
"version": "4.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/lodash.debounce/-/lodash.debounce-4.0.6.tgz",
|
||||||
|
"integrity": "sha512-4WTmnnhCfDvvuLMaF3KV4Qfki93KebocUF45msxhYyjMttZDQYzHkO639ohhk8+oco2cluAFL3t5+Jn4mleylQ==",
|
||||||
|
"requires": {
|
||||||
|
"@types/lodash": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@types/lodash.merge": {
|
||||||
|
"version": "4.6.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/lodash.merge/-/lodash.merge-4.6.6.tgz",
|
||||||
|
"integrity": "sha512-IB90krzMf7YpfgP3u/EvZEdXVvm4e3gJbUvh5ieuI+o+XqiNEt6fCzqNRaiLlPVScLI59RxIGZMQ3+Ko/DJ8vQ==",
|
||||||
|
"requires": {
|
||||||
|
"@types/lodash": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@types/lodash.throttle": {
|
||||||
|
"version": "4.1.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/lodash.throttle/-/lodash.throttle-4.1.6.tgz",
|
||||||
|
"integrity": "sha512-/UIH96i/sIRYGC60NoY72jGkCJtFN5KVPhEMMMTjol65effe1gPn0tycJqV5tlSwMTzX8FqzB5yAj0rfGHTPNg==",
|
||||||
|
"requires": {
|
||||||
|
"@types/lodash": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@types/lodash.uniqueid": {
|
||||||
|
"version": "4.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/lodash.uniqueid/-/lodash.uniqueid-4.0.6.tgz",
|
||||||
|
"integrity": "sha512-WXXsDm7Q1SiAeCG9ubCiDxOuLEDi5x+Crx8SgwTFgBtofATwK1jAeSbGz2bHlfqWezi7mcjynenlBFCeDLhHlw==",
|
||||||
|
"requires": {
|
||||||
|
"@types/lodash": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/minimatch": {
|
"@types/minimatch": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
|
||||||
@@ -5027,6 +5074,14 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
|
||||||
"integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA=="
|
"integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA=="
|
||||||
},
|
},
|
||||||
|
"@types/promise-retry": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/promise-retry/-/promise-retry-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-LxIlEpEX6frE3co3vCO2EUJfHIta1IOmhDlcAsR4GMMv9hev1iTI9VwberVGkePJAuLZs5rMucrV8CziCfuJMw==",
|
||||||
|
"requires": {
|
||||||
|
"@types/retry": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/prop-types": {
|
"@types/prop-types": {
|
||||||
"version": "15.7.3",
|
"version": "15.7.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz",
|
||||||
@@ -5064,6 +5119,24 @@
|
|||||||
"@types/react": "*"
|
"@types/react": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/react-js-pagination": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/react-js-pagination/-/react-js-pagination-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-tXeN/mPSnB2tt7Mn2DL2I85bGUUwA9q04BK8AbdDt6usVW/cl3osocl5P9KXkzSLeCMwQ+Arusn8ww/asMRWUA==",
|
||||||
|
"requires": {
|
||||||
|
"@types/react": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@types/react-select": {
|
||||||
|
"version": "3.0.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/react-select/-/react-select-3.0.13.tgz",
|
||||||
|
"integrity": "sha512-JxmSArGgzAOtb37+Jz2+3av8rVmp/3s3DGwlcP+g59/a3owkiuuU4/Jajd+qA32beDPHy4gJR2kkxagPY3j9kg==",
|
||||||
|
"requires": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"@types/react-transition-group": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/react-syntax-highlighter": {
|
"@types/react-syntax-highlighter": {
|
||||||
"version": "11.0.4",
|
"version": "11.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react-syntax-highlighter/-/react-syntax-highlighter-11.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react-syntax-highlighter/-/react-syntax-highlighter-11.0.4.tgz",
|
||||||
@@ -5091,6 +5164,19 @@
|
|||||||
"@types/react": "*"
|
"@types/react": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/react-transition-group": {
|
||||||
|
"version": "4.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.0.tgz",
|
||||||
|
"integrity": "sha512-/QfLHGpu+2fQOqQaXh8MG9q03bFENooTb/it4jr5kKaZlDQfWvjqWZg48AwzPVMBHlRuTRAY7hRHCEOXz5kV6w==",
|
||||||
|
"requires": {
|
||||||
|
"@types/react": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@types/retry": {
|
||||||
|
"version": "0.12.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz",
|
||||||
|
"integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA=="
|
||||||
|
},
|
||||||
"@types/source-list-map": {
|
"@types/source-list-map": {
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz",
|
||||||
|
|||||||
@@ -11,8 +11,17 @@
|
|||||||
"@juggle/resize-observer": "3.2.0",
|
"@juggle/resize-observer": "3.2.0",
|
||||||
"@popperjs/core": "2.4.4",
|
"@popperjs/core": "2.4.4",
|
||||||
"@sentry/browser": "5.19.2",
|
"@sentry/browser": "5.19.2",
|
||||||
|
"@types/body-scroll-lock": "2.6.1",
|
||||||
|
"@types/bricks.js": "1.8.1",
|
||||||
|
"@types/lodash.debounce": "4.0.6",
|
||||||
|
"@types/lodash.merge": "4.6.6",
|
||||||
|
"@types/lodash.throttle": "4.1.6",
|
||||||
|
"@types/lodash.uniqueid": "4.0.6",
|
||||||
|
"@types/promise-retry": "1.1.3",
|
||||||
"@types/react": "16.9.43",
|
"@types/react": "16.9.43",
|
||||||
"@types/react-dom": "16.9.8",
|
"@types/react-dom": "16.9.8",
|
||||||
|
"@types/react-js-pagination": "3.0.3",
|
||||||
|
"@types/react-select": "3.0.13",
|
||||||
"body-scroll-lock": "3.0.3",
|
"body-scroll-lock": "3.0.3",
|
||||||
"bootstrap": "4.5.0",
|
"bootstrap": "4.5.0",
|
||||||
"bootswatch": "4.5.0",
|
"bootswatch": "4.5.0",
|
||||||
|
|||||||
+2
-11
@@ -18,6 +18,7 @@ import {
|
|||||||
ReactSelectStyles,
|
ReactSelectStyles,
|
||||||
} from "Components/Theme/ReactSelect";
|
} from "Components/Theme/ReactSelect";
|
||||||
import { BodyTheme, ThemeContext } from "Components/Theme";
|
import { BodyTheme, ThemeContext } from "Components/Theme";
|
||||||
|
import { UIDefaults } from "./AppBoot";
|
||||||
import { ErrorBoundary } from "./ErrorBoundary";
|
import { ErrorBoundary } from "./ErrorBoundary";
|
||||||
|
|
||||||
import "Styles/ResetCSS.scss";
|
import "Styles/ResetCSS.scss";
|
||||||
@@ -46,19 +47,9 @@ const FaviconBadge = React.lazy(() =>
|
|||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
|
|
||||||
interface UIDefaults {
|
|
||||||
Refresh: number;
|
|
||||||
HideFiltersWhenIdle: boolean;
|
|
||||||
ColorTitlebar: boolean;
|
|
||||||
Theme: "light" | "dark" | "auto";
|
|
||||||
MinimalGroupWidth: number;
|
|
||||||
AlertsPerGroup: number;
|
|
||||||
CollapseGroups: "expanded" | "collapsed" | "collapsedOnMobile";
|
|
||||||
}
|
|
||||||
|
|
||||||
interface AppProps {
|
interface AppProps {
|
||||||
defaultFilters: Array<string>;
|
defaultFilters: Array<string>;
|
||||||
uiDefaults: UIDefaults;
|
uiDefaults: UIDefaults | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const App: FunctionComponent<AppProps> = ({ defaultFilters, uiDefaults }) => {
|
const App: FunctionComponent<AppProps> = ({ defaultFilters, uiDefaults }) => {
|
||||||
|
|||||||
@@ -2,9 +2,19 @@
|
|||||||
|
|
||||||
import { init } from "@sentry/browser";
|
import { init } from "@sentry/browser";
|
||||||
|
|
||||||
|
export interface UIDefaults {
|
||||||
|
Refresh: number;
|
||||||
|
HideFiltersWhenIdle: boolean;
|
||||||
|
ColorTitlebar: boolean;
|
||||||
|
Theme: "light" | "dark" | "auto";
|
||||||
|
MinimalGroupWidth: number;
|
||||||
|
AlertsPerGroup: number;
|
||||||
|
CollapseGroups: "expanded" | "collapsed" | "collapsedOnMobile";
|
||||||
|
}
|
||||||
|
|
||||||
const SettingsElement = () => document.getElementById("settings");
|
const SettingsElement = () => document.getElementById("settings");
|
||||||
|
|
||||||
const SetupSentry = (settingsElement) => {
|
const SetupSentry = (settingsElement: HTMLElement | null) => {
|
||||||
if (
|
if (
|
||||||
settingsElement !== null &&
|
settingsElement !== null &&
|
||||||
settingsElement.dataset.sentryDsn &&
|
settingsElement.dataset.sentryDsn &&
|
||||||
@@ -29,7 +39,7 @@ const SetupSentry = (settingsElement) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const ParseDefaultFilters = (settingsElement) => {
|
const ParseDefaultFilters = (settingsElement: HTMLElement | null): string[] => {
|
||||||
let defaultFilters = [];
|
let defaultFilters = [];
|
||||||
if (
|
if (
|
||||||
settingsElement !== null &&
|
settingsElement !== null &&
|
||||||
@@ -48,7 +58,9 @@ const ParseDefaultFilters = (settingsElement) => {
|
|||||||
return defaultFilters;
|
return defaultFilters;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ParseUIDefaults = (defaultsElement) => {
|
const ParseUIDefaults = (
|
||||||
|
defaultsElement: HTMLElement | null
|
||||||
|
): UIDefaults | null => {
|
||||||
if (defaultsElement === null) {
|
if (defaultsElement === null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
function IsMobile() {
|
function IsMobile(): boolean {
|
||||||
return window.innerWidth < 768;
|
return window.innerWidth < 768;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -14,7 +14,13 @@ const FetchRetryConfig = {
|
|||||||
maxTimeout: 5000,
|
maxTimeout: 5000,
|
||||||
};
|
};
|
||||||
|
|
||||||
const FetchGet = async (uri, options, beforeRetry) =>
|
type PreRetryCallback = (number: number) => void;
|
||||||
|
|
||||||
|
const FetchGet = async (
|
||||||
|
uri: string,
|
||||||
|
options: RequestInit,
|
||||||
|
beforeRetry: PreRetryCallback
|
||||||
|
) =>
|
||||||
await promiseRetry(
|
await promiseRetry(
|
||||||
(retry, number) =>
|
(retry, number) =>
|
||||||
fetch(
|
fetch(
|
||||||
@@ -14,7 +14,7 @@ const StaticLabels = Object.freeze({
|
|||||||
SilenceID: "@silence_id",
|
SilenceID: "@silence_id",
|
||||||
});
|
});
|
||||||
|
|
||||||
function FormatQuery(name, operator, value) {
|
function FormatQuery(name: string, operator: string, value: string) {
|
||||||
return `${name}${operator}${value}`;
|
return `${name}${operator}${value}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
const NewLabelName = (v) => `New label: ${v}`;
|
|
||||||
|
|
||||||
const NewLabelValue = (v) => `New value: ${v}`;
|
|
||||||
|
|
||||||
export { NewLabelName, NewLabelValue };
|
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
const NewLabelName = (v: string) => `New label: ${v}`;
|
||||||
|
|
||||||
|
const NewLabelValue = (v: string) => `New value: ${v}`;
|
||||||
|
|
||||||
|
export { NewLabelName, NewLabelValue };
|
||||||
+5
-2
@@ -1,9 +1,12 @@
|
|||||||
import React from "react";
|
import React, { FC, ReactNode } from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
|
|
||||||
import { CSSTransition } from "react-transition-group";
|
import { CSSTransition } from "react-transition-group";
|
||||||
|
|
||||||
const DropdownSlide = ({ children, duration, ...props }) => (
|
const DropdownSlide: FC<{
|
||||||
|
children: ReactNode;
|
||||||
|
duration: number;
|
||||||
|
}> = ({ children, duration, ...props }) => (
|
||||||
<CSSTransition
|
<CSSTransition
|
||||||
classNames="components-animation-slide"
|
classNames="components-animation-slide"
|
||||||
timeout={150}
|
timeout={150}
|
||||||
+11
-4
@@ -1,9 +1,13 @@
|
|||||||
import React from "react";
|
import React, { FC, ReactNode } from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
|
|
||||||
import { CSSTransition } from "react-transition-group";
|
import { CSSTransition } from "react-transition-group";
|
||||||
|
|
||||||
const MountModal = ({ children, duration, ...props }) => (
|
const MountModal: FC<{
|
||||||
|
children: ReactNode;
|
||||||
|
in: boolean;
|
||||||
|
unmountOnExit?: boolean;
|
||||||
|
}> = ({ children, ...props }) => (
|
||||||
<CSSTransition
|
<CSSTransition
|
||||||
classNames="components-animation-modal"
|
classNames="components-animation-modal"
|
||||||
timeout={200}
|
timeout={200}
|
||||||
@@ -19,9 +23,12 @@ MountModal.propTypes = {
|
|||||||
children: PropTypes.node.isRequired,
|
children: PropTypes.node.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
const MountModalBackdrop = ({ children, duration, ...props }) => (
|
const MountModalBackdrop: FC<{
|
||||||
|
children: ReactNode;
|
||||||
|
in?: boolean;
|
||||||
|
unmountOnExit?: boolean;
|
||||||
|
}> = ({ children, ...props }) => (
|
||||||
<CSSTransition
|
<CSSTransition
|
||||||
in={true}
|
|
||||||
classNames="components-animation-backdrop"
|
classNames="components-animation-backdrop"
|
||||||
timeout={200}
|
timeout={200}
|
||||||
appear={true}
|
appear={true}
|
||||||
+5
-2
@@ -1,10 +1,13 @@
|
|||||||
import React from "react";
|
import React, { FC, ReactNode } from "react";
|
||||||
|
|
||||||
import { CSSTransition } from "react-transition-group";
|
import { CSSTransition } from "react-transition-group";
|
||||||
|
|
||||||
import { ThemeContext } from "Components/Theme";
|
import { ThemeContext } from "Components/Theme";
|
||||||
|
|
||||||
const CenteredMessage = ({ children, className }) => {
|
const CenteredMessage: FC<{
|
||||||
|
children: ReactNode;
|
||||||
|
className: string;
|
||||||
|
}> = ({ children, className }) => {
|
||||||
const context = React.useContext(ThemeContext);
|
const context = React.useContext(ThemeContext);
|
||||||
return (
|
return (
|
||||||
<CSSTransition
|
<CSSTransition
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect, FC } from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
|
|
||||||
import { autorun } from "mobx";
|
import { autorun } from "mobx";
|
||||||
import { useObserver } from "mobx-react-lite";
|
import { useObserver } from "mobx-react-lite";
|
||||||
@@ -8,7 +7,9 @@ import Favico from "favico.js";
|
|||||||
|
|
||||||
import { AlertStore } from "Stores/AlertStore";
|
import { AlertStore } from "Stores/AlertStore";
|
||||||
|
|
||||||
const FaviconBadge = ({ alertStore }) => {
|
const FaviconBadge: FC<{
|
||||||
|
alertStore: AlertStore;
|
||||||
|
}> = ({ alertStore }) => {
|
||||||
const [favico] = useState(
|
const [favico] = useState(
|
||||||
new Favico({
|
new Favico({
|
||||||
animation: "none",
|
animation: "none",
|
||||||
@@ -38,8 +39,5 @@ const FaviconBadge = ({ alertStore }) => {
|
|||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
FaviconBadge.propTypes = {
|
|
||||||
alertStore: PropTypes.instanceOf(AlertStore).isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
export { FaviconBadge };
|
export { FaviconBadge };
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import { useEffect } from "react";
|
|
||||||
import PropTypes from "prop-types";
|
|
||||||
|
|
||||||
import { AlertStore } from "Stores/AlertStore";
|
|
||||||
|
|
||||||
const FetchPauser = ({ children, alertStore }) => {
|
|
||||||
useEffect(() => {
|
|
||||||
alertStore.status.pause();
|
|
||||||
return alertStore.status.resume;
|
|
||||||
}, [alertStore.status]);
|
|
||||||
|
|
||||||
return children;
|
|
||||||
};
|
|
||||||
FetchPauser.propTypes = {
|
|
||||||
children: PropTypes.any,
|
|
||||||
alertStore: PropTypes.instanceOf(AlertStore).isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
export { FetchPauser };
|
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { FC, ReactElement, useEffect } from "react";
|
||||||
|
|
||||||
|
import { AlertStore } from "Stores/AlertStore";
|
||||||
|
|
||||||
|
const FetchPauser: FC<{
|
||||||
|
children: ReactElement;
|
||||||
|
alertStore: AlertStore;
|
||||||
|
}> = ({ children, alertStore }) => {
|
||||||
|
useEffect(() => {
|
||||||
|
alertStore.status.pause();
|
||||||
|
return alertStore.status.resume;
|
||||||
|
}, [alertStore.status]);
|
||||||
|
|
||||||
|
return children;
|
||||||
|
};
|
||||||
|
|
||||||
|
export { FetchPauser };
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import React, { useEffect, useRef } from "react";
|
import React, { useEffect, useRef, FC } from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
|
|
||||||
import { reaction } from "mobx";
|
import { reaction } from "mobx";
|
||||||
|
|
||||||
@@ -8,8 +7,11 @@ import addSeconds from "date-fns/addSeconds";
|
|||||||
import { AlertStore, AlertStoreStatuses } from "Stores/AlertStore";
|
import { AlertStore, AlertStoreStatuses } from "Stores/AlertStore";
|
||||||
import { Settings } from "Stores/Settings";
|
import { Settings } from "Stores/Settings";
|
||||||
|
|
||||||
const Fetcher = ({ alertStore, settingsStore }) => {
|
const Fetcher: FC<{
|
||||||
const timer = useRef(null);
|
alertStore: AlertStore;
|
||||||
|
settingsStore: Settings;
|
||||||
|
}> = ({ alertStore, settingsStore }) => {
|
||||||
|
const timer = useRef(undefined as number | undefined);
|
||||||
|
|
||||||
const getSortSettings = () => {
|
const getSortSettings = () => {
|
||||||
let sortSettings = {
|
let sortSettings = {
|
||||||
@@ -81,7 +83,7 @@ const Fetcher = ({ alertStore, settingsStore }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return () => clearInterval(timer.current);
|
return () => window.clearInterval(timer.current);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(
|
useEffect(
|
||||||
@@ -89,7 +91,9 @@ const Fetcher = ({ alertStore, settingsStore }) => {
|
|||||||
reaction(
|
reaction(
|
||||||
() =>
|
() =>
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
filters: alertStore.filters.values.map((f) => f.raw).join(" "),
|
filters: alertStore.filters.values
|
||||||
|
.map((f: { raw: string }) => f.raw)
|
||||||
|
.join(" "),
|
||||||
grid: {
|
grid: {
|
||||||
sortOrder: settingsStore.gridConfig.config.sortOrder,
|
sortOrder: settingsStore.gridConfig.config.sortOrder,
|
||||||
sortLabel: settingsStore.gridConfig.config.sortLabel,
|
sortLabel: settingsStore.gridConfig.config.sortLabel,
|
||||||
@@ -115,10 +119,10 @@ const Fetcher = ({ alertStore, settingsStore }) => {
|
|||||||
() => alertStore.status.paused,
|
() => alertStore.status.paused,
|
||||||
(paused) => {
|
(paused) => {
|
||||||
if (paused) {
|
if (paused) {
|
||||||
clearInterval(timer.current);
|
window.clearInterval(timer.current);
|
||||||
timer.current = null;
|
timer.current = undefined;
|
||||||
} else {
|
} else {
|
||||||
timer.current = setInterval(
|
timer.current = window.setInterval(
|
||||||
() => window.requestAnimationFrame(fetchIfIdle),
|
() => window.requestAnimationFrame(fetchIfIdle),
|
||||||
1000
|
1000
|
||||||
);
|
);
|
||||||
@@ -131,9 +135,5 @@ const Fetcher = ({ alertStore, settingsStore }) => {
|
|||||||
|
|
||||||
return <span />;
|
return <span />;
|
||||||
};
|
};
|
||||||
Fetcher.propTypes = {
|
|
||||||
alertStore: PropTypes.instanceOf(AlertStore).isRequired,
|
|
||||||
settingsStore: PropTypes.instanceOf(Settings).isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
export { Fetcher };
|
export { Fetcher };
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import PropTypes from "prop-types";
|
|
||||||
|
|
||||||
const Tab = ({ title, active, onClick }) => (
|
|
||||||
<span
|
|
||||||
className={`nav-item nav-link cursor-pointer mx-1 px-2 ${
|
|
||||||
active ? "active" : "components-tab-inactive"
|
|
||||||
}`}
|
|
||||||
onClick={onClick}
|
|
||||||
>
|
|
||||||
{title}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
Tab.propTypes = {
|
|
||||||
title: PropTypes.string.isRequired,
|
|
||||||
active: PropTypes.bool,
|
|
||||||
onClick: PropTypes.func.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
export { Tab };
|
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import React, { FC, MouseEvent } from "react";
|
||||||
|
|
||||||
|
const Tab: FC<{
|
||||||
|
title: string;
|
||||||
|
active?: boolean;
|
||||||
|
onClick: (event: MouseEvent<HTMLElement>) => void;
|
||||||
|
}> = ({ title, active, onClick }) => (
|
||||||
|
<span
|
||||||
|
className={`nav-item nav-link cursor-pointer mx-1 px-2 ${
|
||||||
|
active ? "active" : "components-tab-inactive"
|
||||||
|
}`}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
|
||||||
|
export { Tab };
|
||||||
@@ -3,7 +3,16 @@ import React from "react";
|
|||||||
import { mount } from "enzyme";
|
import { mount } from "enzyme";
|
||||||
|
|
||||||
import { PressKey } from "__mocks__/PressKey";
|
import { PressKey } from "__mocks__/PressKey";
|
||||||
import { Modal } from ".";
|
import { Modal, ModalInner } from ".";
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.restoreAllMocks();
|
||||||
|
document.body.className = "";
|
||||||
|
});
|
||||||
|
|
||||||
const fakeToggle = jest.fn();
|
const fakeToggle = jest.fn();
|
||||||
|
|
||||||
@@ -15,12 +24,21 @@ const MountedModal = (isOpen, isUpper) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
afterEach(() => {
|
describe("<ModalInner />", () => {
|
||||||
jest.resetAllMocks();
|
it("scroll isn't enabled if ref is null", () => {
|
||||||
document.body.className = "";
|
const useRefSpy = jest.spyOn(React, "useRef").mockImplementation(() =>
|
||||||
});
|
Object.defineProperty({}, "current", {
|
||||||
|
get: () => null,
|
||||||
|
set: () => {},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
const tree = mount(<ModalInner isUpper toggleOpen={fakeToggle} />);
|
||||||
|
tree.setProps({ isUpper: false });
|
||||||
|
tree.setProps({ isUpper: true });
|
||||||
|
tree.setProps({ isUpper: false });
|
||||||
|
expect(useRefSpy).toHaveBeenCalledTimes(4);
|
||||||
|
});
|
||||||
|
|
||||||
describe("<Modal />", () => {
|
|
||||||
it("'modal-open' class is appended to MountModal container", () => {
|
it("'modal-open' class is appended to MountModal container", () => {
|
||||||
const tree = MountedModal(true);
|
const tree = MountedModal(true);
|
||||||
expect(tree.find("div").at(0).hasClass("modal-open")).toBe(true);
|
expect(tree.find("div").at(0).hasClass("modal-open")).toBe(true);
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import React, { useRef, useEffect } from "react";
|
import React, { FC, useEffect } from "react";
|
||||||
import ReactDOM from "react-dom";
|
import ReactDOM from "react-dom";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
|
|
||||||
import { disableBodyScroll, enableBodyScroll } from "body-scroll-lock";
|
import { disableBodyScroll, enableBodyScroll } from "body-scroll-lock";
|
||||||
|
|
||||||
@@ -11,10 +10,16 @@ import {
|
|||||||
MountModalBackdrop,
|
MountModalBackdrop,
|
||||||
} from "Components/Animations/MountModal";
|
} from "Components/Animations/MountModal";
|
||||||
|
|
||||||
const ModalInner = ({ size, isUpper, toggleOpen, children }) => {
|
const ModalInner: FC<{
|
||||||
const ref = useRef(null);
|
size: "lg" | "xl";
|
||||||
|
isUpper: boolean;
|
||||||
|
toggleOpen: () => void;
|
||||||
|
}> = ({ size, isUpper, toggleOpen, children }) => {
|
||||||
|
// needed for tests to spy on useRef
|
||||||
|
const ref = React.useRef(null as HTMLDivElement | null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (ref.current !== null) {
|
||||||
document.body.classList.add("modal-open");
|
document.body.classList.add("modal-open");
|
||||||
disableBodyScroll(ref.current, { reserveScrollBarGap: true });
|
disableBodyScroll(ref.current, { reserveScrollBarGap: true });
|
||||||
|
|
||||||
@@ -23,6 +28,7 @@ const ModalInner = ({ size, isUpper, toggleOpen, children }) => {
|
|||||||
if (!isUpper) document.body.classList.remove("modal-open");
|
if (!isUpper) document.body.classList.remove("modal-open");
|
||||||
enableBodyScroll(modal);
|
enableBodyScroll(modal);
|
||||||
};
|
};
|
||||||
|
}
|
||||||
}, [isUpper]);
|
}, [isUpper]);
|
||||||
|
|
||||||
useHotkeys("esc", toggleOpen);
|
useHotkeys("esc", toggleOpen);
|
||||||
@@ -43,7 +49,19 @@ const ModalInner = ({ size, isUpper, toggleOpen, children }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const Modal = ({ size, isOpen, isUpper, toggleOpen, children, ...props }) => {
|
const Modal: FC<{
|
||||||
|
size?: "lg" | "xl";
|
||||||
|
isOpen: boolean;
|
||||||
|
isUpper?: boolean;
|
||||||
|
toggleOpen: () => void;
|
||||||
|
}> = ({
|
||||||
|
size = "lg",
|
||||||
|
isOpen,
|
||||||
|
isUpper = false,
|
||||||
|
toggleOpen,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}) => {
|
||||||
return ReactDOM.createPortal(
|
return ReactDOM.createPortal(
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<MountModal in={isOpen} unmountOnExit {...props}>
|
<MountModal in={isOpen} unmountOnExit {...props}>
|
||||||
@@ -58,16 +76,5 @@ const Modal = ({ size, isOpen, isUpper, toggleOpen, children, ...props }) => {
|
|||||||
document.body
|
document.body
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
Modal.propTypes = {
|
|
||||||
size: PropTypes.oneOf(["lg", "xl"]),
|
|
||||||
isOpen: PropTypes.bool.isRequired,
|
|
||||||
isUpper: PropTypes.bool,
|
|
||||||
toggleOpen: PropTypes.func.isRequired,
|
|
||||||
children: PropTypes.node.isRequired,
|
|
||||||
};
|
|
||||||
Modal.defaultProps = {
|
|
||||||
size: "lg",
|
|
||||||
isUpper: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
export { Modal };
|
export { Modal, ModalInner };
|
||||||
+7
-12
@@ -1,5 +1,4 @@
|
|||||||
import React from "react";
|
import React, { FC, ReactNode } from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
|
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { faExclamationCircle } from "@fortawesome/free-solid-svg-icons/faExclamationCircle";
|
import { faExclamationCircle } from "@fortawesome/free-solid-svg-icons/faExclamationCircle";
|
||||||
@@ -12,7 +11,7 @@ import {
|
|||||||
} from "Components/LabelSetList";
|
} from "Components/LabelSetList";
|
||||||
import { useFetchGet } from "Hooks/useFetchGet";
|
import { useFetchGet } from "Hooks/useFetchGet";
|
||||||
|
|
||||||
const FetchError = ({ message }) => (
|
const FetchError: FC<{ message: ReactNode }> = ({ message }) => (
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<h2 className="display-2 text-danger">
|
<h2 className="display-2 text-danger">
|
||||||
<FontAwesomeIcon icon={faExclamationCircle} />
|
<FontAwesomeIcon icon={faExclamationCircle} />
|
||||||
@@ -20,9 +19,6 @@ const FetchError = ({ message }) => (
|
|||||||
<p className="lead text-muted">{message}</p>
|
<p className="lead text-muted">{message}</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
FetchError.propTypes = {
|
|
||||||
message: PropTypes.node.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
const Placeholder = () => (
|
const Placeholder = () => (
|
||||||
<div className="jumbotron bg-transparent">
|
<div className="jumbotron bg-transparent">
|
||||||
@@ -32,7 +28,11 @@ const Placeholder = () => (
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
const PaginatedAlertList = ({ alertStore, filters, title }) => {
|
const PaginatedAlertList: FC<{
|
||||||
|
alertStore: AlertStore;
|
||||||
|
filters: string[];
|
||||||
|
title: string;
|
||||||
|
}> = ({ alertStore, filters, title }) => {
|
||||||
const { response, error, isLoading } = useFetchGet(
|
const { response, error, isLoading } = useFetchGet(
|
||||||
FormatBackendURI("alerts.json?") + FormatAlertsQ(filters)
|
FormatBackendURI("alerts.json?") + FormatAlertsQ(filters)
|
||||||
);
|
);
|
||||||
@@ -51,10 +51,5 @@ const PaginatedAlertList = ({ alertStore, filters, title }) => {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
PaginatedAlertList.propTypes = {
|
|
||||||
alertStore: PropTypes.instanceOf(AlertStore).isRequired,
|
|
||||||
filters: PropTypes.arrayOf(PropTypes.string).isRequired,
|
|
||||||
title: PropTypes.string,
|
|
||||||
};
|
|
||||||
|
|
||||||
export { PaginatedAlertList };
|
export { PaginatedAlertList };
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect, FC } from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
|
|
||||||
import { useHotkeys } from "react-hotkeys-hook";
|
import { useHotkeys } from "react-hotkeys-hook";
|
||||||
|
|
||||||
@@ -13,16 +12,24 @@ import { faAngleDoubleRight } from "@fortawesome/free-solid-svg-icons/faAngleDou
|
|||||||
|
|
||||||
import { IsMobile } from "Common/Device";
|
import { IsMobile } from "Common/Device";
|
||||||
|
|
||||||
const PageSelect = ({
|
type PageCallback = (page: number) => void;
|
||||||
|
|
||||||
|
const PageSelect: FC<{
|
||||||
|
totalItemsCount: number;
|
||||||
|
totalPages: number;
|
||||||
|
maxPerPage: number;
|
||||||
|
initialPage: number;
|
||||||
|
setPageCallback: PageCallback;
|
||||||
|
}> = ({
|
||||||
totalItemsCount,
|
totalItemsCount,
|
||||||
totalPages,
|
totalPages,
|
||||||
maxPerPage,
|
maxPerPage,
|
||||||
initialPage,
|
initialPage = 1,
|
||||||
setPageCallback,
|
setPageCallback,
|
||||||
}) => {
|
}) => {
|
||||||
const [activePage, setActivePage] = useState(initialPage);
|
const [activePage, setActivePage] = useState(initialPage);
|
||||||
|
|
||||||
const onChange = (page) => {
|
const onChange = (page: number) => {
|
||||||
setActivePage(page);
|
setActivePage(page);
|
||||||
setPageCallback(page);
|
setPageCallback(page);
|
||||||
};
|
};
|
||||||
@@ -83,15 +90,5 @@ const PageSelect = ({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
PageSelect.propTypes = {
|
|
||||||
totalPages: PropTypes.number.isRequired,
|
|
||||||
initialPage: PropTypes.number,
|
|
||||||
maxPerPage: PropTypes.number.isRequired,
|
|
||||||
totalItemsCount: PropTypes.number.isRequired,
|
|
||||||
setPageCallback: PropTypes.func.isRequired,
|
|
||||||
};
|
|
||||||
PageSelect.defaultProps = {
|
|
||||||
initialPage: 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
export { PageSelect };
|
export { PageSelect };
|
||||||
@@ -1,4 +1,25 @@
|
|||||||
const ReactSelectColors = {
|
import { CSSProperties } from "react";
|
||||||
|
import { Styles } from "react-select";
|
||||||
|
|
||||||
|
interface ReactSelectTheme {
|
||||||
|
color: string;
|
||||||
|
singleValueColor: string;
|
||||||
|
backgroundColor: string;
|
||||||
|
borderColor: string;
|
||||||
|
focusedBoxShadow: string;
|
||||||
|
focusedBorderColor: string;
|
||||||
|
menuBackground: string;
|
||||||
|
optionHoverBackground: string;
|
||||||
|
valueContainerBackground: string;
|
||||||
|
disabledValueContainerBackground: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ReactSelectThemes {
|
||||||
|
Light: ReactSelectTheme;
|
||||||
|
Dark: ReactSelectTheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ReactSelectColors: ReactSelectThemes = {
|
||||||
Light: {
|
Light: {
|
||||||
color: "#fff",
|
color: "#fff",
|
||||||
singleValueColor: "#000",
|
singleValueColor: "#000",
|
||||||
@@ -25,8 +46,8 @@ const ReactSelectColors = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const ReactSelectStyles = (theme) => ({
|
const ReactSelectStyles = (theme: ReactSelectTheme): Styles => ({
|
||||||
control: (base, state) =>
|
control: (base: CSSProperties, state: any) =>
|
||||||
state.isFocused
|
state.isFocused
|
||||||
? {
|
? {
|
||||||
...base,
|
...base,
|
||||||
@@ -47,7 +68,7 @@ const ReactSelectStyles = (theme) => ({
|
|||||||
borderColor: theme.borderColor,
|
borderColor: theme.borderColor,
|
||||||
"&:hover": { borderColor: theme.borderColor },
|
"&:hover": { borderColor: theme.borderColor },
|
||||||
},
|
},
|
||||||
valueContainer: (base, state) =>
|
valueContainer: (base: CSSProperties, state: any) =>
|
||||||
state.isMulti
|
state.isMulti
|
||||||
? {
|
? {
|
||||||
...base,
|
...base,
|
||||||
@@ -72,11 +93,11 @@ const ReactSelectStyles = (theme) => ({
|
|||||||
? theme.disabledValueContainerBackground
|
? theme.disabledValueContainerBackground
|
||||||
: theme.valueContainerBackground,
|
: theme.valueContainerBackground,
|
||||||
},
|
},
|
||||||
singleValue: (base, state) => ({
|
singleValue: (base: CSSProperties) => ({
|
||||||
...base,
|
...base,
|
||||||
color: theme.singleValueColor,
|
color: theme.singleValueColor,
|
||||||
}),
|
}),
|
||||||
multiValue: (base, state) => ({
|
multiValue: (base: CSSProperties) => ({
|
||||||
...base,
|
...base,
|
||||||
borderRadius: "4px",
|
borderRadius: "4px",
|
||||||
backgroundColor: theme.optionHoverBackground,
|
backgroundColor: theme.optionHoverBackground,
|
||||||
@@ -84,7 +105,7 @@ const ReactSelectStyles = (theme) => ({
|
|||||||
backgroundColor: theme.optionHoverBackground,
|
backgroundColor: theme.optionHoverBackground,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
multiValueLabel: (base, state) => ({
|
multiValueLabel: (base: CSSProperties) => ({
|
||||||
...base,
|
...base,
|
||||||
color: theme.color,
|
color: theme.color,
|
||||||
whiteSpace: "normal",
|
whiteSpace: "normal",
|
||||||
@@ -94,7 +115,7 @@ const ReactSelectStyles = (theme) => ({
|
|||||||
color: theme.color,
|
color: theme.color,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
multiValueRemove: (base, state) => ({
|
multiValueRemove: (base: CSSProperties) => ({
|
||||||
...base,
|
...base,
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
color: theme.color,
|
color: theme.color,
|
||||||
@@ -107,11 +128,11 @@ const ReactSelectStyles = (theme) => ({
|
|||||||
opacity: "0.75",
|
opacity: "0.75",
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
input: (base, state) => ({
|
input: (base: CSSProperties) => ({
|
||||||
...base,
|
...base,
|
||||||
color: "inherit",
|
color: "inherit",
|
||||||
}),
|
}),
|
||||||
indicatorsContainer: (base, state) => ({
|
indicatorsContainer: (base: CSSProperties, state: any) => ({
|
||||||
...base,
|
...base,
|
||||||
backgroundColor: state.isDisabled
|
backgroundColor: state.isDisabled
|
||||||
? theme.disabledValueContainerBackground
|
? theme.disabledValueContainerBackground
|
||||||
@@ -119,19 +140,19 @@ const ReactSelectStyles = (theme) => ({
|
|||||||
borderTopRightRadius: "0.25rem",
|
borderTopRightRadius: "0.25rem",
|
||||||
borderBottomRightRadius: "0.25rem",
|
borderBottomRightRadius: "0.25rem",
|
||||||
}),
|
}),
|
||||||
dropdownIndicator: (base, state) =>
|
dropdownIndicator: (base: CSSProperties, state: any) =>
|
||||||
state.isFocused
|
state.isFocused
|
||||||
? {
|
? {
|
||||||
...base,
|
...base,
|
||||||
"&:hover": { color: "inherit" },
|
"&:hover": { color: "inherit" },
|
||||||
}
|
}
|
||||||
: { ...base },
|
: { ...base },
|
||||||
menu: (base, state) => ({
|
menu: (base: CSSProperties) => ({
|
||||||
...base,
|
...base,
|
||||||
zIndex: 1500,
|
zIndex: 1500,
|
||||||
backgroundColor: theme.menuBackground,
|
backgroundColor: theme.menuBackground,
|
||||||
}),
|
}),
|
||||||
option: (base, state) => ({
|
option: (base: CSSProperties) => ({
|
||||||
...base,
|
...base,
|
||||||
color: "inherit",
|
color: "inherit",
|
||||||
backgroundColor: "inherit",
|
backgroundColor: "inherit",
|
||||||
@@ -38,7 +38,19 @@ const Placeholder = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const ThemeContext = React.createContext();
|
interface ThemeCtx {
|
||||||
|
isDark: boolean;
|
||||||
|
reactSelectStyles: any;
|
||||||
|
animations: {
|
||||||
|
duration: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const ThemeContext = React.createContext({
|
||||||
|
isDark: false,
|
||||||
|
reactSelectStyles: {},
|
||||||
|
animations: { duration: 1000 },
|
||||||
|
} as ThemeCtx);
|
||||||
|
|
||||||
const BodyTheme = () => {
|
const BodyTheme = () => {
|
||||||
const context = React.useContext(ThemeContext);
|
const context = React.useContext(ThemeContext);
|
||||||
+25
-23
@@ -1,6 +1,5 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect, ReactNode, FC } from "react";
|
||||||
import { createPortal } from "react-dom";
|
import { createPortal } from "react-dom";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
|
|
||||||
import { CSSTransition } from "react-transition-group";
|
import { CSSTransition } from "react-transition-group";
|
||||||
|
|
||||||
@@ -8,16 +7,24 @@ import { usePopper } from "react-popper";
|
|||||||
|
|
||||||
import { useSupportsTouch } from "Hooks/useSupportsTouch";
|
import { useSupportsTouch } from "Hooks/useSupportsTouch";
|
||||||
|
|
||||||
const TooltipWrapper = ({ title, children, className }) => {
|
const TooltipWrapper: FC<{
|
||||||
const [referenceElement, setReferenceElement] = useState(null);
|
title: ReactNode;
|
||||||
const [popperElement, setPopperElement] = useState(null);
|
children: ReactNode;
|
||||||
|
className?: string;
|
||||||
|
}> = ({ title, children, className }) => {
|
||||||
|
const [referenceElement, setReferenceElement] = useState(
|
||||||
|
null as HTMLElement | null
|
||||||
|
);
|
||||||
|
const [popperElement, setPopperElement] = useState(
|
||||||
|
null as HTMLElement | null
|
||||||
|
);
|
||||||
const { styles, attributes } = usePopper(referenceElement, popperElement, {
|
const { styles, attributes } = usePopper(referenceElement, popperElement, {
|
||||||
placement: "top",
|
placement: "top",
|
||||||
modifiers: [
|
modifiers: [
|
||||||
{
|
{
|
||||||
name: "preventOverflow",
|
name: "preventOverflow",
|
||||||
options: {
|
options: {
|
||||||
boundariesElement: "viewport",
|
rootBoundary: "viewport",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -32,22 +39,22 @@ const TooltipWrapper = ({ title, children, className }) => {
|
|||||||
const hideTooltip = () => setIsHovering(false);
|
const hideTooltip = () => setIsHovering(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let timerShow;
|
let timerShow: number | undefined;
|
||||||
let timerHide;
|
let timerHide: number | undefined;
|
||||||
|
|
||||||
if (!isHovering) {
|
if (!isHovering) {
|
||||||
if (isVisible) {
|
if (isVisible) {
|
||||||
clearTimeout(timerShow);
|
window.clearTimeout(timerShow);
|
||||||
timerHide = setTimeout(() => setIsVisible(false), 100);
|
timerHide = window.setTimeout(() => setIsVisible(false), 100);
|
||||||
}
|
}
|
||||||
setWasClicked(false);
|
setWasClicked(false);
|
||||||
} else if (wasClicked) {
|
} else if (wasClicked) {
|
||||||
clearTimeout(timerShow);
|
window.clearTimeout(timerShow);
|
||||||
clearTimeout(timerHide);
|
window.clearTimeout(timerHide);
|
||||||
setIsVisible(false);
|
setIsVisible(false);
|
||||||
} else if (!isVisible && isHovering) {
|
} else if (!isVisible && isHovering) {
|
||||||
clearTimeout(timerHide);
|
clearTimeout(timerHide);
|
||||||
timerShow = setTimeout(() => setIsVisible(true), 1000);
|
timerShow = window.setTimeout(() => setIsVisible(true), 1000);
|
||||||
}
|
}
|
||||||
return () => {
|
return () => {
|
||||||
clearTimeout(timerShow);
|
clearTimeout(timerShow);
|
||||||
@@ -59,11 +66,11 @@ const TooltipWrapper = ({ title, children, className }) => {
|
|||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<div
|
<div
|
||||||
onClick={() => setWasClicked(true)}
|
onClick={() => setWasClicked(true)}
|
||||||
onMouseOver={supportsTouch ? null : showTooltip}
|
onMouseOver={supportsTouch ? undefined : showTooltip}
|
||||||
onMouseLeave={supportsTouch ? null : hideTooltip}
|
onMouseLeave={supportsTouch ? undefined : hideTooltip}
|
||||||
onTouchStart={supportsTouch ? showTooltip : null}
|
onTouchStart={supportsTouch ? showTooltip : undefined}
|
||||||
onTouchCancel={supportsTouch ? hideTooltip : null}
|
onTouchCancel={supportsTouch ? hideTooltip : undefined}
|
||||||
onTouchEnd={supportsTouch ? hideTooltip : null}
|
onTouchEnd={supportsTouch ? hideTooltip : undefined}
|
||||||
ref={setReferenceElement}
|
ref={setReferenceElement}
|
||||||
className={`${className ? className : ""} tooltip-trigger`}
|
className={`${className ? className : ""} tooltip-trigger`}
|
||||||
>
|
>
|
||||||
@@ -94,10 +101,5 @@ const TooltipWrapper = ({ title, children, className }) => {
|
|||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
TooltipWrapper.propTypes = {
|
|
||||||
title: PropTypes.node.isRequired,
|
|
||||||
children: PropTypes.node.isRequired,
|
|
||||||
className: PropTypes.string,
|
|
||||||
};
|
|
||||||
|
|
||||||
export { TooltipWrapper };
|
export { TooltipWrapper };
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
|
|
||||||
// https://usehooks.com/useDebounce/
|
// https://usehooks.com/useDebounce/
|
||||||
function useDebounce(value, delay) {
|
function useDebounce(value: any, delay: number) {
|
||||||
const [debouncedValue, setDebouncedValue] = useState(value);
|
const [debouncedValue, setDebouncedValue] = useState(value);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -4,14 +4,26 @@ import merge from "lodash.merge";
|
|||||||
|
|
||||||
import { CommonOptions } from "Common/Fetch";
|
import { CommonOptions } from "Common/Fetch";
|
||||||
|
|
||||||
const useFetchAny = (upstreams, { fetcher = null } = {}) => {
|
interface Upstream {
|
||||||
|
uri: string;
|
||||||
|
options: RequestInit;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ResponseState {
|
||||||
|
response: string | null;
|
||||||
|
error: string | null;
|
||||||
|
responseURI: string | null;
|
||||||
|
inProgress: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const useFetchAny = (upstreams: Upstream[], { fetcher = null } = {}) => {
|
||||||
const [index, setIndex] = useState(0);
|
const [index, setIndex] = useState(0);
|
||||||
const [response, setResponse] = useState({
|
const [response, setResponse] = useState({
|
||||||
response: null,
|
response: null,
|
||||||
error: null,
|
error: null,
|
||||||
responseURI: null,
|
responseURI: null,
|
||||||
inProgress: false,
|
inProgress: false,
|
||||||
});
|
} as ResponseState);
|
||||||
|
|
||||||
const reset = useCallback(() => {
|
const reset = useCallback(() => {
|
||||||
setIndex(0);
|
setIndex(0);
|
||||||
@@ -39,7 +51,7 @@ const useFetchAny = (upstreams, { fetcher = null } = {}) => {
|
|||||||
try {
|
try {
|
||||||
const res = await (fetcher || fetch)(
|
const res = await (fetcher || fetch)(
|
||||||
uri,
|
uri,
|
||||||
merge({}, { method: "GET" }, CommonOptions, options)
|
merge({}, { method: "GET" }, CommonOptions, options) as RequestInit
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!isCancelled) {
|
if (!isCancelled) {
|
||||||
@@ -4,9 +4,9 @@ import merge from "lodash.merge";
|
|||||||
|
|
||||||
import { CommonOptions } from "Common/Fetch";
|
import { CommonOptions } from "Common/Fetch";
|
||||||
|
|
||||||
const useFetchDelete = (uri, options, deps = []) => {
|
const useFetchDelete = (uri: string, options: RequestInit, deps = []) => {
|
||||||
const [response, setResponse] = useState(null);
|
const [response, setResponse] = useState(null as string | null);
|
||||||
const [error, setError] = useState(null);
|
const [error, setError] = useState(null as string | null);
|
||||||
const [isDeleting, setIsDeleting] = useState(true);
|
const [isDeleting, setIsDeleting] = useState(true);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -7,11 +7,11 @@ import promiseRetry from "promise-retry";
|
|||||||
import { CommonOptions, FetchRetryConfig } from "Common/Fetch";
|
import { CommonOptions, FetchRetryConfig } from "Common/Fetch";
|
||||||
|
|
||||||
const useFetchGet = (
|
const useFetchGet = (
|
||||||
uri,
|
uri: string,
|
||||||
{ autorun = true, deps = [], fetcher = null } = {}
|
{ autorun = true, deps = [], fetcher = null } = {}
|
||||||
) => {
|
) => {
|
||||||
const [response, setResponse] = useState(null);
|
const [response, setResponse] = useState(null as any);
|
||||||
const [error, setError] = useState(null);
|
const [error, setError] = useState(null as string | null);
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [isRetrying, setIsRetrying] = useState(false);
|
const [isRetrying, setIsRetrying] = useState(false);
|
||||||
const [retryCount, setRetryCount] = useState(0);
|
const [retryCount, setRetryCount] = useState(0);
|
||||||
@@ -29,7 +29,7 @@ const useFetchGet = (
|
|||||||
setRetryCount(0);
|
setRetryCount(0);
|
||||||
setError(null);
|
setError(null);
|
||||||
const res = await promiseRetry(
|
const res = await promiseRetry(
|
||||||
(retry, number) =>
|
(retry: Function, number: number) =>
|
||||||
(fetcher || fetch)(
|
(fetcher || fetch)(
|
||||||
uri,
|
uri,
|
||||||
merge(
|
merge(
|
||||||
@@ -41,7 +41,7 @@ const useFetchGet = (
|
|||||||
{
|
{
|
||||||
mode: number <= FetchRetryConfig.retries ? "cors" : "no-cors",
|
mode: number <= FetchRetryConfig.retries ? "cors" : "no-cors",
|
||||||
}
|
}
|
||||||
)
|
) as RequestInit
|
||||||
).catch((err) => {
|
).catch((err) => {
|
||||||
if (!isCanceled.current) {
|
if (!isCanceled.current) {
|
||||||
setIsRetrying(true);
|
setIsRetrying(true);
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
import { useState, useEffect, useRef } from "react";
|
import { useState, useEffect, useRef } from "react";
|
||||||
|
|
||||||
|
import { TransitionProps } from "react-transition-group/Transition";
|
||||||
|
|
||||||
import { useInView } from "react-intersection-observer";
|
import { useInView } from "react-intersection-observer";
|
||||||
|
|
||||||
const defaultProps = {
|
const defaultProps: TransitionProps = {
|
||||||
in: false,
|
in: false,
|
||||||
classNames: "components-animation-flash",
|
classNames: "components-animation-flash",
|
||||||
timeout: 800,
|
timeout: 800,
|
||||||
@@ -11,7 +13,7 @@ const defaultProps = {
|
|||||||
exit: false,
|
exit: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const useFlashTransition = (flashOn) => {
|
const useFlashTransition = (flashOn: any) => {
|
||||||
const mountRef = useRef(false);
|
const mountRef = useRef(false);
|
||||||
const [ref, inView] = useInView();
|
const [ref, inView] = useInView();
|
||||||
const [isPending, setIsPending] = useState(false);
|
const [isPending, setIsPending] = useState(false);
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
import Bricks from "bricks.js";
|
import Bricks, { SizeDetail, Instance } from "bricks.js";
|
||||||
|
|
||||||
const useGrid = (sizes) => {
|
const useGrid = (sizes: SizeDetail[]) => {
|
||||||
const ref = useRef(null);
|
const ref = useRef(null as Node | null);
|
||||||
const grid = useRef(null);
|
const grid = useRef(null as Instance | null);
|
||||||
const [repack, setRepack] = useState(() => () => {});
|
const [repack, setRepack] = useState(() => () => {});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!grid.current && ref.current) {
|
if (!grid.current && ref.current) {
|
||||||
grid.current = new Bricks({
|
grid.current = Bricks({
|
||||||
container: ref.current,
|
container: ref.current,
|
||||||
sizes: sizes,
|
sizes: sizes,
|
||||||
packed: "packed",
|
packed: "packed",
|
||||||
@@ -1,10 +1,18 @@
|
|||||||
import { useEffect } from "react";
|
import { useEffect, RefObject } from "react";
|
||||||
|
|
||||||
|
type Handler = (event: MouseEvent | TouchEvent) => void;
|
||||||
|
|
||||||
// https://usehooks.com/useOnClickOutside/
|
// https://usehooks.com/useOnClickOutside/
|
||||||
function useOnClickOutside(ref, handler, enabled) {
|
function useOnClickOutside(
|
||||||
|
ref: RefObject<HTMLElement>,
|
||||||
|
handler: Handler,
|
||||||
|
enabled: boolean
|
||||||
|
) {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const listener = (event) => {
|
const listener: { (event: MouseEvent | TouchEvent): void } = (
|
||||||
if (!ref.current || ref.current.contains(event.target)) {
|
event: MouseEvent | TouchEvent
|
||||||
|
) => {
|
||||||
|
if (!ref.current || ref.current.contains(event.target as Node)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
handler(event);
|
handler(event);
|
||||||
Vendored
+2
@@ -3,3 +3,5 @@
|
|||||||
declare module "react-media-hook" {
|
declare module "react-media-hook" {
|
||||||
function useMediaPredicate(query: string): boolean;
|
function useMediaPredicate(query: string): boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare module "favico.js";
|
||||||
|
|||||||
Reference in New Issue
Block a user